diff --git a/.mailmap b/.mailmap index e50662536c48..a0a6efe87186 100644 --- a/.mailmap +++ b/.mailmap @@ -139,6 +139,9 @@ Daniel Borkmann Daniel Borkmann David Brownell David Collins +David Rheinsberg +David Rheinsberg +David Rheinsberg David Woodhouse Dedy Lansky Deepak Kumar Singh diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst index 8d3afeede10e..5f502bf68fbc 100644 --- a/Documentation/admin-guide/cgroup-v1/memory.rst +++ b/Documentation/admin-guide/cgroup-v1/memory.rst @@ -195,11 +195,11 @@ are not accounted. We just account pages under usual VM management. RSS pages are accounted at page_fault unless they've already been accounted for earlier. A file page will be accounted for as Page Cache when it's -inserted into inode (radix-tree). While it's mapped into the page tables of +inserted into inode (xarray). While it's mapped into the page tables of processes, duplicate accounting is carefully avoided. An RSS page is unaccounted when it's fully unmapped. A PageCache page is -unaccounted when it's removed from radix-tree. Even if RSS pages are fully +unaccounted when it's removed from xarray. Even if RSS pages are fully unmapped (by kswapd), they may exist as SwapCache in the system until they are really freed. Such SwapCaches are also accounted. A swapped-in page is accounted after adding into swapcache. @@ -907,7 +907,7 @@ experiences some pressure. In this situation, only group C will receive the notification, i.e. groups A and B will not receive it. This is done to avoid excessive "broadcasting" of messages, which disturbs the system and which is especially bad if we are low on memory or thrashing. Group B, will receive -notification only if there are no event listers for group C. +notification only if there are no event listeners for group C. There are three optional modes that specify different propagation behavior: diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 4ef890191196..b26b5274eaaf 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1045,7 +1045,7 @@ All time durations are in microseconds. - user_usec - system_usec - and the following three when the controller is enabled: + and the following five when the controller is enabled: - nr_periods - nr_throttled diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index e60effbf17e0..5908c458eaa4 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7080,6 +7080,13 @@ disables both lockup detectors. Default is 10 seconds. + workqueue.unbound_cpus= + [KNL,SMP] Specify to constrain one or some CPUs + to use in unbound workqueues. + Format: + By default, all online CPUs are available for + unbound workqueues. + workqueue.watchdog_thresh= If CONFIG_WQ_WATCHDOG is configured, workqueue can warn stall conditions and dump internal state to @@ -7101,15 +7108,6 @@ threshold repeatedly. They are likely good candidates for using WQ_UNBOUND workqueues instead. - workqueue.disable_numa - By default, all work items queued to unbound - workqueues are affine to the NUMA nodes they're - issued on, which results in better behavior in - general. If NUMA affinity needs to be disabled for - whatever reason, this option can be used. Note - that this also can be controlled per-workqueue for - workqueues visible under /sys/bus/workqueue/. - workqueue.power_efficient Per-cpu workqueues are generally preferred because they show better performance thanks to cache @@ -7125,6 +7123,18 @@ The default value of this parameter is determined by the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. + workqueue.default_affinity_scope= + Select the default affinity scope to use for unbound + workqueues. Can be one of "cpu", "smt", "cache", + "numa" and "system". Default is "cache". For more + information, see the Affinity Scopes section in + Documentation/core-api/workqueue.rst. + + This can be changed after boot by writing to the + matching /sys/module/workqueue/parameters file. All + workqueues with the "default" affinity scope will be + updated accordignly. + workqueue.debug_force_rr_cpu Workqueue used to implicitly guarantee that work items queued without explicit CPU specified are put diff --git a/Documentation/admin-guide/media/qcom_camss.rst b/Documentation/admin-guide/media/qcom_camss.rst index a72e17d09cb7..8a8f3ff40105 100644 --- a/Documentation/admin-guide/media/qcom_camss.rst +++ b/Documentation/admin-guide/media/qcom_camss.rst @@ -18,7 +18,7 @@ The driver implements V4L2, Media controller and V4L2 subdev interfaces. Camera sensor using V4L2 subdev interface in the kernel is supported. The driver is implemented using as a reference the Qualcomm Camera Subsystem -driver for Android as found in Code Aurora [#f1]_ [#f2]_. +driver for Android as found in Code Linaro [#f1]_ [#f2]_. Qualcomm Camera Subsystem hardware @@ -181,5 +181,5 @@ Referenced 2018-06-22. References ---------- -.. [#f1] https://source.codeaurora.org/quic/la/kernel/msm-3.10/ -.. [#f2] https://source.codeaurora.org/quic/la/kernel/msm-3.18/ +.. [#f1] https://git.codelinaro.org/clo/la/kernel/msm-3.10/ +.. [#f2] https://git.codelinaro.org/clo/la/kernel/msm-3.18/ diff --git a/Documentation/core-api/workqueue.rst b/Documentation/core-api/workqueue.rst index a4c9b9d1905f..5d7b01aed1fe 100644 --- a/Documentation/core-api/workqueue.rst +++ b/Documentation/core-api/workqueue.rst @@ -1,6 +1,6 @@ -==================================== -Concurrency Managed Workqueue (cmwq) -==================================== +========= +Workqueue +========= :Date: September, 2010 :Author: Tejun Heo @@ -25,8 +25,8 @@ there is no work item left on the workqueue the worker becomes idle. When a new work item gets queued, the worker begins executing again. -Why cmwq? -========= +Why Concurrency Managed Workqueue? +================================== In the original wq implementation, a multi threaded (MT) wq had one worker thread per CPU and a single threaded (ST) wq had one worker @@ -220,17 +220,16 @@ resources, scheduled and executed. ``max_active`` -------------- -``@max_active`` determines the maximum number of execution contexts -per CPU which can be assigned to the work items of a wq. For example, -with ``@max_active`` of 16, at most 16 work items of the wq can be -executing at the same time per CPU. +``@max_active`` determines the maximum number of execution contexts per +CPU which can be assigned to the work items of a wq. For example, with +``@max_active`` of 16, at most 16 work items of the wq can be executing +at the same time per CPU. This is always a per-CPU attribute, even for +unbound workqueues. -Currently, for a bound wq, the maximum limit for ``@max_active`` is -512 and the default value used when 0 is specified is 256. For an -unbound wq, the limit is higher of 512 and 4 * -``num_possible_cpus()``. These values are chosen sufficiently high -such that they are not the limiting factor while providing protection -in runaway cases. +The maximum limit for ``@max_active`` is 512 and the default value used +when 0 is specified is 256. These values are chosen sufficiently high +such that they are not the limiting factor while providing protection in +runaway cases. The number of active work items of a wq is usually regulated by the users of the wq, more specifically, by how many work items the users @@ -348,27 +347,346 @@ Guidelines level of locality in wq operations and work item execution. +Affinity Scopes +=============== + +An unbound workqueue groups CPUs according to its affinity scope to improve +cache locality. For example, if a workqueue is using the default affinity +scope of "cache", it will group CPUs according to last level cache +boundaries. A work item queued on the workqueue will be assigned to a worker +on one of the CPUs which share the last level cache with the issuing CPU. +Once started, the worker may or may not be allowed to move outside the scope +depending on the ``affinity_strict`` setting of the scope. + +Workqueue currently supports the following affinity scopes. + +``default`` + Use the scope in module parameter ``workqueue.default_affinity_scope`` + which is always set to one of the scopes below. + +``cpu`` + CPUs are not grouped. A work item issued on one CPU is processed by a + worker on the same CPU. This makes unbound workqueues behave as per-cpu + workqueues without concurrency management. + +``smt`` + CPUs are grouped according to SMT boundaries. This usually means that the + logical threads of each physical CPU core are grouped together. + +``cache`` + CPUs are grouped according to cache boundaries. Which specific cache + boundary is used is determined by the arch code. L3 is used in a lot of + cases. This is the default affinity scope. + +``numa`` + CPUs are grouped according to NUMA bounaries. + +``system`` + All CPUs are put in the same group. Workqueue makes no effort to process a + work item on a CPU close to the issuing CPU. + +The default affinity scope can be changed with the module parameter +``workqueue.default_affinity_scope`` and a specific workqueue's affinity +scope can be changed using ``apply_workqueue_attrs()``. + +If ``WQ_SYSFS`` is set, the workqueue will have the following affinity scope +related interface files under its ``/sys/devices/virtual/WQ_NAME/`` +directory. + +``affinity_scope`` + Read to see the current affinity scope. Write to change. + + When default is the current scope, reading this file will also show the + current effective scope in parentheses, for example, ``default (cache)``. + +``affinity_strict`` + 0 by default indicating that affinity scopes are not strict. When a work + item starts execution, workqueue makes a best-effort attempt to ensure + that the worker is inside its affinity scope, which is called + repatriation. Once started, the scheduler is free to move the worker + anywhere in the system as it sees fit. This enables benefiting from scope + locality while still being able to utilize other CPUs if necessary and + available. + + If set to 1, all workers of the scope are guaranteed always to be in the + scope. This may be useful when crossing affinity scopes has other + implications, for example, in terms of power consumption or workload + isolation. Strict NUMA scope can also be used to match the workqueue + behavior of older kernels. + + +Affinity Scopes and Performance +=============================== + +It'd be ideal if an unbound workqueue's behavior is optimal for vast +majority of use cases without further tuning. Unfortunately, in the current +kernel, there exists a pronounced trade-off between locality and utilization +necessitating explicit configurations when workqueues are heavily used. + +Higher locality leads to higher efficiency where more work is performed for +the same number of consumed CPU cycles. However, higher locality may also +cause lower overall system utilization if the work items are not spread +enough across the affinity scopes by the issuers. The following performance +testing with dm-crypt clearly illustrates this trade-off. + +The tests are run on a CPU with 12-cores/24-threads split across four L3 +caches (AMD Ryzen 9 3900x). CPU clock boost is turned off for consistency. +``/dev/dm-0`` is a dm-crypt device created on NVME SSD (Samsung 990 PRO) and +opened with ``cryptsetup`` with default settings. + + +Scenario 1: Enough issuers and work spread across the machine +------------------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k --ioengine=libaio \ + --iodepth=64 --runtime=60 --numjobs=24 --time_based --group_reporting \ + --name=iops-test-job --verify=sha512 + +There are 24 issuers, each issuing 64 IOs concurrently. ``--verify=sha512`` +makes ``fio`` generate and read back the content each time which makes +execution locality matter between the issuer and ``kcryptd``. The followings +are the read bandwidths and CPU utilizations depending on different affinity +scope settings on ``kcryptd`` measured over five runs. Bandwidths are in +MiBps, and CPU util in percents. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 1159.40 ±1.34 + - 99.31 ±0.02 + + * - cache + - 1166.40 ±0.89 + - 99.34 ±0.01 + + * - cache (strict) + - 1166.00 ±0.71 + - 99.35 ±0.01 + +With enough issuers spread across the system, there is no downside to +"cache", strict or otherwise. All three configurations saturate the whole +machine but the cache-affine ones outperform by 0.6% thanks to improved +locality. + + +Scenario 2: Fewer issuers, enough work for saturation +----------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=8 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +The only difference from the previous scenario is ``--numjobs=8``. There are +a third of the issuers but is still enough total work to saturate the +system. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 1155.40 ±0.89 + - 97.41 ±0.05 + + * - cache + - 1154.40 ±1.14 + - 96.15 ±0.09 + + * - cache (strict) + - 1112.00 ±4.64 + - 93.26 ±0.35 + +This is more than enough work to saturate the system. Both "system" and +"cache" are nearly saturating the machine but not fully. "cache" is using +less CPU but the better efficiency puts it at the same bandwidth as +"system". + +Eight issuers moving around over four L3 cache scope still allow "cache +(strict)" to mostly saturate the machine but the loss of work conservation +is now starting to hurt with 3.7% bandwidth loss. + + +Scenario 3: Even fewer issuers, not enough work to saturate +----------------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=4 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +Again, the only difference is ``--numjobs=4``. With the number of issuers +reduced to four, there now isn't enough work to saturate the whole system +and the bandwidth becomes dependent on completion latencies. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 993.60 ±1.82 + - 75.49 ±0.06 + + * - cache + - 973.40 ±1.52 + - 74.90 ±0.07 + + * - cache (strict) + - 828.20 ±4.49 + - 66.84 ±0.29 + +Now, the tradeoff between locality and utilization is clearer. "cache" shows +2% bandwidth loss compared to "system" and "cache (struct)" whopping 20%. + + +Conclusion and Recommendations +------------------------------ + +In the above experiments, the efficiency advantage of the "cache" affinity +scope over "system" is, while consistent and noticeable, small. However, the +impact is dependent on the distances between the scopes and may be more +pronounced in processors with more complex topologies. + +While the loss of work-conservation in certain scenarios hurts, it is a lot +better than "cache (strict)" and maximizing workqueue utilization is +unlikely to be the common case anyway. As such, "cache" is the default +affinity scope for unbound pools. + +* As there is no one option which is great for most cases, workqueue usages + that may consume a significant amount of CPU are recommended to configure + the workqueues using ``apply_workqueue_attrs()`` and/or enable + ``WQ_SYSFS``. + +* An unbound workqueue with strict "cpu" affinity scope behaves the same as + ``WQ_CPU_INTENSIVE`` per-cpu workqueue. There is no real advanage to the + latter and an unbound workqueue provides a lot more flexibility. + +* Affinity scopes are introduced in Linux v6.5. To emulate the previous + behavior, use strict "numa" affinity scope. + +* The loss of work-conservation in non-strict affinity scopes is likely + originating from the scheduler. There is no theoretical reason why the + kernel wouldn't be able to do the right thing and maintain + work-conservation in most cases. As such, it is possible that future + scheduler improvements may make most of these tunables unnecessary. + + +Examining Configuration +======================= + +Use tools/workqueue/wq_dump.py to examine unbound CPU affinity +configuration, worker pools and how workqueues map to the pools: :: + + $ tools/workqueue/wq_dump.py + Affinity Scopes + =============== + wq_unbound_cpumask=0000000f + + CPU + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + SMT + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + CACHE (default) + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + NUMA + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + SYSTEM + nr_pods 1 + pod_cpus [0]=0000000f + pod_node [0]=-1 + cpu_pod [0]=0 [1]=0 [2]=0 [3]=0 + + Worker Pools + ============ + pool[00] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 0 + pool[01] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 0 + pool[02] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 1 + pool[03] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 1 + pool[04] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 2 + pool[05] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 2 + pool[06] ref= 1 nice= 0 idle/workers= 3/ 3 cpu= 3 + pool[07] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 3 + pool[08] ref=42 nice= 0 idle/workers= 6/ 6 cpus=0000000f + pool[09] ref=28 nice= 0 idle/workers= 3/ 3 cpus=00000003 + pool[10] ref=28 nice= 0 idle/workers= 17/ 17 cpus=0000000c + pool[11] ref= 1 nice=-20 idle/workers= 1/ 1 cpus=0000000f + pool[12] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=00000003 + pool[13] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=0000000c + + Workqueue CPU -> pool + ===================== + [ workqueue \ CPU 0 1 2 3 dfl] + events percpu 0 2 4 6 + events_highpri percpu 1 3 5 7 + events_long percpu 0 2 4 6 + events_unbound unbound 9 9 10 10 8 + events_freezable percpu 0 2 4 6 + events_power_efficient percpu 0 2 4 6 + events_freezable_power_ percpu 0 2 4 6 + rcu_gp percpu 0 2 4 6 + rcu_par_gp percpu 0 2 4 6 + slub_flushwq percpu 0 2 4 6 + netns ordered 8 8 8 8 8 + ... + +See the command's help message for more info. + + Monitoring ========== Use tools/workqueue/wq_monitor.py to monitor workqueue operations: :: $ tools/workqueue/wq_monitor.py events - total infl CPUtime CPUhog CMwake mayday rescued + total infl CPUtime CPUhog CMW/RPR mayday rescued events 18545 0 6.1 0 5 - - events_highpri 8 0 0.0 0 0 - - events_long 3 0 0.0 0 0 - - - events_unbound 38306 0 0.1 - - - - + events_unbound 38306 0 0.1 - 7 - - events_freezable 0 0 0.0 0 0 - - events_power_efficient 29598 0 0.2 0 0 - - events_freezable_power_ 10 0 0.0 0 0 - - sock_diag_events 0 0 0.0 0 0 - - - total infl CPUtime CPUhog CMwake mayday rescued + total infl CPUtime CPUhog CMW/RPR mayday rescued events 18548 0 6.1 0 5 - - events_highpri 8 0 0.0 0 0 - - events_long 3 0 0.0 0 0 - - - events_unbound 38322 0 0.1 - - - - + events_unbound 38322 0 0.1 - 7 - - events_freezable 0 0 0.0 0 0 - - events_power_efficient 29603 0 0.2 0 0 - - events_freezable_power_ 10 0 0.0 0 0 - - diff --git a/Documentation/devicetree/bindings/i2c/i2c-atr.yaml b/Documentation/devicetree/bindings/i2c/i2c-atr.yaml new file mode 100644 index 000000000000..1939ab339bfc --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-atr.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-atr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common i2c address translator properties + +maintainers: + - Tomi Valkeinen + +description: + An I2C Address Translator (ATR) is a device with an I2C slave parent + ("upstream") port and N I2C master child ("downstream") ports, and + forwards transactions from upstream to the appropriate downstream port + with a modified slave address. The address used on the parent bus is + called the "alias" and is (potentially) different from the physical + slave address of the child bus. Address translation is done by the + hardware. + +properties: + i2c-alias-pool: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + I2C alias pool is a pool of I2C addresses on the main I2C bus that can be + used to access the remote peripherals on the serializer's I2C bus. The + addresses must be available, not used by any other peripheral. Each + remote peripheral is assigned an alias from the pool, and transactions to + that address will be forwarded to the remote peripheral, with the address + translated to the remote peripheral's real address. This property is not + needed if there are no I2C addressable remote peripherals. + +additionalProperties: true +... diff --git a/Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml b/Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml new file mode 100644 index 000000000000..c5d9e0e919f9 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/ilitek,ili9882t.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ili9882t touchscreen controller + +maintainers: + - Cong Yang + +description: + Supports the Ilitek ili9882t touchscreen controller. + This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + const: ilitek,ili9882t + + reg: + const: 0x41 + + interrupts: + maxItems: 1 + + panel: true + + reset-gpios: + maxItems: 1 + description: Reset GPIO. + + vccio-supply: + description: The 1.8V supply to the touchscreen. + +required: + - compatible + - reg + - interrupts + - panel + - vccio-supply + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: touchscreen@41 { + compatible = "ilitek,ili9882t"; + reg = <0x41>; + + interrupt-parent = <&pio>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + + panel = <&panel>; + reset-gpios = <&pio 60 GPIO_ACTIVE_LOW>; + vccio-supply = <&mt6366_vio18_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/amphion,vpu.yaml b/Documentation/devicetree/bindings/media/amphion,vpu.yaml index a9d80eaeeeb6..c0d83d755239 100644 --- a/Documentation/devicetree/bindings/media/amphion,vpu.yaml +++ b/Documentation/devicetree/bindings/media/amphion,vpu.yaml @@ -47,7 +47,7 @@ patternProperties: $ref: ../mailbox/fsl,mu.yaml# - "^vpu_core@[0-9a-f]+$": + "^vpu-core@[0-9a-f]+$": description: Each core correspond a decoder or encoder, need to configure them separately. NXP i.MX8QM SoC has one decoder and two encoder, i.MX8QXP SoC @@ -143,7 +143,7 @@ examples: power-domains = <&pd IMX_SC_R_VPU_MU_2>; }; - vpu_core0: vpu_core@2d080000 { + vpu_core0: vpu-core@2d080000 { compatible = "nxp,imx8q-vpu-decoder"; reg = <0x2d080000 0x10000>; power-domains = <&pd IMX_SC_R_VPU_DEC_0>; @@ -154,7 +154,7 @@ examples: memory-region = <&decoder_boot>, <&decoder_rpc>; }; - vpu_core1: vpu_core@2d090000 { + vpu_core1: vpu-core@2d090000 { compatible = "nxp,imx8q-vpu-encoder"; reg = <0x2d090000 0x10000>; power-domains = <&pd IMX_SC_R_VPU_ENC_0>; @@ -165,7 +165,7 @@ examples: memory-region = <&encoder1_boot>, <&encoder1_rpc>; }; - vpu_core2: vpu_core@2d0a0000 { + vpu_core2: vpu-core@2d0a0000 { reg = <0x2d0a0000 0x10000>; compatible = "nxp,imx8q-vpu-encoder"; power-domains = <&pd IMX_SC_R_VPU_ENC_1>; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt deleted file mode 100644 index 6b02a0657ad9..000000000000 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt +++ /dev/null @@ -1,100 +0,0 @@ -Cadence MIPI-CSI2 RX controller -=============================== - -The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI -lanes in input, and 4 different pixel streams in output. - -Required properties: - - compatible: must be set to "cdns,csi2rx" and an SoC-specific compatible - - reg: base address and size of the memory mapped region - - clocks: phandles to the clocks driving the controller - - clock-names: must contain: - * sys_clk: main clock - * p_clk: register bank clock - * pixel_if[0-3]_clk: pixel stream output clock, one for each stream - implemented in hardware, between 0 and 3 - -Optional properties: - - phys: phandle to the external D-PHY, phy-names must be provided - - phy-names: must contain "dphy", if the implementation uses an - external D-PHY - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 input - 1 Stream 0 output - 2 Stream 1 output - 3 Stream 2 output - 4 Stream 3 output - - The stream output port nodes are optional if they are not - connected to anything at the hardware level or implemented - in the design.Since there is only one endpoint per port, - the endpoints are not numbered. - - -Example: - -csi2rx: csi-bridge@0d060000 { - compatible = "cdns,csi2rx"; - reg = <0x0d060000 0x1000>; - clocks = <&byteclock>, <&byteclock> - <&coreclock>, <&coreclock>, - <&coreclock>, <&coreclock>; - clock-names = "sys_clk", "p_clk", - "pixel_if0_clk", "pixel_if1_clk", - "pixel_if2_clk", "pixel_if3_clk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi2rx_in_sensor: endpoint { - remote-endpoint = <&sensor_out_csi2rx>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - - port@1 { - reg = <1>; - - csi2rx_out_grabber0: endpoint { - remote-endpoint = <&grabber0_in_csi2rx>; - }; - }; - - port@2 { - reg = <2>; - - csi2rx_out_grabber1: endpoint { - remote-endpoint = <&grabber1_in_csi2rx>; - }; - }; - - port@3 { - reg = <3>; - - csi2rx_out_grabber2: endpoint { - remote-endpoint = <&grabber2_in_csi2rx>; - }; - }; - - port@4 { - reg = <4>; - - csi2rx_out_grabber3: endpoint { - remote-endpoint = <&grabber3_in_csi2rx>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml new file mode 100644 index 000000000000..30a335b10762 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/cdns,csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MIPI-CSI2 RX controller + +maintainers: + - Maxime Ripard + +description: + The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI + lanes in input, and 4 different pixel streams in output. + +properties: + compatible: + items: + - enum: + - starfive,jh7110-csi2rx + - const: cdns,csi2rx + + reg: + maxItems: 1 + + clocks: + items: + - description: CSI2Rx system clock + - description: Gated Register bank clock for APB interface + - description: pixel Clock for Stream interface 0 + - description: pixel Clock for Stream interface 1 + - description: pixel Clock for Stream interface 2 + - description: pixel Clock for Stream interface 3 + + clock-names: + items: + - const: sys_clk + - const: p_clk + - const: pixel_if0_clk + - const: pixel_if1_clk + - const: pixel_if2_clk + - const: pixel_if3_clk + + resets: + items: + - description: CSI2Rx system reset + - description: Gated Register bank reset for APB interface + - description: pixel reset for Stream interface 0 + - description: pixel reset for Stream interface 1 + - description: pixel reset for Stream interface 2 + - description: pixel reset for Stream interface 3 + + reset-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + + phys: + maxItems: 1 + description: MIPI D-PHY + + phy-names: + items: + - const: dphy + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + const: 4 + + clock-lanes: + const: 0 + + data-lanes: + minItems: 1 + maxItems: 4 + items: + maximum: 4 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 0 Output port node + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 1 Output port node + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 2 Output port node + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: + Stream 3 Output port node + + required: + - port@0 + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + csi@d060000 { + compatible = "starfive,jh7110-csi2rx", "cdns,csi2rx"; + reg = <0x0d060000 0x1000>; + clocks = <&byteclock 7>, <&byteclock 6>, + <&coreclock 8>, <&coreclock 9>, + <&coreclock 10>, <&coreclock 11>; + clock-names = "sys_clk", "p_clk", + "pixel_if0_clk", "pixel_if1_clk", + "pixel_if2_clk", "pixel_if3_clk"; + resets = <&bytereset 9>, <&bytereset 4>, + <&corereset 5>, <&corereset 6>, + <&corereset 7>, <&corereset 8>; + reset-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + phys = <&csi_phy>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_in_sensor: endpoint { + remote-endpoint = <&sensor_out_csi2rx>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_out_grabber0: endpoint { + remote-endpoint = <&grabber0_in_csi2rx>; + }; + }; + + port@2 { + reg = <2>; + + csi2rx_out_grabber1: endpoint { + remote-endpoint = <&grabber1_in_csi2rx>; + }; + }; + + port@3 { + reg = <3>; + + csi2rx_out_grabber2: endpoint { + remote-endpoint = <&grabber2_in_csi2rx>; + }; + }; + + port@4 { + reg = <4>; + + csi2rx_out_grabber3: endpoint { + remote-endpoint = <&grabber3_in_csi2rx>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml b/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml index 369c48fd9bf9..a6b73498bc21 100644 --- a/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml +++ b/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml @@ -53,6 +53,5 @@ examples: interrupts = ; clocks = <&tegra_car TEGRA124_CLK_CEC>; clock-names = "cec"; - status = "disabled"; hdmi-phandle = <&hdmi>; }; diff --git a/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml b/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml index 19a39d753aad..b68141264c0e 100644 --- a/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml +++ b/Documentation/devicetree/bindings/media/i2c/st,st-mipid02.yaml @@ -143,7 +143,6 @@ examples: mipid02: csi2rx@14 { compatible = "st,st-mipid02"; reg = <0x14>; - status = "okay"; clocks = <&clk_ext_camera_12>; clock-names = "xclk"; VDDE-supply = <&vdd>; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml new file mode 100644 index 000000000000..f6612bb0f667 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ti,ds90ub913.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DS90UB913 FPD-Link III Serializer + +maintainers: + - Tomi Valkeinen + +description: + The TI DS90UB913 is an FPD-Link III video serializer for parallel video. + +properties: + compatible: + enum: + - ti,ds90ub913a-q1 + + '#gpio-cells': + const: 2 + description: + First cell is the GPO pin number, second cell is the flags. The GPO pin + number must be in range of [0, 3]. Note that GPOs 2 and 3 are not + available in external oscillator mode. + + gpio-controller: true + + clocks: + maxItems: 1 + description: + Reference clock connected to the CLKIN pin. + + clock-names: + items: + - const: clkin + + '#clock-cells': + const: 0 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Parallel input port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + required: + - pclk-sample + + port@1: + $ref: /schemas/graph.yaml#/properties/port + unevaluatedProperties: false + description: FPD-Link III output port + + required: + - port@0 + - port@1 + + i2c: + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + +required: + - compatible + - '#gpio-cells' + - gpio-controller + - '#clock-cells' + - ports + +additionalProperties: false + +examples: + - | + #include + + serializer { + compatible = "ti,ds90ub913a-q1"; + + gpio-controller; + #gpio-cells = <2>; + + clocks = <&clk_cam_48M>; + clock-names = "clkin"; + + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub913_in: endpoint { + remote-endpoint = <&sensor_out>; + pclk-sample = <1>; + }; + }; + + port@1 { + reg = <1>; + endpoint { + remote-endpoint = <&deser_fpd_in>; + }; + }; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@48 { + compatible = "aptina,mt9v111"; + reg = <0x48>; + + clocks = <&fixed_clock>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub913_in>; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml new file mode 100644 index 000000000000..2030366994d1 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ti,ds90ub953.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DS90UB953 FPD-Link III Serializer + +maintainers: + - Tomi Valkeinen + +description: + The TI DS90UB953 is an FPD-Link III video serializer for MIPI CSI-2. + +properties: + compatible: + enum: + - ti,ds90ub953-q1 + - ti,ds90ub971-q1 + + '#gpio-cells': + const: 2 + description: + First cell is the GPIO pin number, second cell is the flags. The GPIO pin + number must be in range of [0, 3]. + + gpio-controller: true + + clocks: + maxItems: 1 + description: + Reference clock connected to the CLKIN pin. + + clock-names: + items: + - const: clkin + + '#clock-cells': + const: 0 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 input port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + unevaluatedProperties: false + description: FPD-Link III output port + + required: + - port@0 + - port@1 + + i2c: + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + +required: + - compatible + - '#gpio-cells' + - gpio-controller + - '#clock-cells' + - ports + +additionalProperties: false + +examples: + - | + #include + + serializer { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + endpoint { + remote-endpoint = <&deser_fpd_in>; + }; + }; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx274"; + reg = <0x1a>; + + reset-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + + clocks = <&serializer>; + clock-names = "inck"; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml new file mode 100644 index 000000000000..289737721c2c --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml @@ -0,0 +1,427 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ti,ds90ub960.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DS90UB9XX Family FPD-Link Deserializer Hubs + +maintainers: + - Tomi Valkeinen + +description: + The TI DS90UB9XX devices are FPD-Link video deserializers with I2C and GPIO + forwarding. + +allOf: + - $ref: /schemas/i2c/i2c-atr.yaml# + +properties: + compatible: + enum: + - ti,ds90ub960-q1 + - ti,ds90ub9702-q1 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + Reference clock connected to the REFCLK pin. + + clock-names: + items: + - const: refclk + + powerdown-gpios: + maxItems: 1 + description: + Specifier for the GPIO connected to the PDB pin. + + i2c-alias-pool: + minItems: 1 + maxItems: 32 + + links: + type: object + additionalProperties: false + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + ti,manual-strobe: + type: boolean + description: + Enable manual strobe position and EQ level + + patternProperties: + '^link@[0-3]$': + type: object + additionalProperties: false + properties: + reg: + description: The link number + maxItems: 1 + + i2c-alias: + description: + The I2C address used for the serializer. Transactions to this + address on the I2C bus where the deserializer resides are + forwarded to the serializer. + + ti,rx-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # RAW10 + - 1 # RAW12 HF + - 2 # RAW12 LF + - 3 # CSI2 SYNC + - 4 # CSI2 NON-SYNC + description: + FPD-Link Input Mode. This should reflect the hardware and the + default mode of the connected device. + + ti,cdr-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # FPD-Link III + - 1 # FPD-Link IV + description: + FPD-Link CDR Mode. This should reflect the hardware and the + default mode of the connected device. + + ti,strobe-pos: + $ref: /schemas/types.yaml#/definitions/int32 + minimum: -13 + maximum: 13 + description: Manual strobe position + + ti,eq-level: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 14 + description: Manual EQ level + + serializer: + type: object + description: FPD-Link Serializer node + + required: + - reg + - i2c-alias + - ti,rx-mode + - serializer + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: FPD-Link input 0 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + description: + Endpoint for FPD-Link port. If the RX mode for this port is RAW, + hsync-active and vsync-active must be defined. + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: FPD-Link input 1 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + description: + Endpoint for FPD-Link port. If the RX mode for this port is RAW, + hsync-active and vsync-active must be defined. + + port@2: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: FPD-Link input 2 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + description: + Endpoint for FPD-Link port. If the RX mode for this port is RAW, + hsync-active and vsync-active must be defined. + + port@3: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: FPD-Link input 3 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + description: + Endpoint for FPD-Link port. If the RX mode for this port is RAW, + hsync-active and vsync-active must be defined. + + port@4: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Output 0 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + link-frequencies: + maxItems: 1 + + required: + - data-lanes + - link-frequencies + + port@5: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Output 1 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + link-frequencies: + maxItems: 1 + + required: + - data-lanes + - link-frequencies + + required: + - port@0 + - port@1 + - port@2 + - port@3 + - port@4 + - port@5 + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + + deser@3d { + compatible = "ti,ds90ub960-q1"; + reg = <0x3d>; + + clock-names = "refclk"; + clocks = <&fixed_clock>; + + powerdown-gpios = <&pca9555 7 GPIO_ACTIVE_LOW>; + + i2c-alias-pool = <0x4a 0x4b 0x4c 0x4d 0x4e 0x4f>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* Port 0, Camera 0 */ + port@0 { + reg = <0>; + + ub960_fpd3_1_in: endpoint { + remote-endpoint = <&ub953_1_out>; + }; + }; + + /* Port 1, Camera 1 */ + port@1 { + reg = <1>; + + ub960_fpd3_2_in: endpoint { + remote-endpoint = <&ub913_2_out>; + hsync-active = <0>; + vsync-active = <1>; + }; + }; + + /* Port 2, unconnected */ + port@2 { + reg = <2>; + }; + + /* Port 3, unconnected */ + port@3 { + reg = <3>; + }; + + /* Port 4, CSI-2 TX */ + port@4 { + reg = <4>; + ds90ub960_0_csi_out: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <800000000>; + remote-endpoint = <&csi2_phy0>; + }; + }; + + /* Port 5, unconnected */ + port@5 { + reg = <5>; + }; + }; + + links { + #address-cells = <1>; + #size-cells = <0>; + + /* Link 0 has DS90UB953 serializer and IMX274 sensor */ + + link@0 { + reg = <0>; + i2c-alias = <0x44>; + + ti,rx-mode = <3>; + + serializer1: serializer { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_1_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&sensor_1_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_1_out: endpoint { + remote-endpoint = <&ub960_fpd3_1_in>; + }; + }; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx274"; + reg = <0x1a>; + + reset-gpios = <&serializer1 0 GPIO_ACTIVE_LOW>; + + port { + sensor_1_out: endpoint { + remote-endpoint = <&ub953_1_in>; + }; + }; + }; + }; + }; + }; /* End of link@0 */ + + /* Link 1 has DS90UB913 serializer and MT9V111 sensor */ + + link@1 { + reg = <1>; + i2c-alias = <0x45>; + + ti,rx-mode = <0>; + + serializer2: serializer { + compatible = "ti,ds90ub913a-q1"; + + gpio-controller; + #gpio-cells = <2>; + + clocks = <&clk_cam_48M>; + clock-names = "clkin"; + + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub913_2_in: endpoint { + remote-endpoint = <&sensor_2_out>; + pclk-sample = <1>; + }; + }; + + port@1 { + reg = <1>; + + ub913_2_out: endpoint { + remote-endpoint = <&ub960_fpd3_2_in>; + }; + }; + }; + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@48 { + compatible = "aptina,mt9v111"; + reg = <0x48>; + + clocks = <&serializer2>; + + port { + sensor_2_out: endpoint { + remote-endpoint = <&ub913_2_in>; + }; + }; + }; + }; + }; + }; /* End of link@1 */ + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/mediatek,vcodec-decoder.yaml b/Documentation/devicetree/bindings/media/mediatek,vcodec-decoder.yaml index fad59b486d5d..b401c67e3ba0 100644 --- a/Documentation/devicetree/bindings/media/mediatek,vcodec-decoder.yaml +++ b/Documentation/devicetree/bindings/media/mediatek,vcodec-decoder.yaml @@ -21,24 +21,33 @@ properties: - mediatek,mt8183-vcodec-dec reg: - maxItems: 12 + minItems: 11 + maxItems: 11 + + reg-names: + items: + - const: misc + - const: ld + - const: top + - const: cm + - const: ad + - const: av + - const: pp + - const: hwd + - const: hwq + - const: hwb + - const: hwg interrupts: maxItems: 1 clocks: + minItems: 1 maxItems: 8 clock-names: - items: - - const: vcodecpll - - const: univpll_d2 - - const: clk_cci400_sel - - const: vdec_sel - - const: vdecpll - - const: vencpll - - const: venc_lt_sel - - const: vdec_bus_clk_src + minItems: 1 + maxItems: 8 assigned-clocks: true @@ -66,6 +75,10 @@ properties: description: Describes point to scp. + mediatek,vdecsys: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the vdecsys syscon node. + required: - compatible - reg @@ -73,8 +86,7 @@ required: - clocks - clock-names - iommus - - assigned-clocks - - assigned-clock-parents + - mediatek,vdecsys allOf: - if: @@ -88,6 +100,15 @@ allOf: required: - mediatek,scp + properties: + clocks: + minItems: 1 + maxItems: 1 + + clock-names: + items: + - const: vdec + - if: properties: compatible: @@ -99,6 +120,22 @@ allOf: required: - mediatek,vpu + properties: + clocks: + minItems: 8 + maxItems: 8 + + clock-names: + items: + - const: vcodecpll + - const: univpll_d2 + - const: clk_cci400_sel + - const: vdec_sel + - const: vdecpll + - const: vencpll + - const: venc_lt_sel + - const: vdec_bus_clk_src + additionalProperties: false examples: @@ -109,10 +146,9 @@ examples: #include #include - vcodec_dec: vcodec@16000000 { + vcodec_dec: vcodec@16020000 { compatible = "mediatek,mt8173-vcodec-dec"; - reg = <0x16000000 0x100>, /*VDEC_SYS*/ - <0x16020000 0x1000>, /*VDEC_MISC*/ + reg = <0x16020000 0x1000>, /*VDEC_MISC*/ <0x16021000 0x800>, /*VDEC_LD*/ <0x16021800 0x800>, /*VDEC_TOP*/ <0x16022000 0x1000>, /*VDEC_CM*/ @@ -133,6 +169,7 @@ examples: <&iommu M4U_PORT_HW_VDEC_VLD_EXT>, <&iommu M4U_PORT_HW_VDEC_VLD2_EXT>; mediatek,vpu = <&vpu>; + mediatek,vdecsys = <&vdecsys>; power-domains = <&scpsys MT8173_POWER_DOMAIN_VDEC>; clocks = <&apmixedsys CLK_APMIXED_VCODECPLL>, <&topckgen CLK_TOP_UNIVPLL_D2>, diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml index 6038b9b5ab36..e4665469a86c 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml @@ -21,6 +21,7 @@ properties: enum: - fsl,imx8mn-isi - fsl,imx8mp-isi + - fsl,imx93-isi reg: maxItems: 1 @@ -72,7 +73,9 @@ allOf: properties: compatible: contains: - const: fsl,imx8mn-isi + enum: + - fsl,imx8mn-isi + - fsl,imx93-isi then: properties: interrupts: diff --git a/Documentation/driver-api/media/cec-core.rst b/Documentation/driver-api/media/cec-core.rst index ae0d20798edc..f1ffdec388f3 100644 --- a/Documentation/driver-api/media/cec-core.rst +++ b/Documentation/driver-api/media/cec-core.rst @@ -109,9 +109,11 @@ your driver: int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable); int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); - void (*adap_configured)(struct cec_adapter *adap, bool configured); + void (*adap_unconfigured)(struct cec_adapter *adap); int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg); + void (*adap_nb_transmit_canceled)(struct cec_adapter *adap, + const struct cec_msg *msg); void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); void (*adap_free)(struct cec_adapter *adap); @@ -122,8 +124,8 @@ your driver: ... }; -The seven low-level ops deal with various aspects of controlling the CEC adapter -hardware: +These low-level ops deal with various aspects of controlling the CEC adapter +hardware. They are all called with the mutex adap->lock held. To enable/disable the hardware:: @@ -179,14 +181,12 @@ can receive directed messages to that address. Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID. -Called when the adapter is fully configured or unconfigured:: +Called when the adapter is unconfigured:: - void (*adap_configured)(struct cec_adapter *adap, bool configured); + void (*adap_unconfigured)(struct cec_adapter *adap); -If configured == true, then the adapter is fully configured, i.e. all logical -addresses have been successfully claimed. If configured == false, then the -adapter is unconfigured. If the driver has to take specific actions after -(un)configuration, then that can be done through this optional callback. +The adapter is unconfigured. If the driver has to take specific actions after +unconfiguration, then that can be done through this optional callback. To transmit a new message:: @@ -207,6 +207,19 @@ The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to microseconds (one data bit period is 2.4 ms). +To pass on the result of a canceled non-blocking transmit:: + + void (*adap_nb_transmit_canceled)(struct cec_adapter *adap, + const struct cec_msg *msg); + +This optional callback can be used to obtain the result of a canceled +non-blocking transmit with sequence number msg->sequence. This is +called if the transmit was aborted, the transmit timed out (i.e. the +hardware never signaled that the transmit finished), or the transmit +was successful, but the wait for the expected reply was either aborted +or it timed out. + + To log the current CEC hardware status:: void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); @@ -372,7 +385,8 @@ Implementing the High-Level CEC Adapter --------------------------------------- The low-level operations drive the hardware, the high-level operations are -CEC protocol driven. The following high-level callbacks are available: +CEC protocol driven. The high-level callbacks are called without the adap->lock +mutex being held. The following high-level callbacks are available: .. code-block:: none @@ -384,9 +398,19 @@ CEC protocol driven. The following high-level callbacks are available: ... /* High-level CEC message callback */ + void (*configured)(struct cec_adapter *adap); int (*received)(struct cec_adapter *adap, struct cec_msg *msg); }; +Called when the adapter is configured:: + + void (*configured)(struct cec_adapter *adap); + +The adapter is fully configured, i.e. all logical addresses have been +successfully claimed. If the driver has to take specific actions after +configuration, then that can be done through this optional callback. + + The received() callback allows the driver to optionally handle a newly received CEC message:: diff --git a/Documentation/driver-api/media/v4l2-cci.rst b/Documentation/driver-api/media/v4l2-cci.rst new file mode 100644 index 000000000000..dd297a40ed20 --- /dev/null +++ b/Documentation/driver-api/media/v4l2-cci.rst @@ -0,0 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + +V4L2 CCI kAPI +^^^^^^^^^^^^^ +.. kernel-doc:: include/media/v4l2-cci.h diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst index 1a8c4a5f256b..239045ecc8f4 100644 --- a/Documentation/driver-api/media/v4l2-core.rst +++ b/Documentation/driver-api/media/v4l2-core.rst @@ -22,6 +22,7 @@ Video4Linux devices v4l2-mem2mem v4l2-async v4l2-fwnode + v4l2-cci v4l2-rect v4l2-tuner v4l2-common diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index 602dadaa81d8..e56b50b3f203 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -157,6 +157,9 @@ below. Using one or the other registration method only affects the probing process, the run-time bridge-subdevice interaction is in both cases the same. +Registering synchronous sub-devices +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + In the **synchronous** case a device (bridge) driver needs to register the :c:type:`v4l2_subdev` with the v4l2_device: @@ -175,10 +178,12 @@ You can unregister a sub-device using: :c:func:`v4l2_device_unregister_subdev ` (:c:type:`sd `). - Afterwards the subdev module can be unloaded and :c:type:`sd `->dev == ``NULL``. +Registering asynchronous sub-devices +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + In the **asynchronous** case subdevice probing can be invoked independently of the bridge driver availability. The subdevice driver then has to verify whether all the requirements for a successful probing are satisfied. This can include a @@ -190,64 +195,89 @@ performed using the :c:func:`v4l2_async_unregister_subdev` call. Subdevices registered this way are stored in a global list of subdevices, ready to be picked up by bridge drivers. -Bridge drivers in turn have to register a notifier object. This is -performed using the :c:func:`v4l2_async_nf_register` call. To -unregister the notifier the driver has to call -:c:func:`v4l2_async_nf_unregister`. The former of the two functions -takes two arguments: a pointer to struct :c:type:`v4l2_device` and a -pointer to struct :c:type:`v4l2_async_notifier`. +Asynchronous sub-device notifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Bridge drivers in turn have to register a notifier object. This is performed +using the :c:func:`v4l2_async_nf_register` call. To unregister the notifier the +driver has to call :c:func:`v4l2_async_nf_unregister`. Before releasing memory +of an unregister notifier, it must be cleaned up by calling +:c:func:`v4l2_async_nf_cleanup`. Before registering the notifier, bridge drivers must do two things: first, the -notifier must be initialized using the :c:func:`v4l2_async_nf_init`. -Second, bridge drivers can then begin to form a list of subdevice descriptors -that the bridge device needs for its operation. Several functions are available -to add subdevice descriptors to a notifier, depending on the type of device and -the needs of the driver. +notifier must be initialized using the :c:func:`v4l2_async_nf_init`. Second, +bridge drivers can then begin to form a list of async connection descriptors +that the bridge device needs for its +operation. :c:func:`v4l2_async_nf_add_fwnode`, +:c:func:`v4l2_async_nf_add_fwnode_remote` and :c:func:`v4l2_async_nf_add_i2c` -:c:func:`v4l2_async_nf_add_fwnode_remote` and -:c:func:`v4l2_async_nf_add_i2c` are for bridge and ISP drivers for -registering their async sub-devices with the notifier. +Async connection descriptors describe connections to external sub-devices the +drivers for which are not yet probed. Based on an async connection, a media data +or ancillary link may be created when the related sub-device becomes +available. There may be one or more async connections to a given sub-device but +this is not known at the time of adding the connections to the notifier. Async +connections are bound as matching async sub-devices are found, one by one. -:c:func:`v4l2_async_register_subdev_sensor` is a helper function for -sensor drivers registering their own async sub-device, but it also registers a -notifier and further registers async sub-devices for lens and flash devices -found in firmware. The notifier for the sub-device is unregistered with the -async sub-device. +Asynchronous sub-device notifier for sub-devices +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These functions allocate an async sub-device descriptor which is of type struct -:c:type:`v4l2_async_subdev` embedded in a driver-specific struct. The &struct -:c:type:`v4l2_async_subdev` shall be the first member of this struct: +A driver that registers an asynchronous sub-device may also register an +asynchronous notifier. This is called an asynchronous sub-device notifier andthe +process is similar to that of a bridge driver apart from that the notifier is +initialised using :c:func:`v4l2_async_subdev_nf_init` instead. A sub-device +notifier may complete only after the V4L2 device becomes available, i.e. there's +a path via async sub-devices and notifiers to a notifier that is not an +asynchronous sub-device notifier. + +Asynchronous sub-device registration helper for camera sensor drivers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:c:func:`v4l2_async_register_subdev_sensor` is a helper function for sensor +drivers registering their own async connection, but it also registers a notifier +and further registers async connections for lens and flash devices found in +firmware. The notifier for the sub-device is unregistered and cleaned up with +the async sub-device, using :c:func:`v4l2_async_unregister_subdev`. + +Asynchronous sub-device notifier example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These functions allocate an async connection descriptor which is of type struct +:c:type:`v4l2_async_connection` embedded in a driver-specific struct. The &struct +:c:type:`v4l2_async_connection` shall be the first member of this struct: .. code-block:: c - struct my_async_subdev { - struct v4l2_async_subdev asd; + struct my_async_connection { + struct v4l2_async_connection asc; ... }; - struct my_async_subdev *my_asd; + struct my_async_connection *my_asc; struct fwnode_handle *ep; ... - my_asd = v4l2_async_nf_add_fwnode_remote(¬ifier, ep, - struct my_async_subdev); + my_asc = v4l2_async_nf_add_fwnode_remote(¬ifier, ep, + struct my_async_connection); fwnode_handle_put(ep); - if (IS_ERR(asd)) - return PTR_ERR(asd); + if (IS_ERR(my_asc)) + return PTR_ERR(my_asc); -The V4L2 core will then use these descriptors to match asynchronously -registered subdevices to them. If a match is detected the ``.bound()`` -notifier callback is called. After all subdevices have been located the -.complete() callback is called. When a subdevice is removed from the -system the .unbind() method is called. All three callbacks are optional. +Asynchronous sub-device notifier callbacks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The V4L2 core will then use these connection descriptors to match asynchronously +registered subdevices to them. If a match is detected the ``.bound()`` notifier +callback is called. After all connections have been bound the .complete() +callback is called. When a connection is removed from the system the +``.unbind()`` method is called. All three callbacks are optional. Drivers can store any type of custom data in their driver-specific -:c:type:`v4l2_async_subdev` wrapper. If any of that data requires special +:c:type:`v4l2_async_connection` wrapper. If any of that data requires special handling when the structure is freed, drivers must implement the ``.destroy()`` notifier callback. The framework will call it right before freeing the -:c:type:`v4l2_async_subdev`. +:c:type:`v4l2_async_connection`. Calling subdev operations ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/hid/hidintro.rst b/Documentation/hid/hidintro.rst new file mode 100644 index 000000000000..73523e315ebd --- /dev/null +++ b/Documentation/hid/hidintro.rst @@ -0,0 +1,524 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +Introduction to HID report descriptors +====================================== + +This chapter is meant to give a broad overview of what HID report +descriptors are, and of how a casual (non-kernel) programmer can deal +with HID devices that are not working well with Linux. + +.. contents:: + :local: + :depth: 2 + +.. toctree:: + :maxdepth: 2 + + hidreport-parsing + + +Introduction +============ + +HID stands for Human Interface Device, and can be whatever device you +are using to interact with a computer, be it a mouse, a touchpad, a +tablet, a microphone. + +Many HID devices work out the box, even if their hardware is different. +For example, mice can have any number of buttons; they may have a +wheel; movement sensitivity differs between different models, and so +on. Nonetheless, most of the time everything just works, without the +need to have specialized code in the kernel for every mouse model +developed since 1970. + +This is because modern HID devices do advertise their capabilities +through the *HID report descriptor*, a fixed set of bytes describing +exactly what *HID reports* may be sent between the device and the host +and the meaning of each individual bit in those reports. For example, +a HID Report Descriptor may specify that "in a report with ID 3 the +bits from 8 to 15 is the delta x coordinate of a mouse". + +The HID report itself then merely carries the actual data values +without any extra meta information. Note that HID reports may be sent +from the device ("Input Reports", i.e. input events), to the device +("Output Reports" to e.g. change LEDs) or used for device configuration +("Feature reports"). A device may support one or more HID reports. + +The HID subsystem is in charge of parsing the HID report descriptors, +and converts HID events into normal input device interfaces (see +Documentation/hid/hid-transport.rst). Devices may misbehave because the +HID report descriptor provided by the device is wrong, or because it +needs to be dealt with in a special way, or because some special +device or interaction mode is not handled by the default code. + +The format of HID report descriptors is described by two documents, +available from the `USB Implementers Forum `_ +`HID web page `_ address: + + * the `HID USB Device Class Definition + `_ (HID Spec from now on) + * the `HID Usage Tables `_ (HUT from now on) + +The HID subsystem can deal with different transport drivers +(USB, I2C, Bluetooth, etc.). See Documentation/hid/hid-transport.rst. + +Parsing HID report descriptors +============================== + +The current list of HID devices can be found at ``/sys/bus/hid/devices/``. +For each device, say ``/sys/bus/hid/devices/0003\:093A\:2510.0002/``, +one can read the corresponding report descriptor:: + + $ hexdump -C /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor + 00000000 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 |..............).| + 00000010 15 00 25 01 75 01 95 03 81 02 75 05 95 01 81 01 |..%.u.....u.....| + 00000020 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 |...0.1.8..%.u...| + 00000030 81 06 c0 c0 |....| + 00000034 + +Optional: the HID report descriptor can be read also by +directly accessing the hidraw driver [#hidraw]_. + +The basic structure of HID report descriptors is defined in the HID +spec, while HUT "defines constants that can be interpreted by an +application to identify the purpose and meaning of a data field in a +HID report". Each entry is defined by at least two bytes, where the +first one defines what type of value is following and is described in +the HID spec, while the second one carries the actual value and is +described in the HUT. + +HID report descriptors can, in principle, be painstakingly parsed by +hand, byte by byte. + +A short introduction on how to do this is sketched in +Documentation/hid/hidreport-parsing.rst; you only need to understand it +if you need to patch HID report descriptors. + +In practice you should not parse HID report descriptors by hand; rather, +you should use an existing parser. Among all the available ones + + * the online `USB Descriptor and Request Parser + `_; + * `hidrdd `_, + that provides very detailed and somewhat verbose descriptions + (verbosity can be useful if you are not familiar with HID report + descriptors); + * `hid-tools `_, + a complete utility set that allows, among other things, + to record and replay the raw HID reports and to debug + and replay HID devices. + It is being actively developed by the Linux HID subsystem maintainers. + +Parsing the mouse HID report descriptor with `hid-tools +`_ leads to +(explanations interposed):: + + $ ./hid-decode /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor + # device 0:0 + # 0x05, 0x01, // Usage Page (Generic Desktop) 0 + # 0x09, 0x02, // Usage (Mouse) 2 + # 0xa1, 0x01, // Collection (Application) 4 + # 0x09, 0x01, // Usage (Pointer) 6 + # 0xa1, 0x00, // Collection (Physical) 8 + # 0x05, 0x09, // Usage Page (Button) 10 + +what follows is a button :: + + # 0x19, 0x01, // Usage Minimum (1) 12 + # 0x29, 0x03, // Usage Maximum (3) 14 + +first button is button number 1, last button is button number 3 :: + + # 0x15, 0x00, // Logical Minimum (0) 16 + # 0x25, 0x01, // Logical Maximum (1) 18 + +each button can send values from 0 up to including 1 +(i.e. they are binary buttons) :: + + # 0x75, 0x01, // Report Size (1) 20 + +each button is sent as exactly one bit :: + + # 0x95, 0x03, // Report Count (3) 22 + +and there are three of those bits (matching the three buttons) :: + + # 0x81, 0x02, // Input (Data,Var,Abs) 24 + +it's actual Data (not constant padding), they represent +a single variable (Var) and their values are Absolute (not relative); +See HID spec Sec. 6.2.2.5 "Input, Output, and Feature Items" :: + + # 0x75, 0x05, // Report Size (5) 26 + +five additional padding bits, needed to reach a byte :: + + # 0x95, 0x01, // Report Count (1) 28 + +those five bits are repeated only once :: + + # 0x81, 0x01, // Input (Cnst,Arr,Abs) 30 + +and take Constant (Cnst) values i.e. they can be ignored. :: + + # 0x05, 0x01, // Usage Page (Generic Desktop) 32 + # 0x09, 0x30, // Usage (X) 34 + # 0x09, 0x31, // Usage (Y) 36 + # 0x09, 0x38, // Usage (Wheel) 38 + +The mouse has also two physical positions (Usage (X), Usage (Y)) +and a wheel (Usage (Wheel)) :: + + # 0x15, 0x81, // Logical Minimum (-127) 40 + # 0x25, 0x7f, // Logical Maximum (127) 42 + +each of them can send values ranging from -127 up to including 127 :: + + # 0x75, 0x08, // Report Size (8) 44 + +which is represented by eight bits :: + + # 0x95, 0x03, // Report Count (3) 46 + +and there are three of those eight bits, matching X, Y and Wheel. :: + + # 0x81, 0x06, // Input (Data,Var,Rel) 48 + +This time the data values are Relative (Rel), i.e. they represent +the change from the previously sent report (event) :: + + # 0xc0, // End Collection 50 + # 0xc0, // End Collection 51 + # + R: 52 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 75 05 95 01 81 01 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 c0 c0 + N: device 0:0 + I: 3 0001 0001 + + +This Report Descriptor tells us that the mouse input will be +transmitted using four bytes: the first one for the buttons (three +bits used, five for padding), the last three for the mouse X, Y and +wheel changes, respectively. + +Indeed, for any event, the mouse will send a *report* of four bytes. +We can check the values sent by resorting e.g. to the `hid-recorder` +tool, from `hid-tools `_: +The sequence of bytes sent by clicking and releasing button 1, then button 2, then button 3 is:: + + $ sudo ./hid-recorder /dev/hidraw1 + + .... + output of hid-decode + .... + + # Button: 1 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000000.000000 4 01 00 00 00 + # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000000.183949 4 00 00 00 00 + # Button: 0 1 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000001.959698 4 02 00 00 00 + # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000002.103899 4 00 00 00 00 + # Button: 0 0 1 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000004.855799 4 04 00 00 00 + # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000005.103864 4 00 00 00 00 + +This example shows that when button 2 is clicked, +the bytes ``02 00 00 00`` are sent, and the immediately subsequent +event (``00 00 00 00``) is the release of button 2 (no buttons are +pressed, remember that the data values are *absolute*). + +If instead one clicks and holds button 1, then clicks and holds button +2, releases button 1, and finally releases button 2, the reports are:: + + # Button: 1 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000044.175830 4 01 00 00 00 + # Button: 1 1 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000045.975997 4 03 00 00 00 + # Button: 0 1 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000047.407930 4 02 00 00 00 + # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0 + E: 000049.199919 4 00 00 00 00 + +where with ``03 00 00 00`` both buttons are pressed, and with the +subsequent ``02 00 00 00`` button 1 is released while button 2 is still +active. + +Output, Input and Feature Reports +--------------------------------- + +HID devices can have Input Reports, like in the mouse example, Output +Reports, and Feature Reports. "Output" means that the information is +sent to the device. For example, a joystick with force feedback will +have some output; the led of a keyboard would need an output as well. +"Input" means that data come from the device. + +"Feature"s are not meant to be consumed by the end user and define +configuration options for the device. They can be queried from the host; +when declared as *Volatile* they should be changed by the host. + + +Collections, Report IDs and Evdev events +======================================== + +A single device can logically group data into different independent +sets, called a *Collection*. Collections can be nested and there are +different types of collections (see the HID spec 6.2.2.6 +"Collection, End Collection Items" for details). + +Different reports are identified by means of different *Report ID* +fields, i.e. a number identifying the structure of the immediately +following report. +Whenever a Report ID is needed it is transmitted as the first byte of +any report. A device with only one supported HID report (like the mouse +example above) may omit the report ID. + +Consider the following HID report descriptor:: + + 05 01 09 02 A1 01 85 01 05 09 19 01 29 05 15 00 + 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01 + 09 30 09 31 16 00 F8 26 FF 07 75 0C 95 02 81 06 + 09 38 15 80 25 7F 75 08 95 01 81 06 05 0C 0A 38 + 02 15 80 25 7F 75 08 95 01 81 06 C0 05 01 09 02 + A1 01 85 02 05 09 19 01 29 05 15 00 25 01 95 05 + 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31 + 16 00 F8 26 FF 07 75 0C 95 02 81 06 09 38 15 80 + 25 7F 75 08 95 01 81 06 05 0C 0A 38 02 15 80 25 + 7F 75 08 95 01 81 06 C0 05 01 09 07 A1 01 85 05 + 05 07 15 00 25 01 09 29 09 3E 09 4B 09 4E 09 E3 + 09 E8 09 E8 09 E8 75 01 95 08 81 02 95 00 81 01 + C0 05 0C 09 01 A1 01 85 06 15 00 25 01 75 01 95 + 01 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81 + 06 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81 + 06 C0 05 0C 09 01 A1 01 85 03 09 05 15 00 26 FF + 00 75 08 95 02 B1 02 C0 + +After parsing it (try to parse it on your own using the suggested +tools!) one can see that the device presents two ``Mouse`` Application +Collections (with reports identified by Reports IDs 1 and 2, +respectively), a ``Keypad`` Application Collection (whose report is +identified by the Report ID 5) and two ``Consumer Controls`` Application +Collections, (with Report IDs 6 and 3, respectively). Note, however, +that a device can have different Report IDs for the same Application +Collection. + +The data sent will begin with the Report ID byte, and will be followed +by the corresponding information. For example, the data transmitted for +the last consumer control:: + + 0x05, 0x0C, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Control) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x03, // Report ID (3) + 0x09, 0x05, // Usage (Headphone) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + +will be of three bytes: the first for the Report ID (3), the next two +for the headphone, with two (``Report Count (2)``) bytes +(``Report Size (8)``), each ranging from 0 (``Logical Minimum (0)``) +to 255 (``Logical Maximum (255)``). + +All the Input data sent by the device should be translated into +corresponding Evdev events, so that the remaining part of the stack can +know what is going on, e.g. the bit for the first button translates into +the ``EV_KEY/BTN_LEFT`` evdev event and relative X movement translates +into the ``EV_REL/REL_X`` evdev event". + +Events +====== + +In Linux, one ``/dev/input/event*`` is created for each ``Application +Collection``. Going back to the mouse example, and repeating the +sequence where one clicks and holds button 1, then clicks and holds +button 2, releases button 1, and finally releases button 2, one gets:: + + $ sudo libinput record /dev/input/event1 + # libinput record + version: 1 + ndevices: 1 + libinput: + version: "1.23.0" + git: "unknown" + system: + os: "opensuse-tumbleweed:20230619" + kernel: "6.3.7-1-default" + dmi: "dmi:bvnHP:bvrU77Ver.01.05.00:bd03/24/2022:br5.0:efr20.29:svnHP:pnHPEliteBook64514inchG9NotebookPC:pvr:rvnHP:rn89D2:rvrKBCVersion14.1D.00:cvnHP:ct10:cvr:sku5Y3J1EA#ABZ:" + devices: + - node: /dev/input/event1 + evdev: + # Name: PixArt HP USB Optical Mouse + # ID: bus 0x3 vendor 0x3f0 product 0x94a version 0x111 + # Supported Events: + # Event type 0 (EV_SYN) + # Event type 1 (EV_KEY) + # Event code 272 (BTN_LEFT) + # Event code 273 (BTN_RIGHT) + # Event code 274 (BTN_MIDDLE) + # Event type 2 (EV_REL) + # Event code 0 (REL_X) + # Event code 1 (REL_Y) + # Event code 8 (REL_WHEEL) + # Event code 11 (REL_WHEEL_HI_RES) + # Event type 4 (EV_MSC) + # Event code 4 (MSC_SCAN) + # Properties: + name: "PixArt HP USB Optical Mouse" + id: [3, 1008, 2378, 273] + codes: + 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # EV_SYN + 1: [272, 273, 274] # EV_KEY + 2: [0, 1, 8, 11] # EV_REL + 4: [4] # EV_MSC + properties: [] + hid: [ + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, + 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 0x81, 0x06, 0xc0, 0xc0 + ] + udev: + properties: + - ID_INPUT=1 + - ID_INPUT_MOUSE=1 + - LIBINPUT_DEVICE_GROUP=3/3f0/94a:usb-0000:05:00.3-2 + quirks: + events: + # Current time is 12:31:56 + - evdev: + - [ 0, 0, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated) + - [ 0, 0, 1, 272, 1] # EV_KEY / BTN_LEFT 1 + - [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms + - evdev: + - [ 1, 207892, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated) + - [ 1, 207892, 1, 273, 1] # EV_KEY / BTN_RIGHT 1 + - [ 1, 207892, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +1207ms + - evdev: + - [ 2, 367823, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated) + - [ 2, 367823, 1, 272, 0] # EV_KEY / BTN_LEFT 0 + - [ 2, 367823, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +1160ms + # Current time is 12:32:00 + - evdev: + - [ 3, 247617, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated) + - [ 3, 247617, 1, 273, 0] # EV_KEY / BTN_RIGHT 0 + - [ 3, 247617, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +880ms + +Note: if ``libinput record`` is not available on your system try using +``evemu-record``. + +When something does not work +============================ + +There can be a number of reasons why a device does not behave +correctly. For example + +* The HID report descriptor provided by the HID device may be wrong + because e.g. + + * it does not follow the standard, so that the kernel + will not able to make sense of the HID report descriptor; + * the HID report descriptor *does not match* what is actually + sent by the device (this can be verified by reading the raw HID + data); +* the HID report descriptor may need some "quirks" (see later on). + +As a consequence, a ``/dev/input/event*`` may not be created +for each Application Collection, and/or the events +there may not match what you would expect. + + +Quirks +------ + +There are some known peculiarities of HID devices that the kernel +knows how to fix - these are called the HID quirks and a list of those +is available in `include/linux/hid.h`. + +Should this be the case, it should be enough to add the required quirk +in the kernel, for the HID device at hand. This can be done in the file +`drivers/hid/hid-quirks.c`. How to do it should be relatively +straightforward after looking into the file. + +The list of currently defined quirks, from `include/linux/hid.h`, is + +.. kernel-doc:: include/linux/hid.h + :doc: HID quirks + +Quirks for USB devices can be specified while loading the usbhid module, +see ``modinfo usbhid``, although the proper fix should go into +hid-quirks.c and **be submitted upstream**. +See Documentation/process/submitting-patches.rst for guidelines on how +to submit a patch. Quirks for other busses need to go into hid-quirks.c. + +Fixing HID report descriptors +----------------------------- + +Should you need to patch HID report descriptors the easiest way is to +resort to eBPF, as described in Documentation/hid/hid-bpf.rst. + +Basically, you can change any byte of the original HID report +descriptor. The examples in samples/hid should be a good starting point +for your code, see e.g. `samples/hid/hid_mouse.bpf.c`:: + + SEC("fmod_ret/hid_bpf_rdesc_fixup") + int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx) + { + .... + data[39] = 0x31; + data[41] = 0x30; + return 0; + } + +Of course this can be also done within the kernel source code, see e.g. +`drivers/hid/hid-aureal.c` or `drivers/hid/hid-samsung.c` for a slightly +more complex file. + +Check Documentation/hid/hidreport-parsing.rst if you need any help +navigating the HID manuals and understanding the exact meaning of +the HID report descriptor hex numbers. + +Whatever solution you come up with, please remember to **submit the +fix to the HID maintainers**, so that it can be directly integrated in +the kernel and that particular HID device will start working for +everyone else. See Documentation/process/submitting-patches.rst for +guidelines on how to do this. + + +Modifying the transmitted data on the fly +----------------------------------------- + +Using eBPF it is also possible to modify the data exchanged with the +device. See again the examples in `samples/hid`. + +Again, **please post your fix**, so that it can be integrated in the +kernel! + +Writing a specialized driver +---------------------------- + +This should really be your last resort. + + +.. rubric:: Footnotes + +.. [#hidraw] read hidraw: see Documentation/hid/hidraw.rst and + file `samples/hidraw/hid-example.c` for an example. + The output of ``hid-example`` would be, for the same mouse:: + + $ sudo ./hid-example + Report Descriptor Size: 52 + Report Descriptor: + 5 1 9 2 a1 1 9 1 a1 0 5 9 19 1 29 3 15 0 25 1 75 1 95 3 81 2 75 5 95 1 81 1 5 1 9 30 9 31 9 38 15 81 25 7f 75 8 95 3 81 6 c0 c0 + + Raw Name: PixArt USB Optical Mouse + Raw Phys: usb-0000:05:00.4-2.3/input0 + Raw Info: + bustype: 3 (USB) + vendor: 0x093a + product: 0x2510 + ... diff --git a/Documentation/hid/hidreport-parsing.rst b/Documentation/hid/hidreport-parsing.rst new file mode 100644 index 000000000000..1d3c17f29f2b --- /dev/null +++ b/Documentation/hid/hidreport-parsing.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================== +Manual parsing of HID report descriptors +======================================== + +Consider again the mouse HID report descriptor +introduced in Documentation/hid/hidintro.rst:: + + $ hexdump -C /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor + 00000000 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 |..............).| + 00000010 15 00 25 01 75 01 95 03 81 02 75 05 95 01 81 01 |..%.u.....u.....| + 00000020 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 |...0.1.8..%.u...| + 00000030 81 06 c0 c0 |....| + 00000034 + +and try to parse it by hand. + +Start with the first number, 0x05: it carries 2 bits for the +length of the item, 2 bits for the type of the item and 4 bits for the +function:: + + +----------+ + | 00000101 | + +----------+ + ^^ + ---- Length of data (see HID spec 6.2.2.2) + ^^ + ------ Type of the item (see HID spec 6.2.2.2, then jump to 6.2.2.7) + ^^^^ + --------- Function of the item (see HID spec 6.2.2.7, then HUT Sec 3) + +In our case, the length is 1 byte, the type is ``Global`` and the +function is ``Usage Page``, thus for parsing the value 0x01 in the second byte +we need to refer to HUT Sec 3. + +The second number is the actual data, and its meaning can be found in +the HUT. We have a ``Usage Page``, thus we need to refer to HUT +Sec. 3, "Usage Pages"; from there, one sees that ``0x01`` stands for +``Generic Desktop Page``. + +Moving now to the second two bytes, and following the same scheme, +``0x09`` (i.e. ``00001001``) will be followed by one byte (``01``) +and is a ``Local`` item (``10``). Thus, the meaning of the remaining four bits +(``0000``) is given in the HID spec Sec. 6.2.2.8 "Local Items", so that +we have a ``Usage``. From HUT, Sec. 4, "Generic Desktop Page", we see that +0x02 stands for ``Mouse``. + +The following numbers can be parsed in the same way. diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst index b2028f382f11..af02cf7cfa82 100644 --- a/Documentation/hid/index.rst +++ b/Documentation/hid/index.rst @@ -7,6 +7,7 @@ Human Interface Devices (HID) .. toctree:: :maxdepth: 1 + hidintro hiddev hidraw hid-sensor diff --git a/Documentation/i2c/i2c-address-translators.rst b/Documentation/i2c/i2c-address-translators.rst new file mode 100644 index 000000000000..b22ce9f41ecf --- /dev/null +++ b/Documentation/i2c/i2c-address-translators.rst @@ -0,0 +1,96 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================= +I2C Address Translators +======================= + +Author: Luca Ceresoli +Author: Tomi Valkeinen + +Description +----------- + +An I2C Address Translator (ATR) is a device with an I2C slave parent +("upstream") port and N I2C master child ("downstream") ports, and +forwards transactions from upstream to the appropriate downstream port +with a modified slave address. The address used on the parent bus is +called the "alias" and is (potentially) different from the physical +slave address of the child bus. Address translation is done by the +hardware. + +An ATR looks similar to an i2c-mux except: + - the address on the parent and child busses can be different + - there is normally no need to select the child port; the alias used on the + parent bus implies it + +The ATR functionality can be provided by a chip with many other features. +The kernel i2c-atr provides a helper to implement an ATR within a driver. + +The ATR creates a new I2C "child" adapter on each child bus. Adding +devices on the child bus ends up in invoking the driver code to select +an available alias. Maintaining an appropriate pool of available aliases +and picking one for each new device is up to the driver implementer. The +ATR maintains a table of currently assigned alias and uses it to modify +all I2C transactions directed to devices on the child buses. + +A typical example follows. + +Topology:: + + Slave X @ 0x10 + .-----. | + .-----. | |---+---- B + | CPU |--A--| ATR | + `-----' | |---+---- C + `-----' | + Slave Y @ 0x10 + +Alias table: + +A, B and C are three physical I2C busses, electrically independent from +each other. The ATR receives the transactions initiated on bus A and +propagates them on bus B or bus C or none depending on the device address +in the transaction and based on the alias table. + +Alias table: + +.. table:: + + =============== ===== + Client Alias + =============== ===== + X (bus B, 0x10) 0x20 + Y (bus C, 0x10) 0x30 + =============== ===== + +Transaction: + + - Slave X driver requests a transaction (on adapter B), slave address 0x10 + - ATR driver finds slave X is on bus B and has alias 0x20, rewrites + messages with address 0x20, forwards to adapter A + - Physical I2C transaction on bus A, slave address 0x20 + - ATR chip detects transaction on address 0x20, finds it in table, + propagates transaction on bus B with address translated to 0x10, + keeps clock streched on bus A waiting for reply + - Slave X chip (on bus B) detects transaction at its own physical + address 0x10 and replies normally + - ATR chip stops clock stretching and forwards reply on bus A, + with address translated back to 0x20 + - ATR driver receives the reply, rewrites messages with address 0x10 + as they were initially + - Slave X driver gets back the msgs[], with reply and address 0x10 + +Usage: + + 1. In the driver (typically in the probe function) add an ATR by + calling i2c_atr_new() passing attach/detach callbacks + 2. When the attach callback is called pick an appropriate alias, + configure it in the chip and return the chosen alias in the + alias_id parameter + 3. When the detach callback is called, deconfigure the alias from + the chip and put the alias back in the pool for later usage + +I2C ATR functions and data structures +------------------------------------- + +.. kernel-doc:: include/linux/i2c-atr.h diff --git a/Documentation/i2c/index.rst b/Documentation/i2c/index.rst index 6270f1fd7d4e..2b213d4ce89c 100644 --- a/Documentation/i2c/index.rst +++ b/Documentation/i2c/index.rst @@ -18,6 +18,7 @@ Introduction i2c-topology muxes/i2c-mux-gpio i2c-sysfs + i2c-address-translators Writing device drivers ====================== diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst index 15f78e772384..759907c20e75 100644 --- a/Documentation/trace/events.rst +++ b/Documentation/trace/events.rst @@ -219,6 +219,20 @@ the function "security_prepare_creds" and less than the end of that function. The ".function" postfix can only be attached to values of size long, and can only be compared with "==" or "!=". +Cpumask fields or scalar fields that encode a CPU number can be filtered using +a user-provided cpumask in cpulist format. The format is as follows:: + + CPUS{$cpulist} + +Operators available to cpumask filtering are: + +& (intersection), ==, != + +For example, this will filter events that have their .target_cpu field present +in the given cpumask:: + + target_cpu & CPUS{17-42} + 5.2 Setting filters ------------------- diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst index 675bc2c3c6b8..ef8e8cf31f90 100644 --- a/Documentation/userspace-api/media/v4l/dev-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst @@ -277,7 +277,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``sizeimage`` adjusted size of ``OUTPUT`` buffers. @@ -311,7 +311,7 @@ Initialization ``memory`` follows standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` the actual number of buffers allocated. @@ -339,7 +339,7 @@ Initialization ``format`` follows standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` adjusted to the number of allocated buffers. @@ -410,7 +410,7 @@ Capture Setup ``type`` a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. - * **Return fields:** + * **Returned fields:** ``width``, ``height`` frame buffer resolution for the decoded frames. @@ -443,7 +443,7 @@ Capture Setup ``target`` set to ``V4L2_SEL_TGT_COMPOSE``. - * **Return fields:** + * **Returned fields:** ``r.left``, ``r.top``, ``r.width``, ``r.height`` the visible rectangle; it must fit within the frame buffer resolution @@ -552,7 +552,7 @@ Capture Setup frame is written; defaults to ``V4L2_SEL_TGT_COMPOSE_DEFAULT``; read-only on hardware without additional compose/scaling capabilities. - * **Return fields:** + * **Returned fields:** ``r.left``, ``r.top``, ``r.width``, ``r.height`` the visible rectangle; it must fit within the frame buffer resolution @@ -629,7 +629,7 @@ Capture Setup ``memory`` follows standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` actual number of buffers allocated. @@ -668,7 +668,7 @@ Capture Setup a format representing the maximum framebuffer resolution to be accommodated by newly allocated buffers. - * **Return fields:** + * **Returned fields:** ``count`` adjusted to the number of allocated buffers. diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst index aa338b9624b0..6c523c69bdce 100644 --- a/Documentation/userspace-api/media/v4l/dev-encoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst @@ -115,8 +115,8 @@ Querying Capabilities 4. The client may use :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` to detect supported frame intervals for a given format and resolution, passing the desired pixel - format in :c:type:`v4l2_frmsizeenum` ``pixel_format`` and the resolution - in :c:type:`v4l2_frmsizeenum` ``width`` and :c:type:`v4l2_frmsizeenum` + format in :c:type:`v4l2_frmivalenum` ``pixel_format`` and the resolution + in :c:type:`v4l2_frmivalenum` ``width`` and :c:type:`v4l2_frmivalenum` ``height``. * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a coded pixel @@ -163,7 +163,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``sizeimage`` adjusted size of ``CAPTURE`` buffers. @@ -189,7 +189,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``pixelformat`` raw format supported for the coded format currently selected on @@ -215,7 +215,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``width``, ``height`` may be adjusted to match encoder minimums, maximums and alignment @@ -233,7 +233,7 @@ Initialization :c:func:`VIDIOC_S_PARM`. This also sets the coded frame interval on the ``CAPTURE`` queue to the same value. - * ** Required fields:** + * **Required fields:** ``type`` a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. @@ -245,7 +245,7 @@ Initialization the desired frame interval; the encoder may adjust it to match hardware requirements. - * **Return fields:** + * **Returned fields:** ``parm.output.timeperframe`` the adjusted frame interval. @@ -284,7 +284,7 @@ Initialization the case for off-line encoding. Support for this feature is signalled by the :ref:`V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL ` format flag. - * ** Required fields:** + * **Required fields:** ``type`` a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. @@ -296,7 +296,7 @@ Initialization the desired coded frame interval; the encoder may adjust it to match hardware requirements. - * **Return fields:** + * **Returned fields:** ``parm.capture.timeperframe`` the adjusted frame interval. @@ -339,7 +339,7 @@ Initialization rectangle and may be subject to adjustment to match codec and hardware constraints. - * **Return fields:** + * **Returned fields:** ``r.left``, ``r.top``, ``r.width``, ``r.height`` visible rectangle adjusted by the encoder. @@ -387,7 +387,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` actual number of buffers allocated. @@ -420,7 +420,7 @@ Initialization other fields follow standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` adjusted to the number of allocated buffers. diff --git a/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst b/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst index 4a26646eeec5..35ed05f2695e 100644 --- a/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst @@ -180,7 +180,7 @@ Initialization ``memory`` follows standard semantics. - * **Return fields:** + * **Returned fields:** ``count`` actual number of buffers allocated. @@ -208,7 +208,7 @@ Initialization follows standard semantics. ``V4L2_MEMORY_USERPTR`` is not supported for ``CAPTURE`` buffers. - * **Return fields:** + * **Returned fields:** ``count`` adjusted to allocated number of buffers, in case the codec requires diff --git a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst index 58f6ae25b2e7..296ad2025e8d 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst @@ -275,6 +275,19 @@ please make a proposal on the linux-media mailing list. Decoder's implementation can be found here, `aspeed_codec `__ + * .. _V4L2-PIX-FMT-MT2110T: + + - ``V4L2_PIX_FMT_MT2110T`` + - 'MT2110T' + - This format is two-planar 10-Bit tile mode and having similitude with + ``V4L2_PIX_FMT_MM21`` in term of alignment and tiling. Used for VP9, AV1 + and HEVC. + * .. _V4L2-PIX-FMT-MT2110R: + + - ``V4L2_PIX_FMT_MT2110R`` + - 'MT2110R' + - This format is two-planar 10-Bit raster mode and having similitude with + ``V4L2_PIX_FMT_MM21`` in term of alignment and tiling. Used for AVC. .. raw:: latex \normalsize diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 2d6e3bbdd040..72677a280cd6 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -58,6 +58,9 @@ the subdevice exposes, drivers return the ENOSPC error code and adjust the value of the ``num_routes`` field. Application should then reserve enough memory for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again. +On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the +``num_routes`` field to reflect the actual number of routes returned. + .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| .. c:type:: v4l2_subdev_routing @@ -138,9 +141,7 @@ ENOSPC EINVAL The sink or source pad identifiers reference a non-existing pad, or reference - pads of different types (ie. the sink_pad identifiers refers to a source pad) - or the sink or source stream identifiers reference a non-existing stream on - the sink or source pad. + pads of different types (ie. the sink_pad identifiers refers to a source pad). E2BIG The application provided ``num_routes`` for ``VIDIOC_SUBDEV_S_ROUTING`` is diff --git a/MAINTAINERS b/MAINTAINERS index ae2fbdba3843..ed62f9e1ff33 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1086,7 +1086,6 @@ F: include/soc/amlogic/ AMPHION VPU CODEC V4L2 DRIVER M: Ming Qian -M: Shijie Qin M: Zhou Peng L: linux-media@vger.kernel.org S: Maintained @@ -4469,6 +4468,7 @@ M: Maxime Ripard L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/cdns,*.txt +F: Documentation/devicetree/bindings/media/cdns,csi2rx.yaml F: drivers/media/platform/cadence/cdns-csi2* CADENCE NAND DRIVER @@ -5255,6 +5255,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git F: Documentation/admin-guide/cgroup-v1/cpusets.rst F: include/linux/cpuset.h F: kernel/cgroup/cpuset.c +F: tools/testing/selftests/cgroup/test_cpuset.c +F: tools/testing/selftests/cgroup/test_cpuset_prs.sh CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG) M: Johannes Weiner @@ -6275,11 +6277,17 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.yaml F: drivers/media/i2c/dw9714.c -DONGWOON DW9768 LENS VOICE COIL DRIVER -M: Dongchun Zhu +DONGWOON DW9719 LENS VOICE COIL DRIVER +M: Daniel Scally L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git +F: drivers/media/i2c/dw9719.c + +DONGWOON DW9768 LENS VOICE COIL DRIVER +L: linux-media@vger.kernel.org +S: Orphan +T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml F: drivers/media/i2c/dw9768.c @@ -9668,7 +9676,7 @@ S: Maintained F: arch/x86/kernel/cpu/hygon.c HYNIX HI556 SENSOR DRIVER -M: Shawn Tu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git @@ -9681,7 +9689,7 @@ S: Maintained F: drivers/media/i2c/hi846.c HYNIX HI847 SENSOR DRIVER -M: Shawn Tu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/hi847.c @@ -9752,6 +9760,14 @@ L: linux-acpi@vger.kernel.org S: Maintained F: drivers/i2c/i2c-core-acpi.c +I2C ADDRESS TRANSLATOR (ATR) +M: Tomi Valkeinen +R: Luca Ceresoli +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/i2c-atr.c +F: include/linux/i2c-atr.h + I2C CONTROLLER DRIVER FOR NVIDIA GPU M: Ajay Gupta L: linux-i2c@vger.kernel.org @@ -13101,17 +13117,21 @@ F: drivers/staging/media/imx/ F: include/linux/imx-media.h F: include/media/imx.h -MEDIA DRIVERS FOR FREESCALE IMX7 +MEDIA DRIVERS FOR FREESCALE IMX7/8 M: Rui Miguel Silva M: Laurent Pinchart +M: Martin Kepplinger +R: Purism Kernel Team L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/admin-guide/media/imx7.rst F: Documentation/devicetree/bindings/media/nxp,imx-mipi-csi2.yaml F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml +F: Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml F: drivers/media/platform/nxp/imx-mipi-csis.c F: drivers/media/platform/nxp/imx7-media-csi.c +F: drivers/media/platform/nxp/imx8mq-mipi-csi2.c MEDIA DRIVERS FOR HELENE M: Abylay Ospan @@ -15659,7 +15679,7 @@ F: Documentation/filesystems/omfs.rst F: fs/omfs/ OMNIVISION OG01A1B SENSOR DRIVER -M: Shawn Tu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/og01a1b.c @@ -15672,9 +15692,8 @@ T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov01a10.c OMNIVISION OV02A10 SENSOR DRIVER -M: Dongchun Zhu L: linux-media@vger.kernel.org -S: Maintained +S: Orphan T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml F: drivers/media/i2c/ov02a10.c @@ -15709,6 +15728,7 @@ F: drivers/media/i2c/ov13b10.c OMNIVISION OV2680 SENSOR DRIVER M: Rui Miguel Silva +M: Hans de Goede L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git @@ -15725,7 +15745,7 @@ F: drivers/media/i2c/ov2685.c OMNIVISION OV2740 SENSOR DRIVER M: Tianshu Qiu -R: Shawn Tu +R: Sakari Ailus R: Bingbu Cao L: linux-media@vger.kernel.org S: Maintained @@ -15757,7 +15777,7 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml F: drivers/media/i2c/ov5647.c OMNIVISION OV5670 SENSOR DRIVER -M: Chiranjeevi Rapolu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git @@ -15765,7 +15785,7 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov5670.yaml F: drivers/media/i2c/ov5670.c OMNIVISION OV5675 SENSOR DRIVER -M: Shawn Tu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git @@ -15804,9 +15824,8 @@ F: drivers/media/i2c/ov772x.c F: include/media/i2c/ov772x.h OMNIVISION OV7740 SENSOR DRIVER -M: Wenyou Yang L: linux-media@vger.kernel.org -S: Maintained +S: Orphan T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ov7740.txt F: drivers/media/i2c/ov7740.c @@ -21510,6 +21529,14 @@ F: drivers/misc/tifm* F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h +TI FPD-LINK DRIVERS +M: Tomi Valkeinen +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/ti,ds90* +F: drivers/media/i2c/ds90* +F: include/media/i2c/ds90* + TI KEYSTONE MULTICORE NAVIGATOR DRIVERS M: Nishanth Menon M: Santosh Shilimkar @@ -21969,7 +21996,7 @@ F: Documentation/admin-guide/ufs.rst F: fs/ufs/ UHID USERSPACE HID IO DRIVER -M: David Rheinsberg +M: David Rheinsberg L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/uhid.c @@ -22454,6 +22481,39 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/clk/ux500/ +V4L2 ASYNC AND FWNODE FRAMEWORKS +M: Sakari Ailus +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: drivers/media/v4l2-core/v4l2-async.c +F: drivers/media/v4l2-core/v4l2-fwnode.c +F: include/media/v4l2-async.h +F: include/media/v4l2-fwnode.h + +V4L2 LENS DRIVERS +M: Sakari Ailus +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/ak* +F: drivers/media/i2c/dw* +F: drivers/media/i2c/lm* + +V4L2 CAMERA SENSOR DRIVERS +M: Sakari Ailus +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/driver-api/media/camera-sensor.rst +F: Documentation/driver-api/media/tx-rx.rst +F: drivers/media/i2c/ar* +F: drivers/media/i2c/hi* +F: drivers/media/i2c/imx* +F: drivers/media/i2c/mt* +F: drivers/media/i2c/og* +F: drivers/media/i2c/ov* +F: drivers/media/i2c/s5* +F: drivers/media/i2c/st-vgxy61.c + VF610 NAND DRIVER M: Stefan Agner L: linux-mtd@lists.infradead.org @@ -23108,7 +23168,7 @@ S: Maintained F: drivers/rtc/rtc-sd3078.c WIIMOTE HID DRIVER -M: David Rheinsberg +M: David Rheinsberg L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-wiimote* diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2e9636da2147..5315789f4868 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1159,7 +1159,6 @@ CONFIG_XEN_GNTDEV=y CONFIG_XEN_GRANT_DEV_ALLOC=y CONFIG_STAGING=y CONFIG_STAGING_MEDIA=y -CONFIG_VIDEO_IMX_MEDIA=m CONFIG_VIDEO_MAX96712=m CONFIG_CHROME_PLATFORMS=y CONFIG_CROS_EC=y diff --git a/arch/powerpc/crypto/Kconfig b/arch/powerpc/crypto/Kconfig index 803da4a8a9a2..6fc2248ca561 100644 --- a/arch/powerpc/crypto/Kconfig +++ b/arch/powerpc/crypto/Kconfig @@ -113,7 +113,7 @@ config CRYPTO_AES_GCM_P10 config CRYPTO_CHACHA20_P10 tristate "Ciphers: ChaCha20, XChacha20, XChacha12 (P10 or later)" - depends on PPC64 && CPU_LITTLE_ENDIAN + depends on PPC64 && CPU_LITTLE_ENDIAN && VSX select CRYPTO_SKCIPHER select CRYPTO_LIB_CHACHA_GENERIC select CRYPTO_ARCH_HAVE_LIB_CHACHA @@ -127,7 +127,7 @@ config CRYPTO_CHACHA20_P10 config CRYPTO_POLY1305_P10 tristate "Hash functions: Poly1305 (P10 or later)" - depends on PPC64 && CPU_LITTLE_ENDIAN + depends on PPC64 && CPU_LITTLE_ENDIAN && VSX select CRYPTO_HASH select CRYPTO_LIB_POLY1305_GENERIC help diff --git a/arch/x86/include/asm/audit.h b/arch/x86/include/asm/audit.h index 36aec57ea7a3..fa918f01333e 100644 --- a/arch/x86/include/asm/audit.h +++ b/arch/x86/include/asm/audit.h @@ -4,4 +4,11 @@ int ia32_classify_syscall(unsigned int syscall); +extern unsigned ia32_dir_class[]; +extern unsigned ia32_write_class[]; +extern unsigned ia32_read_class[]; +extern unsigned ia32_chattr_class[]; +extern unsigned ia32_signal_class[]; + + #endif /* _ASM_X86_AUDIT_H */ diff --git a/arch/x86/kernel/audit_64.c b/arch/x86/kernel/audit_64.c index 44c3601cfdc4..190c120f4285 100644 --- a/arch/x86/kernel/audit_64.c +++ b/arch/x86/kernel/audit_64.c @@ -63,11 +63,6 @@ int audit_classify_syscall(int abi, unsigned syscall) static int __init audit_classes_init(void) { #ifdef CONFIG_IA32_EMULATION - extern __u32 ia32_dir_class[]; - extern __u32 ia32_write_class[]; - extern __u32 ia32_read_class[]; - extern __u32 ia32_chattr_class[]; - extern __u32 ia32_signal_class[]; audit_register_class(AUDIT_CLASS_WRITE_32, ia32_write_class); audit_register_class(AUDIT_CLASS_READ_32, ia32_read_class); audit_register_class(AUDIT_CLASS_DIR_WRITE_32, ia32_dir_class); diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 6d75fab10161..382d4e6b848d 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1280,11 +1280,11 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { VULNBL_INTEL_STEPPINGS(BROADWELL_G, X86_STEPPING_ANY, SRBDS), VULNBL_INTEL_STEPPINGS(BROADWELL_X, X86_STEPPING_ANY, MMIO), VULNBL_INTEL_STEPPINGS(BROADWELL, X86_STEPPING_ANY, SRBDS), - VULNBL_INTEL_STEPPINGS(SKYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), VULNBL_INTEL_STEPPINGS(SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED | GDS), - VULNBL_INTEL_STEPPINGS(SKYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), - VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), + VULNBL_INTEL_STEPPINGS(SKYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(SKYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), VULNBL_INTEL_STEPPINGS(CANNONLAKE_L, X86_STEPPING_ANY, RETBLEED), VULNBL_INTEL_STEPPINGS(ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(ICELAKE_D, X86_STEPPING_ANY, MMIO | GDS), diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 41dac93b8ea4..cadf68737e6b 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -71,7 +71,7 @@ static unsigned short xsave_cpuid_features[] __initdata = { [XFEATURE_ZMM_Hi256] = X86_FEATURE_AVX512F, [XFEATURE_Hi16_ZMM] = X86_FEATURE_AVX512F, [XFEATURE_PT_UNIMPLEMENTED_SO_FAR] = X86_FEATURE_INTEL_PT, - [XFEATURE_PKRU] = X86_FEATURE_PKU, + [XFEATURE_PKRU] = X86_FEATURE_OSPKE, [XFEATURE_PASID] = X86_FEATURE_ENQCMD, [XFEATURE_CET_USER] = X86_FEATURE_SHSTK, [XFEATURE_XTILE_CFG] = X86_FEATURE_AMX_TILE, diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c index 4d8aff05a509..30a55207c000 100644 --- a/arch/x86/kernel/i8259.c +++ b/arch/x86/kernel/i8259.c @@ -231,9 +231,7 @@ struct irq_chip i8259A_chip = { }; static char irq_trigger[2]; -/** - * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ - */ +/* ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ */ static void restore_ELCR(char *trigger) { outb(trigger[0], PIC_ELCR1); diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e11c1c803676..0cea301cc9a9 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -412,6 +412,13 @@ config HID_GOOGLE_HAMMER help Say Y here if you have a Google Hammer device. +config HID_GOOGLE_STADIA_FF + tristate "Google Stadia force feedback" + select INPUT_FF_MEMLESS + help + Say Y here if you want to enable force feedback support for the Google + Stadia controller. + config HID_VIVALDI tristate "Vivaldi Keyboard" select HID_VIVALDI_COMMON @@ -1066,9 +1073,11 @@ config STEAM_FF Deck. config HID_STEELSERIES - tristate "Steelseries SRW-S1 steering wheel support" + tristate "Steelseries devices support" + depends on USB_HID help - Support for Steelseries SRW-S1 steering wheel + Support for Steelseries SRW-S1 steering wheel, and the Steelseries + Arctis 1 Wireless for XBox headset. config HID_SUNPLUS tristate "Sunplus wireless desktop" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 7a9e160158f7..8a06d0f840bc 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_HID_GFRM) += hid-gfrm.o obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o +obj-$(CONFIG_HID_GOOGLE_STADIA_FF) += hid-google-stadiaff.o obj-$(CONFIG_HID_VIVALDI) += hid-vivaldi.o obj-$(CONFIG_HID_GT683R) += hid-gt683r.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index d7b932925730..3ca45975c686 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -343,7 +343,8 @@ static const struct apple_non_apple_keyboard non_apple_keyboards[] = { { "SONiX USB DEVICE" }, { "Keychron" }, { "AONE" }, - { "GANSS" } + { "GANSS" }, + { "Hailuck" }, }; static bool apple_is_non_apple_keyboard(struct hid_device *hdev) diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 27cadadda7c9..54c33a24f844 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -16,14 +16,14 @@ * https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf */ -#include -#include +#include #include #include #include #include #include #include +#include #include #include "hid-ids.h" @@ -31,6 +31,8 @@ #define CP2112_GPIO_CONFIG_LENGTH 5 #define CP2112_GPIO_GET_LENGTH 2 #define CP2112_GPIO_SET_LENGTH 3 +#define CP2112_GPIO_MAX_GPIO 8 +#define CP2112_GPIO_ALL_GPIO_MASK GENMASK(7, 0) enum { CP2112_GPIO_CONFIG = 0x02, @@ -163,19 +165,17 @@ struct cp2112_device { atomic_t read_avail; atomic_t xfer_avail; struct gpio_chip gc; - struct irq_chip irq; u8 *in_out_buffer; struct mutex lock; - struct gpio_desc *desc[8]; bool gpio_poll; struct delayed_work gpio_poll_worker; unsigned long irq_mask; u8 gpio_prev_state; }; -static int gpio_push_pull = 0xFF; -module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR); +static int gpio_push_pull = CP2112_GPIO_ALL_GPIO_MASK; +module_param(gpio_push_pull, int, 0644); MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask"); static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -197,7 +197,7 @@ static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset) goto exit; } - buf[1] &= ~(1 << offset); + buf[1] &= ~BIT(offset); buf[2] = gpio_push_pull; ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, @@ -227,8 +227,8 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value) mutex_lock(&dev->lock); buf[0] = CP2112_GPIO_SET; - buf[1] = value ? 0xff : 0; - buf[2] = 1 << offset; + buf[1] = value ? CP2112_GPIO_ALL_GPIO_MASK : 0; + buf[2] = BIT(offset); ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, CP2112_GPIO_SET_LENGTH, HID_FEATURE_REPORT, @@ -532,15 +532,13 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, hid_dbg(hdev, "I2C %d messages\n", num); if (num == 1) { + hid_dbg(hdev, "I2C %s %#04x len %d\n", + str_read_write(msgs->flags & I2C_M_RD), msgs->addr, msgs->len); if (msgs->flags & I2C_M_RD) { - hid_dbg(hdev, "I2C read %#04x len %d\n", - msgs->addr, msgs->len); read_length = msgs->len; read_buf = msgs->buf; count = cp2112_read_req(buf, msgs->addr, msgs->len); } else { - hid_dbg(hdev, "I2C write %#04x len %d\n", - msgs->addr, msgs->len); count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, msgs->len); } @@ -648,7 +646,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, int ret; hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n", - read_write == I2C_SMBUS_WRITE ? "write" : "read", + str_write_read(read_write == I2C_SMBUS_WRITE), addr, flags, command, size); switch (size) { @@ -895,7 +893,7 @@ static ssize_t name##_show(struct device *kdev, \ int ret = cp2112_get_usb_config(hdev, &cfg); \ if (ret) \ return ret; \ - return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \ + return sysfs_emit(buf, format, ##__VA_ARGS__); \ } \ static DEVICE_ATTR_RW(name); @@ -946,18 +944,10 @@ CP2112_CONFIG_ATTR(release_version, ({ #undef CP2112_CONFIG_ATTR -struct cp2112_pstring_attribute { - struct device_attribute attr; - unsigned char report; -}; - -static ssize_t pstr_store(struct device *kdev, - struct device_attribute *kattr, const char *buf, - size_t count) +static ssize_t pstr_store(struct device *kdev, struct device_attribute *kattr, + const char *buf, size_t count, int number) { struct hid_device *hdev = to_hid_device(kdev); - struct cp2112_pstring_attribute *attr = - container_of(kattr, struct cp2112_pstring_attribute, attr); struct cp2112_string_report report; int ret; @@ -965,7 +955,7 @@ static ssize_t pstr_store(struct device *kdev, ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN, report.string, ARRAY_SIZE(report.string)); - report.report = attr->report; + report.report = number; report.length = ret * sizeof(report.string[0]) + 2; report.type = USB_DT_STRING; @@ -983,17 +973,15 @@ static ssize_t pstr_store(struct device *kdev, return count; } -static ssize_t pstr_show(struct device *kdev, - struct device_attribute *kattr, char *buf) +static ssize_t pstr_show(struct device *kdev, struct device_attribute *kattr, + char *buf, int number) { struct hid_device *hdev = to_hid_device(kdev); - struct cp2112_pstring_attribute *attr = - container_of(kattr, struct cp2112_pstring_attribute, attr); struct cp2112_string_report report; u8 length; int ret; - ret = cp2112_hid_get(hdev, attr->report, (u8 *)&report.contents, + ret = cp2112_hid_get(hdev, number, (u8 *)&report.contents, sizeof(report.contents), HID_FEATURE_REPORT); if (ret < 3) { hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name, @@ -1018,10 +1006,16 @@ static ssize_t pstr_show(struct device *kdev, } #define CP2112_PSTR_ATTR(name, _report) \ -static struct cp2112_pstring_attribute dev_attr_##name = { \ - .attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \ - .report = _report, \ -}; +static ssize_t name##_store(struct device *kdev, struct device_attribute *kattr, \ + const char *buf, size_t count) \ +{ \ + return pstr_store(kdev, kattr, buf, count, _report); \ +} \ +static ssize_t name##_show(struct device *kdev, struct device_attribute *kattr, char *buf) \ +{ \ + return pstr_show(kdev, kattr, buf, _report); \ +} \ +static DEVICE_ATTR_RW(name); CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING); CP2112_PSTR_ATTR(product, CP2112_PRODUCT_STRING); @@ -1036,9 +1030,9 @@ static const struct attribute_group cp2112_attr_group = { &dev_attr_max_power.attr, &dev_attr_power_mode.attr, &dev_attr_release_version.attr, - &dev_attr_manufacturer.attr.attr, - &dev_attr_product.attr.attr, - &dev_attr_serial.attr.attr, + &dev_attr_manufacturer.attr, + &dev_attr_product.attr, + &dev_attr_serial.attr, NULL } }; @@ -1063,7 +1057,7 @@ static void chmod_sysfs_attrs(struct hid_device *hdev) } for (attr = cp2112_attr_group.attrs; *attr; ++attr) { - umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO; + umode_t mode = (buf[1] & 1) ? 0644 : 0444; ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode); if (ret < 0) hid_err(hdev, "error chmoding sysfs file %s\n", @@ -1080,16 +1074,20 @@ static void cp2112_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct cp2112_device *dev = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - __clear_bit(d->hwirq, &dev->irq_mask); + __clear_bit(hwirq, &dev->irq_mask); + gpiochip_disable_irq(gc, hwirq); } static void cp2112_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct cp2112_device *dev = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - __set_bit(d->hwirq, &dev->irq_mask); + gpiochip_enable_irq(gc, hwirq); + __set_bit(hwirq, &dev->irq_mask); } static void cp2112_gpio_poll_callback(struct work_struct *work) @@ -1098,7 +1096,6 @@ static void cp2112_gpio_poll_callback(struct work_struct *work) gpio_poll_worker.work); struct irq_data *d; u8 gpio_mask; - u8 virqs = (u8)dev->irq_mask; u32 irq_type; int irq, virq, ret; @@ -1109,15 +1106,10 @@ static void cp2112_gpio_poll_callback(struct work_struct *work) goto exit; gpio_mask = ret; - - while (virqs) { - virq = ffs(virqs) - 1; - virqs &= ~BIT(virq); - - if (!dev->gc.to_irq) - break; - - irq = dev->gc.to_irq(&dev->gc, virq); + for_each_set_bit(virq, &dev->irq_mask, CP2112_GPIO_MAX_GPIO) { + irq = irq_find_mapping(dev->gc.irq.domain, virq); + if (!irq) + continue; d = irq_get_irq_data(irq); if (!d) @@ -1175,6 +1167,7 @@ static void cp2112_gpio_irq_shutdown(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct cp2112_device *dev = gpiochip_get_data(gc); + cp2112_gpio_irq_mask(d); cancel_delayed_work_sync(&dev->gpio_poll_worker); } @@ -1183,50 +1176,17 @@ static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } -static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev, - int pin) -{ - int ret; - - if (dev->desc[pin]) - return -EINVAL; - - dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin, - "HID/I2C:Event", - GPIO_ACTIVE_HIGH, - GPIOD_IN); - if (IS_ERR(dev->desc[pin])) { - dev_err(dev->gc.parent, "Failed to request GPIO\n"); - return PTR_ERR(dev->desc[pin]); - } - - ret = cp2112_gpio_direction_input(&dev->gc, pin); - if (ret < 0) { - dev_err(dev->gc.parent, "Failed to set GPIO to input dir\n"); - goto err_desc; - } - - ret = gpiochip_lock_as_irq(&dev->gc, pin); - if (ret) { - dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n"); - goto err_desc; - } - - ret = gpiod_to_irq(dev->desc[pin]); - if (ret < 0) { - dev_err(dev->gc.parent, "Failed to translate GPIO to IRQ\n"); - goto err_lock; - } - - return ret; - -err_lock: - gpiochip_unlock_as_irq(&dev->gc, pin); -err_desc: - gpiochip_free_own_desc(dev->desc[pin]); - dev->desc[pin] = NULL; - return ret; -} +static const struct irq_chip cp2112_gpio_irqchip = { + .name = "cp2112-gpio", + .irq_startup = cp2112_gpio_irq_startup, + .irq_shutdown = cp2112_gpio_irq_shutdown, + .irq_ack = cp2112_gpio_irq_ack, + .irq_mask = cp2112_gpio_irq_mask, + .irq_unmask = cp2112_gpio_irq_unmask, + .irq_set_type = cp2112_gpio_irq_type, + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -1333,21 +1293,12 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) dev->gc.set = cp2112_gpio_set; dev->gc.get = cp2112_gpio_get; dev->gc.base = -1; - dev->gc.ngpio = 8; + dev->gc.ngpio = CP2112_GPIO_MAX_GPIO; dev->gc.can_sleep = 1; dev->gc.parent = &hdev->dev; - dev->irq.name = "cp2112-gpio"; - dev->irq.irq_startup = cp2112_gpio_irq_startup; - dev->irq.irq_shutdown = cp2112_gpio_irq_shutdown; - dev->irq.irq_ack = cp2112_gpio_irq_ack; - dev->irq.irq_mask = cp2112_gpio_irq_mask; - dev->irq.irq_unmask = cp2112_gpio_irq_unmask; - dev->irq.irq_set_type = cp2112_gpio_irq_type; - dev->irq.flags = IRQCHIP_MASK_ON_SUSPEND; - girq = &dev->gc.irq; - girq->chip = &dev->irq; + gpio_irq_chip_set_chip(girq, &cp2112_gpio_irqchip); /* The event comes from the outside so no parent handler */ girq->parent_handler = NULL; girq->num_parents = 0; @@ -1389,7 +1340,6 @@ err_hid_stop: static void cp2112_remove(struct hid_device *hdev) { struct cp2112_device *dev = hid_get_drvdata(hdev); - int i; sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group); i2c_del_adapter(&dev->adap); @@ -1399,11 +1349,6 @@ static void cp2112_remove(struct hid_device *hdev) cancel_delayed_work_sync(&dev->gpio_poll_worker); } - for (i = 0; i < ARRAY_SIZE(dev->desc); i++) { - gpiochip_unlock_as_irq(&dev->gc, i); - gpiochip_free_own_desc(dev->desc[i]); - } - gpiochip_remove(&dev->gc); /* i2c_del_adapter has finished removing all i2c devices from our * adapter. Well behaved devices should no longer call our cp2112_xfer diff --git a/drivers/hid/hid-google-stadiaff.c b/drivers/hid/hid-google-stadiaff.c new file mode 100644 index 000000000000..3731575562ab --- /dev/null +++ b/drivers/hid/hid-google-stadiaff.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Stadia controller rumble support. + * + * Copyright 2023 Google LLC + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define STADIA_FF_REPORT_ID 5 + +struct stadiaff_device { + struct hid_device *hid; + struct hid_report *report; + spinlock_t lock; + bool removed; + uint16_t strong_magnitude; + uint16_t weak_magnitude; + struct work_struct work; +}; + +static void stadiaff_work(struct work_struct *work) +{ + struct stadiaff_device *stadiaff = + container_of(work, struct stadiaff_device, work); + struct hid_field *rumble_field = stadiaff->report->field[0]; + unsigned long flags; + + spin_lock_irqsave(&stadiaff->lock, flags); + rumble_field->value[0] = stadiaff->strong_magnitude; + rumble_field->value[1] = stadiaff->weak_magnitude; + spin_unlock_irqrestore(&stadiaff->lock, flags); + + hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT); +} + +static int stadiaff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct stadiaff_device *stadiaff = hid_get_drvdata(hid); + unsigned long flags; + + spin_lock_irqsave(&stadiaff->lock, flags); + if (!stadiaff->removed) { + stadiaff->strong_magnitude = effect->u.rumble.strong_magnitude; + stadiaff->weak_magnitude = effect->u.rumble.weak_magnitude; + schedule_work(&stadiaff->work); + } + spin_unlock_irqrestore(&stadiaff->lock, flags); + + return 0; +} + +static int stadiaff_init(struct hid_device *hid) +{ + struct stadiaff_device *stadiaff; + struct hid_report *report; + struct hid_input *hidinput; + struct input_dev *dev; + int error; + + if (list_empty(&hid->inputs)) { + hid_err(hid, "no inputs found\n"); + return -ENODEV; + } + hidinput = list_entry(hid->inputs.next, struct hid_input, list); + dev = hidinput->input; + + report = hid_validate_values(hid, HID_OUTPUT_REPORT, + STADIA_FF_REPORT_ID, 0, 2); + if (!report) + return -ENODEV; + + stadiaff = devm_kzalloc(&hid->dev, sizeof(struct stadiaff_device), + GFP_KERNEL); + if (!stadiaff) + return -ENOMEM; + + hid_set_drvdata(hid, stadiaff); + + input_set_capability(dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(dev, NULL, stadiaff_play); + if (error) + return error; + + stadiaff->removed = false; + stadiaff->hid = hid; + stadiaff->report = report; + INIT_WORK(&stadiaff->work, stadiaff_work); + spin_lock_init(&stadiaff->lock); + + hid_info(hid, "Force Feedback for Google Stadia controller\n"); + + return 0; +} + +static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + ret = stadiaff_init(hdev); + if (ret) { + hid_err(hdev, "force feedback init failed\n"); + hid_hw_stop(hdev); + return ret; + } + + return 0; +} + +static void stadia_remove(struct hid_device *hid) +{ + struct stadiaff_device *stadiaff = hid_get_drvdata(hid); + unsigned long flags; + + spin_lock_irqsave(&stadiaff->lock, flags); + stadiaff->removed = true; + spin_unlock_irqrestore(&stadiaff->lock, flags); + + cancel_work_sync(&stadiaff->work); + hid_hw_stop(hid); +} + +static const struct hid_device_id stadia_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) }, + { } +}; +MODULE_DEVICE_TABLE(hid, stadia_devices); + +static struct hid_driver stadia_driver = { + .name = "stadia", + .id_table = stadia_devices, + .probe = stadia_probe, + .remove = stadia_remove, +}; +module_hid_driver(stadia_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8a310f8ff20f..7e499992a793 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -531,6 +531,7 @@ #define USB_DEVICE_ID_GOOGLE_DON 0x5050 #define USB_DEVICE_ID_GOOGLE_EEL 0x5057 #define USB_DEVICE_ID_GOOGLE_JEWEL 0x5061 +#define USB_DEVICE_ID_GOOGLE_STADIA 0x9400 #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f @@ -866,6 +867,7 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2 0xc547 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 851ee86eff32..0235cc1690a1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -358,6 +358,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD), + HID_BATTERY_QUIRK_IGNORE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084), HID_BATTERY_QUIRK_IGNORE }, @@ -988,6 +991,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel return; case 0x3c: /* Invert */ + device->quirks &= ~HID_QUIRK_NOINVERT; map_key_clear(BTN_TOOL_RUBBER); break; @@ -1013,9 +1017,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x45: /* ERASER */ /* * This event is reported when eraser tip touches the surface. - * Actual eraser (BTN_TOOL_RUBBER) is set by Invert usage when - * tool gets in proximity. + * Actual eraser (BTN_TOOL_RUBBER) is set and released either + * by Invert if tool reports proximity or by Eraser directly. */ + if (!test_bit(BTN_TOOL_RUBBER, input->keybit)) { + device->quirks |= HID_QUIRK_NOINVERT; + set_bit(BTN_TOOL_RUBBER, input->keybit); + } map_key_clear(BTN_TOUCH); break; @@ -1580,6 +1588,15 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct else if (report->tool != BTN_TOOL_RUBBER) /* value is off, tool is not rubber, ignore */ return; + else if (*quirks & HID_QUIRK_NOINVERT && + !test_bit(BTN_TOUCH, input->key)) { + /* + * There is no invert to release the tool, let hid_input + * send BTN_TOUCH with scancode and release the tool after. + */ + hid_report_release_tool(report, input, BTN_TOOL_RUBBER); + return; + } /* let hid-input set BTN_TOUCH */ break; diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 62180414efcc..8afe3be683ba 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1285,6 +1285,9 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, * 50 msec should gives enough time to the receiver to be ready. */ msleep(50); + + if (retval) + return retval; } /* @@ -1306,7 +1309,7 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, buf[5] = 0x09; buf[6] = 0x00; - hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf, + retval = hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf, HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); @@ -1692,11 +1695,12 @@ static int logi_dj_raw_event(struct hid_device *hdev, } /* * Mouse-only receivers send unnumbered mouse data. The 27 MHz - * receiver uses 6 byte packets, the nano receiver 8 bytes. + * receiver uses 6 byte packets, the nano receiver 8 bytes, + * the lightspeed receiver (Pro X Superlight) 13 bytes. */ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE && - size <= 8) { - u8 mouse_report[9]; + size <= 13){ + u8 mouse_report[14]; /* Prepend report id */ mouse_report[0] = REPORT_TYPE_MOUSE; @@ -1980,6 +1984,10 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1), .driver_data = recvr_type_gaming_hidpp}, + { /* Logitech lightspeed receiver (0xc547) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2), + .driver_data = recvr_type_gaming_hidpp}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 129b01be488d..05f5b5f588a2 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -228,7 +228,7 @@ struct hidpp_device { #define HIDPP20_ERROR_INVALID_ARGS 0x02 #define HIDPP20_ERROR_OUT_OF_RANGE 0x03 #define HIDPP20_ERROR_HW_ERROR 0x04 -#define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05 +#define HIDPP20_ERROR_NOT_ALLOWED 0x05 #define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06 #define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07 #define HIDPP20_ERROR_BUSY 0x08 @@ -275,21 +275,22 @@ static int __hidpp_send_report(struct hid_device *hdev, } /* - * hidpp_send_message_sync() returns 0 in case of success, and something else - * in case of a failure. - * - If ' something else' is positive, that means that an error has been raised - * by the protocol itself. - * - If ' something else' is negative, that means that we had a classic error - * (-ENOMEM, -EPIPE, etc...) + * Effectively send the message to the device, waiting for its answer. + * + * Must be called with hidpp->send_mutex locked + * + * Same return protocol than hidpp_send_message_sync(): + * - success on 0 + * - negative error means transport error + * - positive value means protocol error */ -static int hidpp_send_message_sync(struct hidpp_device *hidpp, +static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp, struct hidpp_report *message, struct hidpp_report *response) { - int ret = -1; - int max_retries = 3; + int ret; - mutex_lock(&hidpp->send_mutex); + __must_hold(&hidpp->send_mutex); hidpp->send_receive_buf = response; hidpp->answer_available = false; @@ -300,47 +301,74 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, */ *response = *message; - for (; max_retries != 0 && ret; max_retries--) { - ret = __hidpp_send_report(hidpp->hid_dev, message); - - if (ret) { - dbg_hid("__hidpp_send_report returned err: %d\n", ret); - memset(response, 0, sizeof(struct hidpp_report)); - break; - } - - if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, - 5*HZ)) { - dbg_hid("%s:timeout waiting for response\n", __func__); - memset(response, 0, sizeof(struct hidpp_report)); - ret = -ETIMEDOUT; - break; - } - - if (response->report_id == REPORT_ID_HIDPP_SHORT && - response->rap.sub_id == HIDPP_ERROR) { - ret = response->rap.params[1]; - dbg_hid("%s:got hidpp error %02X\n", __func__, ret); - break; - } - - if ((response->report_id == REPORT_ID_HIDPP_LONG || - response->report_id == REPORT_ID_HIDPP_VERY_LONG) && - response->fap.feature_index == HIDPP20_ERROR) { - ret = response->fap.params[1]; - if (ret != HIDPP20_ERROR_BUSY) { - dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); - break; - } - dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret); - } + ret = __hidpp_send_report(hidpp->hid_dev, message); + if (ret) { + dbg_hid("__hidpp_send_report returned err: %d\n", ret); + memset(response, 0, sizeof(struct hidpp_report)); + return ret; } + if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, + 5*HZ)) { + dbg_hid("%s:timeout waiting for response\n", __func__); + memset(response, 0, sizeof(struct hidpp_report)); + return -ETIMEDOUT; + } + + if (response->report_id == REPORT_ID_HIDPP_SHORT && + response->rap.sub_id == HIDPP_ERROR) { + ret = response->rap.params[1]; + dbg_hid("%s:got hidpp error %02X\n", __func__, ret); + return ret; + } + + if ((response->report_id == REPORT_ID_HIDPP_LONG || + response->report_id == REPORT_ID_HIDPP_VERY_LONG) && + response->fap.feature_index == HIDPP20_ERROR) { + ret = response->fap.params[1]; + dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); + return ret; + } + + return 0; +} + +/* + * hidpp_send_message_sync() returns 0 in case of success, and something else + * in case of a failure. + * + * See __do_hidpp_send_message_sync() for a detailed explanation of the returned + * value. + */ +static int hidpp_send_message_sync(struct hidpp_device *hidpp, + struct hidpp_report *message, + struct hidpp_report *response) +{ + int ret; + int max_retries = 3; + + mutex_lock(&hidpp->send_mutex); + + do { + ret = __do_hidpp_send_message_sync(hidpp, message, response); + if (ret != HIDPP20_ERROR_BUSY) + break; + + dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret); + } while (--max_retries); + mutex_unlock(&hidpp->send_mutex); return ret; } +/* + * hidpp_send_fap_command_sync() returns 0 in case of success, and something else + * in case of a failure. + * + * See __do_hidpp_send_message_sync() for a detailed explanation of the returned + * value. + */ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count, struct hidpp_report *response) @@ -373,6 +401,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp, return ret; } +/* + * hidpp_send_rap_command_sync() returns 0 in case of success, and something else + * in case of a failure. + * + * See __do_hidpp_send_message_sync() for a detailed explanation of the returned + * value. + */ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count, struct hidpp_report *response) @@ -4620,6 +4655,8 @@ static const struct hid_device_id hidpp_devices[] = { .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS }, { /* Logitech G Pro Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, + { /* Logitech G Pro X Superlight Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC094) }, { /* G935 Gaming Headset */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0x0a87), @@ -4647,6 +4684,8 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) }, { /* MX Master 3 mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) }, + { /* MX Anywhere 3 mouse over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb025) }, { /* MX Master 3S mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb034) }, {} diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e31be0cb8b85..521b2ffb4244 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1594,7 +1594,6 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app) static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); - char *name; const char *suffix = NULL; struct mt_report_data *rdata; struct mt_application *mt_application = NULL; @@ -1645,15 +1644,9 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) break; } - if (suffix) { - name = devm_kzalloc(&hi->input->dev, - strlen(hdev->name) + strlen(suffix) + 2, - GFP_KERNEL); - if (name) { - sprintf(name, "%s %s", hdev->name, suffix); - hi->input->name = name; - } - } + if (suffix) + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); return 0; } diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c index a928ad2be62d..9a3576dbf421 100644 --- a/drivers/hid/hid-nvidia-shield.c +++ b/drivers/hid/hid-nvidia-shield.c @@ -6,11 +6,15 @@ */ #include +#include #include #include +#include #include #include +#include #include +#include #include #include "hid-ids.h" @@ -30,6 +34,8 @@ enum { enum { SHIELD_FW_VERSION_INITIALIZED = 0, SHIELD_BOARD_INFO_INITIALIZED, + SHIELD_BATTERY_STATS_INITIALIZED, + SHIELD_CHARGER_STATE_INITIALIZED, }; enum { @@ -37,6 +43,7 @@ enum { THUNDERSTRIKE_BOARD_INFO_UPDATE, THUNDERSTRIKE_HAPTICS_UPDATE, THUNDERSTRIKE_LED_UPDATE, + THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, }; enum { @@ -48,10 +55,46 @@ enum { enum { THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1, THUNDERSTRIKE_HOSTCMD_ID_LED = 6, + THUNDERSTRIKE_HOSTCMD_ID_BATTERY, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16, THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53, THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57, - THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58, + THUNDERSTRIKE_HOSTCMD_ID_CHARGER, +}; + +struct power_supply_dev { + struct power_supply *psy; + struct power_supply_desc desc; +}; + +struct thunderstrike_psy_prop_values { + int voltage_min; + int voltage_now; + int voltage_avg; + int voltage_boot; + int capacity; + int status; + int charge_type; + int temp; +}; + +static const enum power_supply_property thunderstrike_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_BOOT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_MIN, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, }; enum thunderstrike_led_state { @@ -60,6 +103,38 @@ enum thunderstrike_led_state { } __packed; static_assert(sizeof(enum thunderstrike_led_state) == 1); +struct thunderstrike_hostcmd_battery { + __le16 voltage_avg; + u8 reserved_at_10; + __le16 thermistor; + __le16 voltage_min; + __le16 voltage_boot; + __le16 voltage_now; + u8 capacity; +} __packed; + +enum thunderstrike_charger_type { + THUNDERSTRIKE_CHARGER_TYPE_NONE = 0, + THUNDERSTRIKE_CHARGER_TYPE_TRICKLE, + THUNDERSTRIKE_CHARGER_TYPE_NORMAL, +} __packed; +static_assert(sizeof(enum thunderstrike_charger_type) == 1); + +enum thunderstrike_charger_state { + THUNDERSTRIKE_CHARGER_STATE_UNKNOWN = 0, + THUNDERSTRIKE_CHARGER_STATE_DISABLED, + THUNDERSTRIKE_CHARGER_STATE_CHARGING, + THUNDERSTRIKE_CHARGER_STATE_FULL, + THUNDERSTRIKE_CHARGER_STATE_FAILED = 8, +} __packed; +static_assert(sizeof(enum thunderstrike_charger_state) == 1); + +struct thunderstrike_hostcmd_charger { + u8 connected; + enum thunderstrike_charger_type type; + enum thunderstrike_charger_state state; +} __packed; + struct thunderstrike_hostcmd_board_info { __le16 revision; __le16 serial[7]; @@ -80,6 +155,8 @@ struct thunderstrike_hostcmd_resp_report { struct thunderstrike_hostcmd_haptics motors; __le16 fw_version; enum thunderstrike_led_state led_state; + struct thunderstrike_hostcmd_battery battery; + struct thunderstrike_hostcmd_charger charger; u8 payload[30]; } __packed; } __packed; @@ -109,6 +186,7 @@ static_assert(sizeof(struct thunderstrike_hostcmd_req_report) == /* Common struct for shield accessories. */ struct shield_device { struct hid_device *hdev; + struct power_supply_dev battery_dev; unsigned long initialized_flags; const char *codename; @@ -119,9 +197,17 @@ struct shield_device { } board_info; }; +/* + * Non-trivial to uniquely identify Thunderstrike controllers at initialization + * time. Use an ID allocator to help with this. + */ +static DEFINE_IDA(thunderstrike_ida); + struct thunderstrike { struct shield_device base; + int id; + /* Sub-devices */ struct input_dev *haptics_dev; struct led_classdev led_dev; @@ -133,6 +219,9 @@ struct thunderstrike { spinlock_t haptics_update_lock; u8 led_state : 1; enum thunderstrike_led_state led_value; + struct thunderstrike_psy_prop_values psy_stats; + spinlock_t psy_stats_lock; + struct timer_list psy_stats_timer; struct work_struct hostcmd_req_work; }; @@ -164,7 +253,7 @@ static struct input_dev *shield_allocate_input_dev(struct hid_device *hdev, idev->id.product = hdev->product; idev->id.version = hdev->version; idev->uniq = hdev->uniq; - idev->name = devm_kasprintf(&idev->dev, GFP_KERNEL, "%s %s", hdev->name, + idev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s %s", hdev->name, name_suffix); if (!idev->name) goto err_name; @@ -247,6 +336,16 @@ static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work) thunderstrike_send_hostcmd_request(ts); } + if (test_and_clear_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags)) { + thunderstrike_hostcmd_req_report_init( + report, THUNDERSTRIKE_HOSTCMD_ID_BATTERY); + thunderstrike_send_hostcmd_request(ts); + + thunderstrike_hostcmd_req_report_init( + report, THUNDERSTRIKE_HOSTCMD_ID_CHARGER); + thunderstrike_send_hostcmd_request(ts); + } + if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) { thunderstrike_hostcmd_req_report_init( report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO); @@ -352,6 +451,93 @@ static void thunderstrike_led_set_brightness(struct led_classdev *led, schedule_work(&ts->hostcmd_req_work); } +static int thunderstrike_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct shield_device *shield_dev = power_supply_get_drvdata(psy); + struct thunderstrike_psy_prop_values prop_values; + struct thunderstrike *ts; + int ret = 0; + + ts = container_of(shield_dev, struct thunderstrike, base); + spin_lock(&ts->psy_stats_lock); + prop_values = ts->psy_stats; + spin_unlock(&ts->psy_stats_lock); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = prop_values.status; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = prop_values.charge_type; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + val->intval = prop_values.voltage_min; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 2900000; /* 2.9 V */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = 2200000; /* 2.2 V */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = prop_values.voltage_now; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + val->intval = prop_values.voltage_avg; + break; + case POWER_SUPPLY_PROP_VOLTAGE_BOOT: + val->intval = prop_values.voltage_boot; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = prop_values.capacity; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = prop_values.temp; + break; + case POWER_SUPPLY_PROP_TEMP_MIN: + val->intval = 0; /* 0 C */ + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + val->intval = 400; /* 40 C */ + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + val->intval = 15; /* 1.5 C */ + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + val->intval = 380; /* 38 C */ + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static inline void thunderstrike_request_psy_stats(struct thunderstrike *ts) +{ + set_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags); + schedule_work(&ts->hostcmd_req_work); +} + +static void thunderstrike_psy_stats_timer_handler(struct timer_list *timer) +{ + struct thunderstrike *ts = + container_of(timer, struct thunderstrike, psy_stats_timer); + + thunderstrike_request_psy_stats(ts); + /* Query battery statistics from device every five minutes */ + mod_timer(timer, jiffies + 300 * HZ); +} + static void thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev, __le16 fw_version) @@ -416,13 +602,138 @@ thunderstrike_parse_led_payload(struct shield_device *shield_dev, hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state); } +static void thunderstrike_parse_battery_payload( + struct shield_device *shield_dev, + struct thunderstrike_hostcmd_battery *battery) +{ + struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); + u16 hostcmd_voltage_boot = le16_to_cpu(battery->voltage_boot); + u16 hostcmd_voltage_avg = le16_to_cpu(battery->voltage_avg); + u16 hostcmd_voltage_min = le16_to_cpu(battery->voltage_min); + u16 hostcmd_voltage_now = le16_to_cpu(battery->voltage_now); + u16 hostcmd_thermistor = le16_to_cpu(battery->thermistor); + int voltage_boot, voltage_avg, voltage_min, voltage_now; + struct hid_device *hdev = shield_dev->hdev; + u8 capacity = battery->capacity; + int temp; + + /* Convert thunderstrike device values to µV and tenths of degree Celsius */ + voltage_boot = hostcmd_voltage_boot * 1000; + voltage_avg = hostcmd_voltage_avg * 1000; + voltage_min = hostcmd_voltage_min * 1000; + voltage_now = hostcmd_voltage_now * 1000; + temp = (1378 - (int)hostcmd_thermistor) * 10 / 19; + + /* Copy converted values */ + spin_lock(&ts->psy_stats_lock); + ts->psy_stats.voltage_boot = voltage_boot; + ts->psy_stats.voltage_avg = voltage_avg; + ts->psy_stats.voltage_min = voltage_min; + ts->psy_stats.voltage_now = voltage_now; + ts->psy_stats.capacity = capacity; + ts->psy_stats.temp = temp; + spin_unlock(&ts->psy_stats_lock); + + set_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags); + + hid_dbg(hdev, + "Thunderstrike battery HOSTCMD response, voltage_avg: %u voltage_now: %u\n", + hostcmd_voltage_avg, hostcmd_voltage_now); + hid_dbg(hdev, + "Thunderstrike battery HOSTCMD response, voltage_boot: %u voltage_min: %u\n", + hostcmd_voltage_boot, hostcmd_voltage_min); + hid_dbg(hdev, + "Thunderstrike battery HOSTCMD response, thermistor: %u\n", + hostcmd_thermistor); + hid_dbg(hdev, + "Thunderstrike battery HOSTCMD response, capacity: %u%%\n", + capacity); +} + +static void thunderstrike_parse_charger_payload( + struct shield_device *shield_dev, + struct thunderstrike_hostcmd_charger *charger) +{ + struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); + int charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + struct hid_device *hdev = shield_dev->hdev; + int status = POWER_SUPPLY_STATUS_UNKNOWN; + + switch (charger->type) { + case THUNDERSTRIKE_CHARGER_TYPE_NONE: + charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case THUNDERSTRIKE_CHARGER_TYPE_TRICKLE: + charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case THUNDERSTRIKE_CHARGER_TYPE_NORMAL: + charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + break; + default: + hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD type, %u\n", + charger->type); + break; + } + + switch (charger->state) { + case THUNDERSTRIKE_CHARGER_STATE_UNKNOWN: + status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case THUNDERSTRIKE_CHARGER_STATE_DISABLED: + /* Indicates charger is disconnected */ + break; + case THUNDERSTRIKE_CHARGER_STATE_CHARGING: + status = POWER_SUPPLY_STATUS_CHARGING; + break; + case THUNDERSTRIKE_CHARGER_STATE_FULL: + status = POWER_SUPPLY_STATUS_FULL; + break; + case THUNDERSTRIKE_CHARGER_STATE_FAILED: + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + hid_err(hdev, "Thunderstrike device failed to charge\n"); + break; + default: + hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD state, %u\n", + charger->state); + break; + } + + if (!charger->connected) + status = POWER_SUPPLY_STATUS_DISCHARGING; + + spin_lock(&ts->psy_stats_lock); + ts->psy_stats.charge_type = charge_type; + ts->psy_stats.status = status; + spin_unlock(&ts->psy_stats_lock); + + set_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags); + + hid_dbg(hdev, + "Thunderstrike charger HOSTCMD response, connected: %u, type: %u, state: %u\n", + charger->connected, charger->type, charger->state); +} + +static inline void thunderstrike_device_init_info(struct shield_device *shield_dev) +{ + struct thunderstrike *ts = + container_of(shield_dev, struct thunderstrike, base); + + if (!test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags)) + thunderstrike_request_firmware_version(ts); + + if (!test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) + thunderstrike_request_board_info(ts); + + if (!test_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags) || + !test_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags)) + thunderstrike_psy_stats_timer_handler(&ts->psy_stats_timer); +} + static int thunderstrike_parse_report(struct shield_device *shield_dev, struct hid_report *report, u8 *data, int size) { struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report; - struct thunderstrike *ts = - container_of(shield_dev, struct thunderstrike, base); struct hid_device *hdev = shield_dev->hdev; switch (report->id) { @@ -445,6 +756,10 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev, case THUNDERSTRIKE_HOSTCMD_ID_LED: thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state); break; + case THUNDERSTRIKE_HOSTCMD_ID_BATTERY: + thunderstrike_parse_battery_payload(shield_dev, + &hostcmd_resp_report->battery); + break; case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO: thunderstrike_parse_board_info_payload( shield_dev, &hostcmd_resp_report->board_info); @@ -453,14 +768,17 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev, thunderstrike_parse_haptics_payload( shield_dev, &hostcmd_resp_report->motors); break; - case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT: - case THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT: /* May block HOSTCMD requests till received initially */ - thunderstrike_request_firmware_version(ts); - thunderstrike_request_board_info(ts); - /* Only HOSTCMD that can be triggered without a request */ - return 0; + thunderstrike_device_init_info(shield_dev); + break; + case THUNDERSTRIKE_HOSTCMD_ID_CHARGER: + /* May block HOSTCMD requests till received initially */ + thunderstrike_device_init_info(shield_dev); + + thunderstrike_parse_charger_payload( + shield_dev, &hostcmd_resp_report->charger); + break; default: hid_warn(hdev, "Unhandled Thunderstrike HOSTCMD id %d\n", @@ -480,7 +798,8 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts) { struct led_classdev *led = &ts->led_dev; - led->name = "thunderstrike:blue:led"; + led->name = devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL, + "thunderstrike%d:blue:led", ts->id); led->max_brightness = 1; led->flags = LED_CORE_SUSPENDRESUME; led->brightness_get = &thunderstrike_led_get_brightness; @@ -489,6 +808,50 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts) return led_classdev_register(&ts->base.hdev->dev, led); } +static inline int thunderstrike_psy_create(struct shield_device *shield_dev) +{ + struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); + struct power_supply_config psy_cfg = { .drv_data = shield_dev, }; + struct hid_device *hdev = shield_dev->hdev; + int ret; + + /* + * Set an initial capacity and temperature value to avoid prematurely + * triggering alerts. Will be replaced by values queried from initial + * HOSTCMD requests. + */ + ts->psy_stats.capacity = 100; + ts->psy_stats.temp = 182; + + shield_dev->battery_dev.desc.properties = thunderstrike_battery_props; + shield_dev->battery_dev.desc.num_properties = + ARRAY_SIZE(thunderstrike_battery_props); + shield_dev->battery_dev.desc.get_property = thunderstrike_battery_get_property; + shield_dev->battery_dev.desc.type = POWER_SUPPLY_TYPE_BATTERY; + shield_dev->battery_dev.desc.name = + devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL, + "thunderstrike_%d", ts->id); + + shield_dev->battery_dev.psy = power_supply_register( + &hdev->dev, &shield_dev->battery_dev.desc, &psy_cfg); + if (IS_ERR(shield_dev->battery_dev.psy)) { + hid_err(hdev, "Failed to register Thunderstrike battery device\n"); + return PTR_ERR(shield_dev->battery_dev.psy); + } + + ret = power_supply_powers(shield_dev->battery_dev.psy, &hdev->dev); + if (ret) { + hid_err(hdev, "Failed to associate battery device to Thunderstrike\n"); + goto err; + } + + return 0; + +err: + power_supply_unregister(shield_dev->battery_dev.psy); + return ret; +} + static struct shield_device *thunderstrike_create(struct hid_device *hdev) { struct shield_device *shield_dev; @@ -509,26 +872,47 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev) shield_dev->codename = "Thunderstrike"; spin_lock_init(&ts->haptics_update_lock); + spin_lock_init(&ts->psy_stats_lock); INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler); hid_set_drvdata(hdev, shield_dev); + ts->id = ida_alloc(&thunderstrike_ida, GFP_KERNEL); + if (ts->id < 0) + return ERR_PTR(ts->id); + + ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect); + if (IS_ERR(ts->haptics_dev)) { + hid_err(hdev, "Failed to create Thunderstrike haptics instance\n"); + ret = PTR_ERR(ts->haptics_dev); + goto err_id; + } + + ret = thunderstrike_psy_create(shield_dev); + if (ret) { + hid_err(hdev, "Failed to create Thunderstrike power supply instance\n"); + goto err_haptics; + } + ret = thunderstrike_led_create(ts); if (ret) { hid_err(hdev, "Failed to create Thunderstrike LED instance\n"); - return ERR_PTR(ret); + goto err_psy; } - ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect); - if (IS_ERR(ts->haptics_dev)) - goto err; + timer_setup(&ts->psy_stats_timer, thunderstrike_psy_stats_timer_handler, 0); hid_info(hdev, "Registered Thunderstrike controller\n"); return shield_dev; -err: - led_classdev_unregister(&ts->led_dev); - return ERR_CAST(ts->haptics_dev); +err_psy: + power_supply_unregister(shield_dev->battery_dev.psy); +err_haptics: + if (ts->haptics_dev) + input_unregister_device(ts->haptics_dev); +err_id: + ida_free(&thunderstrike_ida, ts->id); + return ERR_PTR(ret); } static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -683,8 +1067,7 @@ static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } - thunderstrike_request_firmware_version(ts); - thunderstrike_request_board_info(ts); + thunderstrike_device_init_info(shield_dev); return ret; @@ -704,9 +1087,12 @@ static void shield_remove(struct hid_device *hdev) ts = container_of(dev, struct thunderstrike, base); hid_hw_close(hdev); - led_classdev_unregister(&ts->led_dev); + power_supply_unregister(dev->battery_dev.psy); if (ts->haptics_dev) input_unregister_device(ts->haptics_dev); + led_classdev_unregister(&ts->led_dev); + ida_free(&thunderstrike_ida, ts->id); + del_timer_sync(&ts->psy_stats_timer); cancel_work_sync(&ts->hostcmd_req_work); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 83237b86c8ff..2eba152e8b90 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -632,7 +632,7 @@ static int sensor_hub_probe(struct hid_device *hdev, } INIT_LIST_HEAD(&hdev->inputs); - ret = hid_hw_start(hdev, 0); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); return ret; diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index aae3afc4107a..43d2cf7153d7 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HID driver for Steelseries SRW-S1 + * HID driver for Steelseries devices * * Copyright (c) 2013 Simon Wood + * Copyright (c) 2023 Bastien Nocera */ /* @@ -11,10 +12,28 @@ #include #include #include +#include #include #include "hid-ids.h" +#define STEELSERIES_SRWS1 BIT(0) +#define STEELSERIES_ARCTIS_1 BIT(1) + +struct steelseries_device { + struct hid_device *hdev; + unsigned long quirks; + + struct delayed_work battery_work; + spinlock_t lock; + bool removed; + + struct power_supply_desc battery_desc; + struct power_supply *battery; + uint8_t battery_capacity; + bool headset_connected; +}; + #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) #define SRWS1_NUMBER_LEDS 15 @@ -353,9 +372,211 @@ static void steelseries_srws1_remove(struct hid_device *hdev) } #endif +#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000 + +#define ARCTIS_1_BATTERY_RESPONSE_LEN 8 +static const char arctis_1_battery_request[] = { 0x06, 0x12 }; + +static int steelseries_headset_arctis_1_fetch_battery(struct hid_device *hdev) +{ + u8 *write_buf; + int ret; + + /* Request battery information */ + write_buf = kmemdup(arctis_1_battery_request, sizeof(arctis_1_battery_request), GFP_KERNEL); + if (!write_buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, arctis_1_battery_request[0], + write_buf, sizeof(arctis_1_battery_request), + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + if (ret < sizeof(arctis_1_battery_request)) { + hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret); + ret = -ENODATA; + } + kfree(write_buf); + return ret; +} + +static void steelseries_headset_fetch_battery(struct hid_device *hdev) +{ + struct steelseries_device *sd = hid_get_drvdata(hdev); + int ret = 0; + + if (sd->quirks & STEELSERIES_ARCTIS_1) + ret = steelseries_headset_arctis_1_fetch_battery(hdev); + + if (ret < 0) + hid_dbg(hdev, + "Battery query failed (err: %d)\n", ret); +} + +static void steelseries_headset_battery_timer_tick(struct work_struct *work) +{ + struct steelseries_device *sd = container_of(work, + struct steelseries_device, battery_work.work); + struct hid_device *hdev = sd->hdev; + + steelseries_headset_fetch_battery(hdev); +} + +static int steelseries_headset_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct steelseries_device *sd = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = sd->headset_connected ? + POWER_SUPPLY_STATUS_DISCHARGING : + POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = sd->battery_capacity; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static void +steelseries_headset_set_wireless_status(struct hid_device *hdev, + bool connected) +{ + struct usb_interface *intf; + + if (!hid_is_usb(hdev)) + return; + + intf = to_usb_interface(hdev->dev.parent); + usb_set_wireless_status(intf, connected ? + USB_WIRELESS_STATUS_CONNECTED : + USB_WIRELESS_STATUS_DISCONNECTED); +} + +static enum power_supply_property steelseries_headset_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static int steelseries_headset_battery_register(struct steelseries_device *sd) +{ + static atomic_t battery_no = ATOMIC_INIT(0); + struct power_supply_config battery_cfg = { .drv_data = sd, }; + unsigned long n; + int ret; + + sd->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + sd->battery_desc.properties = steelseries_headset_battery_props; + sd->battery_desc.num_properties = ARRAY_SIZE(steelseries_headset_battery_props); + sd->battery_desc.get_property = steelseries_headset_battery_get_property; + sd->battery_desc.use_for_apm = 0; + n = atomic_inc_return(&battery_no) - 1; + sd->battery_desc.name = devm_kasprintf(&sd->hdev->dev, GFP_KERNEL, + "steelseries_headset_battery_%ld", n); + if (!sd->battery_desc.name) + return -ENOMEM; + + /* avoid the warning of 0% battery while waiting for the first info */ + steelseries_headset_set_wireless_status(sd->hdev, false); + sd->battery_capacity = 100; + + sd->battery = devm_power_supply_register(&sd->hdev->dev, + &sd->battery_desc, &battery_cfg); + if (IS_ERR(sd->battery)) { + ret = PTR_ERR(sd->battery); + hid_err(sd->hdev, + "%s:power_supply_register failed with error %d\n", + __func__, ret); + return ret; + } + power_supply_powers(sd->battery, &sd->hdev->dev); + + INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_tick); + steelseries_headset_fetch_battery(sd->hdev); + + return 0; +} + +static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct steelseries_device *sd; + int ret; + + sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + hid_set_drvdata(hdev, sd); + sd->hdev = hdev; + sd->quirks = id->driver_data; + + if (sd->quirks & STEELSERIES_SRWS1) { +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + return steelseries_srws1_probe(hdev, id); +#else + return -ENODEV; +#endif + } + + ret = hid_parse(hdev); + if (ret) + return ret; + + spin_lock_init(&sd->lock); + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) + return ret; + + if (steelseries_headset_battery_register(sd) < 0) + hid_err(sd->hdev, + "Failed to register battery for headset\n"); + + return ret; +} + +static void steelseries_remove(struct hid_device *hdev) +{ + struct steelseries_device *sd = hid_get_drvdata(hdev); + unsigned long flags; + + if (sd->quirks & STEELSERIES_SRWS1) { +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + steelseries_srws1_remove(hdev); +#endif + return; + } + + spin_lock_irqsave(&sd->lock, flags); + sd->removed = true; + spin_unlock_irqrestore(&sd->lock, flags); + + cancel_delayed_work_sync(&sd->battery_work); + + hid_hw_stop(hdev); +} + static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { + if (hdev->vendor != USB_VENDOR_ID_STEELSERIES || + hdev->product != USB_DEVICE_ID_STEELSERIES_SRWS1) + return rdesc; + if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8 && rdesc[29] == 0xbb && rdesc[40] == 0xc5) { hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); @@ -365,22 +586,82 @@ static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc return rdesc; } -static const struct hid_device_id steelseries_srws1_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, +static int steelseries_headset_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *read_buf, + int size) +{ + struct steelseries_device *sd = hid_get_drvdata(hdev); + int capacity = sd->battery_capacity; + bool connected = sd->headset_connected; + unsigned long flags; + + /* Not a headset */ + if (sd->quirks & STEELSERIES_SRWS1) + return 0; + + if (sd->quirks & STEELSERIES_ARCTIS_1) { + hid_dbg(sd->hdev, + "Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf); + if (size < ARCTIS_1_BATTERY_RESPONSE_LEN || + memcmp (read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_request))) + return 0; + if (read_buf[2] == 0x01) { + connected = false; + capacity = 100; + } else { + connected = true; + capacity = read_buf[3]; + } + } + + if (connected != sd->headset_connected) { + hid_dbg(sd->hdev, + "Connected status changed from %sconnected to %sconnected\n", + sd->headset_connected ? "" : "not ", + connected ? "" : "not "); + sd->headset_connected = connected; + steelseries_headset_set_wireless_status(hdev, connected); + } + + if (capacity != sd->battery_capacity) { + hid_dbg(sd->hdev, + "Battery capacity changed from %d%% to %d%%\n", + sd->battery_capacity, capacity); + sd->battery_capacity = capacity; + power_supply_changed(sd->battery); + } + + spin_lock_irqsave(&sd->lock, flags); + if (!sd->removed) + schedule_delayed_work(&sd->battery_work, + msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS)); + spin_unlock_irqrestore(&sd->lock, flags); + + return 0; +} + +static const struct hid_device_id steelseries_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1), + .driver_data = STEELSERIES_SRWS1 }, + + { /* SteelSeries Arctis 1 Wireless for XBox */ + HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12b6), + .driver_data = STEELSERIES_ARCTIS_1 }, + { } }; -MODULE_DEVICE_TABLE(hid, steelseries_srws1_devices); +MODULE_DEVICE_TABLE(hid, steelseries_devices); -static struct hid_driver steelseries_srws1_driver = { - .name = "steelseries_srws1", - .id_table = steelseries_srws1_devices, -#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ - (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) - .probe = steelseries_srws1_probe, - .remove = steelseries_srws1_remove, -#endif - .report_fixup = steelseries_srws1_report_fixup +static struct hid_driver steelseries_driver = { + .name = "steelseries", + .id_table = steelseries_devices, + .probe = steelseries_probe, + .remove = steelseries_remove, + .report_fixup = steelseries_srws1_report_fixup, + .raw_event = steelseries_headset_raw_event, }; -module_hid_driver(steelseries_srws1_driver); +module_hid_driver(steelseries_driver); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bastien Nocera "); +MODULE_AUTHOR("Simon Wood "); diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index f67835f9ed4c..ad74cbc9a0aa 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -85,10 +85,8 @@ static int uclogic_input_configured(struct hid_device *hdev, { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params *params = &drvdata->params; - char *name; const char *suffix = NULL; struct hid_field *field; - size_t len; size_t i; const struct uclogic_params_frame *frame; @@ -146,14 +144,9 @@ static int uclogic_input_configured(struct hid_device *hdev, } } - if (suffix) { - len = strlen(hdev->name) + 2 + strlen(suffix); - name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); - if (name) { - snprintf(name, len, "%s %s", hdev->name, suffix); - hi->input->name = name; - } - } + if (suffix) + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); return 0; } diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index a99dcca2e099..00f9be55f148 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -173,7 +173,6 @@ int wiidebug_init(struct wiimote_data *wdata) { struct wiimote_debug *dbg; unsigned long flags; - int ret = -ENOMEM; dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); if (!dbg) @@ -183,13 +182,9 @@ int wiidebug_init(struct wiimote_data *wdata) dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); - if (!dbg->eeprom) - goto err; dbg->drm = debugfs_create_file("drm", S_IRUSR, dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); - if (!dbg->drm) - goto err_drm; spin_lock_irqsave(&wdata->state.lock, flags); wdata->debug = dbg; @@ -197,11 +192,6 @@ int wiidebug_init(struct wiimote_data *wdata) return 0; -err_drm: - debugfs_remove(dbg->eeprom); -err: - kfree(dbg); - return ret; } void wiidebug_deinit(struct wiimote_data *wdata) diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index 029045d9661c..31abab57ad44 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -18,9 +18,11 @@ #include "i2c-hid.h" struct elan_i2c_hid_chip_data { - unsigned int post_gpio_reset_delay_ms; + unsigned int post_gpio_reset_on_delay_ms; + unsigned int post_gpio_reset_off_delay_ms; unsigned int post_power_delay_ms; u16 hid_descriptor_address; + const char *main_supply_name; }; struct i2c_hid_of_elan { @@ -38,9 +40,11 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) container_of(ops, struct i2c_hid_of_elan, ops); int ret; - ret = regulator_enable(ihid_elan->vcc33); - if (ret) - return ret; + if (ihid_elan->vcc33) { + ret = regulator_enable(ihid_elan->vcc33); + if (ret) + return ret; + } ret = regulator_enable(ihid_elan->vccio); if (ret) { @@ -52,8 +56,8 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) msleep(ihid_elan->chip_data->post_power_delay_ms); gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); - if (ihid_elan->chip_data->post_gpio_reset_delay_ms) - msleep(ihid_elan->chip_data->post_gpio_reset_delay_ms); + if (ihid_elan->chip_data->post_gpio_reset_on_delay_ms) + msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms); return 0; } @@ -64,8 +68,12 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) container_of(ops, struct i2c_hid_of_elan, ops); gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms) + msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms); + regulator_disable(ihid_elan->vccio); - regulator_disable(ihid_elan->vcc33); + if (ihid_elan->vcc33) + regulator_disable(ihid_elan->vcc33); } static int i2c_hid_of_elan_probe(struct i2c_client *client) @@ -89,24 +97,42 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client) if (IS_ERR(ihid_elan->vccio)) return PTR_ERR(ihid_elan->vccio); - ihid_elan->vcc33 = devm_regulator_get(&client->dev, "vcc33"); - if (IS_ERR(ihid_elan->vcc33)) - return PTR_ERR(ihid_elan->vcc33); - ihid_elan->chip_data = device_get_match_data(&client->dev); + if (ihid_elan->chip_data->main_supply_name) { + ihid_elan->vcc33 = devm_regulator_get(&client->dev, + ihid_elan->chip_data->main_supply_name); + if (IS_ERR(ihid_elan->vcc33)) + return PTR_ERR(ihid_elan->vcc33); + } + return i2c_hid_core_probe(client, &ihid_elan->ops, ihid_elan->chip_data->hid_descriptor_address, 0); } static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { .post_power_delay_ms = 1, - .post_gpio_reset_delay_ms = 300, + .post_gpio_reset_on_delay_ms = 300, .hid_descriptor_address = 0x0001, + .main_supply_name = "vcc33", +}; + +static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = { + .post_power_delay_ms = 1, + .post_gpio_reset_on_delay_ms = 200, + .post_gpio_reset_off_delay_ms = 65, + .hid_descriptor_address = 0x0001, + /* + * this touchscreen is tightly integrated with the panel and assumes + * that the relevant power rails (other than the IO rail) have already + * been turned on by the panel driver because we're a panel follower. + */ + .main_supply_name = NULL, }; static const struct of_device_id elan_i2c_hid_of_match[] = { { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, + { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data }, { } }; MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 4da50e19808e..166a76c9bcad 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -150,6 +150,7 @@ struct wacom_remote { struct input_dev *input; bool registered; struct wacom_battery battery; + ktime_t active_time; } remotes[WACOM_MAX_REMOTES]; }; diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 76e5353aca0c..3f704b8072e8 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1997,7 +1997,7 @@ static int wacom_initialize_remotes(struct wacom *wacom) spin_lock_init(&remote->remote_lock); error = kfifo_alloc(&remote->remote_fifo, - 5 * sizeof(struct wacom_remote_data), + 5 * sizeof(struct wacom_remote_work_data), GFP_KERNEL); if (error) { hid_err(wacom->hdev, "failed allocating remote_fifo\n"); @@ -2523,6 +2523,18 @@ fail: return; } +static void wacom_remote_destroy_battery(struct wacom *wacom, int index) +{ + struct wacom_remote *remote = wacom->remote; + + if (remote->remotes[index].battery.battery) { + devres_release_group(&wacom->hdev->dev, + &remote->remotes[index].battery.bat_desc); + remote->remotes[index].battery.battery = NULL; + remote->remotes[index].active_time = 0; + } +} + static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index) { struct wacom_remote *remote = wacom->remote; @@ -2537,9 +2549,7 @@ static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index) remote->remotes[i].registered = false; spin_unlock_irqrestore(&remote->remote_lock, flags); - if (remote->remotes[i].battery.battery) - devres_release_group(&wacom->hdev->dev, - &remote->remotes[i].battery.bat_desc); + wacom_remote_destroy_battery(wacom, i); if (remote->remotes[i].group.name) devres_release_group(&wacom->hdev->dev, @@ -2547,7 +2557,6 @@ static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index) remote->remotes[i].serial = 0; remote->remotes[i].group.name = NULL; - remote->remotes[i].battery.battery = NULL; wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN; } } @@ -2632,6 +2641,9 @@ static int wacom_remote_attach_battery(struct wacom *wacom, int index) if (remote->remotes[index].battery.battery) return 0; + if (!remote->remotes[index].active_time) + return 0; + if (wacom->led.groups[index].select == WACOM_STATUS_UNKNOWN) return 0; @@ -2647,17 +2659,19 @@ static void wacom_remote_work(struct work_struct *work) { struct wacom *wacom = container_of(work, struct wacom, remote_work); struct wacom_remote *remote = wacom->remote; - struct wacom_remote_data data; + ktime_t kt = ktime_get(); + struct wacom_remote_work_data remote_work_data; unsigned long flags; unsigned int count; - u32 serial; + u32 work_serial; int i; spin_lock_irqsave(&remote->remote_lock, flags); - count = kfifo_out(&remote->remote_fifo, &data, sizeof(data)); + count = kfifo_out(&remote->remote_fifo, &remote_work_data, + sizeof(remote_work_data)); - if (count != sizeof(data)) { + if (count != sizeof(remote_work_data)) { hid_err(wacom->hdev, "workitem triggered without status available\n"); spin_unlock_irqrestore(&remote->remote_lock, flags); @@ -2670,10 +2684,14 @@ static void wacom_remote_work(struct work_struct *work) spin_unlock_irqrestore(&remote->remote_lock, flags); for (i = 0; i < WACOM_MAX_REMOTES; i++) { - serial = data.remote[i].serial; - if (data.remote[i].connected) { + work_serial = remote_work_data.remote[i].serial; + if (work_serial) { - if (remote->remotes[i].serial == serial) { + if (kt - remote->remotes[i].active_time > WACOM_REMOTE_BATTERY_TIMEOUT + && remote->remotes[i].active_time != 0) + wacom_remote_destroy_battery(wacom, i); + + if (remote->remotes[i].serial == work_serial) { wacom_remote_attach_battery(wacom, i); continue; } @@ -2681,7 +2699,7 @@ static void wacom_remote_work(struct work_struct *work) if (remote->remotes[i].serial) wacom_remote_destroy_one(wacom, i); - wacom_remote_create_one(wacom, serial, i); + wacom_remote_create_one(wacom, work_serial, i); } else if (remote->remotes[i].serial) { wacom_remote_destroy_one(wacom, i); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 174bf03908d7..471db78dbbf0 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1134,6 +1134,7 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) if (index < 0 || !remote->remotes[index].registered) goto out; + remote->remotes[i].active_time = ktime_get(); input = remote->remotes[index].input; input_report_key(input, BTN_0, (data[9] & 0x01)); @@ -1196,22 +1197,20 @@ static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len) struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); unsigned char *data = wacom_wac->data; struct wacom_remote *remote = wacom->remote; - struct wacom_remote_data remote_data; + struct wacom_remote_work_data remote_data; unsigned long flags; int i, ret; if (data[0] != WACOM_REPORT_DEVICE_LIST) return; - memset(&remote_data, 0, sizeof(struct wacom_remote_data)); + memset(&remote_data, 0, sizeof(struct wacom_remote_work_data)); for (i = 0; i < WACOM_MAX_REMOTES; i++) { int j = i * 6; int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4]; - bool connected = data[j+2]; remote_data.remote[i].serial = serial; - remote_data.remote[i].connected = connected; } spin_lock_irqsave(&remote->remote_lock, flags); diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index ee21bb260f22..57e185f18d53 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -13,6 +13,7 @@ #define WACOM_NAME_MAX 64 #define WACOM_MAX_REMOTES 5 #define WACOM_STATUS_UNKNOWN 255 +#define WACOM_REMOTE_BATTERY_TIMEOUT 21000000000ll /* packet length for individual models */ #define WACOM_PKGLEN_BBFUN 9 @@ -327,10 +328,9 @@ struct hid_data { ktime_t time_delayed; }; -struct wacom_remote_data { +struct wacom_remote_work_data { struct { u32 serial; - bool connected; } remote[WACOM_MAX_REMOTES]; }; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 438905e2a1d0..c6d1a345ea6d 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -71,6 +71,15 @@ config I2C_MUX source "drivers/i2c/muxes/Kconfig" +config I2C_ATR + tristate "I2C Address Translator (ATR) support" + help + Enable support for I2C Address Translator (ATR) chips. + + An ATR allows accessing multiple I2C busses from a single + physical bus via address translation instead of bus selection as + i2c-muxes do. + config I2C_HELPER_AUTO bool "Autoselect pertinent helper modules" default y diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index c1d493dc9bac..3f71ce4711e3 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -13,6 +13,7 @@ i2c-core-$(CONFIG_OF) += i2c-core-of.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o +obj-$(CONFIG_I2C_ATR) += i2c-atr.o obj-y += algos/ busses/ muxes/ obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c new file mode 100644 index 000000000000..8ca1daadec93 --- /dev/null +++ b/drivers/i2c/i2c-atr.c @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C Address Translator + * + * Copyright (c) 2019,2022 Luca Ceresoli + * Copyright (c) 2022,2023 Tomi Valkeinen + * + * Originally based on i2c-mux.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ATR_MAX_ADAPTERS 100 /* Just a sanity limit */ +#define ATR_MAX_SYMLINK_LEN 11 /* Longest name is 10 chars: "channel-99" */ + +/** + * struct i2c_atr_alias_pair - Holds the alias assigned to a client. + * @node: List node + * @client: Pointer to the client on the child bus + * @alias: I2C alias address assigned by the driver. + * This is the address that will be used to issue I2C transactions + * on the parent (physical) bus. + */ +struct i2c_atr_alias_pair { + struct list_head node; + const struct i2c_client *client; + u16 alias; +}; + +/** + * struct i2c_atr_chan - Data for a channel. + * @adap: The &struct i2c_adapter for the channel + * @atr: The parent I2C ATR + * @chan_id: The ID of this channel + * @alias_list: List of @struct i2c_atr_alias_pair containing the + * assigned aliases + * @orig_addrs_lock: Mutex protecting @orig_addrs + * @orig_addrs: Buffer used to store the original addresses during transmit + * @orig_addrs_size: Size of @orig_addrs + */ +struct i2c_atr_chan { + struct i2c_adapter adap; + struct i2c_atr *atr; + u32 chan_id; + + struct list_head alias_list; + + /* Lock orig_addrs during xfer */ + struct mutex orig_addrs_lock; + u16 *orig_addrs; + unsigned int orig_addrs_size; +}; + +/** + * struct i2c_atr - The I2C ATR instance + * @parent: The parent &struct i2c_adapter + * @dev: The device that owns the I2C ATR instance + * @ops: &struct i2c_atr_ops + * @priv: Private driver data, set with i2c_atr_set_driver_data() + * @algo: The &struct i2c_algorithm for adapters + * @lock: Lock for the I2C bus segment (see &struct i2c_lock_operations) + * @max_adapters: Maximum number of adapters this I2C ATR can have + * @num_aliases: Number of aliases in the aliases array + * @aliases: The aliases array + * @alias_mask_lock: Lock protecting alias_use_mask + * @alias_use_mask: Bitmask for used aliases in aliases array + * @i2c_nb: Notifier for remote client add & del events + * @adapter: Array of adapters + */ +struct i2c_atr { + struct i2c_adapter *parent; + struct device *dev; + const struct i2c_atr_ops *ops; + + void *priv; + + struct i2c_algorithm algo; + /* lock for the I2C bus segment (see struct i2c_lock_operations) */ + struct mutex lock; + int max_adapters; + + size_t num_aliases; + const u16 *aliases; + /* Protects alias_use_mask */ + spinlock_t alias_mask_lock; + unsigned long *alias_use_mask; + + struct notifier_block i2c_nb; + + struct i2c_adapter *adapter[]; +}; + +static struct i2c_atr_alias_pair * +i2c_atr_find_mapping_by_client(const struct list_head *list, + const struct i2c_client *client) +{ + struct i2c_atr_alias_pair *c2a; + + list_for_each_entry(c2a, list, node) { + if (c2a->client == client) + return c2a; + } + + return NULL; +} + +static struct i2c_atr_alias_pair * +i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr) +{ + struct i2c_atr_alias_pair *c2a; + + list_for_each_entry(c2a, list, node) { + if (c2a->client->addr == phys_addr) + return c2a; + } + + return NULL; +} + +/* + * Replace all message addresses with their aliases, saving the original + * addresses. + * + * This function is internal for use in i2c_atr_master_xfer(). It must be + * followed by i2c_atr_unmap_msgs() to restore the original addresses. + */ +static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, + int num) +{ + struct i2c_atr *atr = chan->atr; + static struct i2c_atr_alias_pair *c2a; + int i; + + /* Ensure we have enough room to save the original addresses */ + if (unlikely(chan->orig_addrs_size < num)) { + u16 *new_buf; + + /* We don't care about old data, hence no realloc() */ + new_buf = kmalloc_array(num, sizeof(*new_buf), GFP_KERNEL); + if (!new_buf) + return -ENOMEM; + + kfree(chan->orig_addrs); + chan->orig_addrs = new_buf; + chan->orig_addrs_size = num; + } + + for (i = 0; i < num; i++) { + chan->orig_addrs[i] = msgs[i].addr; + + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, + msgs[i].addr); + if (!c2a) { + dev_err(atr->dev, "client 0x%02x not mapped!\n", + msgs[i].addr); + + while (i--) + msgs[i].addr = chan->orig_addrs[i]; + + return -ENXIO; + } + + msgs[i].addr = c2a->alias; + } + + return 0; +} + +/* + * Restore all message address aliases with the original addresses. This + * function is internal for use in i2c_atr_master_xfer() and for this reason it + * needs no null and size checks on orig_addr. + * + * @see i2c_atr_map_msgs() + */ +static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs, + int num) +{ + int i; + + for (i = 0; i < num; i++) + msgs[i].addr = chan->orig_addrs[i]; +} + +static int i2c_atr_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_adapter *parent = atr->parent; + int ret; + + /* Translate addresses */ + mutex_lock(&chan->orig_addrs_lock); + + ret = i2c_atr_map_msgs(chan, msgs, num); + if (ret < 0) + goto err_unlock; + + /* Perform the transfer */ + ret = i2c_transfer(parent, msgs, num); + + /* Restore addresses */ + i2c_atr_unmap_msgs(chan, msgs, num); + +err_unlock: + mutex_unlock(&chan->orig_addrs_lock); + + return ret; +} + +static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_adapter *parent = atr->parent; + struct i2c_atr_alias_pair *c2a; + + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr); + if (!c2a) { + dev_err(atr->dev, "client 0x%02x not mapped!\n", addr); + return -ENXIO; + } + + return i2c_smbus_xfer(parent, c2a->alias, flags, read_write, command, + size, data); +} + +static u32 i2c_atr_functionality(struct i2c_adapter *adap) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_adapter *parent = chan->atr->parent; + + return parent->algo->functionality(parent); +} + +static void i2c_atr_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + mutex_lock(&atr->lock); +} + +static int i2c_atr_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + return mutex_trylock(&atr->lock); +} + +static void i2c_atr_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + mutex_unlock(&atr->lock); +} + +static const struct i2c_lock_operations i2c_atr_lock_ops = { + .lock_bus = i2c_atr_lock_bus, + .trylock_bus = i2c_atr_trylock_bus, + .unlock_bus = i2c_atr_unlock_bus, +}; + +static int i2c_atr_reserve_alias(struct i2c_atr *atr) +{ + unsigned long idx; + + spin_lock(&atr->alias_mask_lock); + + idx = find_first_zero_bit(atr->alias_use_mask, atr->num_aliases); + if (idx >= atr->num_aliases) { + spin_unlock(&atr->alias_mask_lock); + dev_err(atr->dev, "failed to find a free alias\n"); + return -EBUSY; + } + + set_bit(idx, atr->alias_use_mask); + + spin_unlock(&atr->alias_mask_lock); + + return atr->aliases[idx]; +} + +static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias) +{ + unsigned int idx; + + spin_lock(&atr->alias_mask_lock); + + for (idx = 0; idx < atr->num_aliases; ++idx) { + if (atr->aliases[idx] == alias) { + clear_bit(idx, atr->alias_use_mask); + spin_unlock(&atr->alias_mask_lock); + return; + } + } + + spin_unlock(&atr->alias_mask_lock); + + /* This should never happen */ + dev_warn(atr->dev, "Unable to find mapped alias\n"); +} + +static int i2c_atr_attach_client(struct i2c_adapter *adapter, + const struct i2c_client *client) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_atr_alias_pair *c2a; + u16 alias; + int ret; + + ret = i2c_atr_reserve_alias(atr); + if (ret < 0) + return ret; + + alias = ret; + + c2a = kzalloc(sizeof(*c2a), GFP_KERNEL); + if (!c2a) { + ret = -ENOMEM; + goto err_release_alias; + } + + ret = atr->ops->attach_client(atr, chan->chan_id, client, alias); + if (ret) + goto err_free; + + dev_dbg(atr->dev, "chan%u: client 0x%02x mapped at alias 0x%02x (%s)\n", + chan->chan_id, client->addr, alias, client->name); + + c2a->client = client; + c2a->alias = alias; + list_add(&c2a->node, &chan->alias_list); + + return 0; + +err_free: + kfree(c2a); +err_release_alias: + i2c_atr_release_alias(atr, alias); + + return ret; +} + +static void i2c_atr_detach_client(struct i2c_adapter *adapter, + const struct i2c_client *client) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_atr_alias_pair *c2a; + + atr->ops->detach_client(atr, chan->chan_id, client); + + c2a = i2c_atr_find_mapping_by_client(&chan->alias_list, client); + if (!c2a) { + /* This should never happen */ + dev_warn(atr->dev, "Unable to find address mapping\n"); + return; + } + + i2c_atr_release_alias(atr, c2a->alias); + + dev_dbg(atr->dev, + "chan%u: client 0x%02x unmapped from alias 0x%02x (%s)\n", + chan->chan_id, client->addr, c2a->alias, client->name); + + list_del(&c2a->node); + kfree(c2a); +} + +static int i2c_atr_bus_notifier_call(struct notifier_block *nb, + unsigned long event, void *device) +{ + struct i2c_atr *atr = container_of(nb, struct i2c_atr, i2c_nb); + struct device *dev = device; + struct i2c_client *client; + u32 chan_id; + int ret; + + client = i2c_verify_client(dev); + if (!client) + return NOTIFY_DONE; + + /* Is the client in one of our adapters? */ + for (chan_id = 0; chan_id < atr->max_adapters; ++chan_id) { + if (client->adapter == atr->adapter[chan_id]) + break; + } + + if (chan_id == atr->max_adapters) + return NOTIFY_DONE; + + switch (event) { + case BUS_NOTIFY_ADD_DEVICE: + ret = i2c_atr_attach_client(client->adapter, client); + if (ret) + dev_err(atr->dev, + "Failed to attach remote client '%s': %d\n", + dev_name(dev), ret); + break; + + case BUS_NOTIFY_DEL_DEVICE: + i2c_atr_detach_client(client->adapter, client); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) +{ + struct device *dev = atr->dev; + unsigned long *alias_use_mask; + size_t num_aliases; + unsigned int i; + u32 *aliases32; + u16 *aliases16; + int ret; + + ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool"); + if (ret < 0) { + dev_err(dev, "Failed to count 'i2c-alias-pool' property: %d\n", + ret); + return ret; + } + + num_aliases = ret; + + if (!num_aliases) + return 0; + + aliases32 = kcalloc(num_aliases, sizeof(*aliases32), GFP_KERNEL); + if (!aliases32) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-alias-pool", + aliases32, num_aliases); + if (ret < 0) { + dev_err(dev, "Failed to read 'i2c-alias-pool' property: %d\n", + ret); + goto err_free_aliases32; + } + + aliases16 = kcalloc(num_aliases, sizeof(*aliases16), GFP_KERNEL); + if (!aliases16) { + ret = -ENOMEM; + goto err_free_aliases32; + } + + for (i = 0; i < num_aliases; i++) { + if (!(aliases32[i] & 0xffff0000)) { + aliases16[i] = aliases32[i]; + continue; + } + + dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n"); + ret = -EINVAL; + goto err_free_aliases16; + } + + alias_use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL); + if (!alias_use_mask) { + ret = -ENOMEM; + goto err_free_aliases16; + } + + kfree(aliases32); + + atr->num_aliases = num_aliases; + atr->aliases = aliases16; + atr->alias_use_mask = alias_use_mask; + + dev_dbg(dev, "i2c-alias-pool has %zu aliases", atr->num_aliases); + + return 0; + +err_free_aliases16: + kfree(aliases16); +err_free_aliases32: + kfree(aliases32); + return ret; +} + +struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, + const struct i2c_atr_ops *ops, int max_adapters) +{ + struct i2c_atr *atr; + int ret; + + if (max_adapters > ATR_MAX_ADAPTERS) + return ERR_PTR(-EINVAL); + + if (!ops || !ops->attach_client || !ops->detach_client) + return ERR_PTR(-EINVAL); + + atr = kzalloc(struct_size(atr, adapter, max_adapters), GFP_KERNEL); + if (!atr) + return ERR_PTR(-ENOMEM); + + mutex_init(&atr->lock); + spin_lock_init(&atr->alias_mask_lock); + + atr->parent = parent; + atr->dev = dev; + atr->ops = ops; + atr->max_adapters = max_adapters; + + if (parent->algo->master_xfer) + atr->algo.master_xfer = i2c_atr_master_xfer; + if (parent->algo->smbus_xfer) + atr->algo.smbus_xfer = i2c_atr_smbus_xfer; + atr->algo.functionality = i2c_atr_functionality; + + ret = i2c_atr_parse_alias_pool(atr); + if (ret) + goto err_destroy_mutex; + + atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call; + ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb); + if (ret) + goto err_free_aliases; + + return atr; + +err_free_aliases: + bitmap_free(atr->alias_use_mask); + kfree(atr->aliases); +err_destroy_mutex: + mutex_destroy(&atr->lock); + kfree(atr); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_new, I2C_ATR); + +void i2c_atr_delete(struct i2c_atr *atr) +{ + unsigned int i; + + for (i = 0; i < atr->max_adapters; ++i) + WARN_ON(atr->adapter[i]); + + bus_unregister_notifier(&i2c_bus_type, &atr->i2c_nb); + bitmap_free(atr->alias_use_mask); + kfree(atr->aliases); + mutex_destroy(&atr->lock); + kfree(atr); +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, I2C_ATR); + +int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, + struct device *adapter_parent, + struct fwnode_handle *bus_handle) +{ + struct i2c_adapter *parent = atr->parent; + struct device *dev = atr->dev; + struct i2c_atr_chan *chan; + char symlink_name[ATR_MAX_SYMLINK_LEN]; + int ret; + + if (chan_id >= atr->max_adapters) { + dev_err(dev, "No room for more i2c-atr adapters\n"); + return -EINVAL; + } + + if (atr->adapter[chan_id]) { + dev_err(dev, "Adapter %d already present\n", chan_id); + return -EEXIST; + } + + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + if (!adapter_parent) + adapter_parent = dev; + + chan->atr = atr; + chan->chan_id = chan_id; + INIT_LIST_HEAD(&chan->alias_list); + mutex_init(&chan->orig_addrs_lock); + + snprintf(chan->adap.name, sizeof(chan->adap.name), "i2c-%d-atr-%d", + i2c_adapter_id(parent), chan_id); + chan->adap.owner = THIS_MODULE; + chan->adap.algo = &atr->algo; + chan->adap.algo_data = chan; + chan->adap.dev.parent = adapter_parent; + chan->adap.retries = parent->retries; + chan->adap.timeout = parent->timeout; + chan->adap.quirks = parent->quirks; + chan->adap.lock_ops = &i2c_atr_lock_ops; + + if (bus_handle) { + device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle)); + } else { + struct fwnode_handle *atr_node; + struct fwnode_handle *child; + u32 reg; + + atr_node = device_get_named_child_node(dev, "i2c-atr"); + + fwnode_for_each_child_node(atr_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) + break; + } + + device_set_node(&chan->adap.dev, child); + fwnode_handle_put(atr_node); + } + + atr->adapter[chan_id] = &chan->adap; + + ret = i2c_add_adapter(&chan->adap); + if (ret) { + dev_err(dev, "failed to add atr-adapter %u (error=%d)\n", + chan_id, ret); + goto err_fwnode_put; + } + + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", + chan->chan_id); + + ret = sysfs_create_link(&chan->adap.dev.kobj, &dev->kobj, "atr_device"); + if (ret) + dev_warn(dev, "can't create symlink to atr device\n"); + ret = sysfs_create_link(&dev->kobj, &chan->adap.dev.kobj, symlink_name); + if (ret) + dev_warn(dev, "can't create symlink for channel %u\n", chan_id); + + dev_dbg(dev, "Added ATR child bus %d\n", i2c_adapter_id(&chan->adap)); + + return 0; + +err_fwnode_put: + fwnode_handle_put(dev_fwnode(&chan->adap.dev)); + mutex_destroy(&chan->orig_addrs_lock); + kfree(chan); + return ret; +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_add_adapter, I2C_ATR); + +void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) +{ + char symlink_name[ATR_MAX_SYMLINK_LEN]; + struct i2c_adapter *adap; + struct i2c_atr_chan *chan; + struct fwnode_handle *fwnode; + struct device *dev = atr->dev; + + adap = atr->adapter[chan_id]; + if (!adap) + return; + + chan = adap->algo_data; + fwnode = dev_fwnode(&adap->dev); + + dev_dbg(dev, "Removing ATR child bus %d\n", i2c_adapter_id(adap)); + + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", + chan->chan_id); + sysfs_remove_link(&dev->kobj, symlink_name); + sysfs_remove_link(&chan->adap.dev.kobj, "atr_device"); + + i2c_del_adapter(adap); + + atr->adapter[chan_id] = NULL; + + fwnode_handle_put(fwnode); + mutex_destroy(&chan->orig_addrs_lock); + kfree(chan->orig_addrs); + kfree(chan); +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_del_adapter, I2C_ATR); + +void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data) +{ + atr->priv = data; +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_set_driver_data, I2C_ATR); + +void *i2c_atr_get_driver_data(struct i2c_atr *atr) +{ + return atr->priv; +} +EXPORT_SYMBOL_NS_GPL(i2c_atr_get_driver_data, I2C_ATR); + +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("I2C Address Translator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 73f913cbd146..7acc0f936dad 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1457,6 +1457,17 @@ static int config_non_roce_gid_cache(struct ib_device *device, i); goto err; } + + if (rdma_protocol_iwarp(device, port)) { + struct net_device *ndev; + + ndev = ib_device_get_netdev(device, port); + if (!ndev) + continue; + RCU_INIT_POINTER(gid_attr.ndev, ndev); + dev_put(ndev); + } + gid_attr.index = i; tprops->subnet_prefix = be64_to_cpu(gid_attr.gid.global.subnet_prefix); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 9891c7dc2af5..c343edf2f664 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -686,30 +686,52 @@ cma_validate_port(struct ib_device *device, u32 port, struct rdma_id_private *id_priv) { struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + const struct ib_gid_attr *sgid_attr = ERR_PTR(-ENODEV); int bound_if_index = dev_addr->bound_dev_if; - const struct ib_gid_attr *sgid_attr; int dev_type = dev_addr->dev_type; struct net_device *ndev = NULL; if (!rdma_dev_access_netns(device, id_priv->id.route.addr.dev_addr.net)) - return ERR_PTR(-ENODEV); + goto out; if ((dev_type == ARPHRD_INFINIBAND) && !rdma_protocol_ib(device, port)) - return ERR_PTR(-ENODEV); + goto out; if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port)) - return ERR_PTR(-ENODEV); + goto out; + + /* + * For drivers that do not associate more than one net device with + * their gid tables, such as iWARP drivers, it is sufficient to + * return the first table entry. + * + * Other driver classes might be included in the future. + */ + if (rdma_protocol_iwarp(device, port)) { + sgid_attr = rdma_get_gid_attr(device, port, 0); + if (IS_ERR(sgid_attr)) + goto out; + + rcu_read_lock(); + ndev = rcu_dereference(sgid_attr->ndev); + if (!net_eq(dev_net(ndev), dev_addr->net) || + ndev->ifindex != bound_if_index) + sgid_attr = ERR_PTR(-ENODEV); + rcu_read_unlock(); + goto out; + } if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) { ndev = dev_get_by_index(dev_addr->net, bound_if_index); if (!ndev) - return ERR_PTR(-ENODEV); + goto out; } else { gid_type = IB_GID_TYPE_IB; } sgid_attr = rdma_find_gid_by_port(device, gid, gid_type, port, ndev); dev_put(ndev); +out: return sgid_attr; } diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 358a2db38d23..eecb369898f5 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -307,7 +307,7 @@ get_remote_info_exit: struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, u8 nl_client, gfp_t gfp) { - struct iwpm_nlmsg_request *nlmsg_request = NULL; + struct iwpm_nlmsg_request *nlmsg_request; unsigned long flags; nlmsg_request = kzalloc(sizeof(struct iwpm_nlmsg_request), gfp); diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 1b2cc9e45ade..ae2db0c70788 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -75,7 +75,7 @@ static bool is_nl_msg_valid(unsigned int type, unsigned int op) if (type >= RDMA_NL_NUM_CLIENTS) return false; - return (op < max_num_ops[type]) ? true : false; + return op < max_num_ops[type]; } static const struct rdma_nl_cbs * diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 7c9c79c13941..bf800f8cb3e4 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -72,12 +72,23 @@ enum { #define IB_UVERBS_BASE_DEV MKDEV(IB_UVERBS_MAJOR, IB_UVERBS_BASE_MINOR) static dev_t dynamic_uverbs_dev; -static struct class *uverbs_class; static DEFINE_IDA(uverbs_ida); static int ib_uverbs_add_one(struct ib_device *device); static void ib_uverbs_remove_one(struct ib_device *device, void *client_data); +static char *uverbs_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev)); +} + +static const struct class uverbs_class = { + .name = "infiniband_verbs", + .devnode = uverbs_devnode, +}; + /* * Must be called with the ufile->device->disassociate_srcu held, and the lock * must be held until use of the ucontext is finished. @@ -1117,7 +1128,7 @@ static int ib_uverbs_add_one(struct ib_device *device) } device_initialize(&uverbs_dev->dev); - uverbs_dev->dev.class = uverbs_class; + uverbs_dev->dev.class = &uverbs_class; uverbs_dev->dev.parent = device->dev.parent; uverbs_dev->dev.release = ib_uverbs_release_dev; uverbs_dev->groups[0] = &dev_attr_group; @@ -1235,13 +1246,6 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data) put_device(&uverbs_dev->dev); } -static char *uverbs_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0666; - return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev)); -} - static int __init ib_uverbs_init(void) { int ret; @@ -1262,16 +1266,13 @@ static int __init ib_uverbs_init(void) goto out_alloc; } - uverbs_class = class_create("infiniband_verbs"); - if (IS_ERR(uverbs_class)) { - ret = PTR_ERR(uverbs_class); + ret = class_register(&uverbs_class); + if (ret) { pr_err("user_verbs: couldn't create class infiniband_verbs\n"); goto out_chrdev; } - uverbs_class->devnode = uverbs_devnode; - - ret = class_create_file(uverbs_class, &class_attr_abi_version.attr); + ret = class_create_file(&uverbs_class, &class_attr_abi_version.attr); if (ret) { pr_err("user_verbs: couldn't create abi_version attribute\n"); goto out_class; @@ -1286,7 +1287,7 @@ static int __init ib_uverbs_init(void) return 0; out_class: - class_destroy(uverbs_class); + class_unregister(&uverbs_class); out_chrdev: unregister_chrdev_region(dynamic_uverbs_dev, @@ -1303,7 +1304,7 @@ out: static void __exit ib_uverbs_cleanup(void) { ib_unregister_client(&uverbs_client); - class_destroy(uverbs_class); + class_unregister(&uverbs_class); unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_NUM_FIXED_MINOR); unregister_chrdev_region(dynamic_uverbs_dev, diff --git a/drivers/infiniband/core/uverbs_std_types_counters.c b/drivers/infiniband/core/uverbs_std_types_counters.c index 999da9c79866..381aa5797641 100644 --- a/drivers/infiniband/core/uverbs_std_types_counters.c +++ b/drivers/infiniband/core/uverbs_std_types_counters.c @@ -107,6 +107,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_COUNTERS_READ)( return ret; uattr = uverbs_attr_get(attrs, UVERBS_ATTR_READ_COUNTERS_BUFF); + if (IS_ERR(uattr)) + return PTR_ERR(uattr); read_attr.ncounters = uattr->ptr_attr.len / sizeof(u64); read_attr.counters_buff = uverbs_zalloc( attrs, array_size(read_attr.ncounters, sizeof(u64))); diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index b99b3cc283b6..41ff5595c860 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1880,6 +1880,89 @@ int ib_modify_qp_with_udata(struct ib_qp *ib_qp, struct ib_qp_attr *attr, } EXPORT_SYMBOL(ib_modify_qp_with_udata); +static void ib_get_width_and_speed(u32 netdev_speed, u32 lanes, + u16 *speed, u8 *width) +{ + if (!lanes) { + if (netdev_speed <= SPEED_1000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_SDR; + } else if (netdev_speed <= SPEED_10000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_FDR10; + } else if (netdev_speed <= SPEED_20000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_DDR; + } else if (netdev_speed <= SPEED_25000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_40000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_FDR10; + } else if (netdev_speed <= SPEED_50000) { + *width = IB_WIDTH_2X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_100000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_200000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_HDR; + } else { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_NDR; + } + + return; + } + + switch (lanes) { + case 1: + *width = IB_WIDTH_1X; + break; + case 2: + *width = IB_WIDTH_2X; + break; + case 4: + *width = IB_WIDTH_4X; + break; + case 8: + *width = IB_WIDTH_8X; + break; + case 12: + *width = IB_WIDTH_12X; + break; + default: + *width = IB_WIDTH_1X; + } + + switch (netdev_speed / lanes) { + case SPEED_2500: + *speed = IB_SPEED_SDR; + break; + case SPEED_5000: + *speed = IB_SPEED_DDR; + break; + case SPEED_10000: + *speed = IB_SPEED_FDR10; + break; + case SPEED_14000: + *speed = IB_SPEED_FDR; + break; + case SPEED_25000: + *speed = IB_SPEED_EDR; + break; + case SPEED_50000: + *speed = IB_SPEED_HDR; + break; + case SPEED_100000: + *speed = IB_SPEED_NDR; + break; + default: + *speed = IB_SPEED_SDR; + } +} + int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) { int rc; @@ -1904,29 +1987,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) netdev_speed = lksettings.base.speed; } else { netdev_speed = SPEED_1000; - pr_warn("%s speed is unknown, defaulting to %u\n", netdev->name, - netdev_speed); + if (rc) + pr_warn("%s speed is unknown, defaulting to %u\n", + netdev->name, netdev_speed); } - if (netdev_speed <= SPEED_1000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_SDR; - } else if (netdev_speed <= SPEED_10000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_FDR10; - } else if (netdev_speed <= SPEED_20000) { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_DDR; - } else if (netdev_speed <= SPEED_25000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_EDR; - } else if (netdev_speed <= SPEED_40000) { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_FDR10; - } else { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_EDR; - } + ib_get_width_and_speed(netdev_speed, lksettings.lanes, + speed, width); return 0; } diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index ea81b2497511..9fd9849ebdd1 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -112,12 +112,34 @@ struct bnxt_re_gsi_context { #define BNXT_RE_NQ_IDX 1 #define BNXT_RE_GEN_P5_MAX_VF 64 +struct bnxt_re_pacing { + u64 dbr_db_fifo_reg_off; + void *dbr_page; + u64 dbr_bar_addr; + u32 pacing_algo_th; + u32 do_pacing_save; + u32 dbq_pacing_time; /* ms */ + u32 dbr_def_do_pacing; + bool dbr_pacing; + struct mutex dbq_lock; /* synchronize db pacing algo */ +}; + +#define BNXT_RE_MAX_DBR_DO_PACING 0xFFFF +#define BNXT_RE_DBR_PACING_TIME 5 /* ms */ +#define BNXT_RE_PACING_ALGO_THRESHOLD 250 /* Entries in DB FIFO */ +#define BNXT_RE_PACING_ALARM_TH_MULTIPLE 2 /* Multiple of pacing algo threshold */ +/* Default do_pacing value when there is no congestion */ +#define BNXT_RE_DBR_DO_PACING_NO_CONGESTION 0x7F /* 1 in 512 probability */ +#define BNXT_RE_DB_FIFO_ROOM_MASK 0x1FFF8000 +#define BNXT_RE_MAX_FIFO_DEPTH 0x2c00 +#define BNXT_RE_DB_FIFO_ROOM_SHIFT 15 +#define BNXT_RE_GRC_FIFO_REG_BASE 0x2000 + struct bnxt_re_dev { struct ib_device ibdev; struct list_head list; unsigned long flags; #define BNXT_RE_FLAG_NETDEV_REGISTERED 0 -#define BNXT_RE_FLAG_GOT_MSIX 2 #define BNXT_RE_FLAG_HAVE_L2_REF 3 #define BNXT_RE_FLAG_RCFW_CHANNEL_EN 4 #define BNXT_RE_FLAG_QOS_WORK_REG 5 @@ -152,16 +174,9 @@ struct bnxt_re_dev { struct bnxt_qplib_res qplib_res; struct bnxt_qplib_dpi dpi_privileged; - atomic_t qp_count; struct mutex qp_lock; /* protect qp list */ struct list_head qp_list; - atomic_t cq_count; - atomic_t srq_count; - atomic_t mr_count; - atomic_t mw_count; - atomic_t ah_count; - atomic_t pd_count; /* Max of 2 lossless traffic class supported per port */ u16 cosq[2]; @@ -171,6 +186,9 @@ struct bnxt_re_dev { atomic_t nq_alloc_cnt; u32 is_virtfn; u32 num_vfs; + struct bnxt_re_pacing pacing; + struct work_struct dbq_fifo_check_work; + struct delayed_work dbq_pacing_work; }; #define to_bnxt_re_dev(ptr, member) \ @@ -181,6 +199,7 @@ struct bnxt_re_dev { #define BNXT_RE_ROCEV2_IPV6_PACKET 3 #define BNXT_RE_CHECK_RC(x) ((x) && ((x) != -ETIMEDOUT)) +void bnxt_re_pacing_alert(struct bnxt_re_dev *rdev); static inline struct device *rdev_to_dev(struct bnxt_re_dev *rdev) { diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.c b/drivers/infiniband/hw/bnxt_re/hw_counters.c index 825d512799d9..93572405d6fa 100644 --- a/drivers/infiniband/hw/bnxt_re/hw_counters.c +++ b/drivers/infiniband/hw/bnxt_re/hw_counters.c @@ -61,15 +61,29 @@ static const struct rdma_stat_desc bnxt_re_stat_descs[] = { [BNXT_RE_ACTIVE_PD].name = "active_pds", [BNXT_RE_ACTIVE_AH].name = "active_ahs", [BNXT_RE_ACTIVE_QP].name = "active_qps", + [BNXT_RE_ACTIVE_RC_QP].name = "active_rc_qps", + [BNXT_RE_ACTIVE_UD_QP].name = "active_ud_qps", [BNXT_RE_ACTIVE_SRQ].name = "active_srqs", [BNXT_RE_ACTIVE_CQ].name = "active_cqs", [BNXT_RE_ACTIVE_MR].name = "active_mrs", [BNXT_RE_ACTIVE_MW].name = "active_mws", + [BNXT_RE_WATERMARK_PD].name = "watermark_pds", + [BNXT_RE_WATERMARK_AH].name = "watermark_ahs", + [BNXT_RE_WATERMARK_QP].name = "watermark_qps", + [BNXT_RE_WATERMARK_RC_QP].name = "watermark_rc_qps", + [BNXT_RE_WATERMARK_UD_QP].name = "watermark_ud_qps", + [BNXT_RE_WATERMARK_SRQ].name = "watermark_srqs", + [BNXT_RE_WATERMARK_CQ].name = "watermark_cqs", + [BNXT_RE_WATERMARK_MR].name = "watermark_mrs", + [BNXT_RE_WATERMARK_MW].name = "watermark_mws", + [BNXT_RE_RESIZE_CQ_CNT].name = "resize_cq_cnt", [BNXT_RE_RX_PKTS].name = "rx_pkts", [BNXT_RE_RX_BYTES].name = "rx_bytes", [BNXT_RE_TX_PKTS].name = "tx_pkts", [BNXT_RE_TX_BYTES].name = "tx_bytes", [BNXT_RE_RECOVERABLE_ERRORS].name = "recoverable_errors", + [BNXT_RE_TX_ERRORS].name = "tx_roce_errors", + [BNXT_RE_TX_DISCARDS].name = "tx_roce_discards", [BNXT_RE_RX_ERRORS].name = "rx_roce_errors", [BNXT_RE_RX_DISCARDS].name = "rx_roce_discards", [BNXT_RE_TO_RETRANSMITS].name = "to_retransmits", @@ -117,14 +131,25 @@ static const struct rdma_stat_desc bnxt_re_stat_descs[] = { [BNXT_RE_TX_READ_RES].name = "tx_read_resp", [BNXT_RE_TX_WRITE_REQ].name = "tx_write_req", [BNXT_RE_TX_SEND_REQ].name = "tx_send_req", + [BNXT_RE_TX_ROCE_PKTS].name = "tx_roce_only_pkts", + [BNXT_RE_TX_ROCE_BYTES].name = "tx_roce_only_bytes", [BNXT_RE_RX_ATOMIC_REQ].name = "rx_atomic_req", [BNXT_RE_RX_READ_REQ].name = "rx_read_req", [BNXT_RE_RX_READ_RESP].name = "rx_read_resp", [BNXT_RE_RX_WRITE_REQ].name = "rx_write_req", [BNXT_RE_RX_SEND_REQ].name = "rx_send_req", + [BNXT_RE_RX_ROCE_PKTS].name = "rx_roce_only_pkts", + [BNXT_RE_RX_ROCE_BYTES].name = "rx_roce_only_bytes", [BNXT_RE_RX_ROCE_GOOD_PKTS].name = "rx_roce_good_pkts", [BNXT_RE_RX_ROCE_GOOD_BYTES].name = "rx_roce_good_bytes", - [BNXT_RE_OOB].name = "rx_out_of_buffer" + [BNXT_RE_OOB].name = "rx_out_of_buffer", + [BNXT_RE_TX_CNP].name = "tx_cnp_pkts", + [BNXT_RE_RX_CNP].name = "rx_cnp_pkts", + [BNXT_RE_RX_ECN].name = "rx_ecn_marked_pkts", + [BNXT_RE_PACING_RESCHED].name = "pacing_reschedule", + [BNXT_RE_PACING_CMPL].name = "pacing_complete", + [BNXT_RE_PACING_ALERT].name = "pacing_alerts", + [BNXT_RE_DB_FIFO_REG].name = "db_fifo_register", }; static void bnxt_re_copy_ext_stats(struct bnxt_re_dev *rdev, @@ -136,14 +161,22 @@ static void bnxt_re_copy_ext_stats(struct bnxt_re_dev *rdev, stats->value[BNXT_RE_TX_READ_RES] = s->tx_read_res; stats->value[BNXT_RE_TX_WRITE_REQ] = s->tx_write_req; stats->value[BNXT_RE_TX_SEND_REQ] = s->tx_send_req; + stats->value[BNXT_RE_TX_ROCE_PKTS] = s->tx_roce_pkts; + stats->value[BNXT_RE_TX_ROCE_BYTES] = s->tx_roce_bytes; stats->value[BNXT_RE_RX_ATOMIC_REQ] = s->rx_atomic_req; stats->value[BNXT_RE_RX_READ_REQ] = s->rx_read_req; stats->value[BNXT_RE_RX_READ_RESP] = s->rx_read_res; stats->value[BNXT_RE_RX_WRITE_REQ] = s->rx_write_req; stats->value[BNXT_RE_RX_SEND_REQ] = s->rx_send_req; + stats->value[BNXT_RE_RX_ROCE_PKTS] = s->rx_roce_pkts; + stats->value[BNXT_RE_RX_ROCE_BYTES] = s->rx_roce_bytes; stats->value[BNXT_RE_RX_ROCE_GOOD_PKTS] = s->rx_roce_good_pkts; stats->value[BNXT_RE_RX_ROCE_GOOD_BYTES] = s->rx_roce_good_bytes; stats->value[BNXT_RE_OOB] = s->rx_out_of_buffer; + stats->value[BNXT_RE_TX_CNP] = s->tx_cnp; + stats->value[BNXT_RE_RX_CNP] = s->rx_cnp; + stats->value[BNXT_RE_RX_ECN] = s->rx_ecn_marked; + stats->value[BNXT_RE_OUT_OF_SEQ_ERR] = s->rx_out_of_sequence; } static int bnxt_re_get_ext_stat(struct bnxt_re_dev *rdev, @@ -249,30 +282,59 @@ static void bnxt_re_copy_err_stats(struct bnxt_re_dev *rdev, err_s->res_oos_drop_count; } +static void bnxt_re_copy_db_pacing_stats(struct bnxt_re_dev *rdev, + struct rdma_hw_stats *stats) +{ + struct bnxt_re_db_pacing_stats *pacing_s = &rdev->stats.pacing; + + stats->value[BNXT_RE_PACING_RESCHED] = pacing_s->resched; + stats->value[BNXT_RE_PACING_CMPL] = pacing_s->complete; + stats->value[BNXT_RE_PACING_ALERT] = pacing_s->alerts; + stats->value[BNXT_RE_DB_FIFO_REG] = + readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); +} + int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats, u32 port, int index) { struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); - struct ctx_hw_stats *hw_stats = NULL; + struct bnxt_re_res_cntrs *res_s = &rdev->stats.res; struct bnxt_qplib_roce_stats *err_s = NULL; + struct ctx_hw_stats *hw_stats = NULL; int rc = 0; hw_stats = rdev->qplib_ctx.stats.dma; if (!port || !stats) return -EINVAL; - stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&rdev->qp_count); - stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&rdev->srq_count); - stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&rdev->cq_count); - stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&rdev->mr_count); - stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&rdev->mw_count); - stats->value[BNXT_RE_ACTIVE_PD] = atomic_read(&rdev->pd_count); - stats->value[BNXT_RE_ACTIVE_AH] = atomic_read(&rdev->ah_count); + stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&res_s->qp_count); + stats->value[BNXT_RE_ACTIVE_RC_QP] = atomic_read(&res_s->rc_qp_count); + stats->value[BNXT_RE_ACTIVE_UD_QP] = atomic_read(&res_s->ud_qp_count); + stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&res_s->srq_count); + stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&res_s->cq_count); + stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&res_s->mr_count); + stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&res_s->mw_count); + stats->value[BNXT_RE_ACTIVE_PD] = atomic_read(&res_s->pd_count); + stats->value[BNXT_RE_ACTIVE_AH] = atomic_read(&res_s->ah_count); + stats->value[BNXT_RE_WATERMARK_QP] = res_s->qp_watermark; + stats->value[BNXT_RE_WATERMARK_RC_QP] = res_s->rc_qp_watermark; + stats->value[BNXT_RE_WATERMARK_UD_QP] = res_s->ud_qp_watermark; + stats->value[BNXT_RE_WATERMARK_SRQ] = res_s->srq_watermark; + stats->value[BNXT_RE_WATERMARK_CQ] = res_s->cq_watermark; + stats->value[BNXT_RE_WATERMARK_MR] = res_s->mr_watermark; + stats->value[BNXT_RE_WATERMARK_MW] = res_s->mw_watermark; + stats->value[BNXT_RE_WATERMARK_PD] = res_s->pd_watermark; + stats->value[BNXT_RE_WATERMARK_AH] = res_s->ah_watermark; + stats->value[BNXT_RE_RESIZE_CQ_CNT] = atomic_read(&res_s->resize_count); if (hw_stats) { stats->value[BNXT_RE_RECOVERABLE_ERRORS] = le64_to_cpu(hw_stats->tx_bcast_pkts); + stats->value[BNXT_RE_TX_DISCARDS] = + le64_to_cpu(hw_stats->tx_discard_pkts); + stats->value[BNXT_RE_TX_ERRORS] = + le64_to_cpu(hw_stats->tx_error_pkts); stats->value[BNXT_RE_RX_ERRORS] = le64_to_cpu(hw_stats->rx_error_pkts); stats->value[BNXT_RE_RX_DISCARDS] = @@ -294,6 +356,7 @@ int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, &rdev->flags); goto done; } + bnxt_re_copy_err_stats(rdev, stats, err_s); if (_is_ext_stats_supported(rdev->dev_attr.dev_cap_flags) && !rdev->is_virtfn) { rc = bnxt_re_get_ext_stat(rdev, stats); @@ -303,7 +366,8 @@ int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, goto done; } } - bnxt_re_copy_err_stats(rdev, stats, err_s); + if (rdev->pacing.dbr_pacing) + bnxt_re_copy_db_pacing_stats(rdev, stats); } done: diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.h b/drivers/infiniband/hw/bnxt_re/hw_counters.h index 7943b2c393e4..e541b6f8ca9f 100644 --- a/drivers/infiniband/hw/bnxt_re/hw_counters.h +++ b/drivers/infiniband/hw/bnxt_re/hw_counters.h @@ -44,15 +44,29 @@ enum bnxt_re_hw_stats { BNXT_RE_ACTIVE_PD, BNXT_RE_ACTIVE_AH, BNXT_RE_ACTIVE_QP, + BNXT_RE_ACTIVE_RC_QP, + BNXT_RE_ACTIVE_UD_QP, BNXT_RE_ACTIVE_SRQ, BNXT_RE_ACTIVE_CQ, BNXT_RE_ACTIVE_MR, BNXT_RE_ACTIVE_MW, + BNXT_RE_WATERMARK_PD, + BNXT_RE_WATERMARK_AH, + BNXT_RE_WATERMARK_QP, + BNXT_RE_WATERMARK_RC_QP, + BNXT_RE_WATERMARK_UD_QP, + BNXT_RE_WATERMARK_SRQ, + BNXT_RE_WATERMARK_CQ, + BNXT_RE_WATERMARK_MR, + BNXT_RE_WATERMARK_MW, + BNXT_RE_RESIZE_CQ_CNT, BNXT_RE_RX_PKTS, BNXT_RE_RX_BYTES, BNXT_RE_TX_PKTS, BNXT_RE_TX_BYTES, BNXT_RE_RECOVERABLE_ERRORS, + BNXT_RE_TX_ERRORS, + BNXT_RE_TX_DISCARDS, BNXT_RE_RX_ERRORS, BNXT_RE_RX_DISCARDS, BNXT_RE_TO_RETRANSMITS, @@ -100,19 +114,58 @@ enum bnxt_re_hw_stats { BNXT_RE_TX_READ_RES, BNXT_RE_TX_WRITE_REQ, BNXT_RE_TX_SEND_REQ, + BNXT_RE_TX_ROCE_PKTS, + BNXT_RE_TX_ROCE_BYTES, BNXT_RE_RX_ATOMIC_REQ, BNXT_RE_RX_READ_REQ, BNXT_RE_RX_READ_RESP, BNXT_RE_RX_WRITE_REQ, BNXT_RE_RX_SEND_REQ, + BNXT_RE_RX_ROCE_PKTS, + BNXT_RE_RX_ROCE_BYTES, BNXT_RE_RX_ROCE_GOOD_PKTS, BNXT_RE_RX_ROCE_GOOD_BYTES, BNXT_RE_OOB, + BNXT_RE_TX_CNP, + BNXT_RE_RX_CNP, + BNXT_RE_RX_ECN, + BNXT_RE_PACING_RESCHED, + BNXT_RE_PACING_CMPL, + BNXT_RE_PACING_ALERT, + BNXT_RE_DB_FIFO_REG, BNXT_RE_NUM_EXT_COUNTERS }; #define BNXT_RE_NUM_STD_COUNTERS (BNXT_RE_OUT_OF_SEQ_ERR + 1) +struct bnxt_re_db_pacing_stats { + u64 resched; + u64 complete; + u64 alerts; +}; + +struct bnxt_re_res_cntrs { + atomic_t qp_count; + atomic_t rc_qp_count; + atomic_t ud_qp_count; + atomic_t cq_count; + atomic_t srq_count; + atomic_t mr_count; + atomic_t mw_count; + atomic_t ah_count; + atomic_t pd_count; + atomic_t resize_count; + u64 qp_watermark; + u64 rc_qp_watermark; + u64 ud_qp_watermark; + u64 cq_watermark; + u64 srq_watermark; + u64 mr_watermark; + u64 mw_watermark; + u64 ah_watermark; + u64 pd_watermark; +}; + struct bnxt_re_rstat { struct bnxt_qplib_roce_stats errs; struct bnxt_qplib_ext_stat ext_stat; @@ -120,6 +173,8 @@ struct bnxt_re_rstat { struct bnxt_re_stats { struct bnxt_re_rstat rstat; + struct bnxt_re_res_cntrs res; + struct bnxt_re_db_pacing_stats pacing; }; struct rdma_hw_stats *bnxt_re_ib_alloc_hw_port_stats(struct ib_device *ibdev, diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 03cc45a5458d..0848c2c2ffcf 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -284,7 +284,7 @@ int bnxt_re_query_gid(struct ib_device *ibdev, u32 port_num, int index, union ib_gid *gid) { struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); - int rc = 0; + int rc; /* Ignore port_num */ memset(gid, 0, sizeof(*gid)); @@ -565,6 +565,8 @@ bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, break; case BNXT_RE_MMAP_UC_DB: case BNXT_RE_MMAP_WC_DB: + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: ret = rdma_user_mmap_entry_insert(&uctx->ib_uctx, &entry->rdma_entry, PAGE_SIZE); break; @@ -600,7 +602,7 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) if (!bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, &pd->qplib_pd)) - atomic_dec(&rdev->pd_count); + atomic_dec(&rdev->stats.res.pd_count); } return 0; } @@ -613,10 +615,11 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) udata, struct bnxt_re_ucontext, ib_uctx); struct bnxt_re_pd *pd = container_of(ibpd, struct bnxt_re_pd, ib_pd); struct bnxt_re_user_mmap_entry *entry = NULL; + u32 active_pds; int rc = 0; pd->rdev = rdev; - if (bnxt_qplib_alloc_pd(&rdev->qplib_res.pd_tbl, &pd->qplib_pd)) { + if (bnxt_qplib_alloc_pd(&rdev->qplib_res, &pd->qplib_pd)) { ibdev_err(&rdev->ibdev, "Failed to allocate HW PD"); rc = -ENOMEM; goto fail; @@ -663,7 +666,9 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) if (bnxt_re_create_fence_mr(pd)) ibdev_warn(&rdev->ibdev, "Failed to create Fence-MR\n"); - atomic_inc(&rdev->pd_count); + active_pds = atomic_inc_return(&rdev->stats.res.pd_count); + if (active_pds > rdev->stats.res.pd_watermark) + rdev->stats.res.pd_watermark = active_pds; return 0; dbfail: @@ -679,7 +684,7 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags) struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); struct bnxt_re_dev *rdev = ah->rdev; bool block = true; - int rc = 0; + int rc; block = !(flags & RDMA_DESTROY_AH_SLEEPABLE); rc = bnxt_qplib_destroy_ah(&rdev->qplib_res, &ah->qplib_ah, block); @@ -689,7 +694,7 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags) else goto fail; } - atomic_dec(&rdev->ah_count); + atomic_dec(&rdev->stats.res.ah_count); fail: return rc; } @@ -723,6 +728,7 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, const struct ib_gid_attr *sgid_attr; struct bnxt_re_gid_ctx *ctx; struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); + u32 active_ahs; u8 nw_type; int rc; @@ -775,7 +781,9 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, wmb(); /* make sure cache is updated. */ spin_unlock_irqrestore(&uctx->sh_lock, flag); } - atomic_inc(&rdev->ah_count); + active_ahs = atomic_inc_return(&rdev->stats.res.ah_count); + if (active_ahs > rdev->stats.res.ah_watermark) + rdev->stats.res.ah_watermark = active_ahs; return 0; } @@ -826,7 +834,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) struct bnxt_re_qp *gsi_sqp; struct bnxt_re_ah *gsi_sah; struct bnxt_re_dev *rdev; - int rc = 0; + int rc; rdev = qp->rdev; gsi_sqp = rdev->gsi_ctx.gsi_sqp; @@ -836,7 +844,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) bnxt_qplib_destroy_ah(&rdev->qplib_res, &gsi_sah->qplib_ah, true); - atomic_dec(&rdev->ah_count); + atomic_dec(&rdev->stats.res.ah_count); bnxt_qplib_clean_qp(&qp->qplib_qp); ibdev_dbg(&rdev->ibdev, "Destroy the shadow QP\n"); @@ -851,7 +859,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) mutex_lock(&rdev->qp_lock); list_del(&gsi_sqp->list); mutex_unlock(&rdev->qp_lock); - atomic_dec(&rdev->qp_count); + atomic_dec(&rdev->stats.res.qp_count); kfree(rdev->gsi_ctx.sqp_tbl); kfree(gsi_sah); @@ -901,7 +909,7 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) mutex_lock(&rdev->qp_lock); list_del(&qp->list); mutex_unlock(&rdev->qp_lock); - atomic_dec(&rdev->qp_count); + atomic_dec(&rdev->stats.res.qp_count); ib_umem_release(qp->rumem); ib_umem_release(qp->sumem); @@ -1095,7 +1103,7 @@ static struct bnxt_re_ah *bnxt_re_create_shadow_qp_ah "Failed to allocate HW AH for Shadow QP"); goto fail; } - atomic_inc(&rdev->ah_count); + atomic_inc(&rdev->stats.res.ah_count); return ah; @@ -1163,7 +1171,7 @@ static struct bnxt_re_qp *bnxt_re_create_shadow_qp INIT_LIST_HEAD(&qp->list); mutex_lock(&rdev->qp_lock); list_add_tail(&qp->list, &rdev->qp_list); - atomic_inc(&rdev->qp_count); + atomic_inc(&rdev->stats.res.qp_count); mutex_unlock(&rdev->qp_lock); return qp; fail: @@ -1340,8 +1348,7 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, qplqp->pd = &pd->qplib_pd; qplqp->qp_handle = (u64)qplqp; qplqp->max_inline_data = init_attr->cap.max_inline_data; - qplqp->sig_type = ((init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? - true : false); + qplqp->sig_type = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR; qptype = bnxt_re_init_qp_type(rdev, init_attr); if (qptype < 0) { rc = qptype; @@ -1446,7 +1453,7 @@ static int bnxt_re_create_gsi_qp(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, { struct bnxt_re_dev *rdev; struct bnxt_qplib_qp *qplqp; - int rc = 0; + int rc; rdev = qp->rdev; qplqp = &qp->qplib_qp; @@ -1497,6 +1504,7 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + u32 active_qps; int rc; rc = bnxt_re_test_qp_limits(rdev, qp_init_attr, dev_attr); @@ -1545,7 +1553,18 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, mutex_lock(&rdev->qp_lock); list_add_tail(&qp->list, &rdev->qp_list); mutex_unlock(&rdev->qp_lock); - atomic_inc(&rdev->qp_count); + active_qps = atomic_inc_return(&rdev->stats.res.qp_count); + if (active_qps > rdev->stats.res.qp_watermark) + rdev->stats.res.qp_watermark = active_qps; + if (qp_init_attr->qp_type == IB_QPT_RC) { + active_qps = atomic_inc_return(&rdev->stats.res.rc_qp_count); + if (active_qps > rdev->stats.res.rc_qp_watermark) + rdev->stats.res.rc_qp_watermark = active_qps; + } else if (qp_init_attr->qp_type == IB_QPT_UD) { + active_qps = atomic_inc_return(&rdev->stats.res.ud_qp_count); + if (active_qps > rdev->stats.res.ud_qp_watermark) + rdev->stats.res.ud_qp_watermark = active_qps; + } return 0; qp_destroy: @@ -1648,7 +1667,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) nq = qplib_srq->cq->nq; bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); ib_umem_release(srq->umem); - atomic_dec(&rdev->srq_count); + atomic_dec(&rdev->stats.res.srq_count); if (nq) nq->budget--; return 0; @@ -1696,6 +1715,7 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, struct bnxt_re_srq *srq; struct bnxt_re_pd *pd; struct ib_pd *ib_pd; + u32 active_srqs; int rc, entries; ib_pd = ib_srq->pd; @@ -1760,7 +1780,9 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, } if (nq) nq->budget++; - atomic_inc(&rdev->srq_count); + active_srqs = atomic_inc_return(&rdev->stats.res.srq_count); + if (active_srqs > rdev->stats.res.srq_watermark) + rdev->stats.res.srq_watermark = active_srqs; spin_lock_init(&srq->lock); return 0; @@ -1862,7 +1884,7 @@ static int bnxt_re_modify_shadow_qp(struct bnxt_re_dev *rdev, int qp_attr_mask) { struct bnxt_re_qp *qp = rdev->gsi_ctx.gsi_sqp; - int rc = 0; + int rc; if (qp_attr_mask & IB_QP_STATE) { qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE; @@ -2212,7 +2234,7 @@ static int bnxt_re_build_qp1_send_v2(struct bnxt_re_qp *qp, u8 ip_version = 0; u16 vlan_id = 0xFFFF; void *buf; - int i, rc = 0; + int i, rc; memset(&qp->qp1_hdr, 0, sizeof(qp->qp1_hdr)); @@ -2250,7 +2272,7 @@ static int bnxt_re_build_qp1_send_v2(struct bnxt_re_qp *qp, } is_eth = true; - is_vlan = (vlan_id && (vlan_id < 0x1000)) ? true : false; + is_vlan = vlan_id && (vlan_id < 0x1000); ib_ud_header_init(payload_size, !is_eth, is_eth, is_vlan, is_grh, ip_version, is_udp, 0, &qp->qp1_hdr); @@ -2787,7 +2809,6 @@ static int bnxt_re_post_recv_shadow_qp(struct bnxt_re_dev *rdev, struct bnxt_qplib_swqe wqe; int rc = 0; - memset(&wqe, 0, sizeof(wqe)); while (wr) { /* House keeping */ memset(&wqe, 0, sizeof(wqe)); @@ -2886,7 +2907,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); ib_umem_release(cq->umem); - atomic_dec(&rdev->cq_count); + atomic_dec(&rdev->stats.res.cq_count); nq->budget--; kfree(cq->cql); return 0; @@ -2902,6 +2923,7 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, int cqe = attr->cqe; struct bnxt_qplib_nq *nq = NULL; unsigned int nq_alloc_cnt; + u32 active_cqs; if (attr->flags) return -EOPNOTSUPP; @@ -2970,7 +2992,9 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->cq_period = cq->qplib_cq.period; nq->budget++; - atomic_inc(&rdev->cq_count); + active_cqs = atomic_inc_return(&rdev->stats.res.cq_count); + if (active_cqs > rdev->stats.res.cq_watermark) + rdev->stats.res.cq_watermark = active_cqs; spin_lock_init(&cq->cq_lock); if (udata) { @@ -3083,6 +3107,7 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) } cq->ib_cq.cqe = cq->resize_cqe; + atomic_inc(&rdev->stats.res.resize_count); return 0; @@ -3319,26 +3344,21 @@ static int bnxt_re_process_raw_qp_pkt_rx(struct bnxt_re_qp *gsi_qp, struct bnxt_re_dev *rdev = gsi_qp->rdev; struct bnxt_re_sqp_entries *sqp_entry = NULL; struct bnxt_re_qp *gsi_sqp = rdev->gsi_ctx.gsi_sqp; - struct bnxt_re_ah *gsi_sah; - struct ib_send_wr *swr; - struct ib_ud_wr udwr; - struct ib_recv_wr rwr; - int pkt_type = 0; - u32 tbl_idx; - void *rq_hdr_buf; - dma_addr_t rq_hdr_buf_map; dma_addr_t shrq_hdr_buf_map; - u32 offset = 0; + struct ib_sge s_sge[2] = {}; + struct ib_sge r_sge[2] = {}; + struct bnxt_re_ah *gsi_sah; + struct ib_recv_wr rwr = {}; + dma_addr_t rq_hdr_buf_map; + struct ib_ud_wr udwr = {}; + struct ib_send_wr *swr; u32 skip_bytes = 0; - struct ib_sge s_sge[2]; - struct ib_sge r_sge[2]; + int pkt_type = 0; + void *rq_hdr_buf; + u32 offset = 0; + u32 tbl_idx; int rc; - memset(&udwr, 0, sizeof(udwr)); - memset(&rwr, 0, sizeof(rwr)); - memset(&s_sge, 0, sizeof(s_sge)); - memset(&r_sge, 0, sizeof(r_sge)); - swr = &udwr.wr; tbl_idx = cqe->wr_id; @@ -3578,7 +3598,7 @@ static int send_phantom_wqe(struct bnxt_re_qp *qp) { struct bnxt_qplib_qp *lib_qp = &qp->qplib_qp; unsigned long flags; - int rc = 0; + int rc; spin_lock_irqsave(&qp->sq_lock, flags); @@ -3768,6 +3788,7 @@ struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags) struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mr *mr; + u32 active_mrs; int rc; mr = kzalloc(sizeof(*mr), GFP_KERNEL); @@ -3795,7 +3816,9 @@ struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags) if (mr_access_flags & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_ATOMIC)) mr->ib_mr.rkey = mr->ib_mr.lkey; - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; @@ -3828,7 +3851,7 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) ib_umem_release(mr->ib_umem); kfree(mr); - atomic_dec(&rdev->mr_count); + atomic_dec(&rdev->stats.res.mr_count); return rc; } @@ -3858,6 +3881,7 @@ struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mr *mr = NULL; + u32 active_mrs; int rc; if (type != IB_MR_TYPE_MEM_REG) { @@ -3896,7 +3920,9 @@ struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, goto fail_mr; } - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; fail_mr: @@ -3914,6 +3940,7 @@ struct ib_mw *bnxt_re_alloc_mw(struct ib_pd *ib_pd, enum ib_mw_type type, struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mw *mw; + u32 active_mws; int rc; mw = kzalloc(sizeof(*mw), GFP_KERNEL); @@ -3932,7 +3959,9 @@ struct ib_mw *bnxt_re_alloc_mw(struct ib_pd *ib_pd, enum ib_mw_type type, } mw->ib_mw.rkey = mw->qplib_mw.rkey; - atomic_inc(&rdev->mw_count); + active_mws = atomic_inc_return(&rdev->stats.res.mw_count); + if (active_mws > rdev->stats.res.mw_watermark) + rdev->stats.res.mw_watermark = active_mws; return &mw->ib_mw; fail: @@ -3953,21 +3982,19 @@ int bnxt_re_dealloc_mw(struct ib_mw *ib_mw) } kfree(mw); - atomic_dec(&rdev->mw_count); + atomic_dec(&rdev->stats.res.mw_count); return rc; } -/* uverbs */ -struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, - u64 virt_addr, int mr_access_flags, - struct ib_udata *udata) +static struct ib_mr *__bnxt_re_user_reg_mr(struct ib_pd *ib_pd, u64 length, u64 virt_addr, + int mr_access_flags, struct ib_umem *umem) { struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; - struct bnxt_re_mr *mr; - struct ib_umem *umem; unsigned long page_size; + struct bnxt_re_mr *mr; int umem_pgs, rc; + u32 active_mrs; if (length > BNXT_RE_MAX_MR_SIZE) { ibdev_err(&rdev->ibdev, "MR Size: %lld > Max supported:%lld\n", @@ -3975,6 +4002,12 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, return ERR_PTR(-ENOMEM); } + page_size = ib_umem_find_best_pgsz(umem, BNXT_RE_PAGE_SIZE_SUPPORTED, virt_addr); + if (!page_size) { + ibdev_err(&rdev->ibdev, "umem page size unsupported!"); + return ERR_PTR(-EINVAL); + } + mr = kzalloc(sizeof(*mr), GFP_KERNEL); if (!mr) return ERR_PTR(-ENOMEM); @@ -3986,45 +4019,33 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &mr->qplib_mr); if (rc) { - ibdev_err(&rdev->ibdev, "Failed to allocate MR"); + ibdev_err(&rdev->ibdev, "Failed to allocate MR rc = %d", rc); + rc = -EIO; goto free_mr; } /* The fixed portion of the rkey is the same as the lkey */ mr->ib_mr.rkey = mr->qplib_mr.rkey; - - umem = ib_umem_get(&rdev->ibdev, start, length, mr_access_flags); - if (IS_ERR(umem)) { - ibdev_err(&rdev->ibdev, "Failed to get umem"); - rc = -EFAULT; - goto free_mrw; - } mr->ib_umem = umem; - mr->qplib_mr.va = virt_addr; - page_size = ib_umem_find_best_pgsz( - umem, BNXT_RE_PAGE_SIZE_SUPPORTED, virt_addr); - if (!page_size) { - ibdev_err(&rdev->ibdev, "umem page size unsupported!"); - rc = -EFAULT; - goto free_umem; - } mr->qplib_mr.total_size = length; umem_pgs = ib_umem_num_dma_blocks(umem, page_size); rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, umem, umem_pgs, page_size); if (rc) { - ibdev_err(&rdev->ibdev, "Failed to register user MR"); - goto free_umem; + ibdev_err(&rdev->ibdev, "Failed to register user MR - rc = %d\n", rc); + rc = -EIO; + goto free_mrw; } mr->ib_mr.lkey = mr->qplib_mr.lkey; mr->ib_mr.rkey = mr->qplib_mr.lkey; - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; -free_umem: - ib_umem_release(umem); + free_mrw: bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); free_mr: @@ -4032,6 +4053,48 @@ free_mr: return ERR_PTR(rc); } +struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, + struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct ib_umem *umem; + struct ib_mr *ib_mr; + + umem = ib_umem_get(&rdev->ibdev, start, length, mr_access_flags); + if (IS_ERR(umem)) + return ERR_CAST(umem); + + ib_mr = __bnxt_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem); + if (IS_ERR(ib_mr)) + ib_umem_release(umem); + return ib_mr; +} + +struct ib_mr *bnxt_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start, + u64 length, u64 virt_addr, int fd, + int mr_access_flags, struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct ib_umem_dmabuf *umem_dmabuf; + struct ib_umem *umem; + struct ib_mr *ib_mr; + + umem_dmabuf = ib_umem_dmabuf_get_pinned(&rdev->ibdev, start, length, + fd, mr_access_flags); + if (IS_ERR(umem_dmabuf)) + return ERR_CAST(umem_dmabuf); + + umem = &umem_dmabuf->umem; + + ib_mr = __bnxt_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem); + if (IS_ERR(ib_mr)) + ib_umem_release(umem); + return ib_mr; +} + int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) { struct ib_device *ibdev = ctx->device; @@ -4087,6 +4150,8 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) goto cfail; } uctx->shpage_mmap = &entry->rdma_entry; + if (rdev->pacing.dbr_pacing) + resp.comp_mask |= BNXT_RE_UCNTX_CMASK_DBR_PACING_ENABLED; rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp))); if (rc) { @@ -4159,6 +4224,19 @@ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) case BNXT_RE_MMAP_SH_PAGE: ret = vm_insert_page(vma, vma->vm_start, virt_to_page(uctx->shpg)); break; + case BNXT_RE_MMAP_DBR_BAR: + pfn = bnxt_entry->mem_offset >> PAGE_SHIFT; + ret = rdma_user_mmap_io(ib_uctx, vma, pfn, PAGE_SIZE, + pgprot_noncached(vma->vm_page_prot), + rdma_entry); + break; + case BNXT_RE_MMAP_DBR_PAGE: + /* Driver doesn't expect write access for user space */ + if (vma->vm_flags & VM_WRITE) + return -EFAULT; + ret = vm_insert_page(vma, vma->vm_start, + virt_to_page((void *)bnxt_entry->mem_offset)); + break; default: ret = -EINVAL; break; @@ -4178,6 +4256,15 @@ void bnxt_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry) kfree(bnxt_entry); } +static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_ucontext *uctx; + + uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); + bnxt_re_pacing_alert(uctx->rdev); + return 0; +} + static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) { struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); @@ -4190,7 +4277,7 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * u64 mmap_offset; u32 length; u32 dpi; - u64 dbr; + u64 addr; int err; uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); @@ -4212,19 +4299,30 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * return -ENOMEM; length = PAGE_SIZE; dpi = uctx->wcdpi.dpi; - dbr = (u64)uctx->wcdpi.umdbr; + addr = (u64)uctx->wcdpi.umdbr; mmap_flag = BNXT_RE_MMAP_WC_DB; } else { return -EINVAL; } break; + case BNXT_RE_ALLOC_DBR_BAR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_bar_addr; + mmap_flag = BNXT_RE_MMAP_DBR_BAR; + break; + + case BNXT_RE_ALLOC_DBR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_page; + mmap_flag = BNXT_RE_MMAP_DBR_PAGE; + break; default: return -EOPNOTSUPP; } - entry = bnxt_re_mmap_entry_insert(uctx, dbr, mmap_flag, &mmap_offset); + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); if (!entry) return -ENOMEM; @@ -4264,6 +4362,9 @@ static int alloc_page_obj_cleanup(struct ib_uobject *uobject, uctx->wcdpi.dbr = NULL; } break; + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: + break; default: goto exit; } @@ -4301,7 +4402,13 @@ DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); + +DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, + &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); + const struct uapi_definition bnxt_re_uapi_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), {} }; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 32d9e9d09791..84715b7e7a4e 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -146,6 +146,8 @@ enum bnxt_re_mmap_flag { BNXT_RE_MMAP_SH_PAGE, BNXT_RE_MMAP_UC_DB, BNXT_RE_MMAP_WC_DB, + BNXT_RE_MMAP_DBR_PAGE, + BNXT_RE_MMAP_DBR_BAR, }; struct bnxt_re_user_mmap_entry { @@ -227,6 +229,10 @@ int bnxt_re_dealloc_mw(struct ib_mw *mw); struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int mr_access_flags, struct ib_udata *udata); +struct ib_mr *bnxt_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start, + u64 length, u64 virt_addr, + int fd, int mr_access_flags, + struct ib_udata *udata); int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata); void bnxt_re_dealloc_ucontext(struct ib_ucontext *context); int bnxt_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 63e98e2d3596..c9066aade412 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -360,7 +360,7 @@ static struct bnxt_ulp_ops bnxt_re_ulp_ops = { static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev) { struct bnxt_en_dev *en_dev; - int rc = 0; + int rc; en_dev = rdev->en_dev; @@ -395,10 +395,9 @@ static int bnxt_re_hwrm_qcfg(struct bnxt_re_dev *rdev, u32 *db_len, struct bnxt_en_dev *en_dev = rdev->en_dev; struct hwrm_func_qcfg_output resp = {0}; struct hwrm_func_qcfg_input req = {0}; - struct bnxt_fw_msg fw_msg; + struct bnxt_fw_msg fw_msg = {}; int rc; - memset(&fw_msg, 0, sizeof(fw_msg)); bnxt_re_init_hwrm_hdr((void *)&req, HWRM_FUNC_QCFG); req.fid = cpu_to_le16(0xffff); bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, @@ -432,9 +431,219 @@ int bnxt_re_hwrm_qcaps(struct bnxt_re_dev *rdev) return rc; cctx->modes.db_push = le32_to_cpu(resp.flags) & FUNC_QCAPS_RESP_FLAGS_WCB_PUSH_MODE; + cctx->modes.dbr_pacing = + le32_to_cpu(resp.flags_ext2) & + FUNC_QCAPS_RESP_FLAGS_EXT2_DBR_PACING_EXT_SUPPORTED; return 0; } +static int bnxt_re_hwrm_dbr_pacing_qcfg(struct bnxt_re_dev *rdev) +{ + struct hwrm_func_dbr_pacing_qcfg_output resp = {}; + struct hwrm_func_dbr_pacing_qcfg_input req = {}; + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct bnxt_qplib_chip_ctx *cctx; + struct bnxt_fw_msg fw_msg = {}; + int rc; + + cctx = rdev->chip_ctx; + bnxt_re_init_hwrm_hdr((void *)&req, HWRM_FUNC_DBR_PACING_QCFG); + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = bnxt_send_msg(en_dev, &fw_msg); + if (rc) + return rc; + + if ((le32_to_cpu(resp.dbr_stat_db_fifo_reg) & + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK) == + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_GRC) + cctx->dbr_stat_db_fifo = + le32_to_cpu(resp.dbr_stat_db_fifo_reg) & + ~FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK; + return 0; +} + +/* Update the pacing tunable parameters to the default values */ +static void bnxt_re_set_default_pacing_data(struct bnxt_re_dev *rdev) +{ + struct bnxt_qplib_db_pacing_data *pacing_data = rdev->qplib_res.pacing_data; + + pacing_data->do_pacing = rdev->pacing.dbr_def_do_pacing; + pacing_data->pacing_th = rdev->pacing.pacing_algo_th; + pacing_data->alarm_th = + pacing_data->pacing_th * BNXT_RE_PACING_ALARM_TH_MULTIPLE; +} + +static void __wait_for_fifo_occupancy_below_th(struct bnxt_re_dev *rdev) +{ + u32 read_val, fifo_occup; + + /* loop shouldn't run infintely as the occupancy usually goes + * below pacing algo threshold as soon as pacing kicks in. + */ + while (1) { + read_val = readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); + fifo_occup = BNXT_RE_MAX_FIFO_DEPTH - + ((read_val & BNXT_RE_DB_FIFO_ROOM_MASK) >> + BNXT_RE_DB_FIFO_ROOM_SHIFT); + /* Fifo occupancy cannot be greater the MAX FIFO depth */ + if (fifo_occup > BNXT_RE_MAX_FIFO_DEPTH) + break; + + if (fifo_occup < rdev->qplib_res.pacing_data->pacing_th) + break; + } +} + +static void bnxt_re_db_fifo_check(struct work_struct *work) +{ + struct bnxt_re_dev *rdev = container_of(work, struct bnxt_re_dev, + dbq_fifo_check_work); + struct bnxt_qplib_db_pacing_data *pacing_data; + u32 pacing_save; + + if (!mutex_trylock(&rdev->pacing.dbq_lock)) + return; + pacing_data = rdev->qplib_res.pacing_data; + pacing_save = rdev->pacing.do_pacing_save; + __wait_for_fifo_occupancy_below_th(rdev); + cancel_delayed_work_sync(&rdev->dbq_pacing_work); + if (pacing_save > rdev->pacing.dbr_def_do_pacing) { + /* Double the do_pacing value during the congestion */ + pacing_save = pacing_save << 1; + } else { + /* + * when a new congestion is detected increase the do_pacing + * by 8 times. And also increase the pacing_th by 4 times. The + * reason to increase pacing_th is to give more space for the + * queue to oscillate down without getting empty, but also more + * room for the queue to increase without causing another alarm. + */ + pacing_save = pacing_save << 3; + pacing_data->pacing_th = rdev->pacing.pacing_algo_th * 4; + } + + if (pacing_save > BNXT_RE_MAX_DBR_DO_PACING) + pacing_save = BNXT_RE_MAX_DBR_DO_PACING; + + pacing_data->do_pacing = pacing_save; + rdev->pacing.do_pacing_save = pacing_data->do_pacing; + pacing_data->alarm_th = + pacing_data->pacing_th * BNXT_RE_PACING_ALARM_TH_MULTIPLE; + schedule_delayed_work(&rdev->dbq_pacing_work, + msecs_to_jiffies(rdev->pacing.dbq_pacing_time)); + rdev->stats.pacing.alerts++; + mutex_unlock(&rdev->pacing.dbq_lock); +} + +static void bnxt_re_pacing_timer_exp(struct work_struct *work) +{ + struct bnxt_re_dev *rdev = container_of(work, struct bnxt_re_dev, + dbq_pacing_work.work); + struct bnxt_qplib_db_pacing_data *pacing_data; + u32 read_val, fifo_occup; + + if (!mutex_trylock(&rdev->pacing.dbq_lock)) + return; + + pacing_data = rdev->qplib_res.pacing_data; + read_val = readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); + fifo_occup = BNXT_RE_MAX_FIFO_DEPTH - + ((read_val & BNXT_RE_DB_FIFO_ROOM_MASK) >> + BNXT_RE_DB_FIFO_ROOM_SHIFT); + + if (fifo_occup > pacing_data->pacing_th) + goto restart_timer; + + /* + * Instead of immediately going back to the default do_pacing + * reduce it by 1/8 times and restart the timer. + */ + pacing_data->do_pacing = pacing_data->do_pacing - (pacing_data->do_pacing >> 3); + pacing_data->do_pacing = max_t(u32, rdev->pacing.dbr_def_do_pacing, pacing_data->do_pacing); + if (pacing_data->do_pacing <= rdev->pacing.dbr_def_do_pacing) { + bnxt_re_set_default_pacing_data(rdev); + rdev->stats.pacing.complete++; + goto dbq_unlock; + } + +restart_timer: + schedule_delayed_work(&rdev->dbq_pacing_work, + msecs_to_jiffies(rdev->pacing.dbq_pacing_time)); + rdev->stats.pacing.resched++; +dbq_unlock: + rdev->pacing.do_pacing_save = pacing_data->do_pacing; + mutex_unlock(&rdev->pacing.dbq_lock); +} + +void bnxt_re_pacing_alert(struct bnxt_re_dev *rdev) +{ + struct bnxt_qplib_db_pacing_data *pacing_data; + + if (!rdev->pacing.dbr_pacing) + return; + mutex_lock(&rdev->pacing.dbq_lock); + pacing_data = rdev->qplib_res.pacing_data; + + /* + * Increase the alarm_th to max so that other user lib instances do not + * keep alerting the driver. + */ + pacing_data->alarm_th = BNXT_RE_MAX_FIFO_DEPTH; + pacing_data->do_pacing = BNXT_RE_MAX_DBR_DO_PACING; + cancel_work_sync(&rdev->dbq_fifo_check_work); + schedule_work(&rdev->dbq_fifo_check_work); + mutex_unlock(&rdev->pacing.dbq_lock); +} + +static int bnxt_re_initialize_dbr_pacing(struct bnxt_re_dev *rdev) +{ + if (bnxt_re_hwrm_dbr_pacing_qcfg(rdev)) + return -EIO; + + /* Allocate a page for app use */ + rdev->pacing.dbr_page = (void *)__get_free_page(GFP_KERNEL); + if (!rdev->pacing.dbr_page) + return -ENOMEM; + + memset((u8 *)rdev->pacing.dbr_page, 0, PAGE_SIZE); + rdev->qplib_res.pacing_data = (struct bnxt_qplib_db_pacing_data *)rdev->pacing.dbr_page; + + /* MAP HW window 2 for reading db fifo depth */ + writel(rdev->chip_ctx->dbr_stat_db_fifo & BNXT_GRC_BASE_MASK, + rdev->en_dev->bar0 + BNXT_GRCPF_REG_WINDOW_BASE_OUT + 4); + rdev->pacing.dbr_db_fifo_reg_off = + (rdev->chip_ctx->dbr_stat_db_fifo & BNXT_GRC_OFFSET_MASK) + + BNXT_RE_GRC_FIFO_REG_BASE; + rdev->pacing.dbr_bar_addr = + pci_resource_start(rdev->qplib_res.pdev, 0) + rdev->pacing.dbr_db_fifo_reg_off; + + rdev->pacing.pacing_algo_th = BNXT_RE_PACING_ALGO_THRESHOLD; + rdev->pacing.dbq_pacing_time = BNXT_RE_DBR_PACING_TIME; + rdev->pacing.dbr_def_do_pacing = BNXT_RE_DBR_DO_PACING_NO_CONGESTION; + rdev->pacing.do_pacing_save = rdev->pacing.dbr_def_do_pacing; + rdev->qplib_res.pacing_data->fifo_max_depth = BNXT_RE_MAX_FIFO_DEPTH; + rdev->qplib_res.pacing_data->fifo_room_mask = BNXT_RE_DB_FIFO_ROOM_MASK; + rdev->qplib_res.pacing_data->fifo_room_shift = BNXT_RE_DB_FIFO_ROOM_SHIFT; + rdev->qplib_res.pacing_data->grc_reg_offset = rdev->pacing.dbr_db_fifo_reg_off; + bnxt_re_set_default_pacing_data(rdev); + /* Initialize worker for DBR Pacing */ + INIT_WORK(&rdev->dbq_fifo_check_work, bnxt_re_db_fifo_check); + INIT_DELAYED_WORK(&rdev->dbq_pacing_work, bnxt_re_pacing_timer_exp); + return 0; +} + +static void bnxt_re_deinitialize_dbr_pacing(struct bnxt_re_dev *rdev) +{ + cancel_work_sync(&rdev->dbq_fifo_check_work); + cancel_delayed_work_sync(&rdev->dbq_pacing_work); + if (rdev->pacing.dbr_page) + free_page((u64)rdev->pacing.dbr_page); + + rdev->pacing.dbr_page = NULL; + rdev->pacing.dbr_pacing = false; +} + static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id, int type) { @@ -652,6 +861,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .query_qp = bnxt_re_query_qp, .query_srq = bnxt_re_query_srq, .reg_user_mr = bnxt_re_reg_user_mr, + .reg_user_mr_dmabuf = bnxt_re_reg_user_mr_dmabuf, .req_notify_cq = bnxt_re_req_notify_cq, .resize_cq = bnxt_re_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, bnxt_re_ah, ib_ah), @@ -711,13 +921,14 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct bnxt_aux_priv *aux_priv, rdev->id = rdev->en_dev->pdev->devfn; INIT_LIST_HEAD(&rdev->qp_list); mutex_init(&rdev->qp_lock); - atomic_set(&rdev->qp_count, 0); - atomic_set(&rdev->cq_count, 0); - atomic_set(&rdev->srq_count, 0); - atomic_set(&rdev->mr_count, 0); - atomic_set(&rdev->mw_count, 0); - atomic_set(&rdev->ah_count, 0); - atomic_set(&rdev->pd_count, 0); + mutex_init(&rdev->pacing.dbq_lock); + atomic_set(&rdev->stats.res.qp_count, 0); + atomic_set(&rdev->stats.res.cq_count, 0); + atomic_set(&rdev->stats.res.srq_count, 0); + atomic_set(&rdev->stats.res.mr_count, 0); + atomic_set(&rdev->stats.res.mw_count, 0); + atomic_set(&rdev->stats.res.ah_count, 0); + atomic_set(&rdev->stats.res.pd_count, 0); rdev->cosq[0] = 0xFFFF; rdev->cosq[1] = 0xFFFF; @@ -759,7 +970,7 @@ static int bnxt_re_handle_unaffi_async_event(struct creq_func_event static int bnxt_re_handle_qp_async_event(struct creq_qp_event *qp_event, struct bnxt_re_qp *qp) { - struct ib_event event; + struct ib_event event = {}; unsigned int flags; if (qp->qplib_qp.state == CMDQ_MODIFY_QP_NEW_STATE_ERR && @@ -769,7 +980,6 @@ static int bnxt_re_handle_qp_async_event(struct creq_qp_event *qp_event, bnxt_re_unlock_cqs(qp, flags); } - memset(&event, 0, sizeof(event)); if (qp->qplib_qp.srq) { event.device = &qp->rdev->ibdev; event.element.qp = &qp->ib_qp; @@ -937,13 +1147,12 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) { struct bnxt_re_ring_attr rattr = {}; int num_vec_created = 0; - int rc = 0, i; + int rc, i; u8 type; /* Configure and allocate resources for qplib */ rdev->qplib_res.rcfw = &rdev->rcfw; - rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr, - rdev->is_virtfn); + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); if (rc) goto fail; @@ -1090,11 +1299,10 @@ static u32 bnxt_re_get_priority_mask(struct bnxt_re_dev *rdev) { u32 prio_map = 0, tmp_map = 0; struct net_device *netdev; - struct dcb_app app; + struct dcb_app app = {}; netdev = rdev->netdev; - memset(&app, 0, sizeof(app)); app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; app.protocol = ETH_P_IBOE; tmp_map = dcb_ieee_getapp_mask(netdev, &app); @@ -1123,8 +1331,7 @@ static int bnxt_re_setup_qos(struct bnxt_re_dev *rdev) */ if ((prio_map == 0 && rdev->qplib_res.prio) || (prio_map != 0 && !rdev->qplib_res.prio)) { - rdev->qplib_res.prio = prio_map ? true : false; - + rdev->qplib_res.prio = prio_map; bnxt_re_update_gid(rdev); } @@ -1138,7 +1345,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev) struct hwrm_ver_get_input req = {}; struct bnxt_qplib_chip_ctx *cctx; struct bnxt_fw_msg fw_msg = {}; - int rc = 0; + int rc; bnxt_re_init_hwrm_hdr((void *)&req, HWRM_VER_GET); req.hwrm_intf_maj = HWRM_VERSION_MAJOR; @@ -1168,7 +1375,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev) static int bnxt_re_ib_init(struct bnxt_re_dev *rdev) { - int rc = 0; + int rc; u32 event; /* Register ib dev */ @@ -1214,8 +1421,11 @@ static void bnxt_re_dev_uninit(struct bnxt_re_dev *rdev) bnxt_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id, type); bnxt_qplib_free_rcfw_channel(&rdev->rcfw); } - if (test_and_clear_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags)) - rdev->num_msix = 0; + + rdev->num_msix = 0; + + if (rdev->pacing.dbr_pacing) + bnxt_re_deinitialize_dbr_pacing(rdev); bnxt_re_destroy_chip_ctx(rdev); if (test_and_clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags)) @@ -1234,15 +1444,14 @@ static void bnxt_re_worker(struct work_struct *work) static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) { + struct bnxt_re_ring_attr rattr = {}; struct bnxt_qplib_creq_ctx *creq; - struct bnxt_re_ring_attr rattr; u32 db_offt; int vid; u8 type; int rc; /* Registered a new RoCE device instance to netdev */ - memset(&rattr, 0, sizeof(rattr)); rc = bnxt_re_register_netdev(rdev); if (rc) { ibdev_err(&rdev->ibdev, @@ -1271,7 +1480,6 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) ibdev_dbg(&rdev->ibdev, "Got %d MSI-X vectors\n", rdev->en_dev->ulp_tbl->msix_requested); rdev->num_msix = rdev->en_dev->ulp_tbl->msix_requested; - set_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags); bnxt_re_query_hwrm_intf_version(rdev); @@ -1311,8 +1519,17 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) goto free_ring; } - rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr, - rdev->is_virtfn); + if (bnxt_qplib_dbr_pacing_en(rdev->chip_ctx)) { + rc = bnxt_re_initialize_dbr_pacing(rdev); + if (!rc) { + rdev->pacing.dbr_pacing = true; + } else { + ibdev_err(&rdev->ibdev, + "DBR pacing disabled with error : %d\n", rc); + rdev->pacing.dbr_pacing = false; + } + } + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); if (rc) goto disable_rcfw; @@ -1400,7 +1617,7 @@ static int bnxt_re_add_device(struct auxiliary_device *adev, u8 wqe_mode) container_of(adev, struct bnxt_aux_priv, aux_dev); struct bnxt_en_dev *en_dev; struct bnxt_re_dev *rdev; - int rc = 0; + int rc; /* en_dev should never be NULL as long as adev and aux_dev are valid. */ en_dev = aux_priv->edev; @@ -1646,7 +1863,7 @@ static struct auxiliary_driver bnxt_re_driver = { static int __init bnxt_re_mod_init(void) { - int rc = 0; + int rc; pr_info("%s: %s", ROCE_DRV_MODULE_NAME, version); rc = auxiliary_driver_register(&bnxt_re_driver); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index a42555623aed..abbabea7f5fa 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -535,7 +535,7 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, cqn_handler_t cqn_handler, srqn_handler_t srqn_handler) { - int rc = -1; + int rc; nq->pdev = pdev; nq->cqn_handler = cqn_handler; @@ -727,27 +727,30 @@ int bnxt_qplib_query_srq(struct bnxt_qplib_res *res, struct bnxt_qplib_rcfw *rcfw = res->rcfw; struct creq_query_srq_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct creq_query_srq_resp_sb *sb; struct cmdq_query_srq req = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_SRQ, sizeof(req)); /* Configure the request */ - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; req.srq_cid = cpu_to_le32(srq->id); - sb = sbuf->sb; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + sb = sbuf.sb; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); srq->threshold = le16_to_cpu(sb->srq_limit); - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -1365,24 +1368,26 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) struct bnxt_qplib_rcfw *rcfw = res->rcfw; struct creq_query_qp_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct creq_query_qp_resp_sb *sb; struct cmdq_query_qp req = {}; u32 temp32[4]; - int i, rc = 0; + int i, rc; + + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) + return -ENOMEM; + sb = sbuf.sb; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_QP, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) - return -ENOMEM; - sb = sbuf->sb; - req.qp_cid = cpu_to_le32(qp->id); - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -1391,8 +1396,7 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) qp->state = sb->en_sqd_async_notify_state & CREQ_QUERY_QP_RESP_SB_STATE_MASK; qp->en_sqd_async_notify = sb->en_sqd_async_notify_state & - CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY ? - true : false; + CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY; qp->access = sb->access; qp->pkey_index = le16_to_cpu(sb->pkey); qp->qkey = le32_to_cpu(sb->qkey); @@ -1442,7 +1446,8 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) memcpy(qp->smac, sb->src_mac, 6); qp->vlan_id = le16_to_cpu(sb->vlan_pcp_vlan_dei_vlan_id); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index bc3aea4592b9..c8c4017fe405 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -55,7 +55,7 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t); /** * bnxt_qplib_map_rc - map return type based on opcode - * @opcode - roce slow path opcode + * @opcode: roce slow path opcode * * case #1 * Firmware initiated error recovery is a safe state machine and @@ -98,8 +98,8 @@ static int bnxt_qplib_map_rc(u8 opcode) /** * bnxt_re_is_fw_stalled - Check firmware health - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * If firmware has not responded any rcfw command within * rcfw->max_timeout, consider firmware as stalled. @@ -133,8 +133,8 @@ static int bnxt_re_is_fw_stalled(struct bnxt_qplib_rcfw *rcfw, /** * __wait_for_resp - Don't hold the cpu context and wait for response - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * Wait for command completion in sleepable context. * @@ -179,8 +179,8 @@ static int __wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) /** * __block_for_resp - hold the cpu context and wait for response - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * This function will hold the cpu (non-sleepable context) and * wait for command completion. Maximum holding interval is 8 second. @@ -216,8 +216,8 @@ static int __block_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) }; /* __send_message_no_waiter - get cookie and post the message. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * This function will just post and don't bother about completion. * Current design of this function is - @@ -335,7 +335,8 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, cpu_to_le64(sbuf->dma_addr)); __set_cmdq_base_resp_size(msg->req, msg->req_sz, ALIGN(sbuf->size, - BNXT_QPLIB_CMDQE_UNITS)); + BNXT_QPLIB_CMDQE_UNITS) / + BNXT_QPLIB_CMDQE_UNITS); } preq = (u8 *)msg->req; @@ -373,8 +374,8 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, /** * __poll_for_resp - self poll completion for rcfw command - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * It works same as __wait_for_resp except this function will * do self polling in sort interval since interrupt is disabled. @@ -470,8 +471,8 @@ static void __destroy_timedout_ah(struct bnxt_qplib_rcfw *rcfw, /** * __bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * This function does not account shadow queue depth. It will send * all the command unconditionally as long as send queue is not full. @@ -487,7 +488,7 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_crsqe *crsqe; unsigned long flags; u16 cookie; - int rc = 0; + int rc; u8 opcode; opcode = __get_cmdq_base_opcode(msg->req, msg->req_sz); @@ -533,8 +534,8 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, /** * bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * Driver interact with Firmware through rcfw channel/slow path in two ways. * a. Blocking rcfw command send. In this path, driver cannot hold @@ -1195,34 +1196,3 @@ int bnxt_qplib_enable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw, return 0; } - -struct bnxt_qplib_rcfw_sbuf *bnxt_qplib_rcfw_alloc_sbuf( - struct bnxt_qplib_rcfw *rcfw, - u32 size) -{ - struct bnxt_qplib_rcfw_sbuf *sbuf; - - sbuf = kzalloc(sizeof(*sbuf), GFP_KERNEL); - if (!sbuf) - return NULL; - - sbuf->size = size; - sbuf->sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf->size, - &sbuf->dma_addr, GFP_KERNEL); - if (!sbuf->sb) - goto bail; - - return sbuf; -bail: - kfree(sbuf); - return NULL; -} - -void bnxt_qplib_rcfw_free_sbuf(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_rcfw_sbuf *sbuf) -{ - if (sbuf->sb) - dma_free_coherent(&rcfw->pdev->dev, sbuf->size, - sbuf->sb, sbuf->dma_addr); - kfree(sbuf); -} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 739d942761d1..157db6b7e119 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -118,11 +118,11 @@ static int __alloc_pbl(struct bnxt_qplib_res *res, else pages = sginfo->npages; /* page ptr arrays */ - pbl->pg_arr = vmalloc(pages * sizeof(void *)); + pbl->pg_arr = vmalloc_array(pages, sizeof(void *)); if (!pbl->pg_arr) return -ENOMEM; - pbl->pg_map_arr = vmalloc(pages * sizeof(dma_addr_t)); + pbl->pg_map_arr = vmalloc_array(pages, sizeof(dma_addr_t)); if (!pbl->pg_map_arr) { vfree(pbl->pg_arr); pbl->pg_arr = NULL; @@ -385,7 +385,7 @@ static int bnxt_qplib_alloc_tqm_rings(struct bnxt_qplib_res *res, struct bnxt_qplib_hwq_attr hwq_attr = {}; struct bnxt_qplib_sg_info sginfo = {}; struct bnxt_qplib_tqm_ctx *tqmctx; - int rc = 0; + int rc; int i; tqmctx = &ctx->tqm_ctx; @@ -463,7 +463,7 @@ static void bnxt_qplib_map_tqm_pgtbl(struct bnxt_qplib_tqm_ctx *ctx) static int bnxt_qplib_setup_tqm_rings(struct bnxt_qplib_res *res, struct bnxt_qplib_ctx *ctx) { - int rc = 0; + int rc; rc = bnxt_qplib_alloc_tqm_rings(res, ctx); if (rc) @@ -501,7 +501,7 @@ int bnxt_qplib_alloc_ctx(struct bnxt_qplib_res *res, { struct bnxt_qplib_hwq_attr hwq_attr = {}; struct bnxt_qplib_sg_info sginfo = {}; - int rc = 0; + int rc; if (virt_fn || is_p5) goto stats_alloc; @@ -642,31 +642,44 @@ static void bnxt_qplib_init_sgid_tbl(struct bnxt_qplib_sgid_tbl *sgid_tbl, } /* PDs */ -int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pdt, struct bnxt_qplib_pd *pd) +int bnxt_qplib_alloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd *pd) { + struct bnxt_qplib_pd_tbl *pdt = &res->pd_tbl; u32 bit_num; + int rc = 0; + mutex_lock(&res->pd_tbl_lock); bit_num = find_first_bit(pdt->tbl, pdt->max); - if (bit_num == pdt->max) - return -ENOMEM; + if (bit_num == pdt->max) { + rc = -ENOMEM; + goto exit; + } /* Found unused PD */ clear_bit(bit_num, pdt->tbl); pd->id = bit_num; - return 0; +exit: + mutex_unlock(&res->pd_tbl_lock); + return rc; } int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd_tbl *pdt, struct bnxt_qplib_pd *pd) { + int rc = 0; + + mutex_lock(&res->pd_tbl_lock); if (test_and_set_bit(pd->id, pdt->tbl)) { dev_warn(&res->pdev->dev, "Freeing an unused PD? pdn = %d\n", pd->id); - return -EINVAL; + rc = -EINVAL; + goto exit; } pd->id = 0; - return 0; +exit: + mutex_unlock(&res->pd_tbl_lock); + return rc; } static void bnxt_qplib_free_pd_tbl(struct bnxt_qplib_pd_tbl *pdt) @@ -691,6 +704,7 @@ static int bnxt_qplib_alloc_pd_tbl(struct bnxt_qplib_res *res, pdt->max = max; memset((u8 *)pdt->tbl, 0xFF, bytes); + mutex_init(&res->pd_tbl_lock); return 0; } @@ -877,7 +891,7 @@ int bnxt_qplib_alloc_res(struct bnxt_qplib_res *res, struct pci_dev *pdev, struct net_device *netdev, struct bnxt_qplib_dev_attr *dev_attr) { - int rc = 0; + int rc; res->pdev = pdev; res->netdev = netdev; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index d850a553821e..5949f004f785 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -48,6 +48,7 @@ extern const struct bnxt_qplib_gid bnxt_qplib_gid_zero; struct bnxt_qplib_drv_modes { u8 wqe_mode; bool db_push; + bool dbr_pacing; }; struct bnxt_qplib_chip_ctx { @@ -58,6 +59,17 @@ struct bnxt_qplib_chip_ctx { u16 hwrm_cmd_max_timeout; struct bnxt_qplib_drv_modes modes; u64 hwrm_intf_ver; + u32 dbr_stat_db_fifo; +}; + +struct bnxt_qplib_db_pacing_data { + u32 do_pacing; + u32 pacing_th; + u32 alarm_th; + u32 fifo_max_depth; + u32 fifo_room_mask; + u32 fifo_room_shift; + u32 grc_reg_offset; }; #define BNXT_QPLIB_DBR_PF_DB_OFFSET 0x10000 @@ -265,12 +277,15 @@ struct bnxt_qplib_res { struct net_device *netdev; struct bnxt_qplib_rcfw *rcfw; struct bnxt_qplib_pd_tbl pd_tbl; + /* To protect the pd table bit map */ + struct mutex pd_tbl_lock; struct bnxt_qplib_sgid_tbl sgid_tbl; struct bnxt_qplib_dpi_tbl dpi_tbl; /* To protect the dpi table bit map */ struct mutex dpi_tbl_lock; bool prio; bool is_vf; + struct bnxt_qplib_db_pacing_data *pacing_data; }; static inline bool bnxt_qplib_is_chip_gen_p5(struct bnxt_qplib_chip_ctx *cctx) @@ -355,7 +370,7 @@ void bnxt_qplib_free_hwq(struct bnxt_qplib_res *res, struct bnxt_qplib_hwq *hwq); int bnxt_qplib_alloc_init_hwq(struct bnxt_qplib_hwq *hwq, struct bnxt_qplib_hwq_attr *hwq_attr); -int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pd_tbl, +int bnxt_qplib_alloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd *pd); int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd_tbl *pd_tbl, @@ -467,4 +482,10 @@ static inline bool _is_ext_stats_supported(u16 dev_cap_flags) return dev_cap_flags & CREQ_QUERY_FUNC_RESP_SB_EXT_STATS; } + +static inline u8 bnxt_qplib_dbr_pacing_en(struct bnxt_qplib_chip_ctx *cctx) +{ + return cctx->modes.dbr_pacing; +} + #endif /* __BNXT_QPLIB_RES_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index ab45f9d4bb02..a27b68515164 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -72,7 +72,7 @@ static void bnxt_qplib_query_version(struct bnxt_qplib_rcfw *rcfw, struct creq_query_version_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; struct cmdq_query_version req = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_VERSION, @@ -89,31 +89,29 @@ static void bnxt_qplib_query_version(struct bnxt_qplib_rcfw *rcfw, } int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_dev_attr *attr, bool vf) + struct bnxt_qplib_dev_attr *attr) { struct creq_query_func_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; struct creq_query_func_resp_sb *sb; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct cmdq_query_func req = {}; u8 *tqm_alloc; - int i, rc = 0; + int i, rc; u32 temp; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_FUNC, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_FUNC alloc side buffer failed\n"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } - - sb = sbuf->sb; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + sb = sbuf.sb; + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -121,9 +119,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, /* Extract the context from the side buffer */ attr->max_qp = le32_to_cpu(sb->max_qp); - /* max_qp value reported by FW for PF doesn't include the QP1 for PF */ - if (!vf) - attr->max_qp += 1; + /* max_qp value reported by FW doesn't include the QP1 */ + attr->max_qp += 1; attr->max_qp_rd_atom = sb->max_qp_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom; @@ -175,7 +172,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, attr->is_atomic = bnxt_qplib_is_atomic_cap(rcfw); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -186,7 +184,7 @@ int bnxt_qplib_set_func_resources(struct bnxt_qplib_res *res, struct creq_set_func_resources_resp resp = {}; struct cmdq_set_func_resources req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_SET_FUNC_RESOURCES, @@ -718,23 +716,22 @@ int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw, struct creq_query_roce_stats_resp_sb *sb; struct cmdq_query_roce_stats req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; - int rc = 0; + struct bnxt_qplib_rcfw_sbuf sbuf; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_ROCE_STATS, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_ROCE_STATS alloc side buffer failed\n"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } + sb = sbuf.sb; - sb = sbuf->sb; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -790,7 +787,8 @@ int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw, } bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -801,49 +799,56 @@ int bnxt_qplib_qext_stat(struct bnxt_qplib_rcfw *rcfw, u32 fid, struct creq_query_roce_stats_ext_resp_sb *sb; struct cmdq_query_roce_stats_ext req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; int rc; - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_ROCE_STATS_EXT alloc sb failed"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } + sb = sbuf.sb; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_QUERY_ROCE_STATS_EXT_OPCODE_QUERY_ROCE_STATS, sizeof(req)); - req.resp_size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); - req.resp_addr = cpu_to_le64(sbuf->dma_addr); + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + req.resp_addr = cpu_to_le64(sbuf.dma_addr); req.function_id = cpu_to_le32(fid); req.flags = cpu_to_le16(CMDQ_QUERY_ROCE_STATS_EXT_FLAGS_FUNCTION_ID); - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) goto bail; - sb = sbuf->sb; estat->tx_atomic_req = le64_to_cpu(sb->tx_atomic_req_pkts); estat->tx_read_req = le64_to_cpu(sb->tx_read_req_pkts); estat->tx_read_res = le64_to_cpu(sb->tx_read_res_pkts); estat->tx_write_req = le64_to_cpu(sb->tx_write_req_pkts); estat->tx_send_req = le64_to_cpu(sb->tx_send_req_pkts); + estat->tx_roce_pkts = le64_to_cpu(sb->tx_roce_pkts); + estat->tx_roce_bytes = le64_to_cpu(sb->tx_roce_bytes); estat->rx_atomic_req = le64_to_cpu(sb->rx_atomic_req_pkts); estat->rx_read_req = le64_to_cpu(sb->rx_read_req_pkts); estat->rx_read_res = le64_to_cpu(sb->rx_read_res_pkts); estat->rx_write_req = le64_to_cpu(sb->rx_write_req_pkts); estat->rx_send_req = le64_to_cpu(sb->rx_send_req_pkts); + estat->rx_roce_pkts = le64_to_cpu(sb->rx_roce_pkts); + estat->rx_roce_bytes = le64_to_cpu(sb->rx_roce_bytes); estat->rx_roce_good_pkts = le64_to_cpu(sb->rx_roce_good_pkts); estat->rx_roce_good_bytes = le64_to_cpu(sb->rx_roce_good_bytes); estat->rx_out_of_buffer = le64_to_cpu(sb->rx_out_of_buffer_pkts); estat->rx_out_of_sequence = le64_to_cpu(sb->rx_out_of_sequence_pkts); + estat->tx_cnp = le64_to_cpu(sb->tx_cnp_pkts); + estat->rx_cnp = le64_to_cpu(sb->rx_cnp_pkts); + estat->rx_ecn_marked = le64_to_cpu(sb->rx_ecn_marked_pkts); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h index 264ef3cedc45..d33c78b96217 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h @@ -322,7 +322,7 @@ int bnxt_qplib_update_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, struct bnxt_qplib_gid *gid, u16 gid_idx, const u8 *smac); int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_dev_attr *attr, bool vf); + struct bnxt_qplib_dev_attr *attr); int bnxt_qplib_set_func_resources(struct bnxt_qplib_res *res, struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_ctx *ctx); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index ffbd9a89981e..d16d8eaa1415 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -2466,7 +2466,7 @@ int c4iw_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, init_attr->cap.max_send_sge = qhp->attr.sq_max_sges; init_attr->cap.max_recv_sge = qhp->attr.rq_max_sges; init_attr->cap.max_inline_data = T4_MAX_SEND_INLINE; - init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : 0; + init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR; return 0; } diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 4e93ef7f84ee..9c65bd27bae0 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -66,6 +66,7 @@ enum efa_admin_get_stats_type { EFA_ADMIN_GET_STATS_TYPE_BASIC = 0, EFA_ADMIN_GET_STATS_TYPE_MESSAGES = 1, EFA_ADMIN_GET_STATS_TYPE_RDMA_READ = 2, + EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE = 3, }; enum efa_admin_get_stats_scope { @@ -570,6 +571,16 @@ struct efa_admin_rdma_read_stats { u64 read_resp_bytes; }; +struct efa_admin_rdma_write_stats { + u64 write_wrs; + + u64 write_bytes; + + u64 write_wr_err; + + u64 write_recv_bytes; +}; + struct efa_admin_acq_get_stats_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -579,6 +590,8 @@ struct efa_admin_acq_get_stats_resp { struct efa_admin_messages_stats messages_stats; struct efa_admin_rdma_read_stats rdma_read_stats; + + struct efa_admin_rdma_write_stats rdma_write_stats; } u; }; diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 8f8885e002ba..576811885d59 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include "efa_com.h" @@ -794,6 +794,12 @@ int efa_com_get_stats(struct efa_com_dev *edev, result->rdma_read_stats.read_wr_err = resp.u.rdma_read_stats.read_wr_err; result->rdma_read_stats.read_resp_bytes = resp.u.rdma_read_stats.read_resp_bytes; break; + case EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE: + result->rdma_write_stats.write_wrs = resp.u.rdma_write_stats.write_wrs; + result->rdma_write_stats.write_bytes = resp.u.rdma_write_stats.write_bytes; + result->rdma_write_stats.write_wr_err = resp.u.rdma_write_stats.write_wr_err; + result->rdma_write_stats.write_recv_bytes = resp.u.rdma_write_stats.write_recv_bytes; + break; } return 0; diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index 0898ad5bc340..fc97f37bb39b 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_COM_CMD_H_ @@ -262,10 +262,18 @@ struct efa_com_rdma_read_stats { u64 read_resp_bytes; }; +struct efa_com_rdma_write_stats { + u64 write_wrs; + u64 write_bytes; + u64 write_wr_err; + u64 write_recv_bytes; +}; + union efa_com_get_stats_result { struct efa_com_basic_stats basic_stats; struct efa_com_messages_stats messages_stats; struct efa_com_rdma_read_stats rdma_read_stats; + struct efa_com_rdma_write_stats rdma_write_stats; }; int efa_com_create_qp(struct efa_com_dev *edev, diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 2a195c4b0f17..0f8ca99d0827 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -61,6 +61,10 @@ struct efa_user_mmap_entry { op(EFA_RDMA_READ_BYTES, "rdma_read_bytes") \ op(EFA_RDMA_READ_WR_ERR, "rdma_read_wr_err") \ op(EFA_RDMA_READ_RESP_BYTES, "rdma_read_resp_bytes") \ + op(EFA_RDMA_WRITE_WRS, "rdma_write_wrs") \ + op(EFA_RDMA_WRITE_BYTES, "rdma_write_bytes") \ + op(EFA_RDMA_WRITE_WR_ERR, "rdma_write_wr_err") \ + op(EFA_RDMA_WRITE_RECV_BYTES, "rdma_write_recv_bytes") \ #define EFA_STATS_ENUM(ename, name) ename, #define EFA_STATS_STR(ename, nam) \ @@ -449,12 +453,12 @@ int efa_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) ibdev_dbg(&dev->ibdev, "Destroy qp[%u]\n", ibqp->qp_num); - efa_qp_user_mmap_entries_remove(qp); - err = efa_destroy_qp_handle(dev, qp->qp_handle); if (err) return err; + efa_qp_user_mmap_entries_remove(qp); + if (qp->rq_cpu_addr) { ibdev_dbg(&dev->ibdev, "qp->cpu_addr[0x%p] freed: size[%lu], dma[%pad]\n", @@ -1013,8 +1017,8 @@ int efa_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) "Destroy cq[%d] virt[0x%p] freed: size[%lu], dma[%pad]\n", cq->cq_idx, cq->cpu_addr, cq->size, &cq->dma_addr); - efa_cq_user_mmap_entries_remove(cq); efa_destroy_cq_idx(dev, cq->cq_idx); + efa_cq_user_mmap_entries_remove(cq); if (cq->eq) { xa_erase(&dev->cqs_xa, cq->cq_idx); synchronize_irq(cq->eq->irq.irqn); @@ -2080,6 +2084,7 @@ static int efa_fill_port_stats(struct efa_dev *dev, struct rdma_hw_stats *stats, { struct efa_com_get_stats_params params = {}; union efa_com_get_stats_result result; + struct efa_com_rdma_write_stats *rws; struct efa_com_rdma_read_stats *rrs; struct efa_com_messages_stats *ms; struct efa_com_basic_stats *bs; @@ -2121,6 +2126,19 @@ static int efa_fill_port_stats(struct efa_dev *dev, struct rdma_hw_stats *stats, stats->value[EFA_RDMA_READ_WR_ERR] = rrs->read_wr_err; stats->value[EFA_RDMA_READ_RESP_BYTES] = rrs->read_resp_bytes; + if (EFA_DEV_CAP(dev, RDMA_WRITE)) { + params.type = EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE; + err = efa_com_get_stats(&dev->edev, ¶ms, &result); + if (err) + return err; + + rws = &result.rdma_write_stats; + stats->value[EFA_RDMA_WRITE_WRS] = rws->write_wrs; + stats->value[EFA_RDMA_WRITE_BYTES] = rws->write_bytes; + stats->value[EFA_RDMA_WRITE_WR_ERR] = rws->write_wr_err; + stats->value[EFA_RDMA_WRITE_RECV_BYTES] = rws->write_recv_bytes; + } + return ARRAY_SIZE(efa_port_stats_descs); } diff --git a/drivers/infiniband/hw/erdma/erdma_hw.h b/drivers/infiniband/hw/erdma/erdma_hw.h index a882b57aa118..9d316fdc6f9a 100644 --- a/drivers/infiniband/hw/erdma/erdma_hw.h +++ b/drivers/infiniband/hw/erdma/erdma_hw.h @@ -228,7 +228,7 @@ struct erdma_cmdq_ext_db_req { /* create_cq cfg1 */ #define ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK GENMASK(31, 16) -#define ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK BIT(15) +#define ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK BIT(15) #define ERDMA_CMD_CREATE_CQ_MTT_DB_CFG_MASK BIT(11) #define ERDMA_CMD_CREATE_CQ_EQN_MASK GENMASK(9, 0) @@ -248,6 +248,7 @@ struct erdma_cmdq_create_cq_req { /* regmr/deregmr cfg0 */ #define ERDMA_CMD_MR_VALID_MASK BIT(31) +#define ERDMA_CMD_MR_VERSION_MASK GENMASK(30, 28) #define ERDMA_CMD_MR_KEY_MASK GENMASK(27, 20) #define ERDMA_CMD_MR_MPT_IDX_MASK GENMASK(19, 0) @@ -258,7 +259,8 @@ struct erdma_cmdq_create_cq_req { /* regmr cfg2 */ #define ERDMA_CMD_REGMR_PAGESIZE_MASK GENMASK(31, 27) -#define ERDMA_CMD_REGMR_MTT_TYPE_MASK GENMASK(21, 20) +#define ERDMA_CMD_REGMR_MTT_PAGESIZE_MASK GENMASK(26, 24) +#define ERDMA_CMD_REGMR_MTT_LEVEL_MASK GENMASK(21, 20) #define ERDMA_CMD_REGMR_MTT_CNT_MASK GENMASK(19, 0) struct erdma_cmdq_reg_mr_req { @@ -268,7 +270,14 @@ struct erdma_cmdq_reg_mr_req { u64 start_va; u32 size; u32 cfg2; - u64 phy_addr[4]; + union { + u64 phy_addr[4]; + struct { + u64 rsvd; + u32 size_h; + u32 mtt_cnt_h; + }; + }; }; struct erdma_cmdq_dereg_mr_req { @@ -309,7 +318,7 @@ struct erdma_cmdq_modify_qp_req { /* create qp mtt_cfg */ #define ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK GENMASK(31, 12) #define ERDMA_CMD_CREATE_QP_MTT_CNT_MASK GENMASK(11, 1) -#define ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK BIT(0) +#define ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK BIT(0) /* create qp db cfg */ #define ERDMA_CMD_CREATE_QP_SQDB_CFG_MASK GENMASK(31, 16) @@ -364,6 +373,7 @@ struct erdma_cmdq_reflush_req { enum { ERDMA_DEV_CAP_FLAGS_ATOMIC = 1 << 7, + ERDMA_DEV_CAP_FLAGS_MTT_VA = 1 << 5, ERDMA_DEV_CAP_FLAGS_EXTEND_DB = 1 << 3, }; diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c index 44923c51a01b..6d0330badd68 100644 --- a/drivers/infiniband/hw/erdma/erdma_qp.c +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -410,7 +410,7 @@ static int erdma_push_one_sqe(struct erdma_qp *qp, u16 *pi, /* Copy SGLs to SQE content to accelerate */ memcpy(get_queue_entry(qp->kern_qp.sq_buf, idx + 1, qp->attrs.sq_size, SQEBB_SHIFT), - mr->mem.mtt_buf, MTT_SIZE(mr->mem.mtt_nents)); + mr->mem.mtt->buf, MTT_SIZE(mr->mem.mtt_nents)); wqe_size = sizeof(struct erdma_reg_mr_sqe) + MTT_SIZE(mr->mem.mtt_nents); } else { diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 517676fbb8b1..dcccb6015232 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -19,6 +19,23 @@ #include "erdma_cm.h" #include "erdma_verbs.h" +static void assemble_qbuf_mtt_for_cmd(struct erdma_mem *mem, u32 *cfg, + u64 *addr0, u64 *addr1) +{ + struct erdma_mtt *mtt = mem->mtt; + + if (mem->mtt_nents > ERDMA_MAX_INLINE_MTT_ENTRIES) { + *addr0 = mtt->buf_dma; + *cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_1LEVEL); + } else { + *addr0 = mtt->buf[0]; + memcpy(addr1, mtt->buf + 1, MTT_SIZE(mem->mtt_nents - 1)); + *cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); + } +} + static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) { struct erdma_dev *dev = to_edev(qp->ibqp.device); @@ -53,8 +70,8 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) req.sq_mtt_cfg = FIELD_PREP(ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK, 0) | FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, 1) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - ERDMA_MR_INLINE_MTT); + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); req.rq_mtt_cfg = req.sq_mtt_cfg; req.rq_buf_addr = qp->kern_qp.rq_buf_dma_addr; @@ -67,30 +84,28 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) user_qp = &qp->user_qp; req.sq_cqn_mtt_cfg = FIELD_PREP( ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, - ilog2(user_qp->sq_mtt.page_size) - ERDMA_HW_PAGE_SHIFT); + ilog2(user_qp->sq_mem.page_size) - ERDMA_HW_PAGE_SHIFT); req.sq_cqn_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->scq->cqn); req.rq_cqn_mtt_cfg = FIELD_PREP( ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, - ilog2(user_qp->rq_mtt.page_size) - ERDMA_HW_PAGE_SHIFT); + ilog2(user_qp->rq_mem.page_size) - ERDMA_HW_PAGE_SHIFT); req.rq_cqn_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->rcq->cqn); - req.sq_mtt_cfg = user_qp->sq_mtt.page_offset; + req.sq_mtt_cfg = user_qp->sq_mem.page_offset; req.sq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, - user_qp->sq_mtt.mtt_nents) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - user_qp->sq_mtt.mtt_type); + user_qp->sq_mem.mtt_nents); - req.rq_mtt_cfg = user_qp->rq_mtt.page_offset; + req.rq_mtt_cfg = user_qp->rq_mem.page_offset; req.rq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, - user_qp->rq_mtt.mtt_nents) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - user_qp->rq_mtt.mtt_type); + user_qp->rq_mem.mtt_nents); - req.sq_buf_addr = user_qp->sq_mtt.mtt_entry[0]; - req.rq_buf_addr = user_qp->rq_mtt.mtt_entry[0]; + assemble_qbuf_mtt_for_cmd(&user_qp->sq_mem, &req.sq_mtt_cfg, + &req.sq_buf_addr, req.sq_mtt_entry); + assemble_qbuf_mtt_for_cmd(&user_qp->rq_mem, &req.rq_mtt_cfg, + &req.rq_buf_addr, req.rq_mtt_entry); req.sq_db_info_dma_addr = user_qp->sq_db_info_dma_addr; req.rq_db_info_dma_addr = user_qp->rq_db_info_dma_addr; @@ -117,13 +132,27 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) { - struct erdma_cmdq_reg_mr_req req; struct erdma_pd *pd = to_epd(mr->ibmr.pd); - u64 *phy_addr; - int i; + struct erdma_cmdq_reg_mr_req req; + u32 mtt_level; erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, CMDQ_OPCODE_REG_MR); + if (mr->type == ERDMA_MR_TYPE_FRMR || + mr->mem.page_cnt > ERDMA_MAX_INLINE_MTT_ENTRIES) { + if (mr->mem.mtt->continuous) { + req.phy_addr[0] = mr->mem.mtt->buf_dma; + mtt_level = ERDMA_MR_MTT_1LEVEL; + } else { + req.phy_addr[0] = sg_dma_address(mr->mem.mtt->sglist); + mtt_level = mr->mem.mtt->level; + } + } else { + memcpy(req.phy_addr, mr->mem.mtt->buf, + MTT_SIZE(mr->mem.page_cnt)); + mtt_level = ERDMA_MR_MTT_0LEVEL; + } + req.cfg0 = FIELD_PREP(ERDMA_CMD_MR_VALID_MASK, mr->valid) | FIELD_PREP(ERDMA_CMD_MR_KEY_MASK, mr->ibmr.lkey & 0xFF) | FIELD_PREP(ERDMA_CMD_MR_MPT_IDX_MASK, mr->ibmr.lkey >> 8); @@ -132,7 +161,7 @@ static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) FIELD_PREP(ERDMA_CMD_REGMR_RIGHT_MASK, mr->access); req.cfg2 = FIELD_PREP(ERDMA_CMD_REGMR_PAGESIZE_MASK, ilog2(mr->mem.page_size)) | - FIELD_PREP(ERDMA_CMD_REGMR_MTT_TYPE_MASK, mr->mem.mtt_type) | + FIELD_PREP(ERDMA_CMD_REGMR_MTT_LEVEL_MASK, mtt_level) | FIELD_PREP(ERDMA_CMD_REGMR_MTT_CNT_MASK, mr->mem.page_cnt); if (mr->type == ERDMA_MR_TYPE_DMA) @@ -143,14 +172,12 @@ static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) req.size = mr->mem.len; } - if (mr->type == ERDMA_MR_TYPE_FRMR || - mr->mem.mtt_type == ERDMA_MR_INDIRECT_MTT) { - phy_addr = req.phy_addr; - *phy_addr = mr->mem.mtt_entry[0]; - } else { - phy_addr = req.phy_addr; - for (i = 0; i < mr->mem.mtt_nents; i++) - *phy_addr++ = mr->mem.mtt_entry[i]; + if (!mr->mem.mtt->continuous && mr->mem.mtt->level > 1) { + req.cfg0 |= FIELD_PREP(ERDMA_CMD_MR_VERSION_MASK, 1); + req.cfg2 |= FIELD_PREP(ERDMA_CMD_REGMR_MTT_PAGESIZE_MASK, + PAGE_SHIFT - ERDMA_HW_PAGE_SHIFT); + req.size_h = upper_32_bits(mr->mem.len); + req.mtt_cnt_h = mr->mem.page_cnt >> 20; } post_cmd: @@ -161,7 +188,7 @@ static int create_cq_cmd(struct erdma_ucontext *uctx, struct erdma_cq *cq) { struct erdma_dev *dev = to_edev(cq->ibcq.device); struct erdma_cmdq_create_cq_req req; - struct erdma_mem *mtt; + struct erdma_mem *mem; u32 page_size; erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, @@ -179,30 +206,34 @@ static int create_cq_cmd(struct erdma_ucontext *uctx, struct erdma_cq *cq) req.qbuf_addr_h = upper_32_bits(cq->kern_cq.qbuf_dma_addr); req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, 1) | - FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, - ERDMA_MR_INLINE_MTT); + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); req.first_page_offset = 0; req.cq_db_info_addr = cq->kern_cq.qbuf_dma_addr + (cq->depth << CQE_SHIFT); } else { - mtt = &cq->user_cq.qbuf_mtt; + mem = &cq->user_cq.qbuf_mem; req.cfg0 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_PAGESIZE_MASK, - ilog2(mtt->page_size) - ERDMA_HW_PAGE_SHIFT); - if (mtt->mtt_nents == 1) { - req.qbuf_addr_l = lower_32_bits(*(u64 *)mtt->mtt_buf); - req.qbuf_addr_h = upper_32_bits(*(u64 *)mtt->mtt_buf); + ilog2(mem->page_size) - ERDMA_HW_PAGE_SHIFT); + if (mem->mtt_nents == 1) { + req.qbuf_addr_l = lower_32_bits(mem->mtt->buf[0]); + req.qbuf_addr_h = upper_32_bits(mem->mtt->buf[0]); + req.cfg1 |= + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); } else { - req.qbuf_addr_l = lower_32_bits(mtt->mtt_entry[0]); - req.qbuf_addr_h = upper_32_bits(mtt->mtt_entry[0]); + req.qbuf_addr_l = lower_32_bits(mem->mtt->buf_dma); + req.qbuf_addr_h = upper_32_bits(mem->mtt->buf_dma); + req.cfg1 |= + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_1LEVEL); } req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, - mtt->mtt_nents); - req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, - mtt->mtt_type); + mem->mtt_nents); - req.first_page_offset = mtt->page_offset; + req.first_page_offset = mem->page_offset; req.cq_db_info_addr = cq->user_cq.db_info_dma_addr; if (uctx->ext_db.enable) { @@ -481,8 +512,8 @@ static int init_kernel_qp(struct erdma_dev *dev, struct erdma_qp *qp, dev->func_bar + (ERDMA_SDB_SHARED_PAGE_INDEX << PAGE_SHIFT); kqp->hw_rq_db = dev->func_bar + ERDMA_BAR_RQDB_SPACE_OFFSET; - kqp->swr_tbl = vmalloc(qp->attrs.sq_size * sizeof(u64)); - kqp->rwr_tbl = vmalloc(qp->attrs.rq_size * sizeof(u64)); + kqp->swr_tbl = vmalloc_array(qp->attrs.sq_size, sizeof(u64)); + kqp->rwr_tbl = vmalloc_array(qp->attrs.rq_size, sizeof(u64)); if (!kqp->swr_tbl || !kqp->rwr_tbl) goto err_out; @@ -508,12 +539,223 @@ err_out: return -ENOMEM; } +static void erdma_fill_bottom_mtt(struct erdma_dev *dev, struct erdma_mem *mem) +{ + struct erdma_mtt *mtt = mem->mtt; + struct ib_block_iter biter; + u32 idx = 0; + + while (mtt->low_level) + mtt = mtt->low_level; + + rdma_umem_for_each_dma_block(mem->umem, &biter, mem->page_size) + mtt->buf[idx++] = rdma_block_iter_dma_address(&biter); +} + +static struct erdma_mtt *erdma_create_cont_mtt(struct erdma_dev *dev, + size_t size) +{ + struct erdma_mtt *mtt; + + mtt = kzalloc(sizeof(*mtt), GFP_KERNEL); + if (!mtt) + return ERR_PTR(-ENOMEM); + + mtt->size = size; + mtt->buf = kzalloc(mtt->size, GFP_KERNEL); + if (!mtt->buf) + goto err_free_mtt; + + mtt->continuous = true; + mtt->buf_dma = dma_map_single(&dev->pdev->dev, mtt->buf, mtt->size, + DMA_TO_DEVICE); + if (dma_mapping_error(&dev->pdev->dev, mtt->buf_dma)) + goto err_free_mtt_buf; + + return mtt; + +err_free_mtt_buf: + kfree(mtt->buf); + +err_free_mtt: + kfree(mtt); + + return ERR_PTR(-ENOMEM); +} + +static void erdma_destroy_mtt_buf_sg(struct erdma_dev *dev, + struct erdma_mtt *mtt) +{ + dma_unmap_sg(&dev->pdev->dev, mtt->sglist, mtt->nsg, DMA_TO_DEVICE); + vfree(mtt->sglist); +} + +static void erdma_destroy_scatter_mtt(struct erdma_dev *dev, + struct erdma_mtt *mtt) +{ + erdma_destroy_mtt_buf_sg(dev, mtt); + vfree(mtt->buf); + kfree(mtt); +} + +static void erdma_init_middle_mtt(struct erdma_mtt *mtt, + struct erdma_mtt *low_mtt) +{ + struct scatterlist *sg; + u32 idx = 0, i; + + for_each_sg(low_mtt->sglist, sg, low_mtt->nsg, i) + mtt->buf[idx++] = sg_dma_address(sg); +} + +static int erdma_create_mtt_buf_sg(struct erdma_dev *dev, struct erdma_mtt *mtt) +{ + struct scatterlist *sglist; + void *buf = mtt->buf; + u32 npages, i, nsg; + struct page *pg; + + /* Failed if buf is not page aligned */ + if ((uintptr_t)buf & ~PAGE_MASK) + return -EINVAL; + + npages = DIV_ROUND_UP(mtt->size, PAGE_SIZE); + sglist = vzalloc(npages * sizeof(*sglist)); + if (!sglist) + return -ENOMEM; + + sg_init_table(sglist, npages); + for (i = 0; i < npages; i++) { + pg = vmalloc_to_page(buf); + if (!pg) + goto err; + sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); + buf += PAGE_SIZE; + } + + nsg = dma_map_sg(&dev->pdev->dev, sglist, npages, DMA_TO_DEVICE); + if (!nsg) + goto err; + + mtt->sglist = sglist; + mtt->nsg = nsg; + + return 0; +err: + vfree(sglist); + + return -ENOMEM; +} + +static struct erdma_mtt *erdma_create_scatter_mtt(struct erdma_dev *dev, + size_t size) +{ + struct erdma_mtt *mtt; + int ret = -ENOMEM; + + mtt = kzalloc(sizeof(*mtt), GFP_KERNEL); + if (!mtt) + return NULL; + + mtt->size = ALIGN(size, PAGE_SIZE); + mtt->buf = vzalloc(mtt->size); + mtt->continuous = false; + if (!mtt->buf) + goto err_free_mtt; + + ret = erdma_create_mtt_buf_sg(dev, mtt); + if (ret) + goto err_free_mtt_buf; + + ibdev_dbg(&dev->ibdev, "create scatter mtt, size:%lu, nsg:%u\n", + mtt->size, mtt->nsg); + + return mtt; + +err_free_mtt_buf: + vfree(mtt->buf); + +err_free_mtt: + kfree(mtt); + + return ERR_PTR(ret); +} + +static struct erdma_mtt *erdma_create_mtt(struct erdma_dev *dev, size_t size, + bool force_continuous) +{ + struct erdma_mtt *mtt, *tmp_mtt; + int ret, level = 0; + + ibdev_dbg(&dev->ibdev, "create_mtt, size:%lu, force cont:%d\n", size, + force_continuous); + + if (!(dev->attrs.cap_flags & ERDMA_DEV_CAP_FLAGS_MTT_VA)) + force_continuous = true; + + if (force_continuous) + return erdma_create_cont_mtt(dev, size); + + mtt = erdma_create_scatter_mtt(dev, size); + if (IS_ERR(mtt)) + return mtt; + level = 1; + + /* convergence the mtt table. */ + while (mtt->nsg != 1 && level <= 3) { + tmp_mtt = erdma_create_scatter_mtt(dev, MTT_SIZE(mtt->nsg)); + if (IS_ERR(tmp_mtt)) { + ret = PTR_ERR(tmp_mtt); + goto err_free_mtt; + } + erdma_init_middle_mtt(tmp_mtt, mtt); + tmp_mtt->low_level = mtt; + mtt = tmp_mtt; + level++; + } + + if (level > 3) { + ret = -ENOMEM; + goto err_free_mtt; + } + + mtt->level = level; + ibdev_dbg(&dev->ibdev, "top mtt: level:%d, dma_addr 0x%llx\n", + mtt->level, mtt->sglist[0].dma_address); + + return mtt; +err_free_mtt: + while (mtt) { + tmp_mtt = mtt->low_level; + erdma_destroy_scatter_mtt(dev, mtt); + mtt = tmp_mtt; + } + + return ERR_PTR(ret); +} + +static void erdma_destroy_mtt(struct erdma_dev *dev, struct erdma_mtt *mtt) +{ + struct erdma_mtt *tmp_mtt; + + if (mtt->continuous) { + dma_unmap_single(&dev->pdev->dev, mtt->buf_dma, mtt->size, + DMA_TO_DEVICE); + kfree(mtt->buf); + kfree(mtt); + } else { + while (mtt) { + tmp_mtt = mtt->low_level; + erdma_destroy_scatter_mtt(dev, mtt); + mtt = tmp_mtt; + } + } +} + static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, u64 start, u64 len, int access, u64 virt, - unsigned long req_page_size, u8 force_indirect_mtt) + unsigned long req_page_size, bool force_continuous) { - struct ib_block_iter biter; - uint64_t *phy_addr = NULL; int ret = 0; mem->umem = ib_umem_get(&dev->ibdev, start, len, access); @@ -529,38 +771,14 @@ static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, mem->page_offset = start & (mem->page_size - 1); mem->mtt_nents = ib_umem_num_dma_blocks(mem->umem, mem->page_size); mem->page_cnt = mem->mtt_nents; - - if (mem->page_cnt > ERDMA_MAX_INLINE_MTT_ENTRIES || - force_indirect_mtt) { - mem->mtt_type = ERDMA_MR_INDIRECT_MTT; - mem->mtt_buf = - alloc_pages_exact(MTT_SIZE(mem->page_cnt), GFP_KERNEL); - if (!mem->mtt_buf) { - ret = -ENOMEM; - goto error_ret; - } - phy_addr = mem->mtt_buf; - } else { - mem->mtt_type = ERDMA_MR_INLINE_MTT; - phy_addr = mem->mtt_entry; + mem->mtt = erdma_create_mtt(dev, MTT_SIZE(mem->page_cnt), + force_continuous); + if (IS_ERR(mem->mtt)) { + ret = PTR_ERR(mem->mtt); + goto error_ret; } - rdma_umem_for_each_dma_block(mem->umem, &biter, mem->page_size) { - *phy_addr = rdma_block_iter_dma_address(&biter); - phy_addr++; - } - - if (mem->mtt_type == ERDMA_MR_INDIRECT_MTT) { - mem->mtt_entry[0] = - dma_map_single(&dev->pdev->dev, mem->mtt_buf, - MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); - if (dma_mapping_error(&dev->pdev->dev, mem->mtt_entry[0])) { - free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); - mem->mtt_buf = NULL; - ret = -ENOMEM; - goto error_ret; - } - } + erdma_fill_bottom_mtt(dev, mem); return 0; @@ -575,11 +793,8 @@ error_ret: static void put_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem) { - if (mem->mtt_buf) { - dma_unmap_single(&dev->pdev->dev, mem->mtt_entry[0], - MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); - free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); - } + if (mem->mtt) + erdma_destroy_mtt(dev, mem->mtt); if (mem->umem) { ib_umem_release(mem->umem); @@ -660,18 +875,18 @@ static int init_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx, qp->attrs.rq_size * RQE_SIZE)) return -EINVAL; - ret = get_mtt_entries(qp->dev, &qp->user_qp.sq_mtt, va, + ret = get_mtt_entries(qp->dev, &qp->user_qp.sq_mem, va, qp->attrs.sq_size << SQEBB_SHIFT, 0, va, - (SZ_1M - SZ_4K), 1); + (SZ_1M - SZ_4K), true); if (ret) return ret; rq_offset = ALIGN(qp->attrs.sq_size << SQEBB_SHIFT, ERDMA_HW_PAGE_SIZE); qp->user_qp.rq_offset = rq_offset; - ret = get_mtt_entries(qp->dev, &qp->user_qp.rq_mtt, va + rq_offset, + ret = get_mtt_entries(qp->dev, &qp->user_qp.rq_mem, va + rq_offset, qp->attrs.rq_size << RQE_SHIFT, 0, va + rq_offset, - (SZ_1M - SZ_4K), 1); + (SZ_1M - SZ_4K), true); if (ret) goto put_sq_mtt; @@ -687,18 +902,18 @@ static int init_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx, return 0; put_rq_mtt: - put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.rq_mem); put_sq_mtt: - put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.sq_mem); return ret; } static void free_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx) { - put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); - put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.sq_mem); + put_mtt_entries(qp->dev, &qp->user_qp.rq_mem); erdma_unmap_user_dbrecords(uctx, &qp->user_qp.user_dbr_page); } @@ -875,33 +1090,20 @@ struct ib_mr *erdma_ib_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, mr->mem.page_size = PAGE_SIZE; /* update it later. */ mr->mem.page_cnt = max_num_sg; - mr->mem.mtt_type = ERDMA_MR_INDIRECT_MTT; - mr->mem.mtt_buf = - alloc_pages_exact(MTT_SIZE(mr->mem.page_cnt), GFP_KERNEL); - if (!mr->mem.mtt_buf) { - ret = -ENOMEM; + mr->mem.mtt = erdma_create_mtt(dev, MTT_SIZE(max_num_sg), true); + if (IS_ERR(mr->mem.mtt)) { + ret = PTR_ERR(mr->mem.mtt); goto out_remove_stag; } - mr->mem.mtt_entry[0] = - dma_map_single(&dev->pdev->dev, mr->mem.mtt_buf, - MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); - if (dma_mapping_error(&dev->pdev->dev, mr->mem.mtt_entry[0])) { - ret = -ENOMEM; - goto out_free_mtt; - } - ret = regmr_cmd(dev, mr); if (ret) - goto out_dma_unmap; + goto out_destroy_mtt; return &mr->ibmr; -out_dma_unmap: - dma_unmap_single(&dev->pdev->dev, mr->mem.mtt_entry[0], - MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); -out_free_mtt: - free_pages_exact(mr->mem.mtt_buf, MTT_SIZE(mr->mem.page_cnt)); +out_destroy_mtt: + erdma_destroy_mtt(dev, mr->mem.mtt); out_remove_stag: erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], @@ -920,7 +1122,7 @@ static int erdma_set_page(struct ib_mr *ibmr, u64 addr) if (mr->mem.mtt_nents >= mr->mem.page_cnt) return -1; - *((u64 *)mr->mem.mtt_buf + mr->mem.mtt_nents) = addr; + mr->mem.mtt->buf[mr->mem.mtt_nents] = addr; mr->mem.mtt_nents++; return 0; @@ -956,7 +1158,7 @@ struct ib_mr *erdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, return ERR_PTR(-ENOMEM); ret = get_mtt_entries(dev, &mr->mem, start, len, access, virt, - SZ_2G - SZ_4K, 0); + SZ_2G - SZ_4K, false); if (ret) goto err_out_free; @@ -1041,7 +1243,7 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr); } else { erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); } xa_erase(&dev->cq_xa, cq->cqn); @@ -1089,8 +1291,8 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) WARPPED_BUFSIZE(qp->attrs.sq_size << SQEBB_SHIFT), qp->kern_qp.sq_buf, qp->kern_qp.sq_buf_dma_addr); } else { - put_mtt_entries(dev, &qp->user_qp.sq_mtt); - put_mtt_entries(dev, &qp->user_qp.rq_mtt); + put_mtt_entries(dev, &qp->user_qp.sq_mem); + put_mtt_entries(dev, &qp->user_qp.rq_mem); erdma_unmap_user_dbrecords(ctx, &qp->user_qp.user_dbr_page); } @@ -1379,9 +1581,9 @@ static int erdma_init_user_cq(struct erdma_ucontext *ctx, struct erdma_cq *cq, int ret; struct erdma_dev *dev = to_edev(cq->ibcq.device); - ret = get_mtt_entries(dev, &cq->user_cq.qbuf_mtt, ureq->qbuf_va, + ret = get_mtt_entries(dev, &cq->user_cq.qbuf_mem, ureq->qbuf_va, ureq->qbuf_len, 0, ureq->qbuf_va, SZ_64M - SZ_4K, - 1); + true); if (ret) return ret; @@ -1389,7 +1591,7 @@ static int erdma_init_user_cq(struct erdma_ucontext *ctx, struct erdma_cq *cq, &cq->user_cq.user_dbr_page, &cq->user_cq.db_info_dma_addr); if (ret) - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); return ret; } @@ -1473,7 +1675,7 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, err_free_res: if (!rdma_is_kernel_res(&ibcq->res)) { erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); } else { dma_free_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(depth << CQE_SHIFT), diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index 429fc3063f98..eb9c0f92fb6f 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -65,7 +65,7 @@ struct erdma_pd { * MemoryRegion definition. */ #define ERDMA_MAX_INLINE_MTT_ENTRIES 4 -#define MTT_SIZE(mtt_cnt) (mtt_cnt << 3) /* per mtt takes 8 Bytes. */ +#define MTT_SIZE(mtt_cnt) ((mtt_cnt) << 3) /* per mtt entry takes 8 Bytes. */ #define ERDMA_MR_MAX_MTT_CNT 524288 #define ERDMA_MTT_ENTRY_SIZE 8 @@ -73,8 +73,8 @@ struct erdma_pd { #define ERDMA_MR_TYPE_FRMR 1 #define ERDMA_MR_TYPE_DMA 2 -#define ERDMA_MR_INLINE_MTT 0 -#define ERDMA_MR_INDIRECT_MTT 1 +#define ERDMA_MR_MTT_0LEVEL 0 +#define ERDMA_MR_MTT_1LEVEL 1 #define ERDMA_MR_ACC_RA BIT(0) #define ERDMA_MR_ACC_LR BIT(1) @@ -90,10 +90,28 @@ static inline u8 to_erdma_access_flags(int access) (access & IB_ACCESS_REMOTE_ATOMIC ? ERDMA_MR_ACC_RA : 0); } +/* Hierarchical storage structure for MTT entries */ +struct erdma_mtt { + u64 *buf; + size_t size; + + bool continuous; + union { + dma_addr_t buf_dma; + struct { + struct scatterlist *sglist; + u32 nsg; + u32 level; + }; + }; + + struct erdma_mtt *low_level; +}; + struct erdma_mem { struct ib_umem *umem; - void *mtt_buf; - u32 mtt_type; + struct erdma_mtt *mtt; + u32 page_size; u32 page_offset; u32 page_cnt; @@ -101,8 +119,6 @@ struct erdma_mem { u64 va; u64 len; - - u64 mtt_entry[ERDMA_MAX_INLINE_MTT_ENTRIES]; }; struct erdma_mr { @@ -121,8 +137,8 @@ struct erdma_user_dbrecords_page { }; struct erdma_uqp { - struct erdma_mem sq_mtt; - struct erdma_mem rq_mtt; + struct erdma_mem sq_mem; + struct erdma_mem rq_mem; dma_addr_t sq_db_info_dma_addr; dma_addr_t rq_db_info_dma_addr; @@ -234,7 +250,7 @@ struct erdma_kcq_info { }; struct erdma_ucq_info { - struct erdma_mem qbuf_mtt; + struct erdma_mem qbuf_mem; struct erdma_user_dbrecords_page *user_dbr_page; dma_addr_t db_info_dma_addr; }; diff --git a/drivers/infiniband/hw/hfi1/Makefile b/drivers/infiniband/hw/hfi1/Makefile index 2e89ec10efed..5d977f363684 100644 --- a/drivers/infiniband/hw/hfi1/Makefile +++ b/drivers/infiniband/hw/hfi1/Makefile @@ -31,6 +31,7 @@ hfi1-y := \ netdev_rx.o \ opfn.o \ pcie.o \ + pin_system.o \ pio.o \ pio_copy.o \ platform.o \ diff --git a/drivers/infiniband/hw/hfi1/affinity.c b/drivers/infiniband/hw/hfi1/affinity.c index 77ee77d4000f..bbc957c578e1 100644 --- a/drivers/infiniband/hw/hfi1/affinity.c +++ b/drivers/infiniband/hw/hfi1/affinity.c @@ -230,11 +230,9 @@ static void node_affinity_add_tail(struct hfi1_affinity_node *entry) /* It must be called with node_affinity.lock held */ static struct hfi1_affinity_node *node_affinity_lookup(int node) { - struct list_head *pos; struct hfi1_affinity_node *entry; - list_for_each(pos, &node_affinity.list) { - entry = list_entry(pos, struct hfi1_affinity_node, list); + list_for_each_entry(entry, &node_affinity.list, list) { if (entry->node == node) return entry; } diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index baaa4406d5e6..0814291a0412 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -1461,7 +1461,8 @@ static u64 dc_access_lcb_cntr(const struct cntr_entry *entry, void *context, ret = write_lcb_csr(dd, csr, data); if (ret) { - dd_dev_err(dd, "Could not acquire LCB for counter 0x%x", csr); + if (!(dd->flags & HFI1_SHUTDOWN)) + dd_dev_err(dd, "Could not acquire LCB for counter 0x%x", csr); return 0; } @@ -6160,7 +6161,7 @@ static int request_host_lcb_access(struct hfi1_devdata *dd) ret = do_8051_command(dd, HCMD_MISC, (u64)HCMD_MISC_REQUEST_LCB_ACCESS << LOAD_DATA_FIELD_ID_SHIFT, NULL); - if (ret != HCMD_SUCCESS) { + if (ret != HCMD_SUCCESS && !(dd->flags & HFI1_SHUTDOWN)) { dd_dev_err(dd, "%s: command failed with error %d\n", __func__, ret); } @@ -6241,7 +6242,8 @@ int acquire_lcb_access(struct hfi1_devdata *dd, int sleep_ok) if (dd->lcb_access_count == 0) { ret = request_host_lcb_access(dd); if (ret) { - dd_dev_err(dd, + if (!(dd->flags & HFI1_SHUTDOWN)) + dd_dev_err(dd, "%s: unable to acquire LCB access, err %d\n", __func__, ret); goto done; diff --git a/drivers/infiniband/hw/hfi1/device.c b/drivers/infiniband/hw/hfi1/device.c index 05be0d119f79..b0a00b7aaec5 100644 --- a/drivers/infiniband/hw/hfi1/device.c +++ b/drivers/infiniband/hw/hfi1/device.c @@ -10,8 +10,29 @@ #include "hfi.h" #include "device.h" -static struct class *class; -static struct class *user_class; +static char *hfi1_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0600; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +static const struct class class = { + .name = "hfi1", + .devnode = hfi1_devnode, +}; + +static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +static const struct class user_class = { + .name = "hfi1_user", + .devnode = hfi1_user_devnode, +}; static dev_t hfi1_dev; int hfi1_cdev_init(int minor, const char *name, @@ -37,9 +58,9 @@ int hfi1_cdev_init(int minor, const char *name, } if (user_accessible) - device = device_create(user_class, NULL, dev, NULL, "%s", name); + device = device_create(&user_class, NULL, dev, NULL, "%s", name); else - device = device_create(class, NULL, dev, NULL, "%s", name); + device = device_create(&class, NULL, dev, NULL, "%s", name); if (IS_ERR(device)) { ret = PTR_ERR(device); @@ -72,26 +93,6 @@ const char *class_name(void) return hfi1_class_name; } -static char *hfi1_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0600; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - -static const char *hfi1_class_name_user = "hfi1_user"; -static const char *class_name_user(void) -{ - return hfi1_class_name_user; -} - -static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0666; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - int __init dev_init(void) { int ret; @@ -102,27 +103,21 @@ int __init dev_init(void) goto done; } - class = class_create(class_name()); - if (IS_ERR(class)) { - ret = PTR_ERR(class); + ret = class_register(&class); + if (ret) { pr_err("Could not create device class (err %d)\n", -ret); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); goto done; } - class->devnode = hfi1_devnode; - user_class = class_create(class_name_user()); - if (IS_ERR(user_class)) { - ret = PTR_ERR(user_class); + ret = class_register(&user_class); + if (ret) { pr_err("Could not create device class for user accessible files (err %d)\n", -ret); - class_destroy(class); - class = NULL; - user_class = NULL; + class_unregister(&class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); goto done; } - user_class->devnode = hfi1_user_devnode; done: return ret; @@ -130,11 +125,8 @@ done: void dev_cleanup(void) { - class_destroy(class); - class = NULL; - - class_destroy(user_class); - user_class = NULL; + class_unregister(&class); + class_unregister(&user_class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); } diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 7fa9cd39254f..38772e52d7ed 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ /* - * Copyright(c) 2020 Cornelis Networks, Inc. + * Copyright(c) 2020-2023 Cornelis Networks, Inc. * Copyright(c) 2015-2020 Intel Corporation. */ @@ -1378,8 +1378,6 @@ struct hfi1_devdata { #define PT_INVALID 3 struct tid_rb_node; -struct mmu_rb_node; -struct mmu_rb_handler; /* Private data for file operations */ struct hfi1_filedata { diff --git a/drivers/infiniband/hw/hfi1/pin_system.c b/drivers/infiniband/hw/hfi1/pin_system.c new file mode 100644 index 000000000000..384f722093e0 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/pin_system.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause +/* + * Copyright(c) 2023 - Cornelis Networks, Inc. + */ + +#include + +#include "hfi.h" +#include "common.h" +#include "device.h" +#include "pinning.h" +#include "mmu_rb.h" +#include "user_sdma.h" +#include "trace.h" + +struct sdma_mmu_node { + struct mmu_rb_node rb; + struct hfi1_user_sdma_pkt_q *pq; + struct page **pages; + unsigned int npages; +}; + +static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, + unsigned long len); +static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, void *arg2, + bool *stop); +static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode); + +static struct mmu_rb_ops sdma_rb_ops = { + .filter = sdma_rb_filter, + .evict = sdma_rb_evict, + .remove = sdma_rb_remove, +}; + +int hfi1_init_system_pinning(struct hfi1_user_sdma_pkt_q *pq) +{ + struct hfi1_devdata *dd = pq->dd; + int ret; + + ret = hfi1_mmu_rb_register(pq, &sdma_rb_ops, dd->pport->hfi1_wq, + &pq->handler); + if (ret) + dd_dev_err(dd, + "[%u:%u] Failed to register system memory DMA support with MMU: %d\n", + pq->ctxt, pq->subctxt, ret); + return ret; +} + +void hfi1_free_system_pinning(struct hfi1_user_sdma_pkt_q *pq) +{ + if (pq->handler) + hfi1_mmu_rb_unregister(pq->handler); +} + +static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages) +{ + struct evict_data evict_data; + + evict_data.cleared = 0; + evict_data.target = npages; + hfi1_mmu_rb_evict(pq->handler, &evict_data); + return evict_data.cleared; +} + +static void unpin_vector_pages(struct mm_struct *mm, struct page **pages, + unsigned int start, unsigned int npages) +{ + hfi1_release_user_pages(mm, pages + start, npages, false); + kfree(pages); +} + +static inline struct mm_struct *mm_from_sdma_node(struct sdma_mmu_node *node) +{ + return node->rb.handler->mn.mm; +} + +static void free_system_node(struct sdma_mmu_node *node) +{ + if (node->npages) { + unpin_vector_pages(mm_from_sdma_node(node), node->pages, 0, + node->npages); + atomic_sub(node->npages, &node->pq->n_locked); + } + kfree(node); +} + +/* + * kref_get()'s an additional kref on the returned rb_node to prevent rb_node + * from being released until after rb_node is assigned to an SDMA descriptor + * (struct sdma_desc) under add_system_iovec_to_sdma_packet(), even if the + * virtual address range for rb_node is invalidated between now and then. + */ +static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, + unsigned long start, + unsigned long end) +{ + struct mmu_rb_node *rb_node; + unsigned long flags; + + spin_lock_irqsave(&handler->lock, flags); + rb_node = hfi1_mmu_rb_get_first(handler, start, (end - start)); + if (!rb_node) { + spin_unlock_irqrestore(&handler->lock, flags); + return NULL; + } + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&rb_node->refcount); + spin_unlock_irqrestore(&handler->lock, flags); + + return container_of(rb_node, struct sdma_mmu_node, rb); +} + +static int pin_system_pages(struct user_sdma_request *req, + uintptr_t start_address, size_t length, + struct sdma_mmu_node *node, int npages) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + int pinned, cleared; + struct page **pages; + + pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); + if (!pages) + return -ENOMEM; + +retry: + if (!hfi1_can_pin_pages(pq->dd, current->mm, atomic_read(&pq->n_locked), + npages)) { + SDMA_DBG(req, "Evicting: nlocked %u npages %u", + atomic_read(&pq->n_locked), npages); + cleared = sdma_cache_evict(pq, npages); + if (cleared >= npages) + goto retry; + } + + SDMA_DBG(req, "Acquire user pages start_address %lx node->npages %u npages %u", + start_address, node->npages, npages); + pinned = hfi1_acquire_user_pages(current->mm, start_address, npages, 0, + pages); + + if (pinned < 0) { + kfree(pages); + SDMA_DBG(req, "pinned %d", pinned); + return pinned; + } + if (pinned != npages) { + unpin_vector_pages(current->mm, pages, node->npages, pinned); + SDMA_DBG(req, "npages %u pinned %d", npages, pinned); + return -EFAULT; + } + node->rb.addr = start_address; + node->rb.len = length; + node->pages = pages; + node->npages = npages; + atomic_add(pinned, &pq->n_locked); + SDMA_DBG(req, "done. pinned %d", pinned); + return 0; +} + +/* + * kref refcount on *node_p will be 2 on successful addition: one kref from + * kref_init() for mmu_rb_handler and one kref to prevent *node_p from being + * released until after *node_p is assigned to an SDMA descriptor (struct + * sdma_desc) under add_system_iovec_to_sdma_packet(), even if the virtual + * address range for *node_p is invalidated between now and then. + */ +static int add_system_pinning(struct user_sdma_request *req, + struct sdma_mmu_node **node_p, + unsigned long start, unsigned long len) + +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + struct sdma_mmu_node *node; + int ret; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + /* First kref "moves" to mmu_rb_handler */ + kref_init(&node->rb.refcount); + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&node->rb.refcount); + + node->pq = pq; + ret = pin_system_pages(req, start, len, node, PFN_DOWN(len)); + if (ret == 0) { + ret = hfi1_mmu_rb_insert(pq->handler, &node->rb); + if (ret) + free_system_node(node); + else + *node_p = node; + + return ret; + } + + kfree(node); + return ret; +} + +static int get_system_cache_entry(struct user_sdma_request *req, + struct sdma_mmu_node **node_p, + size_t req_start, size_t req_len) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + u64 start = ALIGN_DOWN(req_start, PAGE_SIZE); + u64 end = PFN_ALIGN(req_start + req_len); + int ret; + + if ((end - start) == 0) { + SDMA_DBG(req, + "Request for empty cache entry req_start %lx req_len %lx start %llx end %llx", + req_start, req_len, start, end); + return -EINVAL; + } + + SDMA_DBG(req, "req_start %lx req_len %lu", req_start, req_len); + + while (1) { + struct sdma_mmu_node *node = + find_system_node(pq->handler, start, end); + u64 prepend_len = 0; + + SDMA_DBG(req, "node %p start %llx end %llu", node, start, end); + if (!node) { + ret = add_system_pinning(req, node_p, start, + end - start); + if (ret == -EEXIST) { + /* + * Another execution context has inserted a + * conficting entry first. + */ + continue; + } + return ret; + } + + if (node->rb.addr <= start) { + /* + * This entry covers at least part of the region. If it doesn't extend + * to the end, then this will be called again for the next segment. + */ + *node_p = node; + return 0; + } + + SDMA_DBG(req, "prepend: node->rb.addr %lx, node->rb.refcount %d", + node->rb.addr, kref_read(&node->rb.refcount)); + prepend_len = node->rb.addr - start; + + /* + * This node will not be returned, instead a new node + * will be. So release the reference. + */ + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); + + /* Prepend a node to cover the beginning of the allocation */ + ret = add_system_pinning(req, node_p, start, prepend_len); + if (ret == -EEXIST) { + /* Another execution context has inserted a conficting entry first. */ + continue; + } + return ret; + } +} + +static void sdma_mmu_rb_node_get(void *ctx) +{ + struct mmu_rb_node *node = ctx; + + kref_get(&node->refcount); +} + +static void sdma_mmu_rb_node_put(void *ctx) +{ + struct sdma_mmu_node *node = ctx; + + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); +} + +static int add_mapping_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct sdma_mmu_node *cache_entry, + size_t start, + size_t from_this_cache_entry) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + unsigned int page_offset; + unsigned int from_this_page; + size_t page_index; + void *ctx; + int ret; + + /* + * Because the cache may be more fragmented than the memory that is being accessed, + * it's not strictly necessary to have a descriptor per cache entry. + */ + + while (from_this_cache_entry) { + page_index = PFN_DOWN(start - cache_entry->rb.addr); + + if (page_index >= cache_entry->npages) { + SDMA_DBG(req, + "Request for page_index %zu >= cache_entry->npages %u", + page_index, cache_entry->npages); + return -EINVAL; + } + + page_offset = start - ALIGN_DOWN(start, PAGE_SIZE); + from_this_page = PAGE_SIZE - page_offset; + + if (from_this_page < from_this_cache_entry) { + ctx = NULL; + } else { + /* + * In the case they are equal the next line has no practical effect, + * but it's better to do a register to register copy than a conditional + * branch. + */ + from_this_page = from_this_cache_entry; + ctx = cache_entry; + } + + ret = sdma_txadd_page(pq->dd, &tx->txreq, + cache_entry->pages[page_index], + page_offset, from_this_page, + ctx, + sdma_mmu_rb_node_get, + sdma_mmu_rb_node_put); + if (ret) { + /* + * When there's a failure, the entire request is freed by + * user_sdma_send_pkts(). + */ + SDMA_DBG(req, + "sdma_txadd_page failed %d page_index %lu page_offset %u from_this_page %u", + ret, page_index, page_offset, from_this_page); + return ret; + } + start += from_this_page; + from_this_cache_entry -= from_this_page; + } + return 0; +} + +static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + size_t from_this_iovec) +{ + while (from_this_iovec > 0) { + struct sdma_mmu_node *cache_entry; + size_t from_this_cache_entry; + size_t start; + int ret; + + start = (uintptr_t)iovec->iov.iov_base + iovec->offset; + ret = get_system_cache_entry(req, &cache_entry, start, + from_this_iovec); + if (ret) { + SDMA_DBG(req, "pin system segment failed %d", ret); + return ret; + } + + from_this_cache_entry = cache_entry->rb.len - (start - cache_entry->rb.addr); + if (from_this_cache_entry > from_this_iovec) + from_this_cache_entry = from_this_iovec; + + ret = add_mapping_to_sdma_packet(req, tx, cache_entry, start, + from_this_cache_entry); + + /* + * Done adding cache_entry to zero or more sdma_desc. Can + * kref_put() the "safety" kref taken under + * get_system_cache_entry(). + */ + kref_put(&cache_entry->rb.refcount, hfi1_mmu_rb_release); + + if (ret) { + SDMA_DBG(req, "add system segment failed %d", ret); + return ret; + } + + iovec->offset += from_this_cache_entry; + from_this_iovec -= from_this_cache_entry; + } + + return 0; +} + +/* + * Add up to pkt_data_remaining bytes to the txreq, starting at the current + * offset in the given iovec entry and continuing until all data has been added + * to the iovec or the iovec entry type changes. + * + * On success, prior to returning, adjust pkt_data_remaining, req->iov_idx, and + * the offset value in req->iov[req->iov_idx] to reflect the data that has been + * consumed. + */ +int hfi1_add_pages_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + u32 *pkt_data_remaining) +{ + size_t remaining_to_add = *pkt_data_remaining; + /* + * Walk through iovec entries, ensure the associated pages + * are pinned and mapped, add data to the packet until no more + * data remains to be added or the iovec entry type changes. + */ + while (remaining_to_add > 0) { + struct user_sdma_iovec *cur_iovec; + size_t from_this_iovec; + int ret; + + cur_iovec = iovec; + from_this_iovec = iovec->iov.iov_len - iovec->offset; + + if (from_this_iovec > remaining_to_add) { + from_this_iovec = remaining_to_add; + } else { + /* The current iovec entry will be consumed by this pass. */ + req->iov_idx++; + iovec++; + } + + ret = add_system_iovec_to_sdma_packet(req, tx, cur_iovec, + from_this_iovec); + if (ret) + return ret; + + remaining_to_add -= from_this_iovec; + } + *pkt_data_remaining = remaining_to_add; + + return 0; +} + +static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, + unsigned long len) +{ + return (bool)(node->addr == addr); +} + +/* + * Return 1 to remove the node from the rb tree and call the remove op. + * + * Called with the rb tree lock held. + */ +static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, + void *evict_arg, bool *stop) +{ + struct sdma_mmu_node *node = + container_of(mnode, struct sdma_mmu_node, rb); + struct evict_data *evict_data = evict_arg; + + /* this node will be evicted, add its pages to our count */ + evict_data->cleared += node->npages; + + /* have enough pages been cleared? */ + if (evict_data->cleared >= evict_data->target) + *stop = true; + + return 1; /* remove this node */ +} + +static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode) +{ + struct sdma_mmu_node *node = + container_of(mnode, struct sdma_mmu_node, rb); + + free_system_node(node); +} diff --git a/drivers/infiniband/hw/hfi1/pinning.h b/drivers/infiniband/hw/hfi1/pinning.h new file mode 100644 index 000000000000..a814a3aa9654 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/pinning.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* + * Copyright(c) 2023 Cornelis Networks, Inc. + */ +#ifndef _HFI1_PINNING_H +#define _HFI1_PINNING_H + +struct hfi1_user_sdma_pkt_q; +struct user_sdma_request; +struct user_sdma_txreq; +struct user_sdma_iovec; + +int hfi1_init_system_pinning(struct hfi1_user_sdma_pkt_q *pq); +void hfi1_free_system_pinning(struct hfi1_user_sdma_pkt_q *pq); +int hfi1_add_pages_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + u32 *pkt_data_remaining); + +#endif /* _HFI1_PINNING_H */ diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 62e7dc9bea7b..dfea53e0fdeb 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -1893,9 +1893,7 @@ int pio_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_scontexts) vl_scontexts[i] = sc_per_vl + (extra > 0 ? 1 : 0); } /* build new map */ - newmap = kzalloc(sizeof(*newmap) + - roundup_pow_of_two(num_vls) * - sizeof(struct pio_map_elem *), + newmap = kzalloc(struct_size(newmap, map, roundup_pow_of_two(num_vls)), GFP_KERNEL); if (!newmap) goto bail; @@ -1910,9 +1908,8 @@ int pio_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_scontexts) int sz = roundup_pow_of_two(vl_scontexts[i]); /* only allocate once */ - newmap->map[i] = kzalloc(sizeof(*newmap->map[i]) + - sz * sizeof(struct - send_context *), + newmap->map[i] = kzalloc(struct_size(newmap->map[i], + ksc, sz), GFP_KERNEL); if (!newmap->map[i]) goto bail; diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 02bd62b857b7..29ae7beb9b03 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause /* - * Copyright(c) 2020 - Cornelis Networks, Inc. + * Copyright(c) 2020 - 2023 Cornelis Networks, Inc. * Copyright(c) 2015 - 2018 Intel Corporation. */ @@ -60,22 +60,6 @@ static int defer_packet_queue( uint seq, bool pkts_sent); static void activate_packet_queue(struct iowait *wait, int reason); -static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, - unsigned long len); -static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, - void *arg2, bool *stop); -static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode); - -static struct mmu_rb_ops sdma_rb_ops = { - .filter = sdma_rb_filter, - .evict = sdma_rb_evict, - .remove = sdma_rb_remove, -}; - -static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - u32 *pkt_remaining); static int defer_packet_queue( struct sdma_engine *sde, @@ -185,12 +169,9 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, cq->nentries = hfi1_sdma_comp_ring_size; - ret = hfi1_mmu_rb_register(pq, &sdma_rb_ops, dd->pport->hfi1_wq, - &pq->handler); - if (ret) { - dd_dev_err(dd, "Failed to register with MMU %d", ret); + ret = hfi1_init_system_pinning(pq); + if (ret) goto pq_mmu_fail; - } rcu_assign_pointer(fd->pq, pq); fd->cq = cq; @@ -249,8 +230,7 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, pq->wait, !atomic_read(&pq->n_reqs)); kfree(pq->reqs); - if (pq->handler) - hfi1_mmu_rb_unregister(pq->handler); + hfi1_free_system_pinning(pq); bitmap_free(pq->req_in_use); kmem_cache_destroy(pq->txreq_cache); flush_pq_iowait(pq); @@ -821,8 +801,8 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, u16 maxpkts) req->tidoffset += datalen; req->sent += datalen; while (datalen) { - ret = add_system_pages_to_sdma_packet(req, tx, iovec, - &datalen); + ret = hfi1_add_pages_to_sdma_packet(req, tx, iovec, + &datalen); if (ret) goto free_txreq; iovec = &req->iovs[req->iov_idx]; @@ -860,17 +840,6 @@ free_tx: return ret; } -static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages) -{ - struct evict_data evict_data; - struct mmu_rb_handler *handler = pq->handler; - - evict_data.cleared = 0; - evict_data.target = npages; - hfi1_mmu_rb_evict(handler, &evict_data); - return evict_data.cleared; -} - static int check_header_template(struct user_sdma_request *req, struct hfi1_pkt_header *hdr, u32 lrhlen, u32 datalen) @@ -1253,401 +1222,3 @@ static inline void set_comp_state(struct hfi1_user_sdma_pkt_q *pq, trace_hfi1_sdma_user_completion(pq->dd, pq->ctxt, pq->subctxt, idx, state, ret); } - -static void unpin_vector_pages(struct mm_struct *mm, struct page **pages, - unsigned int start, unsigned int npages) -{ - hfi1_release_user_pages(mm, pages + start, npages, false); - kfree(pages); -} - -static void free_system_node(struct sdma_mmu_node *node) -{ - if (node->npages) { - unpin_vector_pages(mm_from_sdma_node(node), node->pages, 0, - node->npages); - atomic_sub(node->npages, &node->pq->n_locked); - } - kfree(node); -} - -/* - * kref_get()'s an additional kref on the returned rb_node to prevent rb_node - * from being released until after rb_node is assigned to an SDMA descriptor - * (struct sdma_desc) under add_system_iovec_to_sdma_packet(), even if the - * virtual address range for rb_node is invalidated between now and then. - */ -static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, - unsigned long start, - unsigned long end) -{ - struct mmu_rb_node *rb_node; - unsigned long flags; - - spin_lock_irqsave(&handler->lock, flags); - rb_node = hfi1_mmu_rb_get_first(handler, start, (end - start)); - if (!rb_node) { - spin_unlock_irqrestore(&handler->lock, flags); - return NULL; - } - - /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ - kref_get(&rb_node->refcount); - spin_unlock_irqrestore(&handler->lock, flags); - - return container_of(rb_node, struct sdma_mmu_node, rb); -} - -static int pin_system_pages(struct user_sdma_request *req, - uintptr_t start_address, size_t length, - struct sdma_mmu_node *node, int npages) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - int pinned, cleared; - struct page **pages; - - pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); - if (!pages) - return -ENOMEM; - -retry: - if (!hfi1_can_pin_pages(pq->dd, current->mm, atomic_read(&pq->n_locked), - npages)) { - SDMA_DBG(req, "Evicting: nlocked %u npages %u", - atomic_read(&pq->n_locked), npages); - cleared = sdma_cache_evict(pq, npages); - if (cleared >= npages) - goto retry; - } - - SDMA_DBG(req, "Acquire user pages start_address %lx node->npages %u npages %u", - start_address, node->npages, npages); - pinned = hfi1_acquire_user_pages(current->mm, start_address, npages, 0, - pages); - - if (pinned < 0) { - kfree(pages); - SDMA_DBG(req, "pinned %d", pinned); - return pinned; - } - if (pinned != npages) { - unpin_vector_pages(current->mm, pages, node->npages, pinned); - SDMA_DBG(req, "npages %u pinned %d", npages, pinned); - return -EFAULT; - } - node->rb.addr = start_address; - node->rb.len = length; - node->pages = pages; - node->npages = npages; - atomic_add(pinned, &pq->n_locked); - SDMA_DBG(req, "done. pinned %d", pinned); - return 0; -} - -/* - * kref refcount on *node_p will be 2 on successful addition: one kref from - * kref_init() for mmu_rb_handler and one kref to prevent *node_p from being - * released until after *node_p is assigned to an SDMA descriptor (struct - * sdma_desc) under add_system_iovec_to_sdma_packet(), even if the virtual - * address range for *node_p is invalidated between now and then. - */ -static int add_system_pinning(struct user_sdma_request *req, - struct sdma_mmu_node **node_p, - unsigned long start, unsigned long len) - -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - struct sdma_mmu_node *node; - int ret; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return -ENOMEM; - - /* First kref "moves" to mmu_rb_handler */ - kref_init(&node->rb.refcount); - - /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ - kref_get(&node->rb.refcount); - - node->pq = pq; - ret = pin_system_pages(req, start, len, node, PFN_DOWN(len)); - if (ret == 0) { - ret = hfi1_mmu_rb_insert(pq->handler, &node->rb); - if (ret) - free_system_node(node); - else - *node_p = node; - - return ret; - } - - kfree(node); - return ret; -} - -static int get_system_cache_entry(struct user_sdma_request *req, - struct sdma_mmu_node **node_p, - size_t req_start, size_t req_len) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - u64 start = ALIGN_DOWN(req_start, PAGE_SIZE); - u64 end = PFN_ALIGN(req_start + req_len); - struct mmu_rb_handler *handler = pq->handler; - int ret; - - if ((end - start) == 0) { - SDMA_DBG(req, - "Request for empty cache entry req_start %lx req_len %lx start %llx end %llx", - req_start, req_len, start, end); - return -EINVAL; - } - - SDMA_DBG(req, "req_start %lx req_len %lu", req_start, req_len); - - while (1) { - struct sdma_mmu_node *node = - find_system_node(handler, start, end); - u64 prepend_len = 0; - - SDMA_DBG(req, "node %p start %llx end %llu", node, start, end); - if (!node) { - ret = add_system_pinning(req, node_p, start, - end - start); - if (ret == -EEXIST) { - /* - * Another execution context has inserted a - * conficting entry first. - */ - continue; - } - return ret; - } - - if (node->rb.addr <= start) { - /* - * This entry covers at least part of the region. If it doesn't extend - * to the end, then this will be called again for the next segment. - */ - *node_p = node; - return 0; - } - - SDMA_DBG(req, "prepend: node->rb.addr %lx, node->rb.refcount %d", - node->rb.addr, kref_read(&node->rb.refcount)); - prepend_len = node->rb.addr - start; - - /* - * This node will not be returned, instead a new node - * will be. So release the reference. - */ - kref_put(&node->rb.refcount, hfi1_mmu_rb_release); - - /* Prepend a node to cover the beginning of the allocation */ - ret = add_system_pinning(req, node_p, start, prepend_len); - if (ret == -EEXIST) { - /* Another execution context has inserted a conficting entry first. */ - continue; - } - return ret; - } -} - -static void sdma_mmu_rb_node_get(void *ctx) -{ - struct mmu_rb_node *node = ctx; - - kref_get(&node->refcount); -} - -static void sdma_mmu_rb_node_put(void *ctx) -{ - struct sdma_mmu_node *node = ctx; - - kref_put(&node->rb.refcount, hfi1_mmu_rb_release); -} - -static int add_mapping_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct sdma_mmu_node *cache_entry, - size_t start, - size_t from_this_cache_entry) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - unsigned int page_offset; - unsigned int from_this_page; - size_t page_index; - void *ctx; - int ret; - - /* - * Because the cache may be more fragmented than the memory that is being accessed, - * it's not strictly necessary to have a descriptor per cache entry. - */ - - while (from_this_cache_entry) { - page_index = PFN_DOWN(start - cache_entry->rb.addr); - - if (page_index >= cache_entry->npages) { - SDMA_DBG(req, - "Request for page_index %zu >= cache_entry->npages %u", - page_index, cache_entry->npages); - return -EINVAL; - } - - page_offset = start - ALIGN_DOWN(start, PAGE_SIZE); - from_this_page = PAGE_SIZE - page_offset; - - if (from_this_page < from_this_cache_entry) { - ctx = NULL; - } else { - /* - * In the case they are equal the next line has no practical effect, - * but it's better to do a register to register copy than a conditional - * branch. - */ - from_this_page = from_this_cache_entry; - ctx = cache_entry; - } - - ret = sdma_txadd_page(pq->dd, &tx->txreq, - cache_entry->pages[page_index], - page_offset, from_this_page, - ctx, - sdma_mmu_rb_node_get, - sdma_mmu_rb_node_put); - if (ret) { - /* - * When there's a failure, the entire request is freed by - * user_sdma_send_pkts(). - */ - SDMA_DBG(req, - "sdma_txadd_page failed %d page_index %lu page_offset %u from_this_page %u", - ret, page_index, page_offset, from_this_page); - return ret; - } - start += from_this_page; - from_this_cache_entry -= from_this_page; - } - return 0; -} - -static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - size_t from_this_iovec) -{ - while (from_this_iovec > 0) { - struct sdma_mmu_node *cache_entry; - size_t from_this_cache_entry; - size_t start; - int ret; - - start = (uintptr_t)iovec->iov.iov_base + iovec->offset; - ret = get_system_cache_entry(req, &cache_entry, start, - from_this_iovec); - if (ret) { - SDMA_DBG(req, "pin system segment failed %d", ret); - return ret; - } - - from_this_cache_entry = cache_entry->rb.len - (start - cache_entry->rb.addr); - if (from_this_cache_entry > from_this_iovec) - from_this_cache_entry = from_this_iovec; - - ret = add_mapping_to_sdma_packet(req, tx, cache_entry, start, - from_this_cache_entry); - - /* - * Done adding cache_entry to zero or more sdma_desc. Can - * kref_put() the "safety" kref taken under - * get_system_cache_entry(). - */ - kref_put(&cache_entry->rb.refcount, hfi1_mmu_rb_release); - - if (ret) { - SDMA_DBG(req, "add system segment failed %d", ret); - return ret; - } - - iovec->offset += from_this_cache_entry; - from_this_iovec -= from_this_cache_entry; - } - - return 0; -} - -static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - u32 *pkt_data_remaining) -{ - size_t remaining_to_add = *pkt_data_remaining; - /* - * Walk through iovec entries, ensure the associated pages - * are pinned and mapped, add data to the packet until no more - * data remains to be added. - */ - while (remaining_to_add > 0) { - struct user_sdma_iovec *cur_iovec; - size_t from_this_iovec; - int ret; - - cur_iovec = iovec; - from_this_iovec = iovec->iov.iov_len - iovec->offset; - - if (from_this_iovec > remaining_to_add) { - from_this_iovec = remaining_to_add; - } else { - /* The current iovec entry will be consumed by this pass. */ - req->iov_idx++; - iovec++; - } - - ret = add_system_iovec_to_sdma_packet(req, tx, cur_iovec, - from_this_iovec); - if (ret) - return ret; - - remaining_to_add -= from_this_iovec; - } - *pkt_data_remaining = remaining_to_add; - - return 0; -} - -static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, - unsigned long len) -{ - return (bool)(node->addr == addr); -} - -/* - * Return 1 to remove the node from the rb tree and call the remove op. - * - * Called with the rb tree lock held. - */ -static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, - void *evict_arg, bool *stop) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - struct evict_data *evict_data = evict_arg; - - /* this node will be evicted, add its pages to our count */ - evict_data->cleared += node->npages; - - /* have enough pages been cleared? */ - if (evict_data->cleared >= evict_data->target) - *stop = true; - - return 1; /* remove this node */ -} - -static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - - free_system_node(node); -} diff --git a/drivers/infiniband/hw/hfi1/user_sdma.h b/drivers/infiniband/hw/hfi1/user_sdma.h index 548347d4c5bc..742ec1470cc5 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.h +++ b/drivers/infiniband/hw/hfi1/user_sdma.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ /* - * Copyright(c) 2020 - Cornelis Networks, Inc. + * Copyright(c) 2023 - Cornelis Networks, Inc. * Copyright(c) 2015 - 2018 Intel Corporation. */ #ifndef _HFI1_USER_SDMA_H @@ -13,6 +13,8 @@ #include "iowait.h" #include "user_exp_rcv.h" #include "mmu_rb.h" +#include "pinning.h" +#include "sdma.h" /* The maximum number of Data io vectors per message/request */ #define MAX_VECTORS_PER_REQ 8 @@ -101,13 +103,6 @@ struct hfi1_user_sdma_comp_q { struct hfi1_sdma_comp_entry *comps; }; -struct sdma_mmu_node { - struct mmu_rb_node rb; - struct hfi1_user_sdma_pkt_q *pq; - struct page **pages; - unsigned int npages; -}; - struct user_sdma_iovec { struct list_head list; struct iovec iov; @@ -203,10 +198,4 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, int hfi1_user_sdma_process_request(struct hfi1_filedata *fd, struct iovec *iovec, unsigned long dim, unsigned long *count); - -static inline struct mm_struct *mm_from_sdma_node(struct sdma_mmu_node *node) -{ - return node->rb.handler->mn.mm; -} - #endif /* _HFI1_USER_SDMA_H */ diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 84239b907de2..7f0d0288beb1 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -97,6 +97,7 @@ #define HNS_ROCE_CQ_BANK_NUM 4 #define CQ_BANKID_SHIFT 2 +#define CQ_BANKID_MASK GENMASK(1, 0) enum { SERV_TYPE_RC, @@ -714,7 +715,6 @@ struct hns_roce_caps { u32 max_rq_sg; u32 rsv0; u32 num_qps; - u32 num_pi_qps; u32 reserved_qps; u32 num_srqs; u32 max_wqes; @@ -840,6 +840,32 @@ enum hns_roce_device_state { HNS_ROCE_DEVICE_STATE_UNINIT, }; +enum hns_roce_hw_pkt_stat_index { + HNS_ROCE_HW_RX_RC_PKT_CNT, + HNS_ROCE_HW_RX_UC_PKT_CNT, + HNS_ROCE_HW_RX_UD_PKT_CNT, + HNS_ROCE_HW_RX_XRC_PKT_CNT, + HNS_ROCE_HW_RX_PKT_CNT, + HNS_ROCE_HW_RX_ERR_PKT_CNT, + HNS_ROCE_HW_RX_CNP_PKT_CNT, + HNS_ROCE_HW_TX_RC_PKT_CNT, + HNS_ROCE_HW_TX_UC_PKT_CNT, + HNS_ROCE_HW_TX_UD_PKT_CNT, + HNS_ROCE_HW_TX_XRC_PKT_CNT, + HNS_ROCE_HW_TX_PKT_CNT, + HNS_ROCE_HW_TX_ERR_PKT_CNT, + HNS_ROCE_HW_TX_CNP_PKT_CNT, + HNS_ROCE_HW_TRP_GET_MPT_ERR_PKT_CNT, + HNS_ROCE_HW_TRP_GET_IRRL_ERR_PKT_CNT, + HNS_ROCE_HW_ECN_DB_CNT, + HNS_ROCE_HW_RX_BUF_CNT, + HNS_ROCE_HW_TRP_RX_SOF_CNT, + HNS_ROCE_HW_CQ_CQE_CNT, + HNS_ROCE_HW_CQ_POE_CNT, + HNS_ROCE_HW_CQ_NOTIFY_CNT, + HNS_ROCE_HW_CNT_TOTAL +}; + struct hns_roce_hw { int (*cmq_init)(struct hns_roce_dev *hr_dev); void (*cmq_exit)(struct hns_roce_dev *hr_dev); @@ -882,6 +908,8 @@ struct hns_roce_hw { int (*query_cqc)(struct hns_roce_dev *hr_dev, u32 cqn, void *buffer); int (*query_qpc)(struct hns_roce_dev *hr_dev, u32 qpn, void *buffer); int (*query_mpt)(struct hns_roce_dev *hr_dev, u32 key, void *buffer); + int (*query_hw_counter)(struct hns_roce_dev *hr_dev, + u64 *stats, u32 port, int *hw_counters); const struct ib_device_ops *hns_roce_dev_ops; const struct ib_device_ops *hns_roce_dev_srq_ops; }; @@ -1112,7 +1140,6 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev); void hns_roce_init_srq_table(struct hns_roce_dev *hr_dev); void hns_roce_init_xrcd_table(struct hns_roce_dev *hr_dev); -void hns_roce_cleanup_eq_table(struct hns_roce_dev *hr_dev); void hns_roce_cleanup_cq_table(struct hns_roce_dev *hr_dev); void hns_roce_cleanup_qp_table(struct hns_roce_dev *hr_dev); @@ -1161,9 +1188,6 @@ int hns_roce_get_umem_bufs(struct hns_roce_dev *hr_dev, dma_addr_t *bufs, int hns_roce_create_srq(struct ib_srq *srq, struct ib_srq_init_attr *srq_init_attr, struct ib_udata *udata); -int hns_roce_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr, - enum ib_srq_attr_mask srq_attr_mask, - struct ib_udata *udata); int hns_roce_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata); int hns_roce_alloc_xrcd(struct ib_xrcd *ib_xrcd, struct ib_udata *udata); @@ -1206,7 +1230,6 @@ void hns_roce_cq_event(struct hns_roce_dev *hr_dev, u32 cqn, int event_type); void flush_cqe(struct hns_roce_dev *dev, struct hns_roce_qp *qp); void hns_roce_qp_event(struct hns_roce_dev *hr_dev, u32 qpn, int event_type); void hns_roce_srq_event(struct hns_roce_dev *hr_dev, u32 srqn, int event_type); -u8 hns_get_gid_index(struct hns_roce_dev *hr_dev, u32 port, int gid_index); void hns_roce_handle_device_err(struct hns_roce_dev *hr_dev); int hns_roce_init(struct hns_roce_dev *hr_dev); void hns_roce_exit(struct hns_roce_dev *hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 47c0efed1821..c4ac06a33869 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -78,7 +78,7 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) return false; } - return hop_num ? true : false; + return hop_num; } static bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 hem_idx, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 8f7eb11066b4..d82daff2d9bd 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -750,7 +750,8 @@ out: qp->sq.head += nreq; qp->next_sge = sge_idx; - if (nreq == 1 && (qp->en_flags & HNS_ROCE_QP_CAP_DIRECT_WQE)) + if (nreq == 1 && !ret && + (qp->en_flags & HNS_ROCE_QP_CAP_DIRECT_WQE)) write_dwqe(hr_dev, qp, wqe); else update_sq_db(hr_dev, qp); @@ -1612,6 +1613,56 @@ static int hns_roce_query_func_info(struct hns_roce_dev *hr_dev) return 0; } +static int hns_roce_hw_v2_query_counter(struct hns_roce_dev *hr_dev, + u64 *stats, u32 port, int *num_counters) +{ +#define CNT_PER_DESC 3 + struct hns_roce_cmq_desc *desc; + int bd_idx, cnt_idx; + __le64 *cnt_data; + int desc_num; + int ret; + int i; + + if (port > hr_dev->caps.num_ports) + return -EINVAL; + + desc_num = DIV_ROUND_UP(HNS_ROCE_HW_CNT_TOTAL, CNT_PER_DESC); + desc = kcalloc(desc_num, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + for (i = 0; i < desc_num; i++) { + hns_roce_cmq_setup_basic_desc(&desc[i], + HNS_ROCE_OPC_QUERY_COUNTER, true); + if (i != desc_num - 1) + desc[i].flag |= cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT); + } + + ret = hns_roce_cmq_send(hr_dev, desc, desc_num); + if (ret) { + ibdev_err(&hr_dev->ib_dev, + "failed to get counter, ret = %d.\n", ret); + goto err_out; + } + + for (i = 0; i < HNS_ROCE_HW_CNT_TOTAL && i < *num_counters; i++) { + bd_idx = i / CNT_PER_DESC; + if (!(desc[bd_idx].flag & HNS_ROCE_CMD_FLAG_NEXT) && + bd_idx != HNS_ROCE_HW_CNT_TOTAL / CNT_PER_DESC) + break; + + cnt_data = (__le64 *)&desc[bd_idx].data[0]; + cnt_idx = i % CNT_PER_DESC; + stats[i] = le64_to_cpu(cnt_data[cnt_idx]); + } + *num_counters = i; + +err_out: + kfree(desc); + return ret; +} + static int hns_roce_config_global_param(struct hns_roce_dev *hr_dev) { struct hns_roce_cmq_desc desc; @@ -1680,29 +1731,6 @@ static int load_func_res_caps(struct hns_roce_dev *hr_dev, bool is_vf) return 0; } -static int load_ext_cfg_caps(struct hns_roce_dev *hr_dev, bool is_vf) -{ - struct hns_roce_cmq_desc desc; - struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; - struct hns_roce_caps *caps = &hr_dev->caps; - u32 func_num, qp_num; - int ret; - - hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_EXT_CFG, true); - ret = hns_roce_cmq_send(hr_dev, &desc, 1); - if (ret) - return ret; - - func_num = is_vf ? 1 : max_t(u32, 1, hr_dev->func_num); - qp_num = hr_reg_read(req, EXT_CFG_QP_PI_NUM) / func_num; - caps->num_pi_qps = round_down(qp_num, HNS_ROCE_QP_BANK_NUM); - - qp_num = hr_reg_read(req, EXT_CFG_QP_NUM) / func_num; - caps->num_qps = round_down(qp_num, HNS_ROCE_QP_BANK_NUM); - - return 0; -} - static int load_pf_timer_res_caps(struct hns_roce_dev *hr_dev) { struct hns_roce_cmq_desc desc; @@ -1723,36 +1751,16 @@ static int load_pf_timer_res_caps(struct hns_roce_dev *hr_dev) return 0; } -static int query_func_resource_caps(struct hns_roce_dev *hr_dev, bool is_vf) -{ - struct device *dev = hr_dev->dev; - int ret; - - ret = load_func_res_caps(hr_dev, is_vf); - if (ret) { - dev_err(dev, "failed to load res caps, ret = %d (%s).\n", ret, - is_vf ? "vf" : "pf"); - return ret; - } - - if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) { - ret = load_ext_cfg_caps(hr_dev, is_vf); - if (ret) - dev_err(dev, "failed to load ext cfg, ret = %d (%s).\n", - ret, is_vf ? "vf" : "pf"); - } - - return ret; -} - static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) { struct device *dev = hr_dev->dev; int ret; - ret = query_func_resource_caps(hr_dev, false); - if (ret) + ret = load_func_res_caps(hr_dev, false); + if (ret) { + dev_err(dev, "failed to load pf res caps, ret = %d.\n", ret); return ret; + } ret = load_pf_timer_res_caps(hr_dev); if (ret) @@ -1764,7 +1772,14 @@ static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) static int hns_roce_query_vf_resource(struct hns_roce_dev *hr_dev) { - return query_func_resource_caps(hr_dev, true); + struct device *dev = hr_dev->dev; + int ret; + + ret = load_func_res_caps(hr_dev, true); + if (ret) + dev_err(dev, "failed to load vf res caps, ret = %d.\n", ret); + + return ret; } static int __hns_roce_set_vf_switch_param(struct hns_roce_dev *hr_dev, @@ -1849,24 +1864,6 @@ static int config_vf_hem_resource(struct hns_roce_dev *hr_dev, int vf_id) return hns_roce_cmq_send(hr_dev, desc, 2); } -static int config_vf_ext_resource(struct hns_roce_dev *hr_dev, u32 vf_id) -{ - struct hns_roce_cmq_desc desc; - struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; - struct hns_roce_caps *caps = &hr_dev->caps; - - hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_EXT_CFG, false); - - hr_reg_write(req, EXT_CFG_VF_ID, vf_id); - - hr_reg_write(req, EXT_CFG_QP_PI_NUM, caps->num_pi_qps); - hr_reg_write(req, EXT_CFG_QP_PI_IDX, vf_id * caps->num_pi_qps); - hr_reg_write(req, EXT_CFG_QP_NUM, caps->num_qps); - hr_reg_write(req, EXT_CFG_QP_IDX, vf_id * caps->num_qps); - - return hns_roce_cmq_send(hr_dev, &desc, 1); -} - static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev) { u32 func_num = max_t(u32, 1, hr_dev->func_num); @@ -1881,16 +1878,6 @@ static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev) vf_id, ret); return ret; } - - if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) { - ret = config_vf_ext_resource(hr_dev, vf_id); - if (ret) { - dev_err(hr_dev->dev, - "failed to config vf-%u ext res, ret = %d.\n", - vf_id, ret); - return ret; - } - } } return 0; @@ -2075,9 +2062,6 @@ static void apply_func_caps(struct hns_roce_dev *hr_dev) caps->qpc_timer_hop_num = HNS_ROCE_HOP_NUM_0; caps->cqc_timer_hop_num = HNS_ROCE_HOP_NUM_0; - caps->num_xrcds = HNS_ROCE_V2_MAX_XRCD_NUM; - caps->reserved_xrcds = HNS_ROCE_V2_RSV_XRCD_NUM; - caps->num_srqwqe_segs = HNS_ROCE_V2_MAX_SRQWQE_SEGS; caps->num_idx_segs = HNS_ROCE_V2_MAX_IDX_SEGS; @@ -2200,6 +2184,7 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) caps->num_cqs = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_CQS); caps->gid_table_len[0] = hr_reg_read(resp_c, PF_CAPS_C_MAX_GID); caps->max_cqes = 1 << hr_reg_read(resp_c, PF_CAPS_C_CQ_DEPTH); + caps->num_xrcds = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_XRCDS); caps->num_mtpts = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_MRWS); caps->num_qps = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_QPS); caps->max_qp_init_rdma = hr_reg_read(resp_c, PF_CAPS_C_MAX_ORD); @@ -2220,6 +2205,7 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) caps->reserved_mrws = hr_reg_read(resp_e, PF_CAPS_E_RSV_MRWS); caps->chunk_sz = 1 << hr_reg_read(resp_e, PF_CAPS_E_CHUNK_SIZE_SHIFT); caps->reserved_cqs = hr_reg_read(resp_e, PF_CAPS_E_RSV_CQS); + caps->reserved_xrcds = hr_reg_read(resp_e, PF_CAPS_E_RSV_XRCDS); caps->reserved_srqs = hr_reg_read(resp_e, PF_CAPS_E_RSV_SRQS); caps->reserved_lkey = hr_reg_read(resp_e, PF_CAPS_E_RSV_LKEYS); @@ -6646,6 +6632,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .query_cqc = hns_roce_v2_query_cqc, .query_qpc = hns_roce_v2_query_qpc, .query_mpt = hns_roce_v2_query_mpt, + .query_hw_counter = hns_roce_hw_v2_query_counter, .hns_roce_dev_ops = &hns_roce_v2_dev_ops, .hns_roce_dev_srq_ops = &hns_roce_v2_dev_srq_ops, }; @@ -6722,14 +6709,14 @@ static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) ret = hns_roce_init(hr_dev); if (ret) { dev_err(hr_dev->dev, "RoCE Engine init failed!\n"); - goto error_failed_cfg; + goto error_failed_roce_init; } if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08) { ret = free_mr_init(hr_dev); if (ret) { dev_err(hr_dev->dev, "failed to init free mr!\n"); - goto error_failed_roce_init; + goto error_failed_free_mr_init; } } @@ -6737,10 +6724,10 @@ static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) return 0; -error_failed_roce_init: +error_failed_free_mr_init: hns_roce_exit(hr_dev); -error_failed_cfg: +error_failed_roce_init: kfree(hr_dev->priv); error_failed_kzalloc: diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 7033eae2407c..cd97cbee682a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -42,7 +42,6 @@ #define HNS_ROCE_V2_MAX_SRQWQE_SEGS 0x1000000 #define HNS_ROCE_V2_MAX_IDX_SEGS 0x1000000 #define HNS_ROCE_V2_MAX_XRCD_NUM 0x1000000 -#define HNS_ROCE_V2_RSV_XRCD_NUM 0 #define HNS_ROCE_V2_QP_ACK_TIMEOUT_OFS_HIP08 10 @@ -199,6 +198,7 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_QUERY_HW_VER = 0x8000, HNS_ROCE_OPC_CFG_GLOBAL_PARAM = 0x8001, HNS_ROCE_OPC_ALLOC_PF_RES = 0x8004, + HNS_ROCE_OPC_QUERY_COUNTER = 0x8206, HNS_ROCE_OPC_QUERY_PF_RES = 0x8400, HNS_ROCE_OPC_ALLOC_VF_RES = 0x8401, HNS_ROCE_OPC_CFG_EXT_LLM = 0x8403, @@ -220,7 +220,6 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_QUERY_VF_RES = 0x850e, HNS_ROCE_OPC_CFG_GMV_TBL = 0x850f, HNS_ROCE_OPC_CFG_GMV_BT = 0x8510, - HNS_ROCE_OPC_EXT_CFG = 0x8512, HNS_ROCE_QUERY_RAM_ECC = 0x8513, HNS_SWITCH_PARAMETER_CFG = 0x1033, }; @@ -957,15 +956,6 @@ struct hns_roce_func_clear { #define HNS_ROCE_V2_READ_FUNC_CLEAR_FLAG_INTERVAL 40 #define HNS_ROCE_V2_READ_FUNC_CLEAR_FLAG_FAIL_WAIT 20 -/* Fields of HNS_ROCE_OPC_EXT_CFG */ -#define EXT_CFG_VF_ID CMQ_REQ_FIELD_LOC(31, 0) -#define EXT_CFG_QP_PI_IDX CMQ_REQ_FIELD_LOC(45, 32) -#define EXT_CFG_QP_PI_NUM CMQ_REQ_FIELD_LOC(63, 48) -#define EXT_CFG_QP_NUM CMQ_REQ_FIELD_LOC(87, 64) -#define EXT_CFG_QP_IDX CMQ_REQ_FIELD_LOC(119, 96) -#define EXT_CFG_LLM_IDX CMQ_REQ_FIELD_LOC(139, 128) -#define EXT_CFG_LLM_NUM CMQ_REQ_FIELD_LOC(156, 144) - #define CFG_LLM_A_BA_L CMQ_REQ_FIELD_LOC(31, 0) #define CFG_LLM_A_BA_H CMQ_REQ_FIELD_LOC(63, 32) #define CFG_LLM_A_DEPTH CMQ_REQ_FIELD_LOC(76, 64) @@ -1202,6 +1192,7 @@ struct hns_roce_query_pf_caps_c { #define PF_CAPS_C_NUM_CQS PF_CAPS_C_FIELD_LOC(51, 32) #define PF_CAPS_C_MAX_GID PF_CAPS_C_FIELD_LOC(60, 52) #define PF_CAPS_C_CQ_DEPTH PF_CAPS_C_FIELD_LOC(86, 64) +#define PF_CAPS_C_NUM_XRCDS PF_CAPS_C_FIELD_LOC(91, 87) #define PF_CAPS_C_NUM_MRWS PF_CAPS_C_FIELD_LOC(115, 96) #define PF_CAPS_C_NUM_QPS PF_CAPS_C_FIELD_LOC(147, 128) #define PF_CAPS_C_MAX_ORD PF_CAPS_C_FIELD_LOC(155, 148) @@ -1260,6 +1251,7 @@ struct hns_roce_query_pf_caps_e { #define PF_CAPS_E_RSV_MRWS PF_CAPS_E_FIELD_LOC(19, 0) #define PF_CAPS_E_CHUNK_SIZE_SHIFT PF_CAPS_E_FIELD_LOC(31, 20) #define PF_CAPS_E_RSV_CQS PF_CAPS_E_FIELD_LOC(51, 32) +#define PF_CAPS_E_RSV_XRCDS PF_CAPS_E_FIELD_LOC(63, 52) #define PF_CAPS_E_RSV_SRQS PF_CAPS_E_FIELD_LOC(83, 64) #define PF_CAPS_E_RSV_LKEYS PF_CAPS_E_FIELD_LOC(115, 96) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 485e110ca433..d9d546cdef52 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -219,6 +219,7 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num, unsigned long flags; enum ib_mtu mtu; u32 port; + int ret; port = port_num - 1; @@ -231,8 +232,10 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num, IB_PORT_BOOT_MGMT_SUP; props->max_msg_sz = HNS_ROCE_MAX_MSG_LEN; props->pkey_tbl_len = 1; - props->active_width = IB_WIDTH_4X; - props->active_speed = 1; + ret = ib_get_eth_speed(ib_dev, port_num, &props->active_speed, + &props->active_width); + if (ret) + ibdev_warn(ib_dev, "failed to get speed, ret = %d.\n", ret); spin_lock_irqsave(&hr_dev->iboe.lock, flags); @@ -512,6 +515,83 @@ static void hns_roce_get_fw_ver(struct ib_device *device, char *str) sub_minor); } +#define HNS_ROCE_HW_CNT(ename, cname) \ + [HNS_ROCE_HW_##ename##_CNT].name = cname + +static const struct rdma_stat_desc hns_roce_port_stats_descs[] = { + HNS_ROCE_HW_CNT(RX_RC_PKT, "rx_rc_pkt"), + HNS_ROCE_HW_CNT(RX_UC_PKT, "rx_uc_pkt"), + HNS_ROCE_HW_CNT(RX_UD_PKT, "rx_ud_pkt"), + HNS_ROCE_HW_CNT(RX_XRC_PKT, "rx_xrc_pkt"), + HNS_ROCE_HW_CNT(RX_PKT, "rx_pkt"), + HNS_ROCE_HW_CNT(RX_ERR_PKT, "rx_err_pkt"), + HNS_ROCE_HW_CNT(RX_CNP_PKT, "rx_cnp_pkt"), + HNS_ROCE_HW_CNT(TX_RC_PKT, "tx_rc_pkt"), + HNS_ROCE_HW_CNT(TX_UC_PKT, "tx_uc_pkt"), + HNS_ROCE_HW_CNT(TX_UD_PKT, "tx_ud_pkt"), + HNS_ROCE_HW_CNT(TX_XRC_PKT, "tx_xrc_pkt"), + HNS_ROCE_HW_CNT(TX_PKT, "tx_pkt"), + HNS_ROCE_HW_CNT(TX_ERR_PKT, "tx_err_pkt"), + HNS_ROCE_HW_CNT(TX_CNP_PKT, "tx_cnp_pkt"), + HNS_ROCE_HW_CNT(TRP_GET_MPT_ERR_PKT, "trp_get_mpt_err_pkt"), + HNS_ROCE_HW_CNT(TRP_GET_IRRL_ERR_PKT, "trp_get_irrl_err_pkt"), + HNS_ROCE_HW_CNT(ECN_DB, "ecn_doorbell"), + HNS_ROCE_HW_CNT(RX_BUF, "rx_buffer"), + HNS_ROCE_HW_CNT(TRP_RX_SOF, "trp_rx_sof"), + HNS_ROCE_HW_CNT(CQ_CQE, "cq_cqe"), + HNS_ROCE_HW_CNT(CQ_POE, "cq_poe"), + HNS_ROCE_HW_CNT(CQ_NOTIFY, "cq_notify"), +}; + +static struct rdma_hw_stats *hns_roce_alloc_hw_port_stats( + struct ib_device *device, u32 port_num) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(device); + u32 port = port_num - 1; + + if (port > hr_dev->caps.num_ports) { + ibdev_err(device, "invalid port num.\n"); + return NULL; + } + + if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 || + hr_dev->is_vf) + return NULL; + + return rdma_alloc_hw_stats_struct(hns_roce_port_stats_descs, + ARRAY_SIZE(hns_roce_port_stats_descs), + RDMA_HW_STATS_DEFAULT_LIFESPAN); +} + +static int hns_roce_get_hw_stats(struct ib_device *device, + struct rdma_hw_stats *stats, + u32 port, int index) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(device); + int num_counters = HNS_ROCE_HW_CNT_TOTAL; + int ret; + + if (port == 0) + return 0; + + if (port > hr_dev->caps.num_ports) + return -EINVAL; + + if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 || + hr_dev->is_vf) + return -EOPNOTSUPP; + + ret = hr_dev->hw->query_hw_counter(hr_dev, stats->value, port, + &num_counters); + if (ret) { + ibdev_err(device, "failed to query hw counter, ret = %d\n", + ret); + return ret; + } + + return num_counters; +} + static void hns_roce_unregister_device(struct hns_roce_dev *hr_dev) { struct hns_roce_ib_iboe *iboe = &hr_dev->iboe; @@ -554,6 +634,8 @@ static const struct ib_device_ops hns_roce_dev_ops = { .query_pkey = hns_roce_query_pkey, .query_port = hns_roce_query_port, .reg_user_mr = hns_roce_reg_user_mr, + .alloc_hw_port_stats = hns_roce_alloc_hw_port_stats, + .get_hw_stats = hns_roce_get_hw_stats, INIT_RDMA_OBJ_SIZE(ib_ah, hns_roce_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, hns_roce_cq, ib_cq), diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index d855a917f4cf..cdc1c6de43a1 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -170,14 +170,29 @@ static void hns_roce_ib_qp_event(struct hns_roce_qp *hr_qp, } } -static u8 get_least_load_bankid_for_qp(struct hns_roce_bank *bank) +static u8 get_affinity_cq_bank(u8 qp_bank) { - u32 least_load = bank[0].inuse; + return (qp_bank >> 1) & CQ_BANKID_MASK; +} + +static u8 get_least_load_bankid_for_qp(struct ib_qp_init_attr *init_attr, + struct hns_roce_bank *bank) +{ +#define INVALID_LOAD_QPNUM 0xFFFFFFFF + struct ib_cq *scq = init_attr->send_cq; + u32 least_load = INVALID_LOAD_QPNUM; + unsigned long cqn = 0; u8 bankid = 0; u32 bankcnt; u8 i; - for (i = 1; i < HNS_ROCE_QP_BANK_NUM; i++) { + if (scq) + cqn = to_hr_cq(scq)->cqn; + + for (i = 0; i < HNS_ROCE_QP_BANK_NUM; i++) { + if (scq && (get_affinity_cq_bank(i) != (cqn & CQ_BANKID_MASK))) + continue; + bankcnt = bank[i].inuse; if (bankcnt < least_load) { least_load = bankcnt; @@ -209,7 +224,8 @@ static int alloc_qpn_with_bankid(struct hns_roce_bank *bank, u8 bankid, return 0; } -static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) +static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, + struct ib_qp_init_attr *init_attr) { struct hns_roce_qp_table *qp_table = &hr_dev->qp_table; unsigned long num = 0; @@ -220,7 +236,7 @@ static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) num = 1; } else { mutex_lock(&qp_table->bank_mutex); - bankid = get_least_load_bankid_for_qp(qp_table->bank); + bankid = get_least_load_bankid_for_qp(init_attr, qp_table->bank); ret = alloc_qpn_with_bankid(&qp_table->bank[bankid], bankid, &num); @@ -1082,7 +1098,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, goto err_buf; } - ret = alloc_qpn(hr_dev, hr_qp); + ret = alloc_qpn(hr_dev, hr_qp, init_attr); if (ret) { ibdev_err(ibdev, "failed to alloc QPN, ret = %d.\n", ret); goto err_qpn; diff --git a/drivers/infiniband/hw/hns/hns_roce_restrack.c b/drivers/infiniband/hw/hns/hns_roce_restrack.c index 989a2af2e938..081a01de3055 100644 --- a/drivers/infiniband/hw/hns/hns_roce_restrack.c +++ b/drivers/infiniband/hw/hns/hns_roce_restrack.c @@ -9,8 +9,6 @@ #include "hns_roce_device.h" #include "hns_roce_hw_v2.h" -#define MAX_ENTRY_NUM 256 - int hns_roce_fill_res_cq_entry(struct sk_buff *msg, struct ib_cq *ib_cq) { struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq); @@ -47,8 +45,6 @@ int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq) struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device); struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq); struct hns_roce_v2_cq_context context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_cqc) @@ -58,23 +54,7 @@ int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, CQC_CQ_ST); - data[offset++] = hr_reg_read(&context, CQC_SHIFT); - data[offset++] = hr_reg_read(&context, CQC_CQE_SIZE); - data[offset++] = hr_reg_read(&context, CQC_CQE_CNT); - data[offset++] = hr_reg_read(&context, CQC_CQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, CQC_CQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, CQC_DB_RECORD_EN); - data[offset++] = hr_reg_read(&context, CQC_ARM_ST); - data[offset++] = hr_reg_read(&context, CQC_CMD_SN); - data[offset++] = hr_reg_read(&context, CQC_CEQN); - data[offset++] = hr_reg_read(&context, CQC_CQ_MAX_CNT); - data[offset++] = hr_reg_read(&context, CQC_CQ_PERIOD); - data[offset++] = hr_reg_read(&context, CQC_CQE_HOP_NUM); - data[offset++] = hr_reg_read(&context, CQC_CQE_BAR_PG_SZ); - data[offset++] = hr_reg_read(&context, CQC_CQE_BUF_PG_SZ); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } @@ -118,8 +98,6 @@ int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp) struct hns_roce_dev *hr_dev = to_hr_dev(ib_qp->device); struct hns_roce_qp *hr_qp = to_hr_qp(ib_qp); struct hns_roce_v2_qp_context context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_qpc) @@ -129,42 +107,7 @@ int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, QPC_QP_ST); - data[offset++] = hr_reg_read(&context, QPC_ERR_TYPE); - data[offset++] = hr_reg_read(&context, QPC_CHECK_FLG); - data[offset++] = hr_reg_read(&context, QPC_SRQ_EN); - data[offset++] = hr_reg_read(&context, QPC_SRQN); - data[offset++] = hr_reg_read(&context, QPC_QKEY_XRCD); - data[offset++] = hr_reg_read(&context, QPC_TX_CQN); - data[offset++] = hr_reg_read(&context, QPC_RX_CQN); - data[offset++] = hr_reg_read(&context, QPC_SQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_RECORD_EN); - data[offset++] = hr_reg_read(&context, QPC_RQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_RQWS); - data[offset++] = hr_reg_read(&context, QPC_RQ_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_SGE_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_SQ_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_RQ_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_SGE_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_WQE_SGE_BA_PG_SZ); - data[offset++] = hr_reg_read(&context, QPC_WQE_SGE_BUF_PG_SZ); - data[offset++] = hr_reg_read(&context, QPC_RETRY_NUM_INIT); - data[offset++] = hr_reg_read(&context, QPC_RETRY_CNT); - data[offset++] = hr_reg_read(&context, QPC_SQ_CUR_PSN); - data[offset++] = hr_reg_read(&context, QPC_SQ_MAX_PSN); - data[offset++] = hr_reg_read(&context, QPC_SQ_FLUSH_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_MAX_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_TX_ERR); - data[offset++] = hr_reg_read(&context, QPC_SQ_RX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_RX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_TX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_CQE_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_RTY_TX_ERR); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } @@ -204,8 +147,6 @@ int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr) struct hns_roce_dev *hr_dev = to_hr_dev(ib_mr->device); struct hns_roce_mr *hr_mr = to_hr_mr(ib_mr); struct hns_roce_v2_mpt_entry context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_mpt) @@ -215,17 +156,7 @@ int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, MPT_ST); - data[offset++] = hr_reg_read(&context, MPT_PD); - data[offset++] = hr_reg_read(&context, MPT_LKEY); - data[offset++] = hr_reg_read(&context, MPT_LEN_L); - data[offset++] = hr_reg_read(&context, MPT_LEN_H); - data[offset++] = hr_reg_read(&context, MPT_PBL_SIZE); - data[offset++] = hr_reg_read(&context, MPT_PBL_HOP_NUM); - data[offset++] = hr_reg_read(&context, MPT_PBL_BA_PG_SZ); - data[offset++] = hr_reg_read(&context, MPT_PBL_BUF_PG_SZ); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c index 70009b970e08..42d1e9771066 100644 --- a/drivers/infiniband/hw/irdma/cm.c +++ b/drivers/infiniband/hw/irdma/cm.c @@ -1555,22 +1555,56 @@ static int irdma_del_multiple_qhash(struct irdma_device *iwdev, return ret; } +static u8 irdma_iw_get_vlan_prio(u32 *loc_addr, u8 prio, bool ipv4) +{ + struct net_device *ndev = NULL; + + rcu_read_lock(); + if (ipv4) { + ndev = ip_dev_find(&init_net, htonl(loc_addr[0])); + } else if (IS_ENABLED(CONFIG_IPV6)) { + struct net_device *ip_dev; + struct in6_addr laddr6; + + irdma_copy_ip_htonl(laddr6.in6_u.u6_addr32, loc_addr); + + for_each_netdev_rcu (&init_net, ip_dev) { + if (ipv6_chk_addr(&init_net, &laddr6, ip_dev, 1)) { + ndev = ip_dev; + break; + } + } + } + + if (!ndev) + goto done; + if (is_vlan_dev(ndev)) + prio = (vlan_dev_get_egress_qos_mask(ndev, prio) & VLAN_PRIO_MASK) + >> VLAN_PRIO_SHIFT; + if (ipv4) + dev_put(ndev); + +done: + rcu_read_unlock(); + + return prio; +} + /** - * irdma_netdev_vlan_ipv6 - Gets the netdev and mac + * irdma_get_vlan_mac_ipv6 - Gets the vlan and mac * @addr: local IPv6 address * @vlan_id: vlan id for the given IPv6 address * @mac: mac address for the given IPv6 address * - * Returns the net_device of the IPv6 address and also sets the - * vlan id and mac for that address. + * Returns the vlan id and mac for an IPv6 address. */ -struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) +void irdma_get_vlan_mac_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) { struct net_device *ip_dev = NULL; struct in6_addr laddr6; if (!IS_ENABLED(CONFIG_IPV6)) - return NULL; + return; irdma_copy_ip_htonl(laddr6.in6_u.u6_addr32, addr); if (vlan_id) @@ -1589,8 +1623,6 @@ struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) } } rcu_read_unlock(); - - return ip_dev; } /** @@ -1667,6 +1699,12 @@ static int irdma_add_mqh_6(struct irdma_device *iwdev, ifp->addr.in6_u.u6_addr32); memcpy(cm_info->loc_addr, child_listen_node->loc_addr, sizeof(cm_info->loc_addr)); + if (!iwdev->vsi.dscp_mode) + cm_info->user_pri = + irdma_iw_get_vlan_prio(child_listen_node->loc_addr, + cm_info->user_pri, + false); + ret = irdma_manage_qhash(iwdev, cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, @@ -1751,6 +1789,11 @@ static int irdma_add_mqh_4(struct irdma_device *iwdev, ntohl(ifa->ifa_address); memcpy(cm_info->loc_addr, child_listen_node->loc_addr, sizeof(cm_info->loc_addr)); + if (!iwdev->vsi.dscp_mode) + cm_info->user_pri = + irdma_iw_get_vlan_prio(child_listen_node->loc_addr, + cm_info->user_pri, + true); ret = irdma_manage_qhash(iwdev, cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, @@ -2219,6 +2262,10 @@ irdma_make_cm_node(struct irdma_cm_core *cm_core, struct irdma_device *iwdev, } else { cm_node->tos = max(listener->tos, cm_info->tos); cm_node->user_pri = rt_tos2priority(cm_node->tos); + cm_node->user_pri = + irdma_iw_get_vlan_prio(cm_info->loc_addr, + cm_node->user_pri, + cm_info->ipv4); } ibdev_dbg(&iwdev->ibdev, "DCB: listener: TOS:[%d] UP:[%d]\n", cm_node->tos, @@ -3577,7 +3624,6 @@ void irdma_free_lsmm_rsrc(struct irdma_qp *iwqp) iwqp->ietf_mem.size, iwqp->ietf_mem.va, iwqp->ietf_mem.pa); iwqp->ietf_mem.va = NULL; - iwqp->ietf_mem.va = NULL; } } @@ -3617,8 +3663,8 @@ int irdma_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_node->vlan_id = irdma_get_vlan_ipv4(cm_node->loc_addr); } else { cm_node->ipv4 = false; - irdma_netdev_vlan_ipv6(cm_node->loc_addr, &cm_node->vlan_id, - NULL); + irdma_get_vlan_mac_ipv6(cm_node->loc_addr, &cm_node->vlan_id, + NULL); } ibdev_dbg(&iwdev->ibdev, "CM: Accept vlan_id=%d\n", cm_node->vlan_id); @@ -3826,17 +3872,21 @@ int irdma_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) raddr6->sin6_addr.in6_u.u6_addr32); cm_info.loc_port = ntohs(laddr6->sin6_port); cm_info.rem_port = ntohs(raddr6->sin6_port); - irdma_netdev_vlan_ipv6(cm_info.loc_addr, &cm_info.vlan_id, - NULL); + irdma_get_vlan_mac_ipv6(cm_info.loc_addr, &cm_info.vlan_id, + NULL); } cm_info.cm_id = cm_id; cm_info.qh_qpid = iwdev->vsi.ilq->qp_id; cm_info.tos = cm_id->tos; - if (iwdev->vsi.dscp_mode) + if (iwdev->vsi.dscp_mode) { cm_info.user_pri = iwqp->sc_qp.vsi->dscp_map[irdma_tos2dscp(cm_info.tos)]; - else + } else { cm_info.user_pri = rt_tos2priority(cm_id->tos); + cm_info.user_pri = irdma_iw_get_vlan_prio(cm_info.loc_addr, + cm_info.user_pri, + cm_info.ipv4); + } if (iwqp->sc_qp.dev->ws_add(iwqp->sc_qp.vsi, cm_info.user_pri)) return -ENOMEM; @@ -3952,8 +4002,8 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) laddr6->sin6_addr.in6_u.u6_addr32); cm_info.loc_port = ntohs(laddr6->sin6_port); if (ipv6_addr_type(&laddr6->sin6_addr) != IPV6_ADDR_ANY) { - irdma_netdev_vlan_ipv6(cm_info.loc_addr, - &cm_info.vlan_id, NULL); + irdma_get_vlan_mac_ipv6(cm_info.loc_addr, + &cm_info.vlan_id, NULL); } else { cm_info.vlan_id = 0xFFFF; wildcard = true; @@ -3980,7 +4030,7 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) cm_listen_node->tos = cm_id->tos; if (iwdev->vsi.dscp_mode) cm_listen_node->user_pri = - iwdev->vsi.dscp_map[irdma_tos2dscp(cm_id->tos)]; + iwdev->vsi.dscp_map[irdma_tos2dscp(cm_id->tos)]; else cm_listen_node->user_pri = rt_tos2priority(cm_id->tos); cm_info.user_pri = cm_listen_node->user_pri; @@ -3990,6 +4040,12 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) if (err) goto error; } else { + if (!iwdev->vsi.dscp_mode) + cm_listen_node->user_pri = + irdma_iw_get_vlan_prio(cm_info.loc_addr, + cm_info.user_pri, + cm_info.ipv4); + cm_info.user_pri = cm_listen_node->user_pri; err = irdma_manage_qhash(iwdev, &cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, diff --git a/drivers/infiniband/hw/irdma/ctrl.c b/drivers/infiniband/hw/irdma/ctrl.c index 45e3344daa04..8a6200e55c54 100644 --- a/drivers/infiniband/hw/irdma/ctrl.c +++ b/drivers/infiniband/hw/irdma/ctrl.c @@ -1061,6 +1061,9 @@ static int irdma_sc_alloc_stag(struct irdma_sc_dev *dev, u64 hdr; enum irdma_page_size page_size; + if (!info->total_len && !info->all_memory) + return -EINVAL; + if (info->page_size == 0x40000000) page_size = IRDMA_PAGE_SIZE_1G; else if (info->page_size == 0x200000) @@ -1126,6 +1129,9 @@ static int irdma_sc_mr_reg_non_shared(struct irdma_sc_dev *dev, u8 addr_type; enum irdma_page_size page_size; + if (!info->total_len && !info->all_memory) + return -EINVAL; + if (info->page_size == 0x40000000) page_size = IRDMA_PAGE_SIZE_1G; else if (info->page_size == 0x200000) @@ -1301,7 +1307,6 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, sq_info.wr_id = info->wr_id; sq_info.signaled = info->signaled; - sq_info.push_wqe = info->push_wqe; wqe = irdma_qp_get_next_send_wqe(&qp->qp_uk, &wqe_idx, IRDMA_QP_WQE_MIN_QUANTA, 0, &sq_info); @@ -1335,7 +1340,6 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, FIELD_PREP(IRDMAQPSQ_HPAGESIZE, page_size) | FIELD_PREP(IRDMAQPSQ_STAGRIGHTS, info->access_rights) | FIELD_PREP(IRDMAQPSQ_VABASEDTO, info->addr_type) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, (sq_info.push_wqe ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -1346,13 +1350,9 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, print_hex_dump_debug("WQE: FAST_REG WQE", DUMP_PREFIX_OFFSET, 16, 8, wqe, IRDMA_QP_WQE_MIN_SIZE, false); - if (sq_info.push_wqe) { - irdma_qp_push_wqe(&qp->qp_uk, wqe, IRDMA_QP_WQE_MIN_QUANTA, - wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(&qp->qp_uk); - } + + if (post_sq) + irdma_uk_qp_post_wr(&qp->qp_uk); return 0; } @@ -4007,7 +4007,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, { u64 temp, compl_ctx; __le64 *aeqe; - u16 wqe_idx; u8 ae_src; u8 polarity; @@ -4027,7 +4026,7 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, aeqe, 16, false); ae_src = (u8)FIELD_GET(IRDMA_AEQE_AESRC, temp); - wqe_idx = (u16)FIELD_GET(IRDMA_AEQE_WQDESCIDX, temp); + info->wqe_idx = (u16)FIELD_GET(IRDMA_AEQE_WQDESCIDX, temp); info->qp_cq_id = (u32)FIELD_GET(IRDMA_AEQE_QPCQID_LOW, temp) | ((u32)FIELD_GET(IRDMA_AEQE_QPCQID_HI, temp) << 18); info->ae_id = (u16)FIELD_GET(IRDMA_AEQE_AECODE, temp); @@ -4110,7 +4109,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, case IRDMA_AE_SOURCE_RQ_0011: info->qp = true; info->rq = true; - info->wqe_idx = wqe_idx; info->compl_ctx = compl_ctx; break; case IRDMA_AE_SOURCE_CQ: @@ -4124,7 +4122,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, case IRDMA_AE_SOURCE_SQ_0111: info->qp = true; info->sq = true; - info->wqe_idx = wqe_idx; info->compl_ctx = compl_ctx; break; case IRDMA_AE_SOURCE_IN_RR_WR: diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c index 457368e324e1..7cbdd5433dba 100644 --- a/drivers/infiniband/hw/irdma/hw.c +++ b/drivers/infiniband/hw/irdma/hw.c @@ -219,7 +219,6 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) struct irdma_aeqe_info *info = &aeinfo; int ret; struct irdma_qp *iwqp = NULL; - struct irdma_sc_cq *cq = NULL; struct irdma_cq *iwcq = NULL; struct irdma_sc_qp *qp = NULL; struct irdma_qp_host_ctx_info *ctx_info = NULL; @@ -336,10 +335,18 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) ibdev_err(&iwdev->ibdev, "Processing an iWARP related AE for CQ misc = 0x%04X\n", info->ae_id); - cq = (struct irdma_sc_cq *)(unsigned long) - info->compl_ctx; - iwcq = cq->back_cq; + spin_lock_irqsave(&rf->cqtable_lock, flags); + iwcq = rf->cq_table[info->qp_cq_id]; + if (!iwcq) { + spin_unlock_irqrestore(&rf->cqtable_lock, + flags); + ibdev_dbg(to_ibdev(dev), + "cq_id %d is already freed\n", info->qp_cq_id); + continue; + } + irdma_cq_add_ref(&iwcq->ibcq); + spin_unlock_irqrestore(&rf->cqtable_lock, flags); if (iwcq->ibcq.event_handler) { struct ib_event ibevent; @@ -350,6 +357,7 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) iwcq->ibcq.event_handler(&ibevent, iwcq->ibcq.cq_context); } + irdma_cq_rem_ref(&iwcq->ibcq); break; case IRDMA_AE_RESET_NOT_SENT: case IRDMA_AE_LLP_DOUBT_REACHABILITY: @@ -563,12 +571,11 @@ static void irdma_destroy_irq(struct irdma_pci_f *rf, /** * irdma_destroy_cqp - destroy control qp * @rf: RDMA PCI function - * @free_hwcqp: 1 if hw cqp should be freed * * Issue destroy cqp request and * free the resources associated with the cqp */ -static void irdma_destroy_cqp(struct irdma_pci_f *rf, bool free_hwcqp) +static void irdma_destroy_cqp(struct irdma_pci_f *rf) { struct irdma_sc_dev *dev = &rf->sc_dev; struct irdma_cqp *cqp = &rf->cqp; @@ -576,8 +583,8 @@ static void irdma_destroy_cqp(struct irdma_pci_f *rf, bool free_hwcqp) if (rf->cqp_cmpl_wq) destroy_workqueue(rf->cqp_cmpl_wq); - if (free_hwcqp) - status = irdma_sc_cqp_destroy(dev->cqp); + + status = irdma_sc_cqp_destroy(dev->cqp); if (status) ibdev_dbg(to_ibdev(dev), "ERR: Destroy CQP failed %d\n", status); @@ -921,8 +928,8 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) cqp->scratch_array = kcalloc(sqsize, sizeof(*cqp->scratch_array), GFP_KERNEL); if (!cqp->scratch_array) { - kfree(cqp->cqp_requests); - return -ENOMEM; + status = -ENOMEM; + goto err_scratch; } dev->cqp = &cqp->sc_cqp; @@ -932,15 +939,14 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) cqp->sq.va = dma_alloc_coherent(dev->hw->device, cqp->sq.size, &cqp->sq.pa, GFP_KERNEL); if (!cqp->sq.va) { - kfree(cqp->scratch_array); - kfree(cqp->cqp_requests); - return -ENOMEM; + status = -ENOMEM; + goto err_sq; } status = irdma_obj_aligned_mem(rf, &mem, sizeof(struct irdma_cqp_ctx), IRDMA_HOST_CTX_ALIGNMENT_M); if (status) - goto exit; + goto err_ctx; dev->cqp->host_ctx_pa = mem.pa; dev->cqp->host_ctx = mem.va; @@ -966,7 +972,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) status = irdma_sc_cqp_init(dev->cqp, &cqp_init_info); if (status) { ibdev_dbg(to_ibdev(dev), "ERR: cqp init status %d\n", status); - goto exit; + goto err_ctx; } spin_lock_init(&cqp->req_lock); @@ -977,7 +983,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) ibdev_dbg(to_ibdev(dev), "ERR: cqp create failed - status %d maj_err %d min_err %d\n", status, maj_err, min_err); - goto exit; + goto err_ctx; } INIT_LIST_HEAD(&cqp->cqp_avail_reqs); @@ -991,8 +997,16 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) init_waitqueue_head(&cqp->remove_wq); return 0; -exit: - irdma_destroy_cqp(rf, false); +err_ctx: + dma_free_coherent(dev->hw->device, cqp->sq.size, + cqp->sq.va, cqp->sq.pa); + cqp->sq.va = NULL; +err_sq: + kfree(cqp->scratch_array); + cqp->scratch_array = NULL; +err_scratch: + kfree(cqp->cqp_requests); + cqp->cqp_requests = NULL; return status; } @@ -1549,7 +1563,7 @@ static void irdma_del_init_mem(struct irdma_pci_f *rf) kfree(dev->hmc_info->sd_table.sd_entry); dev->hmc_info->sd_table.sd_entry = NULL; - kfree(rf->mem_rsrc); + vfree(rf->mem_rsrc); rf->mem_rsrc = NULL; dma_free_coherent(rf->hw.device, rf->obj_mem.size, rf->obj_mem.va, rf->obj_mem.pa); @@ -1747,7 +1761,7 @@ void irdma_ctrl_deinit_hw(struct irdma_pci_f *rf) rf->reset, rf->rdma_ver); fallthrough; case CQP_CREATED: - irdma_destroy_cqp(rf, true); + irdma_destroy_cqp(rf); fallthrough; case INITIAL_STATE: irdma_del_init_mem(rf); @@ -1945,10 +1959,12 @@ static void irdma_set_hw_rsrc(struct irdma_pci_f *rf) rf->allocated_arps = &rf->allocated_mcgs[BITS_TO_LONGS(rf->max_mcg)]; rf->qp_table = (struct irdma_qp **) (&rf->allocated_arps[BITS_TO_LONGS(rf->arp_table_size)]); + rf->cq_table = (struct irdma_cq **)(&rf->qp_table[rf->max_qp]); spin_lock_init(&rf->rsrc_lock); spin_lock_init(&rf->arp_lock); spin_lock_init(&rf->qptable_lock); + spin_lock_init(&rf->cqtable_lock); spin_lock_init(&rf->qh_list_lock); } @@ -1969,6 +1985,7 @@ static u32 irdma_calc_mem_rsrc_size(struct irdma_pci_f *rf) rsrc_size += sizeof(unsigned long) * BITS_TO_LONGS(rf->max_ah); rsrc_size += sizeof(unsigned long) * BITS_TO_LONGS(rf->max_mcg); rsrc_size += sizeof(struct irdma_qp **) * rf->max_qp; + rsrc_size += sizeof(struct irdma_cq **) * rf->max_cq; return rsrc_size; } @@ -2002,10 +2019,10 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) rf->max_mcg = rf->max_qp; rsrc_size = irdma_calc_mem_rsrc_size(rf); - rf->mem_rsrc = kzalloc(rsrc_size, GFP_KERNEL); + rf->mem_rsrc = vzalloc(rsrc_size); if (!rf->mem_rsrc) { ret = -ENOMEM; - goto mem_rsrc_kzalloc_fail; + goto mem_rsrc_vzalloc_fail; } rf->arp_table = (struct irdma_arp_entry *)rf->mem_rsrc; @@ -2033,7 +2050,7 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) return 0; -mem_rsrc_kzalloc_fail: +mem_rsrc_vzalloc_fail: bitmap_free(rf->allocated_ws_nodes); rf->allocated_ws_nodes = NULL; diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.c b/drivers/infiniband/hw/irdma/i40iw_hw.c index 37a40fb4d0d7..638d127fb3e0 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.c +++ b/drivers/infiniband/hw/irdma/i40iw_hw.c @@ -254,5 +254,6 @@ void i40iw_init_hw(struct irdma_sc_dev *dev) dev->hw_attrs.max_stat_idx = IRDMA_HW_STAT_INDEX_MAX_GEN_1; dev->hw_attrs.max_hw_outbound_msg_size = I40IW_MAX_OUTBOUND_MSG_SIZE; dev->hw_attrs.max_hw_inbound_msg_size = I40IW_MAX_INBOUND_MSG_SIZE; + dev->hw_attrs.uk_attrs.min_hw_wq_size = I40IW_MIN_WQ_SIZE; dev->hw_attrs.max_qp_wr = I40IW_MAX_QP_WRS; } diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.h b/drivers/infiniband/hw/irdma/i40iw_hw.h index 1c438b3593ea..10afc165f5ea 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.h +++ b/drivers/infiniband/hw/irdma/i40iw_hw.h @@ -140,11 +140,11 @@ enum i40iw_device_caps_const { I40IW_MAX_CQ_SIZE = 1048575, I40IW_MAX_OUTBOUND_MSG_SIZE = 2147483647, I40IW_MAX_INBOUND_MSG_SIZE = 2147483647, + I40IW_MIN_WQ_SIZE = 4 /* WQEs */, }; #define I40IW_QP_WQE_MIN_SIZE 32 #define I40IW_QP_WQE_MAX_SIZE 128 -#define I40IW_QP_SW_MIN_WQSIZE 4 #define I40IW_MAX_RQ_WQE_SHIFT 2 #define I40IW_MAX_QUANTA_PER_WR 2 diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.c b/drivers/infiniband/hw/irdma/icrdma_hw.c index 298d14905993..10ccf4bc3f2d 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.c +++ b/drivers/infiniband/hw/irdma/icrdma_hw.c @@ -195,6 +195,7 @@ void icrdma_init_hw(struct irdma_sc_dev *dev) dev->hw_attrs.max_stat_inst = ICRDMA_MAX_STATS_COUNT; dev->hw_attrs.max_stat_idx = IRDMA_HW_STAT_INDEX_MAX_GEN_2; + dev->hw_attrs.uk_attrs.min_hw_wq_size = ICRDMA_MIN_WQ_SIZE; dev->hw_attrs.uk_attrs.max_hw_sq_chunk = IRDMA_MAX_QUANTA_PER_WR; dev->hw_attrs.uk_attrs.feature_flags |= IRDMA_FEATURE_RTS_AE | IRDMA_FEATURE_CQ_RESIZE; diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.h b/drivers/infiniband/hw/irdma/icrdma_hw.h index b65c463abf0b..54035a08cc93 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.h +++ b/drivers/infiniband/hw/irdma/icrdma_hw.h @@ -64,6 +64,7 @@ enum icrdma_device_caps_const { ICRDMA_MAX_IRD_SIZE = 127, ICRDMA_MAX_ORD_SIZE = 255, + ICRDMA_MIN_WQ_SIZE = 8 /* WQEs */, }; diff --git a/drivers/infiniband/hw/irdma/irdma.h b/drivers/infiniband/hw/irdma/irdma.h index 173e2dc2fc35..3237fa64bc8f 100644 --- a/drivers/infiniband/hw/irdma/irdma.h +++ b/drivers/infiniband/hw/irdma/irdma.h @@ -119,6 +119,7 @@ struct irdma_uk_attrs { u32 min_hw_cq_size; u32 max_hw_cq_size; u16 max_hw_sq_chunk; + u16 min_hw_wq_size; u8 hw_rev; }; diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h index 2323962cdeac..82fc5f5b002c 100644 --- a/drivers/infiniband/hw/irdma/main.h +++ b/drivers/infiniband/hw/irdma/main.h @@ -239,7 +239,7 @@ struct irdma_qv_info { struct irdma_qvlist_info { u32 num_vectors; - struct irdma_qv_info qv_info[1]; + struct irdma_qv_info qv_info[]; }; struct irdma_gen_ops { @@ -309,7 +309,9 @@ struct irdma_pci_f { spinlock_t arp_lock; /*protect ARP table access*/ spinlock_t rsrc_lock; /* protect HW resource array access */ spinlock_t qptable_lock; /*protect QP table access*/ + spinlock_t cqtable_lock; /*protect CQ table access*/ struct irdma_qp **qp_table; + struct irdma_cq **cq_table; spinlock_t qh_list_lock; /* protect mc_qht_list */ struct mc_table_list mc_qht_list; struct irdma_msix_vector *iw_msixtbl; @@ -500,6 +502,8 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); +void irdma_cq_add_ref(struct ib_cq *ibcq); +void irdma_cq_rem_ref(struct ib_cq *ibcq); void irdma_cq_wq_destroy(struct irdma_pci_f *rf, struct irdma_sc_cq *cq); void irdma_cleanup_pending_cqp_op(struct irdma_pci_f *rf); @@ -529,7 +533,7 @@ void irdma_gen_ae(struct irdma_pci_f *rf, struct irdma_sc_qp *qp, void irdma_copy_ip_ntohl(u32 *dst, __be32 *src); void irdma_copy_ip_htonl(__be32 *dst, u32 *src); u16 irdma_get_vlan_ipv4(u32 *addr); -struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac); +void irdma_get_vlan_mac_ipv6(u32 *addr, u16 *vlan_id, u8 *mac); struct ib_mr *irdma_reg_phys_mr(struct ib_pd *ib_pd, u64 addr, u64 size, int acc, u64 *iova_start); int irdma_upload_qp_context(struct irdma_qp *iwqp, bool freeze, bool raw); diff --git a/drivers/infiniband/hw/irdma/type.h b/drivers/infiniband/hw/irdma/type.h index a20709577ab0..c84ec4dd8536 100644 --- a/drivers/infiniband/hw/irdma/type.h +++ b/drivers/infiniband/hw/irdma/type.h @@ -971,6 +971,7 @@ struct irdma_allocate_stag_info { bool remote_access:1; bool use_hmc_fcn_index:1; bool use_pf_rid:1; + bool all_memory:1; u8 hmc_fcn_index; }; @@ -998,6 +999,7 @@ struct irdma_reg_ns_stag_info { bool use_hmc_fcn_index:1; u8 hmc_fcn_index; bool use_pf_rid:1; + bool all_memory:1; }; struct irdma_fast_reg_stag_info { @@ -1017,7 +1019,6 @@ struct irdma_fast_reg_stag_info { bool local_fence:1; bool read_fence:1; bool signaled:1; - bool push_wqe:1; bool use_hmc_fcn_index:1; u8 hmc_fcn_index; bool use_pf_rid:1; diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c index 280d633d4ec4..d8285ca16293 100644 --- a/drivers/infiniband/hw/irdma/uk.c +++ b/drivers/infiniband/hw/irdma/uk.c @@ -127,10 +127,7 @@ void irdma_uk_qp_post_wr(struct irdma_qp_uk *qp) hw_sq_tail = (u32)FIELD_GET(IRDMA_QP_DBSA_HW_SQ_TAIL, temp); sw_sq_head = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); if (sw_sq_head != qp->initial_ring.head) { - if (qp->push_dropped) { - writel(qp->qp_id, qp->wqe_alloc_db); - qp->push_dropped = false; - } else if (sw_sq_head != hw_sq_tail) { + if (sw_sq_head != hw_sq_tail) { if (sw_sq_head > qp->initial_ring.head) { if (hw_sq_tail >= qp->initial_ring.head && hw_sq_tail < sw_sq_head) @@ -146,38 +143,6 @@ void irdma_uk_qp_post_wr(struct irdma_qp_uk *qp) qp->initial_ring.head = qp->sq_ring.head; } -/** - * irdma_qp_ring_push_db - ring qp doorbell - * @qp: hw qp ptr - * @wqe_idx: wqe index - */ -static void irdma_qp_ring_push_db(struct irdma_qp_uk *qp, u32 wqe_idx) -{ - set_32bit_val(qp->push_db, 0, - FIELD_PREP(IRDMA_WQEALLOC_WQE_DESC_INDEX, wqe_idx >> 3) | qp->qp_id); - qp->initial_ring.head = qp->sq_ring.head; - qp->push_mode = true; - qp->push_dropped = false; -} - -void irdma_qp_push_wqe(struct irdma_qp_uk *qp, __le64 *wqe, u16 quanta, - u32 wqe_idx, bool post_sq) -{ - __le64 *push; - - if (IRDMA_RING_CURRENT_HEAD(qp->initial_ring) != - IRDMA_RING_CURRENT_TAIL(qp->sq_ring) && - !qp->push_mode) { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } else { - push = (__le64 *)((uintptr_t)qp->push_wqe + - (wqe_idx & 0x7) * 0x20); - memcpy(push, wqe, quanta * IRDMA_QP_WQE_MIN_SIZE); - irdma_qp_ring_push_db(qp, wqe_idx); - } -} - /** * irdma_qp_get_next_send_wqe - pad with NOP if needed, return where next WR should go * @qp: hw qp ptr @@ -192,7 +157,6 @@ __le64 *irdma_qp_get_next_send_wqe(struct irdma_qp_uk *qp, u32 *wqe_idx, { __le64 *wqe; __le64 *wqe_0 = NULL; - u32 nop_wqe_idx; u16 avail_quanta; u16 i; @@ -209,14 +173,10 @@ __le64 *irdma_qp_get_next_send_wqe(struct irdma_qp_uk *qp, u32 *wqe_idx, IRDMA_SQ_RING_FREE_QUANTA(qp->sq_ring)) return NULL; - nop_wqe_idx = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); for (i = 0; i < avail_quanta; i++) { irdma_nop_1(qp); IRDMA_RING_MOVE_HEAD_NOCHECK(qp->sq_ring); } - if (qp->push_db && info->push_wqe) - irdma_qp_push_wqe(qp, qp->sq_base[nop_wqe_idx].elem, - avail_quanta, nop_wqe_idx, true); } *wqe_idx = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); @@ -282,8 +242,6 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, bool read_fence = false; u16 quanta; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.rdma_write; if (op_info->num_lo_sges > qp->max_sq_frag_cnt) return -EINVAL; @@ -344,7 +302,6 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_IMMDATAFLAG, info->imm_data_valid) | FIELD_PREP(IRDMAQPSQ_REPORTRTT, info->report_rtt) | FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -353,12 +310,9 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -383,8 +337,6 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, u16 quanta; u64 hdr; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.rdma_read; if (qp->max_sq_frag_cnt < op_info->num_lo_sges) return -EINVAL; @@ -431,7 +383,6 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | FIELD_PREP(IRDMAQPSQ_OPCODE, (inv_stag ? IRDMAQP_OP_RDMA_READ_LOC_INV : IRDMAQP_OP_RDMA_READ)) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -440,12 +391,9 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -468,8 +416,6 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, bool read_fence = false; u16 quanta; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.send; if (qp->max_sq_frag_cnt < op_info->num_sges) return -EINVAL; @@ -530,7 +476,6 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_REPORTRTT, (info->report_rtt ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_OPCODE, info->op_type) | FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -541,12 +486,9 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -720,7 +662,6 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, u32 i, total_size = 0; u16 quanta; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.rdma_write; if (unlikely(qp->max_sq_frag_cnt < op_info->num_lo_sges)) @@ -750,7 +691,6 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, FIELD_PREP(IRDMAQPSQ_REPORTRTT, info->report_rtt ? 1 : 0) | FIELD_PREP(IRDMAQPSQ_INLINEDATAFLAG, 1) | FIELD_PREP(IRDMAQPSQ_IMMDATAFLAG, info->imm_data_valid ? 1 : 0) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe ? 1 : 0) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -767,12 +707,8 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -794,7 +730,6 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, u32 i, total_size = 0; u16 quanta; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.send; if (unlikely(qp->max_sq_frag_cnt < op_info->num_sges)) @@ -827,7 +762,6 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, (info->imm_data_valid ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_REPORTRTT, (info->report_rtt ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_INLINEDATAFLAG, 1) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -845,12 +779,8 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -872,7 +802,6 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, bool local_fence = false; struct ib_sge sge = {}; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.inv_local_stag; local_fence = info->local_fence; @@ -889,7 +818,6 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, set_64bit_val(wqe, 16, 0); hdr = FIELD_PREP(IRDMAQPSQ_OPCODE, IRDMA_OP_TYPE_INV_STAG) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -899,13 +827,8 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, IRDMA_QP_WQE_MIN_QUANTA, wqe_idx, - post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -1124,7 +1047,6 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq, info->q_type = (u8)FIELD_GET(IRDMA_CQ_SQ, qword3); info->error = (bool)FIELD_GET(IRDMA_CQ_ERROR, qword3); - info->push_dropped = (bool)FIELD_GET(IRDMACQ_PSHDROP, qword3); info->ipv4 = (bool)FIELD_GET(IRDMACQ_IPV4, qword3); if (info->error) { info->major_err = FIELD_GET(IRDMA_CQ_MAJERR, qword3); @@ -1213,11 +1135,6 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq, return irdma_uk_cq_poll_cmpl(cq, info); } } - /*cease posting push mode on push drop*/ - if (info->push_dropped) { - qp->push_mode = false; - qp->push_dropped = true; - } if (info->comp_status != IRDMA_COMPL_STATUS_FLUSHED) { info->wr_id = qp->sq_wrtrk_array[wqe_idx].wrid; if (!info->comp_status) @@ -1349,10 +1266,12 @@ void irdma_get_wqe_shift(struct irdma_uk_attrs *uk_attrs, u32 sge, int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, u32 *sqdepth) { + u32 min_size = (u32)uk_attrs->min_hw_wq_size << shift; + *sqdepth = irdma_qp_round_up((sq_size << shift) + IRDMA_SQ_RSVD); - if (*sqdepth < (IRDMA_QP_SW_MIN_WQSIZE << shift)) - *sqdepth = IRDMA_QP_SW_MIN_WQSIZE << shift; + if (*sqdepth < min_size) + *sqdepth = min_size; else if (*sqdepth > uk_attrs->max_hw_wq_quanta) return -EINVAL; @@ -1369,10 +1288,12 @@ int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, int irdma_get_rqdepth(struct irdma_uk_attrs *uk_attrs, u32 rq_size, u8 shift, u32 *rqdepth) { + u32 min_size = (u32)uk_attrs->min_hw_wq_size << shift; + *rqdepth = irdma_qp_round_up((rq_size << shift) + IRDMA_RQ_RSVD); - if (*rqdepth < (IRDMA_QP_SW_MIN_WQSIZE << shift)) - *rqdepth = IRDMA_QP_SW_MIN_WQSIZE << shift; + if (*rqdepth < min_size) + *rqdepth = min_size; else if (*rqdepth > uk_attrs->max_hw_rq_quanta) return -EINVAL; @@ -1414,6 +1335,78 @@ static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp, IRDMA_RING_MOVE_HEAD_BY_COUNT_NOCHECK(qp->initial_ring, move_cnt); } +/** + * irdma_uk_calc_shift_wq - calculate WQE shift for both SQ and RQ + * @ukinfo: qp initialization info + * @sq_shift: Returns shift of SQ + * @rq_shift: Returns shift of RQ + */ +void irdma_uk_calc_shift_wq(struct irdma_qp_uk_init_info *ukinfo, u8 *sq_shift, + u8 *rq_shift) +{ + bool imm_support = ukinfo->uk_attrs->hw_rev >= IRDMA_GEN_2; + + irdma_get_wqe_shift(ukinfo->uk_attrs, + imm_support ? ukinfo->max_sq_frag_cnt + 1 : + ukinfo->max_sq_frag_cnt, + ukinfo->max_inline_data, sq_shift); + + irdma_get_wqe_shift(ukinfo->uk_attrs, ukinfo->max_rq_frag_cnt, 0, + rq_shift); + + if (ukinfo->uk_attrs->hw_rev == IRDMA_GEN_1) { + if (ukinfo->abi_ver > 4) + *rq_shift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; + } +} + +/** + * irdma_uk_calc_depth_shift_sq - calculate depth and shift for SQ size. + * @ukinfo: qp initialization info + * @sq_depth: Returns depth of SQ + * @sq_shift: Returns shift of SQ + */ +int irdma_uk_calc_depth_shift_sq(struct irdma_qp_uk_init_info *ukinfo, + u32 *sq_depth, u8 *sq_shift) +{ + bool imm_support = ukinfo->uk_attrs->hw_rev >= IRDMA_GEN_2; + int status; + + irdma_get_wqe_shift(ukinfo->uk_attrs, + imm_support ? ukinfo->max_sq_frag_cnt + 1 : + ukinfo->max_sq_frag_cnt, + ukinfo->max_inline_data, sq_shift); + status = irdma_get_sqdepth(ukinfo->uk_attrs, ukinfo->sq_size, + *sq_shift, sq_depth); + + return status; +} + +/** + * irdma_uk_calc_depth_shift_rq - calculate depth and shift for RQ size. + * @ukinfo: qp initialization info + * @rq_depth: Returns depth of RQ + * @rq_shift: Returns shift of RQ + */ +int irdma_uk_calc_depth_shift_rq(struct irdma_qp_uk_init_info *ukinfo, + u32 *rq_depth, u8 *rq_shift) +{ + int status; + + irdma_get_wqe_shift(ukinfo->uk_attrs, ukinfo->max_rq_frag_cnt, 0, + rq_shift); + + if (ukinfo->uk_attrs->hw_rev == IRDMA_GEN_1) { + if (ukinfo->abi_ver > 4) + *rq_shift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; + } + + status = irdma_get_rqdepth(ukinfo->uk_attrs, ukinfo->rq_size, + *rq_shift, rq_depth); + + return status; +} + /** * irdma_uk_qp_init - initialize shared qp * @qp: hw qp (user and kernel) @@ -1428,23 +1421,12 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) { int ret_code = 0; u32 sq_ring_size; - u8 sqshift, rqshift; qp->uk_attrs = info->uk_attrs; if (info->max_sq_frag_cnt > qp->uk_attrs->max_hw_wq_frags || info->max_rq_frag_cnt > qp->uk_attrs->max_hw_wq_frags) return -EINVAL; - irdma_get_wqe_shift(qp->uk_attrs, info->max_rq_frag_cnt, 0, &rqshift); - if (qp->uk_attrs->hw_rev == IRDMA_GEN_1) { - irdma_get_wqe_shift(qp->uk_attrs, info->max_sq_frag_cnt, - info->max_inline_data, &sqshift); - if (info->abi_ver > 4) - rqshift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; - } else { - irdma_get_wqe_shift(qp->uk_attrs, info->max_sq_frag_cnt + 1, - info->max_inline_data, &sqshift); - } qp->qp_caps = info->qp_caps; qp->sq_base = info->sq; qp->rq_base = info->rq; @@ -1456,9 +1438,8 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) qp->wqe_alloc_db = info->wqe_alloc_db; qp->qp_id = info->qp_id; qp->sq_size = info->sq_size; - qp->push_mode = false; qp->max_sq_frag_cnt = info->max_sq_frag_cnt; - sq_ring_size = qp->sq_size << sqshift; + sq_ring_size = qp->sq_size << info->sq_shift; IRDMA_RING_INIT(qp->sq_ring, sq_ring_size); IRDMA_RING_INIT(qp->initial_ring, sq_ring_size); if (info->first_sq_wq) { @@ -1473,9 +1454,9 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) qp->rq_size = info->rq_size; qp->max_rq_frag_cnt = info->max_rq_frag_cnt; qp->max_inline_data = info->max_inline_data; - qp->rq_wqe_size = rqshift; + qp->rq_wqe_size = info->rq_shift; IRDMA_RING_INIT(qp->rq_ring, qp->rq_size); - qp->rq_wqe_size_multiplier = 1 << rqshift; + qp->rq_wqe_size_multiplier = 1 << info->rq_shift; if (qp->uk_attrs->hw_rev == IRDMA_GEN_1) qp->wqe_ops = iw_wqe_uk_ops_gen_1; else @@ -1554,7 +1535,6 @@ int irdma_nop(struct irdma_qp_uk *qp, u64 wr_id, bool signaled, bool post_sq) u32 wqe_idx; struct irdma_post_sq_info info = {}; - info.push_wqe = false; info.wr_id = wr_id; wqe = irdma_qp_get_next_send_wqe(qp, &wqe_idx, IRDMA_QP_WQE_MIN_QUANTA, 0, &info); diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h index d0cdf609f5e0..36feca57b274 100644 --- a/drivers/infiniband/hw/irdma/user.h +++ b/drivers/infiniband/hw/irdma/user.h @@ -85,6 +85,7 @@ enum irdma_device_caps_const { IRDMA_Q2_BUF_SIZE = 256, IRDMA_QP_CTX_SIZE = 256, IRDMA_MAX_PDS = 262144, + IRDMA_MIN_WQ_SIZE_GEN2 = 8, }; enum irdma_addressing_type { @@ -215,7 +216,6 @@ struct irdma_post_sq_info { bool local_fence:1; bool inline_data:1; bool imm_data_valid:1; - bool push_wqe:1; bool report_rtt:1; bool udp_hdr:1; bool defer_flag:1; @@ -247,7 +247,6 @@ struct irdma_cq_poll_info { u8 op_type; u8 q_type; bool stag_invalid_set:1; /* or L_R_Key set */ - bool push_dropped:1; bool error:1; bool solicited_event:1; bool ipv4:1; @@ -295,6 +294,12 @@ void irdma_uk_cq_init(struct irdma_cq_uk *cq, struct irdma_cq_uk_init_info *info); int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info); +void irdma_uk_calc_shift_wq(struct irdma_qp_uk_init_info *ukinfo, u8 *sq_shift, + u8 *rq_shift); +int irdma_uk_calc_depth_shift_sq(struct irdma_qp_uk_init_info *ukinfo, + u32 *sq_depth, u8 *sq_shift); +int irdma_uk_calc_depth_shift_rq(struct irdma_qp_uk_init_info *ukinfo, + u32 *rq_depth, u8 *rq_shift); struct irdma_sq_uk_wr_trk_info { u64 wrid; u32 wr_len; @@ -314,8 +319,6 @@ struct irdma_qp_uk { struct irdma_sq_uk_wr_trk_info *sq_wrtrk_array; u64 *rq_wrid_array; __le64 *shadow_area; - __le32 *push_db; - __le64 *push_wqe; struct irdma_ring sq_ring; struct irdma_ring rq_ring; struct irdma_ring initial_ring; @@ -335,8 +338,6 @@ struct irdma_qp_uk { u8 rq_wqe_size; u8 rq_wqe_size_multiplier; bool deferred_flag:1; - bool push_mode:1; /* whether the last post wqe was pushed */ - bool push_dropped:1; bool first_sq_wq:1; bool sq_flush_complete:1; /* Indicates flush was seen and SQ was empty after the flush */ bool rq_flush_complete:1; /* Indicates flush was seen and RQ was empty after the flush */ @@ -374,8 +375,12 @@ struct irdma_qp_uk_init_info { u32 max_sq_frag_cnt; u32 max_rq_frag_cnt; u32 max_inline_data; + u32 sq_depth; + u32 rq_depth; u8 first_sq_wq; u8 type; + u8 sq_shift; + u8 rq_shift; int abi_ver; bool legacy_mode; }; @@ -404,7 +409,5 @@ int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, u32 *wqdepth); int irdma_get_rqdepth(struct irdma_uk_attrs *uk_attrs, u32 rq_size, u8 shift, u32 *wqdepth); -void irdma_qp_push_wqe(struct irdma_qp_uk *qp, __le64 *wqe, u16 quanta, - u32 wqe_idx, bool post_sq); void irdma_clr_wqes(struct irdma_qp_uk *qp, u32 qp_wqe_idx); #endif /* IRDMA_USER_H */ diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index eb083f70b09f..6cd5cb85dafe 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -760,6 +760,31 @@ void irdma_qp_rem_ref(struct ib_qp *ibqp) complete(&iwqp->free_qp); } +void irdma_cq_add_ref(struct ib_cq *ibcq) +{ + struct irdma_cq *iwcq = to_iwcq(ibcq); + + refcount_inc(&iwcq->refcnt); +} + +void irdma_cq_rem_ref(struct ib_cq *ibcq) +{ + struct ib_device *ibdev = ibcq->device; + struct irdma_device *iwdev = to_iwdev(ibdev); + struct irdma_cq *iwcq = to_iwcq(ibcq); + unsigned long flags; + + spin_lock_irqsave(&iwdev->rf->cqtable_lock, flags); + if (!refcount_dec_and_test(&iwcq->refcnt)) { + spin_unlock_irqrestore(&iwdev->rf->cqtable_lock, flags); + return; + } + + iwdev->rf->cq_table[iwcq->cq_num] = NULL; + spin_unlock_irqrestore(&iwdev->rf->cqtable_lock, flags); + complete(&iwcq->free_cq); +} + struct ib_device *to_ibdev(struct irdma_sc_dev *dev) { return &(container_of(dev, struct irdma_pci_f, sc_dev))->iwdev->ibdev; diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 9c4fe4fa9001..3eb7a7a3a975 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -277,7 +277,7 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, struct irdma_alloc_ucontext_req req = {}; struct irdma_alloc_ucontext_resp uresp = {}; struct irdma_ucontext *ucontext = to_ucontext(uctx); - struct irdma_uk_attrs *uk_attrs; + struct irdma_uk_attrs *uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; if (udata->inlen < IRDMA_ALLOC_UCTX_MIN_REQ_LEN || udata->outlen < IRDMA_ALLOC_UCTX_MIN_RESP_LEN) @@ -292,7 +292,9 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, ucontext->iwdev = iwdev; ucontext->abi_ver = req.userspace_ver; - uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; + if (req.comp_mask & IRDMA_ALLOC_UCTX_USE_RAW_ATTR) + ucontext->use_raw_attrs = true; + /* GEN_1 legacy support with libi40iw */ if (udata->outlen == IRDMA_ALLOC_UCTX_MIN_RESP_LEN) { if (uk_attrs->hw_rev != IRDMA_GEN_1) @@ -327,6 +329,9 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, uresp.max_hw_cq_size = uk_attrs->max_hw_cq_size; uresp.min_hw_cq_size = uk_attrs->min_hw_cq_size; uresp.hw_rev = uk_attrs->hw_rev; + uresp.comp_mask |= IRDMA_ALLOC_UCTX_USE_RAW_ATTR; + uresp.min_hw_wq_size = uk_attrs->min_hw_wq_size; + uresp.comp_mask |= IRDMA_ALLOC_UCTX_MIN_HW_WQ_SIZE; if (ib_copy_to_udata(udata, &uresp, min(sizeof(uresp), udata->outlen))) { rdma_user_mmap_entry_remove(ucontext->db_mmap_entry); @@ -566,6 +571,87 @@ static void irdma_setup_virt_qp(struct irdma_device *iwdev, } } +/** + * irdma_setup_umode_qp - setup sq and rq size in user mode qp + * @udata: udata + * @iwdev: iwarp device + * @iwqp: qp ptr (user or kernel) + * @info: initialize info to return + * @init_attr: Initial QP create attributes + */ +static int irdma_setup_umode_qp(struct ib_udata *udata, + struct irdma_device *iwdev, + struct irdma_qp *iwqp, + struct irdma_qp_init_info *info, + struct ib_qp_init_attr *init_attr) +{ + struct irdma_ucontext *ucontext = rdma_udata_to_drv_context(udata, + struct irdma_ucontext, ibucontext); + struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info; + struct irdma_create_qp_req req; + unsigned long flags; + int ret; + + ret = ib_copy_from_udata(&req, udata, + min(sizeof(req), udata->inlen)); + if (ret) { + ibdev_dbg(&iwdev->ibdev, "VERBS: ib_copy_from_data fail\n"); + return ret; + } + + iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; + iwqp->user_mode = 1; + if (req.user_wqe_bufs) { + info->qp_uk_init_info.legacy_mode = ucontext->legacy_mode; + spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); + iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + &ucontext->qp_reg_mem_list); + spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + + if (!iwqp->iwpbl) { + ret = -ENODATA; + ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); + return ret; + } + } + + if (!ucontext->use_raw_attrs) { + /** + * Maintain backward compat with older ABI which passes sq and + * rq depth in quanta in cap.max_send_wr and cap.max_recv_wr. + * There is no way to compute the correct value of + * iwqp->max_send_wr/max_recv_wr in the kernel. + */ + iwqp->max_send_wr = init_attr->cap.max_send_wr; + iwqp->max_recv_wr = init_attr->cap.max_recv_wr; + ukinfo->sq_size = init_attr->cap.max_send_wr; + ukinfo->rq_size = init_attr->cap.max_recv_wr; + irdma_uk_calc_shift_wq(ukinfo, &ukinfo->sq_shift, + &ukinfo->rq_shift); + } else { + ret = irdma_uk_calc_depth_shift_sq(ukinfo, &ukinfo->sq_depth, + &ukinfo->sq_shift); + if (ret) + return ret; + + ret = irdma_uk_calc_depth_shift_rq(ukinfo, &ukinfo->rq_depth, + &ukinfo->rq_shift); + if (ret) + return ret; + + iwqp->max_send_wr = + (ukinfo->sq_depth - IRDMA_SQ_RSVD) >> ukinfo->sq_shift; + iwqp->max_recv_wr = + (ukinfo->rq_depth - IRDMA_RQ_RSVD) >> ukinfo->rq_shift; + ukinfo->sq_size = ukinfo->sq_depth >> ukinfo->sq_shift; + ukinfo->rq_size = ukinfo->rq_depth >> ukinfo->rq_shift; + } + + irdma_setup_virt_qp(iwdev, iwqp, info); + + return 0; +} + /** * irdma_setup_kmode_qp - setup initialization for kernel mode qp * @iwdev: iwarp device @@ -579,40 +665,28 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, struct ib_qp_init_attr *init_attr) { struct irdma_dma_mem *mem = &iwqp->kqp.dma_mem; - u32 sqdepth, rqdepth; - u8 sqshift, rqshift; u32 size; int status; struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info; - struct irdma_uk_attrs *uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; - irdma_get_wqe_shift(uk_attrs, - uk_attrs->hw_rev >= IRDMA_GEN_2 ? ukinfo->max_sq_frag_cnt + 1 : - ukinfo->max_sq_frag_cnt, - ukinfo->max_inline_data, &sqshift); - status = irdma_get_sqdepth(uk_attrs, ukinfo->sq_size, sqshift, - &sqdepth); + status = irdma_uk_calc_depth_shift_sq(ukinfo, &ukinfo->sq_depth, + &ukinfo->sq_shift); if (status) return status; - if (uk_attrs->hw_rev == IRDMA_GEN_1) - rqshift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; - else - irdma_get_wqe_shift(uk_attrs, ukinfo->max_rq_frag_cnt, 0, - &rqshift); - - status = irdma_get_rqdepth(uk_attrs, ukinfo->rq_size, rqshift, - &rqdepth); + status = irdma_uk_calc_depth_shift_rq(ukinfo, &ukinfo->rq_depth, + &ukinfo->rq_shift); if (status) return status; iwqp->kqp.sq_wrid_mem = - kcalloc(sqdepth, sizeof(*iwqp->kqp.sq_wrid_mem), GFP_KERNEL); + kcalloc(ukinfo->sq_depth, sizeof(*iwqp->kqp.sq_wrid_mem), GFP_KERNEL); if (!iwqp->kqp.sq_wrid_mem) return -ENOMEM; iwqp->kqp.rq_wrid_mem = - kcalloc(rqdepth, sizeof(*iwqp->kqp.rq_wrid_mem), GFP_KERNEL); + kcalloc(ukinfo->rq_depth, sizeof(*iwqp->kqp.rq_wrid_mem), GFP_KERNEL); + if (!iwqp->kqp.rq_wrid_mem) { kfree(iwqp->kqp.sq_wrid_mem); iwqp->kqp.sq_wrid_mem = NULL; @@ -622,7 +696,7 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, ukinfo->sq_wrtrk_array = iwqp->kqp.sq_wrid_mem; ukinfo->rq_wrid_array = iwqp->kqp.rq_wrid_mem; - size = (sqdepth + rqdepth) * IRDMA_QP_WQE_MIN_SIZE; + size = (ukinfo->sq_depth + ukinfo->rq_depth) * IRDMA_QP_WQE_MIN_SIZE; size += (IRDMA_SHADOW_AREA_SIZE << 3); mem->size = ALIGN(size, 256); @@ -638,16 +712,19 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, ukinfo->sq = mem->va; info->sq_pa = mem->pa; - ukinfo->rq = &ukinfo->sq[sqdepth]; - info->rq_pa = info->sq_pa + (sqdepth * IRDMA_QP_WQE_MIN_SIZE); - ukinfo->shadow_area = ukinfo->rq[rqdepth].elem; - info->shadow_area_pa = info->rq_pa + (rqdepth * IRDMA_QP_WQE_MIN_SIZE); - ukinfo->sq_size = sqdepth >> sqshift; - ukinfo->rq_size = rqdepth >> rqshift; + ukinfo->rq = &ukinfo->sq[ukinfo->sq_depth]; + info->rq_pa = info->sq_pa + (ukinfo->sq_depth * IRDMA_QP_WQE_MIN_SIZE); + ukinfo->shadow_area = ukinfo->rq[ukinfo->rq_depth].elem; + info->shadow_area_pa = + info->rq_pa + (ukinfo->rq_depth * IRDMA_QP_WQE_MIN_SIZE); + ukinfo->sq_size = ukinfo->sq_depth >> ukinfo->sq_shift; + ukinfo->rq_size = ukinfo->rq_depth >> ukinfo->rq_shift; ukinfo->qp_id = iwqp->ibqp.qp_num; - init_attr->cap.max_send_wr = (sqdepth - IRDMA_SQ_RSVD) >> sqshift; - init_attr->cap.max_recv_wr = (rqdepth - IRDMA_RQ_RSVD) >> rqshift; + iwqp->max_send_wr = (ukinfo->sq_depth - IRDMA_SQ_RSVD) >> ukinfo->sq_shift; + iwqp->max_recv_wr = (ukinfo->rq_depth - IRDMA_RQ_RSVD) >> ukinfo->rq_shift; + init_attr->cap.max_send_wr = iwqp->max_send_wr; + init_attr->cap.max_recv_wr = iwqp->max_recv_wr; return 0; } @@ -803,18 +880,14 @@ static int irdma_create_qp(struct ib_qp *ibqp, struct irdma_device *iwdev = to_iwdev(ibpd->device); struct irdma_pci_f *rf = iwdev->rf; struct irdma_qp *iwqp = to_iwqp(ibqp); - struct irdma_create_qp_req req = {}; struct irdma_create_qp_resp uresp = {}; u32 qp_num = 0; int err_code; - int sq_size; - int rq_size; struct irdma_sc_qp *qp; struct irdma_sc_dev *dev = &rf->sc_dev; struct irdma_uk_attrs *uk_attrs = &dev->hw_attrs.uk_attrs; struct irdma_qp_init_info init_info = {}; struct irdma_qp_host_ctx_info *ctx_info; - unsigned long flags; err_code = irdma_validate_qp_attrs(init_attr, iwdev); if (err_code) @@ -824,13 +897,10 @@ static int irdma_create_qp(struct ib_qp *ibqp, udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN)) return -EINVAL; - sq_size = init_attr->cap.max_send_wr; - rq_size = init_attr->cap.max_recv_wr; - init_info.vsi = &iwdev->vsi; init_info.qp_uk_init_info.uk_attrs = uk_attrs; - init_info.qp_uk_init_info.sq_size = sq_size; - init_info.qp_uk_init_info.rq_size = rq_size; + init_info.qp_uk_init_info.sq_size = init_attr->cap.max_send_wr; + init_info.qp_uk_init_info.rq_size = init_attr->cap.max_recv_wr; init_info.qp_uk_init_info.max_sq_frag_cnt = init_attr->cap.max_send_sge; init_info.qp_uk_init_info.max_rq_frag_cnt = init_attr->cap.max_recv_sge; init_info.qp_uk_init_info.max_inline_data = init_attr->cap.max_inline_data; @@ -880,36 +950,9 @@ static int irdma_create_qp(struct ib_qp *ibqp, init_waitqueue_head(&iwqp->mod_qp_waitq); if (udata) { - err_code = ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen)); - if (err_code) { - ibdev_dbg(&iwdev->ibdev, - "VERBS: ib_copy_from_data fail\n"); - goto error; - } - - iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; - iwqp->user_mode = 1; - if (req.user_wqe_bufs) { - struct irdma_ucontext *ucontext = - rdma_udata_to_drv_context(udata, - struct irdma_ucontext, - ibucontext); - - init_info.qp_uk_init_info.legacy_mode = ucontext->legacy_mode; - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, - &ucontext->qp_reg_mem_list); - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); - - if (!iwqp->iwpbl) { - err_code = -ENODATA; - ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); - goto error; - } - } init_info.qp_uk_init_info.abi_ver = iwpd->sc_pd.abi_ver; - irdma_setup_virt_qp(iwdev, iwqp, &init_info); + err_code = irdma_setup_umode_qp(udata, iwdev, iwqp, &init_info, + init_attr); } else { INIT_DELAYED_WORK(&iwqp->dwork_flush, irdma_flush_worker); init_info.qp_uk_init_info.abi_ver = IRDMA_ABI_VER; @@ -962,10 +1005,8 @@ static int irdma_create_qp(struct ib_qp *ibqp, refcount_set(&iwqp->refcnt, 1); spin_lock_init(&iwqp->lock); spin_lock_init(&iwqp->sc_qp.pfpdu.lock); - iwqp->sig_all = (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? 1 : 0; + iwqp->sig_all = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR; rf->qp_table[qp_num] = iwqp; - iwqp->max_send_wr = sq_size; - iwqp->max_recv_wr = rq_size; if (rdma_protocol_roce(&iwdev->ibdev, 1)) { if (dev->ws_add(&iwdev->vsi, 0)) { @@ -986,8 +1027,8 @@ static int irdma_create_qp(struct ib_qp *ibqp, if (rdma_protocol_iwarp(&iwdev->ibdev, 1)) uresp.lsmm = 1; } - uresp.actual_sq_size = sq_size; - uresp.actual_rq_size = rq_size; + uresp.actual_sq_size = init_info.qp_uk_init_info.sq_size; + uresp.actual_rq_size = init_info.qp_uk_init_info.rq_size; uresp.qp_id = qp_num; uresp.qp_caps = qp->qp_uk.qp_caps; @@ -1098,6 +1139,24 @@ static int irdma_query_pkey(struct ib_device *ibdev, u32 port, u16 index, return 0; } +static u8 irdma_roce_get_vlan_prio(const struct ib_gid_attr *attr, u8 prio) +{ + struct net_device *ndev; + + rcu_read_lock(); + ndev = rcu_dereference(attr->ndev); + if (!ndev) + goto exit; + if (is_vlan_dev(ndev)) { + u16 vlan_qos = vlan_dev_get_egress_qos_mask(ndev, prio); + + prio = (vlan_qos & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + } +exit: + rcu_read_unlock(); + return prio; +} + /** * irdma_modify_qp_roce - modify qp request * @ibqp: qp's pointer for modify @@ -1174,7 +1233,8 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (attr_mask & IB_QP_AV) { struct irdma_av *av = &iwqp->roce_ah.av; - const struct ib_gid_attr *sgid_attr; + const struct ib_gid_attr *sgid_attr = + attr->ah_attr.grh.sgid_attr; u16 vlan_id = VLAN_N_VID; u32 local_ip[4]; @@ -1189,17 +1249,22 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, roce_info->dest_qp); irdma_qp_rem_qos(&iwqp->sc_qp); dev->ws_remove(iwqp->sc_qp.vsi, ctx_info->user_pri); - ctx_info->user_pri = rt_tos2priority(udp_info->tos); - iwqp->sc_qp.user_pri = ctx_info->user_pri; - if (dev->ws_add(iwqp->sc_qp.vsi, ctx_info->user_pri)) - return -ENOMEM; - irdma_qp_add_qos(&iwqp->sc_qp); + if (iwqp->sc_qp.vsi->dscp_mode) + ctx_info->user_pri = + iwqp->sc_qp.vsi->dscp_map[irdma_tos2dscp(udp_info->tos)]; + else + ctx_info->user_pri = rt_tos2priority(udp_info->tos); } - sgid_attr = attr->ah_attr.grh.sgid_attr; ret = rdma_read_gid_l2_fields(sgid_attr, &vlan_id, ctx_info->roce_info->mac_addr); if (ret) return ret; + ctx_info->user_pri = irdma_roce_get_vlan_prio(sgid_attr, + ctx_info->user_pri); + if (dev->ws_add(iwqp->sc_qp.vsi, ctx_info->user_pri)) + return -ENOMEM; + iwqp->sc_qp.user_pri = ctx_info->user_pri; + irdma_qp_add_qos(&iwqp->sc_qp); if (vlan_id >= VLAN_N_VID && iwdev->dcb_vlan_mode) vlan_id = 0; @@ -1781,6 +1846,9 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) irdma_process_resize_list(iwcq, iwdev, NULL); spin_unlock_irqrestore(&iwcq->lock, flags); + irdma_cq_rem_ref(ib_cq); + wait_for_completion(&iwcq->free_cq); + irdma_cq_wq_destroy(iwdev->rf, cq); spin_lock_irqsave(&iwceq->ce_lock, flags); @@ -1990,6 +2058,7 @@ static int irdma_create_cq(struct ib_cq *ibcq, cq = &iwcq->sc_cq; cq->back_cq = iwcq; + refcount_set(&iwcq->refcnt, 1); spin_lock_init(&iwcq->lock); INIT_LIST_HEAD(&iwcq->resize_list); INIT_LIST_HEAD(&iwcq->cmpl_generated); @@ -2141,6 +2210,9 @@ static int irdma_create_cq(struct ib_cq *ibcq, goto cq_destroy; } } + rf->cq_table[cq_num] = iwcq; + init_completion(&iwcq->free_cq); + return 0; cq_destroy: irdma_cq_wq_destroy(rf, cq); @@ -2552,7 +2624,8 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev, struct irdma_mr *iwmr) { struct irdma_allocate_stag_info *info; - struct irdma_pd *iwpd = to_iwpd(iwmr->ibmr.pd); + struct ib_pd *pd = iwmr->ibmr.pd; + struct irdma_pd *iwpd = to_iwpd(pd); int status; struct irdma_cqp_request *cqp_request; struct cqp_cmds_info *cqp_info; @@ -2568,6 +2641,7 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev, info->stag_idx = iwmr->stag >> IRDMA_CQPSQ_STAG_IDX_S; info->pd_id = iwpd->sc_pd.pd_id; info->total_len = iwmr->len; + info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY; info->remote_access = true; cqp_info->cqp_cmd = IRDMA_OP_ALLOC_STAG; cqp_info->post_sq = 1; @@ -2615,6 +2689,8 @@ static struct ib_mr *irdma_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type, iwmr->type = IRDMA_MEMREG_TYPE_MEM; palloc = &iwpbl->pble_alloc; iwmr->page_cnt = max_num_sg; + /* Use system PAGE_SIZE as the sg page sizes are unknown at this point */ + iwmr->len = max_num_sg * PAGE_SIZE; err_code = irdma_get_pble(iwdev->rf->pble_rsrc, palloc, iwmr->page_cnt, false); if (err_code) @@ -2694,7 +2770,8 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr, { struct irdma_pbl *iwpbl = &iwmr->iwpbl; struct irdma_reg_ns_stag_info *stag_info; - struct irdma_pd *iwpd = to_iwpd(iwmr->ibmr.pd); + struct ib_pd *pd = iwmr->ibmr.pd; + struct irdma_pd *iwpd = to_iwpd(pd); struct irdma_pble_alloc *palloc = &iwpbl->pble_alloc; struct irdma_cqp_request *cqp_request; struct cqp_cmds_info *cqp_info; @@ -2713,6 +2790,7 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr, stag_info->total_len = iwmr->len; stag_info->access_rights = irdma_get_mr_access(access); stag_info->pd_id = iwpd->sc_pd.pd_id; + stag_info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY; if (stag_info->access_rights & IRDMA_ACCESS_FLAGS_ZERO_BASED) stag_info->addr_type = IRDMA_ADDR_TYPE_ZERO_BASED; else @@ -2794,8 +2872,8 @@ static struct irdma_mr *irdma_alloc_iwmr(struct ib_umem *region, enum irdma_memreg_type reg_type) { struct irdma_device *iwdev = to_iwdev(pd->device); - struct irdma_pbl *iwpbl = NULL; - struct irdma_mr *iwmr = NULL; + struct irdma_pbl *iwpbl; + struct irdma_mr *iwmr; unsigned long pgsz_bitmap; iwmr = kzalloc(sizeof(*iwmr), GFP_KERNEL); @@ -3476,8 +3554,7 @@ static void irdma_process_cqe(struct ib_wc *entry, set_ib_wc_op_sq(cq_poll_info, entry); } else { set_ib_wc_op_rq(cq_poll_info, entry, - qp->qp_uk.qp_caps & IRDMA_SEND_WITH_IMM ? - true : false); + qp->qp_uk.qp_caps & IRDMA_SEND_WITH_IMM); if (qp->qp_uk.qp_type != IRDMA_QP_TYPE_ROCE_UD && cq_poll_info->stag_invalid_set) { entry->ex.invalidate_rkey = cq_poll_info->inv_stag; @@ -3963,7 +4040,7 @@ static int irdma_attach_mcast(struct ib_qp *ibqp, union ib_gid *ibgid, u16 lid) if (!ipv6_addr_v4mapped((struct in6_addr *)ibgid)) { irdma_copy_ip_ntohl(ip_addr, sgid_addr.saddr_in6.sin6_addr.in6_u.u6_addr32); - irdma_netdev_vlan_ipv6(ip_addr, &vlan_id, NULL); + irdma_get_vlan_mac_ipv6(ip_addr, &vlan_id, NULL); ipv4 = false; ibdev_dbg(&iwdev->ibdev, "VERBS: qp_id=%d, IP6address=%pI6\n", ibqp->qp_num, @@ -4261,9 +4338,12 @@ static int irdma_setup_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr) ah_info->vlan_tag = 0; if (ah_info->vlan_tag < VLAN_N_VID) { + u8 prio = rt_tos2priority(ah_info->tc_tos); + + prio = irdma_roce_get_vlan_prio(sgid_attr, prio); + + ah_info->vlan_tag |= (u16)prio << VLAN_PRIO_SHIFT; ah_info->insert_vlan_tag = true; - ah_info->vlan_tag |= - rt_tos2priority(ah_info->tc_tos) << VLAN_PRIO_SHIFT; } return 0; @@ -4424,7 +4504,6 @@ static int irdma_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr) ah_attr->grh.traffic_class = ah->sc_ah.ah_info.tc_tos; ah_attr->grh.hop_limit = ah->sc_ah.ah_info.hop_ttl; ah_attr->grh.sgid_index = ah->sgid_index; - ah_attr->grh.sgid_index = ah->sgid_index; memcpy(&ah_attr->grh.dgid, &ah->dgid, sizeof(ah_attr->grh.dgid)); } diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index a536e9fa85eb..5d7b983f47a2 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -18,7 +18,8 @@ struct irdma_ucontext { struct list_head qp_reg_mem_list; spinlock_t qp_reg_mem_list_lock; /* protect QP memory list */ int abi_ver; - bool legacy_mode; + u8 legacy_mode : 1; + u8 use_raw_attrs : 1; }; struct irdma_pd { @@ -122,6 +123,8 @@ struct irdma_cq { u32 cq_mem_size; struct irdma_dma_mem kmem; struct irdma_dma_mem kmem_shadow; + struct completion free_cq; + refcount_t refcnt; spinlock_t lock; /* for poll cq */ struct irdma_pbl *iwpbl; struct irdma_pbl *iwpbl_shadow; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 1f8d0d2c5f17..529db874d67c 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -136,7 +136,7 @@ static struct net_device *mlx4_ib_get_netdev(struct ib_device *device, continue; if (mlx4_is_bonded(ibdev->dev)) { - struct net_device *upper = NULL; + struct net_device *upper; upper = netdev_master_upper_dev_get_rcu(dev); if (upper) { @@ -261,7 +261,7 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) int ret = 0; int hw_update = 0; int i; - struct gid_entry *gids = NULL; + struct gid_entry *gids; u16 vlan_id = 0xffff; u8 mac[ETH_ALEN]; @@ -300,8 +300,7 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) ret = -ENOMEM; } else { *context = port_gid_table->gids[free].ctx; - memcpy(&port_gid_table->gids[free].gid, - &attr->gid, sizeof(attr->gid)); + port_gid_table->gids[free].gid = attr->gid; port_gid_table->gids[free].gid_type = attr->gid_type; port_gid_table->gids[free].vlan_id = vlan_id; port_gid_table->gids[free].ctx->real_index = free; @@ -352,7 +351,7 @@ static int mlx4_ib_del_gid(const struct ib_gid_attr *attr, void **context) struct mlx4_port_gid_table *port_gid_table; int ret = 0; int hw_update = 0; - struct gid_entry *gids = NULL; + struct gid_entry *gids; if (!rdma_cap_roce_gid_table(attr->device, attr->port_num)) return -EINVAL; @@ -438,8 +437,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, struct ib_udata *uhw) { struct mlx4_ib_dev *dev = to_mdev(ibdev); - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err; int have_ib_ports; struct mlx4_uverbs_ex_query_device cmd; @@ -656,8 +655,8 @@ mlx4_ib_port_link_layer(struct ib_device *device, u32 port_num) static int ib_link_query_port(struct ib_device *ibdev, u32 port, struct ib_port_attr *props, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int ext_active_speed; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -834,8 +833,8 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u32 port, int __mlx4_ib_query_gid(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; struct mlx4_ib_dev *dev = to_mdev(ibdev); int clear = 0; @@ -899,8 +898,8 @@ static int mlx4_ib_query_sl2vl(struct ib_device *ibdev, u32 port, u64 *sl2vl_tbl) { union sl2vl_tbl_to_u64 sl2vl64; - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; int jj; @@ -959,8 +958,8 @@ static void mlx4_init_sl2vl_tbl(struct mlx4_ib_dev *mdev) int __mlx4_ib_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -1975,8 +1974,8 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) static int init_node_data(struct mlx4_ib_dev *dev) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -2623,7 +2622,7 @@ static int mlx4_ib_probe(struct auxiliary_device *adev, int num_req_counters; int allocated; u32 counter_index; - struct counter_index *new_counter_index = NULL; + struct counter_index *new_counter_index; pr_info_once("%s", mlx4_ib_version); @@ -2946,7 +2945,7 @@ int mlx4_ib_steer_qp_reg(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, { int err; size_t flow_size; - struct ib_flow_attr *flow = NULL; + struct ib_flow_attr *flow; struct ib_flow_spec_ib *ib_spec; if (is_attach) { @@ -2966,11 +2965,11 @@ int mlx4_ib_steer_qp_reg(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, err = __mlx4_ib_create_flow(&mqp->ibqp, flow, MLX4_DOMAIN_NIC, MLX4_FS_REGULAR, &mqp->reg_id); - } else { - err = __mlx4_ib_destroy_flow(mdev->dev, mqp->reg_id); + kfree(flow); + return err; } - kfree(flow); - return err; + + return __mlx4_ib_destroy_flow(mdev->dev, mqp->reg_id); } static void mlx4_ib_remove(struct auxiliary_device *adev) @@ -3019,7 +3018,7 @@ static void mlx4_ib_remove(struct auxiliary_device *adev) static void do_slave_init(struct mlx4_ib_dev *ibdev, int slave, int do_init) { - struct mlx4_ib_demux_work **dm = NULL; + struct mlx4_ib_demux_work **dm; struct mlx4_dev *dev = ibdev->dev; int i; unsigned long flags; diff --git a/drivers/infiniband/hw/mlx5/counters.c b/drivers/infiniband/hw/mlx5/counters.c index 93257fa5aae8..8300ce622835 100644 --- a/drivers/infiniband/hw/mlx5/counters.c +++ b/drivers/infiniband/hw/mlx5/counters.c @@ -27,6 +27,7 @@ static const struct mlx5_ib_counter basic_q_cnts[] = { INIT_Q_COUNTER(rx_write_requests), INIT_Q_COUNTER(rx_read_requests), INIT_Q_COUNTER(rx_atomic_requests), + INIT_Q_COUNTER(rx_dct_connect), INIT_Q_COUNTER(out_of_buffer), }; @@ -46,6 +47,7 @@ static const struct mlx5_ib_counter vport_basic_q_cnts[] = { INIT_VPORT_Q_COUNTER(rx_write_requests), INIT_VPORT_Q_COUNTER(rx_read_requests), INIT_VPORT_Q_COUNTER(rx_atomic_requests), + INIT_VPORT_Q_COUNTER(rx_dct_connect), INIT_VPORT_Q_COUNTER(out_of_buffer), }; diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index 9c8a7b206dcf..8102ef113b7e 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -308,8 +308,8 @@ int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u32 port_num, int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, unsigned int port) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; u16 packet_error; @@ -338,8 +338,8 @@ out: static int mlx5_query_mad_ifc_smp_attr_node_info(struct ib_device *ibdev, struct ib_smp *out_mad) { - struct ib_smp *in_mad = NULL; - int err = -ENOMEM; + struct ib_smp *in_mad; + int err; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); if (!in_mad) @@ -358,8 +358,8 @@ static int mlx5_query_mad_ifc_smp_attr_node_info(struct ib_device *ibdev, int mlx5_query_mad_ifc_system_image_guid(struct ib_device *ibdev, __be64 *sys_image_guid) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -380,8 +380,8 @@ out: int mlx5_query_mad_ifc_max_pkeys(struct ib_device *ibdev, u16 *max_pkeys) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -402,8 +402,8 @@ out: int mlx5_query_mad_ifc_vendor_id(struct ib_device *ibdev, u32 *vendor_id) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -423,8 +423,8 @@ out: int mlx5_query_mad_ifc_node_desc(struct mlx5_ib_dev *dev, char *node_desc) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -448,8 +448,8 @@ out: int mlx5_query_mad_ifc_node_guid(struct mlx5_ib_dev *dev, __be64 *node_guid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -474,8 +474,8 @@ out: int mlx5_query_mad_ifc_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -503,8 +503,8 @@ out: int mlx5_query_mad_ifc_gids(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -545,8 +545,8 @@ int mlx5_query_mad_ifc_port(struct ib_device *ibdev, u32 port, { struct mlx5_ib_dev *dev = to_mdev(ibdev); struct mlx5_core_dev *mdev = dev->mdev; - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int ext_active_speed; int err = -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 2017ede100a6..3e345ef380f1 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1235,7 +1235,8 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, struct ib_umem *umem, } /* The pg_access bit allows setting the access flags - * in the page list submitted with the command. */ + * in the page list submitted with the command. + */ MLX5_SET(create_mkey_in, in, pg_access, !!(pg_cap)); mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); @@ -1766,6 +1767,11 @@ mlx5_alloc_priv_descs(struct ib_device *device, int ret; add_size = max_t(int, MLX5_UMR_ALIGN - ARCH_KMALLOC_MINALIGN, 0); + if (is_power_of_2(MLX5_UMR_ALIGN) && add_size) { + int end = max_t(int, MLX5_UMR_ALIGN, roundup_pow_of_two(size)); + + add_size = min_t(int, end - size, add_size); + } mr->descs_alloc = kzalloc(size + add_size, GFP_KERNEL); if (!mr->descs_alloc) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index c46df53f26cf..e1325f2927d6 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -53,8 +53,8 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *props, struct ib_udata *uhw) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; struct mthca_dev *mdev = to_mdev(ibdev); @@ -121,8 +121,8 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *pr static int mthca_query_port(struct ib_device *ibdev, u32 port, struct ib_port_attr *props) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -217,8 +217,8 @@ out: static int mthca_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -246,8 +246,8 @@ static int mthca_query_pkey(struct ib_device *ibdev, static int mthca_query_gid(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -989,8 +989,8 @@ static const struct attribute_group mthca_attr_group = { static int mthca_init_node_data(struct mthca_dev *dev) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 58f994341e9a..c849fdbd4c99 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -1277,7 +1277,7 @@ static void ocrdma_set_qp_init_params(struct ocrdma_qp *qp, qp->sq.max_sges = attrs->cap.max_send_sge; qp->rq.max_sges = attrs->cap.max_recv_sge; qp->state = OCRDMA_QPS_RST; - qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false; + qp->signaled = attrs->sq_sig_type == IB_SIGNAL_ALL_WR; } static void ocrdma_store_gsi_qp_cq(struct ocrdma_dev *dev, diff --git a/drivers/infiniband/hw/qedr/qedr_roce_cm.c b/drivers/infiniband/hw/qedr/qedr_roce_cm.c index 05307c1488b8..859f66a51bd2 100644 --- a/drivers/infiniband/hw/qedr/qedr_roce_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_roce_cm.c @@ -354,7 +354,6 @@ int qedr_create_gsi_qp(struct qedr_dev *dev, struct ib_qp_init_attr *attrs, /* the GSI CQ is handled by the driver so remove it from the FW */ qedr_destroy_gsi_cq(dev, attrs); dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI; - dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI; DP_DEBUG(dev, QEDR_MSG_GSI, "created GSI QP %p\n", qp); diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index d745ce9dc88a..7887a6786ed4 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1358,7 +1358,7 @@ static void qedr_set_common_qp_params(struct qedr_dev *dev, qp->prev_wqe_size = 0; - qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false; + qp->signaled = attrs->sq_sig_type == IB_SIGNAL_ALL_WR; qp->dev = dev; if (qedr_qp_has_sq(qp)) { qedr_reset_qp_hwq_info(&qp->sq); diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index ef85bc8d9384..152952127f13 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -2250,7 +2250,9 @@ static ssize_t qib_write_iter(struct kiocb *iocb, struct iov_iter *from) return qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs); } -static struct class *qib_class; +static const struct class qib_class = { + .name = "ipath", +}; static dev_t qib_dev; int qib_cdev_init(int minor, const char *name, @@ -2281,7 +2283,7 @@ int qib_cdev_init(int minor, const char *name, goto err_cdev; } - device = device_create(qib_class, NULL, dev, NULL, "%s", name); + device = device_create(&qib_class, NULL, dev, NULL, "%s", name); if (!IS_ERR(device)) goto done; ret = PTR_ERR(device); @@ -2325,9 +2327,8 @@ int __init qib_dev_init(void) goto done; } - qib_class = class_create("ipath"); - if (IS_ERR(qib_class)) { - ret = PTR_ERR(qib_class); + ret = class_register(&qib_class); + if (ret) { pr_err("Could not create device class (err %d)\n", -ret); unregister_chrdev_region(qib_dev, QIB_NMINORS); } @@ -2338,10 +2339,8 @@ done: void qib_dev_cleanup(void) { - if (qib_class) { - class_destroy(qib_class); - qib_class = NULL; - } + if (class_is_registered(&qib_class)) + class_unregister(&qib_class); unregister_chrdev_region(qib_dev, QIB_NMINORS); } diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index 5111735aafae..d0bdc2d8adc8 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -597,6 +597,10 @@ static void flush_send_queue(struct rxe_qp *qp, bool notify) struct rxe_queue *q = qp->sq.queue; int err; + /* send queue never got created. nothing to do. */ + if (!qp->sq.queue) + return; + while ((wqe = queue_head(q, q->type))) { if (notify) { err = flush_send_wqe(qp, wqe); diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 666e06a82bc9..4d2a8ef52c85 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -136,12 +136,6 @@ static inline int qp_mtu(struct rxe_qp *qp) return IB_MTU_4096; } -static inline int rcv_wqe_size(int max_sge) -{ - return sizeof(struct rxe_recv_wqe) + - max_sge * sizeof(struct ib_sge); -} - void free_rd_atomic_resource(struct resp_res *res); static inline void rxe_advance_resp_resource(struct rxe_qp *qp) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index a569b111a9d2..28e379c108bc 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -183,13 +183,63 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp, atomic_set(&qp->skb_out, 0); } +static int rxe_init_sq(struct rxe_qp *qp, struct ib_qp_init_attr *init, + struct ib_udata *udata, + struct rxe_create_qp_resp __user *uresp) +{ + struct rxe_dev *rxe = to_rdev(qp->ibqp.device); + int wqe_size; + int err; + + qp->sq.max_wr = init->cap.max_send_wr; + wqe_size = max_t(int, init->cap.max_send_sge * sizeof(struct ib_sge), + init->cap.max_inline_data); + qp->sq.max_sge = wqe_size / sizeof(struct ib_sge); + qp->sq.max_inline = wqe_size; + wqe_size += sizeof(struct rxe_send_wqe); + + qp->sq.queue = rxe_queue_init(rxe, &qp->sq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); + if (!qp->sq.queue) { + rxe_err_qp(qp, "Unable to allocate send queue"); + err = -ENOMEM; + goto err_out; + } + + /* prepare info for caller to mmap send queue if user space qp */ + err = do_mmap_info(rxe, uresp ? &uresp->sq_mi : NULL, udata, + qp->sq.queue->buf, qp->sq.queue->buf_size, + &qp->sq.queue->ip); + if (err) { + rxe_err_qp(qp, "do_mmap_info failed, err = %d", err); + goto err_free; + } + + /* return actual capabilities to caller which may be larger + * than requested + */ + init->cap.max_send_wr = qp->sq.max_wr; + init->cap.max_send_sge = qp->sq.max_sge; + init->cap.max_inline_data = qp->sq.max_inline; + + return 0; + +err_free: + vfree(qp->sq.queue->buf); + kfree(qp->sq.queue); + qp->sq.queue = NULL; +err_out: + return err; +} + static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, struct ib_qp_init_attr *init, struct ib_udata *udata, struct rxe_create_qp_resp __user *uresp) { int err; - int wqe_size; - enum queue_type type; + + /* if we don't finish qp create make sure queue is valid */ + skb_queue_head_init(&qp->req_pkts); err = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, 0, &qp->sk); if (err < 0) @@ -204,32 +254,10 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, * (0xc000 - 0xffff). */ qp->src_port = RXE_ROCE_V2_SPORT + (hash_32(qp_num(qp), 14) & 0x3fff); - qp->sq.max_wr = init->cap.max_send_wr; - /* These caps are limited by rxe_qp_chk_cap() done by the caller */ - wqe_size = max_t(int, init->cap.max_send_sge * sizeof(struct ib_sge), - init->cap.max_inline_data); - qp->sq.max_sge = init->cap.max_send_sge = - wqe_size / sizeof(struct ib_sge); - qp->sq.max_inline = init->cap.max_inline_data = wqe_size; - wqe_size += sizeof(struct rxe_send_wqe); - - type = QUEUE_TYPE_FROM_CLIENT; - qp->sq.queue = rxe_queue_init(rxe, &qp->sq.max_wr, - wqe_size, type); - if (!qp->sq.queue) - return -ENOMEM; - - err = do_mmap_info(rxe, uresp ? &uresp->sq_mi : NULL, udata, - qp->sq.queue->buf, qp->sq.queue->buf_size, - &qp->sq.queue->ip); - - if (err) { - vfree(qp->sq.queue->buf); - kfree(qp->sq.queue); - qp->sq.queue = NULL; + err = rxe_init_sq(qp, init, udata, uresp); + if (err) return err; - } qp->req.wqe_index = queue_get_producer(qp->sq.queue, QUEUE_TYPE_FROM_CLIENT); @@ -248,36 +276,65 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, return 0; } +static int rxe_init_rq(struct rxe_qp *qp, struct ib_qp_init_attr *init, + struct ib_udata *udata, + struct rxe_create_qp_resp __user *uresp) +{ + struct rxe_dev *rxe = to_rdev(qp->ibqp.device); + int wqe_size; + int err; + + qp->rq.max_wr = init->cap.max_recv_wr; + qp->rq.max_sge = init->cap.max_recv_sge; + wqe_size = sizeof(struct rxe_recv_wqe) + + qp->rq.max_sge*sizeof(struct ib_sge); + + qp->rq.queue = rxe_queue_init(rxe, &qp->rq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); + if (!qp->rq.queue) { + rxe_err_qp(qp, "Unable to allocate recv queue"); + err = -ENOMEM; + goto err_out; + } + + /* prepare info for caller to mmap recv queue if user space qp */ + err = do_mmap_info(rxe, uresp ? &uresp->rq_mi : NULL, udata, + qp->rq.queue->buf, qp->rq.queue->buf_size, + &qp->rq.queue->ip); + if (err) { + rxe_err_qp(qp, "do_mmap_info failed, err = %d", err); + goto err_free; + } + + /* return actual capabilities to caller which may be larger + * than requested + */ + init->cap.max_recv_wr = qp->rq.max_wr; + + return 0; + +err_free: + vfree(qp->rq.queue->buf); + kfree(qp->rq.queue); + qp->rq.queue = NULL; +err_out: + return err; +} + static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, struct ib_qp_init_attr *init, struct ib_udata *udata, struct rxe_create_qp_resp __user *uresp) { int err; - int wqe_size; - enum queue_type type; + + /* if we don't finish qp create make sure queue is valid */ + skb_queue_head_init(&qp->resp_pkts); if (!qp->srq) { - qp->rq.max_wr = init->cap.max_recv_wr; - qp->rq.max_sge = init->cap.max_recv_sge; - - wqe_size = rcv_wqe_size(qp->rq.max_sge); - - type = QUEUE_TYPE_FROM_CLIENT; - qp->rq.queue = rxe_queue_init(rxe, &qp->rq.max_wr, - wqe_size, type); - if (!qp->rq.queue) - return -ENOMEM; - - err = do_mmap_info(rxe, uresp ? &uresp->rq_mi : NULL, udata, - qp->rq.queue->buf, qp->rq.queue->buf_size, - &qp->rq.queue->ip); - if (err) { - vfree(qp->rq.queue->buf); - kfree(qp->rq.queue); - qp->rq.queue = NULL; + err = rxe_init_rq(qp, init, udata, uresp); + if (err) return err; - } } rxe_init_task(&qp->resp.task, qp, rxe_responder); @@ -307,10 +364,10 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd, if (srq) rxe_get(srq); - qp->pd = pd; - qp->rcq = rcq; - qp->scq = scq; - qp->srq = srq; + qp->pd = pd; + qp->rcq = rcq; + qp->scq = scq; + qp->srq = srq; atomic_inc(&rcq->num_wq); atomic_inc(&scq->num_wq); diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 2171f19494bc..d8c41fd626a9 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -578,10 +578,11 @@ static void save_state(struct rxe_send_wqe *wqe, struct rxe_send_wqe *rollback_wqe, u32 *rollback_psn) { - rollback_wqe->state = wqe->state; + rollback_wqe->state = wqe->state; rollback_wqe->first_psn = wqe->first_psn; - rollback_wqe->last_psn = wqe->last_psn; - *rollback_psn = qp->req.psn; + rollback_wqe->last_psn = wqe->last_psn; + rollback_wqe->dma = wqe->dma; + *rollback_psn = qp->req.psn; } static void rollback_state(struct rxe_send_wqe *wqe, @@ -589,10 +590,11 @@ static void rollback_state(struct rxe_send_wqe *wqe, struct rxe_send_wqe *rollback_wqe, u32 rollback_psn) { - wqe->state = rollback_wqe->state; + wqe->state = rollback_wqe->state; wqe->first_psn = rollback_wqe->first_psn; - wqe->last_psn = rollback_wqe->last_psn; - qp->req.psn = rollback_psn; + wqe->last_psn = rollback_wqe->last_psn; + wqe->dma = rollback_wqe->dma; + qp->req.psn = rollback_psn; } static void update_state(struct rxe_qp *qp, struct rxe_pkt_info *pkt) @@ -797,6 +799,9 @@ int rxe_requester(struct rxe_qp *qp) pkt.mask = rxe_opcode[opcode].mask; pkt.wqe = wqe; + /* save wqe state before we build and send packet */ + save_state(wqe, qp, &rollback_wqe, &rollback_psn); + av = rxe_get_av(&pkt, &ah); if (unlikely(!av)) { rxe_dbg_qp(qp, "Failed no address vector\n"); @@ -829,29 +834,29 @@ int rxe_requester(struct rxe_qp *qp) if (ah) rxe_put(ah); - /* - * To prevent a race on wqe access between requester and completer, - * wqe members state and psn need to be set before calling - * rxe_xmit_packet(). - * Otherwise, completer might initiate an unjustified retry flow. - */ - save_state(wqe, qp, &rollback_wqe, &rollback_psn); + /* update wqe state as though we had sent it */ update_wqe_state(qp, wqe, &pkt); update_wqe_psn(qp, wqe, &pkt, payload); err = rxe_xmit_packet(qp, &pkt, skb); if (err) { - qp->need_req_skb = 1; - - rollback_state(wqe, qp, &rollback_wqe, rollback_psn); - - if (err == -EAGAIN) { - rxe_sched_task(&qp->req.task); - goto exit; + if (err != -EAGAIN) { + wqe->status = IB_WC_LOC_QP_OP_ERR; + goto err; } - wqe->status = IB_WC_LOC_QP_OP_ERR; - goto err; + /* the packet was dropped so reset wqe to the state + * before we sent it so we can try to resend + */ + rollback_state(wqe, qp, &rollback_wqe, rollback_psn); + + /* force a delay until the dropped packet is freed and + * the send queue is drained below the low water mark + */ + qp->need_req_skb = 1; + + rxe_sched_task(&qp->req.task); + goto exit; } update_state(qp, &pkt); diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 64c64f5f36a8..da470a925efc 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -1469,6 +1469,10 @@ static void flush_recv_queue(struct rxe_qp *qp, bool notify) return; } + /* recv queue not created. nothing to do. */ + if (!qp->rq.queue) + return; + while ((wqe = queue_head(q, q->type))) { if (notify) { err = flush_recv_wqe(qp, wqe); diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c index 27ca82ec0826..3661cb627d28 100644 --- a/drivers/infiniband/sw/rxe/rxe_srq.c +++ b/drivers/infiniband/sw/rxe/rxe_srq.c @@ -45,40 +45,41 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, struct ib_srq_init_attr *init, struct ib_udata *udata, struct rxe_create_srq_resp __user *uresp) { - int err; - int srq_wqe_size; struct rxe_queue *q; - enum queue_type type; + int wqe_size; + int err; - srq->ibsrq.event_handler = init->event_handler; - srq->ibsrq.srq_context = init->srq_context; - srq->limit = init->attr.srq_limit; - srq->srq_num = srq->elem.index; - srq->rq.max_wr = init->attr.max_wr; - srq->rq.max_sge = init->attr.max_sge; + srq->ibsrq.event_handler = init->event_handler; + srq->ibsrq.srq_context = init->srq_context; + srq->limit = init->attr.srq_limit; + srq->srq_num = srq->elem.index; + srq->rq.max_wr = init->attr.max_wr; + srq->rq.max_sge = init->attr.max_sge; - srq_wqe_size = rcv_wqe_size(srq->rq.max_sge); + wqe_size = sizeof(struct rxe_recv_wqe) + + srq->rq.max_sge*sizeof(struct ib_sge); spin_lock_init(&srq->rq.producer_lock); spin_lock_init(&srq->rq.consumer_lock); - type = QUEUE_TYPE_FROM_CLIENT; - q = rxe_queue_init(rxe, &srq->rq.max_wr, srq_wqe_size, type); + q = rxe_queue_init(rxe, &srq->rq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); if (!q) { rxe_dbg_srq(srq, "Unable to allocate queue\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_out; } - srq->rq.queue = q; - err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, q->buf_size, &q->ip); if (err) { - vfree(q->buf); - kfree(q); - return err; + rxe_dbg_srq(srq, "Unable to init mmap info for caller\n"); + goto err_free; } + srq->rq.queue = q; + init->attr.max_wr = srq->rq.max_wr; + if (uresp) { if (copy_to_user(&uresp->srq_num, &srq->srq_num, sizeof(uresp->srq_num))) { @@ -88,6 +89,12 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, } return 0; + +err_free: + vfree(q->buf); + kfree(q); +err_out: + return err; } int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq, @@ -145,9 +152,10 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, struct ib_srq_attr *attr, enum ib_srq_attr_mask mask, struct rxe_modify_srq_cmd *ucmd, struct ib_udata *udata) { - int err; struct rxe_queue *q = srq->rq.queue; struct mminfo __user *mi = NULL; + int wqe_size; + int err; if (mask & IB_SRQ_MAX_WR) { /* @@ -156,12 +164,16 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, */ mi = u64_to_user_ptr(ucmd->mmap_info_addr); - err = rxe_queue_resize(q, &attr->max_wr, - rcv_wqe_size(srq->rq.max_sge), udata, mi, - &srq->rq.producer_lock, + wqe_size = sizeof(struct rxe_recv_wqe) + + srq->rq.max_sge*sizeof(struct ib_sge); + + err = rxe_queue_resize(q, &attr->max_wr, wqe_size, + udata, mi, &srq->rq.producer_lock, &srq->rq.consumer_lock); if (err) - goto err2; + goto err_free; + + srq->rq.max_wr = attr->max_wr; } if (mask & IB_SRQ_LIMIT) @@ -169,7 +181,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, return 0; -err2: +err_free: rxe_queue_cleanup(q); srq->rq.queue = NULL; return err; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 903f0b71447e..48f86839d36a 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -798,7 +798,6 @@ static int init_send_wr(struct rxe_qp *qp, struct rxe_send_wr *wr, rxe_err_qp(qp, "unsupported wr opcode %d", wr->opcode); return -EINVAL; - break; } } diff --git a/drivers/infiniband/sw/siw/siw.h b/drivers/infiniband/sw/siw/siw.h index 2f3a9cda3850..58dddb143b9f 100644 --- a/drivers/infiniband/sw/siw/siw.h +++ b/drivers/infiniband/sw/siw/siw.h @@ -74,6 +74,7 @@ struct siw_device { u32 vendor_part_id; int numa_node; + char raw_gid[ETH_ALEN]; /* physical port state (only one port per device) */ enum ib_port_state state; @@ -530,11 +531,12 @@ void siw_qp_llp_data_ready(struct sock *sk); void siw_qp_llp_write_space(struct sock *sk); /* QP TX path functions */ +int siw_create_tx_threads(void); +void siw_stop_tx_threads(void); int siw_run_sq(void *arg); int siw_qp_sq_process(struct siw_qp *qp); int siw_sq_start(struct siw_qp *qp); int siw_activate_tx(struct siw_qp *qp); -void siw_stop_tx_thread(int nr_cpu); int siw_get_tx_cpu(struct siw_device *sdev); void siw_put_tx_cpu(int cpu); diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index da530c0404da..a2605178f4ed 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -1501,7 +1501,6 @@ error: cep->cm_id = NULL; id->rem_ref(id); - siw_cep_put(cep); qp->cep = NULL; siw_cep_put(cep); diff --git a/drivers/infiniband/sw/siw/siw_main.c b/drivers/infiniband/sw/siw/siw_main.c index 65b5cda5457b..d4b6e0106851 100644 --- a/drivers/infiniband/sw/siw/siw_main.c +++ b/drivers/infiniband/sw/siw/siw_main.c @@ -75,8 +75,7 @@ static int siw_device_register(struct siw_device *sdev, const char *name) return rv; } - siw_dbg(base_dev, "HWaddr=%pM\n", sdev->netdev->dev_addr); - + siw_dbg(base_dev, "HWaddr=%pM\n", sdev->raw_gid); return 0; } @@ -88,29 +87,6 @@ static void siw_device_cleanup(struct ib_device *base_dev) xa_destroy(&sdev->mem_xa); } -static int siw_create_tx_threads(void) -{ - int cpu, assigned = 0; - - for_each_online_cpu(cpu) { - /* Skip HT cores */ - if (cpu % cpumask_weight(topology_sibling_cpumask(cpu))) - continue; - - siw_tx_thread[cpu] = - kthread_run_on_cpu(siw_run_sq, - (unsigned long *)(long)cpu, - cpu, "siw_tx/%u"); - if (IS_ERR(siw_tx_thread[cpu])) { - siw_tx_thread[cpu] = NULL; - continue; - } - - assigned++; - } - return assigned; -} - static int siw_dev_qualified(struct net_device *netdev) { /* @@ -313,24 +289,19 @@ static struct siw_device *siw_device_create(struct net_device *netdev) return NULL; base_dev = &sdev->base_dev; - sdev->netdev = netdev; - if (netdev->type != ARPHRD_LOOPBACK && netdev->type != ARPHRD_NONE) { - addrconf_addr_eui48((unsigned char *)&base_dev->node_guid, - netdev->dev_addr); + if (netdev->addr_len) { + memcpy(sdev->raw_gid, netdev->dev_addr, + min_t(unsigned int, netdev->addr_len, ETH_ALEN)); } else { /* - * This device does not have a HW address, - * but connection mangagement lib expects gid != 0 + * This device does not have a HW address, but + * connection mangagement requires a unique gid. */ - size_t len = min_t(size_t, strlen(base_dev->name), 6); - char addr[6] = { }; - - memcpy(addr, base_dev->name, len); - addrconf_addr_eui48((unsigned char *)&base_dev->node_guid, - addr); + eth_random_addr(sdev->raw_gid); } + addrconf_addr_eui48((u8 *)&base_dev->node_guid, sdev->raw_gid); base_dev->uverbs_cmd_mask |= BIT_ULL(IB_USER_VERBS_CMD_POST_SEND); @@ -535,7 +506,6 @@ static struct rdma_link_ops siw_link_ops = { static __init int siw_init_module(void) { int rv; - int nr_cpu; if (SENDPAGE_THRESH < SIW_MAX_INLINE) { pr_info("siw: sendpage threshold too small: %u\n", @@ -580,12 +550,8 @@ static __init int siw_init_module(void) return 0; out_error: - for (nr_cpu = 0; nr_cpu < nr_cpu_ids; nr_cpu++) { - if (siw_tx_thread[nr_cpu]) { - siw_stop_tx_thread(nr_cpu); - siw_tx_thread[nr_cpu] = NULL; - } - } + siw_stop_tx_threads(); + if (siw_crypto_shash) crypto_free_shash(siw_crypto_shash); @@ -599,14 +565,8 @@ out_error: static void __exit siw_exit_module(void) { - int cpu; + siw_stop_tx_threads(); - for_each_possible_cpu(cpu) { - if (siw_tx_thread[cpu]) { - siw_stop_tx_thread(cpu); - siw_tx_thread[cpu] = NULL; - } - } unregister_netdevice_notifier(&siw_netdev_nb); rdma_link_unregister(&siw_link_ops); ib_unregister_driver(RDMA_DRIVER_SIW); diff --git a/drivers/infiniband/sw/siw/siw_qp.c b/drivers/infiniband/sw/siw/siw_qp.c index 81e9bbd9ebda..47d0197db9a1 100644 --- a/drivers/infiniband/sw/siw/siw_qp.c +++ b/drivers/infiniband/sw/siw/siw_qp.c @@ -204,7 +204,7 @@ static int siw_qp_readq_init(struct siw_qp *qp, int irq_size, int orq_size) { if (irq_size) { irq_size = roundup_pow_of_two(irq_size); - qp->irq = vzalloc(irq_size * sizeof(struct siw_sqe)); + qp->irq = vcalloc(irq_size, sizeof(struct siw_sqe)); if (!qp->irq) { qp->attrs.irq_size = 0; return -ENOMEM; @@ -212,7 +212,7 @@ static int siw_qp_readq_init(struct siw_qp *qp, int irq_size, int orq_size) } if (orq_size) { orq_size = roundup_pow_of_two(orq_size); - qp->orq = vzalloc(orq_size * sizeof(struct siw_sqe)); + qp->orq = vcalloc(orq_size, sizeof(struct siw_sqe)); if (!qp->orq) { qp->attrs.orq_size = 0; qp->attrs.irq_size = 0; diff --git a/drivers/infiniband/sw/siw/siw_qp_tx.c b/drivers/infiniband/sw/siw/siw_qp_tx.c index 7c7a51d36d0c..60b6a4135961 100644 --- a/drivers/infiniband/sw/siw/siw_qp_tx.c +++ b/drivers/infiniband/sw/siw/siw_qp_tx.c @@ -1208,10 +1208,45 @@ struct tx_task_t { static DEFINE_PER_CPU(struct tx_task_t, siw_tx_task_g); -void siw_stop_tx_thread(int nr_cpu) +int siw_create_tx_threads(void) { - kthread_stop(siw_tx_thread[nr_cpu]); - wake_up(&per_cpu(siw_tx_task_g, nr_cpu).waiting); + int cpu, assigned = 0; + + for_each_online_cpu(cpu) { + struct tx_task_t *tx_task; + + /* Skip HT cores */ + if (cpu % cpumask_weight(topology_sibling_cpumask(cpu))) + continue; + + tx_task = &per_cpu(siw_tx_task_g, cpu); + init_llist_head(&tx_task->active); + init_waitqueue_head(&tx_task->waiting); + + siw_tx_thread[cpu] = + kthread_run_on_cpu(siw_run_sq, + (unsigned long *)(long)cpu, + cpu, "siw_tx/%u"); + if (IS_ERR(siw_tx_thread[cpu])) { + siw_tx_thread[cpu] = NULL; + continue; + } + assigned++; + } + return assigned; +} + +void siw_stop_tx_threads(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (siw_tx_thread[cpu]) { + kthread_stop(siw_tx_thread[cpu]); + wake_up(&per_cpu(siw_tx_task_g, cpu).waiting); + siw_tx_thread[cpu] = NULL; + } + } } int siw_run_sq(void *data) @@ -1221,9 +1256,6 @@ int siw_run_sq(void *data) struct siw_qp *qp; struct tx_task_t *tx_task = &per_cpu(siw_tx_task_g, nr_cpu); - init_llist_head(&tx_task->active); - init_waitqueue_head(&tx_task->waiting); - while (1) { struct llist_node *fifo_list = NULL; @@ -1239,13 +1271,7 @@ int siw_run_sq(void *data) * llist_del_all returns a list with newest entry first. * Re-order list for fairness among QP's. */ - while (active) { - struct llist_node *tmp = active; - - active = llist_next(active); - tmp->next = fifo_list; - fifo_list = tmp; - } + fifo_list = llist_reverse_order(active); while (fifo_list) { qp = container_of(fifo_list, struct siw_qp, tx_list); fifo_list = llist_next(fifo_list); diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index 398ec13db624..fdbef3254e30 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -157,7 +157,7 @@ int siw_query_device(struct ib_device *base_dev, struct ib_device_attr *attr, attr->vendor_part_id = sdev->vendor_part_id; addrconf_addr_eui48((u8 *)&attr->sys_image_guid, - sdev->netdev->dev_addr); + sdev->raw_gid); return 0; } @@ -218,7 +218,7 @@ int siw_query_gid(struct ib_device *base_dev, u32 port, int idx, /* subnet_prefix == interface_id == 0; */ memset(gid, 0, sizeof(*gid)); - memcpy(&gid->raw[0], sdev->netdev->dev_addr, 6); + memcpy(gid->raw, sdev->raw_gid, ETH_ALEN); return 0; } @@ -381,7 +381,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (udata) qp->sendq = vmalloc_user(num_sqe * sizeof(struct siw_sqe)); else - qp->sendq = vzalloc(num_sqe * sizeof(struct siw_sqe)); + qp->sendq = vcalloc(num_sqe, sizeof(struct siw_sqe)); if (qp->sendq == NULL) { rv = -ENOMEM; @@ -414,7 +414,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, qp->recvq = vmalloc_user(num_rqe * sizeof(struct siw_rqe)); else - qp->recvq = vzalloc(num_rqe * sizeof(struct siw_rqe)); + qp->recvq = vcalloc(num_rqe, sizeof(struct siw_rqe)); if (qp->recvq == NULL) { rv = -ENOMEM; @@ -1494,7 +1494,7 @@ int siw_map_mr_sg(struct ib_mr *base_mr, struct scatterlist *sl, int num_sle, if (pbl->max_buf < num_sle) { siw_dbg_mem(mem, "too many SGE's: %d > %d\n", - mem->pbl->max_buf, num_sle); + num_sle, pbl->max_buf); return -ENOMEM; } for_each_sg(sl, slp, num_sle, i) { @@ -1624,7 +1624,7 @@ int siw_create_srq(struct ib_srq *base_srq, srq->recvq = vmalloc_user(srq->num_rqe * sizeof(struct siw_rqe)); else - srq->recvq = vzalloc(srq->num_rqe * sizeof(struct siw_rqe)); + srq->recvq = vcalloc(srq->num_rqe, sizeof(struct siw_rqe)); if (srq->recvq == NULL) { rv = -ENOMEM; diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 92e1e7587af8..00a7303c8cc6 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2570,6 +2570,8 @@ static void isert_wait_conn(struct iscsit_conn *conn) isert_put_unsol_pending_cmds(conn); isert_wait4cmds(conn); isert_wait4logout(isert_conn); + + queue_work(isert_release_wq, &isert_conn->release_work); } static void isert_free_conn(struct iscsit_conn *conn) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index b32941dd67cb..b6ee801fd0ff 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -45,7 +45,9 @@ static struct rtrs_rdma_dev_pd dev_pd = { }; static struct workqueue_struct *rtrs_wq; -static struct class *rtrs_clt_dev_class; +static const struct class rtrs_clt_dev_class = { + .name = "rtrs-client", +}; static inline bool rtrs_clt_is_connected(const struct rtrs_clt_sess *clt) { @@ -2698,7 +2700,7 @@ static struct rtrs_clt_sess *alloc_clt(const char *sessname, size_t paths_num, return ERR_PTR(-ENOMEM); } - clt->dev.class = rtrs_clt_dev_class; + clt->dev.class = &rtrs_clt_dev_class; clt->dev.release = rtrs_clt_dev_release; uuid_gen(&clt->paths_uuid); INIT_LIST_HEAD_RCU(&clt->paths_list); @@ -3151,16 +3153,17 @@ static const struct rtrs_rdma_dev_pd_ops dev_pd_ops = { static int __init rtrs_client_init(void) { - rtrs_rdma_dev_pd_init(0, &dev_pd); + int ret = 0; - rtrs_clt_dev_class = class_create("rtrs-client"); - if (IS_ERR(rtrs_clt_dev_class)) { + rtrs_rdma_dev_pd_init(0, &dev_pd); + ret = class_register(&rtrs_clt_dev_class); + if (ret) { pr_err("Failed to create rtrs-client dev class\n"); - return PTR_ERR(rtrs_clt_dev_class); + return ret; } rtrs_wq = alloc_workqueue("rtrs_client_wq", 0, 0); if (!rtrs_wq) { - class_destroy(rtrs_clt_dev_class); + class_unregister(&rtrs_clt_dev_class); return -ENOMEM; } @@ -3170,7 +3173,7 @@ static int __init rtrs_client_init(void) static void __exit rtrs_client_exit(void) { destroy_workqueue(rtrs_wq); - class_destroy(rtrs_clt_dev_class); + class_unregister(&rtrs_clt_dev_class); rtrs_rdma_dev_pd_deinit(&dev_pd); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c index 5adba0f754b6..3f305e694fe8 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c @@ -164,7 +164,7 @@ static int rtrs_srv_create_once_sysfs_root_folders(struct rtrs_srv_path *srv_pat */ goto unlock; } - srv->dev.class = rtrs_dev_class; + srv->dev.class = &rtrs_dev_class; err = dev_set_name(&srv->dev, "%s", srv_path->s.sessname); if (err) goto unlock; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index c38901e2c8f4..75e56604e462 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -27,7 +27,9 @@ MODULE_LICENSE("GPL"); #define MAX_HDR_SIZE PAGE_SIZE static struct rtrs_rdma_dev_pd dev_pd; -struct class *rtrs_dev_class; +const struct class rtrs_dev_class = { + .name = "rtrs-server", +}; static struct rtrs_srv_ib_ctx ib_ctx; static int __read_mostly max_chunk_size = DEFAULT_MAX_CHUNK_SIZE; @@ -2253,11 +2255,10 @@ static int __init rtrs_server_init(void) err); return err; } - rtrs_dev_class = class_create("rtrs-server"); - if (IS_ERR(rtrs_dev_class)) { - err = PTR_ERR(rtrs_dev_class); + err = class_register(&rtrs_dev_class); + if (err) goto out_err; - } + rtrs_wq = alloc_workqueue("rtrs_server_wq", 0, 0); if (!rtrs_wq) { err = -ENOMEM; @@ -2267,7 +2268,7 @@ static int __init rtrs_server_init(void) return 0; out_dev_class: - class_destroy(rtrs_dev_class); + class_unregister(&rtrs_dev_class); out_err: return err; } @@ -2275,7 +2276,7 @@ out_err: static void __exit rtrs_server_exit(void) { destroy_workqueue(rtrs_wq); - class_destroy(rtrs_dev_class); + class_unregister(&rtrs_dev_class); rtrs_rdma_dev_pd_deinit(&dev_pd); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.h b/drivers/infiniband/ulp/rtrs/rtrs-srv.h index 2f8a638e36fa..5e325b82ff33 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.h @@ -129,7 +129,7 @@ struct rtrs_srv_ib_ctx { int ib_dev_count; }; -extern struct class *rtrs_dev_class; +extern const struct class rtrs_dev_class; void close_path(struct rtrs_srv_path *srv_path); diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 241b1621b197..09ca83c23329 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -385,8 +385,8 @@ static void cec_data_cancel(struct cec_data *data, u8 tx_status, u8 rx_status) cec_queue_msg_monitor(adap, &data->msg, 1); if (!data->blocking && data->msg.sequence) - /* Allow drivers to process the message first */ - call_op(adap, received, &data->msg); + /* Allow drivers to react to a canceled transmit */ + call_void_op(adap, adap_nb_transmit_canceled, &data->msg); cec_data_completed(data); } @@ -1348,7 +1348,7 @@ static void cec_adap_unconfigure(struct cec_adapter *adap) cec_flush(adap); wake_up_interruptible(&adap->kthread_waitq); cec_post_state_event(adap); - call_void_op(adap, adap_configured, false); + call_void_op(adap, adap_unconfigured); } /* @@ -1539,7 +1539,7 @@ configured: adap->kthread_config = NULL; complete(&adap->config_completion); mutex_unlock(&adap->lock); - call_void_op(adap, adap_configured, true); + call_void_op(adap, configured); return 0; unconfigure: diff --git a/drivers/media/cec/core/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c index 389dc664b211..a41f24172b11 100644 --- a/drivers/media/cec/core/cec-notifier.c +++ b/drivers/media/cec/core/cec-notifier.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h index 8eb5819e6ccb..156a9f81be94 100644 --- a/drivers/media/cec/core/cec-pin-priv.h +++ b/drivers/media/cec/core/cec-pin-priv.h @@ -183,6 +183,7 @@ struct cec_pin { u16 la_mask; bool monitor_all; bool rx_eom; + bool enabled_irq; bool enable_irq_failed; enum cec_pin_state state; struct cec_msg tx_msg; diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 68353c5dc501..330d5d5d86ab 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -982,7 +982,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) } if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL || pin->enable_irq_failed || adap->is_configuring || - adap->is_configured || adap->monitor_all_cnt) + adap->is_configured || adap->monitor_all_cnt || !adap->monitor_pin_cnt) break; /* Switch to interrupt mode */ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_ENABLE); @@ -1033,8 +1033,9 @@ static int cec_pin_thread_func(void *_adap) { struct cec_adapter *adap = _adap; struct cec_pin *pin = adap->pin; - bool irq_enabled = false; + pin->enabled_irq = false; + pin->enable_irq_failed = false; for (;;) { wait_event_interruptible(pin->kthread_waitq, kthread_should_stop() || @@ -1088,9 +1089,10 @@ static int cec_pin_thread_func(void *_adap) switch (atomic_xchg(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED)) { case CEC_PIN_IRQ_DISABLE: - if (irq_enabled) { - call_void_pin_op(pin, disable_irq); - irq_enabled = false; + if (pin->enabled_irq) { + pin->ops->disable_irq(adap); + pin->enabled_irq = false; + pin->enable_irq_failed = false; } cec_pin_high(pin); if (pin->state == CEC_ST_OFF) @@ -1100,21 +1102,29 @@ static int cec_pin_thread_func(void *_adap) HRTIMER_MODE_REL); break; case CEC_PIN_IRQ_ENABLE: - if (irq_enabled) + if (pin->enabled_irq || !pin->ops->enable_irq || + pin->adap->devnode.unregistered) break; - pin->enable_irq_failed = !call_pin_op(pin, enable_irq); + pin->enable_irq_failed = !pin->ops->enable_irq(adap); if (pin->enable_irq_failed) { cec_pin_to_idle(pin); hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL); } else { - irq_enabled = true; + pin->enabled_irq = true; } break; default: break; } } + + if (pin->enabled_irq) { + pin->ops->disable_irq(pin->adap); + pin->enabled_irq = false; + pin->enable_irq_failed = false; + cec_pin_high(pin); + } return 0; } @@ -1215,7 +1225,9 @@ static void cec_pin_adap_status(struct cec_adapter *adap, seq_printf(file, "cec pin: %d\n", call_pin_op(pin, read)); seq_printf(file, "cec pin events dropped: %u\n", pin->work_pin_events_dropped_cnt); - seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed); + if (pin->ops->enable_irq) + seq_printf(file, "irq %s\n", pin->enabled_irq ? "enabled" : + (pin->enable_irq_failed ? "failed" : "disabled")); if (pin->timer_100us_overruns) { seq_printf(file, "timer overruns > 100us: %u of %u\n", pin->timer_100us_overruns, pin->timer_cnt); @@ -1305,7 +1317,7 @@ void cec_pin_changed(struct cec_adapter *adap, bool value) cec_pin_update(pin, value, false); if (!value && (adap->is_configuring || adap->is_configured || - adap->monitor_all_cnt)) + adap->monitor_all_cnt || !adap->monitor_pin_cnt)) atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE); } EXPORT_SYMBOL_GPL(cec_pin_changed); diff --git a/drivers/media/cec/i2c/ch7322.c b/drivers/media/cec/i2c/ch7322.c index 439c15bc9e44..b8755337b394 100644 --- a/drivers/media/cec/i2c/ch7322.c +++ b/drivers/media/cec/i2c/ch7322.c @@ -589,7 +589,7 @@ MODULE_DEVICE_TABLE(of, ch7322_of_match); static struct i2c_driver ch7322_i2c_driver = { .driver = { .name = "ch7322", - .of_match_table = of_match_ptr(ch7322_of_match), + .of_match_table = ch7322_of_match, }, .probe = ch7322_probe, .remove = ch7322_remove, diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index ff34490fd869..98dacb0919b6 100644 --- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -159,11 +159,6 @@ static int cec_gpio_read_5v(struct cec_adapter *adap) return gpiod_get_value(cec->v5_gpio); } -static void cec_gpio_free(struct cec_adapter *adap) -{ - cec_gpio_disable_irq(adap); -} - static const struct cec_pin_ops cec_gpio_pin_ops = { .read = cec_gpio_read, .low = cec_gpio_low, @@ -171,7 +166,6 @@ static const struct cec_pin_ops cec_gpio_pin_ops = { .enable_irq = cec_gpio_enable_irq, .disable_irq = cec_gpio_disable_irq, .status = cec_gpio_status, - .free = cec_gpio_free, .read_hpd = cec_gpio_read_hpd, .read_5v = cec_gpio_read_5v, }; @@ -215,13 +209,11 @@ static int cec_gpio_probe(struct platform_device *pdev) return PTR_ERR(cec->adap); ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, cec->adap->name, cec); if (ret) goto del_adap; - cec_gpio_disable_irq(cec->adap); - if (cec->hpd_gpio) { cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio); ret = devm_request_threaded_irq(dev, cec->hpd_irq, diff --git a/drivers/media/cec/platform/meson/ao-cec.c b/drivers/media/cec/platform/meson/ao-cec.c index f6f51a34f7bd..494738daf09a 100644 --- a/drivers/media/cec/platform/meson/ao-cec.c +++ b/drivers/media/cec/platform/meson/ao-cec.c @@ -717,7 +717,7 @@ static struct platform_driver meson_ao_cec_driver = { .remove_new = meson_ao_cec_remove, .driver = { .name = "meson-ao-cec", - .of_match_table = of_match_ptr(meson_ao_cec_of_match), + .of_match_table = meson_ao_cec_of_match, }, }; diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index ada3d153362a..bda9d254041a 100644 --- a/drivers/media/cec/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index 04dc06e3c42a..7c1022cee1e8 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -348,8 +348,8 @@ static int tegra_cec_probe(struct platform_device *pdev) cec->tegra_cec_irq = platform_get_irq(pdev, 0); - if (cec->tegra_cec_irq <= 0) - return -EBUSY; + if (cec->tegra_cec_irq < 0) + return cec->tegra_cec_irq; cec->cec_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); @@ -462,7 +462,7 @@ static const struct of_device_id tegra_cec_of_match[] = { static struct platform_driver tegra_cec_driver = { .driver = { .name = TEGRA_CEC_NAME, - .of_match_table = of_match_ptr(tegra_cec_of_match), + .of_match_table = tegra_cec_of_match, }, .probe = tegra_cec_probe, .remove_new = tegra_cec_remove, diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c index 8916bb644756..e0beefd80d7b 100644 --- a/drivers/media/common/siano/smsdvb-debugfs.c +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -45,89 +45,48 @@ static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_rf_locked = %d\n", p->is_rf_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_demod_locked = %d\n", p->is_demod_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_external_lna_on = %d\n", p->is_external_lna_on); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d\n", p->SNR); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "ber = %d\n", p->ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "FIB_CRC = %d\n", p->FIB_CRC); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "ts_per = %d\n", p->ts_per); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "MFER = %d\n", p->MFER); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d\n", p->RSSI); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "in_band_pwr = %d\n", p->in_band_pwr); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "carrier_offset = %d\n", p->carrier_offset); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "modem_state = %d\n", p->modem_state); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "frequency = %d\n", p->frequency); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "bandwidth = %d\n", p->bandwidth); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "transmission_mode = %d\n", p->transmission_mode); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "modem_state = %d\n", p->modem_state); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "guard_interval = %d\n", p->guard_interval); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "code_rate = %d\n", p->code_rate); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "lp_code_rate = %d\n", p->lp_code_rate); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "hierarchy = %d\n", p->hierarchy); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "constellation = %d\n", p->constellation); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "burst_size = %d\n", p->burst_size); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "burst_duration = %d\n", p->burst_duration); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "burst_cycle_time = %d\n", p->burst_cycle_time); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "calc_burst_cycle_time = %d\n", - p->calc_burst_cycle_time); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_rows = %d\n", p->num_of_rows); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_padd_cols = %d\n", p->num_of_padd_cols); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_punct_cols = %d\n", p->num_of_punct_cols); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "error_ts_packets = %d\n", p->error_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "total_ts_packets = %d\n", p->total_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_corrected_mpe_tlbs = %d\n", p->num_of_corrected_mpe_tlbs); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "ber_error_count = %d\n", p->ber_error_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "ber_bit_count = %d\n", p->ber_bit_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "pre_ber = %d\n", p->pre_ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "cell_id = %d\n", p->cell_id); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_mpe_received = %d\n", p->num_mpe_received); + n += sysfs_emit_at(buf, n, "is_rf_locked = %d\n", p->is_rf_locked); + n += sysfs_emit_at(buf, n, "is_demod_locked = %d\n", p->is_demod_locked); + n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on); + n += sysfs_emit_at(buf, n, "SNR = %d\n", p->SNR); + n += sysfs_emit_at(buf, n, "ber = %d\n", p->ber); + n += sysfs_emit_at(buf, n, "FIB_CRC = %d\n", p->FIB_CRC); + n += sysfs_emit_at(buf, n, "ts_per = %d\n", p->ts_per); + n += sysfs_emit_at(buf, n, "MFER = %d\n", p->MFER); + n += sysfs_emit_at(buf, n, "RSSI = %d\n", p->RSSI); + n += sysfs_emit_at(buf, n, "in_band_pwr = %d\n", p->in_band_pwr); + n += sysfs_emit_at(buf, n, "carrier_offset = %d\n", p->carrier_offset); + n += sysfs_emit_at(buf, n, "modem_state = %d\n", p->modem_state); + n += sysfs_emit_at(buf, n, "frequency = %d\n", p->frequency); + n += sysfs_emit_at(buf, n, "bandwidth = %d\n", p->bandwidth); + n += sysfs_emit_at(buf, n, "transmission_mode = %d\n", p->transmission_mode); + n += sysfs_emit_at(buf, n, "modem_state = %d\n", p->modem_state); + n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval); + n += sysfs_emit_at(buf, n, "code_rate = %d\n", p->code_rate); + n += sysfs_emit_at(buf, n, "lp_code_rate = %d\n", p->lp_code_rate); + n += sysfs_emit_at(buf, n, "hierarchy = %d\n", p->hierarchy); + n += sysfs_emit_at(buf, n, "constellation = %d\n", p->constellation); + n += sysfs_emit_at(buf, n, "burst_size = %d\n", p->burst_size); + n += sysfs_emit_at(buf, n, "burst_duration = %d\n", p->burst_duration); + n += sysfs_emit_at(buf, n, "burst_cycle_time = %d\n", p->burst_cycle_time); + n += sysfs_emit_at(buf, n, "calc_burst_cycle_time = %d\n", p->calc_burst_cycle_time); + n += sysfs_emit_at(buf, n, "num_of_rows = %d\n", p->num_of_rows); + n += sysfs_emit_at(buf, n, "num_of_padd_cols = %d\n", p->num_of_padd_cols); + n += sysfs_emit_at(buf, n, "num_of_punct_cols = %d\n", p->num_of_punct_cols); + n += sysfs_emit_at(buf, n, "error_ts_packets = %d\n", p->error_ts_packets); + n += sysfs_emit_at(buf, n, "total_ts_packets = %d\n", p->total_ts_packets); + n += sysfs_emit_at(buf, n, "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs); + n += sysfs_emit_at(buf, n, "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs); + n += sysfs_emit_at(buf, n, "num_of_corrected_mpe_tlbs = %d\n", + p->num_of_corrected_mpe_tlbs); + n += sysfs_emit_at(buf, n, "ber_error_count = %d\n", p->ber_error_count); + n += sysfs_emit_at(buf, n, "ber_bit_count = %d\n", p->ber_bit_count); + n += sysfs_emit_at(buf, n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); + n += sysfs_emit_at(buf, n, "pre_ber = %d\n", p->pre_ber); + n += sysfs_emit_at(buf, n, "cell_id = %d\n", p->cell_id); + n += sysfs_emit_at(buf, n, "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp); + n += sysfs_emit_at(buf, n, "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp); + n += sysfs_emit_at(buf, n, "num_mpe_received = %d\n", p->num_mpe_received); debug_data->stats_count = n; spin_unlock(&debug_data->lock); @@ -148,78 +107,49 @@ static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += scnprintf(&buf[n], PAGE_SIZE - n, - "statistics_type = %d\t", p->statistics_type); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "full_size = %d\n", p->full_size); + n += sysfs_emit_at(buf, n, "statistics_type = %d\t", p->statistics_type); + n += sysfs_emit_at(buf, n, "full_size = %d\n", p->full_size); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_rf_locked = %d\t\t", p->is_rf_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_demod_locked = %d\t", p->is_demod_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_external_lna_on = %d\n", p->is_external_lna_on); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d dB\t\t", p->SNR); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d dBm\t\t", p->RSSI); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "in_band_pwr = %d dBm\n", p->in_band_pwr); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "carrier_offset = %d\t", p->carrier_offset); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "bandwidth = %d\t\t", p->bandwidth); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "frequency = %d Hz\n", p->frequency); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "transmission_mode = %d\t", p->transmission_mode); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "modem_state = %d\t\t", p->modem_state); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "guard_interval = %d\n", p->guard_interval); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "system_type = %d\t\t", p->system_type); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "partial_reception = %d\t", p->partial_reception); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_layers = %d\n", p->num_of_layers); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); + n += sysfs_emit_at(buf, n, "is_rf_locked = %d\t\t", p->is_rf_locked); + n += sysfs_emit_at(buf, n, "is_demod_locked = %d\t", p->is_demod_locked); + n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on); + n += sysfs_emit_at(buf, n, "SNR = %d dB\t\t", p->SNR); + n += sysfs_emit_at(buf, n, "RSSI = %d dBm\t\t", p->RSSI); + n += sysfs_emit_at(buf, n, "in_band_pwr = %d dBm\n", p->in_band_pwr); + n += sysfs_emit_at(buf, n, "carrier_offset = %d\t", p->carrier_offset); + n += sysfs_emit_at(buf, n, "bandwidth = %d\t\t", p->bandwidth); + n += sysfs_emit_at(buf, n, "frequency = %d Hz\n", p->frequency); + n += sysfs_emit_at(buf, n, "transmission_mode = %d\t", p->transmission_mode); + n += sysfs_emit_at(buf, n, "modem_state = %d\t\t", p->modem_state); + n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval); + n += sysfs_emit_at(buf, n, "system_type = %d\t\t", p->system_type); + n += sysfs_emit_at(buf, n, "partial_reception = %d\t", p->partial_reception); + n += sysfs_emit_at(buf, n, "num_of_layers = %d\n", p->num_of_layers); + n += sysfs_emit_at(buf, n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); for (i = 0; i < 3; i++) { if (p->layer_info[i].number_of_segments < 1 || p->layer_info[i].number_of_segments > 13) continue; - n += scnprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", - p->layer_info[i].code_rate); - n += scnprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", - p->layer_info[i].constellation); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", - p->layer_info[i].ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\tber_error_count = %-5d\t", - p->layer_info[i].ber_error_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", - p->layer_info[i].ber_bit_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", - p->layer_info[i].pre_ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", - p->layer_info[i].ts_per); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\terror_ts_packets = %-5d\t", - p->layer_info[i].error_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "total_ts_packets = %-5d\t", - p->layer_info[i].total_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", - p->layer_info[i].ti_ldepth_i); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\tnumber_of_segments = %d\t", - p->layer_info[i].number_of_segments); - n += scnprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", - p->layer_info[i].tmcc_errors); + n += sysfs_emit_at(buf, n, "\nLayer %d\n", i); + n += sysfs_emit_at(buf, n, "\tcode_rate = %d\t", p->layer_info[i].code_rate); + n += sysfs_emit_at(buf, n, "constellation = %d\n", p->layer_info[i].constellation); + n += sysfs_emit_at(buf, n, "\tber = %-5d\t", p->layer_info[i].ber); + n += sysfs_emit_at(buf, n, "\tber_error_count = %-5d\t", + p->layer_info[i].ber_error_count); + n += sysfs_emit_at(buf, n, "ber_bit_count = %-5d\n", + p->layer_info[i].ber_bit_count); + n += sysfs_emit_at(buf, n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber); + n += sysfs_emit_at(buf, n, "\tts_per = %-5d\n", p->layer_info[i].ts_per); + n += sysfs_emit_at(buf, n, "\terror_ts_packets = %-5d\t", + p->layer_info[i].error_ts_packets); + n += sysfs_emit_at(buf, n, "total_ts_packets = %-5d\t", + p->layer_info[i].total_ts_packets); + n += sysfs_emit_at(buf, n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i); + n += sysfs_emit_at(buf, n, "\tnumber_of_segments = %d\t", + p->layer_info[i].number_of_segments); + n += sysfs_emit_at(buf, n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors); } debug_data->stats_count = n; @@ -241,80 +171,50 @@ static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += scnprintf(&buf[n], PAGE_SIZE - n, - "statistics_type = %d\t", p->statistics_type); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "full_size = %d\n", p->full_size); + n += sysfs_emit_at(buf, n, "statistics_type = %d\t", p->statistics_type); + n += sysfs_emit_at(buf, n, "full_size = %d\n", p->full_size); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_rf_locked = %d\t\t", p->is_rf_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_demod_locked = %d\t", p->is_demod_locked); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "is_external_lna_on = %d\n", p->is_external_lna_on); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "SNR = %d dB\t\t", p->SNR); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "RSSI = %d dBm\t\t", p->RSSI); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "in_band_pwr = %d dBm\n", p->in_band_pwr); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "carrier_offset = %d\t", p->carrier_offset); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "bandwidth = %d\t\t", p->bandwidth); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "frequency = %d Hz\n", p->frequency); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "transmission_mode = %d\t", p->transmission_mode); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "modem_state = %d\t\t", p->modem_state); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "guard_interval = %d\n", p->guard_interval); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "system_type = %d\t\t", p->system_type); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "partial_reception = %d\t", p->partial_reception); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "num_of_layers = %d\n", p->num_of_layers); - n += scnprintf(&buf[n], PAGE_SIZE - n, "segment_number = %d\t", - p->segment_number); - n += scnprintf(&buf[n], PAGE_SIZE - n, "tune_bw = %d\n", - p->tune_bw); + n += sysfs_emit_at(buf, n, "is_rf_locked = %d\t\t", p->is_rf_locked); + n += sysfs_emit_at(buf, n, "is_demod_locked = %d\t", p->is_demod_locked); + n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on); + n += sysfs_emit_at(buf, n, "SNR = %d dB\t\t", p->SNR); + n += sysfs_emit_at(buf, n, "RSSI = %d dBm\t\t", p->RSSI); + n += sysfs_emit_at(buf, n, "in_band_pwr = %d dBm\n", p->in_band_pwr); + n += sysfs_emit_at(buf, n, "carrier_offset = %d\t", p->carrier_offset); + n += sysfs_emit_at(buf, n, "bandwidth = %d\t\t", p->bandwidth); + n += sysfs_emit_at(buf, n, "frequency = %d Hz\n", p->frequency); + n += sysfs_emit_at(buf, n, "transmission_mode = %d\t", p->transmission_mode); + n += sysfs_emit_at(buf, n, "modem_state = %d\t\t", p->modem_state); + n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval); + n += sysfs_emit_at(buf, n, "system_type = %d\t\t", p->system_type); + n += sysfs_emit_at(buf, n, "partial_reception = %d\t", p->partial_reception); + n += sysfs_emit_at(buf, n, "num_of_layers = %d\n", p->num_of_layers); + n += sysfs_emit_at(buf, n, "segment_number = %d\t", p->segment_number); + n += sysfs_emit_at(buf, n, "tune_bw = %d\n", p->tune_bw); for (i = 0; i < 3; i++) { if (p->layer_info[i].number_of_segments < 1 || p->layer_info[i].number_of_segments > 13) continue; - n += scnprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", - p->layer_info[i].code_rate); - n += scnprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", - p->layer_info[i].constellation); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", - p->layer_info[i].ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\tber_error_count = %-5d\t", - p->layer_info[i].ber_error_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", - p->layer_info[i].ber_bit_count); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", - p->layer_info[i].pre_ber); - n += scnprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", - p->layer_info[i].ts_per); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\terror_ts_packets = %-5d\t", - p->layer_info[i].error_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "total_ts_packets = %-5d\t", - p->layer_info[i].total_ts_packets); - n += scnprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", - p->layer_info[i].ti_ldepth_i); - n += scnprintf(&buf[n], PAGE_SIZE - n, - "\tnumber_of_segments = %d\t", - p->layer_info[i].number_of_segments); - n += scnprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", - p->layer_info[i].tmcc_errors); + n += sysfs_emit_at(buf, n, "\nLayer %d\n", i); + n += sysfs_emit_at(buf, n, "\tcode_rate = %d\t", p->layer_info[i].code_rate); + n += sysfs_emit_at(buf, n, "constellation = %d\n", p->layer_info[i].constellation); + n += sysfs_emit_at(buf, n, "\tber = %-5d\t", p->layer_info[i].ber); + n += sysfs_emit_at(buf, n, "\tber_error_count = %-5d\t", + p->layer_info[i].ber_error_count); + n += sysfs_emit_at(buf, n, "ber_bit_count = %-5d\n", + p->layer_info[i].ber_bit_count); + n += sysfs_emit_at(buf, n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber); + n += sysfs_emit_at(buf, n, "\tts_per = %-5d\n", p->layer_info[i].ts_per); + n += sysfs_emit_at(buf, n, "\terror_ts_packets = %-5d\t", + p->layer_info[i].error_ts_packets); + n += sysfs_emit_at(buf, n, "total_ts_packets = %-5d\t", + p->layer_info[i].total_ts_packets); + n += sysfs_emit_at(buf, n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i); + n += sysfs_emit_at(buf, n, "\tnumber_of_segments = %d\t", + p->layer_info[i].number_of_segments); + n += sysfs_emit_at(buf, n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors); } diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c index 8cb8853a1edb..a3573814919b 100644 --- a/drivers/media/common/siano/smsendian.c +++ b/drivers/media/common/siano/smsendian.c @@ -17,7 +17,7 @@ void smsendian_handle_tx_message(void *buffer) { #ifdef __BIG_ENDIAN - struct sms_msg_data *msg = (struct sms_msg_data *)buffer; + struct sms_msg_data *msg = buffer; int i; int msg_words; diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index d8acd582c711..0f778660c72b 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -973,7 +973,9 @@ static void cx24120_set_clock_ratios(struct dvb_frontend *fe) cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff; cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff; - cx24120_message_send(state, &cmd); + ret = cx24120_message_send(state, &cmd); + if (ret != 0) + return; /* Calculate ber window rates for stat work */ cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate); diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index b791e687d2e2..9273758bf140 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -497,7 +497,7 @@ static int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth prediv = reg_1856 & 0x3f; loopdiv = (reg_1856 >> 6) & 0x3f; - if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { + if (loopdiv && bw && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)\n", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); reg_1856 &= 0xf000; reg_1857 = dib7000p_read_word(state, 1857); diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 6ad4f202f1bf..2770baebbbbc 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -229,13 +229,8 @@ static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len) struct i2c_msg msg = { .addr = adr, .flags = 0, .buf = data, .len = len }; - dprintk(3, ":"); - if (debug > 2) { - int i; - for (i = 0; i < len; i++) - pr_cont(" %02x", data[i]); - pr_cont("\n"); - } + dprintk(3, ": %*ph\n", len, data); + status = drxk_i2c_transfer(state, &msg, 1); if (status >= 0 && status != 1) status = -EIO; @@ -267,16 +262,7 @@ static int i2c_read(struct drxk_state *state, pr_err("i2c read error at addr 0x%02x\n", adr); return status; } - if (debug > 2) { - int i; - dprintk(2, ": read from"); - for (i = 0; i < len; i++) - pr_cont(" %02x", msg[i]); - pr_cont(", value = "); - for (i = 0; i < alen; i++) - pr_cont(" %02x", answ[i]); - pr_cont("\n"); - } + dprintk(3, ": read from %*ph, value = %*ph\n", len, msg, alen, answ); return 0; } @@ -441,13 +427,8 @@ static int write_block(struct drxk_state *state, u32 address, } memcpy(&state->chunk[adr_length], p_block, chunk); dprintk(2, "(0x%08x, 0x%02x)\n", address, flags); - if (debug > 1) { - int i; - if (p_block) - for (i = 0; i < chunk; i++) - pr_cont(" %02x", p_block[i]); - pr_cont("\n"); - } + if (p_block) + dprintk(2, "%*ph\n", chunk, p_block); status = i2c_write(state, state->demod_address, &state->chunk[0], chunk + adr_length); if (status < 0) { diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index d3e29937cf4c..3ec2cb4fa504 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1487,10 +1487,12 @@ static int mb86a16_set_fe(struct mb86a16_state *state) } } - mb86a16_read(state, 0x15, &agcval); - mb86a16_read(state, 0x26, &cnmval); - dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval); - + if (mb86a16_read(state, 0x15, &agcval) != 2 || mb86a16_read(state, 0x26, &cnmval) != 2) { + dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); + ret = -EREMOTEIO; + } else { + dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval); + } return ret; } diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c index db2921c736af..7a58f53ab999 100644 --- a/drivers/media/dvb-frontends/mn88443x.c +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c index 71991f8638e6..a36c28412170 100644 --- a/drivers/media/firewire/firedtv-avc.c +++ b/drivers/media/firewire/firedtv-avc.c @@ -597,7 +597,8 @@ int avc_tuner_dsd(struct firedtv *fdtv, case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, p); break; case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, p); break; default: - BUG(); + ret = -EIO; + goto unlock; } pad_operands(c, pos); @@ -612,6 +613,7 @@ int avc_tuner_dsd(struct firedtv *fdtv, if (status) *status = r->operand[2]; #endif +unlock: mutex_unlock(&fdtv->avc_mutex); if (ret == 0) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 226454b6a90d..74ff833ff48c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -25,8 +25,15 @@ config VIDEO_IR_I2C # V4L2 I2C drivers that are related with Camera support # -menu "Camera sensor devices" - visible if MEDIA_CAMERA_SUPPORT +menuconfig VIDEO_CAMERA_SENSOR + bool "Camera sensor devices" + depends on MEDIA_CAMERA_SUPPORT && I2C + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + default y + +if VIDEO_CAMERA_SENSOR config VIDEO_APTINA_PLL tristate @@ -36,10 +43,6 @@ config VIDEO_CCS_PLL config VIDEO_AR0521 tristate "ON Semiconductor AR0521 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the ON Semiconductor AR0521 camera. @@ -49,10 +52,6 @@ config VIDEO_AR0521 config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-556 camera. @@ -62,10 +61,6 @@ config VIDEO_HI556 config VIDEO_HI846 tristate "Hynix Hi-846 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-846 camera. @@ -75,10 +70,6 @@ config VIDEO_HI846 config VIDEO_HI847 tristate "Hynix Hi-847 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-847 camera. @@ -88,10 +79,6 @@ config VIDEO_HI847 config VIDEO_IMX208 tristate "Sony IMX208 sensor support" - depends on I2C && VIDEO_DEV - depends on MEDIA_CAMERA_SUPPORT - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX208 camera. @@ -101,10 +88,7 @@ config VIDEO_IMX208 config VIDEO_IMX214 tristate "Sony IMX214 sensor support" - depends on GPIOLIB && I2C && VIDEO_DEV - select V4L2_FWNODE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB select REGMAP_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -115,10 +99,6 @@ config VIDEO_IMX214 config VIDEO_IMX219 tristate "Sony IMX219 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX219 camera. @@ -128,9 +108,6 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -140,9 +117,6 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C help This is a V4L2 sensor driver for the Sony IMX274 @@ -150,11 +124,8 @@ config VIDEO_IMX274 config VIDEO_IMX290 tristate "Sony IMX290 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX290 camera sensor. @@ -164,10 +135,6 @@ config VIDEO_IMX290 config VIDEO_IMX296 tristate "Sony IMX296 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX296 camera. @@ -177,9 +144,6 @@ config VIDEO_IMX296 config VIDEO_IMX319 tristate "Sony IMX319 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX319 camera. @@ -190,10 +154,6 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX334 camera. @@ -204,10 +164,6 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX335 camera. @@ -217,9 +173,6 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -230,10 +183,6 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. @@ -244,10 +193,6 @@ config VIDEO_IMX412 config VIDEO_IMX415 tristate "Sony IMX415 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX415 camera. @@ -260,35 +205,25 @@ config VIDEO_MAX9271_LIB config VIDEO_MT9M001 tristate "mt9m001 support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This driver supports MT9M001 cameras from Micron, monochrome and colour models. config VIDEO_MT9M111 tristate "mt9m111, mt9m112 and mt9m131 support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This driver supports MT9M111, MT9M112 and MT9M131 cameras from Micron/Aptina config VIDEO_MT9P031 tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select VIDEO_APTINA_PLL - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Aptina (Micron) mt9p031 5 Mpixel camera. config VIDEO_MT9T112 tristate "Aptina MT9T111/MT9T112 support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. @@ -298,7 +233,6 @@ config VIDEO_MT9T112 config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the @@ -306,18 +240,13 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Micron MT9V032 752x480 CMOS sensor. config VIDEO_MT9V111 tristate "Aptina MT9V111 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Aptina/Micron MT9V111 sensor. @@ -327,10 +256,6 @@ config VIDEO_MT9V111 config VIDEO_OG01A1B tristate "OmniVision OG01A1B sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OG01A1B camera. @@ -340,10 +265,6 @@ config VIDEO_OG01A1B config VIDEO_OV01A10 tristate "OmniVision OV01A10 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV01A10 camera. @@ -353,10 +274,6 @@ config VIDEO_OV01A10 config VIDEO_OV02A10 tristate "OmniVision OV02A10 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV02A10 camera. @@ -366,10 +283,6 @@ config VIDEO_OV02A10 config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV08D10 camera sensor. @@ -379,10 +292,6 @@ config VIDEO_OV08D10 config VIDEO_OV08X40 tristate "OmniVision OV08X40 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV08X40 camera. @@ -392,28 +301,18 @@ config VIDEO_OV08X40 config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV13858 camera. config VIDEO_OV13B10 tristate "OmniVision OV13B10 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV13B10 camera. config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" - depends on VIDEO_DEV && I2C - select V4L2_ASYNC help This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. @@ -423,8 +322,7 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" - depends on VIDEO_DEV && I2C && GPIOLIB - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor driver for the OmniVision OV2659 camera. @@ -434,9 +332,7 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV2680 camera. @@ -446,10 +342,6 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV2685 camera. @@ -459,11 +351,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" - depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE select REGMAP_I2C help This is a Video4Linux2 sensor driver for the OmniVision @@ -474,10 +362,7 @@ config VIDEO_OV2740 config VIDEO_OV4689 tristate "OmniVision OV4689 sensor support" - depends on GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor-level driver for the OmniVision OV4689 camera. @@ -488,10 +373,7 @@ config VIDEO_OV4689 config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF - depends on GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor driver for the Omnivision OV5640 camera sensor with a MIPI CSI-2 interface. @@ -499,10 +381,6 @@ config VIDEO_OV5640 config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5645 camera. @@ -512,10 +390,6 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5647 camera. @@ -525,10 +399,7 @@ config VIDEO_OV5647 config VIDEO_OV5648 tristate "OmniVision OV5648 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for the OmniVision OV5648 camera. @@ -538,10 +409,6 @@ config VIDEO_OV5648 config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5670 camera. @@ -551,10 +418,6 @@ config VIDEO_OV5670 config VIDEO_OV5675 tristate "OmniVision OV5675 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5675 camera. @@ -564,8 +427,7 @@ config VIDEO_OV5675 config VIDEO_OV5693 tristate "OmniVision OV5693 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV5693 camera. @@ -575,8 +437,6 @@ config VIDEO_OV5693 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. @@ -586,7 +446,6 @@ config VIDEO_OV5695 config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. @@ -596,10 +455,6 @@ config VIDEO_OV6650 config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV7251 camera. @@ -609,7 +464,6 @@ config VIDEO_OV7251 config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. @@ -619,8 +473,6 @@ config VIDEO_OV7640 config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 @@ -628,9 +480,7 @@ config VIDEO_OV7670 config VIDEO_OV772X tristate "OmniVision OV772x sensor support" - depends on I2C && VIDEO_DEV select REGMAP_SCCB - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. @@ -640,7 +490,6 @@ config VIDEO_OV772X config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" - depends on I2C && VIDEO_DEV select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision @@ -648,10 +497,6 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV8856 camera sensor. @@ -661,10 +506,7 @@ config VIDEO_OV8856 config VIDEO_OV8858 tristate "OmniVision OV8858 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for OmniVision OV8858 camera sensor. @@ -674,10 +516,7 @@ config VIDEO_OV8858 config VIDEO_OV8865 tristate "OmniVision OV8865 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for OmniVision OV8865 camera sensor. @@ -688,10 +527,6 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV9282 camera sensor. @@ -701,16 +536,12 @@ config VIDEO_OV9282 config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV9640 camera sensor. config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_SCCB help This is a V4L2 sensor driver for the Omnivision @@ -718,11 +549,7 @@ config VIDEO_OV9650 config VIDEO_OV9734 tristate "OmniVision OV9734 sensor support" - depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV9734 camera. @@ -732,10 +559,6 @@ config VIDEO_OV9734 config VIDEO_RDACM20 tristate "IMI RDACM20 camera support" - depends on I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM20 GMSL camera, used in @@ -746,10 +569,6 @@ config VIDEO_RDACM20 config VIDEO_RDACM21 tristate "IMI RDACM21 camera support" - depends on I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM21 GMSL camera, used in @@ -760,7 +579,6 @@ config VIDEO_RDACM21 config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" - depends on I2C && VIDEO_DEV help This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. @@ -770,39 +588,26 @@ config VIDEO_RJ54N1 config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" - depends on I2C && SPI && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on SPI help This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K6A3 tristate "Samsung S5K6A3 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. config VIDEO_ST_VGXY61 tristate "ST VGXY61 sensor support" - depends on OF && GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on OF && GPIOLIB help This is a Video4Linux2 sensor driver for the ST VGXY61 camera sensor. @@ -810,7 +615,7 @@ config VIDEO_ST_VGXY61 source "drivers/media/i2c/ccs/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" -endmenu +endif menu "Lens drivers" visible if MEDIA_CAMERA_SUPPORT @@ -848,6 +653,18 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9719 + tristate "DW9719 lens voice coil support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_ASYNC + select V4L2_CCI_I2C + help + This is a driver for the DW9719 camera lens voice coil. + This is designed for linear control of voice coil motors, + controlled via I2C serial interface. + config VIDEO_DW9768 tristate "DW9768 lens voice coil support" depends on I2C && VIDEO_DEV @@ -1625,4 +1442,51 @@ config VIDEO_THS7303 endmenu +# +# Video serializers and deserializers (e.g. FPD-Link) +# + +menu "Video serializers and deserializers" + +config VIDEO_DS90UB913 + tristate "TI DS90UB913 FPD-Link III Serializer" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB913 + FPD-Link III Serializer. + +config VIDEO_DS90UB953 + tristate "TI FPD-Link III/IV CSI-2 Serializers" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB953 + FPD-Link III Serializer and DS90UB971 FPD-Link IV Serializer. + +config VIDEO_DS90UB960 + tristate "TI FPD-Link III/IV Deserializers" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB960 + FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. + +endmenu + endif # VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c743aeb5d1ad..80b00d39b48f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -28,7 +28,11 @@ obj-$(CONFIG_VIDEO_CS3308) += cs3308.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_DS90UB913) += ds90ub913.o +obj-$(CONFIG_VIDEO_DS90UB953) += ds90ub953.o +obj-$(CONFIG_VIDEO_DS90UB960) += ds90ub960.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9719) += dw9719.o obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 5f605b9be3b1..1543d24f522c 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -349,7 +349,6 @@ static void ad5820_remove(struct i2c_client *client) static const struct i2c_device_id ad5820_id_table[] = { { "ad5820", 0 }, { "ad5821", 0 }, - { "ad5823", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); @@ -357,7 +356,6 @@ MODULE_DEVICE_TABLE(i2c, ad5820_id_table); static const struct of_device_id ad5820_of_table[] = { { .compatible = "adi,ad5820" }, { .compatible = "adi,ad5821" }, - { .compatible = "adi,ad5823" }, { } }; MODULE_DEVICE_TABLE(of, ad5820_of_table); diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index bd4f3fe0e309..a5a7cb228896 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -300,9 +300,6 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) MEDIA_ENT_F_VID_IF_BRIDGE, is_txa(tx) ? "txa" : "txb"); - /* Ensure that matching is based upon the endpoint fwnodes */ - tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]); - /* Register internal ops for incremental subdev registration */ tx->sd.internal_ops = &adv748x_csi2_internal_ops; @@ -314,10 +311,15 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) if (ret) return ret; - ret = adv748x_csi2_init_controls(tx); + ret = v4l2_async_subdev_endpoint_add(&tx->sd, + of_fwnode_handle(state->endpoints[tx->port])); if (ret) goto err_free_media; + ret = adv748x_csi2_init_controls(tx); + if (ret) + goto err_cleanup_subdev; + ret = v4l2_async_register_subdev(&tx->sd); if (ret) goto err_free_ctrl; @@ -326,6 +328,8 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) err_free_ctrl: v4l2_ctrl_handler_free(&tx->ctrl_hdl); +err_cleanup_subdev: + v4l2_subdev_cleanup(&tx->sd); err_free_media: media_entity_cleanup(&tx->sd.entity); @@ -340,4 +344,5 @@ void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) v4l2_async_unregister_subdev(&tx->sd); media_entity_cleanup(&tx->sd.entity); v4l2_ctrl_handler_free(&tx->ctrl_hdl); + v4l2_subdev_cleanup(&tx->sd); } diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index fcc39360cc50..cf8858cb13d4 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -296,7 +296,7 @@ __ccs_pll_calculate_vt_tree(struct device *dev, struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk; u32 more_mul; - u16 best_pix_div = SHRT_MAX >> 1, best_div; + u16 best_pix_div = SHRT_MAX >> 1, best_div = lim_bk->max_sys_clk_div; u16 vt_div, min_sys_div, max_sys_div, sys_div; pll_fr->pll_ip_clk_freq_hz = diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig index 71671db3d993..b55c93a2e204 100644 --- a/drivers/media/i2c/ccs/Kconfig +++ b/drivers/media/i2c/ccs/Kconfig @@ -1,11 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CCS tristate "MIPI CCS/SMIA++/SMIA sensor support" - depends on I2C && VIDEO_DEV && HAVE_CLK - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API + depends on HAVE_CLK select VIDEO_CCS_PLL - select V4L2_FWNODE help This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant camera sensors. diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 45f2b2f55ec5..08400edf77ce 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -464,8 +464,7 @@ static int ccs_data_parse_rules(struct bin_container *bin, rule_payload = __rule_type + 1; rule_plen2 = rule_plen - sizeof(*__rule_type); - switch (*__rule_type) { - case CCS_DATA_BLOCK_RULE_ID_IF: { + if (*__rule_type == CCS_DATA_BLOCK_RULE_ID_IF) { const struct __ccs_data_block_rule_if *__if_rules = rule_payload; const size_t __num_if_rules = @@ -514,49 +513,61 @@ static int ccs_data_parse_rules(struct bin_container *bin, rules->if_rules = if_rule; rules->num_if_rules = __num_if_rules; } - break; - } - case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: - rval = ccs_data_parse_reg_rules(bin, &rules->read_only_regs, - &rules->num_read_only_regs, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_FFD: - rval = ccs_data_parse_ffd(bin, &rules->frame_format, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_MSR: - rval = ccs_data_parse_reg_rules(bin, - &rules->manufacturer_regs, - &rules->num_manufacturer_regs, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: - rval = ccs_data_parse_pdaf_readout(bin, - &rules->pdaf_readout, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - default: - dev_dbg(dev, - "Don't know how to handle rule type %u!\n", - *__rule_type); - return -EINVAL; + } else { + /* Check there was an if rule before any other rules */ + if (bin->base && !rules) + return -EINVAL; + + switch (*__rule_type) { + case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: + rval = ccs_data_parse_reg_rules(bin, + rules ? + &rules->read_only_regs : NULL, + rules ? + &rules->num_read_only_regs : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_FFD: + rval = ccs_data_parse_ffd(bin, rules ? + &rules->frame_format : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_MSR: + rval = ccs_data_parse_reg_rules(bin, + rules ? + &rules->manufacturer_regs : NULL, + rules ? + &rules->num_manufacturer_regs : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: + rval = ccs_data_parse_pdaf_readout(bin, + rules ? + &rules->pdaf_readout : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + default: + dev_dbg(dev, + "Don't know how to handle rule type %u!\n", + *__rule_type); + return -EINVAL; + } } __next_rule = __next_rule + rule_hlen + rule_plen; } diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c new file mode 100644 index 000000000000..4bfa3b3cf619 --- /dev/null +++ b/drivers/media/i2c/ds90ub913.c @@ -0,0 +1,903 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB913 video serializer + * + * Based on a driver from Luca Ceresoli + * + * Copyright (c) 2019 Luca Ceresoli + * Copyright (c) 2023 Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define UB913_PAD_SINK 0 +#define UB913_PAD_SOURCE 1 + +/* + * UB913 has 4 gpios, but gpios 3 and 4 are reserved for external oscillator + * mode. Thus we only support 2 gpios for now. + */ +#define UB913_NUM_GPIOS 2 + +#define UB913_REG_RESET_CTL 0x01 +#define UB913_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB913_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB913_REG_GENERAL_CFG 0x03 +#define UB913_REG_GENERAL_CFG_CRC_ERR_RESET BIT(5) +#define UB913_REG_GENERAL_CFG_PCLK_RISING BIT(0) + +#define UB913_REG_MODE_SEL 0x05 +#define UB913_REG_MODE_SEL_MODE_OVERRIDE BIT(5) +#define UB913_REG_MODE_SEL_MODE_UP_TO_DATE BIT(4) +#define UB913_REG_MODE_SEL_MODE_MASK GENMASK(3, 0) + +#define UB913_REG_CRC_ERRORS_LSB 0x0a +#define UB913_REG_CRC_ERRORS_MSB 0x0b + +#define UB913_REG_GENERAL_STATUS 0x0c + +#define UB913_REG_GPIO_CFG(n) (0x0d + (n)) +#define UB913_REG_GPIO_CFG_ENABLE(n) BIT(0 + (n) * 4) +#define UB913_REG_GPIO_CFG_DIR_INPUT(n) BIT(1 + (n) * 4) +#define UB913_REG_GPIO_CFG_REMOTE_EN(n) BIT(2 + (n) * 4) +#define UB913_REG_GPIO_CFG_OUT_VAL(n) BIT(3 + (n) * 4) +#define UB913_REG_GPIO_CFG_MASK(n) (0xf << ((n) * 4)) + +#define UB913_REG_SCL_HIGH_TIME 0x11 +#define UB913_REG_SCL_LOW_TIME 0x12 + +#define UB913_REG_PLL_OVR 0x35 + +struct ub913_data { + struct i2c_client *client; + struct regmap *regmap; + struct clk *clkin; + + struct gpio_chip gpio_chip; + + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + + u64 enabled_source_streams; + + struct clk_hw *clkout_clk_hw; + + struct ds90ub9xx_platform_data *plat_data; + + bool pclk_polarity_rising; +}; + +static inline struct ub913_data *sd_to_ub913(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub913_data, sd); +} + +struct ub913_format_info { + u32 incode; + u32 outcode; +}; + +static const struct ub913_format_info ub913_formats[] = { + /* Only RAW10 with 8-bit payload is supported at the moment */ + { .incode = MEDIA_BUS_FMT_YUYV8_2X8, .outcode = MEDIA_BUS_FMT_YUYV8_1X16 }, + { .incode = MEDIA_BUS_FMT_UYVY8_2X8, .outcode = MEDIA_BUS_FMT_UYVY8_1X16 }, + { .incode = MEDIA_BUS_FMT_VYUY8_2X8, .outcode = MEDIA_BUS_FMT_VYUY8_1X16 }, + { .incode = MEDIA_BUS_FMT_YVYU8_2X8, .outcode = MEDIA_BUS_FMT_YVYU8_1X16 }, +}; + +static const struct ub913_format_info *ub913_find_format(u32 incode) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ub913_formats); i++) { + if (ub913_formats[i].incode == incode) + return &ub913_formats[i]; + } + + return NULL; +} + +static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret < 0) { + dev_err(&priv->client->dev, + "Cannot read register 0x%02x: %d!\n", reg, ret); + return ret; + } + + *val = v; + return 0; +} + +static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) +{ + int ret; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "Cannot write register 0x%02x: %d!\n", reg, ret); + + return ret; +} + +/* + * GPIO chip + */ +static int ub913_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ub913_data *priv = gpiochip_get_data(gc); + unsigned int reg_idx = offset / 2; + unsigned int field_idx = offset % 2; + + return regmap_update_bits(priv->regmap, UB913_REG_GPIO_CFG(reg_idx), + UB913_REG_GPIO_CFG_MASK(field_idx), + UB913_REG_GPIO_CFG_ENABLE(field_idx) | + (value ? UB913_REG_GPIO_CFG_OUT_VAL(field_idx) : + 0)); +} + +static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + ub913_gpio_direction_out(gc, offset, value); +} + +static int ub913_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0]; +} + +static int ub913_gpiochip_probe(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int ret; + + /* Initialize GPIOs 0 and 1 to local control, tri-state */ + ub913_write(priv, UB913_REG_GPIO_CFG(0), 0); + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->can_sleep = true; + gc->ngpio = UB913_NUM_GPIOS; + gc->get_direction = ub913_gpio_get_direction; + gc->direction_output = ub913_gpio_direction_out; + gc->set = ub913_gpio_set; + gc->of_xlate = ub913_gpio_of_xlate; + gc->of_gpio_n_cells = 2; + + ret = gpiochip_add_data(gc, priv); + if (ret) { + dev_err(dev, "Failed to add GPIOs: %d\n", ret); + return ret; + } + + return 0; +} + +static void ub913_gpiochip_remove(struct ub913_data *priv) +{ + gpiochip_remove(&priv->gpio_chip); +} + +static const struct regmap_config ub913_regmap_config = { + .name = "ds90ub913", + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_DEFAULT, + .val_format_endian = REGMAP_ENDIAN_DEFAULT, +}; + +/* + * V4L2 + */ + +static int ub913_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub913_data *priv = sd_to_ub913(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, + UB913_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams |= streams_mask; + + return 0; +} + +static int ub913_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub913_data *priv = sd_to_ub913(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, + UB913_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams &= ~streams_mask; + + return 0; +} + +static int _ub913_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt in_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + static const struct v4l2_mbus_framefmt out_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; i++) { + if (stream_configs->configs[i].pad == UB913_PAD_SINK) + stream_configs->configs[i].fmt = in_format; + else + stream_configs->configs[i].fmt = out_format; + } + + return 0; +} + +static int ub913_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub913_data *priv = sd_to_ub913(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _ub913_set_routing(sd, state, routing); +} + +static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub913_data *priv = sd_to_ub913(sd); + const struct v4l2_subdev_krouting *routing; + struct v4l2_mbus_frame_desc source_fd; + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret; + + if (pad != UB913_PAD_SOURCE) + return -EINVAL; + + ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, + priv->source_sd_pad, &source_fd); + if (ret) + return ret; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + routing = &state->routing; + + for_each_active_route(routing, route) { + unsigned int i; + + if (route->source_pad != pad) + continue; + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) + break; + } + + if (i == source_fd.num_entries) { + dev_err(&priv->client->dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_fd.entry[i].flags; + fd->entry[fd->num_entries].length = source_fd.entry[i].length; + fd->entry[fd->num_entries].pixelcode = + source_fd.entry[i].pixelcode; + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub913_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub913_data *priv = sd_to_ub913(sd); + struct v4l2_mbus_framefmt *fmt; + const struct ub913_format_info *finfo; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* Source format is fully defined by the sink format, so not settable */ + if (format->pad == UB913_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + finfo = ub913_find_format(format->format.code); + if (!finfo) { + finfo = &ub913_formats[0]; + format->format.code = finfo->incode; + } + + /* Set sink format */ + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format, and adjust the mbus code */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + format->format.code = finfo->outcode; + + *fmt = format->format; + + return 0; +} + +static int ub913_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UB913_PAD_SINK, + .sink_stream = 0, + .source_pad = UB913_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub913_set_routing(sd, state, &routing); +} + +static int ub913_log_status(struct v4l2_subdev *sd) +{ + struct ub913_data *priv = sd_to_ub913(sd); + struct device *dev = &priv->client->dev; + u8 v = 0, v1 = 0, v2 = 0; + + ub913_read(priv, UB913_REG_MODE_SEL, &v); + dev_info(dev, "MODE_SEL %#02x\n", v); + + ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1); + ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2); + dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); + + /* clear CRC errors */ + ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + ub913_write(priv, UB913_REG_GENERAL_CFG, + v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET); + ub913_write(priv, UB913_REG_GENERAL_CFG, v); + + ub913_read(priv, UB913_REG_GENERAL_STATUS, &v); + dev_info(dev, "GENERAL_STATUS %#02x\n", v); + + ub913_read(priv, UB913_REG_PLL_OVR, &v); + dev_info(dev, "PLL_OVR %#02x\n", v); + + return 0; +} + +static const struct v4l2_subdev_core_ops ub913_subdev_core_ops = { + .log_status = ub913_log_status, +}; + +static const struct v4l2_subdev_pad_ops ub913_pad_ops = { + .enable_streams = ub913_enable_streams, + .disable_streams = ub913_disable_streams, + .set_routing = ub913_set_routing, + .get_frame_desc = ub913_get_frame_desc, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub913_set_fmt, + .init_cfg = ub913_init_cfg, +}; + +static const struct v4l2_subdev_ops ub913_subdev_ops = { + .core = &ub913_subdev_core_ops, + .pad = &ub913_pad_ops, +}; + +static const struct media_entity_operations ub913_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int ub913_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct ub913_data *priv = sd_to_ub913(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, UB913_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations ub913_notify_ops = { + .bound = ub913_notify_bound, +}; + +static int ub913_v4l2_notifier_register(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB913_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &ub913_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub913_v4l2_nf_unregister(struct ub913_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +static int ub913_register_clkout(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + const char *name; + int ret; + + name = kasprintf(GFP_KERNEL, "ds90ub913.%s.clk_out", dev_name(dev)); + if (!name) + return -ENOMEM; + + priv->clkout_clk_hw = devm_clk_hw_register_fixed_factor(dev, name, + __clk_get_name(priv->clkin), 0, 1, 2); + + kfree(name); + + if (IS_ERR(priv->clkout_clk_hw)) + return dev_err_probe(dev, PTR_ERR(priv->clkout_clk_hw), + "Cannot register clkout hw\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + priv->clkout_clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; +} + +static int ub913_i2c_master_init(struct ub913_data *priv) +{ + /* i2c fast mode */ + u32 scl_high = 600 + 300; /* high period + rise time, ns */ + u32 scl_low = 1300 + 300; /* low period + fall time, ns */ + unsigned long ref; + int ret; + + ref = clk_get_rate(priv->clkin) / 2; + + scl_high = div64_u64((u64)scl_high * ref, 1000000000); + scl_low = div64_u64((u64)scl_low * ref, 1000000000); + + ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high); + if (ret) + return ret; + + ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low); + if (ret) + return ret; + + return 0; +} + +static int ub913_add_i2c_adapter(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *i2c_handle; + int ret; + + i2c_handle = device_get_named_child_node(dev, "i2c"); + if (!i2c_handle) + return 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, + dev, i2c_handle); + + fwnode_handle_put(i2c_handle); + + if (ret) + return ret; + + return 0; +} + +static int ub913_parse_dt(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_PARALLEL, + }; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB913_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "No sink endpoint\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret) + return dev_err_probe(dev, ret, + "failed to parse sink endpoint data\n"); + + if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + priv->pclk_polarity_rising = true; + else if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + priv->pclk_polarity_rising = false; + else + return dev_err_probe(dev, -EINVAL, + "bad value for 'pclk-sample'\n"); + + return 0; +} + +static int ub913_hw_init(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + bool mode_override; + u8 mode; + int ret; + u8 v; + + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v); + if (ret) + return ret; + + if (!(v & UB913_REG_MODE_SEL_MODE_UP_TO_DATE)) + return dev_err_probe(dev, -ENODEV, + "Mode value not stabilized\n"); + + mode_override = v & UB913_REG_MODE_SEL_MODE_OVERRIDE; + mode = v & UB913_REG_MODE_SEL_MODE_MASK; + + dev_dbg(dev, "mode from %s: %#x\n", + mode_override ? "reg" : "deserializer", mode); + + ret = ub913_i2c_master_init(priv); + if (ret) + return dev_err_probe(dev, ret, "i2c master init failed\n"); + + ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING; + v |= priv->pclk_polarity_rising ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0; + ub913_write(priv, UB913_REG_GENERAL_CFG, v); + + return 0; +} + +static int ub913_subdev_init(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub913_entity_ops; + + priv->pads[0].flags = MEDIA_PAD_FL_SINK; + priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub913_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub913_v4l2_nf_unregister(priv); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void ub913_subdev_uninit(struct ub913_data *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + ub913_v4l2_nf_unregister(priv); + v4l2_subdev_cleanup(&priv->sd); + fwnode_handle_put(priv->sd.fwnode); + media_entity_cleanup(&priv->sd.entity); +} + +static int ub913_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub913_data *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->plat_data = dev_get_platdata(&client->dev); + if (!priv->plat_data) + return dev_err_probe(dev, -ENODEV, "Platform data missing\n"); + + priv->regmap = devm_regmap_init_i2c(client, &ub913_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Failed to init regmap\n"); + + /* + * ub913 can also work without ext clock, but that is not supported by + * the driver yet. + */ + priv->clkin = devm_clk_get(dev, "clkin"); + if (IS_ERR(priv->clkin)) + return dev_err_probe(dev, PTR_ERR(priv->clkin), + "Cannot get CLKIN\n"); + + ret = ub913_parse_dt(priv); + if (ret) + return ret; + + ret = ub913_hw_init(priv); + if (ret) + return ret; + + ret = ub913_gpiochip_probe(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to init gpiochip\n"); + + ret = ub913_register_clkout(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clkout\n"); + goto err_gpiochip_remove; + } + + ret = ub913_subdev_init(priv); + if (ret) + goto err_gpiochip_remove; + + ret = ub913_add_i2c_adapter(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + goto err_subdev_uninit; + } + + return 0; + +err_subdev_uninit: + ub913_subdev_uninit(priv); +err_gpiochip_remove: + ub913_gpiochip_remove(priv); + + return ret; +} + +static void ub913_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub913_data *priv = sd_to_ub913(sd); + + i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port); + + ub913_subdev_uninit(priv); + + ub913_gpiochip_remove(priv); +} + +static const struct i2c_device_id ub913_id[] = { { "ds90ub913a-q1", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, ub913_id); + +static const struct of_device_id ub913_dt_ids[] = { + { .compatible = "ti,ds90ub913a-q1" }, + {} +}; +MODULE_DEVICE_TABLE(of, ub913_dt_ids); + +static struct i2c_driver ds90ub913_driver = { + .probe = ub913_probe, + .remove = ub913_remove, + .id_table = ub913_id, + .driver = { + .name = "ds90ub913a", + .of_match_table = ub913_dt_ids, + }, +}; +module_i2c_driver(ds90ub913_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments DS90UB913 FPD-Link III Serializer Driver"); +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c new file mode 100644 index 000000000000..dc394e22a42c --- /dev/null +++ b/drivers/media/i2c/ds90ub953.c @@ -0,0 +1,1430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB953 video serializer + * + * Based on a driver from Luca Ceresoli + * + * Copyright (c) 2019 Luca Ceresoli + * Copyright (c) 2023 Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define UB953_PAD_SINK 0 +#define UB953_PAD_SOURCE 1 + +#define UB953_NUM_GPIOS 4 + +#define UB953_DEFAULT_CLKOUT_RATE 25000000UL + +#define UB953_REG_RESET_CTL 0x01 +#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB953_REG_GENERAL_CFG 0x02 +#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) +#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) +#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) + +#define UB953_REG_MODE_SEL 0x03 +#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) +#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) +#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) + +#define UB953_REG_CLKOUT_CTRL0 0x06 +#define UB953_REG_CLKOUT_CTRL1 0x07 + +#define UB953_REG_SCL_HIGH_TIME 0x0b +#define UB953_REG_SCL_LOW_TIME 0x0c + +#define UB953_REG_LOCAL_GPIO_DATA 0x0d +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) + +#define UB953_REG_GPIO_INPUT_CTRL 0x0e +#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) +#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) + +#define UB953_REG_REV_MASK_ID 0x50 +#define UB953_REG_GENERAL_STATUS 0x52 + +#define UB953_REG_GPIO_PIN_STS 0x53 +#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) + +#define UB953_REG_BIST_ERR_CNT 0x54 +#define UB953_REG_CRC_ERR_CNT1 0x55 +#define UB953_REG_CRC_ERR_CNT2 0x56 + +#define UB953_REG_CSI_ERR_CNT 0x5c +#define UB953_REG_CSI_ERR_STATUS 0x5d +#define UB953_REG_CSI_ERR_DLANE01 0x5e +#define UB953_REG_CSI_ERR_DLANE23 0x5f +#define UB953_REG_CSI_ERR_CLK_LANE 0x60 +#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 +#define UB953_REG_PKT_HDR_WC_LSB 0x62 +#define UB953_REG_PKT_HDR_WC_MSB 0x63 +#define UB953_REG_CSI_ECC 0x64 + +#define UB953_REG_IND_ACC_CTL 0xb0 +#define UB953_REG_IND_ACC_ADDR 0xb1 +#define UB953_REG_IND_ACC_DATA 0xb2 + +#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB953_REG_FPD3_RX_ID_LEN 6 + +/* Indirect register blocks */ +#define UB953_IND_TARGET_PAT_GEN 0x00 +#define UB953_IND_TARGET_FPD3_TX 0x01 +#define UB953_IND_TARGET_DIE_ID 0x02 + +#define UB953_IND_PGEN_CTL 0x01 +#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) +#define UB953_IND_PGEN_CFG 0x02 +#define UB953_IND_PGEN_CSI_DI 0x03 +#define UB953_IND_PGEN_LINE_SIZE1 0x04 +#define UB953_IND_PGEN_LINE_SIZE0 0x05 +#define UB953_IND_PGEN_BAR_SIZE1 0x06 +#define UB953_IND_PGEN_BAR_SIZE0 0x07 +#define UB953_IND_PGEN_ACT_LPF1 0x08 +#define UB953_IND_PGEN_ACT_LPF0 0x09 +#define UB953_IND_PGEN_TOT_LPF1 0x0a +#define UB953_IND_PGEN_TOT_LPF0 0x0b +#define UB953_IND_PGEN_LINE_PD1 0x0c +#define UB953_IND_PGEN_LINE_PD0 0x0d +#define UB953_IND_PGEN_VBP 0x0e +#define UB953_IND_PGEN_VFP 0x0f +#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ + +/* Note: Only sync mode supported for now */ +enum ub953_mode { + /* FPD-Link III CSI-2 synchronous mode */ + UB953_MODE_SYNC, + /* FPD-Link III CSI-2 non-synchronous mode, external ref clock */ + UB953_MODE_NONSYNC_EXT, + /* FPD-Link III CSI-2 non-synchronous mode, internal ref clock */ + UB953_MODE_NONSYNC_INT, + /* FPD-Link III DVP mode */ + UB953_MODE_DVP, +}; + +struct ub953_hw_data { + const char *model; + bool is_ub971; +}; + +struct ub953_clkout_data { + u32 hs_div; + u32 m; + u32 n; + unsigned long rate; +}; + +struct ub953_data { + const struct ub953_hw_data *hw_data; + + struct i2c_client *client; + struct regmap *regmap; + struct clk *clkin; + + u32 num_data_lanes; + bool non_continous_clk; + + struct gpio_chip gpio_chip; + + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + + u64 enabled_source_streams; + + /* lock for register access */ + struct mutex reg_lock; + + u8 current_indirect_target; + + struct clk_hw clkout_clk_hw; + + enum ub953_mode mode; + + const struct ds90ub9xx_platform_data *plat_data; +}; + +static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub953_data, sd); +} + +/* + * HW Access + */ + +static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(&priv->client->dev, "Cannot read register 0x%02x: %d\n", + reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(&priv->client->dev, + "Cannot write register 0x%02x: %d\n", reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub953_select_ind_reg_block(struct ub953_data *priv, u8 block) +{ + struct device *dev = &priv->client->dev; + int ret; + + if (priv->current_indirect_target == block) + return 0; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_CTL, block << 2); + if (ret) { + dev_err(dev, "%s: cannot select indirect target %u (%d)\n", + __func__, block, ret); + return ret; + } + + priv->current_indirect_target = block; + + return 0; +} + +__maybe_unused +static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub953_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_read(priv->regmap, UB953_REG_IND_ACC_DATA, &v); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +__maybe_unused +static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub953_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_DATA failed when writing %u:%x02x\n: %d\n", + block, reg, ret); + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +/* + * GPIO chip + */ +static int ub953_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v); + if (ret) + return ret; + + if (v & UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)) + return GPIO_LINE_DIRECTION_IN; + else + return GPIO_LINE_DIRECTION_OUT; +} + +static int ub953_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + return regmap_update_bits(priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)); +} + +static int ub953_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + + ret = regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : + 0); + + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset)); +} + +static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v); + if (ret) + return ret; + + return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset)); +} + +static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : + 0); +} + +static int ub953_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0]; +} + +static int ub953_gpiochip_probe(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int ret; + + /* Set all GPIOs to local input mode */ + ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); + ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->can_sleep = true; + gc->ngpio = UB953_NUM_GPIOS; + gc->get_direction = ub953_gpio_get_direction; + gc->direction_input = ub953_gpio_direction_in; + gc->direction_output = ub953_gpio_direction_out; + gc->get = ub953_gpio_get; + gc->set = ub953_gpio_set; + gc->of_xlate = ub953_gpio_of_xlate; + gc->of_gpio_n_cells = 2; + + ret = gpiochip_add_data(gc, priv); + if (ret) { + dev_err(dev, "Failed to add GPIOs: %d\n", ret); + return ret; + } + + return 0; +} + +static void ub953_gpiochip_remove(struct ub953_data *priv) +{ + gpiochip_remove(&priv->gpio_chip); +} + +/* + * V4L2 + */ + +static int _ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub953_data *priv = sd_to_ub953(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _ub953_set_routing(sd, state, routing); +} + +static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct v4l2_mbus_frame_desc source_fd; + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret; + + if (pad != UB953_PAD_SOURCE) + return -EINVAL; + + ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, + priv->source_sd_pad, &source_fd); + if (ret) + return ret; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + unsigned int i; + + if (route->source_pad != pad) + continue; + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) { + source_entry = &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_err(&priv->client->dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_entry->flags; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; + fd->entry[fd->num_entries].bus.csi2.vc = + source_entry->bus.csi2.vc; + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub953_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == UB953_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + /* Set sink format */ + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int ub953_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UB953_PAD_SINK, + .sink_stream = 0, + .source_pad = UB953_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub953_set_routing(sd, state, &routing); +} + +static int ub953_log_status(struct v4l2_subdev *sd) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct device *dev = &priv->client->dev; + u8 v = 0, v1 = 0, v2 = 0; + unsigned int i; + char id[UB953_REG_FPD3_RX_ID_LEN]; + u8 gpio_local_data = 0; + u8 gpio_input_ctrl = 0; + u8 gpio_pin_sts = 0; + + for (i = 0; i < sizeof(id); i++) + ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i]); + + dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); + + ub953_read(priv, UB953_REG_GENERAL_STATUS, &v); + dev_info(dev, "GENERAL_STATUS %#02x\n", v); + + ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1); + ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2); + dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); + + ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v); + dev_info(dev, "CSI error count %u\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v); + dev_info(dev, "CSI_ERR_STATUS %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v); + dev_info(dev, "CSI_ERR_DLANE01 %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v); + dev_info(dev, "CSI_ERR_DLANE23 %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v); + dev_info(dev, "CSI_ERR_CLK_LANE %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v); + dev_info(dev, "CSI packet header VC %u ID %u\n", v >> 6, v & 0x3f); + + ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1); + ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2); + dev_info(dev, "CSI packet header WC %u\n", (v2 << 8) | v1); + + ub953_read(priv, UB953_REG_CSI_ECC, &v); + dev_info(dev, "CSI ECC %#02x\n", v); + + ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data); + ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl); + ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts); + + for (i = 0; i < UB953_NUM_GPIOS; i++) { + dev_info(dev, + "GPIO%u: remote: %u is_input: %u is_output: %u val: %u sts: %u\n", + i, + !!(gpio_local_data & UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(i)), + !!(gpio_input_ctrl & UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(i)), + !!(gpio_input_ctrl & UB953_REG_GPIO_INPUT_CTRL_OUT_EN(i)), + !!(gpio_local_data & UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(i)), + !!(gpio_pin_sts & UB953_REG_GPIO_PIN_STS_GPIO_STS(i))); + } + + return 0; +} + +static int ub953_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub953_data *priv = sd_to_ub953(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE, + UB953_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams |= streams_mask; + + return 0; +} + +static int ub953_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub953_data *priv = sd_to_ub953(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE, + UB953_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams &= ~streams_mask; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ub953_pad_ops = { + .enable_streams = ub953_enable_streams, + .disable_streams = ub953_disable_streams, + .set_routing = ub953_set_routing, + .get_frame_desc = ub953_get_frame_desc, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub953_set_fmt, + .init_cfg = ub953_init_cfg, +}; + +static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = { + .log_status = ub953_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ub953_subdev_ops = { + .core = &ub953_subdev_core_ops, + .pad = &ub953_pad_ops, +}; + +static const struct media_entity_operations ub953_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int ub953_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct ub953_data *priv = sd_to_ub953(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations ub953_notify_ops = { + .bound = ub953_notify_bound, +}; + +static int ub953_v4l2_notifier_register(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB953_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &ub953_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub953_v4l2_notifier_unregister(struct ub953_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +/* + * Probing + */ + +static int ub953_i2c_master_init(struct ub953_data *priv) +{ + /* i2c fast mode */ + u32 ref = 26250000; + u32 scl_high = 915; /* ns */ + u32 scl_low = 1641; /* ns */ + int ret; + + scl_high = div64_u64((u64)scl_high * ref, 1000000000) - 5; + scl_low = div64_u64((u64)scl_low * ref, 1000000000) - 5; + + ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low); + if (ret) + return ret; + + return 0; +} + +static u64 ub953_get_fc_rate(struct ub953_data *priv) +{ + switch (priv->mode) { + case UB953_MODE_SYNC: + if (priv->hw_data->is_ub971) + return priv->plat_data->bc_rate * 160ull; + else + return priv->plat_data->bc_rate / 2 * 160ull; + + case UB953_MODE_NONSYNC_EXT: + /* CLKIN_DIV = 1 always */ + return clk_get_rate(priv->clkin) * 80ull; + + default: + /* Not supported */ + return 0; + } +} + +static unsigned long ub953_calc_clkout_ub953(struct ub953_data *priv, + unsigned long target, u64 fc, + u8 *hs_div, u8 *m, u8 *n) +{ + /* + * We always use 4 as a pre-divider (HS_CLK_DIV = 2). + * + * According to the datasheet: + * - "HS_CLK_DIV typically should be set to either 16, 8, or 4 (default)." + * - "if it is not possible to have an integer ratio of N/M, it is best to + * select a smaller value for HS_CLK_DIV. + * + * For above reasons the default HS_CLK_DIV seems the best in the average + * case. Use always that value to keep the code simple. + */ + static const unsigned long hs_clk_div = 4; + + u64 fc_divided; + unsigned long mul, div; + unsigned long res; + + /* clkout = fc / hs_clk_div * m / n */ + + fc_divided = div_u64(fc, hs_clk_div); + + rational_best_approximation(target, fc_divided, (1 << 5) - 1, + (1 << 8) - 1, &mul, &div); + + res = div_u64(fc_divided * mul, div); + + *hs_div = hs_clk_div; + *m = mul; + *n = div; + + return res; +} + +static unsigned long ub953_calc_clkout_ub971(struct ub953_data *priv, + unsigned long target, u64 fc, + u8 *m, u8 *n) +{ + u64 fc_divided; + unsigned long mul, div; + unsigned long res; + + /* clkout = fc * m / (8 * n) */ + + fc_divided = div_u64(fc, 8); + + rational_best_approximation(target, fc_divided, (1 << 5) - 1, + (1 << 8) - 1, &mul, &div); + + res = div_u64(fc_divided * mul, div); + + *m = mul; + *n = div; + + return res; +} + +static void ub953_calc_clkout_params(struct ub953_data *priv, + unsigned long target_rate, + struct ub953_clkout_data *clkout_data) +{ + struct device *dev = &priv->client->dev; + unsigned long clkout_rate; + u64 fc_rate; + + fc_rate = ub953_get_fc_rate(priv); + + if (priv->hw_data->is_ub971) { + u8 m, n; + + clkout_rate = ub953_calc_clkout_ub971(priv, target_rate, + fc_rate, &m, &n); + + clkout_data->m = m; + clkout_data->n = n; + + dev_dbg(dev, "%s %llu * %u / (8 * %u) = %lu (requested %lu)", + __func__, fc_rate, m, n, clkout_rate, target_rate); + } else { + u8 hs_div, m, n; + + clkout_rate = ub953_calc_clkout_ub953(priv, target_rate, + fc_rate, &hs_div, &m, &n); + + clkout_data->hs_div = hs_div; + clkout_data->m = m; + clkout_data->n = n; + + dev_dbg(dev, "%s %llu / %u * %u / %u = %lu (requested %lu)", + __func__, fc_rate, hs_div, m, n, clkout_rate, + target_rate); + } + + clkout_data->rate = clkout_rate; +} + +static void ub953_write_clkout_regs(struct ub953_data *priv, + const struct ub953_clkout_data *clkout_data) +{ + u8 clkout_ctrl0, clkout_ctrl1; + + if (priv->hw_data->is_ub971) + clkout_ctrl0 = clkout_data->m; + else + clkout_ctrl0 = (__ffs(clkout_data->hs_div) << 5) | + clkout_data->m; + + clkout_ctrl1 = clkout_data->n; + + ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); + ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); +} + +static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct device *dev = &priv->client->dev; + u8 ctrl0, ctrl1; + u32 mul, div; + u64 fc_rate; + u32 hs_clk_div; + u64 rate; + int ret; + + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0); + if (ret) { + dev_err(dev, "Failed to read CLKOUT_CTRL0: %d\n", ret); + return 0; + } + + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1); + if (ret) { + dev_err(dev, "Failed to read CLKOUT_CTRL1: %d\n", ret); + return 0; + } + + fc_rate = ub953_get_fc_rate(priv); + + if (priv->hw_data->is_ub971) { + mul = ctrl0 & 0x1f; + div = ctrl1; + + if (div == 0) + return 0; + + rate = div_u64(fc_rate * mul, 8 * div); + + dev_dbg(dev, "clkout: fc rate %llu, mul %u, div %u = %llu\n", + fc_rate, mul, div, rate); + } else { + mul = ctrl0 & 0x1f; + hs_clk_div = 1 << (ctrl0 >> 5); + div = ctrl1; + + if (div == 0) + return 0; + + rate = div_u64(div_u64(fc_rate, hs_clk_div) * mul, div); + + dev_dbg(dev, + "clkout: fc rate %llu, hs_clk_div %u, mul %u, div %u = %llu\n", + fc_rate, hs_clk_div, mul, div, rate); + } + + return rate; +} + +static long ub953_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct ub953_clkout_data clkout_data; + + ub953_calc_clkout_params(priv, rate, &clkout_data); + + return clkout_data.rate; +} + +static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct ub953_clkout_data clkout_data; + + ub953_calc_clkout_params(priv, rate, &clkout_data); + + dev_dbg(&priv->client->dev, "%s %lu (requested %lu)\n", __func__, + clkout_data.rate, rate); + + ub953_write_clkout_regs(priv, &clkout_data); + + return 0; +} + +static const struct clk_ops ub953_clkout_ops = { + .recalc_rate = ub953_clkout_recalc_rate, + .round_rate = ub953_clkout_round_rate, + .set_rate = ub953_clkout_set_rate, +}; + +static int ub953_register_clkout(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + const struct clk_init_data init = { + .name = kasprintf(GFP_KERNEL, "ds90%s.%s.clk_out", + priv->hw_data->model, dev_name(dev)), + .ops = &ub953_clkout_ops, + }; + struct ub953_clkout_data clkout_data; + int ret; + + if (!init.name) + return -ENOMEM; + + /* Initialize clkout to 25MHz by default */ + ub953_calc_clkout_params(priv, UB953_DEFAULT_CLKOUT_RATE, &clkout_data); + ub953_write_clkout_regs(priv, &clkout_data); + + priv->clkout_clk_hw.init = &init; + + ret = devm_clk_hw_register(dev, &priv->clkout_clk_hw); + kfree(init.name); + if (ret) + return dev_err_probe(dev, ret, "Cannot register clock HW\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->clkout_clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; +} + +static int ub953_add_i2c_adapter(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *i2c_handle; + int ret; + + i2c_handle = device_get_named_child_node(dev, "i2c"); + if (!i2c_handle) + return 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, + dev, i2c_handle); + + fwnode_handle_put(i2c_handle); + + if (ret) + return ret; + + return 0; +} + +static const struct regmap_config ub953_regmap_config = { + .name = "ds90ub953", + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_DEFAULT, + .val_format_endian = REGMAP_ENDIAN_DEFAULT, +}; + +static int ub953_parse_dt(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep_fwnode; + unsigned char nlanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB953_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret) + return dev_err_probe(dev, ret, + "failed to parse sink endpoint data\n"); + + nlanes = vep.bus.mipi_csi2.num_data_lanes; + if (nlanes != 1 && nlanes != 2 && nlanes != 4) + return dev_err_probe(dev, -EINVAL, + "bad number of data-lanes: %u\n", nlanes); + + priv->num_data_lanes = nlanes; + + priv->non_continous_clk = vep.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + return 0; +} + +static int ub953_hw_init(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + bool mode_override; + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_MODE_SEL, &v); + if (ret) + return ret; + + if (!(v & UB953_REG_MODE_SEL_MODE_DONE)) + return dev_err_probe(dev, -EIO, "Mode value not stabilized\n"); + + mode_override = v & UB953_REG_MODE_SEL_MODE_OVERRIDE; + + switch (v & UB953_REG_MODE_SEL_MODE_MASK) { + case 0: + priv->mode = UB953_MODE_SYNC; + break; + case 2: + priv->mode = UB953_MODE_NONSYNC_EXT; + break; + case 3: + priv->mode = UB953_MODE_NONSYNC_INT; + break; + case 5: + priv->mode = UB953_MODE_DVP; + break; + default: + return dev_err_probe(dev, -EIO, + "Invalid mode in mode register\n"); + } + + dev_dbg(dev, "mode from %s: %#x\n", mode_override ? "reg" : "strap", + priv->mode); + + if (priv->mode != UB953_MODE_SYNC && + priv->mode != UB953_MODE_NONSYNC_EXT) + return dev_err_probe(dev, -ENODEV, + "Unsupported mode selected: %u\n", + priv->mode); + + if (priv->mode == UB953_MODE_NONSYNC_EXT && !priv->clkin) + return dev_err_probe(dev, -EINVAL, + "clkin required for non-sync ext mode\n"); + + ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v); + if (ret) + return dev_err_probe(dev, ret, "Failed to read revision"); + + dev_info(dev, "Found %s rev/mask %#04x\n", priv->hw_data->model, v); + + ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v); + if (ret) + return ret; + + dev_dbg(dev, "i2c strap setting %s V\n", + (v & UB953_REG_GENERAL_CFG_I2C_STRAP_MODE) ? "1.8" : "3.3"); + + ret = ub953_i2c_master_init(priv); + if (ret) + return dev_err_probe(dev, ret, "i2c init failed\n"); + + ub953_write(priv, UB953_REG_GENERAL_CFG, + (priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK) | + ((priv->num_data_lanes - 1) << UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT) | + UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE); + + return 0; +} + +static int ub953_subdev_init(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub953_entity_ops; + + priv->pads[0].flags = MEDIA_PAD_FL_SINK; + priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub953_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub953_v4l2_notifier_unregister(priv); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void ub953_subdev_uninit(struct ub953_data *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + ub953_v4l2_notifier_unregister(priv); + v4l2_subdev_cleanup(&priv->sd); + fwnode_handle_put(priv->sd.fwnode); + media_entity_cleanup(&priv->sd.entity); +} + +static int ub953_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub953_data *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->hw_data = device_get_match_data(dev); + + priv->plat_data = dev_get_platdata(&client->dev); + if (!priv->plat_data) + return dev_err_probe(dev, -ENODEV, "Platform data missing\n"); + + mutex_init(&priv->reg_lock); + + /* + * Initialize to invalid values so that the first reg writes will + * configure the target. + */ + priv->current_indirect_target = 0xff; + + priv->regmap = devm_regmap_init_i2c(client, &ub953_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err_probe(dev, ret, "Failed to init regmap\n"); + goto err_mutex_destroy; + } + + priv->clkin = devm_clk_get_optional(dev, "clkin"); + if (IS_ERR(priv->clkin)) { + ret = PTR_ERR(priv->clkin); + dev_err_probe(dev, ret, "failed to parse 'clkin'\n"); + goto err_mutex_destroy; + } + + ret = ub953_parse_dt(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub953_hw_init(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub953_gpiochip_probe(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to init gpiochip\n"); + goto err_mutex_destroy; + } + + ret = ub953_register_clkout(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clkout\n"); + goto err_gpiochip_remove; + } + + ret = ub953_subdev_init(priv); + if (ret) + goto err_gpiochip_remove; + + ret = ub953_add_i2c_adapter(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + goto err_subdev_uninit; + } + + return 0; + +err_subdev_uninit: + ub953_subdev_uninit(priv); +err_gpiochip_remove: + ub953_gpiochip_remove(priv); +err_mutex_destroy: + mutex_destroy(&priv->reg_lock); + + return ret; +} + +static void ub953_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub953_data *priv = sd_to_ub953(sd); + + i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port); + + ub953_subdev_uninit(priv); + + ub953_gpiochip_remove(priv); + mutex_destroy(&priv->reg_lock); +} + +static const struct ub953_hw_data ds90ub953_hw = { + .model = "ub953", +}; + +static const struct ub953_hw_data ds90ub971_hw = { + .model = "ub971", + .is_ub971 = true, +}; + +static const struct i2c_device_id ub953_id[] = { + { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw }, + { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ub953_id); + +static const struct of_device_id ub953_dt_ids[] = { + { .compatible = "ti,ds90ub953-q1", .data = &ds90ub953_hw }, + { .compatible = "ti,ds90ub971-q1", .data = &ds90ub971_hw }, + {} +}; +MODULE_DEVICE_TABLE(of, ub953_dt_ids); + +static struct i2c_driver ds90ub953_driver = { + .probe = ub953_probe, + .remove = ub953_remove, + .id_table = ub953_id, + .driver = { + .name = "ds90ub953", + .of_match_table = ub953_dt_ids, + }, +}; +module_i2c_driver(ds90ub953_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV CSI-2 Serializers Driver"); +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c new file mode 100644 index 000000000000..8ba5750f5a23 --- /dev/null +++ b/drivers/media/i2c/ds90ub960.c @@ -0,0 +1,4059 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB960-Q1 video deserializer + * + * Copyright (c) 2019 Luca Ceresoli + * Copyright (c) 2023 Tomi Valkeinen + */ + +/* + * (Possible) TODOs: + * + * - PM for serializer and remote peripherals. We need to manage: + * - VPOC + * - Power domain? Regulator? Somehow any remote device should be able to + * cause the VPOC to be turned on. + * - Link between the deserializer and the serializer + * - Related to VPOC management. We probably always want to turn on the VPOC + * and then enable the link. + * - Serializer's services: i2c, gpios, power + * - The serializer needs to resume before the remote peripherals can + * e.g. use the i2c. + * - How to handle gpios? Reserving a gpio essentially keeps the provider + * (serializer) always powered on. + * - Do we need a new bus for the FPD-Link? At the moment the serializers + * are children of the same i2c-adapter where the deserializer resides. + * - i2c-atr could be made embeddable instead of allocatable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MHZ(v) ((u32)((v) * 1000000U)) + +#define UB960_POLL_TIME_MS 500 + +#define UB960_MAX_RX_NPORTS 4 +#define UB960_MAX_TX_NPORTS 2 +#define UB960_MAX_NPORTS (UB960_MAX_RX_NPORTS + UB960_MAX_TX_NPORTS) + +#define UB960_MAX_PORT_ALIASES 8 + +#define UB960_NUM_BC_GPIOS 4 + +/* + * Register map + * + * 0x00-0x32 Shared (UB960_SR) + * 0x33-0x3a CSI-2 TX (per-port paged on DS90UB960, shared on 954) (UB960_TR) + * 0x4c Shared (UB960_SR) + * 0x4d-0x7f FPD-Link RX, per-port paged (UB960_RR) + * 0xb0-0xbf Shared (UB960_SR) + * 0xd0-0xdf FPD-Link RX, per-port paged (UB960_RR) + * 0xf0-0xf5 Shared (UB960_SR) + * 0xf8-0xfb Shared (UB960_SR) + * All others Reserved + * + * Register prefixes: + * UB960_SR_* = Shared register + * UB960_RR_* = FPD-Link RX, per-port paged register + * UB960_TR_* = CSI-2 TX, per-port paged register + * UB960_XR_* = Reserved register + * UB960_IR_* = Indirect register + */ + +#define UB960_SR_I2C_DEV_ID 0x00 +#define UB960_SR_RESET 0x01 +#define UB960_SR_RESET_DIGITAL_RESET1 BIT(1) +#define UB960_SR_RESET_DIGITAL_RESET0 BIT(0) +#define UB960_SR_RESET_GPIO_LOCK_RELEASE BIT(5) + +#define UB960_SR_GEN_CONFIG 0x02 +#define UB960_SR_REV_MASK 0x03 +#define UB960_SR_DEVICE_STS 0x04 +#define UB960_SR_PAR_ERR_THOLD_HI 0x05 +#define UB960_SR_PAR_ERR_THOLD_LO 0x06 +#define UB960_SR_BCC_WDOG_CTL 0x07 +#define UB960_SR_I2C_CTL1 0x08 +#define UB960_SR_I2C_CTL2 0x09 +#define UB960_SR_SCL_HIGH_TIME 0x0a +#define UB960_SR_SCL_LOW_TIME 0x0b +#define UB960_SR_RX_PORT_CTL 0x0c +#define UB960_SR_IO_CTL 0x0d +#define UB960_SR_GPIO_PIN_STS 0x0e +#define UB960_SR_GPIO_INPUT_CTL 0x0f +#define UB960_SR_GPIO_PIN_CTL(n) (0x10 + (n)) /* n < UB960_NUM_GPIOS */ +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SEL 5 +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SRC_SHIFT 2 +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_EN BIT(0) + +#define UB960_SR_FS_CTL 0x18 +#define UB960_SR_FS_HIGH_TIME_1 0x19 +#define UB960_SR_FS_HIGH_TIME_0 0x1a +#define UB960_SR_FS_LOW_TIME_1 0x1b +#define UB960_SR_FS_LOW_TIME_0 0x1c +#define UB960_SR_MAX_FRM_HI 0x1d +#define UB960_SR_MAX_FRM_LO 0x1e +#define UB960_SR_CSI_PLL_CTL 0x1f + +#define UB960_SR_FWD_CTL1 0x20 +#define UB960_SR_FWD_CTL1_PORT_DIS(n) BIT((n) + 4) + +#define UB960_SR_FWD_CTL2 0x21 +#define UB960_SR_FWD_STS 0x22 + +#define UB960_SR_INTERRUPT_CTL 0x23 +#define UB960_SR_INTERRUPT_CTL_INT_EN BIT(7) +#define UB960_SR_INTERRUPT_CTL_IE_CSI_TX0 BIT(4) +#define UB960_SR_INTERRUPT_CTL_IE_RX(n) BIT((n)) /* rxport[n] IRQ */ + +#define UB960_SR_INTERRUPT_STS 0x24 +#define UB960_SR_INTERRUPT_STS_INT BIT(7) +#define UB960_SR_INTERRUPT_STS_IS_CSI_TX(n) BIT(4 + (n)) /* txport[n] IRQ */ +#define UB960_SR_INTERRUPT_STS_IS_RX(n) BIT((n)) /* rxport[n] IRQ */ + +#define UB960_SR_TS_CONFIG 0x25 +#define UB960_SR_TS_CONTROL 0x26 +#define UB960_SR_TS_LINE_HI 0x27 +#define UB960_SR_TS_LINE_LO 0x28 +#define UB960_SR_TS_STATUS 0x29 +#define UB960_SR_TIMESTAMP_P0_HI 0x2a +#define UB960_SR_TIMESTAMP_P0_LO 0x2b +#define UB960_SR_TIMESTAMP_P1_HI 0x2c +#define UB960_SR_TIMESTAMP_P1_LO 0x2d + +#define UB960_SR_CSI_PORT_SEL 0x32 + +#define UB960_TR_CSI_CTL 0x33 +#define UB960_TR_CSI_CTL_CSI_CAL_EN BIT(6) +#define UB960_TR_CSI_CTL_CSI_CONTS_CLOCK BIT(1) +#define UB960_TR_CSI_CTL_CSI_ENABLE BIT(0) + +#define UB960_TR_CSI_CTL2 0x34 +#define UB960_TR_CSI_STS 0x35 +#define UB960_TR_CSI_TX_ICR 0x36 + +#define UB960_TR_CSI_TX_ISR 0x37 +#define UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR BIT(3) +#define UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR BIT(1) + +#define UB960_TR_CSI_TEST_CTL 0x38 +#define UB960_TR_CSI_TEST_PATT_HI 0x39 +#define UB960_TR_CSI_TEST_PATT_LO 0x3a + +#define UB960_XR_SFILTER_CFG 0x41 +#define UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT 4 +#define UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT 0 + +#define UB960_XR_AEQ_CTL1 0x42 +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_FPD_CLK BIT(6) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_ENCODING BIT(5) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_PARITY BIT(4) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK \ + (UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_FPD_CLK | \ + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_ENCODING | \ + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_PARITY) +#define UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN BIT(0) + +#define UB960_XR_AEQ_ERR_THOLD 0x43 + +#define UB960_RR_BCC_ERR_CTL 0x46 +#define UB960_RR_BCC_STATUS 0x47 +#define UB960_RR_BCC_STATUS_SEQ_ERROR BIT(5) +#define UB960_RR_BCC_STATUS_MASTER_ERR BIT(4) +#define UB960_RR_BCC_STATUS_MASTER_TO BIT(3) +#define UB960_RR_BCC_STATUS_SLAVE_ERR BIT(2) +#define UB960_RR_BCC_STATUS_SLAVE_TO BIT(1) +#define UB960_RR_BCC_STATUS_RESP_ERR BIT(0) +#define UB960_RR_BCC_STATUS_ERROR_MASK \ + (UB960_RR_BCC_STATUS_SEQ_ERROR | UB960_RR_BCC_STATUS_MASTER_ERR | \ + UB960_RR_BCC_STATUS_MASTER_TO | UB960_RR_BCC_STATUS_SLAVE_ERR | \ + UB960_RR_BCC_STATUS_SLAVE_TO | UB960_RR_BCC_STATUS_RESP_ERR) + +#define UB960_RR_FPD3_CAP 0x4a +#define UB960_RR_RAW_EMBED_DTYPE 0x4b +#define UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT 6 + +#define UB960_SR_FPD3_PORT_SEL 0x4c + +#define UB960_RR_RX_PORT_STS1 0x4d +#define UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS1_LOCK_STS_CHG BIT(4) +#define UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS1_PARITY_ERROR BIT(2) +#define UB960_RR_RX_PORT_STS1_PORT_PASS BIT(1) +#define UB960_RR_RX_PORT_STS1_LOCK_STS BIT(0) +#define UB960_RR_RX_PORT_STS1_ERROR_MASK \ + (UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR | \ + UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR | \ + UB960_RR_RX_PORT_STS1_PARITY_ERROR) + +#define UB960_RR_RX_PORT_STS2 0x4e +#define UB960_RR_RX_PORT_STS2_LINE_LEN_UNSTABLE BIT(7) +#define UB960_RR_RX_PORT_STS2_LINE_LEN_CHG BIT(6) +#define UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS2_BUFFER_ERROR BIT(4) +#define UB960_RR_RX_PORT_STS2_CSI_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS2_FREQ_STABLE BIT(2) +#define UB960_RR_RX_PORT_STS2_CABLE_FAULT BIT(1) +#define UB960_RR_RX_PORT_STS2_LINE_CNT_CHG BIT(0) +#define UB960_RR_RX_PORT_STS2_ERROR_MASK \ + UB960_RR_RX_PORT_STS2_BUFFER_ERROR + +#define UB960_RR_RX_FREQ_HIGH 0x4f +#define UB960_RR_RX_FREQ_LOW 0x50 +#define UB960_RR_SENSOR_STS_0 0x51 +#define UB960_RR_SENSOR_STS_1 0x52 +#define UB960_RR_SENSOR_STS_2 0x53 +#define UB960_RR_SENSOR_STS_3 0x54 +#define UB960_RR_RX_PAR_ERR_HI 0x55 +#define UB960_RR_RX_PAR_ERR_LO 0x56 +#define UB960_RR_BIST_ERR_COUNT 0x57 + +#define UB960_RR_BCC_CONFIG 0x58 +#define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) +#define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0) + +#define UB960_RR_DATAPATH_CTL1 0x59 +#define UB960_RR_DATAPATH_CTL2 0x5a +#define UB960_RR_SER_ID 0x5b +#define UB960_RR_SER_ALIAS_ID 0x5c + +/* For these two register sets: n < UB960_MAX_PORT_ALIASES */ +#define UB960_RR_SLAVE_ID(n) (0x5d + (n)) +#define UB960_RR_SLAVE_ALIAS(n) (0x65 + (n)) + +#define UB960_RR_PORT_CONFIG 0x6d +#define UB960_RR_PORT_CONFIG_FPD3_MODE_MASK GENMASK(1, 0) + +#define UB960_RR_BC_GPIO_CTL(n) (0x6e + (n)) /* n < 2 */ +#define UB960_RR_RAW10_ID 0x70 +#define UB960_RR_RAW10_ID_VC_SHIFT 6 +#define UB960_RR_RAW10_ID_DT_SHIFT 0 + +#define UB960_RR_RAW12_ID 0x71 +#define UB960_RR_CSI_VC_MAP 0x72 +#define UB960_RR_CSI_VC_MAP_SHIFT(x) ((x) * 2) + +#define UB960_RR_LINE_COUNT_HI 0x73 +#define UB960_RR_LINE_COUNT_LO 0x74 +#define UB960_RR_LINE_LEN_1 0x75 +#define UB960_RR_LINE_LEN_0 0x76 +#define UB960_RR_FREQ_DET_CTL 0x77 +#define UB960_RR_MAILBOX_1 0x78 +#define UB960_RR_MAILBOX_2 0x79 + +#define UB960_RR_CSI_RX_STS 0x7a +#define UB960_RR_CSI_RX_STS_LENGTH_ERR BIT(3) +#define UB960_RR_CSI_RX_STS_CKSUM_ERR BIT(2) +#define UB960_RR_CSI_RX_STS_ECC2_ERR BIT(1) +#define UB960_RR_CSI_RX_STS_ECC1_ERR BIT(0) +#define UB960_RR_CSI_RX_STS_ERROR_MASK \ + (UB960_RR_CSI_RX_STS_LENGTH_ERR | UB960_RR_CSI_RX_STS_CKSUM_ERR | \ + UB960_RR_CSI_RX_STS_ECC2_ERR | UB960_RR_CSI_RX_STS_ECC1_ERR) + +#define UB960_RR_CSI_ERR_COUNTER 0x7b +#define UB960_RR_PORT_CONFIG2 0x7c +#define UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK GENMASK(7, 6) +#define UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT 6 + +#define UB960_RR_PORT_CONFIG2_LV_POL_LOW BIT(1) +#define UB960_RR_PORT_CONFIG2_FV_POL_LOW BIT(0) + +#define UB960_RR_PORT_PASS_CTL 0x7d +#define UB960_RR_SEN_INT_RISE_CTL 0x7e +#define UB960_RR_SEN_INT_FALL_CTL 0x7f + +#define UB960_SR_CSI_FRAME_COUNT_HI(n) (0x90 + 8 * (n)) +#define UB960_SR_CSI_FRAME_COUNT_LO(n) (0x91 + 8 * (n)) +#define UB960_SR_CSI_FRAME_ERR_COUNT_HI(n) (0x92 + 8 * (n)) +#define UB960_SR_CSI_FRAME_ERR_COUNT_LO(n) (0x93 + 8 * (n)) +#define UB960_SR_CSI_LINE_COUNT_HI(n) (0x94 + 8 * (n)) +#define UB960_SR_CSI_LINE_COUNT_LO(n) (0x95 + 8 * (n)) +#define UB960_SR_CSI_LINE_ERR_COUNT_HI(n) (0x96 + 8 * (n)) +#define UB960_SR_CSI_LINE_ERR_COUNT_LO(n) (0x97 + 8 * (n)) + +#define UB960_XR_REFCLK_FREQ 0xa5 /* UB960 */ + +#define UB960_RR_VC_ID_MAP(x) (0xa0 + (x)) /* UB9702 */ + +#define UB960_SR_IND_ACC_CTL 0xb0 +#define UB960_SR_IND_ACC_CTL_IA_AUTO_INC BIT(1) + +#define UB960_SR_IND_ACC_ADDR 0xb1 +#define UB960_SR_IND_ACC_DATA 0xb2 +#define UB960_SR_BIST_CONTROL 0xb3 +#define UB960_SR_MODE_IDX_STS 0xb8 +#define UB960_SR_LINK_ERROR_COUNT 0xb9 +#define UB960_SR_FPD3_ENC_CTL 0xba +#define UB960_SR_FV_MIN_TIME 0xbc +#define UB960_SR_GPIO_PD_CTL 0xbe + +#define UB960_SR_FPD_RATE_CFG 0xc2 /* UB9702 */ +#define UB960_SR_CSI_PLL_DIV 0xc9 /* UB9702 */ + +#define UB960_RR_PORT_DEBUG 0xd0 +#define UB960_RR_AEQ_CTL2 0xd2 +#define UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR BIT(2) + +#define UB960_RR_AEQ_STATUS 0xd3 +#define UB960_RR_AEQ_STATUS_STATUS_2 GENMASK(5, 3) +#define UB960_RR_AEQ_STATUS_STATUS_1 GENMASK(2, 0) + +#define UB960_RR_AEQ_BYPASS 0xd4 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT 5 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK GENMASK(7, 5) +#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT 1 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK GENMASK(3, 1) +#define UB960_RR_AEQ_BYPASS_ENABLE BIT(0) + +#define UB960_RR_AEQ_MIN_MAX 0xd5 +#define UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT 4 +#define UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT 0 + +#define UB960_RR_SFILTER_STS_0 0xd6 +#define UB960_RR_SFILTER_STS_1 0xd7 +#define UB960_RR_PORT_ICR_HI 0xd8 +#define UB960_RR_PORT_ICR_LO 0xd9 +#define UB960_RR_PORT_ISR_HI 0xda +#define UB960_RR_PORT_ISR_LO 0xdb +#define UB960_RR_FC_GPIO_STS 0xdc +#define UB960_RR_FC_GPIO_ICR 0xdd +#define UB960_RR_SEN_INT_RISE_STS 0xde +#define UB960_RR_SEN_INT_FALL_STS 0xdf + +#define UB960_RR_CHANNEL_MODE 0xe4 /* UB9702 */ + +#define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB960_SR_FPD3_RX_ID_LEN 6 + +#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* < UB960_FPD_RX_NPORTS */ + +/* Indirect register blocks */ +#define UB960_IND_TARGET_PAT_GEN 0x00 +#define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) +#define UB960_IND_TARGET_CSI_CSIPLL_REG_1 0x92 /* UB9702 */ +#define UB960_IND_TARGET_CSI_ANA 0x07 + +/* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ + +#define UB960_IR_PGEN_CTL 0x01 +#define UB960_IR_PGEN_CTL_PGEN_ENABLE BIT(0) + +#define UB960_IR_PGEN_CFG 0x02 +#define UB960_IR_PGEN_CSI_DI 0x03 +#define UB960_IR_PGEN_LINE_SIZE1 0x04 +#define UB960_IR_PGEN_LINE_SIZE0 0x05 +#define UB960_IR_PGEN_BAR_SIZE1 0x06 +#define UB960_IR_PGEN_BAR_SIZE0 0x07 +#define UB960_IR_PGEN_ACT_LPF1 0x08 +#define UB960_IR_PGEN_ACT_LPF0 0x09 +#define UB960_IR_PGEN_TOT_LPF1 0x0a +#define UB960_IR_PGEN_TOT_LPF0 0x0b +#define UB960_IR_PGEN_LINE_PD1 0x0c +#define UB960_IR_PGEN_LINE_PD0 0x0d +#define UB960_IR_PGEN_VBP 0x0e +#define UB960_IR_PGEN_VFP 0x0f +#define UB960_IR_PGEN_COLOR(n) (0x10 + (n)) /* n < 15 */ + +#define UB960_IR_RX_ANA_STROBE_SET_CLK 0x08 +#define UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY BIT(3) +#define UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK GENMASK(2, 0) + +#define UB960_IR_RX_ANA_STROBE_SET_DATA 0x09 +#define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3) +#define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0) + +/* EQ related */ + +#define UB960_MIN_AEQ_STROBE_POS -7 +#define UB960_MAX_AEQ_STROBE_POS 7 + +#define UB960_MANUAL_STROBE_EXTRA_DELAY 6 + +#define UB960_MIN_MANUAL_STROBE_POS -(7 + UB960_MANUAL_STROBE_EXTRA_DELAY) +#define UB960_MAX_MANUAL_STROBE_POS (7 + UB960_MANUAL_STROBE_EXTRA_DELAY) +#define UB960_NUM_MANUAL_STROBE_POS (UB960_MAX_MANUAL_STROBE_POS - UB960_MIN_MANUAL_STROBE_POS + 1) + +#define UB960_MIN_EQ_LEVEL 0 +#define UB960_MAX_EQ_LEVEL 14 +#define UB960_NUM_EQ_LEVELS (UB960_MAX_EQ_LEVEL - UB960_MIN_EQ_LEVEL + 1) + +struct ub960_hw_data { + const char *model; + u8 num_rxports; + u8 num_txports; + bool is_ub9702; + bool is_fpdlink4; +}; + +enum ub960_rxport_mode { + RXPORT_MODE_RAW10 = 0, + RXPORT_MODE_RAW12_HF = 1, + RXPORT_MODE_RAW12_LF = 2, + RXPORT_MODE_CSI2_SYNC = 3, + RXPORT_MODE_CSI2_NONSYNC = 4, + RXPORT_MODE_LAST = RXPORT_MODE_CSI2_NONSYNC, +}; + +enum ub960_rxport_cdr { + RXPORT_CDR_FPD3 = 0, + RXPORT_CDR_FPD4 = 1, + RXPORT_CDR_LAST = RXPORT_CDR_FPD4, +}; + +struct ub960_rxport { + struct ub960_data *priv; + u8 nport; /* RX port number, and index in priv->rxport[] */ + + struct { + struct v4l2_subdev *sd; + u16 pad; + struct fwnode_handle *ep_fwnode; + } source; + + /* Serializer */ + struct { + struct fwnode_handle *fwnode; + struct i2c_client *client; + unsigned short alias; /* I2C alias (lower 7 bits) */ + struct ds90ub9xx_platform_data pdata; + } ser; + + enum ub960_rxport_mode rx_mode; + enum ub960_rxport_cdr cdr_mode; + + u8 lv_fv_pol; /* LV and FV polarities */ + + struct regulator *vpoc; + + /* EQ settings */ + struct { + bool manual_eq; + + s8 strobe_pos; + + union { + struct { + u8 eq_level_min; + u8 eq_level_max; + } aeq; + + struct { + u8 eq_level; + } manual; + }; + } eq; + + const struct i2c_client *aliased_clients[UB960_MAX_PORT_ALIASES]; +}; + +struct ub960_asd { + struct v4l2_async_connection base; + struct ub960_rxport *rxport; +}; + +static inline struct ub960_asd *to_ub960_asd(struct v4l2_async_connection *asd) +{ + return container_of(asd, struct ub960_asd, base); +} + +struct ub960_txport { + struct ub960_data *priv; + u8 nport; /* TX port number, and index in priv->txport[] */ + + u32 num_data_lanes; + bool non_continous_clk; +}; + +struct ub960_data { + const struct ub960_hw_data *hw_data; + struct i2c_client *client; /* for shared local registers */ + struct regmap *regmap; + + /* lock for register access */ + struct mutex reg_lock; + + struct clk *refclk; + + struct regulator *vddio; + + struct gpio_desc *pd_gpio; + struct delayed_work poll_work; + struct ub960_rxport *rxports[UB960_MAX_RX_NPORTS]; + struct ub960_txport *txports[UB960_MAX_TX_NPORTS]; + + struct v4l2_subdev sd; + struct media_pad pads[UB960_MAX_NPORTS]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + + u32 tx_data_rate; /* Nominal data rate (Gb/s) */ + s64 tx_link_freq[1]; + + struct i2c_atr *atr; + + struct { + u8 rxport; + u8 txport; + u8 indirect_target; + } reg_current; + + bool streaming; + + u8 stored_fwd_ctl; + + u64 stream_enable_mask[UB960_MAX_NPORTS]; + + /* These are common to all ports */ + struct { + bool manual; + + s8 min; + s8 max; + } strobe; +}; + +static inline struct ub960_data *sd_to_ub960(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub960_data, sd); +} + +static inline bool ub960_pad_is_sink(struct ub960_data *priv, u32 pad) +{ + return pad < priv->hw_data->num_rxports; +} + +static inline bool ub960_pad_is_source(struct ub960_data *priv, u32 pad) +{ + return pad >= priv->hw_data->num_rxports; +} + +static inline unsigned int ub960_pad_to_port(struct ub960_data *priv, u32 pad) +{ + if (ub960_pad_is_sink(priv, pad)) + return pad; + else + return pad - priv->hw_data->num_rxports; +} + +struct ub960_format_info { + u32 code; + u32 bpp; + u8 datatype; + bool meta; +}; + +static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, +}; + +static const struct ub960_format_info *ub960_find_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ub960_formats); i++) { + if (ub960_formats[i].code == code) + return &ub960_formats[i]; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Basic device access + */ + +static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) +{ + struct device *dev = &priv->client->dev; + __be16 __v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = be16_to_cpu(__v); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.rxport == nport) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_FPD3_PORT_SEL, + (nport << 4) | BIT(nport)); + if (ret) { + dev_err(dev, "%s: cannot select rxport %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->reg_current.rxport = nport; + + return 0; +} + +static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, + u16 *val) +{ + struct device *dev = &priv->client->dev; + __be16 __v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = be16_to_cpu(__v); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.txport == nport) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_CSI_PORT_SEL, + (nport << 4) | BIT(nport)); + if (ret) { + dev_err(dev, "%s: cannot select tx port %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->reg_current.txport = nport; + + return 0; +} + +static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_select_ind_reg_block(struct ub960_data *priv, u8 block) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.indirect_target == block) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_CTL, block << 2); + if (ret) { + dev_err(dev, "%s: cannot select indirect target %u (%d)!\n", + __func__, block, ret); + return ret; + } + + priv->reg_current.indirect_target = block; + + return 0; +} + +static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_read(priv->regmap, UB960_SR_IND_ACC_DATA, &v); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_DATA, val); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when updating %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_update_bits(priv->regmap, UB960_SR_IND_ACC_DATA, mask, + val); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when updating %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C-ATR (address translator) + */ + +static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client, u16 alias) +{ + struct ub960_data *priv = i2c_atr_get_driver_data(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { + if (!rxport->aliased_clients[reg_idx]) + break; + } + + if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + dev_err(dev, "rx%u: alias pool exhausted\n", rxport->nport); + return -EADDRNOTAVAIL; + } + + rxport->aliased_clients[reg_idx] = client; + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), + client->addr << 1); + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), + alias << 1); + + dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", + rxport->nport, client->addr, alias, reg_idx); + + return 0; +} + +static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client) +{ + struct ub960_data *priv = i2c_atr_get_driver_data(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { + if (rxport->aliased_clients[reg_idx] == client) + break; + } + + if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + dev_err(dev, "rx%u: client 0x%02x is not mapped!\n", + rxport->nport, client->addr); + return; + } + + rxport->aliased_clients[reg_idx] = NULL; + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); + + dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, + client->addr, reg_idx); +} + +static const struct i2c_atr_ops ub960_atr_ops = { + .attach_client = ub960_atr_attach_client, + .detach_client = ub960_atr_detach_client, +}; + +static int ub960_init_atr(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + struct i2c_adapter *parent_adap = priv->client->adapter; + + priv->atr = i2c_atr_new(parent_adap, dev, &ub960_atr_ops, + priv->hw_data->num_rxports); + if (IS_ERR(priv->atr)) + return PTR_ERR(priv->atr); + + i2c_atr_set_driver_data(priv->atr, priv); + + return 0; +} + +static void ub960_uninit_atr(struct ub960_data *priv) +{ + i2c_atr_delete(priv->atr); + priv->atr = NULL; +} + +/* ----------------------------------------------------------------------------- + * TX ports + */ + +static int ub960_parse_dt_txport(struct ub960_data *priv, + struct fwnode_handle *ep_fwnode, + u8 nport) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = {}; + struct ub960_txport *txport; + int ret; + + txport = kzalloc(sizeof(*txport), GFP_KERNEL); + if (!txport) + return -ENOMEM; + + txport->priv = priv; + txport->nport = nport; + + vep.bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &vep); + if (ret) { + dev_err(dev, "tx%u: failed to parse endpoint data\n", nport); + goto err_free_txport; + } + + txport->non_continous_clk = vep.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + txport->num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + + if (vep.nr_of_link_frequencies != 1) { + ret = -EINVAL; + goto err_free_vep; + } + + priv->tx_link_freq[0] = vep.link_frequencies[0]; + priv->tx_data_rate = priv->tx_link_freq[0] * 2; + + if (priv->tx_data_rate != MHZ(1600) && + priv->tx_data_rate != MHZ(1200) && + priv->tx_data_rate != MHZ(800) && + priv->tx_data_rate != MHZ(400)) { + dev_err(dev, "tx%u: invalid 'link-frequencies' value\n", nport); + ret = -EINVAL; + goto err_free_vep; + } + + v4l2_fwnode_endpoint_free(&vep); + + priv->txports[nport] = txport; + + return 0; + +err_free_vep: + v4l2_fwnode_endpoint_free(&vep); +err_free_txport: + kfree(txport); + + return ret; +} + +static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 csi_tx_isr; + int ret; + + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr); + if (ret) + return; + + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR) + dev_warn(dev, "TX%u: CSI_SYNC_ERROR\n", nport); + + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR) + dev_warn(dev, "TX%u: CSI_PASS_ERROR\n", nport); +} + +/* ----------------------------------------------------------------------------- + * RX ports + */ + +static int ub960_rxport_enable_vpocs(struct ub960_data *priv) +{ + unsigned int nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + ret = regulator_enable(rxport->vpoc); + if (ret) + goto err_disable_vpocs; + } + + return 0; + +err_disable_vpocs: + while (nport--) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + regulator_disable(rxport->vpoc); + } + + return ret; +} + +static void ub960_rxport_disable_vpocs(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + regulator_disable(rxport->vpoc); + } +} + +static void ub960_rxport_clear_errors(struct ub960_data *priv, + unsigned int nport) +{ + u8 v; + + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v); + + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v); + + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); +} + +static void ub960_clear_rx_errors(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) + ub960_rxport_clear_errors(priv, nport); +} + +static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 *strobe_pos) +{ + u8 v; + u8 clk_delay, data_delay; + int ret; + + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, &v); + + clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, &v); + + data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v); + if (ret) + return ret; + + clk_delay += v & UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK; + + ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v); + if (ret) + return ret; + + data_delay += v & UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK; + + *strobe_pos = data_delay - clk_delay; + + return 0; +} + +static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 strobe_pos) +{ + u8 clk_delay, data_delay; + + clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + if (strobe_pos < UB960_MIN_AEQ_STROBE_POS) + clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS) + data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos < 0) + clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + else if (strobe_pos > 0) + data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay); +} + +static void ub960_rxport_set_strobe_range(struct ub960_data *priv, + s8 strobe_min, s8 strobe_max) +{ + /* Convert the signed strobe pos to positive zero based value */ + strobe_min -= UB960_MIN_AEQ_STROBE_POS; + strobe_max -= UB960_MIN_AEQ_STROBE_POS; + + ub960_write(priv, UB960_XR_SFILTER_CFG, + ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | + ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT)); +} + +static int ub960_rxport_get_eq_level(struct ub960_data *priv, + unsigned int nport, u8 *eq_level) +{ + int ret; + u8 v; + + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v); + if (ret) + return ret; + + *eq_level = (v & UB960_RR_AEQ_STATUS_STATUS_1) + + (v & UB960_RR_AEQ_STATUS_STATUS_2); + + return 0; +} + +static void ub960_rxport_set_eq_level(struct ub960_data *priv, + unsigned int nport, u8 eq_level) +{ + u8 eq_stage_1_select_value, eq_stage_2_select_value; + const unsigned int eq_stage_max = 7; + u8 v; + + if (eq_level <= eq_stage_max) { + eq_stage_1_select_value = eq_level; + eq_stage_2_select_value = 0; + } else { + eq_stage_1_select_value = eq_stage_max; + eq_stage_2_select_value = eq_level - eq_stage_max; + } + + ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + + v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK | + UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK); + v |= eq_stage_1_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT; + v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT; + v |= UB960_RR_AEQ_BYPASS_ENABLE; + + ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v); +} + +static void ub960_rxport_set_eq_range(struct ub960_data *priv, + unsigned int nport, u8 eq_min, u8 eq_max) +{ + ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX, + (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) | + (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT)); + + /* Enable AEQ min setting */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR); +} + +static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + /* We also set common settings here. Should be moved elsewhere. */ + + if (priv->strobe.manual) { + /* Disable AEQ_SFILTER_EN */ + ub960_update_bits(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0); + } else { + /* Enable SFILTER and error control */ + ub960_write(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN); + + /* Set AEQ strobe range */ + ub960_rxport_set_strobe_range(priv, priv->strobe.min, + priv->strobe.max); + } + + /* The rest are port specific */ + + if (priv->strobe.manual) + ub960_rxport_set_strobe_pos(priv, nport, rxport->eq.strobe_pos); + else + ub960_rxport_set_strobe_pos(priv, nport, 0); + + if (rxport->eq.manual_eq) { + ub960_rxport_set_eq_level(priv, nport, + rxport->eq.manual.eq_level); + + /* Enable AEQ Bypass */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, + UB960_RR_AEQ_BYPASS_ENABLE); + } else { + ub960_rxport_set_eq_range(priv, nport, + rxport->eq.aeq.eq_level_min, + rxport->eq.aeq.eq_level_max); + + /* Disable AEQ Bypass */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, 0); + } +} + +static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, + bool *ok) +{ + u8 rx_port_sts1, rx_port_sts2; + u16 parity_errors; + u8 csi_rx_sts; + u8 csi_err_cnt; + u8 bcc_sts; + int ret; + bool errors; + + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (ret) + return ret; + + if (!(rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS)) { + *ok = false; + return 0; + } + + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, + &rx_port_sts2); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, + &csi_err_cnt); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts); + if (ret) + return ret; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &parity_errors); + if (ret) + return ret; + + errors = (rx_port_sts1 & UB960_RR_RX_PORT_STS1_ERROR_MASK) || + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_ERROR_MASK) || + (bcc_sts & UB960_RR_BCC_STATUS_ERROR_MASK) || + (csi_rx_sts & UB960_RR_CSI_RX_STS_ERROR_MASK) || csi_err_cnt || + parity_errors; + + *ok = !errors; + + return 0; +} + +/* + * Wait for the RX ports to lock, have no errors and have stable strobe position + * and EQ level. + */ +static int ub960_rxport_wait_locks(struct ub960_data *priv, + unsigned long port_mask, + unsigned int *lock_mask) +{ + struct device *dev = &priv->client->dev; + unsigned long timeout; + unsigned int link_ok_mask; + unsigned int missing; + unsigned int loops; + u8 nport; + int ret; + + if (port_mask == 0) { + if (lock_mask) + *lock_mask = 0; + return 0; + } + + if (port_mask >= BIT(priv->hw_data->num_rxports)) + return -EINVAL; + + timeout = jiffies + msecs_to_jiffies(1000); + loops = 0; + link_ok_mask = 0; + + while (time_before(jiffies, timeout)) { + missing = 0; + + for_each_set_bit(nport, &port_mask, + priv->hw_data->num_rxports) { + struct ub960_rxport *rxport = priv->rxports[nport]; + bool ok; + + if (!rxport) + continue; + + ret = ub960_rxport_link_ok(priv, nport, &ok); + if (ret) + return ret; + + /* + * We want the link to be ok for two consecutive loops, + * as a link could get established just before our test + * and drop soon after. + */ + if (!ok || !(link_ok_mask & BIT(nport))) + missing++; + + if (ok) + link_ok_mask |= BIT(nport); + else + link_ok_mask &= ~BIT(nport); + } + + loops++; + + if (missing == 0) + break; + + msleep(50); + } + + if (lock_mask) + *lock_mask = link_ok_mask; + + dev_dbg(dev, "Wait locks done in %u loops\n", loops); + for_each_set_bit(nport, &port_mask, priv->hw_data->num_rxports) { + struct ub960_rxport *rxport = priv->rxports[nport]; + s8 strobe_pos, eq_level; + u16 v; + + if (!rxport) + continue; + + if (!(link_ok_mask & BIT(nport))) { + dev_dbg(dev, "\trx%u: not locked\n", nport); + continue; + } + + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); + + ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + if (ret) + return ret; + + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; + + dev_dbg(dev, "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", + nport, strobe_pos, eq_level, (v * 1000000ULL) >> 8); + } + + return 0; +} + +static unsigned long ub960_calc_bc_clk_rate_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int mult; + unsigned int div; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + mult = 1; + div = 10; + break; + + case RXPORT_MODE_CSI2_SYNC: + mult = 2; + div = 1; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + mult = 2; + div = 5; + break; + + default: + return 0; + } + + return clk_get_rate(priv->refclk) * mult / div; +} + +static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + return 2359400; + + case RXPORT_MODE_CSI2_SYNC: + return 47187500; + + case RXPORT_MODE_CSI2_NONSYNC: + return 9437500; + + default: + return 0; + } +} + +static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + struct device *dev = &priv->client->dev; + struct ds90ub9xx_platform_data *ser_pdata = &rxport->ser.pdata; + struct i2c_board_info ser_info = { + .of_node = to_of_node(rxport->ser.fwnode), + .fwnode = rxport->ser.fwnode, + .platform_data = ser_pdata, + }; + + ser_pdata->port = nport; + ser_pdata->atr = priv->atr; + if (priv->hw_data->is_ub9702) + ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub9702(priv, rxport); + else + ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub960(priv, rxport); + + /* + * The serializer is added under the same i2c adapter as the + * deserializer. This is not quite right, as the serializer is behind + * the FPD-Link. + */ + ser_info.addr = rxport->ser.alias; + rxport->ser.client = + i2c_new_client_device(priv->client->adapter, &ser_info); + if (IS_ERR(rxport->ser.client)) { + dev_err(dev, "rx%u: cannot add %s i2c device", nport, + ser_info.type); + return PTR_ERR(rxport->ser.client); + } + + dev_dbg(dev, "rx%u: remote serializer at alias 0x%02x (%u-%04x)\n", + nport, rxport->ser.client->addr, + rxport->ser.client->adapter->nr, rxport->ser.client->addr); + + return 0; +} + +static void ub960_rxport_remove_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + i2c_unregister_device(rxport->ser.client); + rxport->ser.client = NULL; +} + +/* Add serializer i2c devices for all initialized ports */ +static int ub960_rxport_add_serializers(struct ub960_data *priv) +{ + unsigned int nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ret = ub960_rxport_add_serializer(priv, nport); + if (ret) + goto err_remove_sers; + } + + return 0; + +err_remove_sers: + while (nport--) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ub960_rxport_remove_serializer(priv, nport); + } + + return ret; +} + +static void ub960_rxport_remove_serializers(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ub960_rxport_remove_serializer(priv, nport); + } +} + +static void ub960_init_tx_port(struct ub960_data *priv, + struct ub960_txport *txport) +{ + unsigned int nport = txport->nport; + u8 csi_ctl = 0; + + /* + * From the datasheet: "initial CSI Skew-Calibration + * sequence [...] should be set when operating at 1.6 Gbps" + */ + if (priv->tx_data_rate == MHZ(1600)) + csi_ctl |= UB960_TR_CSI_CTL_CSI_CAL_EN; + + csi_ctl |= (4 - txport->num_data_lanes) << 4; + + if (!txport->non_continous_clk) + csi_ctl |= UB960_TR_CSI_CTL_CSI_CONTS_CLOCK; + + ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl); +} + +static int ub960_init_tx_ports(struct ub960_data *priv) +{ + unsigned int nport; + u8 speed_select; + u8 pll_div; + + /* TX ports */ + + switch (priv->tx_data_rate) { + case MHZ(1600): + default: + speed_select = 0; + pll_div = 0x10; + break; + case MHZ(1200): + speed_select = 1; + pll_div = 0x18; + break; + case MHZ(800): + speed_select = 2; + pll_div = 0x10; + break; + case MHZ(400): + speed_select = 3; + pll_div = 0x10; + break; + } + + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select); + + if (priv->hw_data->is_ub9702) { + ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div); + + switch (priv->tx_data_rate) { + case MHZ(1600): + default: + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + break; + case MHZ(800): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + break; + case MHZ(400): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0); + break; + } + } + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + if (!txport) + continue; + + ub960_init_tx_port(priv, txport); + } + + return 0; +} + +static void ub960_init_rx_port_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u32 bc_freq_val; + + /* + * Back channel frequency select. + * Override FREQ_SELECT from the strap. + * 0 - 2.5 Mbps (DS90UB913A-Q1 / DS90UB933-Q1) + * 2 - 10 Mbps + * 6 - 50 Mbps (DS90UB953-Q1) + * + * Note that changing this setting will result in some errors on the back + * channel for a short period of time. + */ + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, + bc_freq_val); + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, + UB960_RR_PORT_CONFIG_FPD3_MODE_MASK, + 0x3); + + /* + * RAW10_8BIT_CTL = 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, + UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK, + 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + return; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + /* CSI-2 Mode (DS90UB953-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, + 0x0); + + break; + } + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + rxport->lv_fv_pol); + + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + + /* Enable I2C communication to the serializer via the alias addr */ + ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, + rxport->ser.alias << 1); + + /* Configure EQ related settings */ + ub960_rxport_config_eq(priv, nport); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); +} + +static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u8 bc_freq_val; + u8 fpd_func_mode; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + bc_freq_val = 0; + fpd_func_mode = 5; + break; + + case RXPORT_MODE_RAW12_HF: + bc_freq_val = 0; + fpd_func_mode = 4; + break; + + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + fpd_func_mode = 6; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + fpd_func_mode = 2; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + fpd_func_mode = 2; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, + bc_freq_val); + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode); + + /* set serdes_eq_mode = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80); + + /* enable serdes driver */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f); + + /* set serdes_eq_offset=4 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + + /* init default serdes_eq_max in 0xa9 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23); + + /* init serdes_eq_min in 0xaa */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0); + + /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, + BIT(3), BIT(3)); + + /* RX port to half-rate */ + ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + BIT(nport * 2)); +} + +static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + bool first_time_power_up = true; + + if (first_time_power_up) { + u8 v; + + /* AEQ init */ + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, v + 1); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x00); + } + + /* enable serdes_eq_ctl2 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00); + + /* enable serdes_eq_ctl1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40); + + /* enable serdes_eq_en */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40); + + /* disable serdes_eq_override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00); + + /* disable serdes_gain_override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00); +} + +static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u8 bc_freq_val; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + bc_freq_val = 0; + break; + + case RXPORT_MODE_RAW12_HF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, + bc_freq_val); + + /* FPD4 Sync Mode */ + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0); + + /* add serdes_eq_offset of 4 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + + /* FPD4 serdes_start_eq in 0x27: assign default */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0); + /* FPD4 serdes_end_eq in 0x28: assign default */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23); + + /* set serdes_driver_mode into FPD IV mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00); + /* set FPD PBC drv into FPD IV mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00); + + /* set serdes_system_init to 0x2f */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f); + /* set serdes_system_rst in reset mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1); + + /* RX port to 7.55G mode */ + ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + 0 << (nport * 2)); + + ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); +} + +static void ub960_init_rx_port_ub9702(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + + if (rxport->cdr_mode == RXPORT_CDR_FPD3) + ub960_init_rx_port_ub9702_fpd3(priv, rxport); + else /* RXPORT_CDR_FPD4 */ + ub960_init_rx_port_ub9702_fpd4(priv, rxport); + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + /* + * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits + * 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, + 0x3 << 6, 0x2 << 6); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + return; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + + break; + } + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + rxport->lv_fv_pol); + + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + + /* Enable I2C communication to the serializer via the alias addr */ + ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, + rxport->ser.alias << 1); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + + if (rxport->cdr_mode == RXPORT_CDR_FPD4) { + /* unreset 960 AEQ */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0x41); + } +} + +static int ub960_init_rx_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + if (priv->hw_data->is_ub9702) + ub960_init_rx_port_ub9702(priv, rxport); + else + ub960_init_rx_port_ub960(priv, rxport); + } + + return 0; +} + +static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 rx_port_sts1; + u8 rx_port_sts2; + u8 csi_rx_sts; + u8 bcc_sts; + int ret = 0; + + /* Read interrupts (also clears most of them) */ + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, + &rx_port_sts2); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, + &csi_rx_sts); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, + &bcc_sts); + + if (ret) + return; + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PARITY_ERROR) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &v); + if (!ret) + dev_err(dev, "rx%u parity errors: %u\n", nport, v); + } + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR) + dev_err(dev, "rx%u BCC CRC error\n", nport); + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR) + dev_err(dev, "rx%u BCC SEQ error\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_UNSTABLE) + dev_err(dev, "rx%u line length unstable\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR) + dev_err(dev, "rx%u FPD3 encode error\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_BUFFER_ERROR) + dev_err(dev, "rx%u buffer error\n", nport); + + if (csi_rx_sts) + dev_err(dev, "rx%u CSI error: %#02x\n", nport, csi_rx_sts); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_ECC1_ERR) + dev_err(dev, "rx%u CSI ECC1 error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_ECC2_ERR) + dev_err(dev, "rx%u CSI ECC2 error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_CKSUM_ERR) + dev_err(dev, "rx%u CSI checksum error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_LENGTH_ERR) + dev_err(dev, "rx%u CSI length error\n", nport); + + if (bcc_sts) + dev_err(dev, "rx%u BCC error: %#02x\n", nport, bcc_sts); + + if (bcc_sts & UB960_RR_BCC_STATUS_RESP_ERR) + dev_err(dev, "rx%u BCC response error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SLAVE_TO) + dev_err(dev, "rx%u BCC slave timeout", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SLAVE_ERR) + dev_err(dev, "rx%u BCC slave error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_MASTER_TO) + dev_err(dev, "rx%u BCC master timeout", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_MASTER_ERR) + dev_err(dev, "rx%u BCC master error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SEQ_ERROR) + dev_err(dev, "rx%u BCC sequence error", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_CHG) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v); + if (!ret) + dev_dbg(dev, "rx%u line len changed: %u\n", nport, v); + } + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_CNT_CHG) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, + &v); + if (!ret) + dev_dbg(dev, "rx%u line count changed: %u\n", nport, v); + } + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS_CHG) { + dev_dbg(dev, "rx%u: %s, %s, %s, %s\n", nport, + (rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS) ? + "locked" : + "unlocked", + (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PORT_PASS) ? + "passed" : + "not passed", + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_CABLE_FAULT) ? + "no clock" : + "clock ok", + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_FREQ_STABLE) ? + "stable freq" : + "unstable freq"); + } +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +/* + * The current implementation only supports a simple VC mapping, where all VCs + * from a one RX port will be mapped to the same VC. Also, the hardware + * dictates that all streams from an RX port must go to a single TX port. + * + * This function decides the target VC numbers for each RX port with a simple + * algorithm, so that for each TX port, we get VC numbers starting from 0, + * and counting up. + * + * E.g. if all four RX ports are in use, of which the first two go to the + * first TX port and the secont two go to the second TX port, we would get + * the following VCs for the four RX ports: 0, 1, 0, 1. + * + * TODO: implement a more sophisticated VC mapping. As the driver cannot know + * what VCs the sinks expect (say, an FPGA with hardcoded VC routing), this + * probably needs to be somehow configurable. Device tree? + */ +static void ub960_get_vc_maps(struct ub960_data *priv, + struct v4l2_subdev_state *state, u8 *vc) +{ + u8 cur_vc[UB960_MAX_TX_NPORTS] = {}; + struct v4l2_subdev_route *route; + u8 handled_mask = 0; + + for_each_active_route(&state->routing, route) { + unsigned int rx, tx; + + rx = ub960_pad_to_port(priv, route->sink_pad); + if (BIT(rx) & handled_mask) + continue; + + tx = ub960_pad_to_port(priv, route->source_pad); + + vc[rx] = cur_vc[tx]++; + handled_mask |= BIT(rx); + } +} + +static int ub960_enable_tx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "enable TX port %u\n", nport); + + return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, + UB960_TR_CSI_CTL_CSI_ENABLE); +} + +static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "disable TX port %u\n", nport); + + ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, 0); +} + +static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "enable RX port %u\n", nport); + + /* Enable forwarding */ + return ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), 0); +} + +static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "disable RX port %u\n", nport); + + /* Disable forwarding */ + ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), + UB960_SR_FWD_CTL1_PORT_DIS(nport)); +} + +/* + * The driver only supports using a single VC for each source. This function + * checks that each source only provides streams using a single VC. + */ +static int ub960_validate_stream_vcs(struct ub960_data *priv) +{ + unsigned int nport; + unsigned int i; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + struct v4l2_mbus_frame_desc desc; + int ret; + u8 vc; + + if (!rxport) + continue; + + ret = v4l2_subdev_call(rxport->source.sd, pad, get_frame_desc, + rxport->source.pad, &desc); + if (ret) + return ret; + + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + continue; + + if (desc.num_entries == 0) + continue; + + vc = desc.entry[0].bus.csi2.vc; + + for (i = 1; i < desc.num_entries; i++) { + if (vc == desc.entry[i].bus.csi2.vc) + continue; + + dev_err(&priv->client->dev, + "rx%u: source with multiple virtual-channels is not supported\n", + nport); + return -ENODEV; + } + } + + return 0; +} + +static int ub960_configure_ports_for_streaming(struct ub960_data *priv, + struct v4l2_subdev_state *state) +{ + u8 fwd_ctl; + struct { + u32 num_streams; + u8 pixel_dt; + u8 meta_dt; + u32 meta_lines; + u32 tx_port; + } rx_data[UB960_MAX_RX_NPORTS] = {}; + u8 vc_map[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int nport; + int ret; + + ret = ub960_validate_stream_vcs(priv); + if (ret) + return ret; + + ub960_get_vc_maps(priv, state, vc_map); + + for_each_active_route(&state->routing, route) { + struct ub960_rxport *rxport; + struct ub960_txport *txport; + struct v4l2_mbus_framefmt *fmt; + const struct ub960_format_info *ub960_fmt; + unsigned int nport; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + rxport = priv->rxports[nport]; + if (!rxport) + return -EINVAL; + + txport = priv->txports[ub960_pad_to_port(priv, route->source_pad)]; + if (!txport) + return -EINVAL; + + rx_data[nport].tx_port = ub960_pad_to_port(priv, route->source_pad); + + rx_data[nport].num_streams++; + + /* For the rest, we are only interested in parallel busses */ + if (rxport->rx_mode == RXPORT_MODE_CSI2_SYNC || + rxport->rx_mode == RXPORT_MODE_CSI2_NONSYNC) + continue; + + if (rx_data[nport].num_streams > 2) + return -EPIPE; + + fmt = v4l2_subdev_state_get_stream_format(state, + route->sink_pad, + route->sink_stream); + if (!fmt) + return -EPIPE; + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) + return -EPIPE; + + if (ub960_fmt->meta) { + if (fmt->height > 3) { + dev_err(&priv->client->dev, + "rx%u: unsupported metadata height %u\n", + nport, fmt->height); + return -EPIPE; + } + + rx_data[nport].meta_dt = ub960_fmt->datatype; + rx_data[nport].meta_lines = fmt->height; + } else { + rx_data[nport].pixel_dt = ub960_fmt->datatype; + } + } + + /* Configure RX ports */ + + /* + * Keep all port forwardings disabled by default. Forwarding will be + * enabled in ub960_enable_rx_port. + */ + fwd_ctl = GENMASK(7, 4); + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + u8 vc = vc_map[nport]; + + if (rx_data[nport].num_streams == 0) + continue; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, + rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT)); + + ub960_rxport_write(priv, rxport->nport, + UB960_RR_RAW_EMBED_DTYPE, + (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | + rx_data[nport].meta_dt); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + break; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + if (!priv->hw_data->is_ub9702) { + /* Map all VCs from this port to the same VC */ + ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP, + (vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(2)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(1)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(0))); + } else { + unsigned int i; + + /* Map all VCs from this port to VC(nport) */ + for (i = 0; i < 8; i++) + ub960_rxport_write(priv, nport, + UB960_RR_VC_ID_MAP(i), + nport); + } + + break; + } + + if (rx_data[nport].tx_port == 1) + fwd_ctl |= BIT(nport); /* forward to TX1 */ + else + fwd_ctl &= ~BIT(nport); /* forward to TX0 */ + } + + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl); + + return 0; +} + +static void ub960_update_streaming_status(struct ub960_data *priv) +{ + unsigned int i; + + for (i = 0; i < UB960_MAX_NPORTS; i++) { + if (priv->stream_enable_mask[i]) + break; + } + + priv->streaming = i < UB960_MAX_NPORTS; +} + +static int ub960_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 source_pad, + u64 source_streams_mask) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int failed_port; + unsigned int nport; + int ret; + + if (!priv->streaming) { + dev_dbg(dev, "Prepare for streaming\n"); + ret = ub960_configure_ports_for_streaming(priv, state); + if (ret) + return ret; + } + + /* Enable TX port if not yet enabled */ + if (!priv->stream_enable_mask[source_pad]) { + ret = ub960_enable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + if (ret) + return ret; + } + + priv->stream_enable_mask[source_pad] |= source_streams_mask; + + /* Collect sink streams per pad which we need to enable */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != source_pad) + continue; + + if (!(source_streams_mask & BIT_ULL(route->source_stream))) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + sink_streams[nport] |= BIT_ULL(route->sink_stream); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + if (!sink_streams[nport]) + continue; + + /* Enable the RX port if not yet enabled */ + if (!priv->stream_enable_mask[nport]) { + ret = ub960_enable_rx_port(priv, nport); + if (ret) { + failed_port = nport; + goto err; + } + } + + priv->stream_enable_mask[nport] |= sink_streams[nport]; + + dev_dbg(dev, "enable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_enable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) { + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + + failed_port = nport; + goto err; + } + } + + priv->streaming = true; + + return 0; + +err: + for (nport = 0; nport < failed_port; nport++) { + if (!sink_streams[nport]) + continue; + + dev_dbg(dev, "disable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_disable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) + dev_err(dev, "Failed to disable streams: %d\n", ret); + + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + /* Disable RX port if no active streams */ + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + } + + priv->stream_enable_mask[source_pad] &= ~source_streams_mask; + + if (!priv->stream_enable_mask[source_pad]) + ub960_disable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + + ub960_update_streaming_status(priv); + + return ret; +} + +static int ub960_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 source_streams_mask) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int nport; + int ret; + + /* Collect sink streams per pad which we need to disable */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != source_pad) + continue; + + if (!(source_streams_mask & BIT_ULL(route->source_stream))) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + sink_streams[nport] |= BIT_ULL(route->sink_stream); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + if (!sink_streams[nport]) + continue; + + dev_dbg(dev, "disable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_disable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) + dev_err(dev, "Failed to disable streams: %d\n", ret); + + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + /* Disable RX port if no active streams */ + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + } + + /* Disable TX port if no active streams */ + + priv->stream_enable_mask[source_pad] &= ~source_streams_mask; + + if (!priv->stream_enable_mask[source_pad]) + ub960_disable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + + ub960_update_streaming_status(priv); + + return 0; +} + +static int _ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -E2BIG; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + return _ub960_set_routing(sd, state, routing); +} + +static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret = 0; + struct device *dev = &priv->client->dev; + u8 vc_map[UB960_MAX_RX_NPORTS] = {}; + + if (!ub960_pad_is_source(priv, pad)) + return -EINVAL; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + state = v4l2_subdev_lock_and_get_active_state(&priv->sd); + + ub960_get_vc_maps(priv, state, vc_map); + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + struct v4l2_mbus_frame_desc source_fd; + unsigned int nport; + unsigned int i; + + if (route->source_pad != pad) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + ret = v4l2_subdev_call(priv->rxports[nport]->source.sd, pad, + get_frame_desc, + priv->rxports[nport]->source.pad, + &source_fd); + if (ret) { + dev_err(dev, + "Failed to get source frame desc for pad %u\n", + route->sink_pad); + goto out_unlock; + } + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) { + source_entry = &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_err(dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_entry->flags; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; + + fd->entry[fd->num_entries].bus.csi2.vc = vc_map[nport]; + + if (source_fd.type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + } else { + const struct ub960_format_info *ub960_fmt; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_stream_format(state, pad, + route->source_stream); + + if (!fmt) { + ret = -EINVAL; + goto out_unlock; + } + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) { + dev_err(dev, "Unable to find format\n"); + ret = -EINVAL; + goto out_unlock; + } + + fd->entry[fd->num_entries].bus.csi2.dt = + ub960_fmt->datatype; + } + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub960_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (ub960_pad_is_source(priv, format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* + * Default to the first format if the requested media bus code isn't + * supported. + */ + if (!ub960_find_format(format->format.code)) + format->format.code = ub960_formats[0].code; + + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int ub960_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + struct v4l2_subdev_route routes[] = { + { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = priv->hw_data->num_rxports, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub960_set_routing(sd, state, &routing); +} + +static const struct v4l2_subdev_pad_ops ub960_pad_ops = { + .enable_streams = ub960_enable_streams, + .disable_streams = ub960_disable_streams, + + .set_routing = ub960_set_routing, + .get_frame_desc = ub960_get_frame_desc, + + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub960_set_fmt, + + .init_cfg = ub960_init_cfg, +}; + +static int ub960_log_status(struct v4l2_subdev *sd) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + struct v4l2_subdev_state *state; + unsigned int nport; + unsigned int i; + u16 v16 = 0; + u8 v = 0; + u8 id[UB960_SR_FPD3_RX_ID_LEN]; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + for (i = 0; i < sizeof(id); i++) + ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); + + dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + dev_info(dev, "TX %u\n", nport); + + if (!txport) { + dev_info(dev, "\tNot initialized\n"); + continue; + } + + ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v); + dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), + v & (u8)BIT(0)); + + ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16); + dev_info(dev, "\tframe counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), &v16); + dev_info(dev, "\tframe error counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16); + dev_info(dev, "\tline counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), &v16); + dev_info(dev, "\tline error counter %u\n", v16); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + u8 eq_level; + s8 strobe_pos; + unsigned int i; + + dev_info(dev, "RX %u\n", nport); + + if (!rxport) { + dev_info(dev, "\tNot initialized\n"); + continue; + } + + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + + if (v & UB960_RR_RX_PORT_STS1_LOCK_STS) + dev_info(dev, "\tLocked\n"); + else + dev_info(dev, "\tNot locked\n"); + + dev_info(dev, "\trx_port_sts1 %#02x\n", v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); + dev_info(dev, "\trx_port_sts2 %#02x\n", v); + + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); + dev_info(dev, "\tlink freq %llu Hz\n", (v16 * 1000000ULL) >> 8); + + ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); + dev_info(dev, "\tparity errors %u\n", v16); + + ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16); + dev_info(dev, "\tlines per frame %u\n", v16); + + ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16); + dev_info(dev, "\tbytes per line %u\n", v16); + + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); + dev_info(dev, "\tcsi_err_counter %u\n", v); + + /* Strobe */ + + ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + + dev_info(dev, "\t%s strobe\n", + (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : + "Manual"); + + if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { + ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + + dev_info(dev, "\tStrobe range [%d, %d]\n", + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); + } + + ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + + dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + + /* EQ */ + + ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + + dev_info(dev, "\t%s EQ\n", + (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : + "Adaptive"); + + if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { + ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + + dev_info(dev, "\tEQ range [%u, %u]\n", + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + } + + if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) + dev_info(dev, "\tEQ level %u\n", eq_level); + + /* GPIOs */ + for (i = 0; i < UB960_NUM_BC_GPIOS; i++) { + u8 ctl_reg; + u8 ctl_shift; + + ctl_reg = UB960_RR_BC_GPIO_CTL(i / 2); + ctl_shift = (i % 2) * 4; + + ub960_rxport_read(priv, nport, ctl_reg, &v); + + dev_info(dev, "\tGPIO%u: mode %u\n", i, + (v >> ctl_shift) & 0xf); + } + } + + v4l2_subdev_unlock_state(state); + + return 0; +} + +static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { + .log_status = ub960_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ub960_subdev_ops = { + .core = &ub960_subdev_core_ops, + .pad = &ub960_pad_ops, +}; + +static const struct media_entity_operations ub960_entity_ops = { + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +/* ----------------------------------------------------------------------------- + * Core + */ + +static irqreturn_t ub960_handle_events(int irq, void *arg) +{ + struct ub960_data *priv = arg; + unsigned int i; + u8 int_sts; + u8 fwd_sts; + int ret; + + ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts); + if (ret || !int_sts) + return IRQ_NONE; + + dev_dbg(&priv->client->dev, "INTERRUPT_STS %x\n", int_sts); + + ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts); + if (ret) + return IRQ_NONE; + + dev_dbg(&priv->client->dev, "FWD_STS %#02x\n", fwd_sts); + + for (i = 0; i < priv->hw_data->num_txports; i++) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) + ub960_csi_handle_events(priv, i); + } + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + if (!priv->rxports[i]) + continue; + + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) + ub960_rxport_handle_events(priv, i); + } + + return IRQ_HANDLED; +} + +static void ub960_handler_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct ub960_data *priv = + container_of(dwork, struct ub960_data, poll_work); + + ub960_handle_events(0, priv); + + schedule_delayed_work(&priv->poll_work, + msecs_to_jiffies(UB960_POLL_TIME_MS)); +} + +static void ub960_txport_free_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + if (!txport) + continue; + + kfree(txport); + priv->txports[nport] = NULL; + } +} + +static void ub960_rxport_free_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + fwnode_handle_put(rxport->source.ep_fwnode); + fwnode_handle_put(rxport->ser.fwnode); + + kfree(rxport); + priv->rxports[nport] = NULL; + } +} + +static int +ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, + struct fwnode_handle *link_fwnode, + struct ub960_rxport *rxport) +{ + struct device *dev = &priv->client->dev; + unsigned int nport = rxport->nport; + u32 rx_mode; + u32 cdr_mode; + s32 strobe_pos; + u32 eq_level; + u32 ser_i2c_alias; + int ret; + + cdr_mode = RXPORT_CDR_FPD3; + + ret = fwnode_property_read_u32(link_fwnode, "ti,cdr-mode", &cdr_mode); + if (ret < 0 && ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,cdr-mode", ret); + return ret; + } + + if (cdr_mode > RXPORT_CDR_LAST) { + dev_err(dev, "rx%u: bad 'ti,cdr-mode' %u\n", nport, cdr_mode); + return -EINVAL; + } + + if (!priv->hw_data->is_fpdlink4 && cdr_mode == RXPORT_CDR_FPD4) { + dev_err(dev, "rx%u: FPD-Link 4 CDR not supported\n", nport); + return -EINVAL; + } + + rxport->cdr_mode = cdr_mode; + + ret = fwnode_property_read_u32(link_fwnode, "ti,rx-mode", &rx_mode); + if (ret < 0) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,rx-mode", ret); + return ret; + } + + if (rx_mode > RXPORT_MODE_LAST) { + dev_err(dev, "rx%u: bad 'ti,rx-mode' %u\n", nport, rx_mode); + return -EINVAL; + } + + switch (rx_mode) { + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + dev_err(dev, "rx%u: unsupported 'ti,rx-mode' %u\n", nport, + rx_mode); + return -EINVAL; + default: + break; + } + + rxport->rx_mode = rx_mode; + + /* EQ & Strobe related */ + + /* Defaults */ + rxport->eq.manual_eq = false; + rxport->eq.aeq.eq_level_min = UB960_MIN_EQ_LEVEL; + rxport->eq.aeq.eq_level_max = UB960_MAX_EQ_LEVEL; + + ret = fwnode_property_read_u32(link_fwnode, "ti,strobe-pos", + &strobe_pos); + if (ret) { + if (ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,strobe-pos", ret); + return ret; + } + } else { + if (strobe_pos < UB960_MIN_MANUAL_STROBE_POS || + strobe_pos > UB960_MAX_MANUAL_STROBE_POS) { + dev_err(dev, "rx%u: illegal 'strobe-pos' value: %d\n", + nport, strobe_pos); + return -EINVAL; + } + + /* NOTE: ignored unless global manual strobe pos is also set */ + rxport->eq.strobe_pos = strobe_pos; + if (!priv->strobe.manual) + dev_warn(dev, + "rx%u: 'ti,strobe-pos' ignored as 'ti,manual-strobe' not set\n", + nport); + } + + ret = fwnode_property_read_u32(link_fwnode, "ti,eq-level", &eq_level); + if (ret) { + if (ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,eq-level", ret); + return ret; + } + } else { + if (eq_level > UB960_MAX_EQ_LEVEL) { + dev_err(dev, "rx%u: illegal 'ti,eq-level' value: %d\n", + nport, eq_level); + return -EINVAL; + } + + rxport->eq.manual_eq = true; + rxport->eq.manual.eq_level = eq_level; + } + + ret = fwnode_property_read_u32(link_fwnode, "i2c-alias", + &ser_i2c_alias); + if (ret) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "i2c-alias", ret); + return ret; + } + rxport->ser.alias = ser_i2c_alias; + + rxport->ser.fwnode = fwnode_get_named_child_node(link_fwnode, "serializer"); + if (!rxport->ser.fwnode) { + dev_err(dev, "rx%u: missing 'serializer' node\n", nport); + return -EINVAL; + } + + return 0; +} + +static int ub960_parse_dt_rxport_ep_properties(struct ub960_data *priv, + struct fwnode_handle *ep_fwnode, + struct ub960_rxport *rxport) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = {}; + unsigned int nport = rxport->nport; + bool hsync_hi; + bool vsync_hi; + int ret; + + rxport->source.ep_fwnode = fwnode_graph_get_remote_endpoint(ep_fwnode); + if (!rxport->source.ep_fwnode) { + dev_err(dev, "rx%u: no remote endpoint\n", nport); + return -ENODEV; + } + + /* We currently have properties only for RAW modes */ + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + break; + default: + return 0; + } + + vep.bus_type = V4L2_MBUS_PARALLEL; + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + if (ret) { + dev_err(dev, "rx%u: failed to parse endpoint data\n", nport); + goto err_put_source_ep_fwnode; + } + + hsync_hi = !!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH); + vsync_hi = !!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH); + + /* LineValid and FrameValid are inverse to the h/vsync active */ + rxport->lv_fv_pol = (hsync_hi ? UB960_RR_PORT_CONFIG2_LV_POL_LOW : 0) | + (vsync_hi ? UB960_RR_PORT_CONFIG2_FV_POL_LOW : 0); + + return 0; + +err_put_source_ep_fwnode: + fwnode_handle_put(rxport->source.ep_fwnode); + return ret; +} + +static int ub960_parse_dt_rxport(struct ub960_data *priv, unsigned int nport, + struct fwnode_handle *link_fwnode, + struct fwnode_handle *ep_fwnode) +{ + static const char *vpoc_names[UB960_MAX_RX_NPORTS] = { + "vpoc0", "vpoc1", "vpoc2", "vpoc3" + }; + struct device *dev = &priv->client->dev; + struct ub960_rxport *rxport; + int ret; + + rxport = kzalloc(sizeof(*rxport), GFP_KERNEL); + if (!rxport) + return -ENOMEM; + + priv->rxports[nport] = rxport; + + rxport->nport = nport; + rxport->priv = priv; + + ret = ub960_parse_dt_rxport_link_properties(priv, link_fwnode, rxport); + if (ret) + goto err_free_rxport; + + rxport->vpoc = devm_regulator_get_optional(dev, vpoc_names[nport]); + if (IS_ERR(rxport->vpoc)) { + ret = PTR_ERR(rxport->vpoc); + if (ret == -ENODEV) { + rxport->vpoc = NULL; + } else { + dev_err(dev, "rx%u: failed to get VPOC supply: %d\n", + nport, ret); + goto err_put_remote_fwnode; + } + } + + ret = ub960_parse_dt_rxport_ep_properties(priv, ep_fwnode, rxport); + if (ret) + goto err_put_remote_fwnode; + + return 0; + +err_put_remote_fwnode: + fwnode_handle_put(rxport->ser.fwnode); +err_free_rxport: + priv->rxports[nport] = NULL; + kfree(rxport); + return ret; +} + +static struct fwnode_handle * +ub960_fwnode_get_link_by_regs(struct fwnode_handle *links_fwnode, + unsigned int nport) +{ + struct fwnode_handle *link_fwnode; + int ret; + + fwnode_for_each_child_node(links_fwnode, link_fwnode) { + u32 link_num; + + if (!str_has_prefix(fwnode_get_name(link_fwnode), "link@")) + continue; + + ret = fwnode_property_read_u32(link_fwnode, "reg", &link_num); + if (ret) { + fwnode_handle_put(link_fwnode); + return NULL; + } + + if (nport == link_num) + return link_fwnode; + } + + return NULL; +} + +static int ub960_parse_dt_rxports(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *links_fwnode; + unsigned int nport; + int ret; + + links_fwnode = fwnode_get_named_child_node(dev_fwnode(dev), "links"); + if (!links_fwnode) { + dev_err(dev, "'links' node missing\n"); + return -ENODEV; + } + + /* Defaults, recommended by TI */ + priv->strobe.min = 2; + priv->strobe.max = 3; + + priv->strobe.manual = fwnode_property_read_bool(links_fwnode, "ti,manual-strobe"); + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct fwnode_handle *link_fwnode; + struct fwnode_handle *ep_fwnode; + + link_fwnode = ub960_fwnode_get_link_by_regs(links_fwnode, nport); + if (!link_fwnode) + continue; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + nport, 0, 0); + if (!ep_fwnode) { + fwnode_handle_put(link_fwnode); + continue; + } + + ret = ub960_parse_dt_rxport(priv, nport, link_fwnode, + ep_fwnode); + + fwnode_handle_put(link_fwnode); + fwnode_handle_put(ep_fwnode); + + if (ret) { + dev_err(dev, "rx%u: failed to parse RX port\n", nport); + goto err_put_links; + } + } + + fwnode_handle_put(links_fwnode); + + return 0; + +err_put_links: + fwnode_handle_put(links_fwnode); + + return ret; +} + +static int ub960_parse_dt_txports(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + u32 nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + unsigned int port = nport + priv->hw_data->num_rxports; + struct fwnode_handle *ep_fwnode; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + port, 0, 0); + if (!ep_fwnode) + continue; + + ret = ub960_parse_dt_txport(priv, ep_fwnode, nport); + + fwnode_handle_put(ep_fwnode); + + if (ret) + break; + } + + return 0; +} + +static int ub960_parse_dt(struct ub960_data *priv) +{ + int ret; + + ret = ub960_parse_dt_rxports(priv); + if (ret) + return ret; + + ret = ub960_parse_dt_txports(priv); + if (ret) + goto err_free_rxports; + + return 0; + +err_free_rxports: + ub960_rxport_free_ports(priv); + + return ret; +} + +static int ub960_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct ub960_data *priv = sd_to_ub960(notifier->sd); + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + struct device *dev = &priv->client->dev; + u8 nport = rxport->nport; + unsigned int i; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + rxport->source.ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + rxport->source.sd = subdev; + rxport->source.pad = ret; + + ret = media_create_pad_link(&rxport->source.sd->entity, + rxport->source.pad, &priv->sd.entity, nport, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:%u\n", + rxport->source.sd->name, rxport->source.pad, + priv->sd.name, nport); + return ret; + } + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + if (priv->rxports[i] && !priv->rxports[i]->source.sd) { + dev_dbg(dev, "Waiting for more subdevs to be bound\n"); + return 0; + } + } + + return 0; +} + +static void ub960_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + + rxport->source.sd = NULL; +} + +static const struct v4l2_async_notifier_operations ub960_notify_ops = { + .bound = ub960_notify_bound, + .unbind = ub960_notify_unbind, +}; + +static int ub960_v4l2_notifier_register(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + struct ub960_rxport *rxport = priv->rxports[i]; + struct ub960_asd *asd; + + if (!rxport) + continue; + + asd = v4l2_async_nf_add_fwnode(&priv->notifier, + rxport->source.ep_fwnode, + struct ub960_asd); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev for source %u: %pe", + i, asd); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + asd->rxport = rxport; + } + + priv->notifier.ops = &ub960_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub960_v4l2_notifier_unregister(struct ub960_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +static int ub960_create_subdev(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(priv->tx_link_freq) - 1, 0, + priv->tx_link_freq); + + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub960_entity_ops; + + for (i = 0; i < priv->hw_data->num_rxports + priv->hw_data->num_txports; i++) { + priv->pads[i].flags = ub960_pad_is_sink(priv, i) ? + MEDIA_PAD_FL_SINK : + MEDIA_PAD_FL_SOURCE; + } + + ret = media_entity_pads_init(&priv->sd.entity, + priv->hw_data->num_rxports + + priv->hw_data->num_txports, + priv->pads); + if (ret) + goto err_free_ctrl; + + priv->sd.state_lock = priv->sd.ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub960_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub960_v4l2_notifier_unregister(priv); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +} + +static void ub960_destroy_subdev(struct ub960_data *priv) +{ + ub960_v4l2_notifier_unregister(priv); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static const struct regmap_config ub960_regmap_config = { + .name = "ds90ub960", + + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + + /* + * We do locking in the driver to cover the TX/RX port selection and the + * indirect register access. + */ + .disable_locking = true, +}; + +static void ub960_reset(struct ub960_data *priv, bool reset_regs) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + u8 bit; + + bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : + UB960_SR_RESET_DIGITAL_RESET0; + + ub960_write(priv, UB960_SR_RESET, bit); + + mutex_lock(&priv->reg_lock); + + ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, + (v & bit) == 0, 2000, 100000); + + mutex_unlock(&priv->reg_lock); + + if (ret) + dev_err(dev, "reset failed: %d\n", ret); +} + +static int ub960_get_hw_resources(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + + priv->regmap = devm_regmap_init_i2c(priv->client, &ub960_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->vddio = devm_regulator_get(dev, "vddio"); + if (IS_ERR(priv->vddio)) + return dev_err_probe(dev, PTR_ERR(priv->vddio), + "cannot get VDDIO regulator\n"); + + /* get power-down pin from DT */ + priv->pd_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->pd_gpio), + "Cannot get powerdown GPIO\n"); + + priv->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(priv->refclk)) + return dev_err_probe(dev, PTR_ERR(priv->refclk), + "Cannot get REFCLK\n"); + + return 0; +} + +static int ub960_enable_core_hw(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + u8 rev_mask; + int ret; + u8 dev_sts; + u8 refclk_freq; + + ret = regulator_enable(priv->vddio); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable VDDIO regulator\n"); + + ret = clk_prepare_enable(priv->refclk); + if (ret) { + dev_err_probe(dev, ret, "Failed to enable refclk\n"); + goto err_disable_vddio; + } + + if (priv->pd_gpio) { + gpiod_set_value_cansleep(priv->pd_gpio, 1); + /* wait min 2 ms for reset to complete */ + fsleep(2000); + gpiod_set_value_cansleep(priv->pd_gpio, 0); + /* wait min 2 ms for power up to finish */ + fsleep(2000); + } + + ub960_reset(priv, true); + + /* Runtime check register accessibility */ + ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask); + if (ret) { + dev_err_probe(dev, ret, "Cannot read first register, abort\n"); + goto err_pd_gpio; + } + + dev_dbg(dev, "Found %s (rev/mask %#04x)\n", priv->hw_data->model, + rev_mask); + + ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts); + if (ret) + goto err_pd_gpio; + + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + if (ret) + goto err_pd_gpio; + + dev_dbg(dev, "refclk valid %u freq %u MHz (clk fw freq %lu MHz)\n", + !!(dev_sts & BIT(4)), refclk_freq, + clk_get_rate(priv->refclk) / 1000000); + + /* Disable all RX ports by default */ + ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); + if (ret) + goto err_pd_gpio; + + /* release GPIO lock */ + if (priv->hw_data->is_ub9702) { + ret = ub960_update_bits(priv, UB960_SR_RESET, + UB960_SR_RESET_GPIO_LOCK_RELEASE, + UB960_SR_RESET_GPIO_LOCK_RELEASE); + if (ret) + goto err_pd_gpio; + } + + return 0; + +err_pd_gpio: + gpiod_set_value_cansleep(priv->pd_gpio, 1); + clk_disable_unprepare(priv->refclk); +err_disable_vddio: + regulator_disable(priv->vddio); + + return ret; +} + +static void ub960_disable_core_hw(struct ub960_data *priv) +{ + gpiod_set_value_cansleep(priv->pd_gpio, 1); + clk_disable_unprepare(priv->refclk); + regulator_disable(priv->vddio); +} + +static int ub960_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub960_data *priv; + unsigned int port_lock_mask; + unsigned int port_mask; + unsigned int nport; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->hw_data = device_get_match_data(dev); + + mutex_init(&priv->reg_lock); + + INIT_DELAYED_WORK(&priv->poll_work, ub960_handler_work); + + /* + * Initialize these to invalid values so that the first reg writes will + * configure the target. + */ + priv->reg_current.indirect_target = 0xff; + priv->reg_current.rxport = 0xff; + priv->reg_current.txport = 0xff; + + ret = ub960_get_hw_resources(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub960_enable_core_hw(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub960_parse_dt(priv); + if (ret) + goto err_disable_core_hw; + + ret = ub960_init_tx_ports(priv); + if (ret) + goto err_free_ports; + + ret = ub960_rxport_enable_vpocs(priv); + if (ret) + goto err_free_ports; + + ret = ub960_init_rx_ports(priv); + if (ret) + goto err_disable_vpocs; + + ub960_reset(priv, false); + + port_mask = 0; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + port_mask |= BIT(nport); + } + + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + goto err_disable_vpocs; + + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + goto err_disable_vpocs; + } + + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + ub960_clear_rx_errors(priv); + + ret = ub960_init_atr(priv); + if (ret) + goto err_disable_vpocs; + + ret = ub960_rxport_add_serializers(priv); + if (ret) + goto err_uninit_atr; + + ret = ub960_create_subdev(priv); + if (ret) + goto err_free_sers; + + if (client->irq) + dev_warn(dev, "irq support not implemented, using polling\n"); + + schedule_delayed_work(&priv->poll_work, + msecs_to_jiffies(UB960_POLL_TIME_MS)); + + return 0; + +err_free_sers: + ub960_rxport_remove_serializers(priv); +err_uninit_atr: + ub960_uninit_atr(priv); +err_disable_vpocs: + ub960_rxport_disable_vpocs(priv); +err_free_ports: + ub960_rxport_free_ports(priv); + ub960_txport_free_ports(priv); +err_disable_core_hw: + ub960_disable_core_hw(priv); +err_mutex_destroy: + mutex_destroy(&priv->reg_lock); + return ret; +} + +static void ub960_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub960_data *priv = sd_to_ub960(sd); + + cancel_delayed_work_sync(&priv->poll_work); + + ub960_destroy_subdev(priv); + ub960_rxport_remove_serializers(priv); + ub960_uninit_atr(priv); + ub960_rxport_disable_vpocs(priv); + ub960_rxport_free_ports(priv); + ub960_txport_free_ports(priv); + ub960_disable_core_hw(priv); + mutex_destroy(&priv->reg_lock); +} + +static const struct ub960_hw_data ds90ub960_hw = { + .model = "ub960", + .num_rxports = 4, + .num_txports = 2, +}; + +static const struct ub960_hw_data ds90ub9702_hw = { + .model = "ub9702", + .num_rxports = 4, + .num_txports = 2, + .is_ub9702 = true, + .is_fpdlink4 = true, +}; + +static const struct i2c_device_id ub960_id[] = { + { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, + { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ub960_id); + +static const struct of_device_id ub960_dt_ids[] = { + { .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw }, + { .compatible = "ti,ds90ub9702-q1", .data = &ds90ub9702_hw }, + {} +}; +MODULE_DEVICE_TABLE(of, ub960_dt_ids); + +static struct i2c_driver ds90ub960_driver = { + .probe = ub960_probe, + .remove = ub960_remove, + .id_table = ub960_id, + .driver = { + .name = "ds90ub960", + .of_match_table = ub960_dt_ids, + }, +}; +module_i2c_driver(ds90ub960_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV Deserializers Driver"); +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c new file mode 100644 index 000000000000..c626ed845928 --- /dev/null +++ b/drivers/media/i2c/dw9719.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2012 Intel Corporation + +/* + * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo: + * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DW9719_MAX_FOCUS_POS 1023 +#define DW9719_CTRL_STEPS 16 +#define DW9719_CTRL_DELAY_US 1000 + +#define DW9719_INFO CCI_REG8(0) +#define DW9719_ID 0xF1 + +#define DW9719_CONTROL CCI_REG8(2) +#define DW9719_ENABLE_RINGING 0x02 + +#define DW9719_VCM_CURRENT CCI_REG16(3) + +#define DW9719_MODE CCI_REG8(6) +#define DW9719_MODE_SAC_SHIFT 4 +#define DW9719_MODE_SAC3 4 + +#define DW9719_VCM_FREQ CCI_REG8(7) +#define DW9719_DEFAULT_VCM_FREQ 0x60 + +#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd) + +struct dw9719_device { + struct v4l2_subdev sd; + struct device *dev; + struct regmap *regmap; + struct regulator *regulator; + u32 sac_mode; + u32 vcm_freq; + + struct dw9719_v4l2_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *focus; + } ctrls; +}; + +static int dw9719_detect(struct dw9719_device *dw9719) +{ + int ret; + u64 val; + + ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); + if (ret < 0) + return ret; + + if (val != DW9719_ID) { + dev_err(dw9719->dev, "Failed to detect correct id\n"); + return -ENXIO; + } + + return 0; +} + +static int dw9719_power_down(struct dw9719_device *dw9719) +{ + return regulator_disable(dw9719->regulator); +} + +static int dw9719_power_up(struct dw9719_device *dw9719) +{ + int ret; + + ret = regulator_enable(dw9719->regulator); + if (ret) + return ret; + + /* Jiggle SCL pin to wake up device */ + cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret); + + /* Need 100us to transit from SHUTDOWN to STANDBY */ + fsleep(100); + + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); + cci_write(dw9719->regmap, DW9719_MODE, + dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret); + cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + + if (ret) + dw9719_power_down(dw9719); + + return ret; +} + +static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value) +{ + return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL); +} + +static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9719_device *dw9719 = container_of(ctrl->handler, + struct dw9719_device, + ctrls.handler); + int ret; + + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(dw9719->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_FOCUS_ABSOLUTE: + ret = dw9719_t_focus_abs(dw9719, ctrl->val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_put(dw9719->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops dw9719_ctrl_ops = { + .s_ctrl = dw9719_set_ctrl, +}; + +static int dw9719_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct dw9719_device *dw9719 = to_dw9719_device(sd); + int ret; + int val; + + for (val = dw9719->ctrls.focus->val; val >= 0; + val -= DW9719_CTRL_STEPS) { + ret = dw9719_t_focus_abs(dw9719, val); + if (ret) + return ret; + + usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10); + } + + return dw9719_power_down(dw9719); +} + +static int dw9719_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct dw9719_device *dw9719 = to_dw9719_device(sd); + int current_focus = dw9719->ctrls.focus->val; + int ret; + int val; + + ret = dw9719_power_up(dw9719); + if (ret) + return ret; + + for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus; + val += DW9719_CTRL_STEPS) { + ret = dw9719_t_focus_abs(dw9719, val); + if (ret) + goto err_power_down; + + usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10); + } + + return 0; + +err_power_down: + dw9719_power_down(dw9719); + return ret; +} + +static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return pm_runtime_resume_and_get(sd->dev); +} + +static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9719_internal_ops = { + .open = dw9719_open, + .close = dw9719_close, +}; + +static int dw9719_init_controls(struct dw9719_device *dw9719) +{ + const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops; + int ret; + + v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1); + + dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops, + V4L2_CID_FOCUS_ABSOLUTE, 0, + DW9719_MAX_FOCUS_POS, 1, 0); + + if (dw9719->ctrls.handler.error) { + dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n"); + ret = dw9719->ctrls.handler.error; + goto err_free_handler; + } + + dw9719->sd.ctrl_handler = &dw9719->ctrls.handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + return ret; +} + +static const struct v4l2_subdev_ops dw9719_ops = { }; + +static int dw9719_probe(struct i2c_client *client) +{ + struct dw9719_device *dw9719; + int ret; + + dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL); + if (!dw9719) + return -ENOMEM; + + dw9719->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(dw9719->regmap)) + return PTR_ERR(dw9719->regmap); + + dw9719->dev = &client->dev; + dw9719->sac_mode = DW9719_MODE_SAC3; + dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; + + /* Optional indication of SAC mode select */ + device_property_read_u32(&client->dev, "dongwoon,sac-mode", + &dw9719->sac_mode); + + /* Optional indication of VCM frequency */ + device_property_read_u32(&client->dev, "dongwoon,vcm-freq", + &dw9719->vcm_freq); + + dw9719->regulator = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(dw9719->regulator)) + return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator), + "getting regulator\n"); + + v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops); + dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9719->sd.internal_ops = &dw9719_internal_ops; + + ret = dw9719_init_controls(dw9719); + if (ret) + return ret; + + ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_ctrl_handler; + + dw9719->sd.entity.function = MEDIA_ENT_F_LENS; + + /* + * We need the driver to work in the event that pm runtime is disable in + * the kernel, so power up and verify the chip now. In the event that + * runtime pm is disabled this will leave the chip on, so that the lens + * will work. + */ + + ret = dw9719_power_up(dw9719); + if (ret) + goto err_cleanup_media; + + ret = dw9719_detect(dw9719); + if (ret) + goto err_powerdown; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + + ret = v4l2_async_register_subdev(&dw9719->sd); + if (ret < 0) + goto err_pm_runtime; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; + +err_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); +err_powerdown: + dw9719_power_down(dw9719); +err_cleanup_media: + media_entity_cleanup(&dw9719->sd.entity); +err_free_ctrl_handler: + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + + return ret; +} + +static void dw9719_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9719_device *dw9719 = + container_of(sd, struct dw9719_device, sd); + + v4l2_async_unregister_subdev(sd); + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + media_entity_cleanup(&dw9719->sd.entity); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + dw9719_power_down(dw9719); + pm_runtime_set_suspended(&client->dev); +} + +static const struct i2c_device_id dw9719_id_table[] = { + { "dw9719" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9719_id_table); + +static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume, + NULL); + +static struct i2c_driver dw9719_i2c_driver = { + .driver = { + .name = "dw9719", + .pm = pm_sleep_ptr(&dw9719_pm_ops), + }, + .probe = dw9719_probe, + .remove = dw9719_remove, + .id_table = dw9719_id_table, +}; +module_i2c_driver(dw9719_i2c_driver); + +MODULE_AUTHOR("Daniel Scally "); +MODULE_DESCRIPTION("DW9719 VCM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig index 398dd4d21df1..987fc62d5e6b 100644 --- a/drivers/media/i2c/et8ek8/Kconfig +++ b/drivers/media/i2c/et8ek8/Kconfig @@ -1,10 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_ET8EK8 tristate "ET8EK8 camera sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a driver for the Toshiba ET8EK8 5 MP camera sensor. It is used for example in Nokia N900 (RX-51). diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index 50e78f5b058c..fd56ba138739 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -1357,6 +1357,6 @@ static struct i2c_driver hi556_i2c_driver = { module_i2c_driver(hi556_i2c_driver); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Hynix HI556 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 7cdce392e137..32547d7a2659 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -3005,6 +3005,6 @@ static struct i2c_driver hi847_i2c_driver = { module_i2c_driver(hi847_i2c_driver); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Hynix HI847 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index 3e870fa9ff79..ee5a28675388 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -1109,6 +1109,6 @@ module_i2c_driver(imx208_i2c_driver); MODULE_AUTHOR("Yeh, Andy "); MODULE_AUTHOR("Chen, Ping-chung "); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Sony IMX208 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index d737d5e9a4a6..a1136fdfbed2 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -345,7 +345,7 @@ static const char * const imx219_supply_name[] = { * - v flip * - h&v flips */ -static const u32 codes[] = { +static const u32 imx219_mbus_formats[] = { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, @@ -460,8 +460,6 @@ struct imx219 { struct v4l2_subdev sd; struct media_pad pad; - struct v4l2_mbus_framefmt fmt; - struct clk *xclk; /* system clock to IMX219 */ u32 xclk_freq; @@ -481,12 +479,6 @@ struct imx219 { /* Current mode */ const struct imx219_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - /* Streaming on/off */ bool streaming; @@ -576,64 +568,17 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) { unsigned int i; - lockdep_assert_held(&imx219->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == code) + for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); i++) + if (imx219_mbus_formats[i] == code) break; - if (i >= ARRAY_SIZE(codes)) + if (i >= ARRAY_SIZE(imx219_mbus_formats)) i = 0; i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | (imx219->hflip->val ? 1 : 0); - return codes[i]; -} - -static void imx219_set_default_format(struct imx219 *imx219) -{ - struct v4l2_mbus_framefmt *fmt; - - fmt = &imx219->fmt; - fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = supported_modes[0].width; - fmt->height = supported_modes[0].height; - fmt->field = V4L2_FIELD_NONE; -} - -static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); - struct v4l2_rect *try_crop; - - mutex_lock(&imx219->mutex); - - /* Initialize try_fmt */ - try_fmt->width = supported_modes[0].width; - try_fmt->height = supported_modes[0].height; - try_fmt->code = imx219_get_format_code(imx219, - MEDIA_BUS_FMT_SRGGB10_1X10); - try_fmt->field = V4L2_FIELD_NONE; - - /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); - try_crop->top = IMX219_PIXEL_ARRAY_TOP; - try_crop->left = IMX219_PIXEL_ARRAY_LEFT; - try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx219->mutex); - - return 0; + return imx219_mbus_formats[i]; } static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) @@ -725,18 +670,52 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { .s_ctrl = imx219_set_ctrl, }; +static void imx219_update_pad_format(struct imx219 *imx219, + const struct imx219_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + /* Bayer order varies with flips */ + fmt->code = imx219_get_format_code(imx219, code); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int imx219_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx219 *imx219 = to_imx219(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize try_fmt */ + format = v4l2_subdev_get_pad_format(sd, state, 0); + imx219_update_pad_format(imx219, &supported_modes[0], format, + MEDIA_BUS_FMT_SRGGB10_1X10); + + /* Initialize crop rectangle. */ + crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop->top = IMX219_PIXEL_ARRAY_TOP; + crop->left = IMX219_PIXEL_ARRAY_LEFT; + crop->width = IMX219_PIXEL_ARRAY_WIDTH; + crop->height = IMX219_PIXEL_ARRAY_HEIGHT; + + return 0; +} + static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx219 *imx219 = to_imx219(sd); - if (code->index >= (ARRAY_SIZE(codes) / 4)) + if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) return -EINVAL; - mutex_lock(&imx219->mutex); - code->code = imx219_get_format_code(imx219, codes[code->index * 4]); - mutex_unlock(&imx219->mutex); + code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); return 0; } @@ -751,9 +730,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - mutex_lock(&imx219->mutex); code = imx219_get_format_code(imx219, fse->code); - mutex_unlock(&imx219->mutex); if (fse->code != code) return -EINVAL; @@ -765,92 +742,27 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) -{ - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} - -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx219_reset_colorspace(&fmt->format); -} - -static int __imx219_get_pad_format(struct imx219 *imx219, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx219->sd, sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); - fmt->format = *try_fmt; - } else { - imx219_update_pad_format(imx219, imx219->mode, fmt); - fmt->format.code = imx219_get_format_code(imx219, - imx219->fmt.code); - } - - return 0; -} - -static int imx219_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx219 *imx219 = to_imx219(sd); - int ret; - - mutex_lock(&imx219->mutex); - ret = __imx219_get_pad_format(imx219, sd_state, fmt); - mutex_unlock(&imx219->mutex); - - return ret; -} - static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; - struct v4l2_mbus_framefmt *framefmt; int exposure_max, exposure_def, hblank; - unsigned int i; - - mutex_lock(&imx219->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == fmt->format.code) - break; - if (i >= ARRAY_SIZE(codes)) - i = 0; - - /* Bayer order varies with flips */ - fmt->format.code = imx219_get_format_code(imx219, codes[i]); + struct v4l2_mbus_framefmt *format; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx219_update_pad_format(imx219, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); - *framefmt = fmt->format; - } else if (imx219->mode != mode || - imx219->fmt.code != fmt->format.code) { - imx219->fmt = fmt->format; + + imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + + if (imx219->mode == mode && format->code == fmt->format.code) + return 0; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx219->mode = mode; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, @@ -876,14 +788,15 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, hblank); } - mutex_unlock(&imx219->mutex); + *format = fmt->format; return 0; } -static int imx219_set_framefmt(struct imx219 *imx219) +static int imx219_set_framefmt(struct imx219 *imx219, + const struct v4l2_mbus_framefmt *format) { - switch (imx219->fmt.code) { + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: @@ -902,7 +815,8 @@ static int imx219_set_framefmt(struct imx219 *imx219) return -EINVAL; } -static int imx219_set_binning(struct imx219 *imx219) +static int imx219_set_binning(struct imx219 *imx219, + const struct v4l2_mbus_framefmt *format) { if (!imx219->mode->binning) { return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE, @@ -910,7 +824,7 @@ static int imx219_set_binning(struct imx219 *imx219) IMX219_BINNING_NONE); } - switch (imx219->fmt.code) { + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: @@ -931,34 +845,13 @@ static int imx219_set_binning(struct imx219 *imx219) return -EINVAL; } -static const struct v4l2_rect * -__imx219_get_pad_crop(struct imx219 *imx219, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx219->sd, sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx219->mode->crop; - } - - return NULL; -} - static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { case V4L2_SEL_TGT_CROP: { - struct imx219 *imx219 = to_imx219(sd); - - mutex_lock(&imx219->mutex); - sel->r = *__imx219_get_pad_crop(imx219, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx219->mutex); - + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); return 0; } @@ -990,9 +883,11 @@ static int imx219_configure_lanes(struct imx219 *imx219) IMX219_CSI_2_LANE_MODE : IMX219_CSI_4_LANE_MODE); }; -static int imx219_start_streaming(struct imx219 *imx219) +static int imx219_start_streaming(struct imx219 *imx219, + struct v4l2_subdev_state *state) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct v4l2_mbus_framefmt *format; const struct imx219_reg_list *reg_list; int ret; @@ -1022,14 +917,15 @@ static int imx219_start_streaming(struct imx219 *imx219) goto err_rpm_put; } - ret = imx219_set_framefmt(imx219); + format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + ret = imx219_set_framefmt(imx219, format); if (ret) { dev_err(&client->dev, "%s failed to set frame format: %d\n", __func__, ret); goto err_rpm_put; } - ret = imx219_set_binning(imx219); + ret = imx219_set_binning(imx219, format); if (ret) { dev_err(&client->dev, "%s failed to set binning: %d\n", __func__, ret); @@ -1078,35 +974,30 @@ static void imx219_stop_streaming(struct imx219 *imx219) static int imx219_set_stream(struct v4l2_subdev *sd, int enable) { struct imx219 *imx219 = to_imx219(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx219->mutex); - if (imx219->streaming == enable) { - mutex_unlock(&imx219->mutex); - return 0; - } + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (imx219->streaming == enable) + goto unlock; if (enable) { /* * Apply default & customized values * and then start streaming. */ - ret = imx219_start_streaming(imx219); + ret = imx219_start_streaming(imx219, state); if (ret) - goto err_unlock; + goto unlock; } else { imx219_stop_streaming(imx219); } imx219->streaming = enable; - mutex_unlock(&imx219->mutex); - - return ret; - -err_unlock: - mutex_unlock(&imx219->mutex); - +unlock: + v4l2_subdev_unlock_state(state); return ret; } @@ -1171,10 +1062,13 @@ static int __maybe_unused imx219_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); + struct v4l2_subdev_state *state; int ret; if (imx219->streaming) { - ret = imx219_start_streaming(imx219); + state = v4l2_subdev_lock_and_get_active_state(sd); + ret = imx219_start_streaming(imx219, state); + v4l2_subdev_unlock_state(state); if (ret) goto error; } @@ -1235,8 +1129,9 @@ static const struct v4l2_subdev_video_ops imx219_video_ops = { }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { + .init_cfg = imx219_init_cfg, .enum_mbus_code = imx219_enum_mbus_code, - .get_fmt = imx219_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, @@ -1248,9 +1143,6 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { .pad = &imx219_pad_ops, }; -static const struct v4l2_subdev_internal_ops imx219_internal_ops = { - .open = imx219_open, -}; static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { @@ -1272,9 +1164,6 @@ static int imx219_init_controls(struct imx219 *imx219) if (ret) return ret; - mutex_init(&imx219->mutex); - ctrl_hdlr->lock = &imx219->mutex; - /* By default, PIXEL_RATE is read only */ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1371,7 +1260,6 @@ static int imx219_init_controls(struct imx219 *imx219) error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx219->mutex); return ret; } @@ -1379,7 +1267,6 @@ error: static void imx219_free_controls(struct imx219 *imx219) { v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); - mutex_destroy(&imx219->mutex); } static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) @@ -1509,7 +1396,6 @@ static int imx219_probe(struct i2c_client *client) goto error_power_off; /* Initialize subdev */ - imx219->sd.internal_ops = &imx219_internal_ops; imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1517,19 +1403,23 @@ static int imx219_probe(struct i2c_client *client) /* Initialize source pad */ imx219->pad.flags = MEDIA_PAD_FL_SOURCE; - /* Initialize default format */ - imx219_set_default_format(imx219); - ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx219->sd.state_lock = imx219->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx219->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx219->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } /* Enable runtime PM and turn off the device */ @@ -1539,6 +1429,9 @@ static int imx219_probe(struct i2c_client *client) return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx219->sd); + error_media_entity: media_entity_cleanup(&imx219->sd.entity); @@ -1557,6 +1450,7 @@ static void imx219_remove(struct i2c_client *client) struct imx219 *imx219 = to_imx219(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); imx219_free_controls(imx219); diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index b3f832e9d7e1..29098612813c 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,91 +21,86 @@ #include #include +#include #include #include #include #include #include -#define IMX290_REG_SIZE_SHIFT 16 -#define IMX290_REG_ADDR_MASK 0xffff -#define IMX290_REG_8BIT(n) ((1U << IMX290_REG_SIZE_SHIFT) | (n)) -#define IMX290_REG_16BIT(n) ((2U << IMX290_REG_SIZE_SHIFT) | (n)) -#define IMX290_REG_24BIT(n) ((3U << IMX290_REG_SIZE_SHIFT) | (n)) - -#define IMX290_STANDBY IMX290_REG_8BIT(0x3000) -#define IMX290_REGHOLD IMX290_REG_8BIT(0x3001) -#define IMX290_XMSTA IMX290_REG_8BIT(0x3002) -#define IMX290_ADBIT IMX290_REG_8BIT(0x3005) +#define IMX290_STANDBY CCI_REG8(0x3000) +#define IMX290_REGHOLD CCI_REG8(0x3001) +#define IMX290_XMSTA CCI_REG8(0x3002) +#define IMX290_ADBIT CCI_REG8(0x3005) #define IMX290_ADBIT_10BIT (0 << 0) #define IMX290_ADBIT_12BIT (1 << 0) -#define IMX290_CTRL_07 IMX290_REG_8BIT(0x3007) +#define IMX290_CTRL_07 CCI_REG8(0x3007) #define IMX290_VREVERSE BIT(0) #define IMX290_HREVERSE BIT(1) #define IMX290_WINMODE_1080P (0 << 4) #define IMX290_WINMODE_720P (1 << 4) #define IMX290_WINMODE_CROP (4 << 4) -#define IMX290_FR_FDG_SEL IMX290_REG_8BIT(0x3009) -#define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) -#define IMX290_GAIN IMX290_REG_8BIT(0x3014) -#define IMX290_VMAX IMX290_REG_24BIT(0x3018) +#define IMX290_FR_FDG_SEL CCI_REG8(0x3009) +#define IMX290_BLKLEVEL CCI_REG16(0x300a) +#define IMX290_GAIN CCI_REG8(0x3014) +#define IMX290_VMAX CCI_REG24(0x3018) #define IMX290_VMAX_MAX 0x3ffff -#define IMX290_HMAX IMX290_REG_16BIT(0x301c) +#define IMX290_HMAX CCI_REG16(0x301c) #define IMX290_HMAX_MAX 0xffff -#define IMX290_SHS1 IMX290_REG_24BIT(0x3020) -#define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) -#define IMX290_WINPV IMX290_REG_16BIT(0x303c) -#define IMX290_WINWV IMX290_REG_16BIT(0x303e) -#define IMX290_WINPH IMX290_REG_16BIT(0x3040) -#define IMX290_WINWH IMX290_REG_16BIT(0x3042) -#define IMX290_OUT_CTRL IMX290_REG_8BIT(0x3046) +#define IMX290_SHS1 CCI_REG24(0x3020) +#define IMX290_WINWV_OB CCI_REG8(0x303a) +#define IMX290_WINPV CCI_REG16(0x303c) +#define IMX290_WINWV CCI_REG16(0x303e) +#define IMX290_WINPH CCI_REG16(0x3040) +#define IMX290_WINWH CCI_REG16(0x3042) +#define IMX290_OUT_CTRL CCI_REG8(0x3046) #define IMX290_ODBIT_10BIT (0 << 0) #define IMX290_ODBIT_12BIT (1 << 0) #define IMX290_OPORTSEL_PARALLEL (0x0 << 4) #define IMX290_OPORTSEL_LVDS_2CH (0xd << 4) #define IMX290_OPORTSEL_LVDS_4CH (0xe << 4) #define IMX290_OPORTSEL_LVDS_8CH (0xf << 4) -#define IMX290_XSOUTSEL IMX290_REG_8BIT(0x304b) +#define IMX290_XSOUTSEL CCI_REG8(0x304b) #define IMX290_XSOUTSEL_XVSOUTSEL_HIGH (0 << 0) #define IMX290_XSOUTSEL_XVSOUTSEL_VSYNC (2 << 0) #define IMX290_XSOUTSEL_XHSOUTSEL_HIGH (0 << 2) #define IMX290_XSOUTSEL_XHSOUTSEL_HSYNC (2 << 2) -#define IMX290_INCKSEL1 IMX290_REG_8BIT(0x305c) -#define IMX290_INCKSEL2 IMX290_REG_8BIT(0x305d) -#define IMX290_INCKSEL3 IMX290_REG_8BIT(0x305e) -#define IMX290_INCKSEL4 IMX290_REG_8BIT(0x305f) -#define IMX290_PGCTRL IMX290_REG_8BIT(0x308c) -#define IMX290_ADBIT1 IMX290_REG_8BIT(0x3129) +#define IMX290_INCKSEL1 CCI_REG8(0x305c) +#define IMX290_INCKSEL2 CCI_REG8(0x305d) +#define IMX290_INCKSEL3 CCI_REG8(0x305e) +#define IMX290_INCKSEL4 CCI_REG8(0x305f) +#define IMX290_PGCTRL CCI_REG8(0x308c) +#define IMX290_ADBIT1 CCI_REG8(0x3129) #define IMX290_ADBIT1_10BIT 0x1d #define IMX290_ADBIT1_12BIT 0x00 -#define IMX290_INCKSEL5 IMX290_REG_8BIT(0x315e) -#define IMX290_INCKSEL6 IMX290_REG_8BIT(0x3164) -#define IMX290_ADBIT2 IMX290_REG_8BIT(0x317c) +#define IMX290_INCKSEL5 CCI_REG8(0x315e) +#define IMX290_INCKSEL6 CCI_REG8(0x3164) +#define IMX290_ADBIT2 CCI_REG8(0x317c) #define IMX290_ADBIT2_10BIT 0x12 #define IMX290_ADBIT2_12BIT 0x00 -#define IMX290_CHIP_ID IMX290_REG_16BIT(0x319a) -#define IMX290_ADBIT3 IMX290_REG_8BIT(0x31ec) +#define IMX290_CHIP_ID CCI_REG16(0x319a) +#define IMX290_ADBIT3 CCI_REG8(0x31ec) #define IMX290_ADBIT3_10BIT 0x37 #define IMX290_ADBIT3_12BIT 0x0e -#define IMX290_REPETITION IMX290_REG_8BIT(0x3405) -#define IMX290_PHY_LANE_NUM IMX290_REG_8BIT(0x3407) -#define IMX290_OPB_SIZE_V IMX290_REG_8BIT(0x3414) -#define IMX290_Y_OUT_SIZE IMX290_REG_16BIT(0x3418) -#define IMX290_CSI_DT_FMT IMX290_REG_16BIT(0x3441) +#define IMX290_REPETITION CCI_REG8(0x3405) +#define IMX290_PHY_LANE_NUM CCI_REG8(0x3407) +#define IMX290_OPB_SIZE_V CCI_REG8(0x3414) +#define IMX290_Y_OUT_SIZE CCI_REG16(0x3418) +#define IMX290_CSI_DT_FMT CCI_REG16(0x3441) #define IMX290_CSI_DT_FMT_RAW10 0x0a0a #define IMX290_CSI_DT_FMT_RAW12 0x0c0c -#define IMX290_CSI_LANE_MODE IMX290_REG_8BIT(0x3443) -#define IMX290_EXTCK_FREQ IMX290_REG_16BIT(0x3444) -#define IMX290_TCLKPOST IMX290_REG_16BIT(0x3446) -#define IMX290_THSZERO IMX290_REG_16BIT(0x3448) -#define IMX290_THSPREPARE IMX290_REG_16BIT(0x344a) -#define IMX290_TCLKTRAIL IMX290_REG_16BIT(0x344c) -#define IMX290_THSTRAIL IMX290_REG_16BIT(0x344e) -#define IMX290_TCLKZERO IMX290_REG_16BIT(0x3450) -#define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) -#define IMX290_TLPX IMX290_REG_16BIT(0x3454) -#define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) -#define IMX290_INCKSEL7 IMX290_REG_8BIT(0x3480) +#define IMX290_CSI_LANE_MODE CCI_REG8(0x3443) +#define IMX290_EXTCK_FREQ CCI_REG16(0x3444) +#define IMX290_TCLKPOST CCI_REG16(0x3446) +#define IMX290_THSZERO CCI_REG16(0x3448) +#define IMX290_THSPREPARE CCI_REG16(0x344a) +#define IMX290_TCLKTRAIL CCI_REG16(0x344c) +#define IMX290_THSTRAIL CCI_REG16(0x344e) +#define IMX290_TCLKZERO CCI_REG16(0x3450) +#define IMX290_TCLKPREPARE CCI_REG16(0x3452) +#define IMX290_TLPX CCI_REG16(0x3454) +#define IMX290_X_OUT_SIZE CCI_REG16(0x3472) +#define IMX290_INCKSEL7 CCI_REG8(0x3480) #define IMX290_PGCTRL_REGEN BIT(0) #define IMX290_PGCTRL_THRU BIT(1) @@ -181,7 +176,7 @@ enum imx290_model { struct imx290_model_info { enum imx290_colour_variant colour_variant; - const struct imx290_regval *init_regs; + const struct cci_reg_sequence *init_regs; size_t init_regs_num; const char *name; }; @@ -192,11 +187,6 @@ enum imx290_clk_freq { IMX290_NUM_CLK }; -struct imx290_regval { - u32 reg; - u32 val; -}; - /* * Clock configuration for registers INCKSEL1 to INCKSEL6. */ @@ -217,7 +207,7 @@ struct imx290_mode { u8 link_freq_index; u8 ctrl_07; - const struct imx290_regval *data; + const struct cci_reg_sequence *data; u32 data_size; const struct imx290_clk_cfg *clk_cfg; @@ -271,7 +261,7 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) * Modes and formats */ -static const struct imx290_regval imx290_global_init_settings[] = { +static const struct cci_reg_sequence imx290_global_init_settings[] = { { IMX290_WINWV_OB, 12 }, { IMX290_WINPH, 0 }, { IMX290_WINPV, 0 }, @@ -279,56 +269,56 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { IMX290_REG_8BIT(0x3011), 0x02 }, - { IMX290_REG_8BIT(0x3012), 0x64 }, - { IMX290_REG_8BIT(0x3013), 0x00 }, + { CCI_REG8(0x3011), 0x02 }, + { CCI_REG8(0x3012), 0x64 }, + { CCI_REG8(0x3013), 0x00 }, }; -static const struct imx290_regval imx290_global_init_settings_290[] = { - { IMX290_REG_8BIT(0x300f), 0x00 }, - { IMX290_REG_8BIT(0x3010), 0x21 }, - { IMX290_REG_8BIT(0x3016), 0x09 }, - { IMX290_REG_8BIT(0x3070), 0x02 }, - { IMX290_REG_8BIT(0x3071), 0x11 }, - { IMX290_REG_8BIT(0x309b), 0x10 }, - { IMX290_REG_8BIT(0x309c), 0x22 }, - { IMX290_REG_8BIT(0x30a2), 0x02 }, - { IMX290_REG_8BIT(0x30a6), 0x20 }, - { IMX290_REG_8BIT(0x30a8), 0x20 }, - { IMX290_REG_8BIT(0x30aa), 0x20 }, - { IMX290_REG_8BIT(0x30ac), 0x20 }, - { IMX290_REG_8BIT(0x30b0), 0x43 }, - { IMX290_REG_8BIT(0x3119), 0x9e }, - { IMX290_REG_8BIT(0x311c), 0x1e }, - { IMX290_REG_8BIT(0x311e), 0x08 }, - { IMX290_REG_8BIT(0x3128), 0x05 }, - { IMX290_REG_8BIT(0x313d), 0x83 }, - { IMX290_REG_8BIT(0x3150), 0x03 }, - { IMX290_REG_8BIT(0x317e), 0x00 }, - { IMX290_REG_8BIT(0x32b8), 0x50 }, - { IMX290_REG_8BIT(0x32b9), 0x10 }, - { IMX290_REG_8BIT(0x32ba), 0x00 }, - { IMX290_REG_8BIT(0x32bb), 0x04 }, - { IMX290_REG_8BIT(0x32c8), 0x50 }, - { IMX290_REG_8BIT(0x32c9), 0x10 }, - { IMX290_REG_8BIT(0x32ca), 0x00 }, - { IMX290_REG_8BIT(0x32cb), 0x04 }, - { IMX290_REG_8BIT(0x332c), 0xd3 }, - { IMX290_REG_8BIT(0x332d), 0x10 }, - { IMX290_REG_8BIT(0x332e), 0x0d }, - { IMX290_REG_8BIT(0x3358), 0x06 }, - { IMX290_REG_8BIT(0x3359), 0xe1 }, - { IMX290_REG_8BIT(0x335a), 0x11 }, - { IMX290_REG_8BIT(0x3360), 0x1e }, - { IMX290_REG_8BIT(0x3361), 0x61 }, - { IMX290_REG_8BIT(0x3362), 0x10 }, - { IMX290_REG_8BIT(0x33b0), 0x50 }, - { IMX290_REG_8BIT(0x33b2), 0x1a }, - { IMX290_REG_8BIT(0x33b3), 0x04 }, +static const struct cci_reg_sequence imx290_global_init_settings_290[] = { + { CCI_REG8(0x300f), 0x00 }, + { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3016), 0x09 }, + { CCI_REG8(0x3070), 0x02 }, + { CCI_REG8(0x3071), 0x11 }, + { CCI_REG8(0x309b), 0x10 }, + { CCI_REG8(0x309c), 0x22 }, + { CCI_REG8(0x30a2), 0x02 }, + { CCI_REG8(0x30a6), 0x20 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30aa), 0x20 }, + { CCI_REG8(0x30ac), 0x20 }, + { CCI_REG8(0x30b0), 0x43 }, + { CCI_REG8(0x3119), 0x9e }, + { CCI_REG8(0x311c), 0x1e }, + { CCI_REG8(0x311e), 0x08 }, + { CCI_REG8(0x3128), 0x05 }, + { CCI_REG8(0x313d), 0x83 }, + { CCI_REG8(0x3150), 0x03 }, + { CCI_REG8(0x317e), 0x00 }, + { CCI_REG8(0x32b8), 0x50 }, + { CCI_REG8(0x32b9), 0x10 }, + { CCI_REG8(0x32ba), 0x00 }, + { CCI_REG8(0x32bb), 0x04 }, + { CCI_REG8(0x32c8), 0x50 }, + { CCI_REG8(0x32c9), 0x10 }, + { CCI_REG8(0x32ca), 0x00 }, + { CCI_REG8(0x32cb), 0x04 }, + { CCI_REG8(0x332c), 0xd3 }, + { CCI_REG8(0x332d), 0x10 }, + { CCI_REG8(0x332e), 0x0d }, + { CCI_REG8(0x3358), 0x06 }, + { CCI_REG8(0x3359), 0xe1 }, + { CCI_REG8(0x335a), 0x11 }, + { CCI_REG8(0x3360), 0x1e }, + { CCI_REG8(0x3361), 0x61 }, + { CCI_REG8(0x3362), 0x10 }, + { CCI_REG8(0x33b0), 0x50 }, + { CCI_REG8(0x33b2), 0x1a }, + { CCI_REG8(0x33b3), 0x04 }, }; #define IMX290_NUM_CLK_REGS 2 -static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { +static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { [IMX290_CLK_37_125] = { { IMX290_EXTCK_FREQ, (37125 * 256) / 1000 }, { IMX290_INCKSEL7, 0x49 }, @@ -339,13 +329,13 @@ static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { }, }; -static const struct imx290_regval imx290_global_init_settings_327[] = { - { IMX290_REG_8BIT(0x309e), 0x4A }, - { IMX290_REG_8BIT(0x309f), 0x4A }, - { IMX290_REG_8BIT(0x313b), 0x61 }, +static const struct cci_reg_sequence imx290_global_init_settings_327[] = { + { CCI_REG8(0x309e), 0x4A }, + { CCI_REG8(0x309f), 0x4A }, + { CCI_REG8(0x313b), 0x61 }, }; -static const struct imx290_regval imx290_1080p_settings[] = { +static const struct cci_reg_sequence imx290_1080p_settings[] = { /* mode settings */ { IMX290_WINWV_OB, 12 }, { IMX290_OPB_SIZE_V, 10 }, @@ -353,7 +343,7 @@ static const struct imx290_regval imx290_1080p_settings[] = { { IMX290_Y_OUT_SIZE, 1080 }, }; -static const struct imx290_regval imx290_720p_settings[] = { +static const struct cci_reg_sequence imx290_720p_settings[] = { /* mode settings */ { IMX290_WINWV_OB, 6 }, { IMX290_OPB_SIZE_V, 4 }, @@ -361,7 +351,7 @@ static const struct imx290_regval imx290_720p_settings[] = { { IMX290_Y_OUT_SIZE, 720 }, }; -static const struct imx290_regval imx290_10bit_settings[] = { +static const struct cci_reg_sequence imx290_10bit_settings[] = { { IMX290_ADBIT, IMX290_ADBIT_10BIT }, { IMX290_OUT_CTRL, IMX290_ODBIT_10BIT }, { IMX290_ADBIT1, IMX290_ADBIT1_10BIT }, @@ -370,7 +360,7 @@ static const struct imx290_regval imx290_10bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 }, }; -static const struct imx290_regval imx290_12bit_settings[] = { +static const struct cci_reg_sequence imx290_12bit_settings[] = { { IMX290_ADBIT, IMX290_ADBIT_12BIT }, { IMX290_OUT_CTRL, IMX290_ODBIT_12BIT }, { IMX290_ADBIT1, IMX290_ADBIT1_12BIT }, @@ -576,7 +566,7 @@ static inline int imx290_modes_num(const struct imx290 *imx290) struct imx290_format_info { u32 code[IMX290_VARIANT_MAX]; u8 bpp; - const struct imx290_regval *regs; + const struct cci_reg_sequence *regs; unsigned int num_regs; }; @@ -615,63 +605,15 @@ imx290_format_info(const struct imx290 *imx290, u32 code) return NULL; } -/* ----------------------------------------------------------------------------- - * Register access - */ - -static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value) -{ - u8 data[3] = { 0, 0, 0 }; - int ret; - - ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK, - data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); - if (ret < 0) { - dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, - addr & IMX290_REG_ADDR_MASK, ret); - return ret; - } - - *value = get_unaligned_le24(data); - return 0; -} - -static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) -{ - u8 data[3]; - int ret; - - if (err && *err) - return *err; - - put_unaligned_le24(value, data); - - ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, - data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); - if (ret < 0) { - dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, - addr & IMX290_REG_ADDR_MASK, ret); - if (err) - *err = ret; - } - - return ret; -} - static int imx290_set_register_array(struct imx290 *imx290, - const struct imx290_regval *settings, + const struct cci_reg_sequence *settings, unsigned int num_settings) { - unsigned int i; int ret; - for (i = 0; i < num_settings; ++i, ++settings) { - ret = imx290_write(imx290, settings->reg, settings->val, NULL); - if (ret < 0) - return ret; - } + ret = cci_multi_reg_write(imx290->regmap, settings, num_settings, NULL); + if (ret < 0) + return ret; /* Provide 10ms settle time */ usleep_range(10000, 11000); @@ -689,12 +631,12 @@ static int imx290_set_clock(struct imx290 *imx290) ret = imx290_set_register_array(imx290, xclk_regs[clk_idx], IMX290_NUM_CLK_REGS); - imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); - imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); - imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); - imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); - imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); - imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); return ret; } @@ -703,9 +645,11 @@ static int imx290_set_data_lanes(struct imx290 *imx290) { int ret = 0; - imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret); + cci_write(imx290->regmap, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, + &ret); + cci_write(imx290->regmap, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, + &ret); + cci_write(imx290->regmap, IMX290_FR_FDG_SEL, 0x01, &ret); return ret; } @@ -716,8 +660,8 @@ static int imx290_set_black_level(struct imx290 *imx290, { unsigned int bpp = imx290_format_info(imx290, format->code)->bpp; - return imx290_write(imx290, IMX290_BLKLEVEL, - black_level >> (16 - bpp), err); + return cci_write(imx290->regmap, IMX290_BLKLEVEL, + black_level >> (16 - bpp), err); } static int imx290_set_csi_config(struct imx290 *imx290) @@ -743,15 +687,16 @@ static int imx290_set_csi_config(struct imx290 *imx290) return -EINVAL; } - imx290_write(imx290, IMX290_REPETITION, csi_cfg->repetition, &ret); - imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); - imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret); - imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); - imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); - imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); - imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); - imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret); - imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret); + cci_write(imx290->regmap, IMX290_REPETITION, csi_cfg->repetition, &ret); + cci_write(imx290->regmap, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); + cci_write(imx290->regmap, IMX290_THSZERO, csi_cfg->thszero, &ret); + cci_write(imx290->regmap, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); + cci_write(imx290->regmap, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); + cci_write(imx290->regmap, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); + cci_write(imx290->regmap, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); + cci_write(imx290->regmap, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, + &ret); + cci_write(imx290->regmap, IMX290_TLPX, csi_cfg->tlpx, &ret); return ret; } @@ -817,13 +762,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); + ret = cci_write(imx290->regmap, IMX290_GAIN, ctrl->val, NULL); break; case V4L2_CID_VBLANK: - ret = imx290_write(imx290, IMX290_VMAX, - ctrl->val + imx290->current_mode->height, - NULL); + ret = cci_write(imx290->regmap, IMX290_VMAX, + ctrl->val + imx290->current_mode->height, NULL); /* * Due to the way that exposure is programmed in this sensor in * relation to VMAX, we have to reprogramme it whenever VMAX is @@ -835,20 +779,20 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) fallthrough; case V4L2_CID_EXPOSURE: vmax = imx290->vblank->val + imx290->current_mode->height; - ret = imx290_write(imx290, IMX290_SHS1, - vmax - ctrl->val - 1, NULL); + ret = cci_write(imx290->regmap, IMX290_SHS1, + vmax - ctrl->val - 1, NULL); break; case V4L2_CID_TEST_PATTERN: if (ctrl->val) { imx290_set_black_level(imx290, format, 0, &ret); usleep_range(10000, 11000); - imx290_write(imx290, IMX290_PGCTRL, - (u8)(IMX290_PGCTRL_REGEN | - IMX290_PGCTRL_THRU | - IMX290_PGCTRL_MODE(ctrl->val)), &ret); + cci_write(imx290->regmap, IMX290_PGCTRL, + (u8)(IMX290_PGCTRL_REGEN | + IMX290_PGCTRL_THRU | + IMX290_PGCTRL_MODE(ctrl->val)), &ret); } else { - imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret); + cci_write(imx290->regmap, IMX290_PGCTRL, 0x00, &ret); usleep_range(10000, 11000); imx290_set_black_level(imx290, format, IMX290_BLACK_LEVEL_DEFAULT, &ret); @@ -856,9 +800,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_HBLANK: - ret = imx290_write(imx290, IMX290_HMAX, - ctrl->val + imx290->current_mode->width, - NULL); + ret = cci_write(imx290->regmap, IMX290_HMAX, + ctrl->val + imx290->current_mode->width, NULL); break; case V4L2_CID_HFLIP: @@ -871,7 +814,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) reg |= IMX290_HREVERSE; if (imx290->vflip->val) reg |= IMX290_VREVERSE; - ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL); + ret = cci_write(imx290->regmap, IMX290_CTRL_07, reg, NULL); break; } @@ -902,7 +845,6 @@ static const char * const imx290_test_pattern_menu[] = { }; static void imx290_ctrl_update(struct imx290 *imx290, - const struct v4l2_mbus_framefmt *format, const struct imx290_mode *mode) { unsigned int hblank_min = mode->hmax_min - mode->width; @@ -1074,12 +1016,12 @@ static int imx290_start_streaming(struct imx290 *imx290, return ret; } - imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); + cci_write(imx290->regmap, IMX290_STANDBY, 0x00, &ret); msleep(30); /* Start streaming */ - return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); + return cci_write(imx290->regmap, IMX290_XMSTA, 0x00, &ret); } /* Stop streaming */ @@ -1087,11 +1029,11 @@ static int imx290_stop_streaming(struct imx290 *imx290) { int ret = 0; - imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); + cci_write(imx290->regmap, IMX290_STANDBY, 0x01, &ret); msleep(30); - return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); + return cci_write(imx290->regmap, IMX290_XMSTA, 0x01, &ret); } static int imx290_set_stream(struct v4l2_subdev *sd, int enable) @@ -1195,7 +1137,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx290->current_mode = mode; - imx290_ctrl_update(imx290, &fmt->format, mode); + imx290_ctrl_update(imx290, mode); imx290_exposure_update(imx290, mode); } @@ -1300,7 +1242,6 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290 *imx290) { struct i2c_client *client = to_i2c_client(imx290->dev); - const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; int ret; @@ -1335,8 +1276,7 @@ static int imx290_subdev_init(struct imx290 *imx290) } state = v4l2_subdev_lock_and_get_active_state(&imx290->sd); - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); - imx290_ctrl_update(imx290, format, imx290->current_mode); + imx290_ctrl_update(imx290, imx290->current_mode); v4l2_subdev_unlock_state(state); return 0; @@ -1417,11 +1357,6 @@ static const struct dev_pm_ops imx290_pm_ops = { * Probe & remove */ -static const struct regmap_config imx290_regmap_config = { - .reg_bits = 16, - .val_bits = 8, -}; - static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = { "vdda", "vddd", @@ -1588,7 +1523,7 @@ static int imx290_probe(struct i2c_client *client) return -ENOMEM; imx290->dev = dev; - imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); + imx290->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx290->regmap)) { dev_err(dev, "Unable to initialize I2C\n"); return -ENODEV; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index c0b9a5349668..3b4539b622b4 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index a2140848d0d6..52ebb096e107 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -2565,7 +2565,7 @@ static struct i2c_driver imx319_i2c_driver = { module_i2c_driver(imx319_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu "); -MODULE_AUTHOR("Rapolu, Chiranjeevi "); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Bingbu Cao "); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Sony imx319 sensor driver"); diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 6571a98b1e9e..9c79ae8dc842 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1851,7 +1851,7 @@ static struct i2c_driver imx355_i2c_driver = { module_i2c_driver(imx355_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu "); -MODULE_AUTHOR("Rapolu, Chiranjeevi "); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Bingbu Cao "); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Sony imx355 sensor driver"); diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 4b5d1ee9cc6b..3f00172df3cc 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 92e49d95363d..73460688c356 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1611,7 +1611,7 @@ static const struct dev_pm_ops isl7998x_pm_ops = { static struct i2c_driver isl7998x_i2c_driver = { .driver = { .name = "isl7998x", - .of_match_table = of_match_ptr(isl7998x_of_match), + .of_match_table = isl7998x_of_match, .pm = &isl7998x_pm_ops, }, .probe = isl7998x_probe, diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 88c58e0c49aa..20e7c7cf5eeb 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -161,11 +161,12 @@ struct max9286_source { }; struct max9286_asd { - struct v4l2_async_subdev base; + struct v4l2_async_connection base; struct max9286_source *source; }; -static inline struct max9286_asd *to_max9286_asd(struct v4l2_async_subdev *asd) +static inline struct max9286_asd * +to_max9286_asd(struct v4l2_async_connection *asd) { return container_of(asd, struct max9286_asd, base); } @@ -659,7 +660,7 @@ static int max9286_set_pixelrate(struct max9286_priv *priv) static int max9286_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); struct max9286_source *source = to_max9286_asd(asd)->source; @@ -721,7 +722,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier, static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); struct max9286_source *source = to_max9286_asd(asd)->source; @@ -745,7 +746,7 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) if (!priv->nsources) return 0; - v4l2_async_nf_init(&priv->notifier); + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); for_each_source(priv, source) { unsigned int i = to_index(priv, source); @@ -765,7 +766,7 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) priv->notifier.ops = &max9286_notify_ops; - ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); + ret = v4l2_async_nf_register(&priv->notifier); if (ret) { dev_err(dev, "Failed to register subdev_notifier"); v4l2_async_nf_cleanup(&priv->notifier); @@ -1051,7 +1052,6 @@ static const struct v4l2_ctrl_ops max9286_ctrl_ops = { static int max9286_v4l2_register(struct max9286_priv *priv) { struct device *dev = &priv->client->dev; - struct fwnode_handle *ep; int ret; int i; @@ -1093,25 +1093,14 @@ static int max9286_v4l2_register(struct max9286_priv *priv) if (ret) goto err_async; - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, - 0, 0); - if (!ep) { - dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); - ret = -ENOENT; - goto err_async; - } - priv->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { dev_err(dev, "Unable to register subdevice\n"); - goto err_put_node; + goto err_async; } return 0; -err_put_node: - fwnode_handle_put(ep); err_async: v4l2_ctrl_handler_free(&priv->ctrls); max9286_v4l2_notifier_unregister(priv); @@ -1714,7 +1703,7 @@ MODULE_DEVICE_TABLE(of, max9286_dt_ids); static struct i2c_driver max9286_i2c_driver = { .driver = { .name = "max9286", - .of_match_table = of_match_ptr(max9286_dt_ids), + .of_match_table = max9286_dt_ids, }, .probe = max9286_probe, .remove = max9286_remove, diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 2878d328fc01..df8d9c9e6a96 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1382,7 +1382,7 @@ MODULE_DEVICE_TABLE(i2c, mt9m111_id); static struct i2c_driver mt9m111_i2c_driver = { .driver = { .name = "mt9m111", - .of_match_table = of_match_ptr(mt9m111_of_match), + .of_match_table = mt9m111_of_match, }, .probe = mt9m111_probe, .remove = mt9m111_remove, diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index b5948759342e..365ce5684583 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -1121,6 +1121,6 @@ static struct i2c_driver og01a1b_i2c_driver = { module_i2c_driver(og01a1b_i2c_driver); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OG01A1B sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index de5bc19e715b..2b9e1b3a3bf4 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -992,7 +992,7 @@ static struct i2c_driver ov01a10_i2c_driver = { .pm = &ov01a10_pm_ops, .acpi_match_table = ACPI_PTR(ov01a10_acpi_ids), }, - .probe_new = ov01a10_probe, + .probe = ov01a10_probe, .remove = ov01a10_remove, }; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 77bcdcd0824c..637da4df6901 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -110,8 +110,6 @@ struct ov08x40_reg_list { /* Link frequency config */ struct ov08x40_link_freq_config { - u32 pixels_per_line; - /* registers for this link frequency */ struct ov08x40_reg_list reg_list; }; @@ -128,6 +126,9 @@ struct ov08x40_mode { u32 vts_def; u32 vts_min; + /* HTS */ + u32 hts; + /* Index of Link frequency config to be used */ u32 link_freq_index; /* Default register values */ @@ -2391,6 +2392,7 @@ static const struct ov08x40_mode supported_modes[] = { .height = 2416, .vts_def = OV08X40_VTS_30FPS, .vts_min = OV08X40_VTS_30FPS, + .hts = 640, .lanes = 4, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3856x2416_regs), @@ -2403,6 +2405,7 @@ static const struct ov08x40_mode supported_modes[] = { .height = 1208, .vts_def = OV08X40_VTS_BIN_30FPS, .vts_min = OV08X40_VTS_BIN_30FPS, + .hts = 720, .lanes = 4, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1928x1208_regs), @@ -2846,9 +2849,7 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, 1, vblank_def); __v4l2_ctrl_s_ctrl(ov08x->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - ov08x->cur_mode->width; + h_blank = ov08x->cur_mode->hts; __v4l2_ctrl_modify_range(ov08x->hblank, h_blank, h_blank, 1, h_blank); } @@ -3074,8 +3075,7 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) OV08X40_VTS_MAX - mode->height, 1, vblank_def); - hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - - mode->width; + hblank = ov08x->cur_mode->hts; ov08x->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); @@ -3320,6 +3320,6 @@ static struct i2c_driver ov08x40_i2c_driver = { module_i2c_driver(ov08x40_i2c_driver); MODULE_AUTHOR("Jason Chen "); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OV08X40 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 3db3e64fa3ff..35652b362347 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1814,7 +1814,7 @@ static struct i2c_driver ov13858_i2c_driver = { module_i2c_driver(ov13858_i2c_driver); MODULE_AUTHOR("Kan, Chris "); -MODULE_AUTHOR("Rapolu, Chiranjeevi "); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Omnivision ov13858 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 6110fb1e6bc6..dbc642c5995b 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -2,6 +2,9 @@ // Copyright (c) 2021 Intel Corporation. #include +#include +#include +#include #include #include #include @@ -573,6 +576,11 @@ struct ov13b10 { struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; + + struct clk *img_clk; + struct regulator *avdd; + struct gpio_desc *reset; + /* V4L2 Controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; @@ -1051,6 +1059,49 @@ static int ov13b10_identify_module(struct ov13b10 *ov13b) return 0; } +static int ov13b10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + gpiod_set_value_cansleep(ov13b10->reset, 1); + + if (ov13b10->avdd) + regulator_disable(ov13b10->avdd); + + clk_disable_unprepare(ov13b10->img_clk); + + return 0; +} + +static int ov13b10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + int ret; + + ret = clk_prepare_enable(ov13b10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + if (ov13b10->avdd) { + ret = regulator_enable(ov13b10->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(ov13b10->img_clk); + return ret; + } + } + + gpiod_set_value_cansleep(ov13b10->reset, 0); + /* 5ms to wait ready after XSHUTDN assert */ + usleep_range(5000, 5500); + + return 0; +} + static int ov13b10_start_streaming(struct ov13b10 *ov13b) { struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); @@ -1145,7 +1196,7 @@ err_unlock: return ret; } -static int __maybe_unused ov13b10_suspend(struct device *dev) +static int ov13b10_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13b10 *ov13b = to_ov13b10(sd); @@ -1153,26 +1204,35 @@ static int __maybe_unused ov13b10_suspend(struct device *dev) if (ov13b->streaming) ov13b10_stop_streaming(ov13b); + ov13b10_power_off(dev); + return 0; } -static int __maybe_unused ov13b10_resume(struct device *dev) +static int ov13b10_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13b10 *ov13b = to_ov13b10(sd); int ret; + ret = ov13b10_power_on(dev); + if (ret) + goto pm_fail; + if (ov13b->streaming) { ret = ov13b10_start_streaming(ov13b); if (ret) - goto error; + goto stop_streaming; } return 0; -error: +stop_streaming: ov13b10_stop_streaming(ov13b); + ov13b10_power_off(dev); +pm_fail: ov13b->streaming = false; + return ret; } @@ -1317,6 +1377,34 @@ static void ov13b10_free_controls(struct ov13b10 *ov13b) mutex_destroy(&ov13b->mutex); } +static int ov13b10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b = to_ov13b10(sd); + int ret; + + ov13b->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov13b->reset)) + return dev_err_probe(dev, PTR_ERR(ov13b->reset), + "failed to get reset gpio\n"); + + ov13b->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov13b->img_clk)) + return dev_err_probe(dev, PTR_ERR(ov13b->img_clk), + "failed to get imaging clock\n"); + + ov13b->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov13b->avdd)) { + ret = PTR_ERR(ov13b->avdd); + ov13b->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + return 0; +} + static int ov13b10_check_hwcfg(struct device *dev) { struct v4l2_fwnode_endpoint bus_cfg = { @@ -1331,6 +1419,10 @@ static int ov13b10_check_hwcfg(struct device *dev) if (!fwnode) return -ENXIO; + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EPROBE_DEFER; + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &ext_clk); if (ret) { @@ -1344,10 +1436,6 @@ static int ov13b10_check_hwcfg(struct device *dev) return -EINVAL; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) @@ -1407,13 +1495,23 @@ static int ov13b10_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); + ret = ov13b10_get_pm_resources(&client->dev); + if (ret) + return ret; + full_power = acpi_dev_state_d0(&client->dev); if (full_power) { + ov13b10_power_on(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + return ret; + } + /* Check module identity */ ret = ov13b10_identify_module(ov13b); if (ret) { dev_err(&client->dev, "failed to find sensor: %d\n", ret); - return ret; + goto error_power_off; } } @@ -1422,7 +1520,7 @@ static int ov13b10_probe(struct i2c_client *client) ret = ov13b10_init_controls(ov13b); if (ret) - return ret; + goto error_power_off; /* Initialize subdev */ ov13b->sd.internal_ops = &ov13b10_internal_ops; @@ -1462,6 +1560,9 @@ error_handler_free: ov13b10_free_controls(ov13b); dev_err(&client->dev, "%s failed:%d\n", __func__, ret); +error_power_off: + ov13b10_power_off(&client->dev); + return ret; } @@ -1477,13 +1578,13 @@ static void ov13b10_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); } -static const struct dev_pm_ops ov13b10_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend, + ov13b10_resume, NULL); #ifdef CONFIG_ACPI static const struct acpi_device_id ov13b10_acpi_ids[] = { {"OVTIDB10"}, + {"OVTI13B1"}, { /* sentinel */ } }; @@ -1493,7 +1594,7 @@ MODULE_DEVICE_TABLE(acpi, ov13b10_acpi_ids); static struct i2c_driver ov13b10_i2c_driver = { .driver = { .name = "ov13b10", - .pm = &ov13b10_pm_ops, + .pm = pm_ptr(&ov13b10_pm_ops), .acpi_match_table = ACPI_PTR(ov13b10_acpi_ids), }, .probe = ov13b10_probe, diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index ec801a81c2d0..bb6c9863a546 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1296,7 +1296,7 @@ MODULE_DEVICE_TABLE(of, ov2640_of_match); static struct i2c_driver ov2640_i2c_driver = { .driver = { .name = "ov2640", - .of_match_table = of_match_ptr(ov2640_of_match), + .of_match_table = ov2640_of_match, }, .probe = ov2640_probe, .remove = ov2640_remove, diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index d06e9fc37f77..72bab0ff8a36 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -10,61 +10,93 @@ * */ -#include #include #include #include +#include #include #include +#include #include -#include -#include +#include +#include #include +#include #include #include +#include #include -#define OV2680_XVCLK_VALUE 24000000 +#define OV2680_CHIP_ID 0x2680 -#define OV2680_CHIP_ID 0x2680 +#define OV2680_REG_STREAM_CTRL CCI_REG8(0x0100) +#define OV2680_REG_SOFT_RESET CCI_REG8(0x0103) -#define OV2680_REG_STREAM_CTRL 0x0100 -#define OV2680_REG_SOFT_RESET 0x0103 +#define OV2680_REG_CHIP_ID CCI_REG16(0x300a) +#define OV2680_REG_SC_CMMN_SUB_ID CCI_REG8(0x302a) +#define OV2680_REG_PLL_MULTIPLIER CCI_REG16(0x3081) -#define OV2680_REG_CHIP_ID_HIGH 0x300a -#define OV2680_REG_CHIP_ID_LOW 0x300b +#define OV2680_REG_EXPOSURE_PK CCI_REG24(0x3500) +#define OV2680_REG_R_MANUAL CCI_REG8(0x3503) +#define OV2680_REG_GAIN_PK CCI_REG16(0x350a) -#define OV2680_REG_R_MANUAL 0x3503 -#define OV2680_REG_GAIN_PK 0x350a -#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 -#define OV2680_REG_TIMING_HTS 0x380c -#define OV2680_REG_TIMING_VTS 0x380e -#define OV2680_REG_FORMAT1 0x3820 -#define OV2680_REG_FORMAT2 0x3821 +#define OV2680_REG_SENSOR_CTRL_0A CCI_REG8(0x370a) -#define OV2680_REG_ISP_CTRL00 0x5080 +#define OV2680_REG_HORIZONTAL_START CCI_REG16(0x3800) +#define OV2680_REG_VERTICAL_START CCI_REG16(0x3802) +#define OV2680_REG_HORIZONTAL_END CCI_REG16(0x3804) +#define OV2680_REG_VERTICAL_END CCI_REG16(0x3806) +#define OV2680_REG_HORIZONTAL_OUTPUT_SIZE CCI_REG16(0x3808) +#define OV2680_REG_VERTICAL_OUTPUT_SIZE CCI_REG16(0x380a) +#define OV2680_REG_TIMING_HTS CCI_REG16(0x380c) +#define OV2680_REG_TIMING_VTS CCI_REG16(0x380e) +#define OV2680_REG_ISP_X_WIN CCI_REG16(0x3810) +#define OV2680_REG_ISP_Y_WIN CCI_REG16(0x3812) +#define OV2680_REG_X_INC CCI_REG8(0x3814) +#define OV2680_REG_Y_INC CCI_REG8(0x3815) +#define OV2680_REG_FORMAT1 CCI_REG8(0x3820) +#define OV2680_REG_FORMAT2 CCI_REG8(0x3821) -#define OV2680_FRAME_RATE 30 +#define OV2680_REG_ISP_CTRL00 CCI_REG8(0x5080) -#define OV2680_REG_VALUE_8BIT 1 -#define OV2680_REG_VALUE_16BIT 2 -#define OV2680_REG_VALUE_24BIT 3 +#define OV2680_REG_X_WIN CCI_REG16(0x5704) +#define OV2680_REG_Y_WIN CCI_REG16(0x5706) -#define OV2680_WIDTH_MAX 1600 -#define OV2680_HEIGHT_MAX 1200 +#define OV2680_FRAME_RATE 30 -enum ov2680_mode_id { - OV2680_MODE_QUXGA_800_600, - OV2680_MODE_720P_1280_720, - OV2680_MODE_UXGA_1600_1200, - OV2680_MODE_MAX, -}; +#define OV2680_NATIVE_WIDTH 1616 +#define OV2680_NATIVE_HEIGHT 1216 +#define OV2680_NATIVE_START_LEFT 0 +#define OV2680_NATIVE_START_TOP 0 +#define OV2680_ACTIVE_WIDTH 1600 +#define OV2680_ACTIVE_HEIGHT 1200 +#define OV2680_ACTIVE_START_LEFT 8 +#define OV2680_ACTIVE_START_TOP 8 +#define OV2680_MIN_CROP_WIDTH 2 +#define OV2680_MIN_CROP_HEIGHT 2 -struct reg_value { - u16 reg_addr; - u8 val; -}; +/* Fixed pre-div of 1/2 */ +#define OV2680_PLL_PREDIV0 2 + +/* Pre-div configurable through reg 0x3080, left at its default of 0x02 : 1/2 */ +#define OV2680_PLL_PREDIV 2 + +/* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */ +#define OV2680_PIXELS_PER_LINE 1704 +#define OV2680_LINES_PER_FRAME 1294 + +/* If possible send 16 extra rows / lines to the ISP as padding */ +#define OV2680_END_MARGIN 16 + +/* Max exposure time is VTS - 8 */ +#define OV2680_INTEGRATION_TIME_MARGIN 8 + +#define OV2680_DEFAULT_WIDTH 800 +#define OV2680_DEFAULT_HEIGHT 600 + +/* For enum_frame_size() full-size + binned-/quarter-size */ +#define OV2680_FRAME_SIZES 2 static const char * const ov2680_supply_name[] = { "DOVDD", @@ -74,52 +106,74 @@ static const char * const ov2680_supply_name[] = { #define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_name) -struct ov2680_mode_info { - const char *name; - enum ov2680_mode_id id; - u32 width; - u32 height; - const struct reg_value *reg_data; - u32 reg_data_size; +enum { + OV2680_19_2_MHZ, + OV2680_24_MHZ, +}; + +static const unsigned long ov2680_xvclk_freqs[] = { + [OV2680_19_2_MHZ] = 19200000, + [OV2680_24_MHZ] = 24000000, +}; + +static const u8 ov2680_pll_multipliers[] = { + [OV2680_19_2_MHZ] = 69, + [OV2680_24_MHZ] = 55, }; struct ov2680_ctrls { struct v4l2_ctrl_handler handler; - struct { - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - }; - struct { - struct v4l2_ctrl *auto_gain; - struct v4l2_ctrl *gain; - }; - + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; +}; + +struct ov2680_mode { + struct v4l2_rect crop; + struct v4l2_mbus_framefmt fmt; + struct v4l2_fract frame_interval; + bool binning; + u16 h_start; + u16 v_start; + u16 h_end; + u16 v_end; + u16 h_output_size; + u16 v_output_size; + u16 hts; + u16 vts; }; struct ov2680_dev { - struct i2c_client *i2c_client; + struct device *dev; + struct regmap *regmap; struct v4l2_subdev sd; struct media_pad pad; struct clk *xvclk; u32 xvclk_freq; + u8 pll_mult; + s64 link_freq[1]; + u64 pixel_rate; struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; - struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; struct mutex lock; /* protect members */ - bool mode_pending_changes; - bool is_enabled; bool is_streaming; struct ov2680_ctrls ctrls; - struct v4l2_mbus_framefmt fmt; - struct v4l2_fract frame_interval; + struct ov2680_mode mode; +}; - const struct ov2680_mode_info *current_mode; +static const struct v4l2_rect ov2680_default_crop = { + .left = OV2680_ACTIVE_START_LEFT, + .top = OV2680_ACTIVE_START_TOP, + .width = OV2680_ACTIVE_WIDTH, + .height = OV2680_ACTIVE_HEIGHT, }; static const char * const test_pattern_menu[] = { @@ -137,45 +191,72 @@ static const int ov2680_hv_flip_bayer_order[] = { MEDIA_BUS_FMT_SRGGB10_1X10, }; -static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = { - {0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20}, - {0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac}, - {0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04}, - {0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00}, - {0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0}, -}; +static const struct reg_sequence ov2680_global_setting[] = { + /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */ + {0x3016, 0x1c}, -static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = { - {0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, - {0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05}, - {0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00}, -}; + /* R MANUAL set exposure and gain to manual (hw does not do auto) */ + {0x3503, 0x03}, -static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = { - {0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06}, - {0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06}, - {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00}, - {0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0}, - {0x4008, 0x00}, {0x4837, 0x18} -}; + /* Analog control register tweaks */ + {0x3603, 0x39}, /* Reset value 0x99 */ + {0x3604, 0x24}, /* Reset value 0x74 */ + {0x3621, 0x37}, /* Reset value 0x44 */ -static const struct ov2680_mode_info ov2680_mode_init_data = { - "mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600, - ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600), -}; + /* Sensor control register tweaks */ + {0x3701, 0x64}, /* Reset value 0x61 */ + {0x3705, 0x3c}, /* Reset value 0x21 */ + {0x370c, 0x50}, /* Reset value 0x10 */ + {0x370d, 0xc0}, /* Reset value 0x00 */ + {0x3718, 0x88}, /* Reset value 0x80 */ -static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = { - {"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, - 800, 600, ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)}, - {"mode_720p_1280_720", OV2680_MODE_720P_1280_720, - 1280, 720, ov2680_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)}, - {"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200, - 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200, - ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)}, + /* PSRAM tweaks */ + {0x3781, 0x80}, /* Reset value 0x00 */ + {0x3784, 0x0c}, /* Reset value 0x00, based on OV2680_R1A_AM10.ovt */ + {0x3789, 0x60}, /* Reset value 0x50 */ + + /* BLC CTRL00 0x01 -> 0x81 set avg_weight to 8 */ + {0x4000, 0x81}, + + /* Set black level compensation range to 0 - 3 (default 0 - 11) */ + {0x4008, 0x00}, + {0x4009, 0x03}, + + /* VFIFO R2 0x00 -> 0x02 set Frame reset enable */ + {0x4602, 0x02}, + + /* MIPI ctrl CLK PREPARE MIN change from 0x26 (38) -> 0x36 (54) */ + {0x481f, 0x36}, + + /* MIPI ctrl CLK LPX P MIN change from 0x32 (50) -> 0x36 (54) */ + {0x4825, 0x36}, + + /* R ISP CTRL2 0x20 -> 0x30, set sof_sel bit */ + {0x5002, 0x30}, + + /* + * Window CONTROL 0x00 -> 0x01, enable manual window control, + * this is necessary for full size flip and mirror support. + */ + {0x5708, 0x01}, + + /* + * DPC CTRL0 0x14 -> 0x3e, set enable_tail, enable_3x3_cluster + * and enable_general_tail bits based OV2680_R1A_AM10.ovt. + */ + {0x5780, 0x3e}, + + /* DPC MORE CONNECTION CASE THRE 0x0c (12) -> 0x02 (2) */ + {0x5788, 0x02}, + + /* DPC GAIN LIST1 0x0f (15) -> 0x08 (8) */ + {0x578e, 0x08}, + + /* DPC GAIN LIST2 0x3f (63) -> 0x0c (12) */ + {0x578f, 0x0c}, + + /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */ + {0x5792, 0x00}, }; static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) @@ -183,380 +264,276 @@ static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) return container_of(sd, struct ov2680_dev, sd); } -static struct device *ov2680_to_dev(struct ov2680_dev *sensor) -{ - return &sensor->i2c_client->dev; -} - static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) { return &container_of(ctrl->handler, struct ov2680_dev, ctrls.handler)->sd; } -static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 val) -{ - struct i2c_client *client = sensor->i2c_client; - u8 buf[6]; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - ret = i2c_master_send(client, buf, len + 2); - if (ret != len + 2) { - dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } - - return 0; -} - -#define ov2680_write_reg(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v) - -#define ov2680_write_reg16(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v) - -#define ov2680_write_reg24(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v) - -static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 *val) -{ - struct i2c_client *client = sensor->i2c_client; - struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; - int ret; - - if (len > 4) - return -EINVAL; - - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -#define ov2680_read_reg(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v) - -#define ov2680_read_reg16(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v) - -#define ov2680_read_reg24(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v) - -static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val) -{ - u32 readval; - int ret; - - ret = ov2680_read_reg(sensor, reg, &readval); - if (ret < 0) - return ret; - - readval &= ~mask; - val &= mask; - val |= readval; - - return ov2680_write_reg(sensor, reg, val); -} - -static int ov2680_load_regs(struct ov2680_dev *sensor, - const struct ov2680_mode_info *mode) -{ - const struct reg_value *regs = mode->reg_data; - unsigned int i; - int ret = 0; - u16 reg_addr; - u8 val; - - for (i = 0; i < mode->reg_data_size; ++i, ++regs) { - reg_addr = regs->reg_addr; - val = regs->val; - - ret = ov2680_write_reg(sensor, reg_addr, val); - if (ret) - break; - } - - return ret; -} - static void ov2680_power_up(struct ov2680_dev *sensor) { - if (!sensor->reset_gpio) + if (!sensor->pwdn_gpio) return; - gpiod_set_value(sensor->reset_gpio, 0); + gpiod_set_value(sensor->pwdn_gpio, 0); usleep_range(5000, 10000); } static void ov2680_power_down(struct ov2680_dev *sensor) { - if (!sensor->reset_gpio) + if (!sensor->pwdn_gpio) return; - gpiod_set_value(sensor->reset_gpio, 1); + gpiod_set_value(sensor->pwdn_gpio, 1); usleep_range(5000, 10000); } -static int ov2680_bayer_order(struct ov2680_dev *sensor) +static void ov2680_set_bayer_order(struct ov2680_dev *sensor, + struct v4l2_mbus_framefmt *fmt) +{ + int hv_flip = 0; + + if (sensor->ctrls.vflip && sensor->ctrls.vflip->val) + hv_flip += 1; + + if (sensor->ctrls.hflip && sensor->ctrls.hflip->val) + hv_flip += 2; + + fmt->code = ov2680_hv_flip_bayer_order[hv_flip]; +} + +static struct v4l2_mbus_framefmt * +__ov2680_get_pad_format(struct ov2680_dev *sensor, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&sensor->sd, state, pad); + + return &sensor->mode.fmt; +} + +static struct v4l2_rect * +__ov2680_get_pad_crop(struct ov2680_dev *sensor, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + + return &sensor->mode.crop; +} + +static void ov2680_fill_format(struct ov2680_dev *sensor, + struct v4l2_mbus_framefmt *fmt, + unsigned int width, unsigned int height) +{ + memset(fmt, 0, sizeof(*fmt)); + fmt->width = width; + fmt->height = height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + ov2680_set_bayer_order(sensor, fmt); +} + +static void ov2680_calc_mode(struct ov2680_dev *sensor) +{ + int width = sensor->mode.fmt.width; + int height = sensor->mode.fmt.height; + int orig_width = width; + int orig_height = height; + + if (width <= (sensor->mode.crop.width / 2) && + height <= (sensor->mode.crop.height / 2)) { + sensor->mode.binning = true; + width *= 2; + height *= 2; + } else { + sensor->mode.binning = false; + } + + sensor->mode.h_start = (sensor->mode.crop.left + + (sensor->mode.crop.width - width) / 2) & ~1; + sensor->mode.v_start = (sensor->mode.crop.top + + (sensor->mode.crop.height - height) / 2) & ~1; + sensor->mode.h_end = + min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, + OV2680_NATIVE_WIDTH - 1); + sensor->mode.v_end = + min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, + OV2680_NATIVE_HEIGHT - 1); + sensor->mode.h_output_size = orig_width; + sensor->mode.v_output_size = orig_height; + sensor->mode.hts = OV2680_PIXELS_PER_LINE; + sensor->mode.vts = OV2680_LINES_PER_FRAME; +} + +static int ov2680_set_mode(struct ov2680_dev *sensor) +{ + u8 sensor_ctrl_0a, inc, fmt1, fmt2; + int ret = 0; + + if (sensor->mode.binning) { + sensor_ctrl_0a = 0x23; + inc = 0x31; + fmt1 = 0xc2; + fmt2 = 0x01; + } else { + sensor_ctrl_0a = 0x21; + inc = 0x11; + fmt1 = 0xc0; + fmt2 = 0x00; + } + + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, + sensor_ctrl_0a, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, + sensor->mode.h_start, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, + sensor->mode.v_start, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, + sensor->mode.h_end, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, + sensor->mode.v_end, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE, + sensor->mode.h_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE, + sensor->mode.v_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, + sensor->mode.hts, &ret); + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, + sensor->mode.vts, &ret); + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret); + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret); + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret); + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret); + cci_write(sensor->regmap, OV2680_REG_X_WIN, + sensor->mode.h_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_Y_WIN, + sensor->mode.v_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret); + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret); + + return ret; +} + +static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val) { - u32 format1; - u32 format2; - u32 hv_flip; int ret; - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1); + if (sensor->is_streaming) + return -EBUSY; + + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT1, + BIT(2), val ? BIT(2) : 0, NULL); if (ret < 0) return ret; - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2); - if (ret < 0) - return ret; - - hv_flip = (format2 & BIT(2) << 1) | (format1 & BIT(2)); - - sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip]; - + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); return 0; } -static int ov2680_vflip_enable(struct ov2680_dev *sensor) +static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val) { int ret; - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2)); + if (sensor->is_streaming) + return -EBUSY; + + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT2, + BIT(2), val ? BIT(2) : 0, NULL); if (ret < 0) return ret; - return ov2680_bayer_order(sensor); -} - -static int ov2680_vflip_disable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_hflip_enable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); -} - -static int ov2680_hflip_disable(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); + return 0; } static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) { - int ret; + int ret = 0; if (!value) - return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0); + return cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + BIT(7), 0, NULL); - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1); - if (ret < 0) - return ret; + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + 0x03, value - 1, &ret); + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + BIT(7), BIT(7), &ret); - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); - if (ret < 0) - return ret; - - return 0; + return ret; } -static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain) +static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain) { - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 gain; - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1), - auto_gain ? 0 : BIT(1)); - if (ret < 0) - return ret; - - if (auto_gain || !ctrls->gain->is_new) - return 0; - - gain = ctrls->gain->val; - - ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain); - - return 0; + return cci_write(sensor->regmap, OV2680_REG_GAIN_PK, gain, NULL); } -static int ov2680_gain_get(struct ov2680_dev *sensor) +static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp) { - u32 gain; - int ret; - - ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain); - if (ret) - return ret; - - return gain; -} - -static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 exp; - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0), - auto_exp ? 0 : BIT(0)); - if (ret < 0) - return ret; - - if (auto_exp || !ctrls->exposure->is_new) - return 0; - - exp = (u32)ctrls->exposure->val; - exp <<= 4; - - return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp); -} - -static int ov2680_exposure_get(struct ov2680_dev *sensor) -{ - int ret; - u32 exp; - - ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp); - if (ret) - return ret; - - return exp >> 4; + return cci_write(sensor->regmap, OV2680_REG_EXPOSURE_PK, exp << 4, + NULL); } static int ov2680_stream_enable(struct ov2680_dev *sensor) { - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1); + int ret; + + ret = cci_write(sensor->regmap, OV2680_REG_PLL_MULTIPLIER, + sensor->pll_mult, NULL); + if (ret < 0) + return ret; + + ret = regmap_multi_reg_write(sensor->regmap, + ov2680_global_setting, + ARRAY_SIZE(ov2680_global_setting)); + if (ret < 0) + return ret; + + ret = ov2680_set_mode(sensor); + if (ret < 0) + return ret; + + /* Restore value of all ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret < 0) + return ret; + + return cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 1, NULL); } static int ov2680_stream_disable(struct ov2680_dev *sensor) { - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0); -} - -static int ov2680_mode_set(struct ov2680_dev *sensor) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - int ret; - - ret = ov2680_gain_set(sensor, false); - if (ret < 0) - return ret; - - ret = ov2680_exposure_set(sensor, false); - if (ret < 0) - return ret; - - ret = ov2680_load_regs(sensor, sensor->current_mode); - if (ret < 0) - return ret; - - if (ctrls->auto_gain->val) { - ret = ov2680_gain_set(sensor, true); - if (ret < 0) - return ret; - } - - if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) { - ret = ov2680_exposure_set(sensor, true); - if (ret < 0) - return ret; - } - - sensor->mode_pending_changes = false; - - return 0; -} - -static int ov2680_mode_restore(struct ov2680_dev *sensor) -{ - int ret; - - ret = ov2680_load_regs(sensor, &ov2680_mode_init_data); - if (ret < 0) - return ret; - - return ov2680_mode_set(sensor); + return cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 0, NULL); } static int ov2680_power_off(struct ov2680_dev *sensor) { - if (!sensor->is_enabled) - return 0; - clk_disable_unprepare(sensor->xvclk); ov2680_power_down(sensor); regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); - sensor->is_enabled = false; - return 0; } static int ov2680_power_on(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); int ret; - if (sensor->is_enabled) - return 0; - ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, sensor->supplies); if (ret < 0) { - dev_err(dev, "failed to enable regulators: %d\n", ret); + dev_err(sensor->dev, "failed to enable regulators: %d\n", ret); return ret; } - if (!sensor->reset_gpio) { - ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01); + if (!sensor->pwdn_gpio) { + ret = cci_write(sensor->regmap, OV2680_REG_SOFT_RESET, 0x01, + NULL); if (ret != 0) { - dev_err(dev, "sensor soft reset failed\n"); - return ret; + dev_err(sensor->dev, "sensor soft reset failed\n"); + goto err_disable_regulators; } usleep_range(1000, 2000); } else { @@ -566,40 +543,12 @@ static int ov2680_power_on(struct ov2680_dev *sensor) ret = clk_prepare_enable(sensor->xvclk); if (ret < 0) - return ret; - - sensor->is_enabled = true; - - /* Set clock lane into LP-11 state */ - ov2680_stream_enable(sensor); - usleep_range(1000, 2000); - ov2680_stream_disable(sensor); + goto err_disable_regulators; return 0; -} - -static int ov2680_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - if (on) - ret = ov2680_power_on(sensor); - else - ret = ov2680_power_off(sensor); - - mutex_unlock(&sensor->lock); - - if (on && ret == 0) { - ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret < 0) - return ret; - - ret = ov2680_mode_restore(sensor); - } +err_disable_regulators: + regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); return ret; } @@ -609,7 +558,7 @@ static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, struct ov2680_dev *sensor = to_ov2680_dev(sd); mutex_lock(&sensor->lock); - fi->interval = sensor->frame_interval; + fi->interval = sensor->mode.frame_interval; mutex_unlock(&sensor->lock); return 0; @@ -625,16 +574,20 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) if (sensor->is_streaming == !!enable) goto unlock; - if (enable && sensor->mode_pending_changes) { - ret = ov2680_mode_set(sensor); + if (enable) { + ret = pm_runtime_resume_and_get(sensor->sd.dev); if (ret < 0) goto unlock; - } - if (enable) ret = ov2680_stream_enable(sensor); - else + if (ret < 0) { + pm_runtime_put(sensor->sd.dev); + goto unlock; + } + } else { ret = ov2680_stream_disable(sensor); + pm_runtime_put(sensor->sd.dev); + } sensor->is_streaming = !!enable; @@ -650,10 +603,10 @@ static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, { struct ov2680_dev *sensor = to_ov2680_dev(sd); - if (code->pad != 0 || code->index != 0) + if (code->index != 0) return -EINVAL; - code->code = sensor->fmt.code; + code->code = sensor->mode.fmt.code; return 0; } @@ -663,31 +616,16 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = NULL; - int ret = 0; + struct v4l2_mbus_framefmt *fmt; - if (format->pad != 0) - return -EINVAL; + fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, + format->which); mutex_lock(&sensor->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); -#else - ret = -EINVAL; -#endif - } else { - fmt = &sensor->fmt; - } - - if (fmt) - format->format = *fmt; - + format->format = *fmt; mutex_unlock(&sensor->lock); - return ret; + return 0; } static int ov2680_set_fmt(struct v4l2_subdev *sd, @@ -695,15 +633,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = &format->format; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *try_fmt; -#endif - const struct ov2680_mode_info *mode; + const struct v4l2_rect *crop; + unsigned int width, height; int ret = 0; - if (format->pad != 0) - return -EINVAL; + crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, + format->which); + + /* Limit set_fmt max size to crop width / height */ + width = clamp_val(ALIGN(format->format.width, 2), + OV2680_MIN_CROP_WIDTH, crop->width); + height = clamp_val(ALIGN(format->format.height, 2), + OV2680_MIN_CROP_HEIGHT, crop->height); + + ov2680_fill_format(sensor, &format->format, width, height); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + *try_fmt = format->format; + return 0; + } mutex_lock(&sensor->lock); @@ -712,30 +662,8 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, goto unlock; } - mode = v4l2_find_nearest_size(ov2680_mode_data, - ARRAY_SIZE(ov2680_mode_data), width, - height, fmt->width, fmt->height); - if (!mode) { - ret = -EINVAL; - goto unlock; - } - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - format->format = *try_fmt; -#endif - goto unlock; - } - - fmt->width = mode->width; - fmt->height = mode->height; - fmt->code = sensor->fmt.code; - fmt->colorspace = sensor->fmt.colorspace; - - sensor->current_mode = mode; - sensor->fmt = format->format; - sensor->mode_pending_changes = true; + sensor->mode.fmt = format->format; + ov2680_calc_mode(sensor); unlock: mutex_unlock(&sensor->lock); @@ -743,81 +671,159 @@ unlock: return ret; } +static int ov2680_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&sensor->lock); + sel->r = *__ov2680_get_pad_crop(sensor, state, sel->pad, + sel->which); + mutex_unlock(&sensor->lock); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV2680_NATIVE_WIDTH; + sel->r.height = OV2680_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = ov2680_default_crop; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov2680_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * Clamp the boundaries of the crop rectangle to the size of the sensor + * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't + * disrupted. + */ + rect.left = clamp_val(ALIGN(sel->r.left, 2), + OV2680_NATIVE_START_LEFT, OV2680_NATIVE_WIDTH); + rect.top = clamp_val(ALIGN(sel->r.top, 2), + OV2680_NATIVE_START_TOP, OV2680_NATIVE_HEIGHT); + rect.width = clamp_val(ALIGN(sel->r.width, 2), + OV2680_MIN_CROP_WIDTH, OV2680_NATIVE_WIDTH); + rect.height = clamp_val(ALIGN(sel->r.height, 2), + OV2680_MIN_CROP_HEIGHT, OV2680_NATIVE_HEIGHT); + + /* Make sure the crop rectangle isn't outside the bounds of the array */ + rect.width = min_t(unsigned int, rect.width, + OV2680_NATIVE_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + OV2680_NATIVE_HEIGHT - rect.top); + + crop = __ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); + + mutex_lock(&sensor->lock); + if (rect.width != crop->width || rect.height != crop->height) { + /* + * Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __ov2680_get_pad_format(sensor, state, sel->pad, + sel->which); + format->width = rect.width; + format->height = rect.height; + } + + *crop = rect; + mutex_unlock(&sensor->lock); + + sel->r = rect; + + return 0; +} + static int ov2680_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - struct v4l2_subdev_format fmt = { - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .width = 800, - .height = 600, - } - }; + struct ov2680_dev *sensor = to_ov2680_dev(sd); - return ov2680_set_fmt(sd, sd_state, &fmt); + sd_state->pads[0].try_crop = ov2680_default_crop; + + ov2680_fill_format(sensor, &sd_state->pads[0].try_fmt, + OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); + return 0; } static int ov2680_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - int index = fse->index; + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_rect *crop; - if (index >= OV2680_MODE_MAX || index < 0) + if (fse->index >= OV2680_FRAME_SIZES) return -EINVAL; - fse->min_width = ov2680_mode_data[index].width; - fse->min_height = ov2680_mode_data[index].height; - fse->max_width = ov2680_mode_data[index].width; - fse->max_height = ov2680_mode_data[index].height; + crop = __ov2680_get_pad_crop(sensor, sd_state, fse->pad, fse->which); + if (!crop) + return -EINVAL; + + fse->min_width = crop->width / (fse->index + 1); + fse->min_height = crop->height / (fse->index + 1); + fse->max_width = fse->min_width; + fse->max_height = fse->min_height; return 0; } +static bool ov2680_valid_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct v4l2_subdev_frame_size_enum fse = { + .pad = fie->pad, + .which = fie->which, + }; + int i; + + for (i = 0; i < OV2680_FRAME_SIZES; i++) { + fse.index = i; + + if (ov2680_enum_frame_size(sd, sd_state, &fse)) + return false; + + if (fie->width == fse.min_width && + fie->height == fse.min_height) + return true; + } + + return false; +} + static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval_enum *fie) { - struct v4l2_fract tpf; + struct ov2680_dev *sensor = to_ov2680_dev(sd); - if (fie->index >= OV2680_MODE_MAX || fie->width > OV2680_WIDTH_MAX || - fie->height > OV2680_HEIGHT_MAX || - fie->which > V4L2_SUBDEV_FORMAT_ACTIVE) + /* Only 1 framerate */ + if (fie->index || !ov2680_valid_frame_size(sd, sd_state, fie)) return -EINVAL; - tpf.denominator = OV2680_FRAME_RATE; - tpf.numerator = 1; - - fie->interval = tpf; - - return 0; -} - -static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; - int val; - - if (!sensor->is_enabled) - return 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - val = ov2680_gain_get(sensor); - if (val < 0) - return val; - ctrls->gain->val = val; - break; - case V4L2_CID_EXPOSURE: - val = ov2680_exposure_get(sensor); - if (val < 0) - return val; - ctrls->exposure->val = val; - break; - } + fie->interval = sensor->mode.frame_interval; return 0; } @@ -826,52 +832,43 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; + int ret; - if (!sensor->is_enabled) + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); return 0; + } switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - return ov2680_gain_set(sensor, !!ctrl->val); - case V4L2_CID_GAIN: - return ov2680_gain_set(sensor, !!ctrls->auto_gain->val); - case V4L2_CID_EXPOSURE_AUTO: - return ov2680_exposure_set(sensor, !!ctrl->val); + case V4L2_CID_ANALOGUE_GAIN: + ret = ov2680_gain_set(sensor, ctrl->val); + break; case V4L2_CID_EXPOSURE: - return ov2680_exposure_set(sensor, !!ctrls->auto_exp->val); + ret = ov2680_exposure_set(sensor, ctrl->val); + break; case V4L2_CID_VFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_vflip_enable(sensor); - else - return ov2680_vflip_disable(sensor); + ret = ov2680_set_vflip(sensor, ctrl->val); + break; case V4L2_CID_HFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_hflip_enable(sensor); - else - return ov2680_hflip_disable(sensor); + ret = ov2680_set_hflip(sensor, ctrl->val); + break; case V4L2_CID_TEST_PATTERN: - return ov2680_test_pattern_set(sensor, ctrl->val); + ret = ov2680_test_pattern_set(sensor, ctrl->val); + break; default: + ret = -EINVAL; break; } - return -EINVAL; + pm_runtime_put(sensor->sd.dev); + return ret; } static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { - .g_volatile_ctrl = ov2680_g_volatile_ctrl, .s_ctrl = ov2680_s_ctrl, }; -static const struct v4l2_subdev_core_ops ov2680_core_ops = { - .s_power = ov2680_s_power, -}; - static const struct v4l2_subdev_video_ops ov2680_video_ops = { .g_frame_interval = ov2680_s_g_frame_interval, .s_frame_interval = ov2680_s_g_frame_interval, @@ -881,54 +878,45 @@ static const struct v4l2_subdev_video_ops ov2680_video_ops = { static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, - .get_fmt = ov2680_get_fmt, - .set_fmt = ov2680_set_fmt, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, + .get_fmt = ov2680_get_fmt, + .set_fmt = ov2680_set_fmt, + .get_selection = ov2680_get_selection, + .set_selection = ov2680_set_selection, }; static const struct v4l2_subdev_ops ov2680_subdev_ops = { - .core = &ov2680_core_ops, .video = &ov2680_video_ops, .pad = &ov2680_pad_ops, }; static int ov2680_mode_init(struct ov2680_dev *sensor) { - const struct ov2680_mode_info *init_mode; - /* set initial mode */ - sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; - sensor->fmt.width = 800; - sensor->fmt.height = 600; - sensor->fmt.field = V4L2_FIELD_NONE; - sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB; + sensor->mode.crop = ov2680_default_crop; + ov2680_fill_format(sensor, &sensor->mode.fmt, + OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); + ov2680_calc_mode(sensor); - sensor->frame_interval.denominator = OV2680_FRAME_RATE; - sensor->frame_interval.numerator = 1; - - init_mode = &ov2680_mode_init_data; - - sensor->current_mode = init_mode; - - sensor->mode_pending_changes = true; + sensor->mode.frame_interval.denominator = OV2680_FRAME_RATE; + sensor->mode.frame_interval.numerator = 1; return 0; } static int ov2680_v4l2_register(struct ov2680_dev *sensor) { + struct i2c_client *client = to_i2c_client(sensor->dev); const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; struct ov2680_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN; int ret = 0; - v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client, - &ov2680_subdev_ops); + v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -936,7 +924,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) if (ret < 0) return ret; - v4l2_ctrl_handler_init(hdl, 7); + v4l2_ctrl_handler_init(hdl, 5); hdl->lock = &sensor->lock; @@ -948,30 +936,26 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 32767, 1, 0); + 0, exp_max, 1, exp_max); - ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, - 0, 1, 1, 1); - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 1023, 1, 250); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ, + 0, 0, sensor->link_freq); + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, + 0, sensor->pixel_rate, + 1, sensor->pixel_rate); if (hdl->error) { ret = hdl->error; goto cleanup_entity; } - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = hdl; @@ -995,61 +979,153 @@ static int ov2680_get_regulators(struct ov2680_dev *sensor) for (i = 0; i < OV2680_NUM_SUPPLIES; i++) sensor->supplies[i].supply = ov2680_supply_name[i]; - return devm_regulator_bulk_get(&sensor->i2c_client->dev, - OV2680_NUM_SUPPLIES, - sensor->supplies); + return devm_regulator_bulk_get(sensor->dev, + OV2680_NUM_SUPPLIES, sensor->supplies); } static int ov2680_check_id(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); - u32 chip_id; - int ret; + u64 chip_id, rev; + int ret = 0; - ov2680_power_on(sensor); - - ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id); + cci_read(sensor->regmap, OV2680_REG_CHIP_ID, &chip_id, &ret); + cci_read(sensor->regmap, OV2680_REG_SC_CMMN_SUB_ID, &rev, &ret); if (ret < 0) { - dev_err(dev, "failed to read chip id high\n"); - return -ENODEV; + dev_err(sensor->dev, "failed to read chip id\n"); + return ret; } if (chip_id != OV2680_CHIP_ID) { - dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n", + dev_err(sensor->dev, "chip id: 0x%04llx does not match expected 0x%04x\n", chip_id, OV2680_CHIP_ID); return -ENODEV; } + dev_info(sensor->dev, "sensor_revision id = 0x%llx, rev= %lld\n", + chip_id, rev & 0x0f); + return 0; } static int ov2680_parse_dt(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); - int ret; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = sensor->dev; + struct fwnode_handle *ep_fwnode; + struct gpio_desc *gpio; + unsigned int rate = 0; + int i, ret; - sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(sensor->reset_gpio); + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!ep_fwnode) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg); + fwnode_handle_put(ep_fwnode); + if (ret) + return ret; + + /* + * The pin we want is named XSHUTDN in the datasheet. Linux sensor + * drivers have standardized on using "powerdown" as con-id name + * for powerdown or shutdown pins. Older DTB files use "reset", + * so fallback to that if there is no "powerdown" pin. + */ + gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (!gpio) + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + + ret = PTR_ERR_OR_ZERO(gpio); if (ret < 0) { dev_dbg(dev, "error while getting reset gpio: %d\n", ret); - return ret; + goto out_free_bus_cfg; } - sensor->xvclk = devm_clk_get(dev, "xvclk"); + sensor->pwdn_gpio = gpio; + + sensor->xvclk = devm_clk_get_optional(dev, "xvclk"); if (IS_ERR(sensor->xvclk)) { - dev_err(dev, "xvclk clock missing or invalid\n"); - return PTR_ERR(sensor->xvclk); + ret = dev_err_probe(dev, PTR_ERR(sensor->xvclk), + "xvclk clock missing or invalid\n"); + goto out_free_bus_cfg; } - sensor->xvclk_freq = clk_get_rate(sensor->xvclk); - if (sensor->xvclk_freq != OV2680_XVCLK_VALUE) { - dev_err(dev, "wrong xvclk frequency %d HZ, expected: %d Hz\n", - sensor->xvclk_freq, OV2680_XVCLK_VALUE); - return -EINVAL; + /* + * We could have either a 24MHz or 19.2MHz clock rate from either DT or + * ACPI... but we also need to support the weird IPU3 case which will + * have an external clock AND a clock-frequency property. Check for the + * clock-frequency property and if found, set that rate if we managed + * to acquire a clock. This should cover the ACPI case. If the system + * uses devicetree then the configured rate should already be set, so + * we can just read it. + */ + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &rate); + if (ret && !sensor->xvclk) { + dev_err_probe(dev, ret, "invalid clock config\n"); + goto out_free_bus_cfg; } - return 0; + if (!ret && sensor->xvclk) { + ret = clk_set_rate(sensor->xvclk, rate); + if (ret) { + dev_err_probe(dev, ret, "failed to set clock rate\n"); + goto out_free_bus_cfg; + } + } + + sensor->xvclk_freq = rate ?: clk_get_rate(sensor->xvclk); + + for (i = 0; i < ARRAY_SIZE(ov2680_xvclk_freqs); i++) { + if (sensor->xvclk_freq == ov2680_xvclk_freqs[i]) + break; + } + + if (i == ARRAY_SIZE(ov2680_xvclk_freqs)) { + ret = dev_err_probe(dev, -EINVAL, + "unsupported xvclk frequency %d Hz\n", + sensor->xvclk_freq); + goto out_free_bus_cfg; + } + + sensor->pll_mult = ov2680_pll_multipliers[i]; + + sensor->link_freq[0] = sensor->xvclk_freq / OV2680_PLL_PREDIV0 / + OV2680_PLL_PREDIV * sensor->pll_mult; + + /* CSI-2 is double data rate, bus-format is 10 bpp */ + sensor->pixel_rate = sensor->link_freq[0] * 2; + do_div(sensor->pixel_rate, 10); + + /* Verify bus cfg */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) { + ret = dev_err_probe(dev, -EINVAL, + "only a 1-lane CSI2 config is supported"); + goto out_free_bus_cfg; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == sensor->link_freq[0]) + break; + + if (bus_cfg.nr_of_link_frequencies == 0 || + bus_cfg.nr_of_link_frequencies == i) { + ret = dev_err_probe(dev, -EINVAL, + "supported link freq %lld not found\n", + sensor->link_freq[0]); + goto out_free_bus_cfg; + } + +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; } static int ov2680_probe(struct i2c_client *client) @@ -1062,11 +1138,15 @@ static int ov2680_probe(struct i2c_client *client) if (!sensor) return -ENOMEM; - sensor->i2c_client = client; + sensor->dev = &client->dev; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return PTR_ERR(sensor->regmap); ret = ov2680_parse_dt(sensor); if (ret < 0) - return -EINVAL; + return ret; ret = ov2680_mode_init(sensor); if (ret < 0) @@ -1080,18 +1160,37 @@ static int ov2680_probe(struct i2c_client *client) mutex_init(&sensor->lock); - ret = ov2680_check_id(sensor); + /* + * Power up and verify the chip now, so that if runtime pm is + * disabled the chip is left on and streaming will work. + */ + ret = ov2680_power_on(sensor); if (ret < 0) goto lock_destroy; + ret = ov2680_check_id(sensor); + if (ret < 0) + goto err_powerdown; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + ret = ov2680_v4l2_register(sensor); if (ret < 0) - goto lock_destroy; + goto err_pm_runtime; - dev_info(dev, "ov2680 init correctly\n"); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return 0; +err_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); +err_powerdown: + ov2680_power_off(sensor); lock_destroy: dev_err(dev, "ov2680 init fail: %d\n", ret); mutex_destroy(&sensor->lock); @@ -1108,9 +1207,18 @@ static void ov2680_remove(struct i2c_client *client) mutex_destroy(&sensor->lock); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov2680_power_off(sensor); + pm_runtime_set_suspended(&client->dev); } -static int __maybe_unused ov2680_suspend(struct device *dev) +static int ov2680_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -1118,15 +1226,19 @@ static int __maybe_unused ov2680_suspend(struct device *dev) if (sensor->is_streaming) ov2680_stream_disable(sensor); - return 0; + return ov2680_power_off(sensor); } -static int __maybe_unused ov2680_resume(struct device *dev) +static int ov2680_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); int ret; + ret = ov2680_power_on(sensor); + if (ret < 0) + goto stream_disable; + if (sensor->is_streaming) { ret = ov2680_stream_enable(sensor); if (ret < 0) @@ -1142,9 +1254,8 @@ stream_disable: return ret; } -static const struct dev_pm_ops ov2680_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ov2680_pm_ops, ov2680_suspend, ov2680_resume, + NULL); static const struct of_device_id ov2680_dt_ids[] = { { .compatible = "ovti,ov2680" }, @@ -1152,11 +1263,18 @@ static const struct of_device_id ov2680_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, ov2680_dt_ids); +static const struct acpi_device_id ov2680_acpi_ids[] = { + { "OVTI2680" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, ov2680_acpi_ids); + static struct i2c_driver ov2680_i2c_driver = { .driver = { .name = "ov2680", - .pm = &ov2680_pm_ops, - .of_match_table = of_match_ptr(ov2680_dt_ids), + .pm = pm_sleep_ptr(&ov2680_pm_ops), + .of_match_table = ov2680_dt_ids, + .acpi_match_table = ov2680_acpi_ids, }, .probe = ov2680_probe, .remove = ov2680_remove, diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 158d934733c3..41d4f85470fd 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1223,7 +1223,7 @@ static struct i2c_driver ov2740_i2c_driver = { module_i2c_driver(ov2740_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu "); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_AUTHOR("Bingbu Cao "); MODULE_DESCRIPTION("OmniVision OV2740 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 36b509714c8c..5fe85aa2d2ec 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -568,9 +568,7 @@ static const struct reg_value ov5640_init_setting[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, - {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0}, - {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x4837, 0x0a, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, @@ -634,7 +632,8 @@ static const struct reg_value ov5640_setting_low_res[] = { {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; static const struct reg_value ov5640_setting_720P_1280_720[] = { @@ -2453,16 +2452,13 @@ static void ov5640_power(struct ov5640_dev *sensor, bool enable) static void ov5640_powerup_sequence(struct ov5640_dev *sensor) { if (sensor->pwdn_gpio) { - gpiod_set_value_cansleep(sensor->reset_gpio, 0); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); /* camera power cycle */ ov5640_power(sensor, false); - usleep_range(5000, 10000); + usleep_range(5000, 10000); /* t2 */ ov5640_power(sensor, true); - usleep_range(5000, 10000); - - gpiod_set_value_cansleep(sensor->reset_gpio, 1); - usleep_range(1000, 2000); + usleep_range(1000, 2000); /* t3 */ gpiod_set_value_cansleep(sensor->reset_gpio, 0); } else { @@ -2470,7 +2466,7 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor) ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, OV5640_REG_SYS_CTRL0_SW_RST); } - usleep_range(20000, 25000); + usleep_range(20000, 25000); /* t4 */ /* * software standby: allows registers programming; @@ -2543,9 +2539,9 @@ static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on) * "ov5640_set_stream_mipi()") * [4] = 0 : Power up MIPI HS Tx * [3] = 0 : Power up MIPI LS Rx - * [2] = 0 : MIPI interface disabled + * [2] = 1 : MIPI interface enabled */ - ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x40); + ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x44); if (ret) return ret; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index d722348b938b..29e773a997dd 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2860,7 +2860,7 @@ static struct i2c_driver ov5670_i2c_driver = { module_i2c_driver(ov5670_i2c_driver); -MODULE_AUTHOR("Rapolu, Chiranjeevi "); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Omnivision ov5670 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 700c4b69846f..d5a2a5f82312 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1442,6 +1442,6 @@ static struct i2c_driver ov5675_i2c_driver = { module_i2c_driver(ov5675_i2c_driver); -MODULE_AUTHOR("Shawn Tu "); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OV5675 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 7f9212cce239..488ee6d9d301 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -12,7 +12,6 @@ * Jake Day */ -#include #include #include #include @@ -23,36 +22,32 @@ #include #include #include + +#include #include #include #include -#define OV5693_REG_8BIT(n) ((1 << 16) | (n)) -#define OV5693_REG_16BIT(n) ((2 << 16) | (n)) -#define OV5693_REG_24BIT(n) ((3 << 16) | (n)) -#define OV5693_REG_SIZE_SHIFT 16 -#define OV5693_REG_ADDR_MASK 0xffff - /* System Control */ -#define OV5693_SW_RESET_REG OV5693_REG_8BIT(0x0103) -#define OV5693_SW_STREAM_REG OV5693_REG_8BIT(0x0100) +#define OV5693_SW_RESET_REG CCI_REG8(0x0103) +#define OV5693_SW_STREAM_REG CCI_REG8(0x0100) #define OV5693_START_STREAMING 0x01 #define OV5693_STOP_STREAMING 0x00 #define OV5693_SW_RESET 0x01 -#define OV5693_REG_CHIP_ID OV5693_REG_16BIT(0x300a) +#define OV5693_REG_CHIP_ID CCI_REG16(0x300a) /* Yes, this is right. The datasheet for the OV5693 gives its ID as 0x5690 */ #define OV5693_CHIP_ID 0x5690 /* Exposure */ -#define OV5693_EXPOSURE_CTRL_REG OV5693_REG_24BIT(0x3500) +#define OV5693_EXPOSURE_CTRL_REG CCI_REG24(0x3500) #define OV5693_EXPOSURE_CTRL_MASK GENMASK(19, 4) #define OV5693_INTEGRATION_TIME_MARGIN 8 #define OV5693_EXPOSURE_MIN 1 #define OV5693_EXPOSURE_STEP 1 /* Analogue Gain */ -#define OV5693_GAIN_CTRL_REG OV5693_REG_16BIT(0x350a) +#define OV5693_GAIN_CTRL_REG CCI_REG16(0x350a) #define OV5693_GAIN_CTRL_MASK GENMASK(10, 4) #define OV5693_GAIN_MIN 1 #define OV5693_GAIN_MAX 127 @@ -60,9 +55,9 @@ #define OV5693_GAIN_STEP 1 /* Digital Gain */ -#define OV5693_MWB_RED_GAIN_REG OV5693_REG_16BIT(0x3400) -#define OV5693_MWB_GREEN_GAIN_REG OV5693_REG_16BIT(0x3402) -#define OV5693_MWB_BLUE_GAIN_REG OV5693_REG_16BIT(0x3404) +#define OV5693_MWB_RED_GAIN_REG CCI_REG16(0x3400) +#define OV5693_MWB_GREEN_GAIN_REG CCI_REG16(0x3402) +#define OV5693_MWB_BLUE_GAIN_REG CCI_REG16(0x3404) #define OV5693_MWB_GAIN_MASK GENMASK(11, 0) #define OV5693_MWB_GAIN_MAX 0x0fff #define OV5693_DIGITAL_GAIN_MIN 1 @@ -71,36 +66,36 @@ #define OV5693_DIGITAL_GAIN_STEP 1 /* Timing and Format */ -#define OV5693_CROP_START_X_REG OV5693_REG_16BIT(0x3800) -#define OV5693_CROP_START_Y_REG OV5693_REG_16BIT(0x3802) -#define OV5693_CROP_END_X_REG OV5693_REG_16BIT(0x3804) -#define OV5693_CROP_END_Y_REG OV5693_REG_16BIT(0x3806) -#define OV5693_OUTPUT_SIZE_X_REG OV5693_REG_16BIT(0x3808) -#define OV5693_OUTPUT_SIZE_Y_REG OV5693_REG_16BIT(0x380a) +#define OV5693_CROP_START_X_REG CCI_REG16(0x3800) +#define OV5693_CROP_START_Y_REG CCI_REG16(0x3802) +#define OV5693_CROP_END_X_REG CCI_REG16(0x3804) +#define OV5693_CROP_END_Y_REG CCI_REG16(0x3806) +#define OV5693_OUTPUT_SIZE_X_REG CCI_REG16(0x3808) +#define OV5693_OUTPUT_SIZE_Y_REG CCI_REG16(0x380a) -#define OV5693_TIMING_HTS_REG OV5693_REG_16BIT(0x380c) +#define OV5693_TIMING_HTS_REG CCI_REG16(0x380c) #define OV5693_FIXED_PPL 2688U -#define OV5693_TIMING_VTS_REG OV5693_REG_16BIT(0x380e) +#define OV5693_TIMING_VTS_REG CCI_REG16(0x380e) #define OV5693_TIMING_MAX_VTS 0xffff #define OV5693_TIMING_MIN_VTS 0x04 -#define OV5693_OFFSET_START_X_REG OV5693_REG_16BIT(0x3810) -#define OV5693_OFFSET_START_Y_REG OV5693_REG_16BIT(0x3812) +#define OV5693_OFFSET_START_X_REG CCI_REG16(0x3810) +#define OV5693_OFFSET_START_Y_REG CCI_REG16(0x3812) -#define OV5693_SUB_INC_X_REG OV5693_REG_8BIT(0x3814) -#define OV5693_SUB_INC_Y_REG OV5693_REG_8BIT(0x3815) +#define OV5693_SUB_INC_X_REG CCI_REG8(0x3814) +#define OV5693_SUB_INC_Y_REG CCI_REG8(0x3815) -#define OV5693_FORMAT1_REG OV5693_REG_8BIT(0x3820) +#define OV5693_FORMAT1_REG CCI_REG8(0x3820) #define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(6) #define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) #define OV5693_FORMAT1_VBIN_EN BIT(0) -#define OV5693_FORMAT2_REG OV5693_REG_8BIT(0x3821) +#define OV5693_FORMAT2_REG CCI_REG8(0x3821) #define OV5693_FORMAT2_HDR_EN BIT(7) #define OV5693_FORMAT2_FLIP_HORZ_ISP_EN BIT(2) #define OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) #define OV5693_FORMAT2_HBIN_EN BIT(0) -#define OV5693_ISP_CTRL2_REG OV5693_REG_8BIT(0x5002) +#define OV5693_ISP_CTRL2_REG CCI_REG8(0x5002) #define OV5693_ISP_SCALE_ENABLE BIT(7) /* Pixel Array */ @@ -116,7 +111,7 @@ #define OV5693_MIN_CROP_HEIGHT 2 /* Test Pattern */ -#define OV5693_TEST_PATTERN_REG OV5693_REG_8BIT(0x5e00) +#define OV5693_TEST_PATTERN_REG CCI_REG8(0x5e00) #define OV5693_TEST_PATTERN_ENABLE BIT(7) #define OV5693_TEST_PATTERN_ROLLING BIT(6) #define OV5693_TEST_PATTERN_RANDOM 0x01 @@ -137,19 +132,9 @@ static const char * const ov5693_supply_names[] = { #define OV5693_NUM_SUPPLIES ARRAY_SIZE(ov5693_supply_names) -struct ov5693_reg { - u32 reg; - u8 val; -}; - -struct ov5693_reg_list { - u32 num_regs; - const struct ov5693_reg *regs; -}; - struct ov5693_device { - struct i2c_client *client; struct device *dev; + struct regmap *regmap; /* Protect against concurrent changes to controls */ struct mutex lock; @@ -189,156 +174,151 @@ struct ov5693_device { } ctrls; }; -static const struct ov5693_reg ov5693_global_regs[] = { - {OV5693_REG_8BIT(0x3016), 0xf0}, - {OV5693_REG_8BIT(0x3017), 0xf0}, - {OV5693_REG_8BIT(0x3018), 0xf0}, - {OV5693_REG_8BIT(0x3022), 0x01}, - {OV5693_REG_8BIT(0x3028), 0x44}, - {OV5693_REG_8BIT(0x3098), 0x02}, - {OV5693_REG_8BIT(0x3099), 0x19}, - {OV5693_REG_8BIT(0x309a), 0x02}, - {OV5693_REG_8BIT(0x309b), 0x01}, - {OV5693_REG_8BIT(0x309c), 0x00}, - {OV5693_REG_8BIT(0x30a0), 0xd2}, - {OV5693_REG_8BIT(0x30a2), 0x01}, - {OV5693_REG_8BIT(0x30b2), 0x00}, - {OV5693_REG_8BIT(0x30b3), 0x83}, - {OV5693_REG_8BIT(0x30b4), 0x03}, - {OV5693_REG_8BIT(0x30b5), 0x04}, - {OV5693_REG_8BIT(0x30b6), 0x01}, - {OV5693_REG_8BIT(0x3080), 0x01}, - {OV5693_REG_8BIT(0x3104), 0x21}, - {OV5693_REG_8BIT(0x3106), 0x00}, - {OV5693_REG_8BIT(0x3406), 0x01}, - {OV5693_REG_8BIT(0x3503), 0x07}, - {OV5693_REG_8BIT(0x350b), 0x40}, - {OV5693_REG_8BIT(0x3601), 0x0a}, - {OV5693_REG_8BIT(0x3602), 0x38}, - {OV5693_REG_8BIT(0x3612), 0x80}, - {OV5693_REG_8BIT(0x3620), 0x54}, - {OV5693_REG_8BIT(0x3621), 0xc7}, - {OV5693_REG_8BIT(0x3622), 0x0f}, - {OV5693_REG_8BIT(0x3625), 0x10}, - {OV5693_REG_8BIT(0x3630), 0x55}, - {OV5693_REG_8BIT(0x3631), 0xf4}, - {OV5693_REG_8BIT(0x3632), 0x00}, - {OV5693_REG_8BIT(0x3633), 0x34}, - {OV5693_REG_8BIT(0x3634), 0x02}, - {OV5693_REG_8BIT(0x364d), 0x0d}, - {OV5693_REG_8BIT(0x364f), 0xdd}, - {OV5693_REG_8BIT(0x3660), 0x04}, - {OV5693_REG_8BIT(0x3662), 0x10}, - {OV5693_REG_8BIT(0x3663), 0xf1}, - {OV5693_REG_8BIT(0x3665), 0x00}, - {OV5693_REG_8BIT(0x3666), 0x20}, - {OV5693_REG_8BIT(0x3667), 0x00}, - {OV5693_REG_8BIT(0x366a), 0x80}, - {OV5693_REG_8BIT(0x3680), 0xe0}, - {OV5693_REG_8BIT(0x3681), 0x00}, - {OV5693_REG_8BIT(0x3700), 0x42}, - {OV5693_REG_8BIT(0x3701), 0x14}, - {OV5693_REG_8BIT(0x3702), 0xa0}, - {OV5693_REG_8BIT(0x3703), 0xd8}, - {OV5693_REG_8BIT(0x3704), 0x78}, - {OV5693_REG_8BIT(0x3705), 0x02}, - {OV5693_REG_8BIT(0x370a), 0x00}, - {OV5693_REG_8BIT(0x370b), 0x20}, - {OV5693_REG_8BIT(0x370c), 0x0c}, - {OV5693_REG_8BIT(0x370d), 0x11}, - {OV5693_REG_8BIT(0x370e), 0x00}, - {OV5693_REG_8BIT(0x370f), 0x40}, - {OV5693_REG_8BIT(0x3710), 0x00}, - {OV5693_REG_8BIT(0x371a), 0x1c}, - {OV5693_REG_8BIT(0x371b), 0x05}, - {OV5693_REG_8BIT(0x371c), 0x01}, - {OV5693_REG_8BIT(0x371e), 0xa1}, - {OV5693_REG_8BIT(0x371f), 0x0c}, - {OV5693_REG_8BIT(0x3721), 0x00}, - {OV5693_REG_8BIT(0x3724), 0x10}, - {OV5693_REG_8BIT(0x3726), 0x00}, - {OV5693_REG_8BIT(0x372a), 0x01}, - {OV5693_REG_8BIT(0x3730), 0x10}, - {OV5693_REG_8BIT(0x3738), 0x22}, - {OV5693_REG_8BIT(0x3739), 0xe5}, - {OV5693_REG_8BIT(0x373a), 0x50}, - {OV5693_REG_8BIT(0x373b), 0x02}, - {OV5693_REG_8BIT(0x373c), 0x41}, - {OV5693_REG_8BIT(0x373f), 0x02}, - {OV5693_REG_8BIT(0x3740), 0x42}, - {OV5693_REG_8BIT(0x3741), 0x02}, - {OV5693_REG_8BIT(0x3742), 0x18}, - {OV5693_REG_8BIT(0x3743), 0x01}, - {OV5693_REG_8BIT(0x3744), 0x02}, - {OV5693_REG_8BIT(0x3747), 0x10}, - {OV5693_REG_8BIT(0x374c), 0x04}, - {OV5693_REG_8BIT(0x3751), 0xf0}, - {OV5693_REG_8BIT(0x3752), 0x00}, - {OV5693_REG_8BIT(0x3753), 0x00}, - {OV5693_REG_8BIT(0x3754), 0xc0}, - {OV5693_REG_8BIT(0x3755), 0x00}, - {OV5693_REG_8BIT(0x3756), 0x1a}, - {OV5693_REG_8BIT(0x3758), 0x00}, - {OV5693_REG_8BIT(0x3759), 0x0f}, - {OV5693_REG_8BIT(0x376b), 0x44}, - {OV5693_REG_8BIT(0x375c), 0x04}, - {OV5693_REG_8BIT(0x3774), 0x10}, - {OV5693_REG_8BIT(0x3776), 0x00}, - {OV5693_REG_8BIT(0x377f), 0x08}, - {OV5693_REG_8BIT(0x3780), 0x22}, - {OV5693_REG_8BIT(0x3781), 0x0c}, - {OV5693_REG_8BIT(0x3784), 0x2c}, - {OV5693_REG_8BIT(0x3785), 0x1e}, - {OV5693_REG_8BIT(0x378f), 0xf5}, - {OV5693_REG_8BIT(0x3791), 0xb0}, - {OV5693_REG_8BIT(0x3795), 0x00}, - {OV5693_REG_8BIT(0x3796), 0x64}, - {OV5693_REG_8BIT(0x3797), 0x11}, - {OV5693_REG_8BIT(0x3798), 0x30}, - {OV5693_REG_8BIT(0x3799), 0x41}, - {OV5693_REG_8BIT(0x379a), 0x07}, - {OV5693_REG_8BIT(0x379b), 0xb0}, - {OV5693_REG_8BIT(0x379c), 0x0c}, - {OV5693_REG_8BIT(0x3a04), 0x06}, - {OV5693_REG_8BIT(0x3a05), 0x14}, - {OV5693_REG_8BIT(0x3e07), 0x20}, - {OV5693_REG_8BIT(0x4000), 0x08}, - {OV5693_REG_8BIT(0x4001), 0x04}, - {OV5693_REG_8BIT(0x4004), 0x08}, - {OV5693_REG_8BIT(0x4006), 0x20}, - {OV5693_REG_8BIT(0x4008), 0x24}, - {OV5693_REG_8BIT(0x4009), 0x10}, - {OV5693_REG_8BIT(0x4058), 0x00}, - {OV5693_REG_8BIT(0x4101), 0xb2}, - {OV5693_REG_8BIT(0x4307), 0x31}, - {OV5693_REG_8BIT(0x4511), 0x05}, - {OV5693_REG_8BIT(0x4512), 0x01}, - {OV5693_REG_8BIT(0x481f), 0x30}, - {OV5693_REG_8BIT(0x4826), 0x2c}, - {OV5693_REG_8BIT(0x4d02), 0xfd}, - {OV5693_REG_8BIT(0x4d03), 0xf5}, - {OV5693_REG_8BIT(0x4d04), 0x0c}, - {OV5693_REG_8BIT(0x4d05), 0xcc}, - {OV5693_REG_8BIT(0x4837), 0x0a}, - {OV5693_REG_8BIT(0x5003), 0x20}, - {OV5693_REG_8BIT(0x5013), 0x00}, - {OV5693_REG_8BIT(0x5842), 0x01}, - {OV5693_REG_8BIT(0x5843), 0x2b}, - {OV5693_REG_8BIT(0x5844), 0x01}, - {OV5693_REG_8BIT(0x5845), 0x92}, - {OV5693_REG_8BIT(0x5846), 0x01}, - {OV5693_REG_8BIT(0x5847), 0x8f}, - {OV5693_REG_8BIT(0x5848), 0x01}, - {OV5693_REG_8BIT(0x5849), 0x0c}, - {OV5693_REG_8BIT(0x5e10), 0x0c}, - {OV5693_REG_8BIT(0x3820), 0x00}, - {OV5693_REG_8BIT(0x3821), 0x1e}, - {OV5693_REG_8BIT(0x5041), 0x14} -}; - -static const struct ov5693_reg_list ov5693_global_setting = { - .num_regs = ARRAY_SIZE(ov5693_global_regs), - .regs = ov5693_global_regs, +static const struct cci_reg_sequence ov5693_global_regs[] = { + {CCI_REG8(0x3016), 0xf0}, + {CCI_REG8(0x3017), 0xf0}, + {CCI_REG8(0x3018), 0xf0}, + {CCI_REG8(0x3022), 0x01}, + {CCI_REG8(0x3028), 0x44}, + {CCI_REG8(0x3098), 0x02}, + {CCI_REG8(0x3099), 0x19}, + {CCI_REG8(0x309a), 0x02}, + {CCI_REG8(0x309b), 0x01}, + {CCI_REG8(0x309c), 0x00}, + {CCI_REG8(0x30a0), 0xd2}, + {CCI_REG8(0x30a2), 0x01}, + {CCI_REG8(0x30b2), 0x00}, + {CCI_REG8(0x30b3), 0x83}, + {CCI_REG8(0x30b4), 0x03}, + {CCI_REG8(0x30b5), 0x04}, + {CCI_REG8(0x30b6), 0x01}, + {CCI_REG8(0x3080), 0x01}, + {CCI_REG8(0x3104), 0x21}, + {CCI_REG8(0x3106), 0x00}, + {CCI_REG8(0x3406), 0x01}, + {CCI_REG8(0x3503), 0x07}, + {CCI_REG8(0x350b), 0x40}, + {CCI_REG8(0x3601), 0x0a}, + {CCI_REG8(0x3602), 0x38}, + {CCI_REG8(0x3612), 0x80}, + {CCI_REG8(0x3620), 0x54}, + {CCI_REG8(0x3621), 0xc7}, + {CCI_REG8(0x3622), 0x0f}, + {CCI_REG8(0x3625), 0x10}, + {CCI_REG8(0x3630), 0x55}, + {CCI_REG8(0x3631), 0xf4}, + {CCI_REG8(0x3632), 0x00}, + {CCI_REG8(0x3633), 0x34}, + {CCI_REG8(0x3634), 0x02}, + {CCI_REG8(0x364d), 0x0d}, + {CCI_REG8(0x364f), 0xdd}, + {CCI_REG8(0x3660), 0x04}, + {CCI_REG8(0x3662), 0x10}, + {CCI_REG8(0x3663), 0xf1}, + {CCI_REG8(0x3665), 0x00}, + {CCI_REG8(0x3666), 0x20}, + {CCI_REG8(0x3667), 0x00}, + {CCI_REG8(0x366a), 0x80}, + {CCI_REG8(0x3680), 0xe0}, + {CCI_REG8(0x3681), 0x00}, + {CCI_REG8(0x3700), 0x42}, + {CCI_REG8(0x3701), 0x14}, + {CCI_REG8(0x3702), 0xa0}, + {CCI_REG8(0x3703), 0xd8}, + {CCI_REG8(0x3704), 0x78}, + {CCI_REG8(0x3705), 0x02}, + {CCI_REG8(0x370a), 0x00}, + {CCI_REG8(0x370b), 0x20}, + {CCI_REG8(0x370c), 0x0c}, + {CCI_REG8(0x370d), 0x11}, + {CCI_REG8(0x370e), 0x00}, + {CCI_REG8(0x370f), 0x40}, + {CCI_REG8(0x3710), 0x00}, + {CCI_REG8(0x371a), 0x1c}, + {CCI_REG8(0x371b), 0x05}, + {CCI_REG8(0x371c), 0x01}, + {CCI_REG8(0x371e), 0xa1}, + {CCI_REG8(0x371f), 0x0c}, + {CCI_REG8(0x3721), 0x00}, + {CCI_REG8(0x3724), 0x10}, + {CCI_REG8(0x3726), 0x00}, + {CCI_REG8(0x372a), 0x01}, + {CCI_REG8(0x3730), 0x10}, + {CCI_REG8(0x3738), 0x22}, + {CCI_REG8(0x3739), 0xe5}, + {CCI_REG8(0x373a), 0x50}, + {CCI_REG8(0x373b), 0x02}, + {CCI_REG8(0x373c), 0x41}, + {CCI_REG8(0x373f), 0x02}, + {CCI_REG8(0x3740), 0x42}, + {CCI_REG8(0x3741), 0x02}, + {CCI_REG8(0x3742), 0x18}, + {CCI_REG8(0x3743), 0x01}, + {CCI_REG8(0x3744), 0x02}, + {CCI_REG8(0x3747), 0x10}, + {CCI_REG8(0x374c), 0x04}, + {CCI_REG8(0x3751), 0xf0}, + {CCI_REG8(0x3752), 0x00}, + {CCI_REG8(0x3753), 0x00}, + {CCI_REG8(0x3754), 0xc0}, + {CCI_REG8(0x3755), 0x00}, + {CCI_REG8(0x3756), 0x1a}, + {CCI_REG8(0x3758), 0x00}, + {CCI_REG8(0x3759), 0x0f}, + {CCI_REG8(0x376b), 0x44}, + {CCI_REG8(0x375c), 0x04}, + {CCI_REG8(0x3774), 0x10}, + {CCI_REG8(0x3776), 0x00}, + {CCI_REG8(0x377f), 0x08}, + {CCI_REG8(0x3780), 0x22}, + {CCI_REG8(0x3781), 0x0c}, + {CCI_REG8(0x3784), 0x2c}, + {CCI_REG8(0x3785), 0x1e}, + {CCI_REG8(0x378f), 0xf5}, + {CCI_REG8(0x3791), 0xb0}, + {CCI_REG8(0x3795), 0x00}, + {CCI_REG8(0x3796), 0x64}, + {CCI_REG8(0x3797), 0x11}, + {CCI_REG8(0x3798), 0x30}, + {CCI_REG8(0x3799), 0x41}, + {CCI_REG8(0x379a), 0x07}, + {CCI_REG8(0x379b), 0xb0}, + {CCI_REG8(0x379c), 0x0c}, + {CCI_REG8(0x3a04), 0x06}, + {CCI_REG8(0x3a05), 0x14}, + {CCI_REG8(0x3e07), 0x20}, + {CCI_REG8(0x4000), 0x08}, + {CCI_REG8(0x4001), 0x04}, + {CCI_REG8(0x4004), 0x08}, + {CCI_REG8(0x4006), 0x20}, + {CCI_REG8(0x4008), 0x24}, + {CCI_REG8(0x4009), 0x10}, + {CCI_REG8(0x4058), 0x00}, + {CCI_REG8(0x4101), 0xb2}, + {CCI_REG8(0x4307), 0x31}, + {CCI_REG8(0x4511), 0x05}, + {CCI_REG8(0x4512), 0x01}, + {CCI_REG8(0x481f), 0x30}, + {CCI_REG8(0x4826), 0x2c}, + {CCI_REG8(0x4d02), 0xfd}, + {CCI_REG8(0x4d03), 0xf5}, + {CCI_REG8(0x4d04), 0x0c}, + {CCI_REG8(0x4d05), 0xcc}, + {CCI_REG8(0x4837), 0x0a}, + {CCI_REG8(0x5003), 0x20}, + {CCI_REG8(0x5013), 0x00}, + {CCI_REG8(0x5842), 0x01}, + {CCI_REG8(0x5843), 0x2b}, + {CCI_REG8(0x5844), 0x01}, + {CCI_REG8(0x5845), 0x92}, + {CCI_REG8(0x5846), 0x01}, + {CCI_REG8(0x5847), 0x8f}, + {CCI_REG8(0x5848), 0x01}, + {CCI_REG8(0x5849), 0x0c}, + {CCI_REG8(0x5e10), 0x0c}, + {CCI_REG8(0x3820), 0x00}, + {CCI_REG8(0x3821), 0x1e}, + {CCI_REG8(0x5041), 0x14} }; static const struct v4l2_rect ov5693_default_crop = { @@ -373,115 +353,6 @@ static const u8 ov5693_test_pattern_bits[] = { OV5693_TEST_PATTERN_ROLLING, }; -/* I2C I/O Operations */ - -static int ov5693_read_reg(struct ov5693_device *ov5693, u32 addr, u32 *value) -{ - struct i2c_client *client = ov5693->client; - __be16 reg; - u8 val[4]; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .buf = (u8 *)&val, - }, - }; - unsigned int len = ((addr >> OV5693_REG_SIZE_SHIFT) & 3); - unsigned int i; - int ret; - - reg = cpu_to_be16(addr & OV5693_REG_ADDR_MASK); - - msg[1].len = len; - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) - return dev_err_probe(&client->dev, ret, - "Failed to read register 0x%04x\n", - addr & OV5693_REG_ADDR_MASK); - - *value = 0; - for (i = 0; i < len; ++i) { - *value <<= 8; - *value |= val[i]; - } - - return 0; -} - -static void ov5693_write_reg(struct ov5693_device *ov5693, u32 addr, u32 value, - int *error) -{ - struct i2c_client *client = ov5693->client; - struct { - __be16 reg; - u8 val[4]; - } __packed buf; - struct i2c_msg msg = { - .addr = client->addr, - .buf = (u8 *)&buf, - }; - unsigned int len = ((addr >> OV5693_REG_SIZE_SHIFT) & 3); - unsigned int i; - int ret; - - if (*error < 0) - return; - - buf.reg = cpu_to_be16(addr & OV5693_REG_ADDR_MASK); - for (i = 0; i < len; ++i) { - buf.val[len - i - 1] = value & 0xff; - value >>= 8; - } - - msg.len = len + 2; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed to write register 0x%04x: %d\n", - addr & OV5693_REG_ADDR_MASK, ret); - *error = ret; - } -} - -static int ov5693_write_reg_array(struct ov5693_device *ov5693, - const struct ov5693_reg_list *reglist) -{ - unsigned int i; - int ret = 0; - - for (i = 0; i < reglist->num_regs; i++) - ov5693_write_reg(ov5693, reglist->regs[i].reg, - reglist->regs[i].val, &ret); - - return ret; -} - -static int ov5693_update_bits(struct ov5693_device *ov5693, u32 address, - u32 mask, u32 bits) -{ - u32 value = 0; - int ret; - - ret = ov5693_read_reg(ov5693, address, &value); - if (ret) - return ret; - - value &= ~mask; - value |= bits; - - ov5693_write_reg(ov5693, address, value, &ret); - - return ret; -} - /* V4L2 Controls Functions */ static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, @@ -491,8 +362,8 @@ static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, OV5693_FORMAT1_FLIP_VERT_SENSOR_EN; int ret; - ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, bits, - enable ? bits : 0); + ret = cci_update_bits(ov5693->regmap, OV5693_FORMAT1_REG, bits, + enable ? bits : 0, NULL); if (ret) return ret; @@ -506,8 +377,8 @@ static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN; int ret; - ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, bits, - enable ? bits : 0); + ret = cci_update_bits(ov5693->regmap, OV5693_FORMAT2_REG, bits, + enable ? bits : 0, NULL); if (ret) return ret; @@ -516,10 +387,11 @@ static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, static int ov5693_get_exposure(struct ov5693_device *ov5693, s32 *value) { - u32 exposure; + u64 exposure; int ret; - ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_CTRL_REG, &exposure); + ret = cci_read(ov5693->regmap, OV5693_EXPOSURE_CTRL_REG, &exposure, + NULL); if (ret) return ret; @@ -536,17 +408,17 @@ static int ov5693_exposure_configure(struct ov5693_device *ov5693, exposure = (exposure << 4) & OV5693_EXPOSURE_CTRL_MASK; - ov5693_write_reg(ov5693, OV5693_EXPOSURE_CTRL_REG, exposure, &ret); + cci_write(ov5693->regmap, OV5693_EXPOSURE_CTRL_REG, exposure, &ret); return ret; } static int ov5693_get_gain(struct ov5693_device *ov5693, u32 *gain) { - u32 value; + u64 value; int ret; - ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_REG, &value); + ret = cci_read(ov5693->regmap, OV5693_GAIN_CTRL_REG, &value, NULL); if (ret) return ret; @@ -563,9 +435,9 @@ static int ov5693_digital_gain_configure(struct ov5693_device *ov5693, gain &= OV5693_MWB_GAIN_MASK; - ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_REG, gain, &ret); - ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_REG, gain, &ret); - ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_RED_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_GREEN_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_BLUE_GAIN_REG, gain, &ret); return ret; } @@ -576,7 +448,7 @@ static int ov5693_analog_gain_configure(struct ov5693_device *ov5693, u32 gain) gain = (gain << 4) & OV5693_GAIN_CTRL_MASK; - ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_GAIN_CTRL_REG, gain, &ret); return ret; } @@ -586,7 +458,7 @@ static int ov5693_vts_configure(struct ov5693_device *ov5693, u32 vblank) u16 vts = ov5693->mode.format.height + vblank; int ret = 0; - ov5693_write_reg(ov5693, OV5693_TIMING_VTS_REG, vts, &ret); + cci_write(ov5693->regmap, OV5693_TIMING_VTS_REG, vts, &ret); return ret; } @@ -595,8 +467,8 @@ static int ov5693_test_pattern_configure(struct ov5693_device *ov5693, u32 idx) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_TEST_PATTERN_REG, - ov5693_test_pattern_bits[idx], &ret); + cci_write(ov5693->regmap, OV5693_TEST_PATTERN_REG, + ov5693_test_pattern_bits[idx], &ret); return ret; } @@ -685,59 +557,54 @@ static int ov5693_mode_configure(struct ov5693_device *ov5693) int ret = 0; /* Crop Start X */ - ov5693_write_reg(ov5693, OV5693_CROP_START_X_REG, mode->crop.left, - &ret); + cci_write(ov5693->regmap, OV5693_CROP_START_X_REG, mode->crop.left, + &ret); /* Offset X */ - ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_REG, 0, &ret); + cci_write(ov5693->regmap, OV5693_OFFSET_START_X_REG, 0, &ret); /* Output Size X */ - ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_REG, mode->format.width, - &ret); + cci_write(ov5693->regmap, OV5693_OUTPUT_SIZE_X_REG, mode->format.width, + &ret); /* Crop End X */ - ov5693_write_reg(ov5693, OV5693_CROP_END_X_REG, - mode->crop.left + mode->crop.width, &ret); + cci_write(ov5693->regmap, OV5693_CROP_END_X_REG, + mode->crop.left + mode->crop.width, &ret); /* Horizontal Total Size */ - ov5693_write_reg(ov5693, OV5693_TIMING_HTS_REG, OV5693_FIXED_PPL, - &ret); + cci_write(ov5693->regmap, OV5693_TIMING_HTS_REG, OV5693_FIXED_PPL, + &ret); /* Crop Start Y */ - ov5693_write_reg(ov5693, OV5693_CROP_START_Y_REG, mode->crop.top, - &ret); + cci_write(ov5693->regmap, OV5693_CROP_START_Y_REG, mode->crop.top, + &ret); /* Offset Y */ - ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_REG, 0, &ret); + cci_write(ov5693->regmap, OV5693_OFFSET_START_Y_REG, 0, &ret); /* Output Size Y */ - ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_REG, mode->format.height, - &ret); + cci_write(ov5693->regmap, OV5693_OUTPUT_SIZE_Y_REG, mode->format.height, + &ret); /* Crop End Y */ - ov5693_write_reg(ov5693, OV5693_CROP_END_Y_REG, - mode->crop.top + mode->crop.height, &ret); + cci_write(ov5693->regmap, OV5693_CROP_END_Y_REG, + mode->crop.top + mode->crop.height, &ret); /* Subsample X increase */ - ov5693_write_reg(ov5693, OV5693_SUB_INC_X_REG, - ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret); + cci_write(ov5693->regmap, OV5693_SUB_INC_X_REG, + ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret); /* Subsample Y increase */ - ov5693_write_reg(ov5693, OV5693_SUB_INC_Y_REG, - ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret); - - if (ret) - return ret; + cci_write(ov5693->regmap, OV5693_SUB_INC_Y_REG, + ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret); /* Binning */ - ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, - OV5693_FORMAT1_VBIN_EN, - mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0); - if (ret) - return ret; + cci_update_bits(ov5693->regmap, OV5693_FORMAT1_REG, + OV5693_FORMAT1_VBIN_EN, + mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0, &ret); - ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, - OV5693_FORMAT2_HBIN_EN, - mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0); + cci_update_bits(ov5693->regmap, OV5693_FORMAT2_REG, + OV5693_FORMAT2_HBIN_EN, + mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0, &ret); return ret; } @@ -746,9 +613,9 @@ static int ov5693_enable_streaming(struct ov5693_device *ov5693, bool enable) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_SW_STREAM_REG, - enable ? OV5693_START_STREAMING : - OV5693_STOP_STREAMING, &ret); + cci_write(ov5693->regmap, OV5693_SW_STREAM_REG, + enable ? OV5693_START_STREAMING : OV5693_STOP_STREAMING, + &ret); return ret; } @@ -757,7 +624,7 @@ static int ov5693_sw_reset(struct ov5693_device *ov5693) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret); + cci_write(ov5693->regmap, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret); return ret; } @@ -771,7 +638,8 @@ static int ov5693_sensor_init(struct ov5693_device *ov5693) return dev_err_probe(ov5693->dev, ret, "software reset error\n"); - ret = ov5693_write_reg_array(ov5693, &ov5693_global_setting); + ret = cci_multi_reg_write(ov5693->regmap, ov5693_global_regs, + ARRAY_SIZE(ov5693_global_regs), NULL); if (ret) return dev_err_probe(ov5693->dev, ret, "global settings error\n"); @@ -871,15 +739,15 @@ out_unlock: static int ov5693_detect(struct ov5693_device *ov5693) { int ret; - u32 id; + u64 id; - ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID, &id); + ret = cci_read(ov5693->regmap, OV5693_REG_CHIP_ID, &id, NULL); if (ret) return ret; if (id != OV5693_CHIP_ID) return dev_err_probe(ov5693->dev, -ENODEV, - "sensor ID mismatch. Found 0x%04x\n", id); + "sensor ID mismatch. Got 0x%04llx\n", id); return 0; } @@ -1407,9 +1275,12 @@ static int ov5693_probe(struct i2c_client *client) if (!ov5693) return -ENOMEM; - ov5693->client = client; ov5693->dev = &client->dev; + ov5693->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov5693->regmap)) + return PTR_ERR(ov5693->regmap); + ret = ov5693_check_hwcfg(ov5693); if (ret) return ret; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 10e47c7d4e0c..dffdb475e433 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1210,7 +1210,7 @@ static struct i2c_driver ov7740_i2c_driver = { .driver = { .name = "ov7740", .pm = &ov7740_pm_ops, - .of_match_table = of_match_ptr(ov7740_of_match), + .of_match_table = ov7740_of_match, }, .probe = ov7740_probe, .remove = ov7740_remove, diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index 01a2596282f0..f4e2e2f3972a 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -567,7 +567,6 @@ again: static int rdacm20_probe(struct i2c_client *client) { struct rdacm20_device *dev; - struct fwnode_handle *ep; int ret; dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); @@ -616,24 +615,12 @@ static int rdacm20_probe(struct i2c_client *client) if (ret < 0) goto error_free_ctrls; - ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); - if (!ep) { - dev_err(&client->dev, - "Unable to get endpoint in node %pOF\n", - client->dev.of_node); - ret = -ENOENT; - goto error_free_ctrls; - } - dev->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&dev->sd); if (ret) - goto error_put_node; + goto error_free_ctrls; return 0; -error_put_node: - fwnode_handle_put(ep); error_free_ctrls: v4l2_ctrl_handler_free(&dev->ctrls); error: @@ -650,7 +637,6 @@ static void rdacm20_remove(struct i2c_client *client) { struct rdacm20_device *dev = i2c_to_rdacm20(client); - fwnode_handle_put(dev->sd.fwnode); v4l2_async_unregister_subdev(&dev->sd); v4l2_ctrl_handler_free(&dev->ctrls); media_entity_cleanup(&dev->sd.entity); diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index 043fec778a5e..a36a709243fd 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -351,7 +351,7 @@ static void ov10640_power_up(struct rdacm21_device *dev) static int ov10640_check_id(struct rdacm21_device *dev) { unsigned int i; - u8 val; + u8 val = 0; /* Read OV10640 ID to test communications. */ for (i = 0; i < OV10640_PID_TIMEOUT; ++i) { @@ -543,7 +543,6 @@ static int rdacm21_initialize(struct rdacm21_device *dev) static int rdacm21_probe(struct i2c_client *client) { struct rdacm21_device *dev; - struct fwnode_handle *ep; int ret; dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); @@ -588,24 +587,12 @@ static int rdacm21_probe(struct i2c_client *client) if (ret < 0) goto error_free_ctrls; - ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); - if (!ep) { - dev_err(&client->dev, - "Unable to get endpoint in node %pOF\n", - client->dev.of_node); - ret = -ENOENT; - goto error_free_ctrls; - } - dev->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&dev->sd); if (ret) - goto error_put_node; + goto error_free_ctrls; return 0; -error_put_node: - fwnode_handle_put(dev->sd.fwnode); error_free_ctrls: v4l2_ctrl_handler_free(&dev->ctrls); error: diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 906553a28676..fa27638edc07 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -545,7 +545,14 @@ static int mipid02_configure_from_code(struct mipid02_dev *bridge) static int mipid02_stream_disable(struct mipid02_dev *bridge) { struct i2c_client *client = bridge->i2c_client; - int ret; + int ret = -EINVAL; + + if (!bridge->s_subdev) + goto error; + + ret = v4l2_subdev_call(bridge->s_subdev, video, s_stream, 0); + if (ret) + goto error; /* Disable all lanes */ ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, 0); @@ -633,6 +640,10 @@ static int mipid02_stream_enable(struct mipid02_dev *bridge) if (ret) goto error; + ret = v4l2_subdev_call(bridge->s_subdev, video, s_stream, 1); + if (ret) + goto error; + return 0; error: @@ -829,7 +840,7 @@ static const struct media_entity_operations mipid02_subdev_entity_ops = { static int mipid02_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); struct i2c_client *client = bridge->i2c_client; @@ -863,7 +874,7 @@ static int mipid02_async_bound(struct v4l2_async_notifier *notifier, static void mipid02_async_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); @@ -879,7 +890,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) { struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct i2c_client *client = bridge->i2c_client; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *ep_node; int ret; @@ -911,10 +922,10 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) bridge->rx = ep; /* register async notifier so we get noticed when sensor is connected */ - v4l2_async_nf_init(&bridge->notifier); + v4l2_async_subdev_nf_init(&bridge->notifier, &bridge->sd); asd = v4l2_async_nf_add_fwnode_remote(&bridge->notifier, of_fwnode_handle(ep_node), - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep_node); if (IS_ERR(asd)) { @@ -924,7 +935,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) } bridge->notifier.ops = &mipid02_notifier_ops; - ret = v4l2_async_subdev_nf_register(&bridge->sd, &bridge->notifier); + ret = v4l2_async_nf_register(&bridge->notifier); if (ret) v4l2_async_nf_cleanup(&bridge->notifier); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 15f8163be9bf..2785935da497 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -133,8 +133,8 @@ static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (err != ARRAY_SIZE(msgs)) { - v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", - __func__, reg, client->addr); + v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed: %d\n", + __func__, reg, client->addr, err); } } @@ -165,8 +165,8 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) err = i2c_transfer(client->adapter, &msg, 1); if (err != 1) { - v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", - __func__, reg, client->addr); + v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed: %d\n", + __func__, reg, client->addr, err); return; } diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 3f7e147ef594..566f5eaddd57 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -1426,7 +1426,7 @@ static int tc358746_init_controls(struct tc358746 *tc358746) static int tc358746_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct tc358746 *tc358746 = container_of(notifier, struct tc358746, notifier); @@ -1445,7 +1445,7 @@ static int tc358746_async_register(struct tc358746 *tc358746) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int err; @@ -1460,9 +1460,9 @@ static int tc358746_async_register(struct tc358746 *tc358746) return err; } - v4l2_async_nf_init(&tc358746->notifier); + v4l2_async_subdev_nf_init(&tc358746->notifier, &tc358746->sd); asd = v4l2_async_nf_add_fwnode_remote(&tc358746->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); if (IS_ERR(asd)) { @@ -1472,13 +1472,10 @@ static int tc358746_async_register(struct tc358746 *tc358746) tc358746->notifier.ops = &tc358746_notify_ops; - err = v4l2_async_subdev_nf_register(&tc358746->sd, &tc358746->notifier); + err = v4l2_async_nf_register(&tc358746->notifier); if (err) goto err_cleanup; - tc358746->sd.fwnode = fwnode_graph_get_endpoint_by_id( - dev_fwnode(tc358746->sd.dev), TC358746_SOURCE, 0, 0); - err = v4l2_async_register_subdev(&tc358746->sd); if (err) goto err_unregister; @@ -1486,7 +1483,6 @@ static int tc358746_async_register(struct tc358746 *tc358746) return 0; err_unregister: - fwnode_handle_put(tc358746->sd.fwnode); v4l2_async_nf_unregister(&tc358746->notifier); err_cleanup: v4l2_async_nf_cleanup(&tc358746->notifier); @@ -1605,7 +1601,6 @@ static void tc358746_remove(struct i2c_client *client) v4l2_fwnode_endpoint_free(&tc358746->csi_vep); v4l2_async_nf_unregister(&tc358746->notifier); v4l2_async_nf_cleanup(&tc358746->notifier); - fwnode_handle_put(sd->fwnode); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index c7fb35ee3f9d..e543b3f7a4d8 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2068,6 +2068,10 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) tvpc->ent.name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", v4l2c->name, v4l2c->label ? v4l2c->label : ""); + if (!tvpc->ent.name) { + ret = -ENOMEM; + goto err_free; + } } ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 6f98abc7ccc1..537ebd9fa8d7 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -16,9 +16,9 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 480194543d05..ee095bde0b68 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -73,7 +73,7 @@ config VIDEO_PCI_SKELETON Enable build of the skeleton PCI driver, used as a reference when developing new drivers. -source "drivers/media/pci/intel/ipu3/Kconfig" +source "drivers/media/pci/intel/Kconfig" endif #MEDIA_PCI_SUPPORT endif #PCI diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index 2d674dc28cec..2f77628246e9 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" depends on PCI && I2C && VIDEO_DEV select I2C_ALGOBIT - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DMA_SG depends on RC_CORE depends on MEDIA_RADIO_SUPPORT select VIDEO_TUNER diff --git a/drivers/media/pci/bt8xx/bt848.h b/drivers/media/pci/bt8xx/bt848.h index 16999e717d18..c8a0e1ab001f 100644 --- a/drivers/media/pci/bt8xx/bt848.h +++ b/drivers/media/pci/bt8xx/bt848.h @@ -231,7 +231,15 @@ #define BT848_INT_ETBF (1<<23) +#define BT848_RISC_VIDEO 1 +#define BT848_RISC_TOP 2 +#define BT848_RISC_VBI 4 + #define BT848_INT_RISCS (0xf<<28) +#define BT848_INT_RISCS_VIDEO (BT848_RISC_VIDEO << 28) +#define BT848_INT_RISCS_TOP (BT848_RISC_TOP << 28) +#define BT848_INT_RISCS_VBI (BT848_RISC_VBI << 28) + #define BT848_INT_RISC_EN (1<<27) #define BT848_INT_RACK (1<<25) #define BT848_INT_FIELD (1<<24) diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c index da1914a20b81..b5d071835354 100644 --- a/drivers/media/pci/bt8xx/bttv-audio-hook.c +++ b/drivers/media/pci/bt8xx/bttv-audio-hook.c @@ -293,16 +293,8 @@ void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set) { unsigned long val; - if (!set) { - /* Not much to do here */ - t->audmode = V4L2_TUNER_MODE_LANG1; - t->rxsubchans = V4L2_TUNER_SUB_MONO | - V4L2_TUNER_SUB_STEREO | - V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; - + if (!set) return; - } /*btor (0xc32000, BT848_GPIO_OUT_EN);*/ switch (t->audmode) { diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 734f02b91aa3..aa708a0e5eac 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -641,15 +641,10 @@ static const unsigned int FORMATS = ARRAY_SIZE(formats); #define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \ RESOURCE_VIDEO_STREAM) -static -int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) +int check_alloc_btres_lock(struct bttv *btv, int bit) { int xbits; /* mutual exclusive resources */ - if (fh->resources & bit) - /* have it already allocated */ - return 1; - xbits = bit; if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM)) xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM; @@ -663,7 +658,7 @@ int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) if ((bit & VIDEO_RESOURCES) && 0 == (btv->resources & VIDEO_RESOURCES)) { /* Do crop - use current, don't - use default parameters. */ - __s32 top = btv->crop[!!fh->do_crop].rect.top; + __s32 top = btv->crop[!!btv->do_crop].rect.top; if (btv->vbi_end > top) goto fail; @@ -672,17 +667,16 @@ int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) Claim scan lines crop[].rect.top to bottom. */ btv->crop_start = top; } else if (bit & VBI_RESOURCES) { - __s32 end = fh->vbi_fmt.end; + __s32 end = btv->vbi_fmt.end; if (end > btv->crop_start) goto fail; - /* Claim scan lines above fh->vbi_fmt.end. */ + /* Claim scan lines above btv->vbi_fmt.end. */ btv->vbi_end = end; } /* it's free, grab it */ - fh->resources |= bit; btv->resources |= bit; return 1; @@ -691,9 +685,9 @@ int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) } static -int check_btres(struct bttv_fh *fh, int bit) +int check_btres(struct bttv *btv, int bit) { - return (fh->resources & bit); + return (btv->resources & bit); } static @@ -731,14 +725,12 @@ disclaim_video_lines(struct bttv *btv) btwrite(0xfe, BT848_O_VDELAY_LO); } -static -void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits) +void free_btres_lock(struct bttv *btv, int bits) { - if ((fh->resources & bits) != bits) { + if ((btv->resources & bits) != bits) { /* trying to free resources not allocated by us ... */ pr_err("BUG! (btres)\n"); } - fh->resources &= ~bits; btv->resources &= ~bits; bits = btv->resources; @@ -1111,8 +1103,8 @@ set_tvnorm(struct bttv *btv, unsigned int norm) const struct bttv_tvnorm *tvnorm; v4l2_std_id id; - BUG_ON(norm >= BTTV_TVNORMS); - BUG_ON(btv->tvnorm >= BTTV_TVNORMS); + WARN_ON(norm >= BTTV_TVNORMS); + WARN_ON(btv->tvnorm >= BTTV_TVNORMS); tvnorm = &bttv_tvnorms[norm]; @@ -1174,7 +1166,7 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm) set_tvnorm(btv, norm); } -static void init_irqreg(struct bttv *btv) +void init_irqreg(struct bttv *btv) { /* clear status */ btwrite(0xfffffUL, BT848_INT_STAT); @@ -1453,23 +1445,6 @@ void bttv_gpio_tracking(struct bttv *btv, char *comment) btv->c.nr, outbits, data & outbits, data & ~outbits, comment); } -static void bttv_field_count(struct bttv *btv) -{ - int need_count = 0; - - if (btv->users) - need_count++; - - if (need_count) { - /* start field counter */ - btor(BT848_INT_VSYNC,BT848_INT_MASK); - } else { - /* stop field counter */ - btand(~BT848_INT_VSYNC,BT848_INT_MASK); - btv->field_count = 0; - } -} - static const struct bttv_format* format_by_fourcc(int fourcc) { @@ -1487,158 +1462,132 @@ format_by_fourcc(int fourcc) /* ----------------------------------------------------------------------- */ /* video4linux (1) interface */ -static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, - struct bttv_buffer *buf, - const struct bttv_format *fmt, - unsigned int width, unsigned int height, - enum v4l2_field field) +static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) { - struct bttv_fh *fh = q->priv_data; - int redo_dma_risc = 0; - struct bttv_crop c; - int norm; - int rc; + struct bttv *btv = vb2_get_drv_priv(q); + unsigned int size = btv->fmt->depth * btv->width * btv->height >> 3; - /* check settings */ - if (NULL == fmt) + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + *num_planes = 1; + sizes[0] = size; + + return 0; +} + +static void buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); + unsigned long flags; + + spin_lock_irqsave(&btv->s_lock, flags); + if (list_empty(&btv->capture)) { + btv->loop_irq = BT848_RISC_VIDEO; + if (vb2_is_streaming(&btv->vbiq)) + btv->loop_irq |= BT848_RISC_VBI; + bttv_set_dma(btv, BT848_CAP_CTL_CAPTURE_ODD | + BT848_CAP_CTL_CAPTURE_EVEN); + } + list_add_tail(&buf->list, &btv->capture); + spin_unlock_irqrestore(&btv->s_lock, flags); +} + +static int buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); + unsigned int size = (btv->fmt->depth * btv->width * btv->height) >> 3; + + if (vb2_plane_size(vb, 0) < size) return -EINVAL; - if (fmt->btformat == BT848_COLOR_FMT_RAW) { - width = RAW_BPL; - height = RAW_LINES*2; - if (width*height > buf->vb.bsize) - return -EINVAL; - buf->vb.size = buf->vb.bsize; + vb2_set_plane_payload(vb, 0, size); - /* Make sure tvnorm and vbi_end remain consistent - until we're done. */ - - norm = btv->tvnorm; - - /* In this mode capturing always starts at defrect.top - (default VDELAY), ignoring cropping parameters. */ - if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) { - return -EINVAL; - } - - c.rect = bttv_tvnorms[norm].cropcap.defrect; + if (btv->field != V4L2_FIELD_ALTERNATE) { + buf->vbuf.field = btv->field; + } else if (btv->field_last == V4L2_FIELD_TOP) { + buf->vbuf.field = V4L2_FIELD_BOTTOM; + btv->field_last = V4L2_FIELD_BOTTOM; } else { - norm = btv->tvnorm; - c = btv->crop[!!fh->do_crop]; + buf->vbuf.field = V4L2_FIELD_TOP; + btv->field_last = V4L2_FIELD_TOP; + } - if (width < c.min_scaled_width || - width > c.max_scaled_width || - height < c.min_scaled_height) - return -EINVAL; + /* Allocate memory for risc struct and create the risc program. */ + return bttv_buffer_risc(btv, buf); +} - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_ALTERNATE: - /* btv->crop counts frame lines. Max. scale - factor is 16:1 for frames, 8:1 for fields. */ - if (height * 2 > c.max_scaled_height) - return -EINVAL; - break; +static void buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); - default: - if (height > c.max_scaled_height) - return -EINVAL; - break; + btcx_riscmem_free(btv->c.pci, &buf->top); + btcx_riscmem_free(btv->c.pci, &buf->bottom); +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret = 1; + int seqnr = 0; + struct bttv_buffer *buf; + struct bttv *btv = vb2_get_drv_priv(q); + + ret = check_alloc_btres_lock(btv, RESOURCE_VIDEO_STREAM); + if (ret == 0) { + if (btv->field_count) + seqnr++; + while (!list_empty(&btv->capture)) { + buf = list_entry(btv->capture.next, + struct bttv_buffer, list); + list_del(&buf->list); + buf->vbuf.sequence = (btv->field_count >> 1) + seqnr++; + vb2_buffer_done(&buf->vbuf.vb2_buf, + VB2_BUF_STATE_QUEUED); } - - buf->vb.size = (width * height * fmt->depth) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; + return !ret; } - - /* alloc + fill struct bttv_buffer (if changed) */ - if (buf->vb.width != width || buf->vb.height != height || - buf->vb.field != field || - buf->tvnorm != norm || buf->fmt != fmt || - buf->crop.top != c.rect.top || - buf->crop.left != c.rect.left || - buf->crop.width != c.rect.width || - buf->crop.height != c.rect.height) { - buf->vb.width = width; - buf->vb.height = height; - buf->vb.field = field; - buf->tvnorm = norm; - buf->fmt = fmt; - buf->crop = c.rect; - redo_dma_risc = 1; + if (!vb2_is_streaming(&btv->vbiq)) { + init_irqreg(btv); + btv->field_count = 0; } + btv->framedrop = 0; - /* alloc risc memory */ - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - redo_dma_risc = 1; - if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf))) - goto fail; - } - - if (redo_dma_risc) - if (0 != (rc = bttv_buffer_risc(btv,buf))) - goto fail; - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - - fail: - bttv_dma_free(q,btv,buf); - return rc; -} - -static int -buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct bttv_fh *fh = q->priv_data; - - *size = fh->fmt->depth*fh->width*fh->height >> 3; - if (0 == *count) - *count = gbuffers; - if (*size * *count > gbuffers * gbufsize) - *count = (gbuffers * gbufsize) / *size; return 0; } -static int -buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static void stop_streaming(struct vb2_queue *q) { - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); - struct bttv_fh *fh = q->priv_data; + unsigned long flags; + struct bttv *btv = vb2_get_drv_priv(q); - return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt, - fh->width, fh->height, field); -} - -static void -buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); - struct bttv_fh *fh = q->priv_data; - struct bttv *btv = fh->btv; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue,&btv->capture); - if (!btv->curr.frame_irq) { - btv->loop_irq |= 1; - bttv_set_dma(btv, 0x03); + vb2_wait_for_all_buffers(q); + spin_lock_irqsave(&btv->s_lock, flags); + free_btres_lock(btv, RESOURCE_VIDEO_STREAM); + if (!vb2_is_streaming(&btv->vbiq)) { + /* stop field counter */ + btand(~BT848_INT_VSYNC, BT848_INT_MASK); } + spin_unlock_irqrestore(&btv->s_lock, flags); } -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); - struct bttv_fh *fh = q->priv_data; - - bttv_dma_free(q,fh->btv,buf); -} - -static const struct videobuf_queue_ops bttv_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, +static const struct vb2_ops bttv_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buf_queue, + .buf_prepare = buf_prepare, + .buf_cleanup = buf_cleanup, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static void radio_enable(struct bttv *btv) @@ -1654,8 +1603,7 @@ static void radio_enable(struct bttv *btv) static int bttv_s_std(struct file *file, void *priv, v4l2_std_id id) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); unsigned int i; for (i = 0; i < BTTV_TVNORMS; i++) @@ -1670,8 +1618,7 @@ static int bttv_s_std(struct file *file, void *priv, v4l2_std_id id) static int bttv_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); *id = btv->std; return 0; @@ -1679,8 +1626,7 @@ static int bttv_g_std(struct file *file, void *priv, v4l2_std_id *id) static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML) *id &= V4L2_STD_625_50; @@ -1692,8 +1638,7 @@ static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id) static int bttv_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (i->index >= bttv_tvcards[btv->c.type].video_inputs) return -EINVAL; @@ -1725,8 +1670,7 @@ static int bttv_enum_input(struct file *file, void *priv, static int bttv_g_input(struct file *file, void *priv, unsigned int *i) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); *i = btv->input; @@ -1735,8 +1679,7 @@ static int bttv_g_input(struct file *file, void *priv, unsigned int *i) static int bttv_s_input(struct file *file, void *priv, unsigned int i) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (i >= bttv_tvcards[btv->c.type].video_inputs) return -EINVAL; @@ -1748,8 +1691,7 @@ static int bttv_s_input(struct file *file, void *priv, unsigned int i) static int bttv_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (t->index) return -EINVAL; @@ -1767,8 +1709,7 @@ static int bttv_s_tuner(struct file *file, void *priv, static int bttv_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (f->tuner) return -EINVAL; @@ -1804,8 +1745,7 @@ static void bttv_set_frequency(struct bttv *btv, const struct v4l2_frequency *f) static int bttv_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (f->tuner) return -EINVAL; @@ -1817,8 +1757,7 @@ static int bttv_s_frequency(struct file *file, void *priv, static int bttv_log_status(struct file *file, void *f) { struct video_device *vdev = video_devdata(file); - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); v4l2_ctrl_handler_log_status(vdev->ctrl_handler, btv->c.v4l2_dev.name); bttv_call_all(btv, core, log_status); @@ -1829,8 +1768,7 @@ static int bttv_log_status(struct file *file, void *f) static int bttv_g_register(struct file *file, void *f, struct v4l2_dbg_register *reg) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); /* bt848 has a 12-bit register space */ reg->reg &= 0xfff; @@ -1843,8 +1781,7 @@ static int bttv_g_register(struct file *file, void *f, static int bttv_s_register(struct file *file, void *f, const struct v4l2_dbg_register *reg) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); /* bt848 has a 12-bit register space */ btwrite(reg->val, reg->reg & 0xfff); @@ -1904,16 +1841,11 @@ bttv_crop_adjust (struct bttv_crop * c, also adjust the current cropping parameters to get closer to the desired image size. */ static int -limit_scaled_size_lock (struct bttv_fh * fh, - __s32 * width, - __s32 * height, - enum v4l2_field field, - unsigned int width_mask, - unsigned int width_bias, - int adjust_size, - int adjust_crop) +limit_scaled_size_lock(struct bttv *btv, __s32 *width, __s32 *height, + enum v4l2_field field, unsigned int width_mask, + unsigned int width_bias, int adjust_size, + int adjust_crop) { - struct bttv *btv = fh->btv; const struct v4l2_rect *b; struct bttv_crop *c; __s32 min_width; @@ -1922,8 +1854,8 @@ limit_scaled_size_lock (struct bttv_fh * fh, __s32 max_height; int rc; - BUG_ON((int) width_mask >= 0 || - width_bias >= (unsigned int) -width_mask); + WARN_ON((int)width_mask >= 0 || + width_bias >= (unsigned int)(-width_mask)); /* Make sure tvnorm, vbi_end and the current cropping parameters remain consistent until we're done. */ @@ -1931,9 +1863,9 @@ limit_scaled_size_lock (struct bttv_fh * fh, b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; /* Do crop - use current, don't - use default parameters. */ - c = &btv->crop[!!fh->do_crop]; + c = &btv->crop[!!btv->do_crop]; - if (fh->do_crop + if (btv->do_crop && adjust_size && adjust_crop && !locked_btres(btv, VIDEO_RESOURCES)) { @@ -2007,52 +1939,31 @@ limit_scaled_size_lock (struct bttv_fh * fh, return rc; } -/* ----------------------------------------------------------------------- */ - -static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) +static int bttv_switch_type(struct bttv *btv, enum v4l2_buf_type type) { - struct videobuf_queue* q = NULL; + int res; + struct vb2_queue *q; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - q = &fh->cap; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - q = &fh->vbi; - break; - default: - BUG(); - } - return q; -} - -static int bttv_resource(struct bttv_fh *fh) -{ - int res = 0; - - switch (fh->type) { + switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &btv->capq; res = RESOURCE_VIDEO_STREAM; break; case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &btv->vbiq; res = RESOURCE_VBI; break; default: - BUG(); + WARN_ON(1); + return -EINVAL; } - return res; -} -static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) -{ - struct videobuf_queue *q = bttv_queue(fh); - int res = bttv_resource(fh); + if (check_btres(btv, res)) + return -EBUSY; + if (vb2_is_busy(q)) + return -EBUSY; + btv->type = type; - if (check_btres(fh,res)) - return -EBUSY; - if (videobuf_queue_is_busy(q)) - return -EBUSY; - fh->type = type; return 0; } @@ -2077,12 +1988,11 @@ pix_format_set_size (struct v4l2_pix_format * f, static int bttv_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct bttv_fh *fh = priv; + struct bttv *btv = video_drvdata(file); - pix_format_set_size(&f->fmt.pix, fh->fmt, - fh->width, fh->height); - f->fmt.pix.field = fh->cap.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; + pix_format_set_size(&f->fmt.pix, btv->fmt, btv->width, btv->height); + f->fmt.pix.field = btv->field; + f->fmt.pix.pixelformat = btv->fmt->fourcc; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; @@ -2105,8 +2015,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { const struct bttv_format *fmt; - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); enum v4l2_field field; __s32 width, height; __s32 height2; @@ -2133,7 +2042,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, } fallthrough; default: /* FIELD_ANY case */ - height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + height2 = btv->crop[!!btv->do_crop].rect.height >> 1; field = (f->fmt.pix.height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; @@ -2144,10 +2053,8 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, height = f->fmt.pix.height; bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); - rc = limit_scaled_size_lock(fh, &width, &height, field, - width_mask, width_bias, - /* adjust_size */ 1, - /* adjust_crop */ 0); + rc = limit_scaled_size_lock(btv, &width, &height, field, width_mask, + width_bias, 1, 0); if (0 != rc) return rc; @@ -2160,17 +2067,16 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, } static int bttv_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { int retval; const struct bttv_format *fmt; - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); __s32 width, height; unsigned int width_mask, width_bias; enum v4l2_field field; - retval = bttv_switch_type(fh, f->type); + retval = bttv_switch_type(btv, f->type); if (0 != retval) return retval; @@ -2184,24 +2090,25 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, fmt = format_by_fourcc(f->fmt.pix.pixelformat); bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); - retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field, - width_mask, width_bias, - /* adjust_size */ 1, - /* adjust_crop */ 1); + retval = limit_scaled_size_lock(btv, &width, &height, f->fmt.pix.field, + width_mask, width_bias, 1, 1); if (0 != retval) return retval; f->fmt.pix.field = field; /* update our state information */ - fh->fmt = fmt; - fh->cap.field = f->fmt.pix.field; - fh->cap.last = V4L2_FIELD_NONE; - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - btv->init.fmt = fmt; - btv->init.width = f->fmt.pix.width; - btv->init.height = f->fmt.pix.height; + btv->fmt = fmt; + btv->width = f->fmt.pix.width; + btv->height = f->fmt.pix.height; + btv->field = f->fmt.pix.field; + /* + * When field is V4L2_FIELD_ALTERNATE, buffers will be either + * V4L2_FIELD_TOP or V4L2_FIELD_BOTTOM depending on the value of + * field_last. Initialize field_last to V4L2_FIELD_BOTTOM so that + * streaming starts with a V4L2_FIELD_TOP buffer. + */ + btv->field_last = V4L2_FIELD_BOTTOM; return 0; } @@ -2209,8 +2116,7 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, static int bttv_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (0 == v4l2) return -EINVAL; @@ -2257,73 +2163,10 @@ static int bttv_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int bttv_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct bttv_fh *fh = priv; - return videobuf_reqbufs(bttv_queue(fh), p); -} - -static int bttv_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct bttv_fh *fh = priv; - return videobuf_querybuf(bttv_queue(fh), b); -} - -static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - int res = bttv_resource(fh); - - if (!check_alloc_btres_lock(btv, fh, res)) - return -EBUSY; - - return videobuf_qbuf(bttv_queue(fh), b); -} - -static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct bttv_fh *fh = priv; - return videobuf_dqbuf(bttv_queue(fh), b, - file->f_flags & O_NONBLOCK); -} - -static int bttv_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - int res = bttv_resource(fh); - - if (!check_alloc_btres_lock(btv, fh, res)) - return -EBUSY; - return videobuf_streamon(bttv_queue(fh)); -} - - -static int bttv_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - int retval; - int res = bttv_resource(fh); - - - retval = videobuf_streamoff(bttv_queue(fh)); - if (retval < 0) - return retval; - free_btres_lock(btv, fh, res); - return 0; -} - static int bttv_g_parm(struct file *file, void *f, struct v4l2_streamparm *parm) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -2337,8 +2180,7 @@ static int bttv_g_parm(struct file *file, void *f, static int bttv_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -2360,8 +2202,7 @@ static int bttv_g_tuner(struct file *file, void *priv, static int bttv_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -2373,20 +2214,14 @@ static int bttv_g_pixelaspect(struct file *file, void *priv, static int bttv_g_selection(struct file *file, void *f, struct v4l2_selection *sel) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP: - /* - * No fh->do_crop = 1; because btv->crop[1] may be - * inconsistent with fh->width or fh->height and apps - * do not expect a change here. - */ - sel->r = btv->crop[!!fh->do_crop].rect; + sel->r = btv->crop[!!btv->do_crop].rect; break; case V4L2_SEL_TGT_CROP_DEFAULT: sel->r = bttv_tvnorms[btv->tvnorm].cropcap.defrect; @@ -2403,8 +2238,7 @@ static int bttv_g_selection(struct file *file, void *f, struct v4l2_selection *s static int bttv_s_selection(struct file *file, void *f, struct v4l2_selection *sel) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); const struct v4l2_rect *b; int retval; struct bttv_crop c; @@ -2424,9 +2258,8 @@ static int bttv_s_selection(struct file *file, void *f, struct v4l2_selection *s read() may change vbi_end in check_alloc_btres_lock(). */ retval = -EBUSY; - if (locked_btres(fh->btv, VIDEO_RESOURCES)) { + if (locked_btres(btv, VIDEO_RESOURCES)) return retval; - } b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; @@ -2460,249 +2293,30 @@ static int bttv_s_selection(struct file *file, void *f, struct v4l2_selection *s btv->crop[1] = c; - fh->do_crop = 1; + btv->do_crop = 1; - if (fh->width < c.min_scaled_width) { - fh->width = c.min_scaled_width; - btv->init.width = c.min_scaled_width; - } else if (fh->width > c.max_scaled_width) { - fh->width = c.max_scaled_width; - btv->init.width = c.max_scaled_width; - } + if (btv->width < c.min_scaled_width) + btv->width = c.min_scaled_width; + else if (btv->width > c.max_scaled_width) + btv->width = c.max_scaled_width; - if (fh->height < c.min_scaled_height) { - fh->height = c.min_scaled_height; - btv->init.height = c.min_scaled_height; - } else if (fh->height > c.max_scaled_height) { - fh->height = c.max_scaled_height; - btv->init.height = c.max_scaled_height; - } + if (btv->height < c.min_scaled_height) + btv->height = c.min_scaled_height; + else if (btv->height > c.max_scaled_height) + btv->height = c.max_scaled_height; return 0; } -static ssize_t bttv_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct bttv_fh *fh = file->private_data; - int retval = 0; - - if (fh->btv->errors) - bttv_reinit_bt848(fh->btv); - dprintk("%d: read count=%d type=%s\n", - fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]); - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) { - /* VIDEO_READ in use by another fh, - or VIDEO_STREAM by any fh. */ - return -EBUSY; - } - retval = videobuf_read_one(&fh->cap, data, count, ppos, - file->f_flags & O_NONBLOCK); - free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI)) - return -EBUSY; - retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1, - file->f_flags & O_NONBLOCK); - break; - default: - BUG(); - } - return retval; -} - -static __poll_t bttv_poll(struct file *file, poll_table *wait) -{ - struct bttv_fh *fh = file->private_data; - struct bttv_buffer *buf; - enum v4l2_field field; - __poll_t rc = 0; - __poll_t req_events = poll_requested_events(wait); - - if (v4l2_event_pending(&fh->fh)) - rc = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->fh.wait, wait); - - if (!(req_events & (EPOLLIN | EPOLLRDNORM))) - return rc; - - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { - if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI)) - return rc | EPOLLERR; - return rc | videobuf_poll_stream(file, &fh->vbi, wait); - } - - if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { - /* streaming capture */ - if (list_empty(&fh->cap.stream)) - return rc | EPOLLERR; - buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); - } else { - /* read() capture */ - if (NULL == fh->cap.read_buf) { - /* need to capture a new frame */ - if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) - return rc | EPOLLERR; - fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize); - if (NULL == fh->cap.read_buf) - return rc | EPOLLERR; - fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; - field = videobuf_next_field(&fh->cap); - if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) { - kfree (fh->cap.read_buf); - fh->cap.read_buf = NULL; - return rc | EPOLLERR; - } - fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); - fh->cap.read_off = 0; - } - buf = (struct bttv_buffer*)fh->cap.read_buf; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - rc = rc | EPOLLIN|EPOLLRDNORM; - return rc; -} - -static int bttv_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct bttv *btv = video_drvdata(file); - struct bttv_fh *fh; - enum v4l2_buf_type type = 0; - - dprintk("open dev=%s\n", video_device_node_name(vdev)); - - if (vdev->vfl_type == VFL_TYPE_VIDEO) { - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } else if (vdev->vfl_type == VFL_TYPE_VBI) { - type = V4L2_BUF_TYPE_VBI_CAPTURE; - } else { - WARN_ON(1); - return -ENODEV; - } - - dprintk("%d: open called (type=%s)\n", - btv->c.nr, v4l2_type_names[type]); - - /* allocate per filehandle data */ - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (unlikely(!fh)) - return -ENOMEM; - btv->users++; - file->private_data = fh; - - *fh = btv->init; - v4l2_fh_init(&fh->fh, vdev); - - fh->type = type; - - videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, - &btv->c.pci->dev, &btv->s_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct bttv_buffer), - fh, &btv->lock); - videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops, - &btv->c.pci->dev, &btv->s_lock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct bttv_buffer), - fh, &btv->lock); - set_tvnorm(btv,btv->tvnorm); - set_input(btv, btv->input, btv->tvnorm); - audio_mute(btv, btv->mute); - - /* The V4L2 spec requires one global set of cropping parameters - which only change on request. These are stored in btv->crop[1]. - However for compatibility with V4L apps and cropping unaware - V4L2 apps we now reset the cropping parameters as seen through - this fh, which is to say VIDIOC_G_SELECTION and scaling limit checks - will use btv->crop[0], the default cropping parameters for the - current video standard, and VIDIOC_S_FMT will not implicitly - change the cropping parameters until VIDIOC_S_SELECTION has been - called. */ - fh->do_crop = !reset_crop; /* module parameter */ - - /* Likewise there should be one global set of VBI capture - parameters, but for compatibility with V4L apps and earlier - driver versions each fh has its own parameters. */ - bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm); - - bttv_field_count(btv); - v4l2_fh_add(&fh->fh); - return 0; -} - -static int bttv_release(struct file *file) -{ - struct bttv_fh *fh = file->private_data; - struct bttv *btv = fh->btv; - - /* stop video capture */ - if (check_btres(fh, RESOURCE_VIDEO_STREAM)) { - videobuf_streamoff(&fh->cap); - free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM); - } - if (fh->cap.read_buf) { - buffer_release(&fh->cap,fh->cap.read_buf); - kfree(fh->cap.read_buf); - } - if (check_btres(fh, RESOURCE_VIDEO_READ)) { - free_btres_lock(btv, fh, RESOURCE_VIDEO_READ); - } - - /* stop vbi capture */ - if (check_btres(fh, RESOURCE_VBI)) { - videobuf_stop(&fh->vbi); - free_btres_lock(btv,fh,RESOURCE_VBI); - } - - /* free stuff */ - - videobuf_mmap_free(&fh->cap); - videobuf_mmap_free(&fh->vbi); - file->private_data = NULL; - - btv->users--; - bttv_field_count(btv); - - if (!btv->users) - audio_mute(btv, btv->mute); - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh); - return 0; -} - -static int -bttv_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct bttv_fh *fh = file->private_data; - - dprintk("%d: mmap type=%s 0x%lx+%ld\n", - fh->btv->c.nr, v4l2_type_names[fh->type], - vma->vm_start, vma->vm_end - vma->vm_start); - return videobuf_mmap_mapper(bttv_queue(fh),vma); -} - static const struct v4l2_file_operations bttv_fops = { .owner = THIS_MODULE, - .open = bttv_open, - .release = bttv_release, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .read = bttv_read, - .mmap = bttv_mmap, - .poll = bttv_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, }; static const struct v4l2_ioctl_ops bttv_ioctl_ops = { @@ -2715,17 +2329,18 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap, .vidioc_g_pixelaspect = bttv_g_pixelaspect, - .vidioc_reqbufs = bttv_reqbufs, - .vidioc_querybuf = bttv_querybuf, - .vidioc_qbuf = bttv_qbuf, - .vidioc_dqbuf = bttv_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_s_std = bttv_s_std, .vidioc_g_std = bttv_g_std, .vidioc_enum_input = bttv_enum_input, .vidioc_g_input = bttv_g_input, .vidioc_s_input = bttv_s_input, - .vidioc_streamon = bttv_streamon, - .vidioc_streamoff = bttv_streamoff, .vidioc_g_tuner = bttv_g_tuner, .vidioc_s_tuner = bttv_s_tuner, .vidioc_g_selection = bttv_g_selection, @@ -2756,52 +2371,40 @@ static int radio_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct bttv *btv = video_drvdata(file); - struct bttv_fh *fh; + int ret = v4l2_fh_open(file); + + if (ret) + return ret; dprintk("open dev=%s\n", video_device_node_name(vdev)); - dprintk("%d: open called (radio)\n", btv->c.nr); - /* allocate per filehandle data */ - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (unlikely(!fh)) - return -ENOMEM; - file->private_data = fh; - *fh = btv->init; - v4l2_fh_init(&fh->fh, vdev); - btv->radio_user++; audio_mute(btv, btv->mute); - v4l2_fh_add(&fh->fh); - return 0; } static int radio_release(struct file *file) { - struct bttv_fh *fh = file->private_data; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); struct saa6588_command cmd; - file->private_data = NULL; - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh); - btv->radio_user--; bttv_call_all(btv, core, command, SAA6588_CMD_CLOSE, &cmd); if (btv->radio_user == 0) btv->has_radio_tuner = 0; + + v4l2_fh_release(file); + return 0; } static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -2823,8 +2426,7 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -2837,8 +2439,7 @@ static int radio_s_tuner(struct file *file, void *priv, static int radio_s_hw_freq_seek(struct file *file, void *priv, const struct v4l2_hw_freq_seek *a) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (btv->has_tea575x) return snd_tea575x_s_hw_freq_seek(file, &btv->tea, a); @@ -2849,8 +2450,7 @@ static int radio_s_hw_freq_seek(struct file *file, void *priv, static int radio_enum_freq_bands(struct file *file, void *priv, struct v4l2_frequency_band *band) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); if (btv->has_tea575x) return snd_tea575x_enum_freq_bands(&btv->tea, band); @@ -2861,8 +2461,7 @@ static int radio_enum_freq_bands(struct file *file, void *priv, static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct bttv_fh *fh = file->private_data; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); struct saa6588_command cmd; cmd.block_count = count / 3; @@ -2879,23 +2478,17 @@ static ssize_t radio_read(struct file *file, char __user *data, static __poll_t radio_poll(struct file *file, poll_table *wait) { - struct bttv_fh *fh = file->private_data; - struct bttv *btv = fh->btv; - __poll_t req_events = poll_requested_events(wait); + struct bttv *btv = video_drvdata(file); struct saa6588_command cmd; - __poll_t res = 0; + __poll_t rc = v4l2_ctrl_poll(file, wait); - if (v4l2_event_pending(&fh->fh)) - res = EPOLLPRI; - else if (req_events & EPOLLPRI) - poll_wait(file, &fh->fh.wait, wait); radio_enable(btv); cmd.instance = file; cmd.event_list = wait; - cmd.poll_mask = res; + cmd.poll_mask = 0; bttv_call_all(btv, core, command, SAA6588_CMD_POLL, &cmd); - return cmd.poll_mask; + return rc | cmd.poll_mask; } static const struct v4l2_file_operations radio_fops = @@ -3070,17 +2663,19 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) /* capture request ? */ if (!list_empty(&btv->capture)) { - set->frame_irq = 1; - item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); - if (V4L2_FIELD_HAS_TOP(item->vb.field)) + set->frame_irq = BT848_RISC_VIDEO; + item = list_entry(btv->capture.next, struct bttv_buffer, list); + + if (V4L2_FIELD_HAS_TOP(item->vbuf.field)) set->top = item; - if (V4L2_FIELD_HAS_BOTTOM(item->vb.field)) + if (V4L2_FIELD_HAS_BOTTOM(item->vbuf.field)) set->bottom = item; /* capture request for other field ? */ - if (!V4L2_FIELD_HAS_BOTH(item->vb.field) && - (item->vb.queue.next != &btv->capture)) { - item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue); + if (!V4L2_FIELD_HAS_BOTH(item->vbuf.field) && + item->list.next != &btv->capture) { + item = list_entry(item->list.next, + struct bttv_buffer, list); /* Mike Isely - Only check * and set up the bottom field in the logic * below. Don't ever do the top field. This @@ -3108,13 +2703,18 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) * sync within a single frame time. (Out of * order fields can screw up deinterlacing * algorithms.) */ - if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) { - if (NULL == set->bottom && - V4L2_FIELD_BOTTOM == item->vb.field) { + if (!V4L2_FIELD_HAS_BOTH(item->vbuf.field)) { + if (!set->bottom && + item->vbuf.field == V4L2_FIELD_BOTTOM) set->bottom = item; + if (set->top && set->bottom) { + /* + * The buffer set has a top buffer and + * a bottom buffer and they are not + * copies of each other. + */ + set->top_irq = BT848_RISC_TOP; } - if (NULL != set->top && NULL != set->bottom) - set->top_irq = 2; } } } @@ -3136,44 +2736,47 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, if (irq_debug > 1) pr_debug("%d: wakeup: both=%p\n", btv->c.nr, wakeup->top); - wakeup->top->vb.ts = ts; - wakeup->top->vb.field_count = btv->field_count; - wakeup->top->vb.state = state; - wake_up(&wakeup->top->vb.done); + wakeup->top->vbuf.vb2_buf.timestamp = ts; + wakeup->top->vbuf.sequence = btv->field_count >> 1; + vb2_buffer_done(&wakeup->top->vbuf.vb2_buf, state); + if (btv->field_count == 0) + btor(BT848_INT_VSYNC, BT848_INT_MASK); } } else { if (NULL != wakeup->top && curr->top != wakeup->top) { if (irq_debug > 1) pr_debug("%d: wakeup: top=%p\n", btv->c.nr, wakeup->top); - wakeup->top->vb.ts = ts; - wakeup->top->vb.field_count = btv->field_count; - wakeup->top->vb.state = state; - wake_up(&wakeup->top->vb.done); + wakeup->top->vbuf.vb2_buf.timestamp = ts; + wakeup->top->vbuf.sequence = btv->field_count >> 1; + vb2_buffer_done(&wakeup->top->vbuf.vb2_buf, state); + if (btv->field_count == 0) + btor(BT848_INT_VSYNC, BT848_INT_MASK); } if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) { if (irq_debug > 1) pr_debug("%d: wakeup: bottom=%p\n", btv->c.nr, wakeup->bottom); - wakeup->bottom->vb.ts = ts; - wakeup->bottom->vb.field_count = btv->field_count; - wakeup->bottom->vb.state = state; - wake_up(&wakeup->bottom->vb.done); + wakeup->bottom->vbuf.vb2_buf.timestamp = ts; + wakeup->bottom->vbuf.sequence = btv->field_count >> 1; + vb2_buffer_done(&wakeup->bottom->vbuf.vb2_buf, state); + if (btv->field_count == 0) + btor(BT848_INT_VSYNC, BT848_INT_MASK); } } } static void bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, - unsigned int state) + unsigned int state) { if (NULL == wakeup) return; - - wakeup->vb.ts = ktime_get_ns(); - wakeup->vb.field_count = btv->field_count; - wakeup->vb.state = state; - wake_up(&wakeup->vb.done); + wakeup->vbuf.vb2_buf.timestamp = ktime_get_ns(); + wakeup->vbuf.sequence = btv->field_count >> 1; + vb2_buffer_done(&wakeup->vbuf.vb2_buf, state); + if (btv->field_count == 0) + btor(BT848_INT_VSYNC, BT848_INT_MASK); } static void bttv_irq_timeout(struct timer_list *t) @@ -3183,6 +2786,7 @@ static void bttv_irq_timeout(struct timer_list *t) struct bttv_buffer *ovbi; struct bttv_buffer *item; unsigned long flags; + int seqnr = 0; if (bttv_verbose) { pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ", @@ -3206,21 +2810,25 @@ static void bttv_irq_timeout(struct timer_list *t) bttv_set_dma(btv, 0); /* wake up */ - bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR); - bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR); + bttv_irq_wakeup_video(btv, &old, &new, VB2_BUF_STATE_DONE); + bttv_irq_wakeup_vbi(btv, ovbi, VB2_BUF_STATE_DONE); /* cancel all outstanding capture / vbi requests */ + if (btv->field_count) + seqnr++; while (!list_empty(&btv->capture)) { - item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); - list_del(&item->vb.queue); - item->vb.state = VIDEOBUF_ERROR; - wake_up(&item->vb.done); + item = list_entry(btv->capture.next, struct bttv_buffer, list); + list_del(&item->list); + item->vbuf.vb2_buf.timestamp = ktime_get_ns(); + item->vbuf.sequence = (btv->field_count >> 1) + seqnr++; + vb2_buffer_done(&item->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); } while (!list_empty(&btv->vcapture)) { - item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); - list_del(&item->vb.queue); - item->vb.state = VIDEOBUF_ERROR; - wake_up(&item->vb.done); + item = list_entry(btv->vcapture.next, struct bttv_buffer, list); + list_del(&item->list); + item->vbuf.vb2_buf.timestamp = ktime_get_ns(); + item->vbuf.sequence = (btv->field_count >> 1) + seqnr++; + vb2_buffer_done(&item->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); } btv->errors++; @@ -3239,11 +2847,11 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top_irq = 0; btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - - wakeup->vb.ts = ktime_get_ns(); - wakeup->vb.field_count = btv->field_count; - wakeup->vb.state = VIDEOBUF_DONE; - wake_up(&wakeup->vb.done); + wakeup->vbuf.vb2_buf.timestamp = ktime_get_ns(); + wakeup->vbuf.sequence = btv->field_count >> 1; + vb2_buffer_done(&wakeup->vbuf.vb2_buf, VB2_BUF_STATE_DONE); + if (btv->field_count == 0) + btor(BT848_INT_VSYNC, BT848_INT_MASK); spin_unlock(&btv->s_lock); } @@ -3280,7 +2888,7 @@ bttv_irq_switch_video(struct bttv *btv) /* switch over */ old = btv->curr; btv->curr = new; - btv->loop_irq &= ~1; + btv->loop_irq &= ~BT848_RISC_VIDEO; bttv_buffer_activate_video(btv, &new); bttv_set_dma(btv, 0); @@ -3291,7 +2899,7 @@ bttv_irq_switch_video(struct bttv *btv) } /* wake up finished buffers */ - bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE); + bttv_irq_wakeup_video(btv, &old, &new, VB2_BUF_STATE_DONE); spin_unlock(&btv->s_lock); } @@ -3305,7 +2913,7 @@ bttv_irq_switch_vbi(struct bttv *btv) spin_lock(&btv->s_lock); if (!list_empty(&btv->vcapture)) - new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); + new = list_entry(btv->vcapture.next, struct bttv_buffer, list); old = btv->cvbi; rc = btread(BT848_RISC_COUNT); @@ -3320,11 +2928,11 @@ bttv_irq_switch_vbi(struct bttv *btv) /* switch */ btv->cvbi = new; - btv->loop_irq &= ~4; + btv->loop_irq &= ~BT848_RISC_VBI; bttv_buffer_activate_vbi(btv, new); bttv_set_dma(btv, 0); - bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE); + bttv_irq_wakeup_vbi(btv, old, VB2_BUF_STATE_DONE); spin_unlock(&btv->s_lock); } @@ -3383,13 +2991,13 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) wake_up(&btv->i2c_queue); } - if ((astat & BT848_INT_RISCI) && (stat & (4<<28))) + if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_VBI)) bttv_irq_switch_vbi(btv); - if ((astat & BT848_INT_RISCI) && (stat & (2<<28))) + if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_TOP)) bttv_irq_wakeup_top(btv); - if ((astat & BT848_INT_RISCI) && (stat & (1<<28))) + if ((astat & BT848_INT_RISCI) && (stat & BT848_INT_RISCS_VIDEO)) bttv_irq_switch_video(btv); if ((astat & BT848_INT_HLOCK) && btv->opt_automute) @@ -3445,11 +3053,12 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) /* ----------------------------------------------------------------------- */ /* initialization */ -static void vdev_init(struct bttv *btv, - struct video_device *vfd, - const struct video_device *template, - const char *type_name) +static int vdev_init(struct bttv *btv, struct video_device *vfd, + const struct video_device *template, + const char *type_name) { + int err; + struct vb2_queue *q; *vfd = *template; vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release_empty; @@ -3463,6 +3072,36 @@ static void vdev_init(struct bttv *btv, v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER); v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER); } + + if (strcmp(type_name, "radio") == 0) + return 0; + + if (strcmp(type_name, "video") == 0) { + q = &btv->capq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->ops = &bttv_video_qops; + } else if (strcmp(type_name, "vbi") == 0) { + q = &btv->vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->ops = &bttv_vbi_qops; + } else { + return -EINVAL; + } + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF; + q->mem_ops = &vb2_dma_sg_memops; + q->drv_priv = btv; + q->gfp_flags = __GFP_DMA32; + q->buf_struct_size = sizeof(struct bttv_buffer); + q->lock = &btv->lock; + q->min_buffers_needed = 2; + q->dev = &btv->c.pci->dev; + err = vb2_queue_init(q); + if (err) + return err; + vfd->queue = q; + + return 0; } static void bttv_unregister_video(struct bttv *btv) @@ -3670,11 +3309,16 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) bttv_ctrl_coring.def = coring; /* fill struct bttv with some useful defaults */ - btv->init.btv = btv; - btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); - btv->init.width = 320; - btv->init.height = 240; + btv->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + btv->width = 320; + btv->height = 240; + btv->field = V4L2_FIELD_INTERLACED; btv->input = 0; + btv->tvnorm = 0; /* Index into bttv_tvnorms[] i.e. PAL. */ + bttv_vbi_fmt_reset(&btv->vbi_fmt, btv->tvnorm); + btv->vbi_count[0] = VBI_DEFLINES; + btv->vbi_count[1] = VBI_DEFLINES; + btv->do_crop = 0; v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 0xff00, 0x100, 32768); @@ -3749,7 +3393,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) result = btv->radio_ctrl_handler.error; goto fail2; } - set_input(btv, 0, btv->tvnorm); + set_input(btv, btv->input, btv->tvnorm); bttv_crop_reset(&btv->crop[0], btv->tvnorm); btv->crop[1] = btv->crop[0]; /* current = default */ disclaim_vbi_lines(btv); diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 4fa4b9da9634..436baf6c8b08 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -67,8 +67,10 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, /* scan lines */ sg = sglist; for (line = 0; line < store_lines; line++) { - if ((btv->opt_vcr_hack) && - (line >= (store_lines - VCR_HACK_LINES))) + if ((line >= (store_lines - VCR_HACK_LINES)) && + (btv->opt_vcr_hack || + (V4L2_FIELD_HAS_BOTH(btv->field) || + btv->field == V4L2_FIELD_ALTERNATE))) continue; while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); @@ -106,7 +108,7 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, /* save pointer to jmp instruction address */ risc->jmp = rp; - BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + WARN_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); return 0; } @@ -227,7 +229,7 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, /* save pointer to jmp instruction address */ risc->jmp = rp; - BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + WARN_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); return 0; } @@ -360,21 +362,75 @@ bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd) /* ---------------------------------------------------------- */ /* risc group / risc main loop / dma management */ -void -bttv_set_dma(struct bttv *btv, int override) +static void bttv_set_risc_status(struct bttv *btv) { - unsigned long cmd; - int capctl; + unsigned long cmd = BT848_RISC_JUMP; + if (btv->loop_irq) { + cmd |= BT848_RISC_IRQ; + cmd |= (btv->loop_irq & 0x0f) << 16; + cmd |= (~btv->loop_irq & 0x0f) << 20; + } + btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd); +} - btv->cap_ctl = 0; - if (NULL != btv->curr.top) btv->cap_ctl |= 0x02; - if (NULL != btv->curr.bottom) btv->cap_ctl |= 0x01; - if (NULL != btv->cvbi) btv->cap_ctl |= 0x0c; +static void bttv_set_irq_timer(struct bttv *btv) +{ + if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) + mod_timer(&btv->timeout, jiffies + BTTV_TIMEOUT); + else + del_timer(&btv->timeout); +} - capctl = 0; - capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00; /* capture */ - capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */ - capctl |= override; +static int bttv_set_capture_control(struct bttv *btv, int start_capture) +{ + int capctl = 0; + + if (btv->curr.top || btv->curr.bottom) + capctl = BT848_CAP_CTL_CAPTURE_ODD | + BT848_CAP_CTL_CAPTURE_EVEN; + + if (btv->cvbi) + capctl |= BT848_CAP_CTL_CAPTURE_VBI_ODD | + BT848_CAP_CTL_CAPTURE_VBI_EVEN; + + capctl |= start_capture; + + btaor(capctl, ~0x0f, BT848_CAP_CTL); + + return capctl; +} + +static void bttv_start_dma(struct bttv *btv) +{ + if (btv->dma_on) + return; + btwrite(btv->main.dma, BT848_RISC_STRT_ADD); + btor(BT848_GPIO_DMA_CTL_RISC_ENABLE | BT848_GPIO_DMA_CTL_FIFO_ENABLE, + BT848_GPIO_DMA_CTL); + btv->dma_on = 1; +} + +static void bttv_stop_dma(struct bttv *btv) +{ + if (!btv->dma_on) + return; + btand(~(BT848_GPIO_DMA_CTL_RISC_ENABLE | + BT848_GPIO_DMA_CTL_FIFO_ENABLE), BT848_GPIO_DMA_CTL); + btv->dma_on = 0; +} + +void bttv_set_dma(struct bttv *btv, int start_capture) +{ + int capctl = 0; + + bttv_set_risc_status(btv); + bttv_set_irq_timer(btv); + capctl = bttv_set_capture_control(btv, start_capture); + + if (capctl) + bttv_start_dma(btv); + else + bttv_stop_dma(btv); d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n", btv->c.nr,capctl,btv->loop_irq, @@ -382,34 +438,6 @@ bttv_set_dma(struct bttv *btv, int override) btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0, btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); - - cmd = BT848_RISC_JUMP; - if (btv->loop_irq) { - cmd |= BT848_RISC_IRQ; - cmd |= (btv->loop_irq & 0x0f) << 16; - cmd |= (~btv->loop_irq & 0x0f) << 20; - } - if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) { - mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT); - } else { - del_timer(&btv->timeout); - } - btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd); - - btaor(capctl, ~0x0f, BT848_CAP_CTL); - if (capctl) { - if (btv->dma_on) - return; - btwrite(btv->main.dma, BT848_RISC_STRT_ADD); - btor(3, BT848_GPIO_DMA_CTL); - btv->dma_on = 1; - } else { - if (!btv->dma_on) - return; - btand(~3, BT848_GPIO_DMA_CTL); - btv->dma_on = 0; - } - return; } int @@ -478,17 +506,50 @@ bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, return 0; } -void -bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf) +int bttv_buffer_risc_vbi(struct bttv *btv, struct bttv_buffer *buf) { - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + int r = 0; + unsigned int offset; + unsigned int bpl = 2044; /* max. vbipack */ + unsigned int padding = VBI_BPL - bpl; + unsigned int skip_lines0 = 0; + unsigned int skip_lines1 = 0; + unsigned int min_vdelay = MIN_VDELAY; - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - btcx_riscmem_free(btv->c.pci,&buf->bottom); - btcx_riscmem_free(btv->c.pci,&buf->top); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + const struct bttv_tvnorm *tvnorm = btv->vbi_fmt.tvnorm; + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vbuf.vb2_buf, 0); + struct scatterlist *list = sgt->sgl; + + if (btv->vbi_fmt.fmt.count[0] > 0) + skip_lines0 = max(0, (btv->vbi_fmt.fmt.start[0] - + tvnorm->vbistart[0])); + if (btv->vbi_fmt.fmt.count[1] > 0) + skip_lines1 = max(0, (btv->vbi_fmt.fmt.start[1] - + tvnorm->vbistart[1])); + + if (btv->vbi_fmt.fmt.count[0] > 0) { + r = bttv_risc_packed(btv, &buf->top, list, 0, bpl, padding, + skip_lines0, btv->vbi_fmt.fmt.count[0]); + if (r) + return r; + } + + if (btv->vbi_fmt.fmt.count[1] > 0) { + offset = btv->vbi_fmt.fmt.count[0] * VBI_BPL; + r = bttv_risc_packed(btv, &buf->bottom, list, offset, bpl, + padding, skip_lines1, + btv->vbi_fmt.fmt.count[1]); + if (r) + return r; + } + + if (btv->vbi_fmt.end >= tvnorm->cropcap.bounds.top) + min_vdelay += btv->vbi_fmt.end - tvnorm->cropcap.bounds.top; + + /* For bttv_buffer_activate_vbi(). */ + buf->geo.vdelay = min_vdelay; + + return r; } int @@ -508,8 +569,7 @@ bttv_buffer_activate_vbi(struct bttv *btv, if (vbi) { unsigned int crop, vdelay; - vbi->vb.state = VIDEOBUF_ACTIVE; - list_del(&vbi->vb.queue); + list_del(&vbi->list); /* VDELAY is start of video, end of VBI capturing. */ crop = btread(BT848_E_CROP); @@ -525,12 +585,12 @@ bttv_buffer_activate_vbi(struct bttv *btv, btwrite(crop, BT848_O_CROP); } - if (vbi->vbi_count[0] > 0) { + if (btv->vbi_count[0] > 0) { top = &vbi->top; top_irq_flags = 4; } - if (vbi->vbi_count[1] > 0) { + if (btv->vbi_count[1] > 0) { top_irq_flags = 0; bottom = &vbi->bottom; bottom_irq_flags = 4; @@ -550,16 +610,13 @@ bttv_buffer_activate_video(struct bttv *btv, /* video capture */ if (NULL != set->top && NULL != set->bottom) { if (set->top == set->bottom) { - set->top->vb.state = VIDEOBUF_ACTIVE; - if (set->top->vb.queue.next) - list_del(&set->top->vb.queue); + if (set->top->list.next) + list_del(&set->top->list); } else { - set->top->vb.state = VIDEOBUF_ACTIVE; - set->bottom->vb.state = VIDEOBUF_ACTIVE; - if (set->top->vb.queue.next) - list_del(&set->top->vb.queue); - if (set->bottom->vb.queue.next) - list_del(&set->bottom->vb.queue); + if (set->top->list.next) + list_del(&set->top->list); + if (set->bottom->list.next) + list_del(&set->bottom->list); } bttv_apply_geo(btv, &set->top->geo, 1); bttv_apply_geo(btv, &set->bottom->geo,0); @@ -572,9 +629,8 @@ bttv_buffer_activate_video(struct bttv *btv, btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05), ~0x0f, BT848_COLOR_CTL); } else if (NULL != set->top) { - set->top->vb.state = VIDEOBUF_ACTIVE; - if (set->top->vb.queue.next) - list_del(&set->top->vb.queue); + if (set->top->list.next) + list_del(&set->top->list); bttv_apply_geo(btv, &set->top->geo,1); bttv_apply_geo(btv, &set->top->geo,0); bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, @@ -583,9 +639,8 @@ bttv_buffer_activate_video(struct bttv *btv, btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT); btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); } else if (NULL != set->bottom) { - set->bottom->vb.state = VIDEOBUF_ACTIVE; - if (set->bottom->vb.queue.next) - list_del(&set->bottom->vb.queue); + if (set->bottom->list.next) + list_del(&set->bottom->list); bttv_apply_geo(btv, &set->bottom->geo,1); bttv_apply_geo(btv, &set->bottom->geo,0); bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); @@ -606,156 +661,146 @@ bttv_buffer_activate_video(struct bttv *btv, int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) { - const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm; - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - dprintk("%d: buffer field: %s format: 0x%08x size: %dx%d\n", - btv->c.nr, v4l2_field_names[buf->vb.field], - buf->fmt->fourcc, buf->vb.width, buf->vb.height); + int r = 0; + const struct bttv_tvnorm *tvnorm = bttv_tvnorms + btv->tvnorm; + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vbuf.vb2_buf, 0); + struct scatterlist *list = sgt->sgl; + unsigned long size = (btv->fmt->depth * btv->width * btv->height) >> 3; /* packed pixel modes */ - if (buf->fmt->flags & FORMAT_FLAGS_PACKED) { - int bpl = (buf->fmt->depth >> 3) * buf->vb.width; - int bpf = bpl * (buf->vb.height >> 1); + if (btv->fmt->flags & FORMAT_FLAGS_PACKED) { + int bpl = (btv->fmt->depth >> 3) * btv->width; + int bpf = bpl * (btv->height >> 1); - bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, - V4L2_FIELD_HAS_BOTH(buf->vb.field), - tvnorm,&buf->crop); - - switch (buf->vb.field) { + bttv_calc_geo(btv, &buf->geo, btv->width, btv->height, + V4L2_FIELD_HAS_BOTH(buf->vbuf.field), tvnorm, + &btv->crop[!!btv->do_crop].rect); + switch (buf->vbuf.field) { case V4L2_FIELD_TOP: - bttv_risc_packed(btv,&buf->top,dma->sglist, - /* offset */ 0,bpl, - /* padding */ 0,/* skip_lines */ 0, - buf->vb.height); + r = bttv_risc_packed(btv, &buf->top, list, 0, bpl, 0, + 0, btv->height); break; case V4L2_FIELD_BOTTOM: - bttv_risc_packed(btv,&buf->bottom,dma->sglist, - 0,bpl,0,0,buf->vb.height); + r = bttv_risc_packed(btv, &buf->bottom, list, 0, bpl, + 0, 0, btv->height); break; case V4L2_FIELD_INTERLACED: - bttv_risc_packed(btv,&buf->top,dma->sglist, - 0,bpl,bpl,0,buf->vb.height >> 1); - bttv_risc_packed(btv,&buf->bottom,dma->sglist, - bpl,bpl,bpl,0,buf->vb.height >> 1); + r = bttv_risc_packed(btv, &buf->top, list, 0, bpl, + bpl, 0, btv->height >> 1); + r = bttv_risc_packed(btv, &buf->bottom, list, bpl, + bpl, bpl, 0, btv->height >> 1); break; case V4L2_FIELD_SEQ_TB: - bttv_risc_packed(btv,&buf->top,dma->sglist, - 0,bpl,0,0,buf->vb.height >> 1); - bttv_risc_packed(btv,&buf->bottom,dma->sglist, - bpf,bpl,0,0,buf->vb.height >> 1); + r = bttv_risc_packed(btv, &buf->top, list, 0, bpl, 0, + 0, btv->height >> 1); + r = bttv_risc_packed(btv, &buf->bottom, list, bpf, + bpl, 0, 0, btv->height >> 1); break; default: - BUG(); + WARN_ON(1); + return -EINVAL; } } - /* planar modes */ - if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) { + if (btv->fmt->flags & FORMAT_FLAGS_PLANAR) { int uoffset, voffset; int ypadding, cpadding, lines; /* calculate chroma offsets */ - uoffset = buf->vb.width * buf->vb.height; - voffset = buf->vb.width * buf->vb.height; - if (buf->fmt->flags & FORMAT_FLAGS_CrCb) { + uoffset = btv->width * btv->height; + voffset = btv->width * btv->height; + if (btv->fmt->flags & FORMAT_FLAGS_CrCb) { /* Y-Cr-Cb plane order */ - uoffset >>= buf->fmt->hshift; - uoffset >>= buf->fmt->vshift; + uoffset >>= btv->fmt->hshift; + uoffset >>= btv->fmt->vshift; uoffset += voffset; } else { /* Y-Cb-Cr plane order */ - voffset >>= buf->fmt->hshift; - voffset >>= buf->fmt->vshift; + voffset >>= btv->fmt->hshift; + voffset >>= btv->fmt->vshift; voffset += uoffset; } - - switch (buf->vb.field) { + switch (buf->vbuf.field) { case V4L2_FIELD_TOP: - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,/* both_fields */ 0, - tvnorm,&buf->crop); - bttv_risc_planar(btv, &buf->top, dma->sglist, - 0,buf->vb.width,0,buf->vb.height, - uoffset,voffset,buf->fmt->hshift, - buf->fmt->vshift,0); + bttv_calc_geo(btv, &buf->geo, btv->width, btv->height, + 0, tvnorm, + &btv->crop[!!btv->do_crop].rect); + r = bttv_risc_planar(btv, &buf->top, list, 0, + btv->width, 0, btv->height, + uoffset, voffset, + btv->fmt->hshift, + btv->fmt->vshift, 0); break; case V4L2_FIELD_BOTTOM: - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0, - tvnorm,&buf->crop); - bttv_risc_planar(btv, &buf->bottom, dma->sglist, - 0,buf->vb.width,0,buf->vb.height, - uoffset,voffset,buf->fmt->hshift, - buf->fmt->vshift,0); + bttv_calc_geo(btv, &buf->geo, btv->width, btv->height, + 0, tvnorm, + &btv->crop[!!btv->do_crop].rect); + r = bttv_risc_planar(btv, &buf->bottom, list, 0, + btv->width, 0, btv->height, + uoffset, voffset, + btv->fmt->hshift, + btv->fmt->vshift, 0); break; case V4L2_FIELD_INTERLACED: - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1, - tvnorm,&buf->crop); - lines = buf->vb.height >> 1; - ypadding = buf->vb.width; - cpadding = buf->vb.width >> buf->fmt->hshift; - bttv_risc_planar(btv,&buf->top, - dma->sglist, - 0,buf->vb.width,ypadding,lines, - uoffset,voffset, - buf->fmt->hshift, - buf->fmt->vshift, - cpadding); - bttv_risc_planar(btv,&buf->bottom, - dma->sglist, - ypadding,buf->vb.width,ypadding,lines, - uoffset+cpadding, - voffset+cpadding, - buf->fmt->hshift, - buf->fmt->vshift, - cpadding); + bttv_calc_geo(btv, &buf->geo, btv->width, btv->height, + 1, tvnorm, + &btv->crop[!!btv->do_crop].rect); + lines = btv->height >> 1; + ypadding = btv->width; + cpadding = btv->width >> btv->fmt->hshift; + r = bttv_risc_planar(btv, &buf->top, list, 0, + btv->width, ypadding, lines, + uoffset, voffset, + btv->fmt->hshift, + btv->fmt->vshift, cpadding); + + r = bttv_risc_planar(btv, &buf->bottom, list, + ypadding, btv->width, ypadding, + lines, uoffset + cpadding, + voffset + cpadding, + btv->fmt->hshift, + btv->fmt->vshift, cpadding); break; case V4L2_FIELD_SEQ_TB: - bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1, - tvnorm,&buf->crop); - lines = buf->vb.height >> 1; - ypadding = buf->vb.width; - cpadding = buf->vb.width >> buf->fmt->hshift; - bttv_risc_planar(btv,&buf->top, - dma->sglist, - 0,buf->vb.width,0,lines, - uoffset >> 1, - voffset >> 1, - buf->fmt->hshift, - buf->fmt->vshift, - 0); - bttv_risc_planar(btv,&buf->bottom, - dma->sglist, - lines * ypadding,buf->vb.width,0,lines, - lines * ypadding + (uoffset >> 1), - lines * ypadding + (voffset >> 1), - buf->fmt->hshift, - buf->fmt->vshift, - 0); + bttv_calc_geo(btv, &buf->geo, btv->width, btv->height, + 1, tvnorm, + &btv->crop[!!btv->do_crop].rect); + lines = btv->height >> 1; + ypadding = btv->width; + cpadding = btv->width >> btv->fmt->hshift; + r = bttv_risc_planar(btv, &buf->top, list, 0, + btv->width, 0, lines, + uoffset >> 1, voffset >> 1, + btv->fmt->hshift, + btv->fmt->vshift, 0); + r = bttv_risc_planar(btv, &buf->bottom, list, + lines * ypadding, + btv->width, 0, lines, + lines * ypadding + (uoffset >> 1), + lines * ypadding + (voffset >> 1), + btv->fmt->hshift, + btv->fmt->vshift, 0); break; default: - BUG(); + WARN_ON(1); + return -EINVAL; } } - /* raw data */ - if (buf->fmt->flags & FORMAT_FLAGS_RAW) { + if (btv->fmt->flags & FORMAT_FLAGS_RAW) { /* build risc code */ - buf->vb.field = V4L2_FIELD_SEQ_TB; - bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, - 1,tvnorm,&buf->crop); - bttv_risc_packed(btv, &buf->top, dma->sglist, - /* offset */ 0, RAW_BPL, /* padding */ 0, - /* skip_lines */ 0, RAW_LINES); - bttv_risc_packed(btv, &buf->bottom, dma->sglist, - buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES); + buf->vbuf.field = V4L2_FIELD_SEQ_TB; + bttv_calc_geo(btv, &buf->geo, tvnorm->swidth, tvnorm->sheight, + 1, tvnorm, &btv->crop[!!btv->do_crop].rect); + r = bttv_risc_packed(btv, &buf->top, list, 0, RAW_BPL, 0, 0, + RAW_LINES); + r = bttv_risc_packed(btv, &buf->bottom, list, size / 2, + RAW_BPL, 0, 0, RAW_LINES); } /* copy format info */ - buf->btformat = buf->fmt->btformat; - buf->btswap = buf->fmt->btswap; - return 0; + buf->btformat = btv->fmt->btformat; + buf->btswap = btv->fmt->btswap; + + return r; } diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c index ce36a2c0f60b..ab213e51ec95 100644 --- a/drivers/media/pci/bt8xx/bttv-vbi.c +++ b/drivers/media/pci/bt8xx/bttv-vbi.c @@ -34,16 +34,6 @@ to be about 244. */ #define VBI_OFFSET 244 -/* 2048 for compatibility with earlier driver versions. The driver - really stores 1024 + tvnorm->vbipack * 4 samples per line in the - buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI - is 0x1FF DWORDs) and VBI read()s store a frame counter in the last - four bytes of the VBI image. */ -#define VBI_BPL 2048 - -/* Compatibility. */ -#define VBI_DEFLINES 16 - static unsigned int vbibufs = 4; static unsigned int vbi_debug; @@ -67,165 +57,123 @@ do { \ /* ----------------------------------------------------------------------- */ /* vbi risc code + mm */ -static int vbi_buffer_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup_vbi(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) { - struct bttv_fh *fh = q->priv_data; - struct bttv *btv = fh->btv; + struct bttv *btv = vb2_get_drv_priv(q); + unsigned int size = IMAGE_SIZE(&btv->vbi_fmt.fmt); - if (0 == *count) - *count = vbibufs; - - *size = IMAGE_SIZE(&fh->vbi_fmt.fmt); - - dprintk("setup: samples=%u start=%d,%d count=%u,%u\n", - fh->vbi_fmt.fmt.samples_per_line, - fh->vbi_fmt.fmt.start[0], - fh->vbi_fmt.fmt.start[1], - fh->vbi_fmt.fmt.count[0], - fh->vbi_fmt.fmt.count[1]); + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + *num_planes = 1; + sizes[0] = size; return 0; } -static int vbi_buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, - enum v4l2_field field) +static void buf_queue_vbi(struct vb2_buffer *vb) { - struct bttv_fh *fh = q->priv_data; - struct bttv *btv = fh->btv; - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); - const struct bttv_tvnorm *tvnorm; - unsigned int skip_lines0, skip_lines1, min_vdelay; - int redo_dma_risc; - int rc; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); + unsigned long flags; - buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + spin_lock_irqsave(&btv->s_lock, flags); + if (list_empty(&btv->vcapture)) { + btv->loop_irq = BT848_RISC_VBI; + if (vb2_is_streaming(&btv->capq)) + btv->loop_irq |= BT848_RISC_VIDEO; + bttv_set_dma(btv, BT848_CAP_CTL_CAPTURE_VBI_ODD | + BT848_CAP_CTL_CAPTURE_VBI_EVEN); + } + list_add_tail(&buf->list, &btv->vcapture); + spin_unlock_irqrestore(&btv->s_lock, flags); +} + +static int buf_prepare_vbi(struct vb2_buffer *vb) +{ + int ret = 0; + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); + unsigned int size = IMAGE_SIZE(&btv->vbi_fmt.fmt); + + if (vb2_plane_size(vb, 0) < size) return -EINVAL; + vb2_set_plane_payload(vb, 0, size); + buf->vbuf.field = V4L2_FIELD_NONE; + ret = bttv_buffer_risc_vbi(btv, buf); - tvnorm = fh->vbi_fmt.tvnorm; - - /* There's no VBI_VDELAY register, RISC must skip the lines - we don't want. With default parameters we skip zero lines - as earlier driver versions did. The driver permits video - standard changes while capturing, so we use vbi_fmt.tvnorm - instead of btv->tvnorm to skip zero lines after video - standard changes as well. */ - - skip_lines0 = 0; - skip_lines1 = 0; - - if (fh->vbi_fmt.fmt.count[0] > 0) - skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0] - - tvnorm->vbistart[0])); - if (fh->vbi_fmt.fmt.count[1] > 0) - skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1] - - tvnorm->vbistart[1])); - - redo_dma_risc = 0; - - if (buf->vbi_skip[0] != skip_lines0 || - buf->vbi_skip[1] != skip_lines1 || - buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] || - buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) { - buf->vbi_skip[0] = skip_lines0; - buf->vbi_skip[1] = skip_lines1; - buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0]; - buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1]; - redo_dma_risc = 1; - } - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - redo_dma_risc = 1; - if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL))) - goto fail; - } - - if (redo_dma_risc) { - unsigned int bpl, padding, offset; - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - bpl = 2044; /* max. vbipack */ - padding = VBI_BPL - bpl; - - if (fh->vbi_fmt.fmt.count[0] > 0) { - rc = bttv_risc_packed(btv, &buf->top, - dma->sglist, - /* offset */ 0, bpl, - padding, skip_lines0, - fh->vbi_fmt.fmt.count[0]); - if (0 != rc) - goto fail; - } - - if (fh->vbi_fmt.fmt.count[1] > 0) { - offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL; - - rc = bttv_risc_packed(btv, &buf->bottom, - dma->sglist, - offset, bpl, - padding, skip_lines1, - fh->vbi_fmt.fmt.count[1]); - if (0 != rc) - goto fail; - } - } - - /* VBI capturing ends at VDELAY, start of video capturing, - no matter where the RISC program ends. VDELAY minimum is 2, - bounds.top is the corresponding first field line number - times two. VDELAY counts half field lines. */ - min_vdelay = MIN_VDELAY; - if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top) - min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top; - - /* For bttv_buffer_activate_vbi(). */ - buf->geo.vdelay = min_vdelay; - - buf->vb.state = VIDEOBUF_PREPARED; - buf->vb.field = field; - dprintk("buf prepare %p: top=%p bottom=%p field=%s\n", - vb, &buf->top, &buf->bottom, - v4l2_field_names[buf->vb.field]); - return 0; - - fail: - bttv_dma_free(q,btv,buf); - return rc; + return ret; } -static void -vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +static void buf_cleanup_vbi(struct vb2_buffer *vb) { - struct bttv_fh *fh = q->priv_data; - struct bttv *btv = fh->btv; - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct bttv_buffer *buf = container_of(vbuf, struct bttv_buffer, vbuf); + struct vb2_queue *vq = vb->vb2_queue; + struct bttv *btv = vb2_get_drv_priv(vq); - dprintk("queue %p\n",vb); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue,&btv->vcapture); - if (NULL == btv->cvbi) { - fh->btv->loop_irq |= 4; - bttv_set_dma(btv,0x0c); - } + btcx_riscmem_free(btv->c.pci, &buf->top); + btcx_riscmem_free(btv->c.pci, &buf->bottom); } -static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +static int start_streaming_vbi(struct vb2_queue *q, unsigned int count) { - struct bttv_fh *fh = q->priv_data; - struct bttv *btv = fh->btv; - struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); + int ret; + int seqnr = 0; + struct bttv_buffer *buf; + struct bttv *btv = vb2_get_drv_priv(q); - dprintk("free %p\n",vb); - bttv_dma_free(q,fh->btv,buf); + btv->framedrop = 0; + ret = check_alloc_btres_lock(btv, RESOURCE_VBI); + if (ret == 0) { + if (btv->field_count) + seqnr++; + while (!list_empty(&btv->vcapture)) { + buf = list_entry(btv->vcapture.next, + struct bttv_buffer, list); + list_del(&buf->list); + buf->vbuf.sequence = (btv->field_count >> 1) + seqnr++; + vb2_buffer_done(&buf->vbuf.vb2_buf, + VB2_BUF_STATE_QUEUED); + } + return !ret; + } + if (!vb2_is_streaming(&btv->capq)) { + init_irqreg(btv); + btv->field_count = 0; + } + return !ret; } -const struct videobuf_queue_ops bttv_vbi_qops = { - .buf_setup = vbi_buffer_setup, - .buf_prepare = vbi_buffer_prepare, - .buf_queue = vbi_buffer_queue, - .buf_release = vbi_buffer_release, +static void stop_streaming_vbi(struct vb2_queue *q) +{ + struct bttv *btv = vb2_get_drv_priv(q); + unsigned long flags; + + vb2_wait_for_all_buffers(q); + spin_lock_irqsave(&btv->s_lock, flags); + free_btres_lock(btv, RESOURCE_VBI); + if (!vb2_is_streaming(&btv->capq)) { + /* stop field counter */ + btand(~BT848_INT_VSYNC, BT848_INT_MASK); + } + spin_unlock_irqrestore(&btv->s_lock, flags); +} + +const struct vb2_ops bttv_vbi_qops = { + .queue_setup = queue_setup_vbi, + .buf_queue = buf_queue_vbi, + .buf_prepare = buf_prepare_vbi, + .buf_cleanup = buf_cleanup_vbi, + .start_streaming = start_streaming_vbi, + .stop_streaming = stop_streaming_vbi, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* ----------------------------------------------------------------------- */ @@ -250,7 +198,7 @@ static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm, if (min_start > max_start) return -EBUSY; - BUG_ON(max_start >= max_end); + WARN_ON(max_start >= max_end); f->sampling_rate = tvnorm->Fsc; f->samples_per_line = VBI_BPL; @@ -299,8 +247,7 @@ static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm, int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); const struct bttv_tvnorm *tvnorm; __s32 crop_start; @@ -317,8 +264,7 @@ int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; + struct bttv *btv = video_drvdata(file); const struct bttv_tvnorm *tvnorm; __s32 start1, end; int rc; @@ -326,7 +272,7 @@ int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) mutex_lock(&btv->lock); rc = -EBUSY; - if (fh->resources & RESOURCE_VBI) + if (btv->resources & RESOURCE_VBI) goto fail; tvnorm = &bttv_tvnorms[btv->tvnorm]; @@ -346,13 +292,9 @@ int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) because vbi_fmt.end counts field lines times two. */ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2; - mutex_lock(&fh->vbi.vb_lock); - - fh->vbi_fmt.fmt = frt->fmt.vbi; - fh->vbi_fmt.tvnorm = tvnorm; - fh->vbi_fmt.end = end; - - mutex_unlock(&fh->vbi.vb_lock); + btv->vbi_fmt.fmt = frt->fmt.vbi; + btv->vbi_fmt.tvnorm = tvnorm; + btv->vbi_fmt.end = end; rc = 0; @@ -365,14 +307,14 @@ int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { - struct bttv_fh *fh = f; const struct bttv_tvnorm *tvnorm; + struct bttv *btv = video_drvdata(file); - frt->fmt.vbi = fh->vbi_fmt.fmt; + frt->fmt.vbi = btv->vbi_fmt.fmt; - tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; + tvnorm = &bttv_tvnorms[btv->tvnorm]; - if (tvnorm != fh->vbi_fmt.tvnorm) { + if (tvnorm != btv->vbi_fmt.tvnorm) { __s32 max_end; unsigned int i; @@ -388,9 +330,8 @@ int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) for (i = 0; i < 2; ++i) { __s32 new_start; - new_start = frt->fmt.vbi.start[i] - + tvnorm->vbistart[i] - - fh->vbi_fmt.tvnorm->vbistart[i]; + new_start = frt->fmt.vbi.start[i] + tvnorm->vbistart[i] + - btv->vbi_fmt.tvnorm->vbistart[i]; frt->fmt.vbi.start[i] = min(new_start, max_end - 1); frt->fmt.vbi.count[i] = @@ -430,8 +371,8 @@ void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm) real_count = ((tvnorm->cropcap.defrect.top >> 1) - tvnorm->vbistart[0]); - BUG_ON(real_samples_per_line > VBI_BPL); - BUG_ON(real_count > VBI_DEFLINES); + WARN_ON(real_samples_per_line > VBI_BPL); + WARN_ON(real_count > VBI_DEFLINES); f->tvnorm = tvnorm; diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 717f002a41df..0368a583cf07 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -142,19 +142,15 @@ struct bttv_geometry { struct bttv_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_v4l2_buffer vbuf; + struct list_head list; /* bttv specific */ - const struct bttv_format *fmt; - unsigned int tvnorm; int btformat; int btswap; struct bttv_geometry geo; struct btcx_riscmem top; struct btcx_riscmem bottom; - struct v4l2_rect crop; - unsigned int vbi_skip[2]; - unsigned int vbi_count[2]; }; struct bttv_buffer_set { @@ -176,6 +172,8 @@ struct bttv_vbi_fmt { }; /* bttv-vbi.c */ +extern const struct vb2_ops bttv_vbi_qops; + void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm); struct bttv_crop { @@ -192,31 +190,6 @@ struct bttv_crop { __s32 max_scaled_height; }; -struct bttv_fh { - /* This must be the first field in this struct */ - struct v4l2_fh fh; - - struct bttv *btv; - int resources; - enum v4l2_buf_type type; - - /* video capture */ - struct videobuf_queue cap; - const struct bttv_format *fmt; - int width; - int height; - - /* Application called VIDIOC_S_SELECTION. */ - int do_crop; - - /* vbi capture */ - struct videobuf_queue vbi; - /* Current VBI capture window as seen through this fh (cannot - be global for compatibility with earlier drivers). Protected - by struct bttv.lock and struct bttv_fh.vbi.lock. */ - struct bttv_vbi_fmt vbi_fmt; -}; - /* ---------------------------------------------------------- */ /* bttv-risc.c */ @@ -237,20 +210,27 @@ int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf); int bttv_buffer_activate_video(struct bttv *btv, struct bttv_buffer_set *set); +int bttv_buffer_risc_vbi(struct bttv *btv, struct bttv_buffer *buf); int bttv_buffer_activate_vbi(struct bttv *btv, struct bttv_buffer *vbi); -void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv, - struct bttv_buffer *buf); /* ---------------------------------------------------------- */ /* bttv-vbi.c */ +/* + * 2048 for compatibility with earlier driver versions. The driver really + * stores 1024 + tvnorm->vbipack * 4 samples per line in the buffer. Note + * tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI is 0x1FF DWORDs) and + * VBI read()s store a frame counter in the last four bytes of the VBI image. + */ +#define VBI_BPL 2048 + +#define VBI_DEFLINES 16 + int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); -extern const struct videobuf_queue_ops bttv_vbi_qops; - /* ---------------------------------------------------------- */ /* bttv-gpio.c */ @@ -275,6 +255,8 @@ extern int fini_bttv_i2c(struct bttv *btv); extern unsigned int bttv_verbose; extern unsigned int bttv_debug; extern unsigned int bttv_gpio; +int check_alloc_btres_lock(struct bttv *btv, int bit); +void free_btres_lock(struct bttv *btv, int bits); extern void bttv_gpio_tracking(struct bttv *btv, char *comment); #define dprintk(fmt, ...) \ @@ -396,7 +378,7 @@ struct bttv { v4l2_std_id std; int hue, contrast, bright, saturation; struct v4l2_framebuffer fbuf; - unsigned int field_count; + __u32 field_count; /* various options */ int opt_combfilter; @@ -436,7 +418,6 @@ struct bttv { int loop_irq; int new_input; - unsigned long cap_ctl; unsigned long dma_on; struct timer_list timeout; struct bttv_suspend_state state; @@ -448,7 +429,25 @@ struct bttv { unsigned int irq_me; unsigned int users; - struct bttv_fh init; + struct v4l2_fh fh; + enum v4l2_buf_type type; + + enum v4l2_field field; + int field_last; + + /* video capture */ + struct vb2_queue capq; + const struct bttv_format *fmt; + int width; + int height; + + /* vbi capture */ + struct vb2_queue vbiq; + struct bttv_vbi_fmt vbi_fmt; + unsigned int vbi_count[2]; + + /* Application called VIDIOC_S_SELECTION. */ + int do_crop; /* used to make dvb-bt8xx autoloadable */ struct work_struct request_module_wk; @@ -487,6 +486,8 @@ static inline unsigned int bttv_muxsel(const struct bttv *btv, #endif +void init_irqreg(struct bttv *btv); + #define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr)) #define btread(adr) readl(btv->bt848_mmio+(adr)) diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c index 160c8377e352..c85eb8d25837 100644 --- a/drivers/media/pci/cx18/cx18-gpio.c +++ b/drivers/media/pci/cx18/cx18-gpio.c @@ -307,7 +307,7 @@ int cx18_gpio_register(struct cx18 *cx, u32 hw) void cx18_reset_ir_gpio(void *data) { - struct cx18 *cx = to_cx18((struct v4l2_device *)data); + struct cx18 *cx = to_cx18(data); if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) return; diff --git a/drivers/media/pci/cx18/cx18-irq.c b/drivers/media/pci/cx18/cx18-irq.c index fb10e9c2c5b8..db63077821b1 100644 --- a/drivers/media/pci/cx18/cx18-irq.c +++ b/drivers/media/pci/cx18/cx18-irq.c @@ -30,7 +30,7 @@ static void epu_cmd(struct cx18 *cx, u32 sw1) irqreturn_t cx18_irq_handler(int irq, void *dev_id) { - struct cx18 *cx = (struct cx18 *)dev_id; + struct cx18 *cx = dev_id; u32 sw1, sw2, hw2; sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask; diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 2ce2914576cf..c8705d786cdd 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -554,14 +554,14 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev, for (i = 0; i < 4; i++) { risc = cx_read(ch->cmds_start + 4 * (i + 14)); - pr_warn("%s: risc%d: ", dev->name, i); + pr_warn("%s: risc%d:", dev->name, i); cx23885_risc_decode(risc); } for (i = 0; i < (64 >> 2); i += n) { risc = cx_read(ch->ctrl_start + 4 * i); /* No consideration for bits 63-32 */ - pr_warn("%s: (0x%08x) iq %x: ", dev->name, + pr_warn("%s: (0x%08x) iq %x:", dev->name, ch->ctrl_start + 4 * i, i); n = cx23885_risc_decode(risc); for (j = 1; j < n; j++) { @@ -594,7 +594,7 @@ static void cx23885_risc_disasm(struct cx23885_tsport *port, pr_info("%s: risc disasm: %p [dma=0x%08lx]\n", dev->name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { - pr_info("%s: %04d: ", dev->name, i); + pr_info("%s: %04d:", dev->name, i); n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) pr_info("%s: %04d: 0x%08x [ arg #%d ]\n", diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 671fc0588e43..9af2c5596121 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -413,7 +413,7 @@ static int buffer_prepare(struct vb2_buffer *vb) dev->height >> 1); break; default: - BUG(); + return -EINVAL; /* should not happen */ } dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp 0x%08x - dma=0x%08lx\n", buf, buf->vb.vb2_buf.index, diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig new file mode 100644 index 000000000000..e113902fa806 --- /dev/null +++ b/drivers/media/pci/intel/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config IPU_BRIDGE + tristate + depends on I2C && ACPI + help + This is a helper module for the IPU bridge, which can be + used by ipu3 and other drivers. In order to handle module + dependencies, this is selected by each driver that needs it. + +source "drivers/media/pci/intel/ipu3/Kconfig" +source "drivers/media/pci/intel/ivsc/Kconfig" diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile index 0b4236c4db49..f199a97e1d78 100644 --- a/drivers/media/pci/intel/Makefile +++ b/drivers/media/pci/intel/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only # -# Makefile for the IPU3 cio2 and ImGU drivers +# Makefile for the IPU drivers # - +obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o obj-y += ipu3/ +obj-y += ivsc/ diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c new file mode 100644 index 000000000000..1bde8b6e0b11 --- /dev/null +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dan Scally */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * 92335fcf-3203-4472-af93-7b4453ac29da + * + * Used to build MEI CSI device name to lookup MEI CSI device by + * device_find_child_by_name(). + */ +#define MEI_CSI_UUID \ + UUID_LE(0x92335FCF, 0x3203, 0x4472, \ + 0xAF, 0x93, 0x7B, 0x44, 0x53, 0xAC, 0x29, 0xDA) + +/* + * IVSC device name + * + * Used to match IVSC device by ipu_bridge_match_ivsc_dev() + */ +#define IVSC_DEV_NAME "intel_vsc" + +/* + * Extend this array with ACPI Hardware IDs of devices known to be working + * plus the number of link-frequencies expected by their drivers, along with + * the frequency values in hertz. This is somewhat opportunistic way of adding + * support for this for now in the hopes of a better source for the information + * (possibly some encoded value in the SSDB buffer that we're unaware of) + * becoming apparent in the future. + * + * Do not add an entry for a sensor that is not actually supported. + */ +static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* Omnivision OV5693 */ + IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), + /* Omnivision OV8865 */ + IPU_SENSOR_CONFIG("INT347A", 1, 360000000), + /* Omnivision OV7251 */ + IPU_SENSOR_CONFIG("INT347E", 1, 319200000), + /* Omnivision OV2680 */ + IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), + /* Omnivision ov8856 */ + IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), + /* Omnivision ov2740 */ + IPU_SENSOR_CONFIG("INT3474", 1, 360000000), + /* Hynix hi556 */ + IPU_SENSOR_CONFIG("INT3537", 1, 437000000), + /* Omnivision ov13b10 */ + IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), + /* GalaxyCore GC0310 */ + IPU_SENSOR_CONFIG("INT0310", 0), +}; + +static const struct ipu_property_names prop_names = { + .clock_frequency = "clock-frequency", + .rotation = "rotation", + .orientation = "orientation", + .bus_type = "bus-type", + .data_lanes = "data-lanes", + .remote_endpoint = "remote-endpoint", + .link_frequencies = "link-frequencies", +}; + +static const char * const ipu_vcm_types[] = { + "ad5823", + "dw9714", + "ad5816", + "dw9719", + "dw9718", + "dw9806b", + "wv517s", + "lc898122xa", + "lc898212axb", +}; + +/* + * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev() + * instead of device and driver match to probe IVSC device. + */ +static const struct acpi_device_id ivsc_acpi_ids[] = { + { "INTC1059" }, + { "INTC1095" }, + { "INTC100A" }, + { "INTC10CF" }, +}; + +static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) +{ + acpi_handle handle = acpi_device_handle(adev); + struct acpi_device *consumer, *ivsc_adev; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { + const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; + + for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) + /* camera sensor depends on IVSC in DSDT if exist */ + for_each_acpi_consumer_dev(ivsc_adev, consumer) + if (consumer->handle == handle) + return ivsc_adev; + } + + return NULL; +} + +static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev) +{ + if (ACPI_COMPANION(dev) != adev) + return 0; + + if (!sysfs_streq(dev_name(dev), IVSC_DEV_NAME)) + return 0; + + return 1; +} + +static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) +{ + struct device *dev, *csi_dev; + uuid_le uuid = MEI_CSI_UUID; + char name[64]; + + /* IVSC device on platform bus */ + dev = bus_find_device(&platform_bus_type, NULL, adev, + ipu_bridge_match_ivsc_dev); + if (dev) { + snprintf(name, sizeof(name), "%s-%pUl", dev_name(dev), &uuid); + + csi_dev = device_find_child_by_name(dev, name); + + put_device(dev); + + return csi_dev; + } + + return NULL; +} + +static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, + struct acpi_device *sensor_adev) +{ + struct acpi_device *adev; + struct device *csi_dev; + + adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); + if (adev) { + csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); + if (!csi_dev) { + acpi_dev_put(adev); + dev_err(&adev->dev, "Failed to find MEI CSI dev\n"); + return -ENODEV; + } + + sensor->csi_dev = csi_dev; + sensor->ivsc_adev = adev; + } + + return 0; +} + +static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, + void *data, u32 size) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int ret = 0; + + status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = buffer.pointer; + if (!obj) { + dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); + return -ENODEV; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(&adev->dev, "Not an ACPI buffer\n"); + ret = -ENODEV; + goto out_free_buff; + } + + if (obj->buffer.length > size) { + dev_err(&adev->dev, "Given buffer is too small\n"); + ret = -EINVAL; + goto out_free_buff; + } + + memcpy(data, obj->buffer.pointer, obj->buffer.length); + +out_free_buff: + kfree(buffer.pointer); + return ret; +} + +static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, + struct ipu_sensor_ssdb *ssdb) +{ + switch (ssdb->degree) { + case IPU_SENSOR_ROTATION_NORMAL: + return 0; + case IPU_SENSOR_ROTATION_INVERTED: + return 180; + default: + dev_warn(&adev->dev, + "Unknown rotation %d. Assume 0 degree rotation\n", + ssdb->degree); + return 0; + } +} + +static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev) +{ + enum v4l2_fwnode_orientation orientation; + struct acpi_pld_info *pld; + acpi_status status; + + status = acpi_get_physical_device_location(adev->handle, &pld); + if (ACPI_FAILURE(status)) { + dev_warn(&adev->dev, "_PLD call failed, using default orientation\n"); + return V4L2_FWNODE_ORIENTATION_EXTERNAL; + } + + switch (pld->panel) { + case ACPI_PLD_PANEL_FRONT: + orientation = V4L2_FWNODE_ORIENTATION_FRONT; + break; + case ACPI_PLD_PANEL_BACK: + orientation = V4L2_FWNODE_ORIENTATION_BACK; + break; + case ACPI_PLD_PANEL_TOP: + case ACPI_PLD_PANEL_LEFT: + case ACPI_PLD_PANEL_RIGHT: + case ACPI_PLD_PANEL_UNKNOWN: + orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; + break; + default: + dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel); + orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL; + break; + } + + ACPI_FREE(pld); + return orientation; +} + +int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) +{ + struct ipu_sensor_ssdb ssdb = {}; + int ret; + + ret = ipu_bridge_read_acpi_buffer(adev, "SSDB", &ssdb, sizeof(ssdb)); + if (ret) + return ret; + + if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) { + dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype); + ssdb.vcmtype = 0; + } + + if (ssdb.lanes > IPU_MAX_LANES) { + dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n"); + return -EINVAL; + } + + sensor->link = ssdb.link; + sensor->lanes = ssdb.lanes; + sensor->mclkspeed = ssdb.mclkspeed; + sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); + sensor->orientation = ipu_bridge_parse_orientation(adev); + + if (ssdb.vcmtype) + sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1]; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, INTEL_IPU_BRIDGE); + +static void ipu_bridge_create_fwnode_properties( + struct ipu_sensor *sensor, + struct ipu_bridge *bridge, + const struct ipu_sensor_config *cfg) +{ + struct ipu_property_names *names = &sensor->prop_names; + struct software_node *nodes = sensor->swnodes; + + sensor->prop_names = prop_names; + + if (sensor->csi_dev) { + sensor->local_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_SENSOR_ENDPOINT]); + sensor->remote_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IVSC_IPU_ENDPOINT]); + sensor->ivsc_sensor_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); + sensor->ivsc_ipu_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); + + sensor->ivsc_sensor_ep_properties[0] = + PROPERTY_ENTRY_U32(names->bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ivsc_sensor_ep_properties[1] = + PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, + bridge->data_lanes, + sensor->lanes); + sensor->ivsc_sensor_ep_properties[2] = + PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, + sensor->ivsc_sensor_ref); + + sensor->ivsc_ipu_ep_properties[0] = + PROPERTY_ENTRY_U32(names->bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ivsc_ipu_ep_properties[1] = + PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes, + bridge->data_lanes, + sensor->lanes); + sensor->ivsc_ipu_ep_properties[2] = + PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint, + sensor->ivsc_ipu_ref); + } else { + sensor->local_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_IPU_ENDPOINT]); + sensor->remote_ref[0] = + SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]); + } + + sensor->dev_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.clock_frequency, + sensor->mclkspeed); + sensor->dev_properties[1] = PROPERTY_ENTRY_U32( + sensor->prop_names.rotation, + sensor->rotation); + sensor->dev_properties[2] = PROPERTY_ENTRY_U32( + sensor->prop_names.orientation, + sensor->orientation); + if (sensor->vcm_type) { + sensor->vcm_ref[0] = + SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); + sensor->dev_properties[3] = + PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); + } + + sensor->ep_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, sensor->lanes); + sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->local_ref); + + if (cfg->nr_link_freqs > 0) + sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( + sensor->prop_names.link_frequencies, + cfg->link_freqs, + cfg->nr_link_freqs); + + sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, sensor->lanes); + sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->remote_ref); +} + +static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) +{ + snprintf(sensor->node_names.remote_port, + sizeof(sensor->node_names.remote_port), + SWNODE_GRAPH_PORT_NAME_FMT, sensor->link); + snprintf(sensor->node_names.port, + sizeof(sensor->node_names.port), + SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ + snprintf(sensor->node_names.endpoint, + sizeof(sensor->node_names.endpoint), + SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ + if (sensor->vcm_type) { + /* append link to distinguish nodes with same model VCM */ + snprintf(sensor->node_names.vcm, sizeof(sensor->node_names.vcm), + "%s-%u", sensor->vcm_type, sensor->link); + } + + if (sensor->csi_dev) { + snprintf(sensor->node_names.ivsc_sensor_port, + sizeof(sensor->node_names.ivsc_sensor_port), + SWNODE_GRAPH_PORT_NAME_FMT, 0); + snprintf(sensor->node_names.ivsc_ipu_port, + sizeof(sensor->node_names.ivsc_ipu_port), + SWNODE_GRAPH_PORT_NAME_FMT, 1); + } +} + +static void ipu_bridge_init_swnode_group(struct ipu_sensor *sensor) +{ + struct software_node *nodes = sensor->swnodes; + + sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; + sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; + sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; + sensor->group[SWNODE_IPU_PORT] = &nodes[SWNODE_IPU_PORT]; + sensor->group[SWNODE_IPU_ENDPOINT] = &nodes[SWNODE_IPU_ENDPOINT]; + if (sensor->vcm_type) + sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; + + if (sensor->csi_dev) { + sensor->group[SWNODE_IVSC_HID] = + &nodes[SWNODE_IVSC_HID]; + sensor->group[SWNODE_IVSC_SENSOR_PORT] = + &nodes[SWNODE_IVSC_SENSOR_PORT]; + sensor->group[SWNODE_IVSC_SENSOR_ENDPOINT] = + &nodes[SWNODE_IVSC_SENSOR_ENDPOINT]; + sensor->group[SWNODE_IVSC_IPU_PORT] = + &nodes[SWNODE_IVSC_IPU_PORT]; + sensor->group[SWNODE_IVSC_IPU_ENDPOINT] = + &nodes[SWNODE_IVSC_IPU_ENDPOINT]; + + if (sensor->vcm_type) + sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; + } else { + if (sensor->vcm_type) + sensor->group[SWNODE_IVSC_HID] = &nodes[SWNODE_VCM]; + } +} + +static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge, + struct ipu_sensor *sensor) +{ + struct ipu_node_names *names = &sensor->node_names; + struct software_node *nodes = sensor->swnodes; + + ipu_bridge_init_swnode_names(sensor); + + nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, + sensor->dev_properties); + nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, + &nodes[SWNODE_SENSOR_HID]); + nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( + sensor->node_names.endpoint, + &nodes[SWNODE_SENSOR_PORT], + sensor->ep_properties); + nodes[SWNODE_IPU_PORT] = NODE_PORT(sensor->node_names.remote_port, + &bridge->ipu_hid_node); + nodes[SWNODE_IPU_ENDPOINT] = NODE_ENDPOINT( + sensor->node_names.endpoint, + &nodes[SWNODE_IPU_PORT], + sensor->ipu_properties); + + if (sensor->csi_dev) { + snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u", + acpi_device_hid(sensor->ivsc_adev), sensor->link); + + nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name, + sensor->ivsc_properties); + nodes[SWNODE_IVSC_SENSOR_PORT] = + NODE_PORT(names->ivsc_sensor_port, + &nodes[SWNODE_IVSC_HID]); + nodes[SWNODE_IVSC_SENSOR_ENDPOINT] = + NODE_ENDPOINT(names->endpoint, + &nodes[SWNODE_IVSC_SENSOR_PORT], + sensor->ivsc_sensor_ep_properties); + nodes[SWNODE_IVSC_IPU_PORT] = + NODE_PORT(names->ivsc_ipu_port, + &nodes[SWNODE_IVSC_HID]); + nodes[SWNODE_IVSC_IPU_ENDPOINT] = + NODE_ENDPOINT(names->endpoint, + &nodes[SWNODE_IVSC_IPU_PORT], + sensor->ivsc_ipu_ep_properties); + } + + nodes[SWNODE_VCM] = NODE_VCM(sensor->node_names.vcm); + + ipu_bridge_init_swnode_group(sensor); +} + +/* + * The actual instantiation must be done from a workqueue to avoid + * a deadlock on taking list_lock from v4l2-async twice. + */ +struct ipu_bridge_instantiate_vcm_work_data { + struct work_struct work; + struct device *sensor; + char name[16]; + struct i2c_board_info board_info; +}; + +static void ipu_bridge_instantiate_vcm_work(struct work_struct *work) +{ + struct ipu_bridge_instantiate_vcm_work_data *data = + container_of(work, struct ipu_bridge_instantiate_vcm_work_data, + work); + struct acpi_device *adev = ACPI_COMPANION(data->sensor); + struct i2c_client *vcm_client; + bool put_fwnode = true; + int ret; + + /* + * The client may get probed before the device_link gets added below + * make sure the sensor is powered-up during probe. + */ + ret = pm_runtime_get_sync(data->sensor); + if (ret < 0) { + dev_err(data->sensor, "Error %d runtime-resuming sensor, cannot instantiate VCM\n", + ret); + goto out_pm_put; + } + + /* + * Note the client is created only once and then kept around + * even after a rmmod, just like the software-nodes. + */ + vcm_client = i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(adev), + 1, &data->board_info); + if (IS_ERR(vcm_client)) { + dev_err(data->sensor, "Error instantiating VCM client: %ld\n", + PTR_ERR(vcm_client)); + goto out_pm_put; + } + + device_link_add(&vcm_client->dev, data->sensor, DL_FLAG_PM_RUNTIME); + + dev_info(data->sensor, "Instantiated %s VCM\n", data->board_info.type); + put_fwnode = false; /* Ownership has passed to the i2c-client */ + +out_pm_put: + pm_runtime_put(data->sensor); + put_device(data->sensor); + if (put_fwnode) + fwnode_handle_put(data->board_info.fwnode); + kfree(data); +} + +int ipu_bridge_instantiate_vcm(struct device *sensor) +{ + struct ipu_bridge_instantiate_vcm_work_data *data; + struct fwnode_handle *vcm_fwnode; + struct i2c_client *vcm_client; + struct acpi_device *adev; + char *sep; + + adev = ACPI_COMPANION(sensor); + if (!adev) + return 0; + + vcm_fwnode = fwnode_find_reference(dev_fwnode(sensor), "lens-focus", 0); + if (IS_ERR(vcm_fwnode)) + return 0; + + /* When reloading modules the client will already exist */ + vcm_client = i2c_find_device_by_fwnode(vcm_fwnode); + if (vcm_client) { + fwnode_handle_put(vcm_fwnode); + put_device(&vcm_client->dev); + return 0; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + fwnode_handle_put(vcm_fwnode); + return -ENOMEM; + } + + INIT_WORK(&data->work, ipu_bridge_instantiate_vcm_work); + data->sensor = get_device(sensor); + snprintf(data->name, sizeof(data->name), "%s-VCM", + acpi_dev_name(adev)); + data->board_info.dev_name = data->name; + data->board_info.fwnode = vcm_fwnode; + snprintf(data->board_info.type, sizeof(data->board_info.type), + "%pfwP", vcm_fwnode); + /* Strip "-" postfix */ + sep = strchrnul(data->board_info.type, '-'); + *sep = 0; + + queue_work(system_long_wq, &data->work); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu_bridge_instantiate_vcm, INTEL_IPU_BRIDGE); + +static int ipu_bridge_instantiate_ivsc(struct ipu_sensor *sensor) +{ + struct fwnode_handle *fwnode; + + if (!sensor->csi_dev) + return 0; + + fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_IVSC_HID]); + if (!fwnode) + return -ENODEV; + + set_secondary_fwnode(sensor->csi_dev, fwnode); + + return 0; +} + +static void ipu_bridge_unregister_sensors(struct ipu_bridge *bridge) +{ + struct ipu_sensor *sensor; + unsigned int i; + + for (i = 0; i < bridge->n_sensors; i++) { + sensor = &bridge->sensors[i]; + software_node_unregister_node_group(sensor->group); + acpi_dev_put(sensor->adev); + put_device(sensor->csi_dev); + acpi_dev_put(sensor->ivsc_adev); + } +} + +static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, + struct ipu_bridge *bridge) +{ + struct fwnode_handle *fwnode, *primary; + struct ipu_sensor *sensor; + struct acpi_device *adev; + int ret; + + for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { + if (!adev->status.enabled) + continue; + + if (bridge->n_sensors >= IPU_MAX_PORTS) { + acpi_dev_put(adev); + dev_err(bridge->dev, "Exceeded available IPU ports\n"); + return -EINVAL; + } + + sensor = &bridge->sensors[bridge->n_sensors]; + + ret = bridge->parse_sensor_fwnode(adev, sensor); + if (ret) + goto err_put_adev; + + snprintf(sensor->name, sizeof(sensor->name), "%s-%u", + cfg->hid, sensor->link); + + ret = ipu_bridge_check_ivsc_dev(sensor, adev); + if (ret) + goto err_put_adev; + + ipu_bridge_create_fwnode_properties(sensor, bridge, cfg); + ipu_bridge_create_connection_swnodes(bridge, sensor); + + ret = software_node_register_node_group(sensor->group); + if (ret) + goto err_put_ivsc; + + fwnode = software_node_fwnode(&sensor->swnodes[ + SWNODE_SENSOR_HID]); + if (!fwnode) { + ret = -ENODEV; + goto err_free_swnodes; + } + + sensor->adev = acpi_dev_get(adev); + + primary = acpi_fwnode_handle(adev); + primary->secondary = fwnode; + + ret = ipu_bridge_instantiate_ivsc(sensor); + if (ret) + goto err_free_swnodes; + + dev_info(bridge->dev, "Found supported sensor %s\n", + acpi_dev_name(adev)); + + bridge->n_sensors++; + } + + return 0; + +err_free_swnodes: + software_node_unregister_node_group(sensor->group); +err_put_ivsc: + put_device(sensor->csi_dev); + acpi_dev_put(sensor->ivsc_adev); +err_put_adev: + acpi_dev_put(adev); + return ret; +} + +static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { + const struct ipu_sensor_config *cfg = + &ipu_supported_sensors[i]; + + ret = ipu_bridge_connect_sensor(cfg, bridge); + if (ret) + goto err_unregister_sensors; + } + + return 0; + +err_unregister_sensors: + ipu_bridge_unregister_sensors(bridge); + return ret; +} + +static int ipu_bridge_ivsc_is_ready(void) +{ + struct acpi_device *sensor_adev, *adev; + struct device *csi_dev; + bool ready = true; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) { + const struct ipu_sensor_config *cfg = + &ipu_supported_sensors[i]; + + for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) { + if (!sensor_adev->status.enabled) + continue; + + adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev); + if (!adev) + continue; + + csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); + if (!csi_dev) + ready = false; + + put_device(csi_dev); + acpi_dev_put(adev); + } + } + + return ready; +} + +int ipu_bridge_init(struct device *dev, + ipu_parse_sensor_fwnode_t parse_sensor_fwnode) +{ + struct fwnode_handle *fwnode; + struct ipu_bridge *bridge; + unsigned int i; + int ret; + + if (!ipu_bridge_ivsc_is_ready()) + return -EPROBE_DEFER; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + strscpy(bridge->ipu_node_name, IPU_HID, + sizeof(bridge->ipu_node_name)); + bridge->ipu_hid_node.name = bridge->ipu_node_name; + bridge->dev = dev; + bridge->parse_sensor_fwnode = parse_sensor_fwnode; + + ret = software_node_register(&bridge->ipu_hid_node); + if (ret < 0) { + dev_err(dev, "Failed to register the IPU HID node\n"); + goto err_free_bridge; + } + + /* + * Map the lane arrangement, which is fixed for the IPU3 (meaning we + * only need one, rather than one per sensor). We include it as a + * member of the struct ipu_bridge rather than a global variable so + * that it survives if the module is unloaded along with the rest of + * the struct. + */ + for (i = 0; i < IPU_MAX_LANES; i++) + bridge->data_lanes[i] = i + 1; + + ret = ipu_bridge_connect_sensors(bridge); + if (ret || bridge->n_sensors == 0) + goto err_unregister_ipu; + + dev_info(dev, "Connected %d cameras\n", bridge->n_sensors); + + fwnode = software_node_fwnode(&bridge->ipu_hid_node); + if (!fwnode) { + dev_err(dev, "Error getting fwnode from ipu software_node\n"); + ret = -ENODEV; + goto err_unregister_sensors; + } + + set_secondary_fwnode(dev, fwnode); + + return 0; + +err_unregister_sensors: + ipu_bridge_unregister_sensors(bridge); +err_unregister_ipu: + software_node_unregister(&bridge->ipu_hid_node); +err_free_bridge: + kfree(bridge); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(ipu_bridge_init, INTEL_IPU_BRIDGE); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel IPU Sensors Bridge driver"); diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index 65b0c1598fbf..0951545eab21 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -8,6 +8,7 @@ config VIDEO_IPU3_CIO2 select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE select VIDEOBUF2_DMA_SG + select IPU_BRIDGE if CIO2_BRIDGE help This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel diff --git a/drivers/media/pci/intel/ipu3/Makefile b/drivers/media/pci/intel/ipu3/Makefile index 933777e6ea8a..98ddd5beafe0 100644 --- a/drivers/media/pci/intel/ipu3/Makefile +++ b/drivers/media/pci/intel/ipu3/Makefile @@ -1,5 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO_IPU3_CIO2) += ipu3-cio2.o - -ipu3-cio2-y += ipu3-cio2-main.o -ipu3-cio2-$(CONFIG_CIO2_BRIDGE) += cio2-bridge.o diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c deleted file mode 100644 index 3c2accfe5455..000000000000 --- a/drivers/media/pci/intel/ipu3/cio2-bridge.c +++ /dev/null @@ -1,494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Author: Dan Scally */ - -#include -#include -#include -#include -#include -#include - -#include "cio2-bridge.h" - -/* - * Extend this array with ACPI Hardware IDs of devices known to be working - * plus the number of link-frequencies expected by their drivers, along with - * the frequency values in hertz. This is somewhat opportunistic way of adding - * support for this for now in the hopes of a better source for the information - * (possibly some encoded value in the SSDB buffer that we're unaware of) - * becoming apparent in the future. - * - * Do not add an entry for a sensor that is not actually supported. - */ -static const struct cio2_sensor_config cio2_supported_sensors[] = { - /* Omnivision OV5693 */ - CIO2_SENSOR_CONFIG("INT33BE", 1, 419200000), - /* Omnivision OV8865 */ - CIO2_SENSOR_CONFIG("INT347A", 1, 360000000), - /* Omnivision OV7251 */ - CIO2_SENSOR_CONFIG("INT347E", 1, 319200000), - /* Omnivision OV2680 */ - CIO2_SENSOR_CONFIG("OVTI2680", 0), - /* Omnivision ov8856 */ - CIO2_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Omnivision ov2740 */ - CIO2_SENSOR_CONFIG("INT3474", 1, 360000000), - /* Hynix hi556 */ - CIO2_SENSOR_CONFIG("INT3537", 1, 437000000), - /* Omnivision ov13b10 */ - CIO2_SENSOR_CONFIG("OVTIDB10", 1, 560000000), -}; - -static const struct cio2_property_names prop_names = { - .clock_frequency = "clock-frequency", - .rotation = "rotation", - .orientation = "orientation", - .bus_type = "bus-type", - .data_lanes = "data-lanes", - .remote_endpoint = "remote-endpoint", - .link_frequencies = "link-frequencies", -}; - -static const char * const cio2_vcm_types[] = { - "ad5823", - "dw9714", - "ad5816", - "dw9719", - "dw9718", - "dw9806b", - "wv517s", - "lc898122xa", - "lc898212axb", -}; - -static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, - void *data, u32 size) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; - int ret = 0; - - status = acpi_evaluate_object(adev->handle, id, NULL, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - obj = buffer.pointer; - if (!obj) { - dev_err(&adev->dev, "Couldn't locate ACPI buffer\n"); - return -ENODEV; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - dev_err(&adev->dev, "Not an ACPI buffer\n"); - ret = -ENODEV; - goto out_free_buff; - } - - if (obj->buffer.length > size) { - dev_err(&adev->dev, "Given buffer is too small\n"); - ret = -EINVAL; - goto out_free_buff; - } - - memcpy(data, obj->buffer.pointer, obj->buffer.length); - -out_free_buff: - kfree(buffer.pointer); - return ret; -} - -static u32 cio2_bridge_parse_rotation(struct cio2_sensor *sensor) -{ - switch (sensor->ssdb.degree) { - case CIO2_SENSOR_ROTATION_NORMAL: - return 0; - case CIO2_SENSOR_ROTATION_INVERTED: - return 180; - default: - dev_warn(&sensor->adev->dev, - "Unknown rotation %d. Assume 0 degree rotation\n", - sensor->ssdb.degree); - return 0; - } -} - -static enum v4l2_fwnode_orientation cio2_bridge_parse_orientation(struct cio2_sensor *sensor) -{ - switch (sensor->pld->panel) { - case ACPI_PLD_PANEL_FRONT: - return V4L2_FWNODE_ORIENTATION_FRONT; - case ACPI_PLD_PANEL_BACK: - return V4L2_FWNODE_ORIENTATION_BACK; - case ACPI_PLD_PANEL_TOP: - case ACPI_PLD_PANEL_LEFT: - case ACPI_PLD_PANEL_RIGHT: - case ACPI_PLD_PANEL_UNKNOWN: - return V4L2_FWNODE_ORIENTATION_EXTERNAL; - default: - dev_warn(&sensor->adev->dev, "Unknown _PLD panel value %d\n", - sensor->pld->panel); - return V4L2_FWNODE_ORIENTATION_EXTERNAL; - } -} - -static void cio2_bridge_create_fwnode_properties( - struct cio2_sensor *sensor, - struct cio2_bridge *bridge, - const struct cio2_sensor_config *cfg) -{ - u32 rotation; - enum v4l2_fwnode_orientation orientation; - - rotation = cio2_bridge_parse_rotation(sensor); - orientation = cio2_bridge_parse_orientation(sensor); - - sensor->prop_names = prop_names; - - sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]); - sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]); - - sensor->dev_properties[0] = PROPERTY_ENTRY_U32( - sensor->prop_names.clock_frequency, - sensor->ssdb.mclkspeed); - sensor->dev_properties[1] = PROPERTY_ENTRY_U32( - sensor->prop_names.rotation, - rotation); - sensor->dev_properties[2] = PROPERTY_ENTRY_U32( - sensor->prop_names.orientation, - orientation); - if (sensor->ssdb.vcmtype) { - sensor->vcm_ref[0] = - SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); - sensor->dev_properties[3] = - PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); - } - - sensor->ep_properties[0] = PROPERTY_ENTRY_U32( - sensor->prop_names.bus_type, - V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); - sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( - sensor->prop_names.data_lanes, - bridge->data_lanes, - sensor->ssdb.lanes); - sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY( - sensor->prop_names.remote_endpoint, - sensor->local_ref); - - if (cfg->nr_link_freqs > 0) - sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN( - sensor->prop_names.link_frequencies, - cfg->link_freqs, - cfg->nr_link_freqs); - - sensor->cio2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN( - sensor->prop_names.data_lanes, - bridge->data_lanes, - sensor->ssdb.lanes); - sensor->cio2_properties[1] = PROPERTY_ENTRY_REF_ARRAY( - sensor->prop_names.remote_endpoint, - sensor->remote_ref); -} - -static void cio2_bridge_init_swnode_names(struct cio2_sensor *sensor) -{ - snprintf(sensor->node_names.remote_port, - sizeof(sensor->node_names.remote_port), - SWNODE_GRAPH_PORT_NAME_FMT, sensor->ssdb.link); - snprintf(sensor->node_names.port, - sizeof(sensor->node_names.port), - SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ - snprintf(sensor->node_names.endpoint, - sizeof(sensor->node_names.endpoint), - SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ -} - -static void cio2_bridge_init_swnode_group(struct cio2_sensor *sensor) -{ - struct software_node *nodes = sensor->swnodes; - - sensor->group[SWNODE_SENSOR_HID] = &nodes[SWNODE_SENSOR_HID]; - sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; - sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; - sensor->group[SWNODE_CIO2_PORT] = &nodes[SWNODE_CIO2_PORT]; - sensor->group[SWNODE_CIO2_ENDPOINT] = &nodes[SWNODE_CIO2_ENDPOINT]; - if (sensor->ssdb.vcmtype) - sensor->group[SWNODE_VCM] = &nodes[SWNODE_VCM]; -} - -static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, - struct cio2_sensor *sensor) -{ - struct software_node *nodes = sensor->swnodes; - char vcm_name[ACPI_ID_LEN + 4]; - - cio2_bridge_init_swnode_names(sensor); - - nodes[SWNODE_SENSOR_HID] = NODE_SENSOR(sensor->name, - sensor->dev_properties); - nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, - &nodes[SWNODE_SENSOR_HID]); - nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT( - sensor->node_names.endpoint, - &nodes[SWNODE_SENSOR_PORT], - sensor->ep_properties); - nodes[SWNODE_CIO2_PORT] = NODE_PORT(sensor->node_names.remote_port, - &bridge->cio2_hid_node); - nodes[SWNODE_CIO2_ENDPOINT] = NODE_ENDPOINT( - sensor->node_names.endpoint, - &nodes[SWNODE_CIO2_PORT], - sensor->cio2_properties); - if (sensor->ssdb.vcmtype) { - /* append ssdb.link to distinguish VCM nodes with same HID */ - snprintf(vcm_name, sizeof(vcm_name), "%s-%u", - cio2_vcm_types[sensor->ssdb.vcmtype - 1], - sensor->ssdb.link); - nodes[SWNODE_VCM] = NODE_VCM(vcm_name); - } - - cio2_bridge_init_swnode_group(sensor); -} - -static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) -{ - struct i2c_board_info board_info = { }; - char name[16]; - - if (!sensor->ssdb.vcmtype) - return; - - snprintf(name, sizeof(name), "%s-VCM", acpi_dev_name(sensor->adev)); - board_info.dev_name = name; - strscpy(board_info.type, cio2_vcm_types[sensor->ssdb.vcmtype - 1], - ARRAY_SIZE(board_info.type)); - board_info.swnode = &sensor->swnodes[SWNODE_VCM]; - - sensor->vcm_i2c_client = - i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(sensor->adev), - 1, &board_info); - if (IS_ERR(sensor->vcm_i2c_client)) { - dev_warn(&sensor->adev->dev, "Error instantiation VCM i2c-client: %ld\n", - PTR_ERR(sensor->vcm_i2c_client)); - sensor->vcm_i2c_client = NULL; - } -} - -static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) -{ - struct cio2_sensor *sensor; - unsigned int i; - - for (i = 0; i < bridge->n_sensors; i++) { - sensor = &bridge->sensors[i]; - software_node_unregister_node_group(sensor->group); - ACPI_FREE(sensor->pld); - acpi_dev_put(sensor->adev); - i2c_unregister_device(sensor->vcm_i2c_client); - } -} - -static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, - struct cio2_bridge *bridge, - struct pci_dev *cio2) -{ - struct fwnode_handle *fwnode, *primary; - struct cio2_sensor *sensor; - struct acpi_device *adev; - acpi_status status; - int ret; - - for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { - if (!adev->status.enabled) - continue; - - if (bridge->n_sensors >= CIO2_NUM_PORTS) { - acpi_dev_put(adev); - dev_err(&cio2->dev, "Exceeded available CIO2 ports\n"); - return -EINVAL; - } - - sensor = &bridge->sensors[bridge->n_sensors]; - - ret = cio2_bridge_read_acpi_buffer(adev, "SSDB", - &sensor->ssdb, - sizeof(sensor->ssdb)); - if (ret) - goto err_put_adev; - - snprintf(sensor->name, sizeof(sensor->name), "%s-%u", - cfg->hid, sensor->ssdb.link); - - if (sensor->ssdb.vcmtype > ARRAY_SIZE(cio2_vcm_types)) { - dev_warn(&adev->dev, "Unknown VCM type %d\n", - sensor->ssdb.vcmtype); - sensor->ssdb.vcmtype = 0; - } - - status = acpi_get_physical_device_location(adev->handle, &sensor->pld); - if (ACPI_FAILURE(status)) { - ret = -ENODEV; - goto err_put_adev; - } - - if (sensor->ssdb.lanes > CIO2_MAX_LANES) { - dev_err(&adev->dev, - "Number of lanes in SSDB is invalid\n"); - ret = -EINVAL; - goto err_free_pld; - } - - cio2_bridge_create_fwnode_properties(sensor, bridge, cfg); - cio2_bridge_create_connection_swnodes(bridge, sensor); - - ret = software_node_register_node_group(sensor->group); - if (ret) - goto err_free_pld; - - fwnode = software_node_fwnode(&sensor->swnodes[ - SWNODE_SENSOR_HID]); - if (!fwnode) { - ret = -ENODEV; - goto err_free_swnodes; - } - - sensor->adev = acpi_dev_get(adev); - - primary = acpi_fwnode_handle(adev); - primary->secondary = fwnode; - - cio2_bridge_instantiate_vcm_i2c_client(sensor); - - dev_info(&cio2->dev, "Found supported sensor %s\n", - acpi_dev_name(adev)); - - bridge->n_sensors++; - } - - return 0; - -err_free_swnodes: - software_node_unregister_node_group(sensor->group); -err_free_pld: - ACPI_FREE(sensor->pld); -err_put_adev: - acpi_dev_put(adev); - return ret; -} - -static int cio2_bridge_connect_sensors(struct cio2_bridge *bridge, - struct pci_dev *cio2) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) { - const struct cio2_sensor_config *cfg = - &cio2_supported_sensors[i]; - - ret = cio2_bridge_connect_sensor(cfg, bridge, cio2); - if (ret) - goto err_unregister_sensors; - } - - return 0; - -err_unregister_sensors: - cio2_bridge_unregister_sensors(bridge); - return ret; -} - -/* - * The VCM cannot be probed until the PMIC is completely setup. We cannot rely - * on -EPROBE_DEFER for this, since the consumer<->supplier relations between - * the VCM and regulators/clks are not described in ACPI, instead they are - * passed as board-data to the PMIC drivers. Since -PROBE_DEFER does not work - * for the clks/regulators the VCM i2c-clients must not be instantiated until - * the PMIC is fully setup. - * - * The sensor/VCM ACPI device has an ACPI _DEP on the PMIC, check this using the - * acpi_dev_ready_for_enumeration() helper, like the i2c-core-acpi code does - * for the sensors. - */ -static int cio2_bridge_sensors_are_ready(void) -{ - struct acpi_device *adev; - bool ready = true; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(cio2_supported_sensors); i++) { - const struct cio2_sensor_config *cfg = - &cio2_supported_sensors[i]; - - for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { - if (!adev->status.enabled) - continue; - - if (!acpi_dev_ready_for_enumeration(adev)) - ready = false; - } - } - - return ready; -} - -int cio2_bridge_init(struct pci_dev *cio2) -{ - struct device *dev = &cio2->dev; - struct fwnode_handle *fwnode; - struct cio2_bridge *bridge; - unsigned int i; - int ret; - - if (!cio2_bridge_sensors_are_ready()) - return -EPROBE_DEFER; - - bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return -ENOMEM; - - strscpy(bridge->cio2_node_name, CIO2_HID, - sizeof(bridge->cio2_node_name)); - bridge->cio2_hid_node.name = bridge->cio2_node_name; - - ret = software_node_register(&bridge->cio2_hid_node); - if (ret < 0) { - dev_err(dev, "Failed to register the CIO2 HID node\n"); - goto err_free_bridge; - } - - /* - * Map the lane arrangement, which is fixed for the IPU3 (meaning we - * only need one, rather than one per sensor). We include it as a - * member of the struct cio2_bridge rather than a global variable so - * that it survives if the module is unloaded along with the rest of - * the struct. - */ - for (i = 0; i < CIO2_MAX_LANES; i++) - bridge->data_lanes[i] = i + 1; - - ret = cio2_bridge_connect_sensors(bridge, cio2); - if (ret || bridge->n_sensors == 0) - goto err_unregister_cio2; - - dev_info(dev, "Connected %d cameras\n", bridge->n_sensors); - - fwnode = software_node_fwnode(&bridge->cio2_hid_node); - if (!fwnode) { - dev_err(dev, "Error getting fwnode from cio2 software_node\n"); - ret = -ENODEV; - goto err_unregister_sensors; - } - - set_secondary_fwnode(dev, fwnode); - - return 0; - -err_unregister_sensors: - cio2_bridge_unregister_sensors(bridge); -err_unregister_cio2: - software_node_unregister(&bridge->cio2_hid_node); -err_free_bridge: - kfree(bridge); - - return ret; -} diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c similarity index 99% rename from drivers/media/pci/intel/ipu3/ipu3-cio2-main.c rename to drivers/media/pci/intel/ipu3/ipu3-cio2.c index 34984a7474ed..5dd69a251b6a 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -22,6 +22,8 @@ #include #include #include + +#include #include #include #include @@ -354,7 +356,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) void __iomem *const base = cio2->base; u8 lanes, csi2bus = q->csi2.port; u8 sensor_vc = SENSOR_VIR_CH_DFLT; - struct cio2_csi2_timing timing; + struct cio2_csi2_timing timing = { 0 }; int i, r; fmt = cio2_find_format(NULL, &q->subdev_fmt.code); @@ -1371,7 +1373,7 @@ static const struct v4l2_subdev_ops cio2_subdev_ops = { /******* V4L2 sub-device asynchronous registration callbacks***********/ struct sensor_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; struct csi2_bus_info csi2; }; @@ -1381,15 +1383,20 @@ struct sensor_async_subdev { /* The .bound() notifier callback when a match is found */ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct cio2_device *cio2 = to_cio2_device(notifier); struct sensor_async_subdev *s_asd = to_sensor_asd(asd); struct cio2_queue *q; + int ret; if (cio2->queue[s_asd->csi2.port].sensor) return -EBUSY; + ret = ipu_bridge_instantiate_vcm(sd->dev); + if (ret) + return ret; + q = &cio2->queue[s_asd->csi2.port]; q->csi2 = s_asd->csi2; @@ -1402,7 +1409,7 @@ static int cio2_notifier_bound(struct v4l2_async_notifier *notifier, /* The .unbind callback */ static void cio2_notifier_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct cio2_device *cio2 = to_cio2_device(notifier); struct sensor_async_subdev *s_asd = to_sensor_asd(asd); @@ -1416,11 +1423,11 @@ static int cio2_notifier_complete(struct v4l2_async_notifier *notifier) struct cio2_device *cio2 = to_cio2_device(notifier); struct device *dev = &cio2->pci_dev->dev; struct sensor_async_subdev *s_asd; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct cio2_queue *q; int ret; - list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &cio2->notifier.done_list, asc_entry) { s_asd = to_sensor_asd(asd); q = &cio2->queue[s_asd->csi2.port]; @@ -1499,7 +1506,7 @@ err_parse: * suspend. */ cio2->notifier.ops = &cio2_async_ops; - ret = v4l2_async_nf_register(&cio2->v4l2_dev, &cio2->notifier); + ret = v4l2_async_nf_register(&cio2->notifier); if (ret) dev_err(dev, "failed to register async notifier : %d\n", ret); @@ -1724,7 +1731,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, return -EINVAL; } - r = cio2_bridge_init(pci_dev); + r = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); if (r) return r; } @@ -1794,7 +1801,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, if (r) goto fail_v4l2_device_unregister; - v4l2_async_nf_init(&cio2->notifier); + v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev); /* Register notifier for subdevices we care */ r = cio2_parse_firmware(cio2); @@ -2057,3 +2064,4 @@ MODULE_AUTHOR("Yuning Pu "); MODULE_AUTHOR("Yong Zhi "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("IPU3 CIO2 driver"); +MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index 3a1f394e05aa..d731ce8adbe3 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -459,10 +459,4 @@ static inline struct cio2_queue *vb2q_to_cio2_queue(struct vb2_queue *vq) return container_of(vq, struct cio2_queue, vbq); } -#if IS_ENABLED(CONFIG_CIO2_BRIDGE) -int cio2_bridge_init(struct pci_dev *cio2); -#else -static inline int cio2_bridge_init(struct pci_dev *cio2) { return 0; } -#endif - #endif diff --git a/drivers/media/pci/intel/ivsc/Kconfig b/drivers/media/pci/intel/ivsc/Kconfig new file mode 100644 index 000000000000..1ef1c4e3750d --- /dev/null +++ b/drivers/media/pci/intel/ivsc/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2023, Intel Corporation. All rights reserved. + +config INTEL_VSC + tristate "Intel Visual Sensing Controller" + depends on INTEL_MEI && ACPI + help + This adds support for Intel Visual Sensing Controller (IVSC). + + Enables the IVSC firmware services required for controlling + camera sensor ownership and CSI-2 link through Image Processing + Unit(IPU) driver of Intel. diff --git a/drivers/media/pci/intel/ivsc/Makefile b/drivers/media/pci/intel/ivsc/Makefile new file mode 100644 index 000000000000..00fad29a6e6e --- /dev/null +++ b/drivers/media/pci/intel/ivsc/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2023, Intel Corporation. All rights reserved. + +obj-$(CONFIG_INTEL_VSC) += ivsc-csi.o +ivsc-csi-y += mei_csi.o + +obj-$(CONFIG_INTEL_VSC) += ivsc-ace.o +ivsc-ace-y += mei_ace.o diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c new file mode 100644 index 000000000000..a0491f307831 --- /dev/null +++ b/drivers/media/pci/intel/ivsc/mei_ace.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * Intel Visual Sensing Controller ACE Linux driver + */ + +/* + * To set ownership of camera sensor, there is specific command, which + * is sent via MEI protocol. That's a two-step scheme where the firmware + * first acks receipt of the command and later responses the command was + * executed. The command sending function uses "completion" as the + * synchronization mechanism. The notification for command is received + * via a mei callback which wakes up the caller. There can be only one + * outstanding command at a time. + * + * The power line of camera sensor is directly connected to IVSC instead + * of host, when camera sensor ownership is switched to host, sensor is + * already powered up by firmware. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEI_ACE_DRIVER_NAME "ivsc_ace" + +/* indicating driver message */ +#define ACE_DRV_MSG 1 +/* indicating set command */ +#define ACE_CMD_SET 4 +/* command timeout determined experimentally */ +#define ACE_CMD_TIMEOUT (5 * HZ) +/* indicating the first command block */ +#define ACE_CMD_INIT_BLOCK 1 +/* indicating the last command block */ +#define ACE_CMD_FINAL_BLOCK 1 +/* size of camera status notification content */ +#define ACE_CAMERA_STATUS_SIZE 5 + +/* UUID used to get firmware id */ +#define ACE_GET_FW_ID_UUID UUID_LE(0x6167DCFB, 0x72F1, 0x4584, 0xBF, \ + 0xE3, 0x84, 0x17, 0x71, 0xAA, 0x79, 0x0B) + +/* UUID used to get csi device */ +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \ + 0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA) + +/* identify firmware event type */ +enum ace_event_type { + /* firmware ready */ + ACE_FW_READY = 0x8, + + /* command response */ + ACE_CMD_RESPONSE = 0x10, +}; + +/* identify camera sensor ownership */ +enum ace_camera_owner { + ACE_CAMERA_IVSC, + ACE_CAMERA_HOST, +}; + +/* identify the command id supported by firmware IPC */ +enum ace_cmd_id { + /* used to switch camera sensor to host */ + ACE_SWITCH_CAMERA_TO_HOST = 0x13, + + /* used to switch camera sensor to IVSC */ + ACE_SWITCH_CAMERA_TO_IVSC = 0x14, + + /* used to get firmware id */ + ACE_GET_FW_ID = 0x1A, +}; + +/* ACE command header structure */ +struct ace_cmd_hdr { + u32 firmware_id : 16; + u32 instance_id : 8; + u32 type : 5; + u32 rsp : 1; + u32 msg_tgt : 1; + u32 _hw_rsvd_1 : 1; + u32 param_size : 20; + u32 cmd_id : 8; + u32 final_block : 1; + u32 init_block : 1; + u32 _hw_rsvd_2 : 2; +} __packed; + +/* ACE command parameter structure */ +union ace_cmd_param { + uuid_le uuid; + u32 param; +}; + +/* ACE command structure */ +struct ace_cmd { + struct ace_cmd_hdr hdr; + union ace_cmd_param param; +} __packed; + +/* ACE notification header */ +union ace_notif_hdr { + struct _confirm { + u32 status : 24; + u32 type : 5; + u32 rsp : 1; + u32 msg_tgt : 1; + u32 _hw_rsvd_1 : 1; + u32 param_size : 20; + u32 cmd_id : 8; + u32 final_block : 1; + u32 init_block : 1; + u32 _hw_rsvd_2 : 2; + } __packed ack; + + struct _event { + u32 rsvd1 : 16; + u32 event_type : 8; + u32 type : 5; + u32 ack : 1; + u32 msg_tgt : 1; + u32 _hw_rsvd_1 : 1; + u32 rsvd2 : 30; + u32 _hw_rsvd_2 : 2; + } __packed event; + + struct _response { + u32 event_id : 16; + u32 notif_type : 8; + u32 type : 5; + u32 rsp : 1; + u32 msg_tgt : 1; + u32 _hw_rsvd_1 : 1; + u32 event_data_size : 16; + u32 request_target : 1; + u32 request_type : 5; + u32 cmd_id : 8; + u32 _hw_rsvd_2 : 2; + } __packed response; +}; + +/* ACE notification content */ +union ace_notif_cont { + u16 firmware_id; + u8 state_notif; + u8 camera_status[ACE_CAMERA_STATUS_SIZE]; +}; + +/* ACE notification structure */ +struct ace_notif { + union ace_notif_hdr hdr; + union ace_notif_cont cont; +} __packed; + +struct mei_ace { + struct mei_cl_device *cldev; + + /* command ack */ + struct ace_notif cmd_ack; + /* command response */ + struct ace_notif cmd_response; + /* used to wait for command ack and response */ + struct completion cmd_completion; + /* lock used to prevent multiple call to send command */ + struct mutex lock; + + /* used to construct command */ + u16 firmware_id; + + struct device *csi_dev; + + /* runtime PM link from ace to csi */ + struct device_link *csi_link; + + struct work_struct work; +}; + +static inline void init_cmd_hdr(struct ace_cmd_hdr *hdr) +{ + memset(hdr, 0, sizeof(struct ace_cmd_hdr)); + + hdr->type = ACE_CMD_SET; + hdr->msg_tgt = ACE_DRV_MSG; + hdr->init_block = ACE_CMD_INIT_BLOCK; + hdr->final_block = ACE_CMD_FINAL_BLOCK; +} + +static int construct_command(struct mei_ace *ace, struct ace_cmd *cmd, + enum ace_cmd_id cmd_id) +{ + union ace_cmd_param *param = &cmd->param; + struct ace_cmd_hdr *hdr = &cmd->hdr; + + init_cmd_hdr(hdr); + + hdr->cmd_id = cmd_id; + switch (cmd_id) { + case ACE_GET_FW_ID: + param->uuid = ACE_GET_FW_ID_UUID; + hdr->param_size = sizeof(param->uuid); + break; + case ACE_SWITCH_CAMERA_TO_IVSC: + param->param = 0; + hdr->firmware_id = ace->firmware_id; + hdr->param_size = sizeof(param->param); + break; + case ACE_SWITCH_CAMERA_TO_HOST: + hdr->firmware_id = ace->firmware_id; + break; + default: + return -EINVAL; + } + + return hdr->param_size + sizeof(cmd->hdr); +} + +/* send command to firmware */ +static int mei_ace_send(struct mei_ace *ace, struct ace_cmd *cmd, + size_t len, bool only_ack) +{ + union ace_notif_hdr *resp_hdr = &ace->cmd_response.hdr; + union ace_notif_hdr *ack_hdr = &ace->cmd_ack.hdr; + struct ace_cmd_hdr *cmd_hdr = &cmd->hdr; + int ret; + + mutex_lock(&ace->lock); + + reinit_completion(&ace->cmd_completion); + + ret = mei_cldev_send(ace->cldev, (u8 *)cmd, len); + if (ret < 0) + goto out; + + ret = wait_for_completion_killable_timeout(&ace->cmd_completion, + ACE_CMD_TIMEOUT); + if (ret < 0) { + goto out; + } else if (!ret) { + ret = -ETIMEDOUT; + goto out; + } + + if (ack_hdr->ack.cmd_id != cmd_hdr->cmd_id) { + ret = -EINVAL; + goto out; + } + + /* command ack status */ + ret = ack_hdr->ack.status; + if (ret) { + ret = -EIO; + goto out; + } + + if (only_ack) + goto out; + + ret = wait_for_completion_killable_timeout(&ace->cmd_completion, + ACE_CMD_TIMEOUT); + if (ret < 0) { + goto out; + } else if (!ret) { + ret = -ETIMEDOUT; + goto out; + } else { + ret = 0; + } + + if (resp_hdr->response.cmd_id != cmd_hdr->cmd_id) + ret = -EINVAL; + +out: + mutex_unlock(&ace->lock); + + return ret; +} + +static int ace_set_camera_owner(struct mei_ace *ace, + enum ace_camera_owner owner) +{ + enum ace_cmd_id cmd_id; + struct ace_cmd cmd; + int cmd_size; + int ret; + + if (owner == ACE_CAMERA_IVSC) + cmd_id = ACE_SWITCH_CAMERA_TO_IVSC; + else + cmd_id = ACE_SWITCH_CAMERA_TO_HOST; + + cmd_size = construct_command(ace, &cmd, cmd_id); + if (cmd_size >= 0) + ret = mei_ace_send(ace, &cmd, cmd_size, false); + else + ret = cmd_size; + + return ret; +} + +/* the first command downloaded to firmware */ +static inline int ace_get_firmware_id(struct mei_ace *ace) +{ + struct ace_cmd cmd; + int cmd_size; + int ret; + + cmd_size = construct_command(ace, &cmd, ACE_GET_FW_ID); + if (cmd_size >= 0) + ret = mei_ace_send(ace, &cmd, cmd_size, true); + else + ret = cmd_size; + + return ret; +} + +static void handle_command_response(struct mei_ace *ace, + struct ace_notif *resp, int len) +{ + union ace_notif_hdr *hdr = &resp->hdr; + + switch (hdr->response.cmd_id) { + case ACE_SWITCH_CAMERA_TO_IVSC: + case ACE_SWITCH_CAMERA_TO_HOST: + memcpy(&ace->cmd_response, resp, len); + complete(&ace->cmd_completion); + break; + case ACE_GET_FW_ID: + break; + default: + break; + } +} + +static void handle_command_ack(struct mei_ace *ace, + struct ace_notif *ack, int len) +{ + union ace_notif_hdr *hdr = &ack->hdr; + + switch (hdr->ack.cmd_id) { + case ACE_GET_FW_ID: + ace->firmware_id = ack->cont.firmware_id; + fallthrough; + case ACE_SWITCH_CAMERA_TO_IVSC: + case ACE_SWITCH_CAMERA_TO_HOST: + memcpy(&ace->cmd_ack, ack, len); + complete(&ace->cmd_completion); + break; + default: + break; + } +} + +/* callback for receive */ +static void mei_ace_rx(struct mei_cl_device *cldev) +{ + struct mei_ace *ace = mei_cldev_get_drvdata(cldev); + struct ace_notif event; + union ace_notif_hdr *hdr = &event.hdr; + int ret; + + ret = mei_cldev_recv(cldev, (u8 *)&event, sizeof(event)); + if (ret < 0) { + dev_err(&cldev->dev, "recv error: %d\n", ret); + return; + } + + if (hdr->event.ack) { + handle_command_ack(ace, &event, ret); + return; + } + + switch (hdr->event.event_type) { + case ACE_CMD_RESPONSE: + handle_command_response(ace, &event, ret); + break; + case ACE_FW_READY: + /* + * firmware ready notification sent to driver + * after HECI client connected with firmware. + */ + dev_dbg(&cldev->dev, "firmware ready\n"); + break; + default: + break; + } +} + +static int mei_ace_setup_dev_link(struct mei_ace *ace) +{ + struct device *dev = &ace->cldev->dev; + uuid_le uuid = MEI_CSI_UUID; + struct device *csi_dev; + char name[64]; + int ret; + + snprintf(name, sizeof(name), "%s-%pUl", dev_name(dev->parent), &uuid); + + csi_dev = device_find_child_by_name(dev->parent, name); + if (!csi_dev) { + ret = -EPROBE_DEFER; + goto err; + } + + /* setup link between mei_ace and mei_csi */ + ace->csi_link = device_link_add(csi_dev, dev, DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE | DL_FLAG_STATELESS); + if (!ace->csi_link) { + ret = -EINVAL; + dev_err(dev, "failed to link to %s\n", dev_name(csi_dev)); + goto err_put; + } + + ace->csi_dev = csi_dev; + + return 0; + +err_put: + put_device(csi_dev); + +err: + return ret; +} + +/* switch camera to host before probe sensor device */ +static void mei_ace_post_probe_work(struct work_struct *work) +{ + struct acpi_device *adev; + struct mei_ace *ace; + struct device *dev; + int ret; + + ace = container_of(work, struct mei_ace, work); + dev = &ace->cldev->dev; + + ret = ace_set_camera_owner(ace, ACE_CAMERA_HOST); + if (ret) { + dev_err(dev, "switch camera to host failed: %d\n", ret); + return; + } + + adev = ACPI_COMPANION(dev->parent); + if (!adev) + return; + + acpi_dev_clear_dependencies(adev); +} + +static int mei_ace_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct device *dev = &cldev->dev; + struct mei_ace *ace; + int ret; + + ace = devm_kzalloc(dev, sizeof(struct mei_ace), GFP_KERNEL); + if (!ace) + return -ENOMEM; + + ace->cldev = cldev; + mutex_init(&ace->lock); + init_completion(&ace->cmd_completion); + INIT_WORK(&ace->work, mei_ace_post_probe_work); + + mei_cldev_set_drvdata(cldev, ace); + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + dev_err(dev, "mei_cldev_enable failed: %d\n", ret); + goto destroy_mutex; + } + + ret = mei_cldev_register_rx_cb(cldev, mei_ace_rx); + if (ret) { + dev_err(dev, "event cb registration failed: %d\n", ret); + goto err_disable; + } + + ret = ace_get_firmware_id(ace); + if (ret) { + dev_err(dev, "get firmware id failed: %d\n", ret); + goto err_disable; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = mei_ace_setup_dev_link(ace); + if (ret) + goto disable_pm; + + schedule_work(&ace->work); + + return 0; + +disable_pm: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +err_disable: + mei_cldev_disable(cldev); + +destroy_mutex: + mutex_destroy(&ace->lock); + + return ret; +} + +static void mei_ace_remove(struct mei_cl_device *cldev) +{ + struct mei_ace *ace = mei_cldev_get_drvdata(cldev); + + cancel_work_sync(&ace->work); + + device_link_del(ace->csi_link); + put_device(ace->csi_dev); + + pm_runtime_disable(&cldev->dev); + pm_runtime_set_suspended(&cldev->dev); + + ace_set_camera_owner(ace, ACE_CAMERA_IVSC); + + mutex_destroy(&ace->lock); +} + +static int __maybe_unused mei_ace_runtime_suspend(struct device *dev) +{ + struct mei_ace *ace = dev_get_drvdata(dev); + + return ace_set_camera_owner(ace, ACE_CAMERA_IVSC); +} + +static int __maybe_unused mei_ace_runtime_resume(struct device *dev) +{ + struct mei_ace *ace = dev_get_drvdata(dev); + + return ace_set_camera_owner(ace, ACE_CAMERA_HOST); +} + +static const struct dev_pm_ops mei_ace_pm_ops = { + SET_RUNTIME_PM_OPS(mei_ace_runtime_suspend, + mei_ace_runtime_resume, NULL) +}; + +#define MEI_ACE_UUID UUID_LE(0x5DB76CF6, 0x0A68, 0x4ED6, \ + 0x9B, 0x78, 0x03, 0x61, 0x63, 0x5E, 0x24, 0x47) + +static const struct mei_cl_device_id mei_ace_tbl[] = { + { MEI_ACE_DRIVER_NAME, MEI_ACE_UUID, MEI_CL_VERSION_ANY }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(mei, mei_ace_tbl); + +static struct mei_cl_driver mei_ace_driver = { + .id_table = mei_ace_tbl, + .name = MEI_ACE_DRIVER_NAME, + + .probe = mei_ace_probe, + .remove = mei_ace_remove, + + .driver = { + .pm = &mei_ace_pm_ops, + }, +}; + +module_mei_cl_driver(mei_ace_driver); + +MODULE_AUTHOR("Wentong Wu "); +MODULE_AUTHOR("Zhifeng Wang "); +MODULE_DESCRIPTION("Device driver for IVSC ACE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c new file mode 100644 index 000000000000..00ba611e0f68 --- /dev/null +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * Intel Visual Sensing Controller CSI Linux driver + */ + +/* + * To set ownership of CSI-2 link and to configure CSI-2 link, there + * are specific commands, which are sent via MEI protocol. The send + * command function uses "completion" as a synchronization mechanism. + * The response for command is received via a mei callback which wakes + * up the caller. There can be only one outstanding command at a time. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MEI_CSI_DRIVER_NAME "ivsc_csi" +#define MEI_CSI_ENTITY_NAME "Intel IVSC CSI" + +#define MEI_CSI_LINK_FREQ_400MHZ 400000000ULL + +/* the 5s used here is based on experiment */ +#define CSI_CMD_TIMEOUT (5 * HZ) +/* to setup CSI-2 link an extra delay needed and determined experimentally */ +#define CSI_FW_READY_DELAY_MS 100 +/* link frequency unit is 100kHz */ +#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ))) + +/* + * identify the command id supported by firmware + * IPC, as well as the privacy notification id + * used when processing privacy event. + */ +enum csi_cmd_id { + /* used to set csi ownership */ + CSI_SET_OWNER = 0, + + /* used to configure CSI-2 link */ + CSI_SET_CONF = 2, + + /* privacy notification id used when privacy state changes */ + CSI_PRIVACY_NOTIF = 6, +}; + +/* CSI-2 link ownership definition */ +enum csi_link_owner { + CSI_LINK_IVSC, + CSI_LINK_HOST, +}; + +/* privacy status definition */ +enum ivsc_privacy_status { + CSI_PRIVACY_OFF, + CSI_PRIVACY_ON, + CSI_PRIVACY_MAX, +}; + +enum csi_pads { + CSI_PAD_SOURCE, + CSI_PAD_SINK, + CSI_NUM_PADS +}; + +/* configuration of the CSI-2 link between host and IVSC */ +struct csi_link_cfg { + /* number of data lanes used on the CSI-2 link */ + u32 nr_of_lanes; + + /* frequency of the CSI-2 link */ + u32 link_freq; + + /* for future use */ + u32 rsvd[2]; +} __packed; + +/* CSI command structure */ +struct csi_cmd { + u32 cmd_id; + union _cmd_param { + u32 param; + struct csi_link_cfg conf; + } param; +} __packed; + +/* CSI notification structure */ +struct csi_notif { + u32 cmd_id; + int status; + union _resp_cont { + u32 cont; + struct csi_link_cfg conf; + } cont; +} __packed; + +struct mei_csi { + struct mei_cl_device *cldev; + + /* command response */ + struct csi_notif cmd_response; + /* used to wait for command response from firmware */ + struct completion cmd_completion; + /* protect command download */ + struct mutex lock; + + struct v4l2_subdev subdev; + struct v4l2_subdev *remote; + struct v4l2_async_notifier notifier; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *freq_ctrl; + struct v4l2_ctrl *privacy_ctrl; + unsigned int remote_pad; + /* start streaming or not */ + int streaming; + + struct media_pad pads[CSI_NUM_PADS]; + struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS]; + + /* number of data lanes used on the CSI-2 link */ + u32 nr_of_lanes; + /* frequency of the CSI-2 link */ + u64 link_freq; + + /* privacy status */ + enum ivsc_privacy_status status; +}; + +static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + +static s64 link_freq_menu_items[] = { + MEI_CSI_LINK_FREQ_400MHZ +}; + +static inline struct mei_csi *notifier_to_csi(struct v4l2_async_notifier *n) +{ + return container_of(n, struct mei_csi, notifier); +} + +static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mei_csi, subdev); +} + +static inline struct mei_csi *ctrl_to_csi(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mei_csi, ctrl_handler); +} + +/* send a command to firmware and mutex must be held by caller */ +static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) +{ + struct csi_cmd *cmd = (struct csi_cmd *)buf; + int ret; + + reinit_completion(&csi->cmd_completion); + + ret = mei_cldev_send(csi->cldev, buf, len); + if (ret < 0) + goto out; + + ret = wait_for_completion_killable_timeout(&csi->cmd_completion, + CSI_CMD_TIMEOUT); + if (ret < 0) { + goto out; + } else if (!ret) { + ret = -ETIMEDOUT; + goto out; + } + + /* command response status */ + ret = csi->cmd_response.status; + if (ret) { + ret = -EINVAL; + goto out; + } + + if (csi->cmd_response.cmd_id != cmd->cmd_id) + ret = -EINVAL; + +out: + return ret; +} + +/* set CSI-2 link ownership */ +static int csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner) +{ + struct csi_cmd cmd = { 0 }; + size_t cmd_size; + int ret; + + cmd.cmd_id = CSI_SET_OWNER; + cmd.param.param = owner; + cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param); + + mutex_lock(&csi->lock); + + ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size); + + mutex_unlock(&csi->lock); + + return ret; +} + +/* configure CSI-2 link between host and IVSC */ +static int csi_set_link_cfg(struct mei_csi *csi) +{ + struct csi_cmd cmd = { 0 }; + size_t cmd_size; + int ret; + + cmd.cmd_id = CSI_SET_CONF; + cmd.param.conf.nr_of_lanes = csi->nr_of_lanes; + cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq); + cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + + mutex_lock(&csi->lock); + + ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size); + /* + * wait configuration ready if download success. placing + * delay under mutex is to make sure current command flow + * completed before starting a possible new one. + */ + if (!ret) + msleep(CSI_FW_READY_DELAY_MS); + + mutex_unlock(&csi->lock); + + return ret; +} + +/* callback for receive */ +static void mei_csi_rx(struct mei_cl_device *cldev) +{ + struct mei_csi *csi = mei_cldev_get_drvdata(cldev); + struct csi_notif notif = { 0 }; + int ret; + + ret = mei_cldev_recv(cldev, (u8 *)¬if, sizeof(notif)); + if (ret < 0) { + dev_err(&cldev->dev, "recv error: %d\n", ret); + return; + } + + switch (notif.cmd_id) { + case CSI_PRIVACY_NOTIF: + if (notif.cont.cont < CSI_PRIVACY_MAX) { + csi->status = notif.cont.cont; + v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status); + } + break; + case CSI_SET_OWNER: + case CSI_SET_CONF: + memcpy(&csi->cmd_response, ¬if, ret); + + complete(&csi->cmd_completion); + break; + default: + break; + } +} + +static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mei_csi *csi = sd_to_csi(sd); + s64 freq; + int ret; + + if (enable && csi->streaming == 0) { + freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0); + if (freq < 0) { + dev_err(&csi->cldev->dev, + "error %lld, invalid link_freq\n", freq); + ret = freq; + goto err; + } + csi->link_freq = freq; + + /* switch CSI-2 link to host */ + ret = csi_set_link_owner(csi, CSI_LINK_HOST); + if (ret < 0) + goto err; + + /* configure CSI-2 link */ + ret = csi_set_link_cfg(csi); + if (ret < 0) + goto err_switch; + + ret = v4l2_subdev_call(csi->remote, video, s_stream, 1); + if (ret) + goto err_switch; + } else if (!enable && csi->streaming == 1) { + v4l2_subdev_call(csi->remote, video, s_stream, 0); + + /* switch CSI-2 link to IVSC */ + ret = csi_set_link_owner(csi, CSI_LINK_IVSC); + if (ret < 0) + dev_warn(&csi->cldev->dev, + "failed to switch CSI2 link: %d\n", ret); + } + + csi->streaming = enable; + + return 0; + +err_switch: + csi_set_link_owner(csi, CSI_LINK_IVSC); + +err: + return ret; +} + +static struct v4l2_mbus_framefmt * +mei_csi_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct mei_csi *csi = sd_to_csi(sd); + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &csi->format_mbus[pad]; + default: + return NULL; + } +} + +static int mei_csi_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *mbusformat; + struct mei_csi *csi = sd_to_csi(sd); + unsigned int i; + + mutex_lock(&csi->lock); + + for (i = 0; i < sd->entity.num_pads; i++) { + mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); + *mbusformat = mei_csi_format_mbus_default; + } + + mutex_unlock(&csi->lock); + + return 0; +} + +static int mei_csi_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mbusformat; + struct mei_csi *csi = sd_to_csi(sd); + + mutex_lock(&csi->lock); + + mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad, + format->which); + if (mbusformat) + format->format = *mbusformat; + + mutex_unlock(&csi->lock); + + return 0; +} + +static int mei_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *source_mbusformat; + struct v4l2_mbus_framefmt *mbusformat; + struct mei_csi *csi = sd_to_csi(sd); + struct media_pad *pad; + + mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad, + format->which); + if (!mbusformat) + return -EINVAL; + + source_mbusformat = mei_csi_get_pad_format(sd, sd_state, CSI_PAD_SOURCE, + format->which); + if (!source_mbusformat) + return -EINVAL; + + v4l_bound_align_image(&format->format.width, 1, 65536, 0, + &format->format.height, 1, 65536, 0, 0); + + switch (format->format.code) { + case MEDIA_BUS_FMT_RGB444_1X12: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_BGR565_2X8_BE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_GBR888_1X24: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_ARGB8888_1X32: + case MEDIA_BUS_FMT_RGB888_1X32_PADHI: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_UV8_1X8: + case MEDIA_BUS_FMT_UYVY8_1_5X8: + case MEDIA_BUS_FMT_VYUY8_1_5X8: + case MEDIA_BUS_FMT_YUYV8_1_5X8: + case MEDIA_BUS_FMT_YVYU8_1_5X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_VYUY10_2X10: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_YVYU10_2X10: + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_VYUY12_2X12: + case MEDIA_BUS_FMT_YUYV12_2X12: + case MEDIA_BUS_FMT_YVYU12_2X12: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YDYUYDYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_AYUV8_1X32: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + case MEDIA_BUS_FMT_JPEG_1X8: + case MEDIA_BUS_FMT_AHSV8888_1X32: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SRGGB16_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + + if (format->format.field == V4L2_FIELD_ANY) + format->format.field = V4L2_FIELD_NONE; + + mutex_lock(&csi->lock); + + pad = &csi->pads[format->pad]; + if (pad->flags & MEDIA_PAD_FL_SOURCE) + format->format = csi->format_mbus[CSI_PAD_SINK]; + + *mbusformat = format->format; + + if (pad->flags & MEDIA_PAD_FL_SINK) + *source_mbusformat = format->format; + + mutex_unlock(&csi->lock); + + return 0; +} + +static int mei_csi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mei_csi *csi = ctrl_to_csi(ctrl); + s64 freq; + + if (ctrl->id == V4L2_CID_LINK_FREQ) { + if (!csi->remote) + return -EINVAL; + + freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0); + if (freq < 0) { + dev_err(&csi->cldev->dev, + "error %lld, invalid link_freq\n", freq); + return -EINVAL; + } + + link_freq_menu_items[0] = freq; + ctrl->val = 0; + + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops mei_csi_ctrl_ops = { + .g_volatile_ctrl = mei_csi_g_volatile_ctrl, +}; + +static const struct v4l2_subdev_video_ops mei_csi_video_ops = { + .s_stream = mei_csi_set_stream, +}; + +static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = { + .init_cfg = mei_csi_init_cfg, + .get_fmt = mei_csi_get_fmt, + .set_fmt = mei_csi_set_fmt, +}; + +static const struct v4l2_subdev_ops mei_csi_subdev_ops = { + .video = &mei_csi_video_ops, + .pad = &mei_csi_pad_ops, +}; + +static const struct media_entity_operations mei_csi_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct mei_csi *csi = notifier_to_csi(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) + return pad; + + csi->remote = subdev; + csi->remote_pad = pad; + + return media_create_pad_link(&subdev->entity, pad, + &csi->subdev.entity, 1, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct mei_csi *csi = notifier_to_csi(notifier); + + csi->remote = NULL; +} + +static const struct v4l2_async_notifier_operations mei_csi_notify_ops = { + .bound = mei_csi_notify_bound, + .unbind = mei_csi_notify_unbind, +}; + +static int mei_csi_init_controls(struct mei_csi *csi) +{ + u32 max; + int ret; + + ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 2); + if (ret) + return ret; + + csi->ctrl_handler.lock = &csi->lock; + + max = ARRAY_SIZE(link_freq_menu_items) - 1; + csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler, + &mei_csi_ctrl_ops, + V4L2_CID_LINK_FREQ, + max, + 0, + link_freq_menu_items); + if (csi->freq_ctrl) + csi->freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY | + V4L2_CTRL_FLAG_VOLATILE; + + csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL, + V4L2_CID_PRIVACY, 0, 1, 1, 0); + if (csi->privacy_ctrl) + csi->privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (csi->ctrl_handler.error) + return csi->ctrl_handler.error; + + csi->subdev.ctrl_handler = &csi->ctrl_handler; + + return 0; +} + +static int mei_csi_parse_firmware(struct mei_csi *csi) +{ + struct v4l2_fwnode_endpoint v4l2_ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = &csi->cldev->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *fwnode; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!ep) { + dev_err(dev, "not connected to subdevice\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + if (ret) { + dev_err(dev, "could not parse v4l2 endpoint\n"); + fwnode_handle_put(ep); + return -EINVAL; + } + + fwnode = fwnode_graph_get_remote_endpoint(ep); + fwnode_handle_put(ep); + + v4l2_async_subdev_nf_init(&csi->notifier, &csi->subdev); + csi->notifier.ops = &mei_csi_notify_ops; + + asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + fwnode_handle_put(fwnode); + return PTR_ERR(asd); + } + + ret = v4l2_fwnode_endpoint_alloc_parse(fwnode, &v4l2_ep); + fwnode_handle_put(fwnode); + if (ret) + return ret; + csi->nr_of_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + + ret = v4l2_async_nf_register(&csi->notifier); + if (ret) + v4l2_async_nf_cleanup(&csi->notifier); + + v4l2_fwnode_endpoint_free(&v4l2_ep); + + return ret; +} + +static int mei_csi_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct device *dev = &cldev->dev; + struct mei_csi *csi; + int ret; + + if (!dev_fwnode(dev)) + return -EPROBE_DEFER; + + csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + csi->cldev = cldev; + mutex_init(&csi->lock); + init_completion(&csi->cmd_completion); + + mei_cldev_set_drvdata(cldev, csi); + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + dev_err(dev, "mei_cldev_enable failed: %d\n", ret); + goto destroy_mutex; + } + + ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx); + if (ret) { + dev_err(dev, "event cb registration failed: %d\n", ret); + goto err_disable; + } + + ret = mei_csi_parse_firmware(csi); + if (ret) + goto err_disable; + + csi->subdev.dev = &cldev->dev; + v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops); + v4l2_set_subdevdata(&csi->subdev, csi); + csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi->subdev.entity.ops = &mei_csi_entity_ops; + + snprintf(csi->subdev.name, sizeof(csi->subdev.name), + MEI_CSI_ENTITY_NAME); + + ret = mei_csi_init_controls(csi); + if (ret) + goto err_ctrl_handler; + + csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default; + csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default; + + csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS, + csi->pads); + if (ret) + goto err_ctrl_handler; + + ret = v4l2_subdev_init_finalize(&csi->subdev); + if (ret < 0) + goto err_entity; + + ret = v4l2_async_register_subdev(&csi->subdev); + if (ret < 0) + goto err_subdev; + + pm_runtime_enable(&cldev->dev); + + return 0; + +err_subdev: + v4l2_subdev_cleanup(&csi->subdev); + +err_entity: + media_entity_cleanup(&csi->subdev.entity); + +err_ctrl_handler: + v4l2_ctrl_handler_free(&csi->ctrl_handler); + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); + +err_disable: + mei_cldev_disable(cldev); + +destroy_mutex: + mutex_destroy(&csi->lock); + + return ret; +} + +static void mei_csi_remove(struct mei_cl_device *cldev) +{ + struct mei_csi *csi = mei_cldev_get_drvdata(cldev); + + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); + v4l2_ctrl_handler_free(&csi->ctrl_handler); + v4l2_async_unregister_subdev(&csi->subdev); + v4l2_subdev_cleanup(&csi->subdev); + media_entity_cleanup(&csi->subdev.entity); + + pm_runtime_disable(&cldev->dev); + + mutex_destroy(&csi->lock); +} + +#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \ + 0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA) + +static const struct mei_cl_device_id mei_csi_tbl[] = { + { MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(mei, mei_csi_tbl); + +static struct mei_cl_driver mei_csi_driver = { + .id_table = mei_csi_tbl, + .name = MEI_CSI_DRIVER_NAME, + + .probe = mei_csi_probe, + .remove = mei_csi_remove, +}; + +module_mei_cl_driver(mei_csi_driver); + +MODULE_AUTHOR("Wentong Wu "); +MODULE_AUTHOR("Zhifeng Wang "); +MODULE_DESCRIPTION("Device driver for IVSC CSI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c index c1b6a0596801..bf73e9e83f52 100644 --- a/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/drivers/media/pci/saa7164/saa7164-encoder.c @@ -383,7 +383,7 @@ int saa7164_s_frequency(struct saa7164_port *port, else if (port->nr == SAA7164_PORT_ENC2) tsport = &dev->ports[SAA7164_PORT_TS2]; else - BUG(); + return -EINVAL; /* should not happen */ fe = tsport->dvb.frontend; diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c index 363689484c54..cc9f384f7f1e 100644 --- a/drivers/media/pci/saa7164/saa7164-fw.c +++ b/drivers/media/pci/saa7164/saa7164-fw.c @@ -271,7 +271,6 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev) dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", __func__); first_timeout = SAA_DEVICE_TIMEOUT; - second_timeout = 60 * SAA_DEVICE_TIMEOUT; second_timeout = 100; err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index 824529f3c74b..230b104a7cdf 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -123,7 +123,7 @@ static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; int result; if (slot != 0) @@ -142,7 +142,7 @@ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int ad static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; int result; if (slot != 0) @@ -161,7 +161,7 @@ static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int a static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; int result; if (slot != 0) @@ -181,7 +181,7 @@ static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addre static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; int result; if (slot != 0) @@ -200,7 +200,7 @@ static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addr static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) @@ -229,7 +229,7 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) @@ -245,7 +245,7 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; struct saa7146_dev *saa = budget_av->budget.dev; if (slot != 0) @@ -260,7 +260,7 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) { - struct budget_av *budget_av = (struct budget_av *) ca->data; + struct budget_av *budget_av = ca->data; struct saa7146_dev *saa = budget_av->budget.dev; int result; @@ -491,7 +491,7 @@ static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; u32 div; u8 buf[4]; - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; if ((c->frequency < 950000) || (c->frequency > 2150000)) @@ -604,7 +604,7 @@ static const struct stv0299_config cinergy_1200s_1894_0010_config = { static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u8 buf[6]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; int i; @@ -668,7 +668,7 @@ static struct tda10023_config philips_cu1216_tda10023_config = { static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; @@ -685,7 +685,7 @@ static int philips_tu1216_tuner_init(struct dvb_frontend *fe) static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; @@ -769,7 +769,7 @@ static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) static int philips_tu1216_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; return request_firmware(fw, name, &budget->dev->pci->dev); } @@ -1353,7 +1353,7 @@ static void frontend_init(struct budget_av *budget_av) static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) { - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + struct budget_av *budget_av = dev->ext_priv; dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); @@ -1363,7 +1363,7 @@ static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) static int budget_av_detach(struct saa7146_dev *dev) { - struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + struct budget_av *budget_av = dev->ext_priv; int err; dprintk(2, "dev: %p\n", dev); @@ -1412,7 +1412,7 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) { struct saa7146_dev *dev = video_drvdata(file); - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + struct budget_av *budget_av = dev->ext_priv; *i = budget_av->cur_input; @@ -1423,7 +1423,7 @@ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) static int vidioc_s_input(struct file *file, void *fh, unsigned int input) { struct saa7146_dev *dev = video_drvdata(file); - struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + struct budget_av *budget_av = dev->ext_priv; dprintk(1, "VIDIOC_S_INPUT %d\n", input); return saa7113_setinput(budget_av, input); diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index d59d18647371..66e1a004ee43 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -251,7 +251,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; if (slot != 0) return -EINVAL; @@ -262,7 +262,7 @@ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int ad static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; if (slot != 0) return -EINVAL; @@ -273,7 +273,7 @@ static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int a static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; if (slot != 0) return -EINVAL; @@ -284,7 +284,7 @@ static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addre static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; if (slot != 0) return -EINVAL; @@ -295,7 +295,7 @@ static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addr static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; if (slot != 0) @@ -318,7 +318,7 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; if (slot != 0) @@ -331,7 +331,7 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; struct saa7146_dev *saa = budget_ci->budget.dev; int tmp; @@ -400,7 +400,7 @@ static void ciintf_interrupt(struct tasklet_struct *t) static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) { - struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct budget_ci *budget_ci = ca->data; unsigned int flags; // ensure we don't get spurious IRQs during initialisation @@ -553,7 +553,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci) static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) { - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + struct budget_ci *budget_ci = dev->ext_priv; dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); @@ -648,7 +648,7 @@ static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + struct budget_ci *budget_ci = fe->dvb->priv; u32 div; u8 buf[4]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; @@ -698,7 +698,7 @@ static const struct stv0299_config philips_su1278_tt_config = { static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) { - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + struct budget_ci *budget_ci = fe->dvb->priv; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = @@ -729,7 +729,7 @@ static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + struct budget_ci *budget_ci = fe->dvb->priv; u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; int tuner_frequency = 0; @@ -815,7 +815,7 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + struct budget_ci *budget_ci = fe->dvb->priv; return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); } @@ -845,7 +845,7 @@ static struct tda1004x_config philips_tdm1316l_config_invert = { static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + struct budget_ci *budget_ci = fe->dvb->priv; u8 tuner_buf[5]; struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, .flags = 0, @@ -1494,7 +1494,7 @@ out1: static int budget_ci_detach(struct saa7146_dev *dev) { - struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + struct budget_ci *budget_ci = dev->ext_priv; struct saa7146_dev *saa = budget_ci->budget.dev; int err; diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index 710595987522..25f44c3eebf3 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -147,7 +147,7 @@ static int start_ts_capture(struct budget *budget) static int budget_read_fe_status(struct dvb_frontend *fe, enum fe_status *status) { - struct budget *budget = (struct budget *) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; int synced; int ret; @@ -570,7 +570,7 @@ int ttpci_budget_deinit(struct budget *budget) void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) { - struct budget *budget = (struct budget *) dev->ext_priv; + struct budget *budget = dev->ext_priv; dprintk(8, "dev: %p, budget: %p\n", dev, budget); @@ -580,7 +580,7 @@ void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) { - struct budget *budget = (struct budget *) dev->ext_priv; + struct budget *budget = dev->ext_priv; spin_lock(&budget->feedlock); budget->video_port = video_port; diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c index a88711a3ac7f..b76a1b330b50 100644 --- a/drivers/media/pci/ttpci/budget.c +++ b/drivers/media/pci/ttpci/budget.c @@ -144,7 +144,7 @@ static int SetVoltage_Activy(struct budget *budget, static int siemens_budget_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; return SetVoltage_Activy (budget, voltage); } @@ -152,7 +152,7 @@ static int siemens_budget_set_voltage(struct dvb_frontend *fe, static int budget_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; switch (tone) { case SEC_TONE_ON: @@ -172,7 +172,7 @@ static int budget_set_tone(struct dvb_frontend *fe, static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) { - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); @@ -182,7 +182,7 @@ static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_dis static int budget_diseqc_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd minicmd) { - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; SendDiSEqCMsg (budget, 0, NULL, minicmd); @@ -192,7 +192,7 @@ static int budget_diseqc_send_burst(struct dvb_frontend *fe, static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u8 pwr = 0; u8 buf[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; @@ -234,7 +234,7 @@ static struct ves1x93_config alps_bsrv2_config = static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -320,7 +320,7 @@ static u8 tuner_address_grundig_29504_401_activy = 0x60; static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -344,7 +344,7 @@ static struct tda8083_config grundig_29504_451_config = { static int s5h1420_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct budget* budget = (struct budget*) fe->dvb->priv; + struct budget *budget = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -405,7 +405,7 @@ static const struct stv0299_config alps_bsbe1_config_activy = { static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { - struct budget *budget = (struct budget *)fe->dvb->priv; + struct budget *budget = fe->dvb->priv; return request_firmware(fw, name, &budget->dev->pci->dev); } @@ -800,7 +800,7 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_ static int budget_detach (struct saa7146_dev* dev) { - struct budget *budget = (struct budget*) dev->ext_priv; + struct budget *budget = dev->ext_priv; int err; if (budget->dvb_frontend) { diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index ec03e17727d7..da61f9beb6b4 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -4007,7 +4006,7 @@ static struct platform_driver allegro_driver = { .remove_new = allegro_remove, .driver = { .name = "allegro", - .of_match_table = of_match_ptr(allegro_dt_ids), + .of_match_table = allegro_dt_ids, .pm = &allegro_pm_ops, }, }; diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 6515f3cdb7a7..133d77d1ea0c 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -299,7 +299,8 @@ static int vdec_update_state(struct vpu_inst *inst, enum vpu_codec_state state, vdec->state = VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE; if (inst->state != pre_state) - vpu_trace(inst->dev, "[%d] %d -> %d\n", inst->id, pre_state, inst->state); + vpu_trace(inst->dev, "[%d] %s -> %s\n", inst->id, + vpu_codec_state_name(pre_state), vpu_codec_state_name(inst->state)); if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) vdec_handle_resolution_change(inst); @@ -741,6 +742,21 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg) dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id); vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED); vdec->decoded_frame_count++; + if (vdec->params.display_delay_enable) { + struct vpu_format *cur_fmt; + + cur_fmt = vpu_get_format(inst, inst->cap_format.type); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY); + for (int i = 0; i < vbuf->vb2_buf.num_planes; i++) + vb2_set_plane_payload(&vbuf->vb2_buf, + i, vpu_get_fmt_plane_size(cur_fmt, i)); + vbuf->field = cur_fmt->field; + vbuf->sequence = vdec->sequence++; + dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + vdec->display_frame_count++; + } exit: vpu_inst_unlock(inst); @@ -768,14 +784,14 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) struct vpu_format *cur_fmt; struct vpu_vb2_buffer *vpu_buf; struct vb2_v4l2_buffer *vbuf; - u32 sequence; int i; if (!frame) return; vpu_inst_lock(inst); - sequence = vdec->sequence++; + if (!vdec->params.display_delay_enable) + vdec->sequence++; vpu_buf = vdec_find_buffer(inst, frame->luma); vpu_inst_unlock(inst); if (!vpu_buf) { @@ -794,13 +810,17 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n", inst->id, vbuf->vb2_buf.index, frame->id); + if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable) + return; + if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED) dev_err(inst->dev, "[%d] buffer(%d) ready without decoded\n", inst->id, frame->id); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY); for (i = 0; i < vbuf->vb2_buf.num_planes; i++) vb2_set_plane_payload(&vbuf->vb2_buf, i, vpu_get_fmt_plane_size(cur_fmt, i)); vbuf->field = cur_fmt->field; - vbuf->sequence = sequence; + vbuf->sequence = vdec->sequence; dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); @@ -999,6 +1019,7 @@ static int vdec_response_frame_abnormal(struct vpu_inst *inst) { struct vdec_t *vdec = inst->priv; struct vpu_fs_info info; + int ret; if (!vdec->req_frame_count) return 0; @@ -1006,7 +1027,9 @@ static int vdec_response_frame_abnormal(struct vpu_inst *inst) memset(&info, 0, sizeof(info)); info.type = MEM_RES_FRAME; info.tag = vdec->seq_tag + 0xf0; - vpu_session_alloc_fs(inst, &info); + ret = vpu_session_alloc_fs(inst, &info); + if (ret) + return ret; vdec->req_frame_count--; return 0; @@ -1037,8 +1060,8 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb return -EINVAL; } - dev_dbg(inst->dev, "[%d] state = %d, alloc fs %d, tag = 0x%x\n", - inst->id, inst->state, vbuf->vb2_buf.index, vdec->seq_tag); + dev_dbg(inst->dev, "[%d] state = %s, alloc fs %d, tag = 0x%x\n", + inst->id, vpu_codec_state_name(inst->state), vbuf->vb2_buf.index, vdec->seq_tag); vpu_buf = to_vpu_vb2_buffer(vbuf); memset(&info, 0, sizeof(info)); @@ -1400,7 +1423,7 @@ static void vdec_abort(struct vpu_inst *inst) struct vpu_rpc_buffer_desc desc; int ret; - vpu_trace(inst->dev, "[%d] state = %d\n", inst->id, inst->state); + vpu_trace(inst->dev, "[%d] state = %s\n", inst->id, vpu_codec_state_name(inst->state)); vdec->aborting = true; vpu_iface_add_scode(inst, SCODE_PADDING_ABORT); @@ -1453,9 +1476,7 @@ static void vdec_release(struct vpu_inst *inst) { if (inst->id != VPU_INST_NULL_ID) vpu_trace(inst->dev, "[%d]\n", inst->id); - vpu_inst_lock(inst); vdec_stop(inst, true); - vpu_inst_unlock(inst); } static void vdec_cleanup(struct vpu_inst *inst) diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 58480e2755ec..4eb57d793a9c 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -268,7 +268,7 @@ static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm { struct vpu_inst *inst = to_inst(file); struct venc_t *venc = inst->priv; - struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe; + struct v4l2_fract *timeperframe; if (!parm) return -EINVAL; @@ -279,6 +279,7 @@ static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm if (!vpu_helper_check_type(inst, parm->type)) return -EINVAL; + timeperframe = &parm->parm.capture.timeperframe; parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.readbuffers = 0; timeperframe->numerator = venc->params.frame_rate.numerator; @@ -291,7 +292,7 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm { struct vpu_inst *inst = to_inst(file); struct venc_t *venc = inst->priv; - struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe; + struct v4l2_fract *timeperframe; unsigned long n, d; if (!parm) @@ -303,6 +304,7 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm if (!vpu_helper_check_type(inst, parm->type)) return -EINVAL; + timeperframe = &parm->parm.capture.timeperframe; if (!timeperframe->numerator) timeperframe->numerator = venc->params.frame_rate.numerator; if (!timeperframe->denominator) diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 3bfe193722af..5a701f64289e 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -355,6 +355,9 @@ void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow); int vpu_core_driver_init(void); void vpu_core_driver_exit(void); +const char *vpu_id_name(u32 id); +const char *vpu_codec_state_name(enum vpu_codec_state state); + extern bool debug; #define vpu_trace(dev, fmt, arg...) \ do { \ diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c index fa581ba6bab2..c2337812573e 100644 --- a/drivers/media/platform/amphion/vpu_cmds.c +++ b/drivers/media/platform/amphion/vpu_cmds.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -98,7 +96,7 @@ static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data cmd->id = id; ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data); if (ret) { - dev_err(inst->dev, "iface pack cmd(%d) fail\n", id); + dev_err(inst->dev, "iface pack cmd %s fail\n", vpu_id_name(id)); vfree(cmd->pkt); vfree(cmd); return NULL; @@ -125,14 +123,14 @@ static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd) { int ret; - dev_dbg(inst->dev, "[%d]send cmd(0x%x)\n", inst->id, cmd->id); + dev_dbg(inst->dev, "[%d]send cmd %s\n", inst->id, vpu_id_name(cmd->id)); vpu_iface_pre_send_cmd(inst); ret = vpu_cmd_send(inst->core, cmd->pkt); if (!ret) { vpu_iface_post_send_cmd(inst); vpu_inst_record_flow(inst, cmd->id); } else { - dev_err(inst->dev, "[%d] iface send cmd(0x%x) fail\n", inst->id, cmd->id); + dev_err(inst->dev, "[%d] iface send cmd %s fail\n", inst->id, vpu_id_name(cmd->id)); } return ret; @@ -149,7 +147,8 @@ static void vpu_process_cmd_request(struct vpu_inst *inst) list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) { list_del_init(&cmd->list); if (vpu_session_process_cmd(inst, cmd)) - dev_err(inst->dev, "[%d] process cmd(%d) fail\n", inst->id, cmd->id); + dev_err(inst->dev, "[%d] process cmd %s fail\n", + inst->id, vpu_id_name(cmd->id)); if (cmd->request) { inst->pending = (void *)cmd; break; @@ -305,7 +304,8 @@ static void vpu_core_keep_active(struct vpu_core *core) dev_dbg(core->dev, "try to wake up\n"); mutex_lock(&core->cmd_lock); - vpu_cmd_send(core, &pkt); + if (vpu_cmd_send(core, &pkt)) + dev_err(core->dev, "fail to keep active\n"); mutex_unlock(&core->cmd_lock); } @@ -313,7 +313,7 @@ static int vpu_session_send_cmd(struct vpu_inst *inst, u32 id, void *data) { unsigned long key; int sync = false; - int ret = -EINVAL; + int ret; if (inst->id < 0) return -EINVAL; @@ -339,7 +339,7 @@ static int vpu_session_send_cmd(struct vpu_inst *inst, u32 id, void *data) exit: if (ret) - dev_err(inst->dev, "[%d] send cmd(0x%x) fail\n", inst->id, id); + dev_err(inst->dev, "[%d] send cmd %s fail\n", inst->id, vpu_id_name(id)); return ret; } diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 7863b7b53494..1af6fc9460d4 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -88,6 +88,8 @@ static int vpu_core_boot_done(struct vpu_core *core) core->supported_instance_count = min(core->supported_instance_count, count); } + if (core->supported_instance_count >= BITS_PER_TYPE(core->instance_mask)) + core->supported_instance_count = BITS_PER_TYPE(core->instance_mask); core->fw_version = fw_version; vpu_core_set_state(core, VPU_CORE_ACTIVE); diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index 44b830ae01d8..982c2c777484 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -50,6 +50,13 @@ static char *vpu_stat_name[] = { [VPU_BUF_STATE_ERROR] = "error", }; +static inline const char *to_vpu_stat_name(int state) +{ + if (state <= VPU_BUF_STATE_ERROR) + return vpu_stat_name[state]; + return "unknown"; +} + static int vpu_dbg_instance(struct seq_file *s, void *data) { struct vpu_inst *inst = s->private; @@ -67,7 +74,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "tgig = %d,pid = %d\n", inst->tgid, inst->pid); if (seq_write(s, str, num)) return 0; - num = scnprintf(str, sizeof(str), "state = %d\n", inst->state); + num = scnprintf(str, sizeof(str), "state = %s\n", vpu_codec_state_name(inst->state)); if (seq_write(s, str, num)) return 0; num = scnprintf(str, sizeof(str), @@ -141,7 +148,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "output [%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], - vpu_stat_name[vpu_get_buffer_state(vbuf)]); + to_vpu_stat_name(vpu_get_buffer_state(vbuf))); if (seq_write(s, str, num)) return 0; } @@ -156,7 +163,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) num = scnprintf(str, sizeof(str), "capture[%2d] state = %10s, %8s\n", i, vb2_stat_name[vb->state], - vpu_stat_name[vpu_get_buffer_state(vbuf)]); + to_vpu_stat_name(vpu_get_buffer_state(vbuf))); if (seq_write(s, str, num)) return 0; } @@ -188,9 +195,9 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) if (!inst->flows[idx]) continue; - num = scnprintf(str, sizeof(str), "\t[%s]0x%x\n", + num = scnprintf(str, sizeof(str), "\t[%s] %s\n", inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C", - inst->flows[idx]); + vpu_id_name(inst->flows[idx])); if (seq_write(s, str, num)) { mutex_unlock(&inst->core->cmd_lock); return 0; diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c index 4187b2b5562f..2bf70aafd2ba 100644 --- a/drivers/media/platform/amphion/vpu_drv.c +++ b/drivers/media/platform/amphion/vpu_drv.c @@ -10,8 +10,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c index 019c77e84514..af3b336e5dc3 100644 --- a/drivers/media/platform/amphion/vpu_helpers.c +++ b/drivers/media/platform/amphion/vpu_helpers.c @@ -11,6 +11,7 @@ #include #include #include "vpu.h" +#include "vpu_defs.h" #include "vpu_core.h" #include "vpu_rpc.h" #include "vpu_helpers.h" @@ -447,3 +448,63 @@ int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst) return -EINVAL; } + +const char *vpu_id_name(u32 id) +{ + switch (id) { + case VPU_CMD_ID_NOOP: return "noop"; + case VPU_CMD_ID_CONFIGURE_CODEC: return "configure codec"; + case VPU_CMD_ID_START: return "start"; + case VPU_CMD_ID_STOP: return "stop"; + case VPU_CMD_ID_ABORT: return "abort"; + case VPU_CMD_ID_RST_BUF: return "reset buf"; + case VPU_CMD_ID_SNAPSHOT: return "snapshot"; + case VPU_CMD_ID_FIRM_RESET: return "reset firmware"; + case VPU_CMD_ID_UPDATE_PARAMETER: return "update parameter"; + case VPU_CMD_ID_FRAME_ENCODE: return "encode frame"; + case VPU_CMD_ID_SKIP: return "skip"; + case VPU_CMD_ID_FS_ALLOC: return "alloc fb"; + case VPU_CMD_ID_FS_RELEASE: return "release fb"; + case VPU_CMD_ID_TIMESTAMP: return "timestamp"; + case VPU_CMD_ID_DEBUG: return "debug"; + case VPU_MSG_ID_RESET_DONE: return "reset done"; + case VPU_MSG_ID_START_DONE: return "start done"; + case VPU_MSG_ID_STOP_DONE: return "stop done"; + case VPU_MSG_ID_ABORT_DONE: return "abort done"; + case VPU_MSG_ID_BUF_RST: return "buf reset done"; + case VPU_MSG_ID_MEM_REQUEST: return "mem request"; + case VPU_MSG_ID_PARAM_UPD_DONE: return "param upd done"; + case VPU_MSG_ID_FRAME_INPUT_DONE: return "frame input done"; + case VPU_MSG_ID_ENC_DONE: return "encode done"; + case VPU_MSG_ID_DEC_DONE: return "frame display"; + case VPU_MSG_ID_FRAME_REQ: return "fb request"; + case VPU_MSG_ID_FRAME_RELEASE: return "fb release"; + case VPU_MSG_ID_SEQ_HDR_FOUND: return "seq hdr found"; + case VPU_MSG_ID_RES_CHANGE: return "resolution change"; + case VPU_MSG_ID_PIC_HDR_FOUND: return "pic hdr found"; + case VPU_MSG_ID_PIC_DECODED: return "picture decoded"; + case VPU_MSG_ID_PIC_EOS: return "eos"; + case VPU_MSG_ID_FIFO_LOW: return "fifo low"; + case VPU_MSG_ID_BS_ERROR: return "bs error"; + case VPU_MSG_ID_UNSUPPORTED: return "unsupported"; + case VPU_MSG_ID_FIRMWARE_XCPT: return "exception"; + case VPU_MSG_ID_PIC_SKIPPED: return "skipped"; + } + return ""; +} + +const char *vpu_codec_state_name(enum vpu_codec_state state) +{ + switch (state) { + case VPU_CODEC_STATE_DEINIT: return "initialization"; + case VPU_CODEC_STATE_CONFIGURED: return "configured"; + case VPU_CODEC_STATE_START: return "start"; + case VPU_CODEC_STATE_STARTED: return "started"; + case VPU_CODEC_STATE_ACTIVE: return "active"; + case VPU_CODEC_STATE_SEEK: return "seek"; + case VPU_CODEC_STATE_STOP: return "stop"; + case VPU_CODEC_STATE_DRAIN: return "drain"; + case VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE: return "resolution change"; + } + return ""; +} diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index c1d6606ad7e5..f771661980c0 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c index b6d5b4844f67..c2963b8deb48 100644 --- a/drivers/media/platform/amphion/vpu_mbox.c +++ b/drivers/media/platform/amphion/vpu_mbox.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include "vpu.h" #include "vpu_mbox.h" diff --git a/drivers/media/platform/amphion/vpu_msgs.c b/drivers/media/platform/amphion/vpu_msgs.c index 92672a802b49..d0ead051f7d1 100644 --- a/drivers/media/platform/amphion/vpu_msgs.c +++ b/drivers/media/platform/amphion/vpu_msgs.c @@ -32,7 +32,7 @@ static void vpu_session_handle_start_done(struct vpu_inst *inst, struct vpu_rpc_ static void vpu_session_handle_mem_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - struct vpu_pkt_mem_req_data req_data; + struct vpu_pkt_mem_req_data req_data = { 0 }; vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&req_data); vpu_trace(inst->dev, "[%d] %d:%d %d:%d %d:%d\n", @@ -80,7 +80,7 @@ static void vpu_session_handle_resolution_change(struct vpu_inst *inst, struct v static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - struct vpu_enc_pic_info info; + struct vpu_enc_pic_info info = { 0 }; vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); dev_dbg(inst->dev, "[%d] frame id = %d, wptr = 0x%x, size = %d\n", @@ -90,7 +90,7 @@ static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_ static void vpu_session_handle_frame_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - struct vpu_fs_info fs; + struct vpu_fs_info fs = { 0 }; vpu_iface_unpack_msg_data(inst->core, pkt, &fs); call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_REQ, &fs); @@ -107,7 +107,7 @@ static void vpu_session_handle_frame_release(struct vpu_inst *inst, struct vpu_r info.type = inst->out_format.type; call_void_vop(inst, buf_done, &info); } else if (inst->core->type == VPU_CORE_TYPE_DEC) { - struct vpu_fs_info fs; + struct vpu_fs_info fs = { 0 }; vpu_iface_unpack_msg_data(inst->core, pkt, &fs); call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_RELEASE, &fs); @@ -122,7 +122,7 @@ static void vpu_session_handle_input_done(struct vpu_inst *inst, struct vpu_rpc_ static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - struct vpu_dec_pic_info info; + struct vpu_dec_pic_info info = { 0 }; vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info); call_void_vop(inst, get_one_frame, &info); @@ -130,7 +130,7 @@ static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc static void vpu_session_handle_pic_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - struct vpu_dec_pic_info info; + struct vpu_dec_pic_info info = { 0 }; struct vpu_frame_info frame; memset(&frame, 0, sizeof(frame)); @@ -210,7 +210,7 @@ static int vpu_session_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *m return -EINVAL; msg_id = ret; - dev_dbg(inst->dev, "[%d] receive event(0x%x)\n", inst->id, msg_id); + dev_dbg(inst->dev, "[%d] receive event(%s)\n", inst->id, vpu_id_name(msg_id)); for (i = 0; i < ARRAY_SIZE(handlers); i++) { if (handlers[i].id == msg_id) { diff --git a/drivers/media/platform/amphion/vpu_rpc.c b/drivers/media/platform/amphion/vpu_rpc.c index 676f7da041bd..f626a9f835e0 100644 --- a/drivers/media/platform/amphion/vpu_rpc.c +++ b/drivers/media/platform/amphion/vpu_rpc.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 021235e1c144..0f6e4c666440 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -489,6 +489,11 @@ static int vpu_vb2_queue_setup(struct vb2_queue *vq, for (i = 0; i < cur_fmt->mem_planes; i++) psize[i] = vpu_get_fmt_plane_size(cur_fmt, i); + if (V4L2_TYPE_IS_OUTPUT(vq->type) && inst->state == VPU_CODEC_STATE_SEEK) { + vpu_trace(inst->dev, "reinit when VIDIOC_REQBUFS(OUTPUT, 0)\n"); + call_void_vop(inst, release); + } + return 0; } @@ -773,9 +778,9 @@ int vpu_v4l2_close(struct file *file) v4l2_m2m_ctx_release(inst->fh.m2m_ctx); inst->fh.m2m_ctx = NULL; } + call_void_vop(inst, release); vpu_inst_unlock(inst); - call_void_vop(inst, release); vpu_inst_unregister(inst); vpu_inst_put(inst); diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c index b245ff6a1102..5f1101d7cf9e 100644 --- a/drivers/media/platform/amphion/vpu_windsor.c +++ b/drivers/media/platform/amphion/vpu_windsor.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 374eb7781936..a9c2c69b2ed9 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1130,7 +1129,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) static void aspeed_video_set_resolution(struct aspeed_video *video) { struct v4l2_bt_timings *act = &video->active_timings; - unsigned int size = act->width * act->height; + unsigned int size = act->width * ALIGN(act->height, 8); /* Set capture/compression frame sizes */ aspeed_video_calc_compressed_size(video, size); @@ -1147,7 +1146,7 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) u32 width = ALIGN(act->width, 64); aspeed_video_write(video, VE_CAP_WINDOW, width << 16 | act->height); - size = width * act->height; + size = width * ALIGN(act->height, 8); } else { aspeed_video_write(video, VE_CAP_WINDOW, act->width << 16 | act->height); diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index c29e04864445..4046212d48b4 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1120,7 +1120,7 @@ static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct atmel_isi *isi = notifier_to_isi(notifier); @@ -1132,7 +1132,7 @@ static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier, static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct atmel_isi *isi = notifier_to_isi(notifier); @@ -1151,7 +1151,7 @@ static const struct v4l2_async_notifier_operations isi_graph_notify_ops = { static int isi_graph_init(struct atmel_isi *isi) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *ep; int ret; @@ -1159,11 +1159,11 @@ static int isi_graph_init(struct atmel_isi *isi) if (!ep) return -EINVAL; - v4l2_async_nf_init(&isi->notifier); + v4l2_async_nf_init(&isi->notifier, &isi->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, of_fwnode_handle(ep), - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep); if (IS_ERR(asd)) @@ -1171,7 +1171,7 @@ static int isi_graph_init(struct atmel_isi *isi) isi->notifier.ops = &isi_graph_notify_ops; - ret = v4l2_async_nf_register(&isi->v4l2_dev, &isi->notifier); + ret = v4l2_async_nf_register(&isi->notifier); if (ret < 0) { dev_err(isi->dev, "Notifier registration failed\n"); v4l2_async_nf_cleanup(&isi->notifier); @@ -1187,7 +1187,6 @@ static int atmel_isi_probe(struct platform_device *pdev) int irq; struct atmel_isi *isi; struct vb2_queue *q; - struct resource *regs; int ret, i; isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); @@ -1268,8 +1267,7 @@ static int atmel_isi_probe(struct platform_device *pdev) list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - isi->regs = devm_ioremap_resource(&pdev->dev, regs); + isi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(isi->regs)) { ret = PTR_ERR(isi->regs); goto err_ioremap; diff --git a/drivers/media/platform/atmel/atmel-isi.h b/drivers/media/platform/atmel/atmel-isi.h index 7ad3895a2c87..ef38eddef5fc 100644 --- a/drivers/media/platform/atmel/atmel-isi.h +++ b/drivers/media/platform/atmel/atmel-isi.h @@ -121,8 +121,6 @@ #define ISI_DATAWIDTH_8 0x01 #define ISI_DATAWIDTH_10 0x02 -struct v4l2_async_subdev; - struct isi_platform_data { u8 has_emb_sync; u8 hsync_act_low; diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 9755d1c8ceb9..0d879d71d818 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,12 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +#define CSI2RX_DPHY_LANE_CTRL_REG 0x40 +#define CSI2RX_DPHY_CL_RST BIT(16) +#define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) +#define CSI2RX_DPHY_CL_EN BIT(4) +#define CSI2RX_DPHY_DL_EN(i) BIT(i) + #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) @@ -68,6 +75,9 @@ struct csi2rx_priv { struct clk *sys_clk; struct clk *p_clk; struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; + struct reset_control *sys_rst; + struct reset_control *p_rst; + struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; struct phy *dphy; u8 lanes[CSI2RX_LANES_MAX]; @@ -101,6 +111,24 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); } +static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) +{ + union phy_configure_opts opts = { }; + int ret; + + ret = phy_power_on(csi2rx->dphy); + if (ret) + return ret; + + ret = phy_configure(csi2rx->dphy, &opts); + if (ret) { + phy_power_off(csi2rx->dphy); + return ret; + } + + return 0; +} + static int csi2rx_start(struct csi2rx_priv *csi2rx) { unsigned int i; @@ -112,6 +140,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) return ret; + reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); reg = csi2rx->num_lanes << 8; @@ -139,6 +168,17 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pclk; + /* Enable DPHY clk and data lanes. */ + if (csi2rx->dphy) { + reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); + reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); + } + + writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + } + /* * Create a static mapping between the CSI virtual channels * and the output stream. @@ -154,6 +194,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->pixel_rst[i]); + writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); @@ -169,13 +211,28 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->sys_rst); + + if (csi2rx->dphy) { + ret = csi2rx_configure_ext_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_sysclk; + } + } + clk_disable_unprepare(csi2rx->p_clk); return 0; +err_disable_sysclk: + clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: - for (; i > 0; i--) + for (; i > 0; i--) { + reset_control_assert(csi2rx->pixel_rst[i - 1]); clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); + } err_disable_pclk: clk_disable_unprepare(csi2rx->p_clk); @@ -188,18 +245,28 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) unsigned int i; clk_prepare_enable(csi2rx->p_clk); + reset_control_assert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->sys_clk); for (i = 0; i < csi2rx->max_streams; i++) { writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + reset_control_assert(csi2rx->pixel_rst[i]); clk_disable_unprepare(csi2rx->pixel_clk[i]); } + reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); + + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + if (phy_power_off(csi2rx->dphy)) + dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + } } static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) @@ -246,7 +313,7 @@ static const struct v4l2_subdev_ops csi2rx_subdev_ops = { static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct v4l2_subdev *subdev = notifier->sd; struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); @@ -299,21 +366,22 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, return PTR_ERR(csi2rx->p_clk); } + csi2rx->sys_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, + "sys"); + if (IS_ERR(csi2rx->sys_rst)) + return PTR_ERR(csi2rx->sys_rst); + + csi2rx->p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, + "reg_bank"); + if (IS_ERR(csi2rx->p_rst)) + return PTR_ERR(csi2rx->p_rst); + csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); if (IS_ERR(csi2rx->dphy)) { dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); return PTR_ERR(csi2rx->dphy); } - /* - * FIXME: Once we'll have external D-PHY support, the check - * will need to be removed. - */ - if (csi2rx->dphy) { - dev_err(&pdev->dev, "External D-PHY not supported yet\n"); - return -EINVAL; - } - ret = clk_prepare_enable(csi2rx->p_clk); if (ret) { dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); @@ -343,20 +411,27 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, * FIXME: Once we'll have internal D-PHY support, the check * will need to be removed. */ - if (csi2rx->has_internal_dphy) { + if (!csi2rx->dphy && csi2rx->has_internal_dphy) { dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); return -EINVAL; } for (i = 0; i < csi2rx->max_streams; i++) { - char clk_name[16]; + char name[16]; - snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); - csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); + snprintf(name, sizeof(name), "pixel_if%u_clk", i); + csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, name); if (IS_ERR(csi2rx->pixel_clk[i])) { - dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name); + dev_err(&pdev->dev, "Couldn't get clock %s\n", name); return PTR_ERR(csi2rx->pixel_clk[i]); } + + snprintf(name, sizeof(name), "pixel_if%u", i); + csi2rx->pixel_rst[i] = + devm_reset_control_get_optional_exclusive(&pdev->dev, + name); + if (IS_ERR(csi2rx->pixel_rst[i])) + return PTR_ERR(csi2rx->pixel_rst[i]); } return 0; @@ -365,7 +440,7 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) { struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwh; struct device_node *ep; int ret; @@ -399,17 +474,17 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) return -EINVAL; } - v4l2_async_nf_init(&csi2rx->notifier); + v4l2_async_subdev_nf_init(&csi2rx->notifier, &csi2rx->subdev); asd = v4l2_async_nf_add_fwnode_remote(&csi2rx->notifier, fwh, - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep); if (IS_ERR(asd)) return PTR_ERR(asd); csi2rx->notifier.ops = &csi2rx_notifier_ops; - ret = v4l2_async_subdev_nf_register(&csi2rx->subdev, &csi2rx->notifier); + ret = v4l2_async_nf_register(&csi2rx->notifier); if (ret) v4l2_async_nf_cleanup(&csi2rx->notifier); @@ -462,6 +537,7 @@ static int csi2rx_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->dphy ? "external" : csi2rx->has_internal_dphy ? "internal" : "no"); return 0; @@ -482,6 +558,7 @@ static void csi2rx_remove(struct platform_device *pdev) } static const struct of_device_id csi2rx_of_table[] = { + { .compatible = "starfive,jh7110-csi2rx" }, { .compatible = "cdns,csi2rx" }, { }, }; diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda-common.c index ac9a642ae76f..cc4892129aaf 100644 --- a/drivers/media/platform/chips-media/coda-common.c +++ b/drivers/media/platform/chips-media/coda-common.c @@ -19,12 +19,12 @@ #include #include #include -#include +#include +#include #include #include #include #include -#include #include #include diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index 9ed3c2e063de..6e6caf50e11e 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -2044,7 +2044,7 @@ static const struct video_device pxa_camera_videodev_template = { static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { int err; struct v4l2_device *v4l2_dev = notifier->v4l2_dev; @@ -2123,7 +2123,7 @@ out: static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(notifier->v4l2_dev); @@ -2197,7 +2197,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, struct pxa_camera_dev *pcdev) { u32 mclk_rate; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *np = dev->of_node; struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; int err = of_property_read_u32(np, "clock-frequency", @@ -2252,7 +2252,7 @@ static int pxa_camera_pdata_from_dt(struct device *dev, asd = v4l2_async_nf_add_fwnode_remote(&pcdev->notifier, of_fwnode_handle(np), - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) err = PTR_ERR(asd); out: @@ -2274,9 +2274,8 @@ static int pxa_camera_probe(struct platform_device *pdev) int irq; int err = 0, i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || irq < 0) + if (irq < 0) return -ENODEV; pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); @@ -2289,27 +2288,41 @@ static int pxa_camera_probe(struct platform_device *pdev) if (IS_ERR(pcdev->clk)) return PTR_ERR(pcdev->clk); - v4l2_async_nf_init(&pcdev->notifier); + /* + * Request the regions. + */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + pcdev->irq = irq; + pcdev->base = base; + + err = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); + if (err) + return err; + + v4l2_async_nf_init(&pcdev->notifier, &pcdev->v4l2_dev); pcdev->res = res; pcdev->pdata = pdev->dev.platform_data; if (pcdev->pdata) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; asd = v4l2_async_nf_add_i2c(&pcdev->notifier, pcdev->pdata->sensor_i2c_adapter_id, pcdev->pdata->sensor_i2c_address, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) err = PTR_ERR(asd); } else if (pdev->dev.of_node) { err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); } else { - return -ENODEV; + err = -ENODEV; } if (err < 0) - return err; + goto exit_v4l2_device_unregister; if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) { @@ -2338,21 +2351,12 @@ static int pxa_camera_probe(struct platform_device *pdev) spin_lock_init(&pcdev->lock); mutex_init(&pcdev->mlock); - /* - * Request the regions. - */ - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - pcdev->irq = irq; - pcdev->base = base; - /* request dma */ pcdev->dma_chans[0] = dma_request_chan(&pdev->dev, "CI_Y"); if (IS_ERR(pcdev->dma_chans[0])) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - return PTR_ERR(pcdev->dma_chans[0]); + err = PTR_ERR(pcdev->dma_chans[0]); + goto exit_notifier_cleanup; } pcdev->dma_chans[1] = dma_request_chan(&pdev->dev, "CI_U"); @@ -2379,36 +2383,30 @@ static int pxa_camera_probe(struct platform_device *pdev) } } - /* request irq */ - err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0, - PXA_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_free_dma; - } - tasklet_setup(&pcdev->task_eof, pxa_camera_eof); pxa_camera_activate(pcdev); platform_set_drvdata(pdev, pcdev); - err = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); - if (err) - goto exit_deactivate; err = pxa_camera_init_videobuf2(pcdev); if (err) - goto exit_notifier_cleanup; + goto exit_deactivate; + + /* request irq */ + err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0, + PXA_CAM_DRV_NAME, pcdev); + if (err) { + dev_err(&pdev->dev, "Camera interrupt register failed\n"); + goto exit_v4l2_device_unregister; + } pcdev->notifier.ops = &pxa_camera_sensor_ops; - err = v4l2_async_nf_register(&pcdev->v4l2_dev, &pcdev->notifier); + err = v4l2_async_nf_register(&pcdev->notifier); if (err) - goto exit_notifier_cleanup; + goto exit_deactivate; return 0; -exit_notifier_cleanup: - v4l2_async_nf_cleanup(&pcdev->notifier); - v4l2_device_unregister(&pcdev->v4l2_dev); exit_deactivate: pxa_camera_deactivate(pcdev); tasklet_kill(&pcdev->task_eof); @@ -2418,6 +2416,10 @@ exit_free_dma_u: dma_release_channel(pcdev->dma_chans[1]); exit_free_dma_y: dma_release_channel(pcdev->dma_chans[0]); +exit_notifier_cleanup: + v4l2_async_nf_cleanup(&pcdev->notifier); +exit_v4l2_device_unregister: + v4l2_device_unregister(&pcdev->v4l2_dev); return err; } @@ -2454,7 +2456,7 @@ static struct platform_driver pxa_camera_driver = { .driver = { .name = PXA_CAM_DRV_NAME, .pm = &pxa_camera_pm, - .of_match_table = of_match_ptr(pxa_camera_of_match), + .of_match_table = pxa_camera_of_match, }, .probe = pxa_camera_probe, .remove_new = pxa_camera_remove, diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index ae97ce4ead98..ef810249def6 100644 --- a/drivers/media/platform/marvell/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c @@ -478,7 +478,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, int ret; struct cafe_camera *cam; struct mcam_camera *mcam; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct i2c_client *i2c_dev; /* @@ -536,19 +536,24 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_pdown; - v4l2_async_nf_init(&mcam->notifier); + ret = v4l2_device_register(mcam->dev, &mcam->v4l2_dev); + if (ret) + goto out_smbus_shutdown; + + v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev); asd = v4l2_async_nf_add_i2c(&mcam->notifier, i2c_adapter_id(cam->i2c_adapter), - ov7670_info.addr, struct v4l2_async_subdev); + ov7670_info.addr, + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); - goto out_smbus_shutdown; + goto out_v4l2_device_unregister; } ret = mccic_register(mcam); if (ret) - goto out_smbus_shutdown; + goto out_v4l2_device_unregister; clkdev_create(mcam->mclk, "xclk", "%d-%04x", i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); @@ -564,6 +569,8 @@ static int cafe_pci_probe(struct pci_dev *pdev, out_mccic_shutdown: mccic_shutdown(mcam); +out_v4l2_device_unregister: + v4l2_device_unregister(&mcam->v4l2_dev); out_smbus_shutdown: cafe_smbus_shutdown(cam); out_pdown: @@ -586,6 +593,7 @@ out: static void cafe_shutdown(struct cafe_camera *cam) { mccic_shutdown(&cam->mcam); + v4l2_device_unregister(&cam->mcam.v4l2_dev); cafe_smbus_shutdown(cam); free_irq(cam->pdev->irq, cam); pci_iounmap(cam->pdev, cam->mcam.regs); diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 154bdcb3f2cc..66688b4aece5 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -1756,7 +1756,7 @@ EXPORT_SYMBOL_GPL(mccic_irq); */ static int mccic_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) + struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) { struct mcam_camera *cam = notifier_to_mcam(notifier); int ret; @@ -1801,7 +1801,7 @@ out: } static void mccic_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) + struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) { struct mcam_camera *cam = notifier_to_mcam(notifier); @@ -1863,13 +1863,6 @@ int mccic_register(struct mcam_camera *cam) goto out; } - /* - * Register with V4L - */ - ret = v4l2_device_register(cam->dev, &cam->v4l2_dev); - if (ret) - goto out; - mutex_init(&cam->s_mutex); cam->state = S_NOTREADY; mcam_set_config_needed(cam, 1); @@ -1877,7 +1870,7 @@ int mccic_register(struct mcam_camera *cam) cam->mbus_code = mcam_def_mbus_code; cam->notifier.ops = &mccic_notify_ops; - ret = v4l2_async_nf_register(&cam->v4l2_dev, &cam->notifier); + ret = v4l2_async_nf_register(&cam->notifier); if (ret < 0) { cam_warn(cam, "failed to register a sensor notifier"); goto out; @@ -1915,7 +1908,6 @@ int mccic_register(struct mcam_camera *cam) out: v4l2_async_nf_unregister(&cam->notifier); - v4l2_device_unregister(&cam->v4l2_dev); v4l2_async_nf_cleanup(&cam->notifier); return ret; } @@ -1937,7 +1929,6 @@ void mccic_shutdown(struct mcam_camera *cam) mcam_free_dma_bufs(cam); v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_async_nf_unregister(&cam->notifier); - v4l2_device_unregister(&cam->v4l2_dev); v4l2_async_nf_cleanup(&cam->notifier); } EXPORT_SYMBOL_GPL(mccic_shutdown); diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index e93feefb447b..170907cc1885 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -180,7 +180,7 @@ static int mmpcam_probe(struct platform_device *pdev) struct resource *res; struct fwnode_handle *ep; struct mmp_camera_platform_data *pdata; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret; cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); @@ -223,8 +223,7 @@ static int mmpcam_probe(struct platform_device *pdev) /* * Get our I/O memory. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mcam->regs = devm_ioremap_resource(&pdev->dev, res); + mcam->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(mcam->regs)) return PTR_ERR(mcam->regs); mcam->regs_size = resource_size(res); @@ -239,10 +238,10 @@ static int mmpcam_probe(struct platform_device *pdev) if (!ep) return -ENODEV; - v4l2_async_nf_init(&mcam->notifier); + v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&mcam->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); if (IS_ERR(asd)) { ret = PTR_ERR(asd); @@ -362,7 +361,7 @@ static struct platform_driver mmpcam_driver = { .remove_new = mmpcam_remove, .driver = { .name = "mmp-camera", - .of_match_table = of_match_ptr(mmpcam_of_match), + .of_match_table = mmpcam_of_match, .pm = &mmpcam_pm_ops, } }; diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 60425c99a2b8..7194f88edc0f 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1403,6 +1403,7 @@ static void mtk_jpeg_remove(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev); + cancel_delayed_work_sync(&jpeg->job_timeout_work); pm_runtime_disable(&pdev->dev); video_unregister_device(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c index baa7be58ce69..4a6ee211e18f 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c index 244018365b6f..2bbc48c7402c 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c @@ -10,9 +10,9 @@ #include #include #include +#include #include -#include -#include +#include #include #include #include diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c index ad5fab2d8bfa..3501ac411242 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c @@ -7,8 +7,6 @@ #include #include #include -#include -#include #include "mtk_mdp_comp.h" diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c index 77e310e588e5..917cdf38f230 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c @@ -28,7 +28,7 @@ EXPORT_SYMBOL(mtk_mdp_dbg_level); module_param(mtk_mdp_dbg_level, int, 0644); -static const struct of_device_id mtk_mdp_comp_dt_ids[] = { +static const struct of_device_id mtk_mdp_comp_dt_ids[] __maybe_unused = { { .compatible = "mediatek,mt8173-mdp-rdma", .data = (void *)MTK_MDP_RDMA diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c index a605e80c7dc3..667933ea15f4 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c @@ -746,7 +746,7 @@ static const struct mdp_comp_ops *mdp_comp_ops[MDP_COMP_TYPE_COUNT] = { [MDP_COMP_TYPE_CCORR] = &ccorr_ops, }; -static const struct of_device_id mdp_comp_dt_ids[] = { +static const struct of_device_id mdp_comp_dt_ids[] __maybe_unused = { { .compatible = "mediatek,mt8183-mdp3-rdma", .data = (void *)MDP_COMP_TYPE_RDMA, @@ -892,11 +892,13 @@ static int mdp_get_subsys_id(struct mdp_dev *mdp, struct device *dev, ret = cmdq_dev_get_client_reg(&comp_pdev->dev, &cmdq_reg, index); if (ret != 0) { dev_err(&comp_pdev->dev, "cmdq_dev_get_subsys fail!\n"); + put_device(&comp_pdev->dev); return -EINVAL; } comp->subsys_id = cmdq_reg.subsys; dev_dbg(&comp_pdev->dev, "subsys id=%d\n", cmdq_reg.subsys); + put_device(&comp_pdev->dev); return 0; } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index aa6c225302f0..cc44be10fdb7 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -322,7 +322,7 @@ static struct platform_driver mdp_driver = { .driver = { .name = MDP_MODULE_NAME, .pm = &mdp_pm_ops, - .of_match_table = of_match_ptr(mdp_of_ids), + .of_match_table = mdp_of_ids, }, }; diff --git a/drivers/media/platform/mediatek/vcodec/Makefile b/drivers/media/platform/mediatek/vcodec/Makefile index 5f4c30fec85a..014abbfbd993 100644 --- a/drivers/media/platform/mediatek/vcodec/Makefile +++ b/drivers/media/platform/mediatek/vcodec/Makefile @@ -1,54 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \ - mtk-vcodec-enc.o \ - mtk-vcodec-common.o \ - mtk-vcodec-dec-hw.o - -mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ - vdec/vdec_vp8_if.o \ - vdec/vdec_vp8_req_if.o \ - vdec/vdec_vp9_if.o \ - vdec/vdec_vp9_req_lat_if.o \ - vdec/vdec_av1_req_lat_if.o \ - vdec/vdec_h264_req_if.o \ - vdec/vdec_h264_req_common.o \ - vdec/vdec_h264_req_multi_if.o \ - vdec/vdec_hevc_req_multi_if.o \ - mtk_vcodec_dec_drv.o \ - vdec_drv_if.o \ - vdec_vpu_if.o \ - vdec_msg_queue.o \ - mtk_vcodec_dec.o \ - mtk_vcodec_dec_stateful.o \ - mtk_vcodec_dec_stateless.o \ - mtk_vcodec_dec_pm.o \ - -mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o - -mtk-vcodec-enc-y := venc/venc_vp8_if.o \ - venc/venc_h264_if.o \ - mtk_vcodec_enc.o \ - mtk_vcodec_enc_drv.o \ - mtk_vcodec_enc_pm.o \ - venc_drv_if.o \ - venc_vpu_if.o \ - - -mtk-vcodec-common-y := mtk_vcodec_intr.o \ - mtk_vcodec_util.o \ - mtk_vcodec_fw.o \ - -ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU),) -mtk-vcodec-common-y += mtk_vcodec_fw_vpu.o -endif - -ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),) -mtk-vcodec-common-y += mtk_vcodec_fw_scp.o -endif - -ifneq ($(CONFIG_DEBUG_FS),) -obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dbgfs.o - -mtk-vcodec-dbgfs-y := mtk_vcodec_dbgfs.o -endif \ No newline at end of file +obj-y += common/ +obj-y += encoder/ +obj-y += decoder/ diff --git a/drivers/media/platform/mediatek/vcodec/common/Makefile b/drivers/media/platform/mediatek/vcodec/common/Makefile new file mode 100644 index 000000000000..d0479914dfb3 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/common/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-common.o + +mtk-vcodec-common-y := mtk_vcodec_intr.o \ + mtk_vcodec_util.o \ + mtk_vcodec_fw.o \ + +ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU),) +mtk-vcodec-common-y += mtk_vcodec_fw_vpu.o +endif + +ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),) +mtk-vcodec-common-y += mtk_vcodec_fw_scp.o +endif + +ifneq ($(CONFIG_DEBUG_FS),) +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dbgfs.o + +mtk-vcodec-dbgfs-y := mtk_vcodec_dbgfs.o +endif \ No newline at end of file diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_cmn_drv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_cmn_drv.h new file mode 100644 index 000000000000..6087e27bd604 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_cmn_drv.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong + */ + +#ifndef _MTK_VCODEC_COM_DRV_H_ +#define _MTK_VCODEC_COM_DRV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define MTK_VCODEC_MAX_PLANES 3 + +#define WAIT_INTR_TIMEOUT_MS 1000 + +/* + * enum mtk_q_type - Type of queue + */ +enum mtk_q_type { + MTK_Q_DATA_SRC = 0, + MTK_Q_DATA_DST = 1, +}; + +/* + * enum mtk_hw_reg_idx - MTK hw register base index + */ +enum mtk_hw_reg_idx { + VDEC_SYS, + VDEC_MISC, + VDEC_LD, + VDEC_TOP, + VDEC_CM, + VDEC_AD, + VDEC_AV, + VDEC_PP, + VDEC_HWD, + VDEC_HWQ, + VDEC_HWB, + VDEC_HWG, + NUM_MAX_VDEC_REG_BASE, + /* h264 encoder */ + VENC_SYS = NUM_MAX_VDEC_REG_BASE, + /* vp8 encoder */ + VENC_LT_SYS, + NUM_MAX_VCODEC_REG_BASE +}; + +/* + * struct mtk_vcodec_clk_info - Structure used to store clock name + */ +struct mtk_vcodec_clk_info { + const char *clk_name; + struct clk *vcodec_clk; +}; + +/* + * struct mtk_vcodec_clk - Structure used to store vcodec clock information + */ +struct mtk_vcodec_clk { + struct mtk_vcodec_clk_info *clk_info; + int clk_num; +}; + +/* + * struct mtk_vcodec_pm - Power management data structure + */ +struct mtk_vcodec_pm { + struct mtk_vcodec_clk vdec_clk; + struct mtk_vcodec_clk venc_clk; + struct device *dev; +}; + +/* + * enum mtk_vdec_hw_id - Hardware index used to separate + * different hardware + */ +enum mtk_vdec_hw_id { + MTK_VDEC_CORE, + MTK_VDEC_LAT0, + MTK_VDEC_LAT1, + MTK_VDEC_LAT_SOC, + MTK_VDEC_HW_MAX, +}; + +/** + * enum mtk_instance_state - The state of an MTK Vcodec instance. + * @MTK_STATE_FREE: default state when instance is created + * @MTK_STATE_INIT: vcodec instance is initialized + * @MTK_STATE_HEADER: vdec had sps/pps header parsed or venc + * had sps/pps header encoded + * @MTK_STATE_FLUSH: vdec is flushing. Only used by decoder + * @MTK_STATE_ABORT: vcodec should be aborted + */ +enum mtk_instance_state { + MTK_STATE_FREE = 0, + MTK_STATE_INIT = 1, + MTK_STATE_HEADER = 2, + MTK_STATE_FLUSH = 3, + MTK_STATE_ABORT = 4, +}; + +enum mtk_fmt_type { + MTK_FMT_DEC = 0, + MTK_FMT_ENC = 1, + MTK_FMT_FRAME = 2, +}; + +/* + * struct mtk_video_fmt - Structure used to store information about pixelformats + */ +struct mtk_video_fmt { + u32 fourcc; + enum mtk_fmt_type type; + u32 num_planes; + u32 flags; + struct v4l2_frmsize_stepwise frmsize; +}; + +/* + * struct mtk_q_data - Structure used to store information about queue + */ +struct mtk_q_data { + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + enum v4l2_field field; + unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; + unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; + const struct mtk_video_fmt *fmt; +}; + +/* + * enum mtk_instance_type - The type of an MTK Vcodec instance. + */ +enum mtk_instance_type { + MTK_INST_DECODER = 0, + MTK_INST_ENCODER = 1, +}; + +#endif /* _MTK_VCODEC_COM_DRV_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c similarity index 75% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c index b5cdbbfcc388..5ad3797836db 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c @@ -7,10 +7,11 @@ #include #include "mtk_vcodec_dbgfs.h" -#include "mtk_vcodec_drv.h" +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" #include "mtk_vcodec_util.h" -static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_ctx *ctx, char *buf, +static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_dec_ctx *ctx, char *buf, int *used, int total) { int curr_len; @@ -72,7 +73,7 @@ static void mtk_vdec_dbgfs_get_help(char *buf, int *used, int total) static ssize_t mtk_vdec_dbgfs_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos) { - struct mtk_vcodec_dev *vcodec_dev = filp->private_data; + struct mtk_vcodec_dec_dev *vcodec_dev = filp->private_data; struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs; mutex_lock(&dbgfs->dbgfs_lock); @@ -88,10 +89,10 @@ static ssize_t mtk_vdec_dbgfs_write(struct file *filp, const char __user *ubuf, static ssize_t mtk_vdec_dbgfs_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - struct mtk_vcodec_dev *vcodec_dev = filp->private_data; + struct mtk_vcodec_dec_dev *vcodec_dev = filp->private_data; struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs; struct mtk_vcodec_dbgfs_inst *dbgfs_inst; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; int total_len = 200 * (dbgfs->inst_count == 0 ? 1 : dbgfs->inst_count); int used_len = 0, curr_len, ret; bool dbgfs_index[MTK_VDEC_DBGFS_MAX] = {0}; @@ -143,10 +144,10 @@ static const struct file_operations vdec_fops = { .read = mtk_vdec_dbgfs_read, }; -void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx) +void mtk_vcodec_dbgfs_create(struct mtk_vcodec_dec_ctx *ctx) { struct mtk_vcodec_dbgfs_inst *dbgfs_inst; - struct mtk_vcodec_dev *vcodec_dev = ctx->dev; + struct mtk_vcodec_dec_dev *vcodec_dev = ctx->dev; dbgfs_inst = kzalloc(sizeof(*dbgfs_inst), GFP_KERNEL); if (!dbgfs_inst) @@ -161,34 +162,47 @@ void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx) } EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_create); -void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id) +void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dec_dev *vcodec_dev, int ctx_id) { struct mtk_vcodec_dbgfs_inst *dbgfs_inst; list_for_each_entry(dbgfs_inst, &vcodec_dev->dbgfs.dbgfs_head, node) { if (dbgfs_inst->inst_id == ctx_id) { vcodec_dev->dbgfs.inst_count--; - break; + list_del(&dbgfs_inst->node); + kfree(dbgfs_inst); + return; } } - - if (dbgfs_inst) { - list_del(&dbgfs_inst->node); - kfree(dbgfs_inst); - } } EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_remove); -void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode) +static void mtk_vcodec_dbgfs_vdec_init(struct mtk_vcodec_dec_dev *vcodec_dev) { struct dentry *vcodec_root; - if (is_encode) - vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-enc", NULL); - else - vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL); + vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL); if (IS_ERR(vcodec_dev->dbgfs.vcodec_root)) - dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%d\n", + dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%ld\n", + PTR_ERR(vcodec_dev->dbgfs.vcodec_root)); + + vcodec_root = vcodec_dev->dbgfs.vcodec_root; + debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level); + debugfs_create_x32("mtk_vcodec_dbg", 0644, vcodec_root, &mtk_vcodec_dbg); + + vcodec_dev->dbgfs.inst_count = 0; + INIT_LIST_HEAD(&vcodec_dev->dbgfs.dbgfs_head); + debugfs_create_file("vdec", 0200, vcodec_root, vcodec_dev, &vdec_fops); + mutex_init(&vcodec_dev->dbgfs.dbgfs_lock); +} + +static void mtk_vcodec_dbgfs_venc_init(struct mtk_vcodec_enc_dev *vcodec_dev) +{ + struct dentry *vcodec_root; + + vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-enc", NULL); + if (IS_ERR(vcodec_dev->dbgfs.vcodec_root)) + dev_err(&vcodec_dev->plat_dev->dev, "create venc dir err:%d\n", IS_ERR(vcodec_dev->dbgfs.vcodec_root)); vcodec_root = vcodec_dev->dbgfs.vcodec_root; @@ -196,18 +210,20 @@ void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode) debugfs_create_x32("mtk_vcodec_dbg", 0644, vcodec_root, &mtk_vcodec_dbg); vcodec_dev->dbgfs.inst_count = 0; - if (is_encode) - return; +} - INIT_LIST_HEAD(&vcodec_dev->dbgfs.dbgfs_head); - debugfs_create_file("vdec", 0200, vcodec_root, vcodec_dev, &vdec_fops); - mutex_init(&vcodec_dev->dbgfs.dbgfs_lock); +void mtk_vcodec_dbgfs_init(void *vcodec_dev, bool is_encode) +{ + if (is_encode) + mtk_vcodec_dbgfs_venc_init(vcodec_dev); + else + mtk_vcodec_dbgfs_vdec_init(vcodec_dev); } EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_init); -void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev) +void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dbgfs *dbgfs) { - debugfs_remove_recursive(vcodec_dev->dbgfs.vcodec_root); + debugfs_remove_recursive(dbgfs->vcodec_root); } EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_deinit); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.h similarity index 62% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.h index 241ff8197e73..073d2fedb54a 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.h @@ -7,8 +7,8 @@ #ifndef __MTK_VCODEC_DBGFS_H__ #define __MTK_VCODEC_DBGFS_H__ -struct mtk_vcodec_dev; -struct mtk_vcodec_ctx; +struct mtk_vcodec_dec_dev; +struct mtk_vcodec_dec_ctx; /* * enum mtk_vdec_dbgfs_log_index - used to get different debug information @@ -22,12 +22,12 @@ enum mtk_vdec_dbgfs_log_index { /** * struct mtk_vcodec_dbgfs_inst - debugfs information for each inst * @node: list node for each inst - * @vcodec_ctx: struct mtk_vcodec_ctx + * @vcodec_ctx: struct mtk_vcodec_dec_ctx * @inst_id: index of the context that the same with ctx->id */ struct mtk_vcodec_dbgfs_inst { struct list_head node; - struct mtk_vcodec_ctx *vcodec_ctx; + struct mtk_vcodec_dec_ctx *vcodec_ctx; int inst_id; }; @@ -50,24 +50,24 @@ struct mtk_vcodec_dbgfs { }; #if defined(CONFIG_DEBUG_FS) -void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx); -void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id); -void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode); -void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev); +void mtk_vcodec_dbgfs_create(struct mtk_vcodec_dec_ctx *ctx); +void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dec_dev *vcodec_dev, int ctx_id); +void mtk_vcodec_dbgfs_init(void *vcodec_dev, bool is_encode); +void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dbgfs *dbgfs); #else -static inline void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx) +static inline void mtk_vcodec_dbgfs_create(struct mtk_vcodec_dec_ctx *ctx) { } -static inline void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id) +static inline void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dec_dev *vcodec_dev, int ctx_id) { } -static inline void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode) +static inline void mtk_vcodec_dbgfs_init(void *vcodec_dev, bool is_encode) { } -static inline void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev) +static inline void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dbgfs *dbgfs) { } #endif diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c similarity index 75% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c index 556e54aadac9..08949b08fbc6 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c @@ -1,21 +1,26 @@ // SPDX-License-Identifier: GPL-2.0 -#include "mtk_vcodec_fw.h" +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" #include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" -struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_type type, +struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type, enum mtk_vcodec_fw_use fw_use) { + struct platform_device *plat_dev; + + if (fw_use == ENCODER) + plat_dev = ((struct mtk_vcodec_enc_dev *)priv)->plat_dev; + else + plat_dev = ((struct mtk_vcodec_dec_dev *)priv)->plat_dev; + switch (type) { case VPU: - return mtk_vcodec_fw_vpu_init(dev, fw_use); + return mtk_vcodec_fw_vpu_init(priv, fw_use); case SCP: - return mtk_vcodec_fw_scp_init(dev); + return mtk_vcodec_fw_scp_init(priv, fw_use); default: - mtk_v4l2_err("invalid vcodec fw type"); + dev_err(&plat_dev->dev, "Invalid vcodec fw type"); return ERR_PTR(-EINVAL); } } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h similarity index 86% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h index 16824114657f..300363a40158 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h @@ -6,9 +6,10 @@ #include #include -#include "../vpu/mtk_vpu.h" +#include "../../vpu/mtk_vpu.h" -struct mtk_vcodec_dev; +struct mtk_vcodec_dec_dev; +struct mtk_vcodec_enc_dev; enum mtk_vcodec_fw_type { VPU, @@ -25,8 +26,7 @@ struct mtk_vcodec_fw; typedef void (*mtk_vcodec_ipi_handler) (void *data, unsigned int len, void *priv); -struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_type type, +struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type, enum mtk_vcodec_fw_use fw_use); void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h similarity index 75% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h index b41e66185cec..99603accd82e 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h @@ -5,13 +5,15 @@ #include "mtk_vcodec_fw.h" -struct mtk_vcodec_dev; +struct mtk_vcodec_dec_dev; +struct mtk_vcodec_enc_dev; struct mtk_vcodec_fw { enum mtk_vcodec_fw_type type; const struct mtk_vcodec_fw_ops *ops; struct platform_device *pdev; struct mtk_scp *scp; + enum mtk_vcodec_fw_use fw_use; }; struct mtk_vcodec_fw_ops { @@ -28,22 +30,20 @@ struct mtk_vcodec_fw_ops { }; #if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU) -struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use); +struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use fw_use); #else static inline struct mtk_vcodec_fw * -mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use) +mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use fw_use) { return ERR_PTR(-ENODEV); } #endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_VPU */ #if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP) -struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev); +struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use fw_use); #else static inline struct mtk_vcodec_fw * -mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) +mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use fw_use) { return ERR_PTR(-ENODEV); } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c similarity index 70% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c index d8e66b645bd8..9e744d07a1e8 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" #include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" static int mtk_vcodec_scp_load_firmware(struct mtk_vcodec_fw *fw) { @@ -53,18 +53,32 @@ static const struct mtk_vcodec_fw_ops mtk_vcodec_rproc_msg = { .release = mtk_vcodec_scp_release, }; -struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) +struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use fw_use) { struct mtk_vcodec_fw *fw; + struct platform_device *plat_dev; struct mtk_scp *scp; - scp = scp_get(dev->plat_dev); + if (fw_use == ENCODER) { + struct mtk_vcodec_enc_dev *enc_dev = priv; + + plat_dev = enc_dev->plat_dev; + } else if (fw_use == DECODER) { + struct mtk_vcodec_dec_dev *dec_dev = priv; + + plat_dev = dec_dev->plat_dev; + } else { + pr_err("Invalid fw_use %d (use a resonable fw id here)\n", fw_use); + return ERR_PTR(-EINVAL); + } + + scp = scp_get(plat_dev); if (!scp) { - mtk_v4l2_err("could not get vdec scp handle"); + dev_err(&plat_dev->dev, "could not get vdec scp handle"); return ERR_PTR(-EPROBE_DEFER); } - fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL); fw->type = SCP; fw->ops = &mtk_vcodec_rproc_msg; fw->scp = scp; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c similarity index 58% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c index cfc7ebed8fb7..5e03b0886559 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" #include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" static int mtk_vcodec_vpu_load_firmware(struct mtk_vcodec_fw *fw) { @@ -51,18 +51,32 @@ static void mtk_vcodec_vpu_release(struct mtk_vcodec_fw *fw) put_device(&fw->pdev->dev); } -static void mtk_vcodec_vpu_reset_handler(void *priv) +static void mtk_vcodec_vpu_reset_dec_handler(void *priv) { - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_dev *dev = priv; + struct mtk_vcodec_dec_ctx *ctx; - mtk_v4l2_err("Watchdog timeout!!"); + dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); mutex_lock(&dev->dev_mutex); list_for_each_entry(ctx, &dev->ctx_list, list) { ctx->state = MTK_STATE_ABORT; - mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", - ctx->id); + mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); + } + mutex_unlock(&dev->dev_mutex); +} + +static void mtk_vcodec_vpu_reset_enc_handler(void *priv) +{ + struct mtk_vcodec_enc_dev *dev = priv; + struct mtk_vcodec_enc_ctx *ctx; + + dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); + + mutex_lock(&dev->dev_mutex); + list_for_each_entry(ctx, &dev->ctx_list, list) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); } mutex_unlock(&dev->dev_mutex); } @@ -77,36 +91,46 @@ static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { .release = mtk_vcodec_vpu_release, }; -struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use) +struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use fw_use) { struct platform_device *fw_pdev; + struct platform_device *plat_dev; struct mtk_vcodec_fw *fw; enum rst_id rst_id; - switch (fw_use) { - case ENCODER: - rst_id = VPU_RST_ENC; - break; - case DECODER: - default: - rst_id = VPU_RST_DEC; - break; - } + if (fw_use == ENCODER) { + struct mtk_vcodec_enc_dev *enc_dev = priv; - fw_pdev = vpu_get_plat_device(dev->plat_dev); - if (!fw_pdev) { - mtk_v4l2_err("firmware device is not ready"); + plat_dev = enc_dev->plat_dev; + rst_id = VPU_RST_ENC; + } else if (fw_use == DECODER) { + struct mtk_vcodec_dec_dev *dec_dev = priv; + + plat_dev = dec_dev->plat_dev; + rst_id = VPU_RST_DEC; + } else { + pr_err("Invalid fw_use %d (use a resonable fw id here)\n", fw_use); return ERR_PTR(-EINVAL); } - vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id); - fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + fw_pdev = vpu_get_plat_device(plat_dev); + if (!fw_pdev) { + dev_err(&plat_dev->dev, "firmware device is not ready"); + return ERR_PTR(-EINVAL); + } + + if (fw_use == DECODER) + vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_dec_handler, priv, rst_id); + else + vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_enc_handler, priv, rst_id); + + fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL); if (!fw) return ERR_PTR(-ENOMEM); fw->type = VPU; fw->ops = &mtk_vcodec_vpu_msg; fw->pdev = fw_pdev; + fw->fw_use = fw_use; return fw; } diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.c new file mode 100644 index 000000000000..f203fc25636b --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +*/ + +#include +#include + +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" +#include "mtk_vcodec_intr.h" + +int mtk_vcodec_wait_for_done_ctx(void *priv, int command, unsigned int timeout_ms, + unsigned int hw_id) +{ + int instance_type = *((int *)priv); + long timeout_jiff, ret; + int ctx_id, ctx_type, status = 0; + int *ctx_int_cond, *ctx_int_type; + wait_queue_head_t *ctx_queue; + struct platform_device *pdev; + + if (instance_type == DECODER) { + struct mtk_vcodec_dec_ctx *ctx; + + ctx = priv; + ctx_id = ctx->id; + ctx_type = ctx->type; + ctx_int_cond = ctx->int_cond; + ctx_int_type = ctx->int_type; + ctx_queue = ctx->queue; + pdev = ctx->dev->plat_dev; + } else { + struct mtk_vcodec_enc_ctx *ctx; + + ctx = priv; + ctx_id = ctx->id; + ctx_type = ctx->type; + ctx_int_cond = ctx->int_cond; + ctx_int_type = ctx->int_type; + ctx_queue = ctx->queue; + pdev = ctx->dev->plat_dev; + } + + timeout_jiff = msecs_to_jiffies(timeout_ms); + ret = wait_event_interruptible_timeout(ctx_queue[hw_id], + ctx_int_cond[hw_id], + timeout_jiff); + + if (!ret) { + status = -1; /* timeout */ + dev_err(&pdev->dev, "[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)", + ctx_id, command, ctx_type, timeout_ms, + ctx_int_cond[hw_id], ctx_int_type[hw_id]); + } else if (-ERESTARTSYS == ret) { + status = -1; + dev_err(&pdev->dev, "[%d] cmd=%d, type=%d, dec inter fail (%d %d)", + ctx_id, command, ctx_type, + ctx_int_cond[hw_id], ctx_int_type[hw_id]); + } + + ctx_int_cond[hw_id] = 0; + ctx_int_type[hw_id] = 0; + + return status; +} +EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.h similarity index 68% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.h index 9681f492813b..3e3cc71ee572 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.h @@ -9,11 +9,11 @@ #define MTK_INST_IRQ_RECEIVED 0x1 -struct mtk_vcodec_ctx; +struct mtk_vcodec_dec_ctx; +struct mtk_vcodec_enc_ctx; /* timeout is ms */ -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, - int command, unsigned int timeout_ms, +int mtk_vcodec_wait_for_done_ctx(void *priv, int command, unsigned int timeout_ms, unsigned int hw_id); #endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c similarity index 50% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c rename to drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c index f214e6f67005..908602031fd0 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c @@ -7,11 +7,11 @@ #include #include -#include +#include -#include "mtk_vcodec_dec_hw.h" -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" +#include "../decoder/mtk_vcodec_dec_drv.h" +#include "../encoder/mtk_vcodec_enc_drv.h" +#include "../decoder/mtk_vcodec_dec_hw.h" #if defined(CONFIG_DEBUG_FS) int mtk_vcodec_dbg; @@ -21,59 +21,66 @@ int mtk_v4l2_dbg_level; EXPORT_SYMBOL(mtk_v4l2_dbg_level); #endif -void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, - unsigned int reg_idx) +void __iomem *mtk_vcodec_get_reg_addr(void __iomem **reg_base, unsigned int reg_idx) { - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; - - if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { - mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); + if (reg_idx >= NUM_MAX_VCODEC_REG_BASE) { + pr_err(MTK_DBG_V4L2_STR "Invalid arguments, reg_idx=%d", reg_idx); return NULL; } - return ctx->dev->reg_base[reg_idx]; + return reg_base[reg_idx]; } EXPORT_SYMBOL(mtk_vcodec_get_reg_addr); -int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem) +int mtk_vcodec_write_vdecsys(struct mtk_vcodec_dec_ctx *ctx, unsigned int reg, + unsigned int val) +{ + struct mtk_vcodec_dec_dev *dev = ctx->dev; + + if (dev->vdecsys_regmap) + return regmap_write(dev->vdecsys_regmap, reg, val); + + writel(val, dev->reg_base[VDEC_SYS] + reg); + + return 0; +} +EXPORT_SYMBOL(mtk_vcodec_write_vdecsys); + +int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem) { unsigned long size = mem->size; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct mtk_vcodec_dec_ctx *ctx = priv; struct device *dev = &ctx->dev->plat_dev->dev; mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); if (!mem->va) { - mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), - size); + mtk_v4l2_vdec_err(ctx, "%s dma_alloc size=%ld failed!", dev_name(dev), size); return -ENOMEM; } - mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); - mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, - (unsigned long)mem->dma_addr); - mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] size = 0x%lx", ctx->id, size); return 0; } EXPORT_SYMBOL(mtk_vcodec_mem_alloc); -void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem) +void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem) { unsigned long size = mem->size; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct mtk_vcodec_dec_ctx *ctx = priv; struct device *dev = &ctx->dev->plat_dev->dev; if (!mem->va) { - mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), - size); + mtk_v4l2_vdec_err(ctx, "%s dma_free size=%ld failed!", dev_name(dev), size); return; } - mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); - mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, - (unsigned long)mem->dma_addr); - mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] size = 0x%lx", ctx->id, size); dma_free_coherent(dev, size, mem->va, mem->dma_addr); mem->va = NULL; @@ -82,10 +89,10 @@ void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, } EXPORT_SYMBOL(mtk_vcodec_mem_free); -void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx) +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dec_dev *dev, int hw_idx) { if (hw_idx >= MTK_VDEC_HW_MAX || hw_idx < 0 || !dev->subdev_dev[hw_idx]) { - mtk_v4l2_err("hw idx is out of range:%d", hw_idx); + dev_err(&dev->plat_dev->dev, "hw idx is out of range:%d", hw_idx); return NULL; } @@ -93,8 +100,8 @@ void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx) } EXPORT_SYMBOL(mtk_vcodec_get_hw_dev); -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - struct mtk_vcodec_ctx *ctx, int hw_idx) +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dec_dev *vdec_dev, + struct mtk_vcodec_dec_ctx *ctx, int hw_idx) { unsigned long flags; struct mtk_vdec_hw_dev *subdev_dev; @@ -103,7 +110,7 @@ void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, if (vdec_dev->vdec_pdata->is_subdev_supported) { subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev"); + dev_err(&vdec_dev->plat_dev->dev, "Failed to get hw dev"); spin_unlock_irqrestore(&vdec_dev->irqlock, flags); return; } @@ -115,18 +122,18 @@ void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, } EXPORT_SYMBOL(mtk_vcodec_set_curr_ctx); -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - unsigned int hw_idx) +struct mtk_vcodec_dec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dec_dev *vdec_dev, + unsigned int hw_idx) { unsigned long flags; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct mtk_vdec_hw_dev *subdev_dev; spin_lock_irqsave(&vdec_dev->irqlock, flags); if (vdec_dev->vdec_pdata->is_subdev_supported) { subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev"); + dev_err(&vdec_dev->plat_dev->dev, "Failed to get hw dev"); spin_unlock_irqrestore(&vdec_dev->irqlock, flags); return NULL; } diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.h new file mode 100644 index 000000000000..85f615cdd4d3 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_UTIL_H_ +#define _MTK_VCODEC_UTIL_H_ + +#include +#include + +#define MTK_DBG_VCODEC_STR "[MTK_VCODEC]" +#define MTK_DBG_V4L2_STR "[MTK_V4L2]" + +struct mtk_vcodec_mem { + size_t size; + void *va; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_fb { + size_t size; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_dec_ctx; +struct mtk_vcodec_dec_dev; + +#undef pr_fmt +#define pr_fmt(fmt) "%s(),%d: " fmt, __func__, __LINE__ + +#define mtk_v4l2_err(plat_dev, fmt, args...) \ + dev_err(&(plat_dev)->dev, "[MTK_V4L2][ERROR] " fmt "\n", ##args) + +#define mtk_vcodec_err(inst_id, plat_dev, fmt, args...) \ + dev_err(&(plat_dev)->dev, "[MTK_VCODEC][ERROR][%d]: " fmt "\n", inst_id, ##args) + +#if defined(CONFIG_DEBUG_FS) +extern int mtk_v4l2_dbg_level; +extern int mtk_vcodec_dbg; + +#define mtk_v4l2_debug(plat_dev, level, fmt, args...) \ + do { \ + if (mtk_v4l2_dbg_level >= (level)) \ + dev_dbg(&(plat_dev)->dev, "[MTK_V4L2] %s, %d: " fmt "\n", \ + __func__, __LINE__, ##args); \ + } while (0) + +#define mtk_vcodec_debug(inst_id, plat_dev, fmt, args...) \ + do { \ + if (mtk_vcodec_dbg) \ + dev_dbg(&(plat_dev)->dev, "[MTK_VCODEC][%d]: %s, %d " fmt "\n", \ + inst_id, __func__, __LINE__, ##args); \ + } while (0) +#else +#define mtk_v4l2_debug(plat_dev, level, fmt, args...) \ + dev_dbg(&(plat_dev)->dev, "[MTK_V4L2]: " fmt "\n", ##args) + +#define mtk_vcodec_debug(inst_id, plat_dev, fmt, args...) \ + dev_dbg(&(plat_dev)->dev, "[MTK_VCODEC][%d]: " fmt "\n", inst_id, ##args) +#endif + +void __iomem *mtk_vcodec_get_reg_addr(void __iomem **reg_base, unsigned int reg_idx); +int mtk_vcodec_write_vdecsys(struct mtk_vcodec_dec_ctx *ctx, unsigned int reg, unsigned int val); +int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem); +void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem); +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dec_dev *vdec_dev, + struct mtk_vcodec_dec_ctx *ctx, int hw_idx); +struct mtk_vcodec_dec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dec_dev *vdec_dev, + unsigned int hw_idx); +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dec_dev *dev, int hw_idx); + +#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/decoder/Makefile b/drivers/media/platform/mediatek/vcodec/decoder/Makefile new file mode 100644 index 000000000000..904cd22def84 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/decoder/Makefile @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \ + mtk-vcodec-dec-hw.o + +mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ + vdec/vdec_vp8_if.o \ + vdec/vdec_vp8_req_if.o \ + vdec/vdec_vp9_if.o \ + vdec/vdec_vp9_req_lat_if.o \ + vdec/vdec_av1_req_lat_if.o \ + vdec/vdec_h264_req_if.o \ + vdec/vdec_h264_req_common.o \ + vdec/vdec_h264_req_multi_if.o \ + vdec/vdec_hevc_req_multi_if.o \ + mtk_vcodec_dec_drv.o \ + vdec_drv_if.o \ + vdec_vpu_if.o \ + vdec_msg_queue.o \ + mtk_vcodec_dec.o \ + mtk_vcodec_dec_stateful.o \ + mtk_vcodec_dec_stateless.o \ + mtk_vcodec_dec_pm.o \ + +mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c similarity index 82% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 93fcea821001..91ed576d6821 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -9,10 +9,8 @@ #include #include -#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec_drv.h" #include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" #include "vdec_drv_if.h" #include "mtk_vcodec_dec_pm.h" @@ -35,11 +33,13 @@ mtk_vdec_find_format(struct v4l2_format *f, return NULL; } -static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_ctx *ctx, int format_index) +static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_dec_ctx *ctx, int format_index) { const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; const struct mtk_video_fmt *fmt; + struct mtk_q_data *q_data; int num_frame_count = 0, i; + bool ret = false; fmt = &dec_pdata->vdec_formats[format_index]; for (i = 0; i < *dec_pdata->num_formats; i++) { @@ -49,13 +49,29 @@ static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_ctx *ctx, int format_index) num_frame_count++; } - if (num_frame_count == 1 || fmt->fourcc == V4L2_PIX_FMT_MM21) + if (num_frame_count == 1 || (!ctx->is_10bit_bitstream && fmt->fourcc == V4L2_PIX_FMT_MM21)) return true; - return false; + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + switch (q_data->fmt->fourcc) { + case V4L2_PIX_FMT_H264_SLICE: + if (ctx->is_10bit_bitstream && fmt->fourcc == V4L2_PIX_FMT_MT2110R) + ret = true; + break; + case V4L2_PIX_FMT_VP9_FRAME: + case V4L2_PIX_FMT_AV1_FRAME: + case V4L2_PIX_FMT_HEVC_SLICE: + if (ctx->is_10bit_bitstream && fmt->fourcc == V4L2_PIX_FMT_MT2110T) + ret = true; + break; + default: + break; + } + + return ret; } -static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx, +static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_dec_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -74,7 +90,7 @@ static int vidioc_try_decoder_cmd(struct file *file, void *priv, static int vidioc_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct vb2_queue *src_vq, *dst_vq; int ret; @@ -82,7 +98,7 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, if (ret) return ret; - mtk_v4l2_debug(1, "decoder cmd=%u", cmd->cmd); + mtk_v4l2_vdec_dbg(1, ctx, "decoder cmd=%u", cmd->cmd); dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); switch (cmd->cmd) { @@ -90,11 +106,11 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); if (!vb2_is_streaming(src_vq)) { - mtk_v4l2_debug(1, "Output stream is off. No need to flush."); + mtk_v4l2_vdec_dbg(1, ctx, "Output stream is off. No need to flush."); return 0; } if (!vb2_is_streaming(dst_vq)) { - mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); + mtk_v4l2_vdec_dbg(1, ctx, "Capture stream is off. No need to flush."); return 0; } v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf.vb); @@ -112,23 +128,23 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, return 0; } -void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx) +void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx) { mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); } -void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx) +void mtk_vdec_lock(struct mtk_vcodec_dec_ctx *ctx) { mutex_lock(&ctx->dev->dec_mutex[ctx->hw_id]); } -void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx) +void mtk_vcodec_dec_release(struct mtk_vcodec_dec_ctx *ctx) { vdec_if_deinit(ctx); ctx->state = MTK_STATE_FREE; } -void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) +void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_dec_ctx *ctx) { struct mtk_q_data *q_data; @@ -169,11 +185,10 @@ void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) static int vidioc_vdec_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d] Call on QBUF after unrecoverable error", ctx->id); return -EIO; } @@ -183,11 +198,10 @@ static int vidioc_vdec_qbuf(struct file *file, void *priv, static int vidioc_vdec_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on DQBUF after unrecoverable error", - ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d] Call on DQBUF after unrecoverable error", ctx->id); return -EIO; } @@ -196,7 +210,7 @@ static int vidioc_vdec_dqbuf(struct file *file, void *priv, static int mtk_vcodec_dec_get_chip_name(void *priv) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) @@ -218,7 +232,7 @@ static int mtk_vcodec_dec_get_chip_name(void *priv) static int vidioc_vdec_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; int platform_name = mtk_vcodec_dec_get_chip_name(priv); @@ -231,7 +245,7 @@ static int vidioc_vdec_querycap(struct file *file, void *priv, static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(fh); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(fh); if (ctx->dev->vdec_pdata->uses_stateless_api) return v4l2_ctrl_subscribe_event(fh, sub); @@ -246,7 +260,7 @@ static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, } } -static int vidioc_try_fmt(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, +static int vidioc_try_fmt(struct mtk_vcodec_dec_ctx *ctx, struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; @@ -288,11 +302,10 @@ static int vidioc_try_fmt(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, (pix_fmt_mp->height + 64) <= frmsize->max_height) pix_fmt_mp->height += 64; - mtk_v4l2_debug(0, - "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d", - tmp_w, tmp_h, pix_fmt_mp->width, - pix_fmt_mp->height, - pix_fmt_mp->width * pix_fmt_mp->height); + mtk_v4l2_vdec_dbg(0, ctx, + "before resize wxh=%dx%d, after resize wxh=%dx%d, sizeimage=%d", + tmp_w, tmp_h, pix_fmt_mp->width, pix_fmt_mp->height, + pix_fmt_mp->width * pix_fmt_mp->height); pix_fmt_mp->num_planes = fmt->num_planes; pix_fmt_mp->plane_fmt[0].sizeimage = @@ -315,7 +328,7 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; fmt = mtk_vdec_find_format(f, dec_pdata); @@ -333,7 +346,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; fmt = mtk_vdec_find_format(f, dec_pdata); @@ -344,7 +357,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, } if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - mtk_v4l2_err("sizeimage of output format must be given"); + mtk_v4l2_vdec_err(ctx, "sizeimage of output format must be given"); return -EINVAL; } @@ -354,7 +367,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, static int vidioc_vdec_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct mtk_q_data *q_data; if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -403,7 +416,7 @@ static int vidioc_vdec_g_selection(struct file *file, void *priv, static int vidioc_vdec_s_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -425,14 +438,14 @@ static int vidioc_vdec_s_selection(struct file *file, void *priv, static int vidioc_vdec_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct v4l2_pix_format_mplane *pix_mp; struct mtk_q_data *q_data; int ret = 0; const struct mtk_video_fmt *fmt; const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - mtk_v4l2_debug(3, "[%d]", ctx->id); + mtk_v4l2_vdec_dbg(3, ctx, "[%d]", ctx->id); q_data = mtk_vdec_get_q_data(ctx, f->type); if (!q_data) @@ -446,7 +459,7 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, if (!dec_pdata->uses_stateless_api && f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) { - mtk_v4l2_err("out_q_ctx buffers already requested"); + mtk_v4l2_vdec_err(ctx, "out_q_ctx buffers already requested"); ret = -EBUSY; } @@ -456,7 +469,7 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, */ if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) { - mtk_v4l2_err("cap_q_ctx buffers already requested"); + mtk_v4l2_vdec_err(ctx, "cap_q_ctx buffers already requested"); ret = -EBUSY; } @@ -491,8 +504,8 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, if (ctx->state == MTK_STATE_FREE) { ret = vdec_if_init(ctx, q_data->fmt->fourcc); if (ret) { - mtk_v4l2_err("[%d]: vdec_if_init() fail ret=%d", - ctx->id, ret); + mtk_v4l2_vdec_err(ctx, "[%d]: vdec_if_init() fail ret=%d", + ctx->id, ret); return -EINVAL; } ctx->state = MTK_STATE_INIT; @@ -515,8 +528,8 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, */ ret = vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo); if (ret) { - mtk_v4l2_err("[%d]Error!! Get GET_PARAM_PICTURE_INFO Fail", - ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d]Error!! Get GET_PARAM_PICTURE_INFO Fail", + ctx->id); } ctx->last_decoded_picinfo = ctx->picinfo; @@ -540,11 +553,13 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, ctx->q_data[MTK_Q_DATA_DST].coded_width = ctx->picinfo.buf_w; ctx->q_data[MTK_Q_DATA_DST].coded_height = ctx->picinfo.buf_h; - mtk_v4l2_debug(2, "[%d] vdec_if_init() num_plane = %d wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", - ctx->id, pix_mp->num_planes, ctx->picinfo.buf_w, ctx->picinfo.buf_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->q_data[MTK_Q_DATA_DST].sizeimage[0], - ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]); + mtk_v4l2_vdec_dbg(2, ctx, + "[%d] init() plane:%d wxh=%dx%d pic wxh=%dx%d sz=0x%x_0x%x", + ctx->id, pix_mp->num_planes, + ctx->picinfo.buf_w, ctx->picinfo.buf_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0], + ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]); } return 0; } @@ -553,7 +568,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { int i = 0; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; if (fsize->index != 0) @@ -570,14 +585,11 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; fsize->stepwise = dec_pdata->vdec_formats[i].frmsize; - mtk_v4l2_debug(1, "%x, %d %d %d %d %d %d", - ctx->dev->dec_capability, - fsize->stepwise.min_width, - fsize->stepwise.max_width, - fsize->stepwise.step_width, - fsize->stepwise.min_height, - fsize->stepwise.max_height, - fsize->stepwise.step_height); + mtk_v4l2_vdec_dbg(1, ctx, "%x, %d %d %d %d %d %d", + ctx->dev->dec_capability, fsize->stepwise.min_width, + fsize->stepwise.max_width, fsize->stepwise.step_width, + fsize->stepwise.min_height, fsize->stepwise.max_height, + fsize->stepwise.step_height); return 0; } @@ -588,7 +600,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, void *priv, bool output_queue) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; const struct mtk_video_fmt *fmt; int i, j = 0; @@ -634,14 +646,14 @@ static int vidioc_vdec_enum_fmt_vid_out(struct file *file, void *priv, static int vidioc_vdec_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; struct vb2_queue *vq; struct mtk_q_data *q_data; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) { - mtk_v4l2_err("no vb2 queue for type=%d", f->type); + mtk_v4l2_vdec_err(ctx, "no vb2 queue for type=%d", f->type); return -EINVAL; } @@ -712,8 +724,8 @@ static int vidioc_vdec_g_fmt(struct file *file, void *priv, pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; - mtk_v4l2_debug(1, "[%d] type=%d state=%d Format information could not be read, not ready yet!", - ctx->id, f->type, ctx->state); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] type=%d state=%d Format information not ready!", + ctx->id, f->type, ctx->state); } return 0; @@ -723,14 +735,14 @@ int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vq); struct mtk_q_data *q_data; unsigned int i; q_data = mtk_vdec_get_q_data(ctx, vq->type); if (q_data == NULL) { - mtk_v4l2_err("vq->type=%d err\n", vq->type); + mtk_v4l2_vdec_err(ctx, "vq->type=%d err\n", vq->type); return -EINVAL; } @@ -756,30 +768,28 @@ int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, sizes[i] = q_data->sizeimage[i]; } - mtk_v4l2_debug(1, - "[%d]\t type = %d, get %d plane(s), %d buffer(s) of size 0x%x 0x%x ", - ctx->id, vq->type, *nplanes, *nbuffers, - sizes[0], sizes[1]); + mtk_v4l2_vdec_dbg(1, ctx, + "[%d]\t type = %d, get %d plane(s), %d buffer(s) of size 0x%x 0x%x ", + ctx->id, vq->type, *nplanes, *nbuffers, sizes[0], sizes[1]); return 0; } int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mtk_q_data *q_data; int i; - mtk_v4l2_debug(3, "[%d] (%d) id=%d", - ctx->id, vb->vb2_queue->type, vb->index); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] (%d) id=%d", + ctx->id, vb->vb2_queue->type, vb->index); q_data = mtk_vdec_get_q_data(ctx, vb->vb2_queue->type); for (i = 0; i < q_data->fmt->num_planes; i++) { if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { - mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", - i, vb2_plane_size(vb, i), - q_data->sizeimage[i]); + mtk_v4l2_vdec_err(ctx, "data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), q_data->sizeimage[i]); return -EINVAL; } if (!V4L2_TYPE_IS_OUTPUT(vb->type)) @@ -791,7 +801,7 @@ int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2_v4l2; struct mtk_video_dec_buf *buf; bool buf_error; @@ -807,7 +817,7 @@ void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) mutex_unlock(&ctx->lock); if (buf_error) { - mtk_v4l2_err("Unrecoverable error on buffer."); + mtk_v4l2_vdec_err(ctx, "Unrecoverable error on buffer."); ctx->state = MTK_STATE_ABORT; } } @@ -829,7 +839,7 @@ int vb2ops_vdec_buf_init(struct vb2_buffer *vb) int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(q); if (ctx->state == MTK_STATE_FLUSH) ctx->state = MTK_STATE_HEADER; @@ -840,11 +850,11 @@ int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) void vb2ops_vdec_stop_streaming(struct vb2_queue *q) { struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL; - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(q); int ret; - mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", - ctx->id, q->type, ctx->state, ctx->decoded_frame_cnt); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", + ctx->id, q->type, ctx->state, ctx->decoded_frame_cnt); if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { @@ -870,17 +880,17 @@ void vb2ops_vdec_stop_streaming(struct vb2_queue *q) */ ctx->picinfo = ctx->last_decoded_picinfo; - mtk_v4l2_debug(2, - "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", - ctx->id, ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->last_decoded_picinfo.buf_w, - ctx->last_decoded_picinfo.buf_h); + mtk_v4l2_vdec_dbg(2, ctx, + "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", + ctx->id, ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->last_decoded_picinfo.buf_w, + ctx->last_decoded_picinfo.buf_h); ret = ctx->dev->vdec_pdata->flush_decoder(ctx); if (ret) - mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); + mtk_v4l2_vdec_err(ctx, "DecodeFinal failed, ret=%d", ret); } ctx->state = MTK_STATE_FLUSH; @@ -895,17 +905,17 @@ void vb2ops_vdec_stop_streaming(struct vb2_queue *q) static void m2mops_vdec_device_run(void *priv) { - struct mtk_vcodec_ctx *ctx = priv; - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_dec_ctx *ctx = priv; + struct mtk_vcodec_dec_dev *dev = ctx->dev; queue_work(dev->decode_workqueue, &ctx->decode_work); } static int m2mops_vdec_job_ready(void *m2m_priv) { - struct mtk_vcodec_ctx *ctx = m2m_priv; + struct mtk_vcodec_dec_ctx *ctx = m2m_priv; - mtk_v4l2_debug(3, "[%d]", ctx->id); + mtk_v4l2_vdec_dbg(3, ctx, "[%d]", ctx->id); if (ctx->state == MTK_STATE_ABORT) return 0; @@ -922,7 +932,7 @@ static int m2mops_vdec_job_ready(void *m2m_priv) static void m2mops_vdec_job_abort(void *priv) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_dec_ctx *ctx = priv; ctx->state = MTK_STATE_ABORT; } @@ -970,10 +980,10 @@ const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops = { int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_dec_ctx *ctx = priv; int ret = 0; - mtk_v4l2_debug(3, "[%d]", ctx->id); + mtk_v4l2_vdec_dbg(3, ctx, "[%d]", ctx->id); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; src_vq->io_modes = VB2_DMABUF | VB2_MMAP; @@ -988,7 +998,7 @@ int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, ret = vb2_queue_init(src_vq); if (ret) { - mtk_v4l2_err("Failed to initialize videobuf2 queue(output)"); + mtk_v4l2_vdec_err(ctx, "Failed to initialize videobuf2 queue(output)"); return ret; } dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -1004,7 +1014,7 @@ int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, ret = vb2_queue_init(dst_vq); if (ret) - mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)"); + mtk_v4l2_vdec_err(ctx, "Failed to initialize videobuf2 queue(capture)"); return ret; } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.h similarity index 91% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.h index 4572f92826f2..ece27c880e50 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.h @@ -11,6 +11,8 @@ #include #include +#include "mtk_vcodec_dec_drv.h" + #define VCODEC_DEC_ALIGNED_64 64 #define VCODEC_CAPABILITY_4K_DISABLED 0x10 #define VCODEC_DEC_4K_CODED_WIDTH 4096U @@ -78,12 +80,12 @@ extern const struct mtk_vcodec_dec_pdata mtk_vdec_single_core_pdata; * mtk_vdec_lock get decoder hw lock and set curr_ctx * to ctx instance that get lock */ -void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx); -void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx); +void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx); +void mtk_vdec_lock(struct mtk_vcodec_dec_ctx *ctx); int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); -void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx); -void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_dec_ctx *ctx); +void mtk_vcodec_dec_release(struct mtk_vcodec_dec_ctx *ctx); /* * VB2 ops diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c similarity index 72% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c index d41f2121b94f..0a89ce452ac3 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c @@ -5,27 +5,28 @@ * Tiffany Lin */ +#include #include #include #include +#include #include -#include #include +#include +#include #include +#include #include #include #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" #include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_fw.h" +#include "../common/mtk_vcodec_intr.h" -static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev) +static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dec_ctx *ctx, struct mtk_vcodec_dec_dev *dev) { switch (dev->vdec_pdata->hw_arch) { case MTK_VDEC_PURE_SINGLE_CORE: @@ -33,27 +34,35 @@ static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev) case MTK_VDEC_LAT_SINGLE_CORE: return MTK_VDEC_ONE_LAT_ONE_CORE; default: - mtk_v4l2_err("hw arch %d not supported", dev->vdec_pdata->hw_arch); + mtk_v4l2_vdec_err(ctx, "hw arch %d not supported", dev->vdec_pdata->hw_arch); return MTK_VDEC_NO_HW; } } +static bool mtk_vcodec_is_hw_active(struct mtk_vcodec_dec_dev *dev) +{ + u32 cg_status; + + if (dev->vdecsys_regmap) + return !regmap_test_bits(dev->vdecsys_regmap, VDEC_HW_ACTIVE_ADDR, + VDEC_HW_ACTIVE_MASK); + + cg_status = readl(dev->reg_base[VDEC_SYS] + VDEC_HW_ACTIVE_ADDR); + return !FIELD_GET(VDEC_HW_ACTIVE_MASK, cg_status); +} + static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) { - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - u32 cg_status = 0; + struct mtk_vcodec_dec_dev *dev = priv; + struct mtk_vcodec_dec_ctx *ctx; unsigned int dec_done_status = 0; void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG; ctx = mtk_vcodec_get_curr_ctx(dev, MTK_VDEC_CORE); - /* check if HW active or not */ - cg_status = readl(dev->reg_base[0]); - if ((cg_status & VDEC_HW_ACTIVE) != 0) { - mtk_v4l2_err("DEC ISR, VDEC active is not 0x0 (0x%08x)", - cg_status); + if (!mtk_vcodec_is_hw_active(dev)) { + mtk_v4l2_vdec_err(ctx, "DEC ISR, VDEC active is not 0x0"); return IRQ_HANDLED; } @@ -69,40 +78,86 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR), dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); + wake_up_dec_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); - mtk_v4l2_debug(3, - "mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x", - ctx->id, dec_done_status); + mtk_v4l2_vdec_dbg(3, ctx, "wake up ctx %d, dec_done_status=%x", ctx->id, dec_done_status); return IRQ_HANDLED; } -static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dev *dev) +static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dec_dev *dev) { struct platform_device *pdev = dev->plat_dev; int reg_num, i; + struct resource *res; + bool has_vdecsys_reg; + int num_max_vdec_regs; + static const char * const mtk_dec_reg_names[] = { + "misc", + "ld", + "top", + "cm", + "ad", + "av", + "pp", + "hwd", + "hwq", + "hwb", + "hwg" + }; + + /* + * If we have reg-names in devicetree, this means that we're on a new + * register organization, which implies that the VDEC_SYS iospace gets + * R/W through a syscon (regmap). + * Here we try to get the "misc" iostart only to check if we have reg-names + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "misc"); + if (res) + has_vdecsys_reg = false; + else + has_vdecsys_reg = true; + + num_max_vdec_regs = has_vdecsys_reg ? NUM_MAX_VDEC_REG_BASE : + ARRAY_SIZE(mtk_dec_reg_names); /* Sizeof(u32) * 4 bytes for each register base. */ reg_num = of_property_count_elems_of_size(pdev->dev.of_node, "reg", sizeof(u32) * 4); - if (reg_num <= 0 || reg_num > NUM_MAX_VDEC_REG_BASE) { + if (reg_num <= 0 || reg_num > num_max_vdec_regs) { dev_err(&pdev->dev, "Invalid register property size: %d\n", reg_num); return -EINVAL; } - for (i = 0; i < reg_num; i++) { - dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); - if (IS_ERR(dev->reg_base[i])) - return PTR_ERR(dev->reg_base[i]); + if (has_vdecsys_reg) { + for (i = 0; i < reg_num; i++) { + dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(dev->reg_base[i])) + return PTR_ERR(dev->reg_base[i]); - mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); + dev_dbg(&pdev->dev, "reg[%d] base=%p", i, dev->reg_base[i]); + } + } else { + for (i = 0; i < reg_num; i++) { + dev->reg_base[i+1] = devm_platform_ioremap_resource_byname(pdev, mtk_dec_reg_names[i]); + if (IS_ERR(dev->reg_base[i+1])) + return PTR_ERR(dev->reg_base[i+1]); + + dev_dbg(&pdev->dev, "reg[%d] base=%p", i + 1, dev->reg_base[i + 1]); + } + + dev->vdecsys_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,vdecsys"); + if (IS_ERR(dev->vdecsys_regmap)) { + dev_err(&pdev->dev, "Missing mediatek,vdecsys property"); + return PTR_ERR(dev->vdecsys_regmap); + } } return 0; } -static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev) +static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dec_dev *dev) { struct platform_device *pdev = dev->plat_dev; int ret; @@ -139,8 +194,8 @@ static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev) static int fops_vcodec_open(struct file *file) { - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = NULL; + struct mtk_vcodec_dec_dev *dev = video_drvdata(file); + struct mtk_vcodec_dec_ctx *ctx = NULL; int ret = 0, i, hw_count; struct vb2_queue *src_vq; @@ -156,7 +211,7 @@ static int fops_vcodec_open(struct file *file) INIT_LIST_HEAD(&ctx->list); ctx->dev = dev; if (ctx->dev->vdec_pdata->is_subdev_supported) { - hw_count = mtk_vcodec_get_hw_count(dev); + hw_count = mtk_vcodec_get_hw_count(ctx, dev); if (!hw_count || !dev->subdev_prob_done) { ret = -EINVAL; goto err_ctrls_setup; @@ -176,15 +231,14 @@ static int fops_vcodec_open(struct file *file) ctx->type = MTK_INST_DECODER; ret = dev->vdec_pdata->ctrls_setup(ctx); if (ret) { - mtk_v4l2_err("Failed to setup mt vcodec controls"); + mtk_v4l2_vdec_err(ctx, "Failed to setup mt vcodec controls"); goto err_ctrls_setup; } ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, &mtk_vcodec_dec_queue_init); if (IS_ERR((__force void *)ctx->m2m_ctx)) { ret = PTR_ERR((__force void *)ctx->m2m_ctx); - mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", - ret); + mtk_v4l2_vdec_err(ctx, "Failed to v4l2_m2m_ctx_init() (%d)", ret); goto err_m2m_ctx_init; } src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, @@ -202,14 +256,14 @@ static int fops_vcodec_open(struct file *file) * Return 0 if downloading firmware successfully, * otherwise it is failed */ - mtk_v4l2_err("failed to load firmware!"); + mtk_v4l2_vdec_err(ctx, "failed to load firmware!"); goto err_load_fw; } dev->dec_capability = mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); - mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); + mtk_v4l2_vdec_dbg(0, ctx, "decoder capability %x", dev->dec_capability); } ctx->dev->vdec_pdata->init_vdec_params(ctx); @@ -218,8 +272,7 @@ static int fops_vcodec_open(struct file *file) mtk_vcodec_dbgfs_create(ctx); mutex_unlock(&dev->dev_mutex); - mtk_v4l2_debug(0, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), - ctx->id); + mtk_v4l2_vdec_dbg(0, ctx, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), ctx->id); return ret; /* Deinit when failure occurred */ @@ -238,10 +291,10 @@ err_ctrls_setup: static int fops_vcodec_release(struct file *file) { - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + struct mtk_vcodec_dec_dev *dev = video_drvdata(file); + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(file->private_data); - mtk_v4l2_debug(0, "[%d] decoder", ctx->id); + mtk_v4l2_vdec_dbg(0, ctx, "[%d] decoder", ctx->id); mutex_lock(&dev->dev_mutex); /* @@ -275,7 +328,7 @@ static const struct v4l2_file_operations mtk_vcodec_fops = { static int mtk_vcodec_probe(struct platform_device *pdev) { - struct mtk_vcodec_dev *dev; + struct mtk_vcodec_dec_dev *dev; struct video_device *vfd_dec; phandle rproc_phandle; enum mtk_vcodec_fw_type fw_type; @@ -296,7 +349,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) &rproc_phandle)) { fw_type = SCP; } else { - mtk_v4l2_err("Could not get vdec IPI device"); + dev_dbg(&pdev->dev, "Could not get vdec IPI device"); return -ENODEV; } dma_set_max_seg_size(&pdev->dev, UINT_MAX); @@ -316,7 +369,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) alloc_ordered_workqueue("core-decoder", WQ_MEM_RECLAIM | WQ_FREEZABLE); if (!dev->core_workqueue) { - mtk_v4l2_err("Failed to create core workqueue"); + dev_dbg(&pdev->dev, "Failed to create core workqueue"); ret = -EINVAL; goto err_res; } @@ -332,15 +385,13 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { - mtk_v4l2_err("v4l2_device_register err=%d", ret); + dev_err(&pdev->dev, "v4l2_device_register err=%d", ret); goto err_core_workq; } - init_waitqueue_head(&dev->queue); - vfd_dec = video_device_alloc(); if (!vfd_dec) { - mtk_v4l2_err("Failed to allocate video device"); + dev_err(&pdev->dev, "Failed to allocate video device"); ret = -ENOMEM; goto err_dec_alloc; } @@ -361,7 +412,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) dev->m2m_dev_dec = v4l2_m2m_init(&mtk_vdec_m2m_ops); if (IS_ERR((__force void *)dev->m2m_dev_dec)) { - mtk_v4l2_err("Failed to init mem2mem dec device"); + dev_err(&pdev->dev, "Failed to init mem2mem dec device"); ret = PTR_ERR((__force void *)dev->m2m_dev_dec); goto err_dec_alloc; } @@ -370,7 +421,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) alloc_ordered_workqueue(MTK_VCODEC_DEC_NAME, WQ_MEM_RECLAIM | WQ_FREEZABLE); if (!dev->decode_workqueue) { - mtk_v4l2_err("Failed to create decode workqueue"); + dev_err(&pdev->dev, "Failed to create decode workqueue"); ret = -EINVAL; goto err_event_workq; } @@ -379,7 +430,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (ret) { - mtk_v4l2_err("Main device of_platform_populate failed."); + dev_err(&pdev->dev, "Main device of_platform_populate failed."); goto err_reg_cont; } } else { @@ -392,7 +443,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); if (ret) { - mtk_v4l2_err("Failed to register video device"); + dev_err(&pdev->dev, "Failed to register video device"); goto err_reg_cont; } @@ -411,21 +462,21 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = v4l2_m2m_register_media_controller(dev->m2m_dev_dec, dev->vfd_dec, MEDIA_ENT_F_PROC_VIDEO_DECODER); if (ret) { - mtk_v4l2_err("Failed to register media controller"); + dev_err(&pdev->dev, "Failed to register media controller"); goto err_dec_mem_init; } ret = media_device_register(&dev->mdev_dec); if (ret) { - mtk_v4l2_err("Failed to register media device"); + dev_err(&pdev->dev, "Failed to register media device"); goto err_media_reg; } - mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor); + dev_dbg(&pdev->dev, "media registered as /dev/media%d", vfd_dec->minor); } mtk_vcodec_dbgfs_init(dev, false); - mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor); + dev_dbg(&pdev->dev, "decoder registered as /dev/video%d", vfd_dec->minor); return 0; @@ -484,7 +535,7 @@ MODULE_DEVICE_TABLE(of, mtk_vcodec_match); static void mtk_vcodec_dec_remove(struct platform_device *pdev) { - struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + struct mtk_vcodec_dec_dev *dev = platform_get_drvdata(pdev); destroy_workqueue(dev->decode_workqueue); @@ -500,7 +551,7 @@ static void mtk_vcodec_dec_remove(struct platform_device *pdev) if (dev->vfd_dec) video_unregister_device(dev->vfd_dec); - mtk_vcodec_dbgfs_deinit(dev); + mtk_vcodec_dbgfs_deinit(&dev->dbgfs); v4l2_device_unregister(&dev->v4l2_dev); if (!dev->vdec_pdata->is_subdev_supported) pm_runtime_disable(dev->pm.dev); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h new file mode 100644 index 000000000000..7e36b2c69b7d --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong + */ + +#ifndef _MTK_VCODEC_DEC_DRV_H_ +#define _MTK_VCODEC_DEC_DRV_H_ + +#include "../common/mtk_vcodec_cmn_drv.h" +#include "../common/mtk_vcodec_dbgfs.h" +#include "../common/mtk_vcodec_fw_priv.h" +#include "../common/mtk_vcodec_util.h" +#include "vdec_msg_queue.h" + +#define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" + +#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) +#define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) + +/* + * enum mtk_vdec_format_types - Structure used to get supported + * format types according to decoder capability + */ +enum mtk_vdec_format_types { + MTK_VDEC_FORMAT_MM21 = 0x20, + MTK_VDEC_FORMAT_MT21C = 0x40, + MTK_VDEC_FORMAT_H264_SLICE = 0x100, + MTK_VDEC_FORMAT_VP8_FRAME = 0x200, + MTK_VDEC_FORMAT_VP9_FRAME = 0x400, + MTK_VDEC_FORMAT_AV1_FRAME = 0x800, + MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000, + MTK_VCODEC_INNER_RACING = 0x20000, + MTK_VDEC_IS_SUPPORT_10BIT = 0x40000, +}; + +/* + * enum mtk_vdec_hw_count - Supported hardware count + */ +enum mtk_vdec_hw_count { + MTK_VDEC_NO_HW = 0, + MTK_VDEC_ONE_CORE, + MTK_VDEC_ONE_LAT_ONE_CORE, + MTK_VDEC_MAX_HW_COUNT, +}; + +/* + * enum mtk_vdec_hw_arch - Used to separate different hardware architecture + */ +enum mtk_vdec_hw_arch { + MTK_VDEC_PURE_SINGLE_CORE, + MTK_VDEC_LAT_SINGLE_CORE, +}; + +/** + * struct vdec_pic_info - picture size information + * @pic_w: picture width + * @pic_h: picture height + * @buf_w: picture buffer width (64 aligned up from pic_w) + * @buf_h: picture buffer heiht (64 aligned up from pic_h) + * @fb_sz: bitstream size of each plane + * E.g. suppose picture size is 176x144, + * buffer size will be aligned to 176x160. + * @cap_fourcc: fourcc number(may changed when resolution change) + * @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os. + */ +struct vdec_pic_info { + unsigned int pic_w; + unsigned int pic_h; + unsigned int buf_w; + unsigned int buf_h; + unsigned int fb_sz[VIDEO_MAX_PLANES]; + unsigned int cap_fourcc; + unsigned int reserved; +}; + +/** + * struct mtk_vcodec_dec_pdata - compatible data for each IC + * @init_vdec_params: init vdec params + * @ctrls_setup: init vcodec dec ctrls + * @worker: worker to start a decode job + * @flush_decoder: function that flushes the decoder + * @get_cap_buffer: get capture buffer from capture queue + * @cap_to_disp: put capture buffer to disp list for lat and core arch + * @vdec_vb2_ops: struct vb2_ops + * + * @vdec_formats: supported video decoder formats + * @num_formats: count of video decoder formats + * @default_out_fmt: default output buffer format + * @default_cap_fmt: default capture buffer format + * + * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core + * + * @is_subdev_supported: whether support parent-node architecture(subdev) + * @uses_stateless_api: whether the decoder uses the stateless API with requests + */ +struct mtk_vcodec_dec_pdata { + void (*init_vdec_params)(struct mtk_vcodec_dec_ctx *ctx); + int (*ctrls_setup)(struct mtk_vcodec_dec_ctx *ctx); + void (*worker)(struct work_struct *work); + int (*flush_decoder)(struct mtk_vcodec_dec_ctx *ctx); + struct vdec_fb *(*get_cap_buffer)(struct mtk_vcodec_dec_ctx *ctx); + void (*cap_to_disp)(struct mtk_vcodec_dec_ctx *ctx, int error, + struct media_request *src_buf_req); + + const struct vb2_ops *vdec_vb2_ops; + + const struct mtk_video_fmt *vdec_formats; + const int *num_formats; + const struct mtk_video_fmt *default_out_fmt; + const struct mtk_video_fmt *default_cap_fmt; + + enum mtk_vdec_hw_arch hw_arch; + + bool is_subdev_supported; + bool uses_stateless_api; +}; + +/** + * struct mtk_vcodec_dec_ctx - Context (instance) private data. + * + * @type: type of decoder instance + * @dev: pointer to the mtk_vcodec_dec_dev of the device + * @list: link to ctx_list of mtk_vcodec_dec_dev + * + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue of the context + * @id: index of the context that this structure describes + * @state: state of the context + * + * @dec_if: hooked decoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @picinfo: store picture info after header parsing + * @dpb_size: store dpb count after header parsing + * + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @decode_work: worker for the decoding + * @last_decoded_picinfo: pic information get from latest decode + * @empty_flush_buf: a fake size-0 capture buffer that indicates flush. Used + * for stateful decoder. + * @is_flushing: set to true if flushing is in progress. + * + * @current_codec: current set input codec, in V4L2 pixel format + * @capture_fourcc: capture queue type in V4L2 pixel format + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * + * @decoded_frame_cnt: number of decoded frames + * @lock: protect variables accessed by V4L2 threads and worker thread such as + * mtk_video_dec_buf. + * @hw_id: hardware index used to identify different hardware. + * + * @msg_queue: msg queue used to store lat buffer information. + * @vpu_inst: vpu instance pointer. + * + * @is_10bit_bitstream: set to true if it's 10bit bitstream + */ +struct mtk_vcodec_dec_ctx { + enum mtk_instance_type type; + struct mtk_vcodec_dec_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct mtk_q_data q_data[2]; + int id; + enum mtk_instance_state state; + + const struct vdec_common_if *dec_if; + void *drv_handle; + + struct vdec_pic_info picinfo; + int dpb_size; + + int int_cond[MTK_VDEC_HW_MAX]; + int int_type[MTK_VDEC_HW_MAX]; + wait_queue_head_t queue[MTK_VDEC_HW_MAX]; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct decode_work; + struct vdec_pic_info last_decoded_picinfo; + struct v4l2_m2m_buffer empty_flush_buf; + bool is_flushing; + + u32 current_codec; + u32 capture_fourcc; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + int decoded_frame_cnt; + struct mutex lock; + int hw_id; + + struct vdec_msg_queue msg_queue; + void *vpu_inst; + + bool is_10bit_bitstream; +}; + +/** + * struct mtk_vcodec_dec_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_dec: Video device for decoder + * @mdev_dec: Media device for decoder + * + * @m2m_dev_dec: m2m device for decoder + * @plat_dev: platform device + * @ctx_list: list of struct mtk_vcodec_ctx + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of MTK Vcodec registers. + * @vdec_pdata: decoder IC-specific data + * @vdecsys_regmap: VDEC_SYS register space passed through syscon + * + * @fw_handler: used to communicate with the firmware. + * @id_counter: used to identify current opened instance + * + * @dec_mutex: decoder hardware lock + * @dev_mutex: video_device lock + * @decode_workqueue: decode work queue + * + * @irqlock: protect data access by irq handler and work thread + * @dec_irq: decoder irq resource + * + * @pm: power management control + * @dec_capability: used to identify decode capability, ex: 4k + * + * @core_workqueue: queue used for core hardware decode + * + * @subdev_dev: subdev hardware device + * @subdev_prob_done: check whether all used hw device is prob done + * @subdev_bitmap: used to record hardware is ready or not + * + * @dec_active_cnt: used to mark whether need to record register value + * @vdec_racing_info: record register value + * @dec_racing_info_mutex: mutex lock used for inner racing mode + * @dbgfs: debug log related information + */ +struct mtk_vcodec_dec_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_dec; + struct media_device mdev_dec; + + struct v4l2_m2m_dev *m2m_dev_dec; + struct platform_device *plat_dev; + struct list_head ctx_list; + struct mtk_vcodec_dec_ctx *curr_ctx; + + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + const struct mtk_vcodec_dec_pdata *vdec_pdata; + struct regmap *vdecsys_regmap; + + struct mtk_vcodec_fw *fw_handler; + u64 id_counter; + + /* decoder hardware mutex lock */ + struct mutex dec_mutex[MTK_VDEC_HW_MAX]; + struct mutex dev_mutex; + struct workqueue_struct *decode_workqueue; + + spinlock_t irqlock; + int dec_irq; + + struct mtk_vcodec_pm pm; + unsigned int dec_capability; + + struct workqueue_struct *core_workqueue; + + void *subdev_dev[MTK_VDEC_HW_MAX]; + int (*subdev_prob_done)(struct mtk_vcodec_dec_dev *vdec_dev); + DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); + + atomic_t dec_active_cnt; + u32 vdec_racing_info[132]; + /* Protects access to vdec_racing_info data */ + struct mutex dec_racing_info_mutex; + struct mtk_vcodec_dbgfs dbgfs; +}; + +static inline struct mtk_vcodec_dec_ctx *fh_to_dec_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mtk_vcodec_dec_ctx, fh); +} + +static inline struct mtk_vcodec_dec_ctx *ctrl_to_dec_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_vcodec_dec_ctx, ctrl_hdl); +} + +/* Wake up context wait_queue */ +static inline void +wake_up_dec_ctx(struct mtk_vcodec_dec_ctx *ctx, unsigned int reason, unsigned int hw_id) +{ + ctx->int_cond[hw_id] = 1; + ctx->int_type[hw_id] = reason; + wake_up_interruptible(&ctx->queue[hw_id]); +} + +#define mtk_vdec_err(ctx, fmt, args...) \ + mtk_vcodec_err((ctx)->id, (ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_vdec_debug(ctx, fmt, args...) \ + mtk_vcodec_debug((ctx)->id, (ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_v4l2_vdec_err(ctx, fmt, args...) mtk_v4l2_err((ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_v4l2_vdec_dbg(level, ctx, fmt, args...) \ + mtk_v4l2_debug((ctx)->dev->plat_dev, level, fmt, ##args) + +#endif /* _MTK_VCODEC_DEC_DRV_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c similarity index 89% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c index e1cb2f8dca33..881d5de41e05 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c @@ -12,12 +12,10 @@ #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" #include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" +#include "../common/mtk_vcodec_intr.h" static const struct of_device_id mtk_vdec_hw_match[] = { { @@ -36,7 +34,7 @@ static const struct of_device_id mtk_vdec_hw_match[] = { }; MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); -static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev) +static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dec_dev *vdec_dev) { struct platform_device *pdev = vdec_dev->plat_dev; struct device_node *subdev_node; @@ -66,7 +64,7 @@ static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev) static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) { struct mtk_vdec_hw_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; u32 cg_status; unsigned int dec_done_status; void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] + @@ -75,10 +73,9 @@ static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx); /* check if HW active or not */ - cg_status = readl(dev->reg_base[VDEC_HW_SYS]); - if (cg_status & VDEC_HW_ACTIVE) { - mtk_v4l2_err("vdec active is not 0x0 (0x%08x)", - cg_status); + cg_status = readl(dev->reg_base[VDEC_HW_SYS] + VDEC_HW_ACTIVE_ADDR); + if (cg_status & VDEC_HW_ACTIVE_MASK) { + mtk_v4l2_vdec_err(ctx, "vdec active is not 0x0 (0x%08x)", cg_status); return IRQ_HANDLED; } @@ -91,10 +88,10 @@ static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr); writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr); - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); + wake_up_dec_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); - mtk_v4l2_debug(3, "wake up ctx %d, dec_done_status=%x", - ctx->id, dec_done_status); + mtk_v4l2_vdec_dbg(3, ctx, "wake up ctx %d, dec_done_status=%x", + ctx->id, dec_done_status); return IRQ_HANDLED; } @@ -124,7 +121,7 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_dev *main_dev; + struct mtk_vcodec_dec_dev *main_dev; const struct of_device_id *of_id; int hw_idx; int ret; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h similarity index 87% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h index 36faa8d9d681..83fe8b9428e6 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h @@ -10,9 +10,10 @@ #include #include -#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec_drv.h" -#define VDEC_HW_ACTIVE 0x10 +#define VDEC_HW_ACTIVE_ADDR 0x0 +#define VDEC_HW_ACTIVE_MASK BIT(4) #define VDEC_IRQ_CFG 0x11 #define VDEC_IRQ_CLR 0x10 #define VDEC_IRQ_CFG_REG 0xa4 @@ -45,10 +46,10 @@ enum mtk_vdec_hw_reg_idx { */ struct mtk_vdec_hw_dev { struct platform_device *plat_dev; - struct mtk_vcodec_dev *main_dev; + struct mtk_vcodec_dec_dev *main_dev; void __iomem *reg_base[VDEC_HW_MAX]; - struct mtk_vcodec_ctx *curr_ctx; + struct mtk_vcodec_dec_ctx *curr_ctx; int dec_irq; struct mtk_vcodec_pm pm; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c similarity index 81% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c index 777d445999e9..aefd3e9e3061 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c @@ -6,13 +6,11 @@ #include #include -#include -#include +#include #include #include "mtk_vcodec_dec_hw.h" #include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_util.h" int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm) { @@ -32,7 +30,7 @@ int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm * if (!dec_clk->clk_info) return -ENOMEM; } else { - mtk_v4l2_err("Failed to get vdec clock count"); + dev_err(&pdev->dev, "Failed to get vdec clock count"); return -EINVAL; } @@ -41,14 +39,13 @@ int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm * ret = of_property_read_string_index(pdev->dev.of_node, "clock-names", i, &clk_info->clk_name); if (ret) { - mtk_v4l2_err("Failed to get clock name id = %d", i); + dev_err(&pdev->dev, "Failed to get clock name id = %d", i); return ret; } clk_info->vcodec_clk = devm_clk_get(&pdev->dev, clk_info->clk_name); if (IS_ERR(clk_info->vcodec_clk)) { - mtk_v4l2_err("devm_clk_get (%d)%s fail", i, - clk_info->clk_name); + dev_err(&pdev->dev, "devm_clk_get (%d)%s fail", i, clk_info->clk_name); return PTR_ERR(clk_info->vcodec_clk); } } @@ -63,7 +60,7 @@ static int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm) ret = pm_runtime_resume_and_get(pm->dev); if (ret) - mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); + dev_err(pm->dev, "pm_runtime_resume_and_get fail %d", ret); return ret; } @@ -74,7 +71,7 @@ static void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm) ret = pm_runtime_put(pm->dev); if (ret && ret != -EAGAIN) - mtk_v4l2_err("pm_runtime_put fail %d", ret); + dev_err(pm->dev, "pm_runtime_put fail %d", ret); } static void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) @@ -86,7 +83,7 @@ static void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) for (i = 0; i < dec_clk->clk_num; i++) { ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); if (ret) { - mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, + dev_err(pm->dev, "clk_prepare_enable %d %s fail %d", i, dec_clk->clk_info[i].clk_name, ret); goto error; } @@ -108,7 +105,7 @@ static void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm) clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); } -static void mtk_vcodec_dec_enable_irq(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +static void mtk_vcodec_dec_enable_irq(struct mtk_vcodec_dec_dev *vdec_dev, int hw_idx) { struct mtk_vdec_hw_dev *subdev_dev; @@ -120,13 +117,13 @@ static void mtk_vcodec_dec_enable_irq(struct mtk_vcodec_dev *vdec_dev, int hw_id if (subdev_dev) enable_irq(subdev_dev->dec_irq); else - mtk_v4l2_err("Failed to get hw dev\n"); + dev_err(&vdec_dev->plat_dev->dev, "Failed to get hw dev\n"); } else { enable_irq(vdec_dev->dec_irq); } } -static void mtk_vcodec_dec_disable_irq(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +static void mtk_vcodec_dec_disable_irq(struct mtk_vcodec_dec_dev *vdec_dev, int hw_idx) { struct mtk_vdec_hw_dev *subdev_dev; @@ -138,13 +135,13 @@ static void mtk_vcodec_dec_disable_irq(struct mtk_vcodec_dev *vdec_dev, int hw_i if (subdev_dev) disable_irq(subdev_dev->dec_irq); else - mtk_v4l2_err("Failed to get hw dev\n"); + dev_err(&vdec_dev->plat_dev->dev, "Failed to get hw dev\n"); } else { disable_irq(vdec_dev->dec_irq); } } -static void mtk_vcodec_load_racing_info(struct mtk_vcodec_ctx *ctx) +static void mtk_vcodec_load_racing_info(struct mtk_vcodec_dec_ctx *ctx) { void __iomem *vdec_racing_addr; int j; @@ -158,7 +155,7 @@ static void mtk_vcodec_load_racing_info(struct mtk_vcodec_ctx *ctx) mutex_unlock(&ctx->dev->dec_racing_info_mutex); } -static void mtk_vcodec_record_racing_info(struct mtk_vcodec_ctx *ctx) +static void mtk_vcodec_record_racing_info(struct mtk_vcodec_dec_ctx *ctx) { void __iomem *vdec_racing_addr; int j; @@ -172,7 +169,7 @@ static void mtk_vcodec_record_racing_info(struct mtk_vcodec_ctx *ctx) mutex_unlock(&ctx->dev->dec_racing_info_mutex); } -static struct mtk_vcodec_pm *mtk_vcodec_dec_get_pm(struct mtk_vcodec_dev *vdec_dev, +static struct mtk_vcodec_pm *mtk_vcodec_dec_get_pm(struct mtk_vcodec_dec_dev *vdec_dev, int hw_idx) { struct mtk_vdec_hw_dev *subdev_dev; @@ -185,14 +182,14 @@ static struct mtk_vcodec_pm *mtk_vcodec_dec_get_pm(struct mtk_vcodec_dev *vdec_d if (subdev_dev) return &subdev_dev->pm; - mtk_v4l2_err("Failed to get hw dev\n"); + dev_err(&vdec_dev->plat_dev->dev, "Failed to get hw dev\n"); return NULL; } return &vdec_dev->pm; } -static void mtk_vcodec_dec_child_dev_on(struct mtk_vcodec_dev *vdec_dev, +static void mtk_vcodec_dec_child_dev_on(struct mtk_vcodec_dec_dev *vdec_dev, int hw_idx) { struct mtk_vcodec_pm *pm; @@ -212,7 +209,7 @@ static void mtk_vcodec_dec_child_dev_on(struct mtk_vcodec_dev *vdec_dev, } } -static void mtk_vcodec_dec_child_dev_off(struct mtk_vcodec_dev *vdec_dev, +static void mtk_vcodec_dec_child_dev_off(struct mtk_vcodec_dec_dev *vdec_dev, int hw_idx) { struct mtk_vcodec_pm *pm; @@ -232,7 +229,7 @@ static void mtk_vcodec_dec_child_dev_off(struct mtk_vcodec_dev *vdec_dev, } } -void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) +void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_dec_ctx *ctx, int hw_idx) { mutex_lock(&ctx->dev->dec_mutex[hw_idx]); @@ -248,7 +245,7 @@ void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) } EXPORT_SYMBOL_GPL(mtk_vcodec_dec_enable_hardware); -void mtk_vcodec_dec_disable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) +void mtk_vcodec_dec_disable_hardware(struct mtk_vcodec_dec_ctx *ctx, int hw_idx) { if (IS_VDEC_INNER_RACING(ctx->dev->dec_capability)) mtk_vcodec_record_racing_info(ctx); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.h similarity index 61% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.h index dbcf3cabe6f3..87a50d589d42 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.h @@ -7,11 +7,11 @@ #ifndef _MTK_VCODEC_DEC_PM_H_ #define _MTK_VCODEC_DEC_PM_H_ -#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec_drv.h" int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm); -void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx); -void mtk_vcodec_dec_disable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx); +void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_dec_ctx *ctx, int hw_idx); +void mtk_vcodec_dec_disable_hardware(struct mtk_vcodec_dec_ctx *ctx, int hw_idx); #endif /* _MTK_VCODEC_DEC_PM_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c similarity index 73% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c index 0fbd030026c7..11ca2c2fbaad 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c @@ -4,10 +4,7 @@ #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" #include "mtk_vcodec_dec_pm.h" #include "vdec_drv_if.h" @@ -55,21 +52,22 @@ static const unsigned int num_supported_formats = * Note the buffers returned from codec driver may still be in driver's * reference list. */ -static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) +static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_fb *disp_frame_buffer = NULL; struct mtk_video_dec_buf *dstbuf; struct vb2_v4l2_buffer *vb; - mtk_v4l2_debug(3, "[%d]", ctx->id); + mtk_v4l2_vdec_dbg(3, ctx, "[%d]", ctx->id); if (vdec_if_get_param(ctx, GET_PARAM_DISP_FRAME_BUFFER, &disp_frame_buffer)) { - mtk_v4l2_err("[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER", ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER", + ctx->id); return NULL; } if (!disp_frame_buffer) { - mtk_v4l2_debug(3, "No display frame buffer"); + mtk_v4l2_vdec_dbg(3, ctx, "No display frame buffer"); return NULL; } @@ -78,9 +76,9 @@ static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) vb = &dstbuf->m2m_buf.vb; mutex_lock(&ctx->lock); if (dstbuf->used) { - mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to done_list %d", - ctx->id, disp_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2); + mtk_v4l2_vdec_dbg(2, ctx, "[%d]status=%x queue id=%d to done_list %d", + ctx->id, disp_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2); v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); ctx->decoded_frame_cnt++; @@ -97,7 +95,7 @@ static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) * previous sps/pps/resolution change decode, or do nothing if user * space still owns this buffer */ -static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) +static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_dec_ctx *ctx) { struct mtk_video_dec_buf *dstbuf; struct vdec_fb *free_frame_buffer = NULL; @@ -105,16 +103,16 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) if (vdec_if_get_param(ctx, GET_PARAM_FREE_FRAME_BUFFER, &free_frame_buffer)) { - mtk_v4l2_err("[%d] Error!! Cannot get param", ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d] Error!! Cannot get param", ctx->id); return NULL; } if (!free_frame_buffer) { - mtk_v4l2_debug(3, " No free frame buffer"); + mtk_v4l2_vdec_dbg(3, ctx, " No free frame buffer"); return NULL; } - mtk_v4l2_debug(3, "[%d] tmp_frame_addr = 0x%p", ctx->id, - free_frame_buffer); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] tmp_frame_addr = 0x%p", ctx->id, + free_frame_buffer); dstbuf = container_of(free_frame_buffer, struct mtk_video_dec_buf, frame_buffer); @@ -131,9 +129,9 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) * This reduce overheads that dq/q unused capture * buffer. In this case, queued_in_vb2 = true. */ - mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to rdy_queue %d", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2); + mtk_v4l2_vdec_dbg(2, ctx, "[%d]status=%x queue id=%d to rdy_queue %d", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2); v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); } else if (!dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2) { /* @@ -146,10 +144,10 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) * means this buffer is not from previous decode * output. */ - mtk_v4l2_debug(2, - "[%d]status=%x queue id=%d to rdy_queue", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index); + mtk_v4l2_vdec_dbg(2, ctx, + "[%d]status=%x queue id=%d to rdy_queue", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index); v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); dstbuf->queued_in_vb2 = true; } else { @@ -161,10 +159,10 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) * When this buffer q from user space, it could * directly q to vb2 buffer */ - mtk_v4l2_debug(3, "[%d]status=%x err queue id=%d %d %d", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2, - dstbuf->queued_in_v4l2); + mtk_v4l2_vdec_dbg(3, ctx, "[%d]status=%x err queue id=%d %d %d", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2, + dstbuf->queued_in_v4l2); } dstbuf->used = false; } @@ -172,37 +170,37 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) return &vb->vb2_buf; } -static void clean_display_buffer(struct mtk_vcodec_ctx *ctx) +static void clean_display_buffer(struct mtk_vcodec_dec_ctx *ctx) { while (get_display_buffer(ctx)) ; } -static void clean_free_buffer(struct mtk_vcodec_ctx *ctx) +static void clean_free_buffer(struct mtk_vcodec_dec_ctx *ctx) { while (get_free_buffer(ctx)) ; } -static void mtk_vdec_queue_res_chg_event(struct mtk_vcodec_ctx *ctx) +static void mtk_vdec_queue_res_chg_event(struct mtk_vcodec_dec_ctx *ctx) { static const struct v4l2_event ev_src_ch = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - mtk_v4l2_debug(1, "[%d]", ctx->id); + mtk_v4l2_vdec_dbg(1, ctx, "[%d]", ctx->id); v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); } -static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) +static int mtk_vdec_flush_decoder(struct mtk_vcodec_dec_ctx *ctx) { bool res_chg; int ret; ret = vdec_if_decode(ctx, NULL, NULL, &res_chg); if (ret) - mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); + mtk_v4l2_vdec_err(ctx, "DecodeFinal failed, ret=%d", ret); clean_display_buffer(ctx); clean_free_buffer(ctx); @@ -210,7 +208,7 @@ static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) return 0; } -static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, +static void mtk_vdec_update_fmt(struct mtk_vcodec_dec_ctx *ctx, unsigned int pixelformat) { const struct mtk_video_fmt *fmt; @@ -221,24 +219,25 @@ static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, for (k = 0; k < num_supported_formats; k++) { fmt = &mtk_video_formats[k]; if (fmt->fourcc == pixelformat) { - mtk_v4l2_debug(1, "Update cap fourcc(%d -> %d)", - dst_q_data->fmt->fourcc, pixelformat); + mtk_v4l2_vdec_dbg(1, ctx, "Update cap fourcc(%d -> %d)", + dst_q_data->fmt->fourcc, pixelformat); dst_q_data->fmt = fmt; return; } } - mtk_v4l2_err("Cannot get fourcc(%d), using init value", pixelformat); + mtk_v4l2_vdec_err(ctx, "Cannot get fourcc(%d), using init value", pixelformat); } -static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) +static int mtk_vdec_pic_info_update(struct mtk_vcodec_dec_ctx *ctx) { unsigned int dpbsize = 0; int ret; if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->last_decoded_picinfo)) { - mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", + ctx->id); return -EINVAL; } @@ -246,7 +245,7 @@ static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) ctx->last_decoded_picinfo.pic_h == 0 || ctx->last_decoded_picinfo.buf_w == 0 || ctx->last_decoded_picinfo.buf_h == 0) { - mtk_v4l2_err("Cannot get correct pic info"); + mtk_v4l2_vdec_err(ctx, "Cannot get correct pic info"); return -EINVAL; } @@ -258,15 +257,15 @@ static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h) return 0; - mtk_v4l2_debug(1, "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", ctx->id, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, ctx->picinfo.pic_w, - ctx->picinfo.pic_h, ctx->last_decoded_picinfo.buf_w, - ctx->last_decoded_picinfo.buf_h); + mtk_v4l2_vdec_dbg(1, ctx, "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", ctx->id, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, ctx->picinfo.pic_w, + ctx->picinfo.pic_h, ctx->last_decoded_picinfo.buf_w, + ctx->last_decoded_picinfo.buf_h); ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); if (dpbsize == 0) - mtk_v4l2_err("Incorrect dpb size, ret=%d", ret); + mtk_v4l2_vdec_err(ctx, "Incorrect dpb size, ret=%d", ret); ctx->dpb_size = dpbsize; @@ -275,9 +274,9 @@ static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) static void mtk_vdec_worker(struct work_struct *work) { - struct mtk_vcodec_ctx *ctx = - container_of(work, struct mtk_vcodec_ctx, decode_work); - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_dec_ctx *ctx = + container_of(work, struct mtk_vcodec_dec_ctx, decode_work); + struct mtk_vcodec_dec_dev *dev = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem buf; struct vdec_fb *pfb; @@ -288,14 +287,14 @@ static void mtk_vdec_worker(struct work_struct *work) src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (!src_buf) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] src_buf empty!!", ctx->id); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] src_buf empty!!", ctx->id); return; } dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); if (!dst_buf) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] dst_buf empty!!", ctx->id); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] dst_buf empty!!", ctx->id); return; } @@ -313,15 +312,15 @@ static void mtk_vdec_worker(struct work_struct *work) vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); pfb->base_c.size = ctx->picinfo.fb_sz[1]; pfb->status = 0; - mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id); + mtk_v4l2_vdec_dbg(3, ctx, "===>[%d] vdec_if_decode() ===>", ctx->id); - mtk_v4l2_debug(3, - "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", - dst_buf->vb2_buf.index, pfb, pfb->base_y.va, - &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); + mtk_v4l2_vdec_dbg(3, ctx, + "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", + dst_buf->vb2_buf.index, pfb, pfb->base_y.va, + &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); if (src_buf == &ctx->empty_flush_buf.vb) { - mtk_v4l2_debug(1, "Got empty flush input buffer."); + mtk_v4l2_vdec_dbg(1, ctx, "Got empty flush input buffer."); src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); /* update dst buf status */ @@ -350,12 +349,12 @@ static void mtk_vdec_worker(struct work_struct *work) buf.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; if (!buf.va) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_err("[%d] id=%d src_addr is NULL!!", ctx->id, - src_buf->vb2_buf.index); + mtk_v4l2_vdec_err(ctx, "[%d] id=%d src_addr is NULL!!", ctx->id, + src_buf->vb2_buf.index); return; } - mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", - ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", + ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf); dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; dst_buf->timecode = src_buf->timecode; mutex_lock(&ctx->lock); @@ -366,9 +365,10 @@ static void mtk_vdec_worker(struct work_struct *work) ret = vdec_if_decode(ctx, &buf, pfb, &res_chg); if (ret) { - mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>", - ctx->id, src_buf->vb2_buf.index, buf.size, - src_buf->vb2_buf.timestamp, dst_buf->vb2_buf.index, ret, res_chg); + mtk_v4l2_vdec_err(ctx, + "[%d] decode src[%d] sz=0x%zx pts=%llu dst[%d] ret=%d res_chg=%d", + ctx->id, src_buf->vb2_buf.index, buf.size, + src_buf->vb2_buf.timestamp, dst_buf->vb2_buf.index, ret, res_chg); src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); if (ret == -EIO) { mutex_lock(&ctx->lock); @@ -417,12 +417,12 @@ static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) bool res_chg = false; int ret; unsigned int dpbsize = 1, i; - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2_v4l2; struct mtk_q_data *dst_q_data; - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, - vb->vb2_queue->type, vb->index, vb); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] (%d) id=%d, vb=%p", ctx->id, + vb->vb2_queue->type, vb->index, vb); /* * check if this buffer is ready to be used after decode */ @@ -448,20 +448,19 @@ static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); if (ctx->state != MTK_STATE_INIT) { - mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, - ctx->state); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] already init driver %d", ctx->id, ctx->state); return; } src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (!src_buf) { - mtk_v4l2_err("No src buffer"); + mtk_v4l2_vdec_err(ctx, "No src buffer"); return; } if (src_buf == &ctx->empty_flush_buf.vb) { /* This shouldn't happen. Just in case. */ - mtk_v4l2_err("Invalid flush buffer."); + mtk_v4l2_vdec_err(ctx, "Invalid flush buffer."); v4l2_m2m_src_buf_remove(ctx->m2m_ctx); return; } @@ -469,9 +468,8 @@ static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); src_mem.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; - mtk_v4l2_debug(2, "[%d] buf id=%d va=%p dma=%pad size=%zx", ctx->id, - src_buf->vb2_buf.index, src_mem.va, &src_mem.dma_addr, - src_mem.size); + mtk_v4l2_vdec_dbg(2, ctx, "[%d] buf id=%d va=%p dma=%pad size=%zx", ctx->id, + src_buf->vb2_buf.index, src_mem.va, &src_mem.dma_addr, src_mem.size); ret = vdec_if_decode(ctx, &src_mem, NULL, &res_chg); if (ret || !res_chg) { @@ -484,20 +482,22 @@ static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); if (ret == -EIO) { - mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.", ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d] Unrecoverable error in vdec_if_decode.", + ctx->id); ctx->state = MTK_STATE_ABORT; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); } else { v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); } - mtk_v4l2_debug(ret ? 0 : 1, - "[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", - ctx->id, src_buf->vb2_buf.index, src_mem.size, ret, res_chg); + mtk_v4l2_vdec_dbg(ret ? 0 : 1, ctx, + "[%d] decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", + ctx->id, src_buf->vb2_buf.index, src_mem.size, ret, res_chg); return; } if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo)) { - mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); + mtk_v4l2_vdec_err(ctx, "[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", + ctx->id); return; } @@ -508,24 +508,24 @@ static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) dst_q_data->bytesperline[i] = ctx->picinfo.buf_w; } - mtk_v4l2_debug(2, "[%d] vdec_if_init() OK wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", - ctx->id, ctx->picinfo.buf_w, ctx->picinfo.buf_h, ctx->picinfo.pic_w, - ctx->picinfo.pic_h, dst_q_data->sizeimage[0], dst_q_data->sizeimage[1]); + mtk_v4l2_vdec_dbg(2, ctx, "[%d] init OK wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", + ctx->id, ctx->picinfo.buf_w, ctx->picinfo.buf_h, ctx->picinfo.pic_w, + ctx->picinfo.pic_h, dst_q_data->sizeimage[0], dst_q_data->sizeimage[1]); ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); if (dpbsize == 0) - mtk_v4l2_err("[%d] GET_PARAM_DPB_SIZE fail=%d", ctx->id, ret); + mtk_v4l2_vdec_err(ctx, "[%d] GET_PARAM_DPB_SIZE fail=%d", ctx->id, ret); ctx->dpb_size = dpbsize; ctx->state = MTK_STATE_HEADER; - mtk_v4l2_debug(1, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size); mtk_vdec_queue_res_chg_event(ctx); } static int mtk_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl) { - struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + struct mtk_vcodec_dec_ctx *ctx = ctrl_to_dec_ctx(ctrl); int ret = 0; switch (ctrl->id) { @@ -533,7 +533,7 @@ static int mtk_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl) if (ctx->state >= MTK_STATE_HEADER) { ctrl->val = ctx->dpb_size; } else { - mtk_v4l2_debug(0, "Seqinfo not ready"); + mtk_v4l2_vdec_dbg(0, ctx, "Seqinfo not ready"); ctrl->val = 0; } break; @@ -547,7 +547,7 @@ static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { .g_volatile_ctrl = mtk_vdec_g_v_ctrl, }; -static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) +static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) { struct v4l2_ctrl *ctrl; @@ -570,7 +570,7 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("Adding control failed %d", ctx->ctrl_hdl.error); + mtk_v4l2_vdec_err(ctx, "Adding control failed %d", ctx->ctrl_hdl.error); return ctx->ctrl_hdl.error; } @@ -578,7 +578,7 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) return 0; } -static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) +static void mtk_init_vdec_params(struct mtk_vcodec_dec_ctx *ctx) { unsigned int i; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c similarity index 67% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c rename to drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index db1e14a1bd6c..e29c9c58f3da 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -6,10 +6,7 @@ #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" #include "mtk_vcodec_dec_pm.h" #include "vdec_drv_if.h" @@ -203,7 +200,7 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { #define NUM_CTRLS ARRAY_SIZE(mtk_stateless_controls) -static struct mtk_video_fmt mtk_video_formats[7]; +static struct mtk_video_fmt mtk_video_formats[9]; static struct mtk_video_fmt default_out_format; static struct mtk_video_fmt default_cap_format; @@ -218,7 +215,7 @@ static const struct v4l2_frmsize_stepwise stepwise_fhd = { .step_height = 16 }; -static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_ctx *ctx, int error, +static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_dec_ctx *ctx, int error, struct media_request *src_buf_req) { struct vb2_v4l2_buffer *vb2_dst; @@ -232,17 +229,17 @@ static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_ctx *ctx, int error vb2_dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (vb2_dst) { v4l2_m2m_buf_done(vb2_dst, state); - mtk_v4l2_debug(2, "free frame buffer id:%d to done list", - vb2_dst->vb2_buf.index); + mtk_v4l2_vdec_dbg(2, ctx, "free frame buffer id:%d to done list", + vb2_dst->vb2_buf.index); } else { - mtk_v4l2_err("dst buffer is NULL"); + mtk_v4l2_vdec_err(ctx, "dst buffer is NULL"); } if (src_buf_req) v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); } -static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_ctx *ctx) +static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_dec_ctx *ctx) { struct mtk_video_dec_buf *framebuf; struct vb2_v4l2_buffer *vb2_v4l2; @@ -251,7 +248,7 @@ static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_ctx *ctx) vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); if (!vb2_v4l2) { - mtk_v4l2_debug(1, "[%d] dst_buf empty!!", ctx->id); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] dst_buf empty!!", ctx->id); return NULL; } @@ -269,25 +266,26 @@ static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_ctx *ctx) vb2_dma_contig_plane_dma_addr(dst_buf, 1); pfb->base_c.size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]; } - mtk_v4l2_debug(1, "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx frame_count = %d", - dst_buf->index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, - &pfb->base_c.dma_addr, pfb->base_y.size, ctx->decoded_frame_cnt); + mtk_v4l2_vdec_dbg(1, ctx, + "id=%d Framebuf pfb=%p VA=%p Y/C_DMA=%pad_%pad Sz=%zx frame_count = %d", + dst_buf->index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, + &pfb->base_c.dma_addr, pfb->base_y.size, ctx->decoded_frame_cnt); return pfb; } static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); } static void mtk_vdec_worker(struct work_struct *work) { - struct mtk_vcodec_ctx *ctx = - container_of(work, struct mtk_vcodec_ctx, decode_work); - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_dec_ctx *ctx = + container_of(work, struct mtk_vcodec_dec_ctx, decode_work); + struct mtk_vcodec_dec_dev *dev = ctx->dev; struct vb2_v4l2_buffer *vb2_v4l2_src; struct vb2_buffer *vb2_src; struct mtk_vcodec_mem *bs_src; @@ -300,7 +298,7 @@ static void mtk_vdec_worker(struct work_struct *work) vb2_v4l2_src = v4l2_m2m_next_src_buf(ctx->m2m_ctx); if (!vb2_v4l2_src) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] no available source buffer", ctx->id); + mtk_v4l2_vdec_dbg(1, ctx, "[%d] no available source buffer", ctx->id); return; } @@ -309,33 +307,34 @@ static void mtk_vdec_worker(struct work_struct *work) m2m_buf.vb); bs_src = &dec_buf_src->bs_buffer; - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, - vb2_src->vb2_queue->type, vb2_src->index, vb2_src); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] (%d) id=%d, vb=%p", ctx->id, + vb2_src->vb2_queue->type, vb2_src->index, vb2_src); bs_src->va = vb2_plane_vaddr(vb2_src, 0); bs_src->dma_addr = vb2_dma_contig_plane_dma_addr(vb2_src, 0); bs_src->size = (size_t)vb2_src->planes[0].bytesused; if (!bs_src->va) { v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_err("[%d] id=%d source buffer is NULL", ctx->id, - vb2_src->index); + mtk_v4l2_vdec_err(ctx, "[%d] id=%d source buffer is NULL", ctx->id, + vb2_src->index); return; } - mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", - ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", + ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src); /* Apply request controls. */ src_buf_req = vb2_src->req_obj.req; if (src_buf_req) v4l2_ctrl_request_setup(src_buf_req, &ctx->ctrl_hdl); else - mtk_v4l2_err("vb2 buffer media request is NULL"); + mtk_v4l2_vdec_err(ctx, "vb2 buffer media request is NULL"); ret = vdec_if_decode(ctx, bs_src, NULL, &res_chg); if (ret && ret != -EAGAIN) { - mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu vdec_if_decode() ret=%d res_chg=%d===>", - ctx->id, vb2_src->index, bs_src->size, - vb2_src->timestamp, ret, res_chg); + mtk_v4l2_vdec_err(ctx, + "[%d] decode src_buf[%d] sz=0x%zx pts=%llu ret=%d res_chg=%d", + ctx->id, vb2_src->index, bs_src->size, + vb2_src->timestamp, ret, res_chg); if (ret == -EIO) { mutex_lock(&ctx->lock); dec_buf_src->error = true; @@ -360,10 +359,11 @@ static void mtk_vdec_worker(struct work_struct *work) static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2_v4l2 = to_vb2_v4l2_buffer(vb); - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, vb->vb2_queue->type, vb->index, vb); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] (%d) id=%d, vb=%p", ctx->id, vb->vb2_queue->type, + vb->index, vb); mutex_lock(&ctx->lock); v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); @@ -374,35 +374,168 @@ static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb) /* If an OUTPUT buffer, we may need to update the state */ if (ctx->state == MTK_STATE_INIT) { ctx->state = MTK_STATE_HEADER; - mtk_v4l2_debug(1, "Init driver from init to header."); + mtk_v4l2_vdec_dbg(1, ctx, "Init driver from init to header."); } else { - mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, ctx->state); + mtk_v4l2_vdec_dbg(3, ctx, "[%d] already init driver %d", ctx->id, ctx->state); } } -static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) +static int mtk_vdec_flush_decoder(struct mtk_vcodec_dec_ctx *ctx) { bool res_chg; return vdec_if_decode(ctx, NULL, NULL, &res_chg); } -static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) +static int mtk_vcodec_get_pic_info(struct mtk_vcodec_dec_ctx *ctx) +{ + struct mtk_q_data *q_data; + int ret = 0; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + if (q_data->fmt->num_planes == 1) { + mtk_v4l2_vdec_err(ctx, "[%d]Error!! 10bit mode not support one plane", ctx->id); + return -EINVAL; + } + + ctx->capture_fourcc = q_data->fmt->fourcc; + ret = vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo); + if (ret) { + mtk_v4l2_vdec_err(ctx, "[%d]Error!! Get GET_PARAM_PICTURE_INFO Fail", ctx->id); + return ret; + } + + ctx->last_decoded_picinfo = ctx->picinfo; + + q_data->sizeimage[0] = ctx->picinfo.fb_sz[0]; + q_data->bytesperline[0] = ctx->picinfo.buf_w * 5 / 4; + + q_data->sizeimage[1] = ctx->picinfo.fb_sz[1]; + q_data->bytesperline[1] = ctx->picinfo.buf_w * 5 / 4; + + q_data->coded_width = ctx->picinfo.buf_w; + q_data->coded_height = ctx->picinfo.buf_h; + mtk_v4l2_vdec_dbg(1, ctx, "[%d] wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", + ctx->id, ctx->picinfo.buf_w, ctx->picinfo.buf_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + q_data->sizeimage[0], q_data->sizeimage[1]); + + return ret; +} + +static int mtk_vdec_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_vcodec_dec_ctx *ctx = ctrl_to_dec_ctx(ctrl); + struct v4l2_ctrl_h264_sps *h264; + struct v4l2_ctrl_hevc_sps *h265; + struct v4l2_ctrl_vp9_frame *frame; + struct v4l2_ctrl_av1_sequence *seq; + struct v4l2_ctrl *hdr_ctrl; + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + const struct mtk_video_fmt *fmt; + int i = 0, ret = 0; + + hdr_ctrl = ctrl; + if (!hdr_ctrl || !hdr_ctrl->p_new.p) + return -EINVAL; + + switch (hdr_ctrl->id) { + case V4L2_CID_STATELESS_H264_SPS: + h264 = (struct v4l2_ctrl_h264_sps *)hdr_ctrl->p_new.p; + + if (h264->bit_depth_chroma_minus8 == 2 && h264->bit_depth_luma_minus8 == 2) { + ctx->is_10bit_bitstream = true; + } else if (h264->bit_depth_chroma_minus8 != 0 && + h264->bit_depth_luma_minus8 != 0) { + mtk_v4l2_vdec_err(ctx, "H264: chroma_minus8:%d, luma_minus8:%d", + h264->bit_depth_chroma_minus8, + h264->bit_depth_luma_minus8); + return -EINVAL; + } + break; + case V4L2_CID_STATELESS_HEVC_SPS: + h265 = (struct v4l2_ctrl_hevc_sps *)hdr_ctrl->p_new.p; + + if (h265->bit_depth_chroma_minus8 == 2 && h265->bit_depth_luma_minus8 == 2) { + ctx->is_10bit_bitstream = true; + } else if (h265->bit_depth_chroma_minus8 != 0 && + h265->bit_depth_luma_minus8 != 0) { + mtk_v4l2_vdec_err(ctx, "HEVC: chroma_minus8:%d, luma_minus8:%d", + h265->bit_depth_chroma_minus8, + h265->bit_depth_luma_minus8); + return -EINVAL; + } + break; + case V4L2_CID_STATELESS_VP9_FRAME: + frame = (struct v4l2_ctrl_vp9_frame *)hdr_ctrl->p_new.p; + + if (frame->bit_depth == 10) { + ctx->is_10bit_bitstream = true; + } else if (frame->bit_depth != 8) { + mtk_v4l2_vdec_err(ctx, "VP9: bit_depth:%d", frame->bit_depth); + return -EINVAL; + } + break; + case V4L2_CID_STATELESS_AV1_SEQUENCE: + seq = (struct v4l2_ctrl_av1_sequence *)hdr_ctrl->p_new.p; + + if (seq->bit_depth == 10) { + ctx->is_10bit_bitstream = true; + } else if (seq->bit_depth != 8) { + mtk_v4l2_vdec_err(ctx, "AV1: bit_depth:%d", seq->bit_depth); + return -EINVAL; + } + break; + default: + mtk_v4l2_vdec_dbg(3, ctx, "Not supported to set ctrl id: 0x%x\n", hdr_ctrl->id); + return ret; + } + + if (!ctx->is_10bit_bitstream) + return ret; + + for (i = 0; i < *dec_pdata->num_formats; i++) { + fmt = &dec_pdata->vdec_formats[i]; + if (fmt->fourcc == V4L2_PIX_FMT_MT2110R && + hdr_ctrl->id == V4L2_CID_STATELESS_H264_SPS) { + ctx->q_data[MTK_Q_DATA_DST].fmt = fmt; + break; + } + + if (fmt->fourcc == V4L2_PIX_FMT_MT2110T && + (hdr_ctrl->id == V4L2_CID_STATELESS_HEVC_SPS || + hdr_ctrl->id == V4L2_CID_STATELESS_VP9_FRAME || + hdr_ctrl->id == V4L2_CID_STATELESS_AV1_SEQUENCE)) { + ctx->q_data[MTK_Q_DATA_DST].fmt = fmt; + break; + } + } + ret = mtk_vcodec_get_pic_info(ctx); + + return ret; +} + +static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { + .s_ctrl = mtk_vdec_s_ctrl, +}; + +static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) { unsigned int i; v4l2_ctrl_handler_init(&ctx->ctrl_hdl, NUM_CTRLS); if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("v4l2_ctrl_handler_init failed\n"); + mtk_v4l2_vdec_err(ctx, "v4l2_ctrl_handler_init failed\n"); return ctx->ctrl_hdl.error; } for (i = 0; i < NUM_CTRLS; i++) { struct v4l2_ctrl_config cfg = mtk_stateless_controls[i].cfg; - + cfg.ops = &mtk_vcodec_dec_ctrl_ops; v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &cfg, NULL); if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("Adding control %d failed %d", i, ctx->ctrl_hdl.error); + mtk_v4l2_vdec_err(ctx, "Adding control %d failed %d", i, + ctx->ctrl_hdl.error); return ctx->ctrl_hdl.error; } } @@ -421,11 +554,11 @@ static int fops_media_request_validate(struct media_request *mreq) /* We expect exactly one buffer with the request */ break; case 0: - mtk_v4l2_debug(1, "No buffer provided with the request"); + pr_debug(MTK_DBG_VCODEC_STR "No buffer provided with the request."); return -ENOENT; default: - mtk_v4l2_debug(1, "Too many buffers (%d) provided with the request", - buffer_cnt); + pr_debug(MTK_DBG_VCODEC_STR "Too many buffers (%d) provided with the request.", + buffer_cnt); return -EINVAL; } @@ -438,9 +571,9 @@ const struct media_device_ops mtk_vcodec_media_ops = { }; static void mtk_vcodec_add_formats(unsigned int fourcc, - struct mtk_vcodec_ctx *ctx) + struct mtk_vcodec_dec_ctx *ctx) { - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_dec_dev *dev = ctx->dev; const struct mtk_vcodec_dec_pdata *pdata = dev->vdec_pdata; int count_formats = *pdata->num_formats; @@ -465,21 +598,23 @@ static void mtk_vcodec_add_formats(unsigned int fourcc, break; case V4L2_PIX_FMT_MM21: case V4L2_PIX_FMT_MT21C: + case V4L2_PIX_FMT_MT2110T: + case V4L2_PIX_FMT_MT2110R: mtk_video_formats[count_formats].fourcc = fourcc; mtk_video_formats[count_formats].type = MTK_FMT_FRAME; mtk_video_formats[count_formats].num_planes = 2; break; default: - mtk_v4l2_err("Can not add unsupported format type"); + mtk_v4l2_vdec_err(ctx, "Can not add unsupported format type"); return; } num_formats++; - mtk_v4l2_debug(3, "num_formats: %d dec_capability: 0x%x", - count_formats, ctx->dev->dec_capability); + mtk_v4l2_vdec_dbg(3, ctx, "num_formats: %d dec_capability: 0x%x", + count_formats, ctx->dev->dec_capability); } -static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) +static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_dec_ctx *ctx) { int cap_format_count = 0, out_format_count = 0; @@ -490,6 +625,12 @@ static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) mtk_vcodec_add_formats(V4L2_PIX_FMT_MT21C, ctx); cap_format_count++; } + if (ctx->dev->dec_capability & MTK_VDEC_IS_SUPPORT_10BIT) { + mtk_vcodec_add_formats(V4L2_PIX_FMT_MT2110T, ctx); + cap_format_count++; + mtk_vcodec_add_formats(V4L2_PIX_FMT_MT2110R, ctx); + cap_format_count++; + } if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_MM21) { mtk_vcodec_add_formats(V4L2_PIX_FMT_MM21, ctx); cap_format_count++; @@ -522,7 +663,7 @@ static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) mtk_video_formats[cap_format_count + out_format_count - 1]; } -static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) +static void mtk_init_vdec_params(struct mtk_vcodec_dec_ctx *ctx) { struct vb2_queue *src_vq; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c similarity index 93% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c index 404a1a23fd40..2b6a5adbc419 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c @@ -8,9 +8,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -706,7 +705,7 @@ struct vdec_av1_slice_pfc { * @seq: global picture sequence */ struct vdec_av1_slice_instance { - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vpu_inst vpu; struct mtk_vcodec_mem iq_table; @@ -756,7 +755,7 @@ static inline bool vdec_av1_slice_need_scale(u32 ref_width, u32 ref_height, (this_height <= (ref_height << 4)); } -static void *vdec_av1_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +static void *vdec_av1_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); @@ -769,7 +768,7 @@ static void *vdec_av1_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instance) { u8 *remote_cdf_table; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_av1_slice_init_vsi *vsi; int ret; @@ -778,12 +777,11 @@ static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instanc remote_cdf_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->cdf_table_addr); if (IS_ERR(remote_cdf_table)) { - mtk_vcodec_err(instance, "failed to map cdf table\n"); + mtk_vdec_err(ctx, "failed to map cdf table\n"); return PTR_ERR(remote_cdf_table); } - mtk_vcodec_debug(instance, "map cdf table to 0x%p\n", - remote_cdf_table); + mtk_vdec_debug(ctx, "map cdf table to 0x%p\n", remote_cdf_table); if (instance->cdf_table.va) mtk_vcodec_mem_free(ctx, &instance->cdf_table); @@ -801,7 +799,7 @@ static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instanc static int vdec_av1_slice_init_iq_table(struct vdec_av1_slice_instance *instance) { u8 *remote_iq_table; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_av1_slice_init_vsi *vsi; int ret; @@ -810,11 +808,11 @@ static int vdec_av1_slice_init_iq_table(struct vdec_av1_slice_instance *instance remote_iq_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->iq_table_addr); if (IS_ERR(remote_iq_table)) { - mtk_vcodec_err(instance, "failed to map iq table\n"); + mtk_vdec_err(ctx, "failed to map iq table\n"); return PTR_ERR(remote_iq_table); } - mtk_vcodec_debug(instance, "map iq table to 0x%p\n", remote_iq_table); + mtk_vdec_debug(ctx, "map iq table to 0x%p\n", remote_iq_table); if (instance->iq_table.va) mtk_vcodec_mem_free(ctx, &instance->iq_table); @@ -862,8 +860,8 @@ static void vdec_av1_slice_decrease_ref_count(struct vdec_av1_slice_slot *slots, frame_info[fb_idx].ref_count--; if (frame_info[fb_idx].ref_count < 0) { frame_info[fb_idx].ref_count = 0; - mtk_v4l2_err("av1_error: %s() fb_idx %d decrease ref_count error\n", - __func__, fb_idx); + pr_err(MTK_DBG_V4L2_STR "av1_error: %s() fb_idx %d decrease ref_count error\n", + __func__, fb_idx); } vdec_av1_slice_clear_fb(&frame_info[fb_idx]); @@ -911,7 +909,7 @@ static void vdec_av1_slice_setup_slot(struct vdec_av1_slice_instance *instance, vsi->slot_id = vdec_av1_slice_get_new_slot(vsi); if (vsi->slot_id == AV1_INVALID_IDX) { - mtk_v4l2_err("warning:av1 get invalid index slot\n"); + mtk_v4l2_vdec_err(instance->ctx, "warning:av1 get invalid index slot\n"); vsi->slot_id = 0; } cur_frame_info = &vsi->slots.frame_info[vsi->slot_id]; @@ -938,7 +936,7 @@ static void vdec_av1_slice_setup_slot(struct vdec_av1_slice_instance *instance, static int vdec_av1_slice_alloc_working_buffer(struct vdec_av1_slice_instance *instance, struct vdec_av1_slice_vsi *vsi) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; enum vdec_av1_slice_resolution_level level; u32 max_sb_w, max_sb_h, max_w, max_h, w, h; int i, ret; @@ -965,8 +963,8 @@ static int vdec_av1_slice_alloc_working_buffer(struct vdec_av1_slice_instance *i if (level == instance->level) return 0; - mtk_vcodec_debug(instance, "resolution level changed from %u to %u, %ux%u", - instance->level, level, w, h); + mtk_vdec_debug(ctx, "resolution level changed from %u to %u, %ux%u", + instance->level, level, w, h); max_sb_w = DIV_ROUND_UP(max_w, 128); max_sb_h = DIV_ROUND_UP(max_h, 128); @@ -1021,7 +1019,7 @@ err: static void vdec_av1_slice_free_working_buffer(struct vdec_av1_slice_instance *instance) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; int i; for (i = 0; i < ARRAY_SIZE(instance->mv); i++) @@ -1400,17 +1398,17 @@ static int vdec_av1_slice_setup_tile_group(struct vdec_av1_slice_instance *insta if (tile_group->num_tiles != tge_size || tile_group->num_tiles > V4L2_AV1_MAX_TILE_COUNT) { - mtk_vcodec_err(instance, "invalid tge_size %d, tile_num:%d\n", - tge_size, tile_group->num_tiles); + mtk_vdec_err(instance->ctx, "invalid tge_size %d, tile_num:%d\n", + tge_size, tile_group->num_tiles); return -EINVAL; } for (i = 0; i < tge_size; i++) { if (i != ctrl_tge[i].tile_row * vsi->frame.uh.tile.tile_cols + ctrl_tge[i].tile_col) { - mtk_vcodec_err(instance, "invalid tge info %d, %d %d %d\n", - i, ctrl_tge[i].tile_row, ctrl_tge[i].tile_col, - vsi->frame.uh.tile.tile_rows); + mtk_vdec_err(instance->ctx, "invalid tge info %d, %d %d %d\n", + i, ctrl_tge[i].tile_row, ctrl_tge[i].tile_col, + vsi->frame.uh.tile.tile_rows); return -EINVAL; } tile_group->tile_size[i] = ctrl_tge[i].tile_size; @@ -1505,8 +1503,8 @@ static void vdec_av1_slice_setup_ref(struct vdec_av1_slice_pfc *pfc, slot_id = frame->ref_frame_map[ref_idx]; frame_info = &slots->frame_info[slot_id]; if (slot_id == AV1_INVALID_IDX) { - mtk_v4l2_err("cannot match reference[%d] 0x%llx\n", i, - ctrl_fh->reference_frame_ts[ref_idx]); + pr_err(MTK_DBG_V4L2_STR "cannot match reference[%d] 0x%llx\n", i, + ctrl_fh->reference_frame_ts[ref_idx]); frame->order_hints[i] = 0; frame->ref_frame_valid[i] = 0; continue; @@ -1639,7 +1637,7 @@ static void vdec_av1_slice_setup_seg_buffer(struct vdec_av1_slice_instance *inst /* reset segment buffer */ if (uh->primary_ref_frame == AV1_PRIMARY_REF_NONE || !uh->seg.segmentation_enabled) { - mtk_vcodec_debug(instance, "reset seg %d\n", vsi->slot_id); + mtk_vdec_debug(instance->ctx, "reset seg %d\n", vsi->slot_id); if (vsi->slot_id != AV1_INVALID_IDX) { buf = &instance->seg[vsi->slot_id]; memset(buf->va, 0, buf->size); @@ -1658,9 +1656,9 @@ static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *ins u32 allow_update_cdf = 0; u32 sb_boundary_x_m1 = 0, sb_boundary_y_m1 = 0; int tile_info_base; - u32 tile_buf_pa; + u64 tile_buf_pa; u32 *tile_info_buf = instance->tile.va; - u32 pa = (u32)bs->dma_addr; + u64 pa = (u64)bs->dma_addr; if (uh->disable_cdf_update == 0) allow_update_cdf = 1; @@ -1673,8 +1671,12 @@ static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *ins tile_info_buf[tile_info_base + 0] = (tile_group->tile_size[tile_num] << 3); tile_buf_pa = pa + tile_group->tile_start_offset[tile_num]; - tile_info_buf[tile_info_base + 1] = (tile_buf_pa >> 4) << 4; - tile_info_buf[tile_info_base + 2] = (tile_buf_pa % 16) << 3; + /* save av1 tile high 4bits(bit 32-35) address in lower 4 bits position + * and clear original for hw requirement. + */ + tile_info_buf[tile_info_base + 1] = (tile_buf_pa & 0xFFFFFFF0ull) | + ((tile_buf_pa & 0xF00000000ull) >> 32); + tile_info_buf[tile_info_base + 2] = (tile_buf_pa & 0xFull) << 3; sb_boundary_x_m1 = (tile->mi_col_starts[tile_col + 1] - tile->mi_col_starts[tile_col] - 1) & @@ -1690,18 +1692,18 @@ static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *ins uh->disable_frame_end_update_cdf == 0) tile_info_buf[tile_info_base + 4] |= (1 << 17); - mtk_vcodec_debug(instance, "// tile buf %d pos(%dx%d) offset 0x%x\n", - tile_num, tile_row, tile_col, tile_info_base); - mtk_vcodec_debug(instance, "// %08x %08x %08x %08x\n", - tile_info_buf[tile_info_base + 0], - tile_info_buf[tile_info_base + 1], - tile_info_buf[tile_info_base + 2], - tile_info_buf[tile_info_base + 3]); - mtk_vcodec_debug(instance, "// %08x %08x %08x %08x\n", - tile_info_buf[tile_info_base + 4], - tile_info_buf[tile_info_base + 5], - tile_info_buf[tile_info_base + 6], - tile_info_buf[tile_info_base + 7]); + mtk_vdec_debug(instance->ctx, "// tile buf %d pos(%dx%d) offset 0x%x\n", + tile_num, tile_row, tile_col, tile_info_base); + mtk_vdec_debug(instance->ctx, "// %08x %08x %08x %08x\n", + tile_info_buf[tile_info_base + 0], + tile_info_buf[tile_info_base + 1], + tile_info_buf[tile_info_base + 2], + tile_info_buf[tile_info_base + 3]); + mtk_vdec_debug(instance->ctx, "// %08x %08x %08x %08x\n", + tile_info_buf[tile_info_base + 4], + tile_info_buf[tile_info_base + 5], + tile_info_buf[tile_info_base + 6], + tile_info_buf[tile_info_base + 7]); } } @@ -1743,8 +1745,8 @@ static int vdec_av1_slice_update_lat(struct vdec_av1_slice_instance *instance, struct vdec_av1_slice_vsi *vsi; vsi = &pfc->vsi; - mtk_vcodec_debug(instance, "frame %u LAT CRC 0x%08x, output size is %d\n", - pfc->seq, vsi->state.crc[0], vsi->state.out_size); + mtk_vdec_debug(instance->ctx, "frame %u LAT CRC 0x%08x, output size is %d\n", + pfc->seq, vsi->state.crc[0], vsi->state.out_size); /* buffer full, need to re-decode */ if (vsi->state.full) { @@ -1855,17 +1857,17 @@ static int vdec_av1_slice_update_core(struct vdec_av1_slice_instance *instance, { struct vdec_av1_slice_vsi *vsi = instance->core_vsi; - mtk_vcodec_debug(instance, "frame %u Y_CRC %08x %08x %08x %08x\n", - pfc->seq, vsi->state.crc[0], vsi->state.crc[1], - vsi->state.crc[2], vsi->state.crc[3]); - mtk_vcodec_debug(instance, "frame %u C_CRC %08x %08x %08x %08x\n", - pfc->seq, vsi->state.crc[8], vsi->state.crc[9], - vsi->state.crc[10], vsi->state.crc[11]); + mtk_vdec_debug(instance->ctx, "frame %u Y_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[0], vsi->state.crc[1], + vsi->state.crc[2], vsi->state.crc[3]); + mtk_vdec_debug(instance->ctx, "frame %u C_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[8], vsi->state.crc[9], + vsi->state.crc[10], vsi->state.crc[11]); return 0; } -static int vdec_av1_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_av1_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_av1_slice_instance *instance; struct vdec_av1_slice_init_vsi *vsi; @@ -1883,14 +1885,14 @@ static int vdec_av1_slice_init(struct mtk_vcodec_ctx *ctx) ret = vpu_dec_init(&instance->vpu); if (ret) { - mtk_vcodec_err(instance, "failed to init vpu dec, ret %d\n", ret); + mtk_vdec_err(ctx, "failed to init vpu dec, ret %d\n", ret); goto error_vpu_init; } /* init vsi and global flags */ vsi = instance->vpu.vsi; if (!vsi) { - mtk_vcodec_err(instance, "failed to get AV1 vsi\n"); + mtk_vdec_err(ctx, "failed to get AV1 vsi\n"); ret = -EINVAL; goto error_vsi; } @@ -1898,20 +1900,20 @@ static int vdec_av1_slice_init(struct mtk_vcodec_ctx *ctx) instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->core_vsi); if (!instance->core_vsi) { - mtk_vcodec_err(instance, "failed to get AV1 core vsi\n"); + mtk_vdec_err(ctx, "failed to get AV1 core vsi\n"); ret = -EINVAL; goto error_vsi; } if (vsi->vsi_size != sizeof(struct vdec_av1_slice_vsi)) - mtk_vcodec_err(instance, "remote vsi size 0x%x mismatch! expected: 0x%zx\n", - vsi->vsi_size, sizeof(struct vdec_av1_slice_vsi)); + mtk_vdec_err(ctx, "remote vsi size 0x%x mismatch! expected: 0x%zx\n", + vsi->vsi_size, sizeof(struct vdec_av1_slice_vsi)); instance->irq_enabled = 1; instance->inneracing_mode = IS_VDEC_INNER_RACING(instance->ctx->dev->dec_capability); - mtk_vcodec_debug(instance, "vsi 0x%p core_vsi 0x%llx 0x%p, inneracing_mode %d\n", - vsi, vsi->core_vsi, instance->core_vsi, instance->inneracing_mode); + mtk_vdec_debug(ctx, "vsi 0x%p core_vsi 0x%llx 0x%p, inneracing_mode %d\n", + vsi, vsi->core_vsi, instance->core_vsi, instance->inneracing_mode); ret = vdec_av1_slice_init_cdf_table(instance); if (ret) @@ -1938,7 +1940,7 @@ static void vdec_av1_slice_deinit(void *h_vdec) if (!instance) return; - mtk_vcodec_debug(instance, "h_vdec 0x%p\n", h_vdec); + mtk_vdec_debug(instance->ctx, "h_vdec 0x%p\n", h_vdec); vpu_dec_deinit(&instance->vpu); vdec_av1_slice_free_working_buffer(instance); vdec_msg_queue_deinit(&instance->ctx->msg_queue, instance->ctx); @@ -1951,7 +1953,7 @@ static int vdec_av1_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_av1_slice_instance *instance = h_vdec; int i; - mtk_vcodec_debug(instance, "flush ...\n"); + mtk_vdec_debug(instance->ctx, "flush ...\n"); vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue); @@ -1963,10 +1965,10 @@ static int vdec_av1_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, static void vdec_av1_slice_get_pic_info(struct vdec_av1_slice_instance *instance) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; u32 data[3]; - mtk_vcodec_debug(instance, "w %u h %u\n", ctx->picinfo.pic_w, ctx->picinfo.pic_h); + mtk_vdec_debug(ctx, "w %u h %u\n", ctx->picinfo.pic_w, ctx->picinfo.pic_h); data[0] = ctx->picinfo.pic_w; data[1] = ctx->picinfo.pic_h; @@ -1989,15 +1991,15 @@ static inline void vdec_av1_slice_get_dpb_size(struct vdec_av1_slice_instance *i static void vdec_av1_slice_get_crop_info(struct vdec_av1_slice_instance *instance, struct v4l2_rect *cr) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; cr->left = 0; cr->top = 0; cr->width = ctx->picinfo.pic_w; cr->height = ctx->picinfo.pic_h; - mtk_vcodec_debug(instance, "l=%d, t=%d, w=%d, h=%d\n", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(ctx, "l=%d, t=%d, w=%d, h=%d\n", + cr->left, cr->top, cr->width, cr->height); } static int vdec_av1_slice_get_param(void *h_vdec, enum vdec_get_param_type type, void *out) @@ -2015,7 +2017,7 @@ static int vdec_av1_slice_get_param(void *h_vdec, enum vdec_get_param_type type, vdec_av1_slice_get_crop_info(instance, out); break; default: - mtk_vcodec_err(instance, "invalid get parameter type=%d\n", type); + mtk_vdec_err(instance->ctx, "invalid get parameter type=%d\n", type); return -EINVAL; } @@ -2029,7 +2031,7 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_lat_buf *lat_buf; struct vdec_av1_slice_pfc *pfc; struct vdec_av1_slice_vsi *vsi; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; int ret; if (!instance || !instance->ctx) @@ -2039,7 +2041,7 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* init msgQ for the first time */ if (vdec_msg_queue_init(&ctx->msg_queue, ctx, vdec_av1_slice_core_decode, sizeof(*pfc))) { - mtk_vcodec_err(instance, "failed to init AV1 msg queue\n"); + mtk_vdec_err(ctx, "failed to init AV1 msg queue\n"); return -ENOMEM; } @@ -2049,7 +2051,7 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_err(instance, "failed to get AV1 lat buf\n"); + mtk_vdec_err(ctx, "failed to get AV1 lat buf\n"); return -EAGAIN; } pfc = (struct vdec_av1_slice_pfc *)lat_buf->private_data; @@ -2061,14 +2063,14 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, ret = vdec_av1_slice_setup_lat(instance, bs, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "failed to setup AV1 lat ret %d\n", ret); + mtk_vdec_err(ctx, "failed to setup AV1 lat ret %d\n", ret); goto err_free_fb_out; } vdec_av1_slice_vsi_to_remote(vsi, instance->vsi); ret = vpu_dec_start(&instance->vpu, NULL, 0); if (ret) { - mtk_vcodec_err(instance, "failed to dec AV1 ret %d\n", ret); + mtk_vdec_err(ctx, "failed to dec AV1 ret %d\n", ret); goto err_free_fb_out; } if (instance->inneracing_mode) @@ -2080,7 +2082,7 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, MTK_VDEC_LAT0); /* update remote vsi if decode timeout */ if (ret) { - mtk_vcodec_err(instance, "AV1 Frame %d decode timeout %d\n", pfc->seq, ret); + mtk_vdec_err(ctx, "AV1 Frame %d decode timeout %d\n", pfc->seq, ret); WRITE_ONCE(instance->vsi->state.timeout, 1); } vpu_dec_end(&instance->vpu); @@ -2091,7 +2093,7 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* LAT trans full, re-decode */ if (ret == -EAGAIN) { - mtk_vcodec_err(instance, "AV1 Frame %d trans full\n", pfc->seq); + mtk_vdec_err(ctx, "AV1 Frame %d trans full\n", pfc->seq); if (!instance->inneracing_mode) vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); return 0; @@ -2099,14 +2101,14 @@ static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* LAT trans full, no more UBE or decode timeout */ if (ret == -ENOMEM || vsi->state.timeout) { - mtk_vcodec_err(instance, "AV1 Frame %d insufficient buffer or timeout\n", pfc->seq); + mtk_vdec_err(ctx, "AV1 Frame %d insufficient buffer or timeout\n", pfc->seq); if (!instance->inneracing_mode) vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); return -EBUSY; } vsi->trans.dma_addr_end += ctx->msg_queue.wdma_addr.dma_addr; - mtk_vcodec_debug(instance, "lat dma 1 0x%pad 0x%pad\n", - &pfc->vsi.trans.dma_addr, &pfc->vsi.trans.dma_addr_end); + mtk_vdec_debug(ctx, "lat dma 1 0x%pad 0x%pad\n", + &pfc->vsi.trans.dma_addr, &pfc->vsi.trans.dma_addr_end); vdec_msg_queue_update_ube_wptr(&ctx->msg_queue, vsi->trans.dma_addr_end); @@ -2120,7 +2122,7 @@ err_free_fb_out: vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); if (pfc) - mtk_vcodec_err(instance, "slice dec number: %d err: %d", pfc->seq, ret); + mtk_vdec_err(ctx, "slice dec number: %d err: %d", pfc->seq, ret); return ret; } @@ -2129,7 +2131,7 @@ static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf) { struct vdec_av1_slice_instance *instance; struct vdec_av1_slice_pfc *pfc; - struct mtk_vcodec_ctx *ctx = NULL; + struct mtk_vcodec_dec_ctx *ctx = NULL; struct vdec_fb *fb = NULL; int ret = -EINVAL; @@ -2153,13 +2155,13 @@ static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf) ret = vdec_av1_slice_setup_core(instance, fb, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "vdec_av1_slice_setup_core\n"); + mtk_vdec_err(ctx, "vdec_av1_slice_setup_core\n"); goto err; } vdec_av1_slice_vsi_to_remote(&pfc->vsi, instance->core_vsi); ret = vpu_dec_core(&instance->vpu); if (ret) { - mtk_vcodec_err(instance, "vpu_dec_core\n"); + mtk_vdec_err(ctx, "vpu_dec_core\n"); goto err; } @@ -2169,7 +2171,7 @@ static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf) MTK_VDEC_CORE); /* update remote vsi if decode timeout */ if (ret) { - mtk_vcodec_err(instance, "AV1 frame %d core timeout\n", pfc->seq); + mtk_vdec_err(ctx, "AV1 frame %d core timeout\n", pfc->seq); WRITE_ONCE(instance->vsi->state.timeout, 1); } vpu_dec_core_end(&instance->vpu); @@ -2177,12 +2179,12 @@ static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf) ret = vdec_av1_slice_update_core(instance, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "vdec_av1_slice_update_core\n"); + mtk_vdec_err(ctx, "vdec_av1_slice_update_core\n"); goto err; } - mtk_vcodec_debug(instance, "core dma_addr_end 0x%pad\n", - &instance->core_vsi->trans.dma_addr_end); + mtk_vdec_debug(ctx, "core dma_addr_end 0x%pad\n", + &instance->core_vsi->trans.dma_addr_end); vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, instance->core_vsi->trans.dma_addr_end); ctx->dev->vdec_pdata->cap_to_disp(ctx, 0, lat_buf->src_buf_req); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c similarity index 84% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c index 481655bb6016..bf7dffe60d07 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c @@ -8,9 +8,8 @@ #include #include "../vdec_drv_if.h" -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_vpu_if.h" #include "../vdec_drv_base.h" @@ -117,7 +116,7 @@ struct vdec_h264_vsi { /** * struct vdec_h264_inst - h264 decoder instance * @num_nalu : how many nalus be decoded - * @ctx : point to mtk_vcodec_ctx + * @ctx : point to mtk_vcodec_dec_ctx * @pred_buf : HW working predication buffer * @mv_buf : HW working motion vector buffer * @vpu : VPU instance @@ -125,7 +124,7 @@ struct vdec_h264_vsi { */ struct vdec_h264_inst { unsigned int num_nalu; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct mtk_vcodec_mem pred_buf; struct mtk_vcodec_mem mv_buf[H264_MAX_FB_NUM]; struct vdec_vpu_inst vpu; @@ -144,7 +143,7 @@ static int allocate_predication_buf(struct vdec_h264_inst *inst) inst->pred_buf.size = BUF_PREDICTION_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); if (err) { - mtk_vcodec_err(inst, "failed to allocate ppl buf"); + mtk_vdec_err(inst->ctx, "failed to allocate ppl buf"); return err; } @@ -156,8 +155,6 @@ static void free_predication_buf(struct vdec_h264_inst *inst) { struct mtk_vcodec_mem *mem = NULL; - mtk_vcodec_debug_enter(inst); - inst->vsi->pred_buf_dma = 0; mem = &inst->pred_buf; if (mem->va) @@ -178,7 +175,7 @@ static int alloc_mv_buf(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) mem->size = buf_sz; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); + mtk_vdec_err(inst->ctx, "failed to allocate mv buf"); return err; } inst->vsi->mv_buf_dma[i] = mem->dma_addr; @@ -209,9 +206,9 @@ static int check_list_validity(struct vdec_h264_inst *inst, bool disp_list) if (list->count > H264_MAX_FB_NUM || list->read_idx >= H264_MAX_FB_NUM || list->write_idx >= H264_MAX_FB_NUM) { - mtk_vcodec_err(inst, "%s list err: cnt=%d r_idx=%d w_idx=%d", - disp_list ? "disp" : "free", list->count, - list->read_idx, list->write_idx); + mtk_vdec_err(inst->ctx, "%s list err: cnt=%d r_idx=%d w_idx=%d", + disp_list ? "disp" : "free", list->count, + list->read_idx, list->write_idx); return -EINVAL; } @@ -228,12 +225,12 @@ static void put_fb_to_free(struct vdec_h264_inst *inst, struct vdec_fb *fb) list = &inst->vsi->list_free; if (list->count == H264_MAX_FB_NUM) { - mtk_vcodec_err(inst, "[FB] put fb free_list full"); + mtk_vdec_err(inst->ctx, "[FB] put fb free_list full"); return; } - mtk_vcodec_debug(inst, "[FB] put fb into free_list @(%p, %llx)", - fb->base_y.va, (u64)fb->base_y.dma_addr); + mtk_vdec_debug(inst->ctx, "[FB] put fb into free_list @(%p, %llx)", + fb->base_y.va, (u64)fb->base_y.dma_addr); list->fb_list[list->write_idx].vdec_fb_va = (u64)(uintptr_t)fb; list->write_idx = (list->write_idx == H264_MAX_FB_NUM - 1) ? @@ -246,10 +243,9 @@ static void get_pic_info(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) { *pic = inst->vsi->pic; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); - mtk_vcodec_debug(inst, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], pic->fb_sz[1]); + mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", + pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); + mtk_vdec_debug(inst->ctx, "fb size: Y(%d), C(%d)", pic->fb_sz[0], pic->fb_sz[1]); } static void get_crop_info(struct vdec_h264_inst *inst, struct v4l2_rect *cr) @@ -259,17 +255,17 @@ static void get_crop_info(struct vdec_h264_inst *inst, struct v4l2_rect *cr) cr->width = inst->vsi->crop.width; cr->height = inst->vsi->crop.height; - mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "l=%d, t=%d, w=%d, h=%d", cr->left, cr->top, + cr->width, cr->height); } static void get_dpb_size(struct vdec_h264_inst *inst, unsigned int *dpb_sz) { *dpb_sz = inst->vsi->dec.dpb_sz; - mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); + mtk_vdec_debug(inst->ctx, "sz=%d", *dpb_sz); } -static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) +static int vdec_h264_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_h264_inst *inst = NULL; int err; @@ -285,7 +281,7 @@ static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); + mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); goto error_free_inst; } @@ -294,7 +290,7 @@ static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) if (err) goto error_deinit; - mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); + mtk_vdec_debug(ctx, "H264 Instance >> %p", inst); ctx->drv_handle = inst; return 0; @@ -311,8 +307,6 @@ static void vdec_h264_deinit(void *h_vdec) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); free_predication_buf(inst); free_mv_buf(inst); @@ -348,8 +342,8 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, uint64_t y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; uint64_t c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; - mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", - ++inst->num_nalu, y_fb_dma, c_fb_dma, fb); + mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", + ++inst->num_nalu, y_fb_dma, c_fb_dma, fb); /* bs NULL means flush decoder */ if (bs == NULL) @@ -359,15 +353,15 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, buf_sz = bs->size; nal_start_idx = find_start_code(buf, buf_sz); if (nal_start_idx < 0) { - mtk_vcodec_err(inst, "invalid nal start code"); + mtk_vdec_err(inst->ctx, "invalid nal start code"); err = -EIO; goto err_free_fb_out; } nal_start = buf[nal_start_idx]; nal_type = NAL_TYPE(buf[nal_start_idx]); - mtk_vcodec_debug(inst, "\n + NALU[%d] type %d +\n", inst->num_nalu, - nal_type); + mtk_vdec_debug(inst->ctx, "\n + NALU[%d] type %d +\n", inst->num_nalu, + nal_type); if (nal_type == NAL_H264_PPS) { buf_sz -= nal_start_idx; @@ -388,8 +382,7 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, err = vpu_dec_start(vpu, data, 2); if (err) { if (err > 0 && (DEC_ERR_RET(err) == H264_ERR_NOT_VALID)) { - mtk_vcodec_err(inst, "- error bitstream - err = %d -", - err); + mtk_vdec_err(inst->ctx, "- error bitstream - err = %d -", err); err = -EIO; } goto err_free_fb_out; @@ -399,7 +392,7 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (*res_chg) { struct vdec_pic_info pic; - mtk_vcodec_debug(inst, "- resolution changed -"); + mtk_vdec_debug(inst->ctx, "- resolution changed -"); get_pic_info(inst, &pic); if (inst->vsi->dec.realloc_mv_buf) { @@ -420,13 +413,12 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, vpu_dec_end(vpu); } - mtk_vcodec_debug(inst, "\n - NALU[%d] type=%d -\n", inst->num_nalu, - nal_type); + mtk_vdec_debug(inst->ctx, "\n - NALU[%d] type=%d -\n", inst->num_nalu, nal_type); return 0; err_free_fb_out: put_fb_to_free(inst, fb); - mtk_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); + mtk_vdec_err(inst->ctx, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); return err; } @@ -440,8 +432,7 @@ static void vdec_h264_get_fb(struct vdec_h264_inst *inst, return; if (list->count == 0) { - mtk_vcodec_debug(inst, "[FB] there is no %s fb", - disp_list ? "disp" : "free"); + mtk_vdec_debug(inst->ctx, "[FB] there is no %s fb", disp_list ? "disp" : "free"); *out_fb = NULL; return; } @@ -451,10 +442,10 @@ static void vdec_h264_get_fb(struct vdec_h264_inst *inst, fb->status |= (disp_list ? FB_ST_DISPLAY : FB_ST_FREE); *out_fb = fb; - mtk_vcodec_debug(inst, "[FB] get %s fb st=%d poc=%d %llx", - disp_list ? "disp" : "free", - fb->status, list->fb_list[list->read_idx].poc, - list->fb_list[list->read_idx].vdec_fb_va); + mtk_vdec_debug(inst->ctx, "[FB] get %s fb st=%d poc=%d %llx", + disp_list ? "disp" : "free", + fb->status, list->fb_list[list->read_idx].poc, + list->fb_list[list->read_idx].vdec_fb_va); list->read_idx = (list->read_idx == H264_MAX_FB_NUM - 1) ? 0 : list->read_idx + 1; @@ -488,7 +479,7 @@ static int vdec_h264_get_param(void *h_vdec, enum vdec_get_param_type type, break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.c similarity index 98% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.c index 580ce979e2a3..5ca20d75dc8e 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.c @@ -33,7 +33,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list, memset(&ref_list[num_valid], 0x20, 32 - num_valid); } -void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); @@ -43,7 +43,7 @@ void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) return ctrl->p_cur.p; } -void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_ctx *ctx, +void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_dec_ctx *ctx, struct slice_api_h264_decode_param *decode_params, struct mtk_h264_dpb_info *h264_dpb_info) { diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h similarity index 97% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h index 53d0a7c962a9..ac82be336055 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h @@ -13,7 +13,7 @@ #include #include -#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_dec_drv.h" #define NAL_NON_IDR_SLICE 0x01 #define NAL_IDR_SLICE 0x05 @@ -182,7 +182,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list, * * Return: returns CID ctrl address. */ -void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id); +void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id); /** * mtk_vdec_h264_fill_dpb_info - get each CID contrl address. @@ -191,7 +191,7 @@ void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id); * @decode_params: slice decode params * @h264_dpb_info: dpb buffer information */ -void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_ctx *ctx, +void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_dec_ctx *ctx, struct slice_api_h264_decode_param *decode_params, struct mtk_h264_dpb_info *h264_dpb_info); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c similarity index 86% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c index 4bc05ab5afea..5600f1df653d 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c @@ -6,9 +6,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -74,7 +73,7 @@ struct vdec_h264_vsi { /** * struct vdec_h264_slice_inst - h264 decoder instance * @num_nalu : how many nalus be decoded - * @ctx : point to mtk_vcodec_ctx + * @ctx : point to mtk_vcodec_dec_ctx * @pred_buf : HW working predication buffer * @mv_buf : HW working motion vector buffer * @vpu : VPU instance @@ -84,7 +83,7 @@ struct vdec_h264_vsi { */ struct vdec_h264_slice_inst { unsigned int num_nalu; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct mtk_vcodec_mem pred_buf; struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM]; struct vdec_vpu_inst vpu; @@ -162,7 +161,7 @@ static int allocate_predication_buf(struct vdec_h264_slice_inst *inst) inst->pred_buf.size = BUF_PREDICTION_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); if (err) { - mtk_vcodec_err(inst, "failed to allocate ppl buf"); + mtk_vdec_err(inst->ctx, "failed to allocate ppl buf"); return err; } @@ -174,8 +173,6 @@ static void free_predication_buf(struct vdec_h264_slice_inst *inst) { struct mtk_vcodec_mem *mem = &inst->pred_buf; - mtk_vcodec_debug_enter(inst); - inst->vsi_ctx.pred_buf_dma = 0; if (mem->va) mtk_vcodec_mem_free(inst->ctx, mem); @@ -189,7 +186,7 @@ static int alloc_mv_buf(struct vdec_h264_slice_inst *inst, struct mtk_vcodec_mem *mem = NULL; unsigned int buf_sz = mtk_vdec_h264_get_mv_buf_size(pic->buf_w, pic->buf_h); - mtk_v4l2_debug(3, "size = 0x%x", buf_sz); + mtk_v4l2_vdec_dbg(3, inst->ctx, "size = 0x%x", buf_sz); for (i = 0; i < H264_MAX_MV_NUM; i++) { mem = &inst->mv_buf[i]; if (mem->va) @@ -197,7 +194,7 @@ static int alloc_mv_buf(struct vdec_h264_slice_inst *inst, mem->size = buf_sz; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); + mtk_vdec_err(inst->ctx, "failed to allocate mv buf"); return err; } inst->vsi_ctx.mv_buf_dma[i] = mem->dma_addr; @@ -222,7 +219,7 @@ static void free_mv_buf(struct vdec_h264_slice_inst *inst) static void get_pic_info(struct vdec_h264_slice_inst *inst, struct vdec_pic_info *pic) { - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; ctx->picinfo.buf_w = ALIGN(ctx->picinfo.pic_w, VCODEC_DEC_ALIGNED_64); ctx->picinfo.buf_h = ALIGN(ctx->picinfo.pic_h, VCODEC_DEC_ALIGNED_64); @@ -232,11 +229,11 @@ static void get_pic_info(struct vdec_h264_slice_inst *inst, ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; *pic = ctx->picinfo; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->picinfo.buf_w, ctx->picinfo.buf_h); - mtk_vcodec_debug(inst, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], - ctx->picinfo.fb_sz[1]); + mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + mtk_vdec_debug(inst->ctx, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], + ctx->picinfo.fb_sz[1]); if (ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w || ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h) { @@ -245,12 +242,12 @@ static void get_pic_info(struct vdec_h264_slice_inst *inst, ctx->last_decoded_picinfo.buf_h != ctx->picinfo.buf_h) inst->vsi_ctx.dec.realloc_mv_buf = true; - mtk_v4l2_debug(1, "ResChg: (%d %d) : old(%d, %d) -> new(%d, %d)", - inst->vsi_ctx.dec.resolution_changed, - inst->vsi_ctx.dec.realloc_mv_buf, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h); + mtk_v4l2_vdec_dbg(1, inst->ctx, "ResChg: (%d %d) : old(%d, %d) -> new(%d, %d)", + inst->vsi_ctx.dec.resolution_changed, + inst->vsi_ctx.dec.realloc_mv_buf, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h); } } @@ -261,17 +258,17 @@ static void get_crop_info(struct vdec_h264_slice_inst *inst, struct v4l2_rect *c cr->width = inst->vsi_ctx.crop.width; cr->height = inst->vsi_ctx.crop.height; - mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); } static void get_dpb_size(struct vdec_h264_slice_inst *inst, unsigned int *dpb_sz) { *dpb_sz = inst->vsi_ctx.dec.dpb_sz; - mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); + mtk_vdec_debug(inst->ctx, "sz=%d", *dpb_sz); } -static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_h264_slice_inst *inst; int err; @@ -287,7 +284,7 @@ static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); + mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); goto error_free_inst; } @@ -299,13 +296,13 @@ static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) if (err) goto error_deinit; - mtk_vcodec_debug(inst, "struct size = %zu,%zu,%zu,%zu\n", - sizeof(struct mtk_h264_sps_param), - sizeof(struct mtk_h264_pps_param), - sizeof(struct mtk_h264_dec_slice_param), - sizeof(struct mtk_h264_dpb_info)); + mtk_vdec_debug(ctx, "struct size = %zu,%zu,%zu,%zu\n", + sizeof(struct mtk_h264_sps_param), + sizeof(struct mtk_h264_pps_param), + sizeof(struct mtk_h264_dec_slice_param), + sizeof(struct mtk_h264_dpb_info)); - mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); + mtk_vdec_debug(ctx, "H264 Instance >> %p", inst); ctx->drv_handle = inst; return 0; @@ -322,8 +319,6 @@ static void vdec_h264_slice_deinit(void *h_vdec) { struct vdec_h264_slice_inst *inst = h_vdec; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); free_predication_buf(inst); free_mv_buf(inst); @@ -358,8 +353,8 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; - mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", - inst->num_nalu, y_fb_dma, c_fb_dma, fb); + mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", + inst->num_nalu, y_fb_dma, c_fb_dma, fb); inst->vsi_ctx.dec.bs_dma = (uint64_t)bs->dma_addr; inst->vsi_ctx.dec.y_fb_dma = y_fb_dma; @@ -384,7 +379,7 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, *res_chg = inst->vsi_ctx.dec.resolution_changed; if (*res_chg) { - mtk_vcodec_debug(inst, "- resolution changed -"); + mtk_vdec_debug(inst->ctx, "- resolution changed -"); if (inst->vsi_ctx.dec.realloc_mv_buf) { err = alloc_mv_buf(inst, &inst->ctx->picinfo); inst->vsi_ctx.dec.realloc_mv_buf = false; @@ -408,11 +403,11 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, vpu_dec_end(vpu); memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); - mtk_vcodec_debug(inst, "\n - NALU[%d]", inst->num_nalu); + mtk_vdec_debug(inst->ctx, "\n - NALU[%d]", inst->num_nalu); return 0; err_free_fb_out: - mtk_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); + mtk_vdec_err(inst->ctx, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); return err; } @@ -434,7 +429,7 @@ static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c similarity index 85% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index a7e8e3257b7f..0e741e0dc8ba 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -10,9 +10,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -133,7 +132,7 @@ struct vdec_h264_slice_share_info { * struct vdec_h264_slice_inst - h264 decoder instance * * @slice_dec_num: how many picture be decoded - * @ctx: point to mtk_vcodec_ctx + * @ctx: point to mtk_vcodec_dec_ctx * @pred_buf: HW working predication buffer * @mv_buf: HW working motion vector buffer * @vpu: VPU instance @@ -153,7 +152,7 @@ struct vdec_h264_slice_share_info { */ struct vdec_h264_slice_inst { unsigned int slice_dec_num; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct mtk_vcodec_mem pred_buf; struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM]; struct vdec_vpu_inst vpu; @@ -199,7 +198,7 @@ static int vdec_h264_slice_fill_decode_parameters(struct vdec_h264_slice_inst *i return PTR_ERR(pps); if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) { - mtk_vcodec_err(inst, "No support for H.264 field decoding."); + mtk_vdec_err(inst->ctx, "No support for H.264 field decoding."); inst->is_field_bitstream = true; return -EINVAL; } @@ -294,7 +293,7 @@ static void vdec_h264_slice_fill_decode_reflist(struct vdec_h264_slice_inst *ins mtk_vdec_h264_fill_dpb_info(inst->ctx, &slice_param->decode_params, slice_param->h264_dpb_info); - mtk_v4l2_debug(3, "cur poc = %d\n", dec_params->bottom_field_order_cnt); + mtk_v4l2_vdec_dbg(3, inst->ctx, "cur poc = %d\n", dec_params->bottom_field_order_cnt); /* Build the reference lists */ v4l2_h264_init_reflist_builder(&reflist_builder, dec_params, sps, inst->dpb); @@ -314,7 +313,7 @@ static int vdec_h264_slice_alloc_mv_buf(struct vdec_h264_slice_inst *inst, struct mtk_vcodec_mem *mem; int i, err; - mtk_v4l2_debug(3, "size = 0x%x", buf_sz); + mtk_v4l2_vdec_dbg(3, inst->ctx, "size = 0x%x", buf_sz); for (i = 0; i < H264_MAX_MV_NUM; i++) { mem = &inst->mv_buf[i]; if (mem->va) @@ -322,7 +321,7 @@ static int vdec_h264_slice_alloc_mv_buf(struct vdec_h264_slice_inst *inst, mem->size = buf_sz; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); + mtk_vdec_err(inst->ctx, "failed to allocate mv buf"); return err; } } @@ -344,7 +343,7 @@ static void vdec_h264_slice_free_mv_buf(struct vdec_h264_slice_inst *inst) static void vdec_h264_slice_get_pic_info(struct vdec_h264_slice_inst *inst) { - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; u32 data[3]; data[0] = ctx->picinfo.pic_w; @@ -359,11 +358,11 @@ static void vdec_h264_slice_get_pic_info(struct vdec_h264_slice_inst *inst) inst->cap_num_planes = ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->picinfo.buf_w, ctx->picinfo.buf_h); - mtk_vcodec_debug(inst, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], - ctx->picinfo.fb_sz[1]); + mtk_vdec_debug(ctx, "pic(%d, %d), buf(%d, %d)", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + mtk_vdec_debug(ctx, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], + ctx->picinfo.fb_sz[1]); if (ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w || ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h) { @@ -372,12 +371,12 @@ static void vdec_h264_slice_get_pic_info(struct vdec_h264_slice_inst *inst) ctx->last_decoded_picinfo.buf_h != ctx->picinfo.buf_h) inst->realloc_mv_buf = true; - mtk_v4l2_debug(1, "resChg: (%d %d) : old(%d, %d) -> new(%d, %d)", - inst->resolution_changed, - inst->realloc_mv_buf, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h); + mtk_v4l2_vdec_dbg(1, inst->ctx, "resChg: (%d %d) : old(%d, %d) -> new(%d, %d)", + inst->resolution_changed, + inst->realloc_mv_buf, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h); } } @@ -389,11 +388,11 @@ static void vdec_h264_slice_get_crop_info(struct vdec_h264_slice_inst *inst, cr->width = inst->ctx->picinfo.pic_w; cr->height = inst->ctx->picinfo.pic_h; - mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); } -static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_h264_slice_inst *inst; int err, vsi_size; @@ -412,7 +411,7 @@ static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); + mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); goto error_free_inst; } @@ -423,14 +422,14 @@ static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) inst->resolution_changed = true; inst->realloc_mv_buf = true; - mtk_vcodec_debug(inst, "lat struct size = %d,%d,%d,%d vsi: %d\n", - (int)sizeof(struct mtk_h264_sps_param), - (int)sizeof(struct mtk_h264_pps_param), - (int)sizeof(struct vdec_h264_slice_lat_dec_param), - (int)sizeof(struct mtk_h264_dpb_info), - vsi_size); - mtk_vcodec_debug(inst, "lat H264 instance >> %p, codec_type = 0x%x", - inst, inst->vpu.codec_type); + mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n", + (int)sizeof(struct mtk_h264_sps_param), + (int)sizeof(struct mtk_h264_pps_param), + (int)sizeof(struct vdec_h264_slice_lat_dec_param), + (int)sizeof(struct mtk_h264_dpb_info), + vsi_size); + mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x", + inst, inst->vpu.codec_type); ctx->drv_handle = inst; return 0; @@ -444,8 +443,6 @@ static void vdec_h264_slice_deinit(void *h_vdec) { struct vdec_h264_slice_inst *inst = h_vdec; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); vdec_h264_slice_free_mv_buf(inst); vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx); @@ -459,21 +456,21 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) u64 vdec_fb_va; u64 y_fb_dma, c_fb_dma; int err, timeout, i; - struct mtk_vcodec_ctx *ctx = lat_buf->ctx; + struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx; struct vdec_h264_slice_inst *inst = ctx->drv_handle; struct vb2_v4l2_buffer *vb2_v4l2; struct vdec_h264_slice_share_info *share_info = lat_buf->private_data; struct mtk_vcodec_mem *mem; struct vdec_vpu_inst *vpu = &inst->vpu; - mtk_vcodec_debug(inst, "[h264-core] vdec_h264 core decode"); + mtk_vdec_debug(ctx, "[h264-core] vdec_h264 core decode"); memcpy(&inst->vsi_core->h264_slice_params, &share_info->h264_slice_params, sizeof(share_info->h264_slice_params)); fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); if (!fb) { err = -EBUSY; - mtk_vcodec_err(inst, "fb buffer is NULL"); + mtk_vdec_err(ctx, "fb buffer is NULL"); goto vdec_dec_end; } @@ -485,8 +482,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) else c_fb_dma = (u64)fb->base_c.dma_addr; - mtk_vcodec_debug(inst, "[h264-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, - c_fb_dma); + mtk_vdec_debug(ctx, "[h264-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, c_fb_dma); inst->vsi_core->dec.y_fb_dma = y_fb_dma; inst->vsi_core->dec.c_fb_dma = c_fb_dma; @@ -516,7 +512,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) err = vpu_dec_core(vpu); if (err) { - mtk_vcodec_err(inst, "core decode err=%d", err); + mtk_vdec_err(ctx, "core decode err=%d", err); goto vdec_dec_end; } @@ -524,27 +520,26 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); if (timeout) - mtk_vcodec_err(inst, "core decode timeout: pic_%d", - ctx->decoded_frame_cnt); + mtk_vdec_err(ctx, "core decode timeout: pic_%d", ctx->decoded_frame_cnt); inst->vsi_core->dec.timeout = !!timeout; vpu_dec_core_end(vpu); - mtk_vcodec_debug(inst, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", - ctx->decoded_frame_cnt, - inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], - inst->vsi_core->dec.crc[2], inst->vsi_core->dec.crc[3], - inst->vsi_core->dec.crc[4], inst->vsi_core->dec.crc[5], - inst->vsi_core->dec.crc[6], inst->vsi_core->dec.crc[7]); + mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ctx->decoded_frame_cnt, + inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], + inst->vsi_core->dec.crc[2], inst->vsi_core->dec.crc[3], + inst->vsi_core->dec.crc[4], inst->vsi_core->dec.crc[5], + inst->vsi_core->dec.crc[6], inst->vsi_core->dec.crc[7]); vdec_dec_end: vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans_end); ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req); - mtk_vcodec_debug(inst, "core decode done err=%d", err); + mtk_vdec_debug(ctx, "core decode done err=%d", err); ctx->decoded_frame_cnt++; return 0; } -static void vdec_h264_insert_startcode(struct mtk_vcodec_dev *vcodec_dev, unsigned char *buf, +static void vdec_h264_insert_startcode(struct mtk_vcodec_dec_dev *vcodec_dev, unsigned char *buf, size_t *bs_size, struct mtk_h264_pps_param *pps) { struct device *dev = &vcodec_dev->plat_dev->dev; @@ -596,7 +591,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_debug(inst, "failed to get lat buffer"); + mtk_vdec_debug(inst->ctx, "failed to get lat buffer"); return -EAGAIN; } share_info = lat_buf->private_data; @@ -625,7 +620,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, *res_chg = inst->resolution_changed; if (inst->resolution_changed) { - mtk_vcodec_debug(inst, "- resolution changed -"); + mtk_vdec_debug(inst->ctx, "- resolution changed -"); if (inst->realloc_mv_buf) { err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); inst->realloc_mv_buf = false; @@ -648,19 +643,19 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->vsi->trans_end = inst->ctx->msg_queue.wdma_rptr_addr; inst->vsi->trans_start = inst->ctx->msg_queue.wdma_wptr_addr; - mtk_vcodec_debug(inst, "lat:trans(0x%llx 0x%llx) err:0x%llx", - inst->vsi->wdma_start_addr, - inst->vsi->wdma_end_addr, - inst->vsi->wdma_err_addr); + mtk_vdec_debug(inst->ctx, "lat:trans(0x%llx 0x%llx) err:0x%llx", + inst->vsi->wdma_start_addr, + inst->vsi->wdma_end_addr, + inst->vsi->wdma_err_addr); - mtk_vcodec_debug(inst, "slice(0x%llx 0x%llx) rprt((0x%llx 0x%llx))", - inst->vsi->slice_bc_start_addr, - inst->vsi->slice_bc_end_addr, - inst->vsi->trans_start, - inst->vsi->trans_end); + mtk_vdec_debug(inst->ctx, "slice(0x%llx 0x%llx) rprt((0x%llx 0x%llx))", + inst->vsi->slice_bc_start_addr, + inst->vsi->slice_bc_end_addr, + inst->vsi->trans_start, + inst->vsi->trans_end); err = vpu_dec_start(vpu, data, 2); if (err) { - mtk_vcodec_debug(inst, "lat decode err: %d", err); + mtk_vdec_debug(inst->ctx, "lat decode err: %d", err); goto err_free_fb_out; } @@ -679,7 +674,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); if (timeout) - mtk_vcodec_err(inst, "lat decode timeout: pic_%d", inst->slice_dec_num); + mtk_vdec_err(inst->ctx, "lat decode timeout: pic_%d", inst->slice_dec_num); inst->vsi->dec.timeout = !!timeout; err = vpu_dec_end(vpu); @@ -687,7 +682,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); inst->slice_dec_num++; - mtk_vcodec_err(inst, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); + mtk_vdec_err(inst->ctx, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); return -EINVAL; } @@ -700,14 +695,14 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, sizeof(share_info->h264_slice_params)); vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); } - mtk_vcodec_debug(inst, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, - inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); + mtk_vdec_debug(inst->ctx, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, + inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); inst->slice_dec_num++; return 0; err_free_fb_out: vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); - mtk_vcodec_err(inst, "slice dec number: %d err: %d", inst->slice_dec_num, err); + mtk_vdec_err(inst->ctx, "slice dec number: %d err: %d", inst->slice_dec_num, err); return err; } @@ -734,8 +729,8 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; - mtk_vcodec_debug(inst, "[h264-dec] [%d] y_dma=%llx c_dma=%llx", - inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma); + mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx", + inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma); inst->vsi_ctx.dec.bs_buf_addr = (u64)bs->dma_addr; inst->vsi_ctx.dec.bs_buf_size = bs->size; @@ -759,7 +754,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs *res_chg = inst->resolution_changed; if (inst->resolution_changed) { - mtk_vcodec_debug(inst, "- resolution changed -"); + mtk_vdec_debug(inst->ctx, "- resolution changed -"); if (inst->realloc_mv_buf) { err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); inst->realloc_mv_buf = false; @@ -783,8 +778,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); if (err) - mtk_vcodec_err(inst, "decode timeout: pic_%d", - inst->ctx->decoded_frame_cnt); + mtk_vdec_err(inst->ctx, "decode timeout: pic_%d", inst->ctx->decoded_frame_cnt); inst->vsi->dec.timeout = !!err; err = vpu_dec_end(vpu); @@ -792,19 +786,18 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs goto err_free_fb_out; memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); - mtk_vcodec_debug(inst, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", - inst->ctx->decoded_frame_cnt, - inst->vsi_ctx.dec.crc[0], inst->vsi_ctx.dec.crc[1], - inst->vsi_ctx.dec.crc[2], inst->vsi_ctx.dec.crc[3], - inst->vsi_ctx.dec.crc[4], inst->vsi_ctx.dec.crc[5], - inst->vsi_ctx.dec.crc[6], inst->vsi_ctx.dec.crc[7]); + mtk_vdec_debug(inst->ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + inst->ctx->decoded_frame_cnt, + inst->vsi_ctx.dec.crc[0], inst->vsi_ctx.dec.crc[1], + inst->vsi_ctx.dec.crc[2], inst->vsi_ctx.dec.crc[3], + inst->vsi_ctx.dec.crc[4], inst->vsi_ctx.dec.crc[5], + inst->vsi_ctx.dec.crc[6], inst->vsi_ctx.dec.crc[7]); inst->ctx->decoded_frame_cnt++; return 0; err_free_fb_out: - mtk_vcodec_err(inst, "dec frame number: %d err: %d", - inst->ctx->decoded_frame_cnt, err); + mtk_vdec_err(inst->ctx, "dec frame number: %d err: %d", inst->ctx->decoded_frame_cnt, err); return err; } @@ -841,7 +834,7 @@ static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type vdec_h264_slice_get_crop_info(inst, out); break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } return 0; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c similarity index 90% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c index 1e6ab138b0bb..06ed47df693b 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c @@ -8,9 +8,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -344,7 +343,7 @@ struct vdec_hevc_slice_share_info { * struct vdec_hevc_slice_inst - hevc decoder instance * * @slice_dec_num: how many picture be decoded - * @ctx: point to mtk_vcodec_ctx + * @ctx: point to mtk_vcodec_dec_ctx * @mv_buf: HW working motion vector buffer * @vpu: VPU instance * @vsi: vsi used for lat @@ -359,7 +358,7 @@ struct vdec_hevc_slice_share_info { */ struct vdec_hevc_slice_inst { unsigned int slice_dec_num; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct mtk_vcodec_mem mv_buf[HEVC_MAX_MV_NUM]; struct vdec_vpu_inst vpu; struct vdec_hevc_slice_vsi *vsi; @@ -380,7 +379,7 @@ static unsigned int vdec_hevc_get_mv_buf_size(unsigned int width, unsigned int h return 64 * unit_size; } -static void *vdec_hevc_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +static void *vdec_hevc_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); @@ -390,7 +389,7 @@ static void *vdec_hevc_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) return ctrl->p_cur.p; } -static void vdec_hevc_fill_dpb_info(struct mtk_vcodec_ctx *ctx, +static void vdec_hevc_fill_dpb_info(struct mtk_vcodec_dec_ctx *ctx, struct slice_api_hevc_decode_param *decode_params, struct mtk_hevc_dpb_info *hevc_dpb_info) { @@ -649,7 +648,7 @@ static int vdec_hevc_slice_alloc_mv_buf(struct vdec_hevc_slice_inst *inst, struct mtk_vcodec_mem *mem; int i, err; - mtk_v4l2_debug(3, "allocate mv buffer size = 0x%x", buf_sz); + mtk_v4l2_vdec_dbg(3, inst->ctx, "allocate mv buffer size = 0x%x", buf_sz); for (i = 0; i < HEVC_MAX_MV_NUM; i++) { mem = &inst->mv_buf[i]; if (mem->va) @@ -657,7 +656,7 @@ static int vdec_hevc_slice_alloc_mv_buf(struct vdec_hevc_slice_inst *inst, mem->size = buf_sz; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); + mtk_vdec_err(inst->ctx, "failed to allocate mv buf"); return err; } } @@ -679,7 +678,7 @@ static void vdec_hevc_slice_free_mv_buf(struct vdec_hevc_slice_inst *inst) static void vdec_hevc_slice_get_pic_info(struct vdec_hevc_slice_inst *inst) { - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; u32 data[3]; data[0] = ctx->picinfo.pic_w; @@ -694,11 +693,11 @@ static void vdec_hevc_slice_get_pic_info(struct vdec_hevc_slice_inst *inst) inst->cap_num_planes = ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->picinfo.buf_w, ctx->picinfo.buf_h); - mtk_vcodec_debug(inst, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], - ctx->picinfo.fb_sz[1]); + mtk_vdec_debug(ctx, "pic(%d, %d), buf(%d, %d)", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + mtk_vdec_debug(ctx, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], + ctx->picinfo.fb_sz[1]); if (ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w || ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h) { @@ -707,12 +706,12 @@ static void vdec_hevc_slice_get_pic_info(struct vdec_hevc_slice_inst *inst) ctx->last_decoded_picinfo.buf_h != ctx->picinfo.buf_h) inst->realloc_mv_buf = true; - mtk_v4l2_debug(1, "resChg: (%d %d) : old(%d, %d) -> new(%d, %d)", - inst->resolution_changed, - inst->realloc_mv_buf, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h); + mtk_v4l2_vdec_dbg(1, inst->ctx, "resChg: (%d %d) : old(%d, %d) -> new(%d, %d)", + inst->resolution_changed, + inst->realloc_mv_buf, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h); } } @@ -724,8 +723,8 @@ static void vdec_hevc_slice_get_crop_info(struct vdec_hevc_slice_inst *inst, cr->width = inst->ctx->picinfo.pic_w; cr->height = inst->ctx->picinfo.pic_h; - mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); } static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst, @@ -747,7 +746,7 @@ static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst, *res_chg = inst->resolution_changed; if (inst->resolution_changed) { - mtk_vcodec_debug(inst, "- resolution changed -"); + mtk_vdec_debug(inst->ctx, "- resolution changed -"); if (inst->realloc_mv_buf) { err = vdec_hevc_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); inst->realloc_mv_buf = false; @@ -779,16 +778,16 @@ static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst, share_info->trans.dma_addr = inst->vsi->trans.dma_addr; share_info->trans.dma_addr_end = inst->vsi->trans.dma_addr_end; - mtk_vcodec_debug(inst, "lat: ube addr/size(0x%llx 0x%llx) err:0x%llx", - inst->vsi->ube.buf, - inst->vsi->ube.padding, - inst->vsi->err_map.buf); + mtk_vdec_debug(inst->ctx, "lat: ube addr/size(0x%llx 0x%llx) err:0x%llx", + inst->vsi->ube.buf, + inst->vsi->ube.padding, + inst->vsi->err_map.buf); - mtk_vcodec_debug(inst, "slice addr/size(0x%llx 0x%llx) trans start/end((0x%llx 0x%llx))", - inst->vsi->slice_bc.buf, - inst->vsi->slice_bc.padding, - inst->vsi->trans.buf, - inst->vsi->trans.padding); + mtk_vdec_debug(inst->ctx, "slice addr/size(0x%llx 0x%llx) trans start/end((0x%llx 0x%llx))", + inst->vsi->slice_bc.buf, + inst->vsi->slice_bc.padding, + inst->vsi->trans.buf, + inst->vsi->trans.padding); return 0; } @@ -798,7 +797,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, struct vdec_lat_buf *lat_buf) { struct mtk_vcodec_mem *mem; - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; struct vb2_v4l2_buffer *vb2_v4l2; struct vdec_fb *fb; u64 y_fb_dma, c_fb_dma; @@ -806,7 +805,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); if (!fb) { - mtk_vcodec_err(inst, "fb buffer is NULL"); + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); return -EBUSY; } @@ -817,8 +816,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, else c_fb_dma = (u64)fb->base_c.dma_addr; - mtk_vcodec_debug(inst, "[hevc-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, - c_fb_dma); + mtk_vdec_debug(inst->ctx, "[hevc-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, c_fb_dma); inst->vsi_core->fb.y.dma_addr = y_fb_dma; inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[0]; @@ -854,7 +852,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, return 0; } -static int vdec_hevc_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_hevc_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_hevc_slice_inst *inst; int err, vsi_size; @@ -874,7 +872,7 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_ctx *ctx) ctx->drv_handle = inst; err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_hevc init err=%d", err); + mtk_vdec_err(ctx, "vdec_hevc init err=%d", err); goto error_free_inst; } @@ -891,14 +889,14 @@ static int vdec_hevc_slice_init(struct mtk_vcodec_ctx *ctx) if (err) goto error_free_inst; - mtk_vcodec_debug(inst, "lat struct size = %d,%d,%d,%d vsi: %d\n", - (int)sizeof(struct mtk_hevc_sps_param), - (int)sizeof(struct mtk_hevc_pps_param), - (int)sizeof(struct vdec_hevc_slice_lat_dec_param), - (int)sizeof(struct mtk_hevc_dpb_info), + mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n", + (int)sizeof(struct mtk_hevc_sps_param), + (int)sizeof(struct mtk_hevc_pps_param), + (int)sizeof(struct vdec_hevc_slice_lat_dec_param), + (int)sizeof(struct mtk_hevc_dpb_info), vsi_size); - mtk_vcodec_debug(inst, "lat hevc instance >> %p, codec_type = 0x%x", - inst, inst->vpu.codec_type); + mtk_vdec_debug(ctx, "lat hevc instance >> %p, codec_type = 0x%x", + inst, inst->vpu.codec_type); return 0; error_free_inst: @@ -911,8 +909,6 @@ static void vdec_hevc_slice_deinit(void *h_vdec) struct vdec_hevc_slice_inst *inst = h_vdec; struct mtk_vcodec_mem *mem; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); vdec_hevc_slice_free_mv_buf(inst); @@ -927,12 +923,12 @@ static void vdec_hevc_slice_deinit(void *h_vdec) static int vdec_hevc_slice_core_decode(struct vdec_lat_buf *lat_buf) { int err, timeout; - struct mtk_vcodec_ctx *ctx = lat_buf->ctx; + struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx; struct vdec_hevc_slice_inst *inst = ctx->drv_handle; struct vdec_hevc_slice_share_info *share_info = lat_buf->private_data; struct vdec_vpu_inst *vpu = &inst->vpu; - mtk_vcodec_debug(inst, "[hevc-core] vdec_hevc core decode"); + mtk_vdec_debug(ctx, "[hevc-core] vdec_hevc core decode"); memcpy(&inst->vsi_core->hevc_slice_params, &share_info->hevc_slice_params, sizeof(share_info->hevc_slice_params)); @@ -944,7 +940,7 @@ static int vdec_hevc_slice_core_decode(struct vdec_lat_buf *lat_buf) share_info); err = vpu_dec_core(vpu); if (err) { - mtk_vcodec_err(inst, "core decode err=%d", err); + mtk_vdec_err(ctx, "core decode err=%d", err); goto vdec_dec_end; } @@ -952,22 +948,21 @@ static int vdec_hevc_slice_core_decode(struct vdec_lat_buf *lat_buf) timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); if (timeout) - mtk_vcodec_err(inst, "core decode timeout: pic_%d", - ctx->decoded_frame_cnt); + mtk_vdec_err(ctx, "core decode timeout: pic_%d", ctx->decoded_frame_cnt); inst->vsi_core->dec.timeout = !!timeout; vpu_dec_core_end(vpu); - mtk_vcodec_debug(inst, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", - ctx->decoded_frame_cnt, - inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], - inst->vsi_core->dec.crc[2], inst->vsi_core->dec.crc[3], - inst->vsi_core->dec.crc[4], inst->vsi_core->dec.crc[5], - inst->vsi_core->dec.crc[6], inst->vsi_core->dec.crc[7]); + mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ctx->decoded_frame_cnt, + inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], + inst->vsi_core->dec.crc[2], inst->vsi_core->dec.crc[3], + inst->vsi_core->dec.crc[4], inst->vsi_core->dec.crc[5], + inst->vsi_core->dec.crc[6], inst->vsi_core->dec.crc[7]); vdec_dec_end: vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans.dma_addr_end); ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req); - mtk_vcodec_debug(inst, "core decode done err=%d", err); + mtk_vdec_debug(ctx, "core decode done err=%d", err); ctx->decoded_frame_cnt++; return 0; } @@ -995,7 +990,7 @@ static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_debug(inst, "failed to get lat buffer"); + mtk_vdec_debug(inst->ctx, "failed to get lat buffer"); return -EAGAIN; } @@ -1010,7 +1005,7 @@ static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, err = vpu_dec_start(vpu, data, 2); if (err) { - mtk_vcodec_debug(inst, "lat decode err: %d", err); + mtk_vdec_debug(inst->ctx, "lat decode err: %d", err); goto err_free_fb_out; } @@ -1024,7 +1019,7 @@ static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); if (timeout) - mtk_vcodec_err(inst, "lat decode timeout: pic_%d", inst->slice_dec_num); + mtk_vdec_err(inst->ctx, "lat decode timeout: pic_%d", inst->slice_dec_num); inst->vsi->dec.timeout = !!timeout; err = vpu_dec_end(vpu); @@ -1032,7 +1027,7 @@ static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); inst->slice_dec_num++; - mtk_vcodec_err(inst, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); + mtk_vdec_err(inst->ctx, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); return -EINVAL; } @@ -1045,14 +1040,14 @@ static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, sizeof(share_info->hevc_slice_params)); vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); } - mtk_vcodec_debug(inst, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, - inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); + mtk_vdec_debug(inst->ctx, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, + inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); inst->slice_dec_num++; return 0; err_free_fb_out: vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); - mtk_vcodec_err(inst, "slice dec number: %d err: %d", inst->slice_dec_num, err); + mtk_vdec_err(inst->ctx, "slice dec number: %d err: %d", inst->slice_dec_num, err); return err; } @@ -1083,7 +1078,7 @@ static int vdec_hevc_slice_get_param(void *h_vdec, enum vdec_get_param_type type vdec_hevc_slice_get_crop_info(inst, out); break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } return 0; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c similarity index 87% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c index 88c046731754..19407f9bc773 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c @@ -7,9 +7,8 @@ #include #include "../vdec_drv_if.h" -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_vpu_if.h" #include "../vdec_drv_base.h" @@ -91,7 +90,6 @@ struct vdec_vp8_vsi { /** * struct vdec_vp8_hw_reg_base - HW register base - * @sys : base address for sys * @misc : base address for misc * @ld : base address for ld * @top : base address for top @@ -100,7 +98,6 @@ struct vdec_vp8_vsi { * @hwb : base address for hwb */ struct vdec_vp8_hw_reg_base { - void __iomem *sys; void __iomem *misc; void __iomem *ld; void __iomem *top; @@ -160,20 +157,21 @@ struct vdec_vp8_inst { struct mtk_vcodec_mem working_buf; struct vdec_vp8_hw_reg_base reg_base; unsigned int frm_cnt; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vpu_inst vpu; struct vdec_vp8_vsi *vsi; }; static void get_hw_reg_base(struct vdec_vp8_inst *inst) { - inst->reg_base.top = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_TOP); - inst->reg_base.cm = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_CM); - inst->reg_base.hwd = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWD); - inst->reg_base.sys = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_SYS); - inst->reg_base.misc = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_MISC); - inst->reg_base.ld = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_LD); - inst->reg_base.hwb = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWB); + void __iomem **reg_base = inst->ctx->dev->reg_base; + + inst->reg_base.top = mtk_vcodec_get_reg_addr(reg_base, VDEC_TOP); + inst->reg_base.cm = mtk_vcodec_get_reg_addr(reg_base, VDEC_CM); + inst->reg_base.hwd = mtk_vcodec_get_reg_addr(reg_base, VDEC_HWD); + inst->reg_base.misc = mtk_vcodec_get_reg_addr(reg_base, VDEC_MISC); + inst->reg_base.ld = mtk_vcodec_get_reg_addr(reg_base, VDEC_LD); + inst->reg_base.hwb = mtk_vcodec_get_reg_addr(reg_base, VDEC_HWB); } static void write_hw_segmentation_data(struct vdec_vp8_inst *inst) @@ -222,17 +220,16 @@ static void read_hw_segmentation_data(struct vdec_vp8_inst *inst) static void enable_hw_rw_function(struct vdec_vp8_inst *inst) { u32 val = 0; - void __iomem *sys = inst->reg_base.sys; void __iomem *misc = inst->reg_base.misc; void __iomem *ld = inst->reg_base.ld; void __iomem *hwb = inst->reg_base.hwb; void __iomem *hwd = inst->reg_base.hwd; - writel(0x1, sys + VP8_RW_CKEN_SET); + mtk_vcodec_write_vdecsys(inst->ctx, VP8_RW_CKEN_SET, 0x1); writel(0x101, ld + VP8_WO_VLD_SRST); writel(0x101, hwb + VP8_WO_VLD_SRST); - writel(1, sys); + mtk_vcodec_write_vdecsys(inst->ctx, 0, 0x1); val = readl(misc + VP8_RW_MISC_SRST); writel((val & 0xFFFFFFFE), misc + VP8_RW_MISC_SRST); @@ -241,7 +238,7 @@ static void enable_hw_rw_function(struct vdec_vp8_inst *inst) writel(0x71201100, misc + VP8_RW_MISC_FUNC_CON); writel(0x0, ld + VP8_WO_VLD_SRST); writel(0x0, hwb + VP8_WO_VLD_SRST); - writel(0x1, sys + VP8_RW_DCM_CON); + mtk_vcodec_write_vdecsys(inst->ctx, VP8_RW_DCM_CON, 0x1); writel(0x1, misc + VP8_RW_MISC_DCM_CON); writel(0x1, hwd + VP8_RW_VP8_CTRL); } @@ -284,10 +281,10 @@ static void get_pic_info(struct vdec_vp8_inst *inst, struct vdec_pic_info *pic) { *pic = inst->vsi->pic; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); - mtk_vcodec_debug(inst, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], pic->fb_sz[1]); + mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", + pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); + mtk_vdec_debug(inst->ctx, "fb size: Y(%d), C(%d)", + pic->fb_sz[0], pic->fb_sz[1]); } static void vp8_dec_finish(struct vdec_vp8_inst *inst) @@ -295,7 +292,7 @@ static void vp8_dec_finish(struct vdec_vp8_inst *inst) struct vdec_fb_node *node; uint64_t prev_y_dma = inst->vsi->dec.prev_y_dma; - mtk_vcodec_debug(inst, "prev fb base dma=%llx", prev_y_dma); + mtk_vdec_debug(inst->ctx, "prev fb base dma=%llx", prev_y_dma); /* put last decode ok frame to fb_free_list */ if (prev_y_dma != 0) { @@ -370,7 +367,7 @@ static int alloc_working_buf(struct vdec_vp8_inst *inst) mem->size = VP8_WORKING_BUF_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "Cannot allocate working buffer"); + mtk_vdec_err(inst->ctx, "Cannot allocate working buffer"); return err; } @@ -388,7 +385,7 @@ static void free_working_buf(struct vdec_vp8_inst *inst) inst->vsi->dec.working_buf_dma = 0; } -static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) +static int vdec_vp8_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_vp8_inst *inst; int err; @@ -404,7 +401,7 @@ static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_vp8 init err=%d", err); + mtk_vdec_err(ctx, "vdec_vp8 init err=%d", err); goto error_free_inst; } @@ -415,7 +412,7 @@ static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) goto error_deinit; get_hw_reg_base(inst); - mtk_vcodec_debug(inst, "VP8 Instance >> %p", inst); + mtk_vdec_debug(ctx, "VP8 Instance >> %p", inst); ctx->drv_handle = inst; return 0; @@ -448,8 +445,8 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; - mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx fb=%p", - inst->frm_cnt, y_fb_dma, c_fb_dma, fb); + mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx fb=%p", + inst->frm_cnt, y_fb_dma, c_fb_dma, fb); inst->cur_fb = fb; dec->bs_dma = (unsigned long)bs->dma_addr; @@ -457,7 +454,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, dec->cur_y_fb_dma = y_fb_dma; dec->cur_c_fb_dma = c_fb_dma; - mtk_vcodec_debug(inst, "\n + FRAME[%d] +\n", inst->frm_cnt); + mtk_vdec_debug(inst->ctx, "\n + FRAME[%d] +\n", inst->frm_cnt); write_hw_segmentation_data(inst); enable_hw_rw_function(inst); @@ -472,7 +469,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (err) { add_fb_to_free_list(inst, fb); if (dec->wait_key_frame) { - mtk_vcodec_debug(inst, "wait key frame !"); + mtk_vdec_debug(inst->ctx, "wait key frame !"); return 0; } @@ -480,7 +477,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, } if (dec->resolution_changed) { - mtk_vcodec_debug(inst, "- resolution_changed -"); + mtk_vdec_debug(inst->ctx, "- resolution_changed -"); *res_chg = true; add_fb_to_free_list(inst, fb); return 0; @@ -500,14 +497,13 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (err) goto error; - mtk_vcodec_debug(inst, "\n - FRAME[%d] - show=%d\n", inst->frm_cnt, - dec->show_frame); + mtk_vdec_debug(inst->ctx, "\n - FRAME[%d] - show=%d\n", inst->frm_cnt, dec->show_frame); inst->frm_cnt++; *res_chg = false; return 0; error: - mtk_vcodec_err(inst, "\n - FRAME[%d] - err=%d\n", inst->frm_cnt, err); + mtk_vdec_err(inst->ctx, "\n - FRAME[%d] - err=%d\n", inst->frm_cnt, err); return err; } @@ -522,11 +518,10 @@ static void get_disp_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) list_move_tail(&node->list, &inst->available_fb_node_list); fb = (struct vdec_fb *)node->fb; fb->status |= FB_ST_DISPLAY; - mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", - node->fb, fb->status); + mtk_vdec_debug(inst->ctx, "[FB] get disp fb %p st=%d", node->fb, fb->status); } else { fb = NULL; - mtk_vcodec_debug(inst, "[FB] there is no disp fb"); + mtk_vdec_debug(inst->ctx, "[FB] there is no disp fb"); } *out_fb = fb; @@ -543,11 +538,10 @@ static void get_free_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) list_move_tail(&node->list, &inst->available_fb_node_list); fb = (struct vdec_fb *)node->fb; fb->status |= FB_ST_FREE; - mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", - node->fb, fb->status); + mtk_vdec_debug(inst->ctx, "[FB] get free fb %p st=%d", node->fb, fb->status); } else { fb = NULL; - mtk_vcodec_debug(inst, "[FB] there is no free fb"); + mtk_vdec_debug(inst->ctx, "[FB] there is no free fb"); } *out_fb = fb; @@ -559,8 +553,8 @@ static void get_crop_info(struct vdec_vp8_inst *inst, struct v4l2_rect *cr) cr->top = 0; cr->width = inst->vsi->pic.pic_w; cr->height = inst->vsi->pic.pic_h; - mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "get crop info l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); } static int vdec_vp8_get_param(void *h_vdec, enum vdec_get_param_type type, @@ -590,7 +584,7 @@ static int vdec_vp8_get_param(void *h_vdec, enum vdec_get_param_type type, break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } @@ -601,8 +595,6 @@ static void vdec_vp8_deinit(void *h_vdec) { struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); free_working_buf(inst); kfree(inst); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c similarity index 81% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c index e1fe2603e92e..f64b21c07169 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c @@ -9,9 +9,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -101,12 +100,12 @@ struct vdec_vp8_slice_inst { struct mtk_vcodec_mem wrap_y_buf; struct mtk_vcodec_mem wrap_c_buf; struct mtk_vcodec_mem vld_wrapper_buf; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vpu_inst vpu; struct vdec_vp8_slice_vsi *vsi; }; -static void *vdec_vp8_slice_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +static void *vdec_vp8_slice_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); @@ -118,7 +117,7 @@ static void *vdec_vp8_slice_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) static void vdec_vp8_slice_get_pic_info(struct vdec_vp8_slice_inst *inst) { - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; unsigned int data[3]; data[0] = ctx->picinfo.pic_w; @@ -137,11 +136,11 @@ static void vdec_vp8_slice_get_pic_info(struct vdec_vp8_slice_inst *inst) inst->vsi->pic.buf_h = ctx->picinfo.buf_h; inst->vsi->pic.fb_sz[0] = ctx->picinfo.fb_sz[0]; inst->vsi->pic.fb_sz[1] = ctx->picinfo.fb_sz[1]; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->picinfo.buf_w, ctx->picinfo.buf_h); - mtk_vcodec_debug(inst, "fb size: Y(%d), C(%d)", - ctx->picinfo.fb_sz[0], ctx->picinfo.fb_sz[1]); + mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + mtk_vdec_debug(inst->ctx, "fb size: Y(%d), C(%d)", + ctx->picinfo.fb_sz[0], ctx->picinfo.fb_sz[1]); } static int vdec_vp8_slice_alloc_working_buf(struct vdec_vp8_slice_inst *inst) @@ -153,7 +152,7 @@ static int vdec_vp8_slice_alloc_working_buf(struct vdec_vp8_slice_inst *inst) mem->size = VP8_SEG_ID_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "Cannot allocate working buffer"); + mtk_vdec_err(inst->ctx, "Cannot allocate working buffer"); return err; } inst->vsi->dec.seg_id_buf_dma = (u64)mem->dma_addr; @@ -162,7 +161,7 @@ static int vdec_vp8_slice_alloc_working_buf(struct vdec_vp8_slice_inst *inst) mem->size = VP8_PP_WRAPY_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "cannot allocate WRAP Y buffer"); + mtk_vdec_err(inst->ctx, "cannot allocate WRAP Y buffer"); return err; } inst->vsi->dec.wrap_y_dma = (u64)mem->dma_addr; @@ -171,7 +170,7 @@ static int vdec_vp8_slice_alloc_working_buf(struct vdec_vp8_slice_inst *inst) mem->size = VP8_PP_WRAPC_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "cannot allocate WRAP C buffer"); + mtk_vdec_err(inst->ctx, "cannot allocate WRAP C buffer"); return err; } inst->vsi->dec.wrap_c_dma = (u64)mem->dma_addr; @@ -180,7 +179,7 @@ static int vdec_vp8_slice_alloc_working_buf(struct vdec_vp8_slice_inst *inst) mem->size = VP8_VLD_PRED_SZ; err = mtk_vcodec_mem_alloc(inst->ctx, mem); if (err) { - mtk_vcodec_err(inst, "cannot allocate vld wrapper buffer"); + mtk_vdec_err(inst->ctx, "cannot allocate vld wrapper buffer"); return err; } inst->vsi->dec.vld_wrapper_dma = (u64)mem->dma_addr; @@ -233,7 +232,7 @@ static u64 vdec_vp8_slice_get_ref_by_ts(const struct v4l2_ctrl_vp8_frame *frame_ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst) { const struct v4l2_ctrl_vp8_frame *frame_header; - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; struct vb2_queue *vq; struct vb2_buffer *vb; u64 referenct_ts; @@ -249,8 +248,8 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst vb = vb2_find_buffer(vq, referenct_ts); if (!vb) { if (!V4L2_VP8_FRAME_IS_KEY_FRAME(frame_header)) - mtk_vcodec_err(inst, "reference invalid: index(%d) ts(%lld)", - index, referenct_ts); + mtk_vdec_err(inst->ctx, "reference invalid: index(%d) ts(%lld)", + index, referenct_ts); inst->vsi->vp8_dpb_info[index].reference_flag = 0; continue; } @@ -272,7 +271,7 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst return 0; } -static int vdec_vp8_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_vp8_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_vp8_slice_inst *inst; int err; @@ -291,7 +290,7 @@ static int vdec_vp8_slice_init(struct mtk_vcodec_ctx *ctx) err = vpu_dec_init(&inst->vpu); if (err) { - mtk_vcodec_err(inst, "vdec_vp8 init err=%d", err); + mtk_vdec_err(ctx, "vdec_vp8 init err=%d", err); goto error_free_inst; } @@ -300,11 +299,11 @@ static int vdec_vp8_slice_init(struct mtk_vcodec_ctx *ctx) if (err) goto error_deinit; - mtk_vcodec_debug(inst, "vp8 struct size = %d vsi: %d\n", - (int)sizeof(struct v4l2_ctrl_vp8_frame), - (int)sizeof(struct vdec_vp8_slice_vsi)); - mtk_vcodec_debug(inst, "vp8:%p, codec_type = 0x%x vsi: 0x%p", - inst, inst->vpu.codec_type, inst->vpu.vsi); + mtk_vdec_debug(ctx, "vp8 struct size = %d vsi: %d\n", + (int)sizeof(struct v4l2_ctrl_vp8_frame), + (int)sizeof(struct vdec_vp8_slice_vsi)); + mtk_vdec_debug(ctx, "vp8:%p, codec_type = 0x%x vsi: 0x%p", + inst, inst->vpu.codec_type, inst->vpu.vsi); ctx->drv_handle = inst; return 0; @@ -350,10 +349,10 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->vsi->dec.cur_y_fb_dma = y_fb_dma; inst->vsi->dec.cur_c_fb_dma = c_fb_dma; - mtk_vcodec_debug(inst, "frame[%d] bs(%zu 0x%llx) y/c(0x%llx 0x%llx)", - inst->ctx->decoded_frame_cnt, - bs->size, (u64)bs->dma_addr, - y_fb_dma, c_fb_dma); + mtk_vdec_debug(inst->ctx, "frame[%d] bs(%zu 0x%llx) y/c(0x%llx 0x%llx)", + inst->ctx->decoded_frame_cnt, + bs->size, (u64)bs->dma_addr, + y_fb_dma, c_fb_dma); v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &dst_buf_info->m2m_buf.vb, true); @@ -364,12 +363,12 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, err = vpu_dec_start(vpu, &data, 1); if (err) { - mtk_vcodec_debug(inst, "vp8 dec start err!"); + mtk_vdec_debug(inst->ctx, "vp8 dec start err!"); goto error; } if (inst->vsi->dec.resolution_changed) { - mtk_vcodec_debug(inst, "- resolution_changed -"); + mtk_vdec_debug(inst->ctx, "- resolution_changed -"); *res_chg = true; return 0; } @@ -380,15 +379,15 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, err = vpu_dec_end(vpu); if (err || timeout) - mtk_vcodec_debug(inst, "vp8 dec error timeout:%d err: %d pic_%d", - timeout, err, inst->ctx->decoded_frame_cnt); + mtk_vdec_debug(inst->ctx, "vp8 dec error timeout:%d err: %d pic_%d", + timeout, err, inst->ctx->decoded_frame_cnt); - mtk_vcodec_debug(inst, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", - inst->ctx->decoded_frame_cnt, - inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], - inst->vsi->dec.crc[2], inst->vsi->dec.crc[3], - inst->vsi->dec.crc[4], inst->vsi->dec.crc[5], - inst->vsi->dec.crc[6], inst->vsi->dec.crc[7]); + mtk_vdec_debug(inst->ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + inst->ctx->decoded_frame_cnt, + inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], + inst->vsi->dec.crc[2], inst->vsi->dec.crc[3], + inst->vsi->dec.crc[4], inst->vsi->dec.crc[5], + inst->vsi->dec.crc[6], inst->vsi->dec.crc[7]); inst->ctx->decoded_frame_cnt++; error: @@ -404,13 +403,13 @@ static int vdec_vp8_slice_get_param(void *h_vdec, enum vdec_get_param_type type, vdec_vp8_slice_get_pic_info(inst); break; case GET_PARAM_CROP_INFO: - mtk_vcodec_debug(inst, "No need to get vp8 crop information."); + mtk_vdec_debug(inst->ctx, "No need to get vp8 crop information."); break; case GET_PARAM_DPB_SIZE: *((unsigned int *)out) = VP8_DPB_SIZE; break; default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + mtk_vdec_err(inst->ctx, "invalid get parameter type=%d", type); return -EINVAL; } @@ -421,8 +420,6 @@ static void vdec_vp8_slice_deinit(void *h_vdec) { struct vdec_vp8_slice_inst *inst = h_vdec; - mtk_vcodec_debug_enter(inst); - vpu_dec_deinit(&inst->vpu); vdec_vp8_slice_free_working_buf(inst); kfree(inst); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c similarity index 87% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c index 70b8383f7c8e..55355fa70090 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c @@ -12,7 +12,7 @@ #include #include -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_vpu_if.h" @@ -196,7 +196,7 @@ struct vdec_vp9_inst { struct list_head fb_free_list; struct list_head fb_disp_list; struct vdec_fb *cur_fb; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vpu_inst vpu; struct vdec_vp9_vsi *vsi; unsigned int total_frm_cnt; @@ -226,10 +226,11 @@ static struct vdec_fb *vp9_rm_from_fb_use_list(struct vdec_vp9_inst if (fb->base_y.va == addr) { list_move_tail(&node->list, &inst->available_fb_node_list); - break; + return fb; } } - return fb; + + return NULL; } static void vp9_add_to_fb_free_list(struct vdec_vp9_inst *inst, @@ -246,7 +247,7 @@ static void vp9_add_to_fb_free_list(struct vdec_vp9_inst *inst, list_move_tail(&node->list, &inst->fb_free_list); } } else { - mtk_vcodec_debug(inst, "No free fb node"); + mtk_vdec_debug(inst->ctx, "No free fb node"); } } @@ -330,7 +331,7 @@ static int vp9_get_sf_ref_fb(struct vdec_vp9_inst *inst) } if (idx == ARRAY_SIZE(vsi->sf_ref_fb)) { - mtk_vcodec_err(inst, "List Full"); + mtk_vdec_err(inst->ctx, "List Full"); return -1; } @@ -339,7 +340,7 @@ static int vp9_get_sf_ref_fb(struct vdec_vp9_inst *inst) vsi->buf_len_sz_y; if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_y)) { - mtk_vcodec_err(inst, "Cannot allocate sf_ref_buf y_buf"); + mtk_vdec_err(inst->ctx, "Cannot allocate sf_ref_buf y_buf"); return -1; } @@ -348,7 +349,7 @@ static int vp9_get_sf_ref_fb(struct vdec_vp9_inst *inst) vsi->buf_len_sz_c; if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_c)) { - mtk_vcodec_err(inst, "Cannot allocate sf_ref_fb c_buf"); + mtk_vdec_err(inst->ctx, "Cannot allocate sf_ref_fb c_buf"); return -1; } vsi->sf_ref_fb[idx].used = 0; @@ -377,17 +378,13 @@ static bool vp9_alloc_work_buf(struct vdec_vp9_inst *inst) if ((vsi->pic_w > max_pic_w) || (vsi->pic_h > max_pic_h)) { - mtk_vcodec_err(inst, "Invalid w/h %d/%d", - vsi->pic_w, vsi->pic_h); + mtk_vdec_err(inst->ctx, "Invalid w/h %d/%d", vsi->pic_w, vsi->pic_h); return false; } - mtk_vcodec_debug(inst, "BUF CHG(%d): w/h/sb_w/sb_h=%d/%d/%d/%d", - vsi->resolution_changed, - vsi->pic_w, - vsi->pic_h, - vsi->buf_w, - vsi->buf_h); + mtk_vdec_debug(inst->ctx, "BUF CHG(%d): w/h/sb_w/sb_h=%d/%d/%d/%d", + vsi->resolution_changed, vsi->pic_w, + vsi->pic_h, vsi->buf_w, vsi->buf_h); mem = &inst->mv_buf; if (mem->va) @@ -398,7 +395,7 @@ static bool vp9_alloc_work_buf(struct vdec_vp9_inst *inst) result = mtk_vcodec_mem_alloc(inst->ctx, mem); if (result) { mem->size = 0; - mtk_vcodec_err(inst, "Cannot allocate mv_buf"); + mtk_vdec_err(inst->ctx, "Cannot allocate mv_buf"); return false; } /* Set the va again */ @@ -415,7 +412,7 @@ static bool vp9_alloc_work_buf(struct vdec_vp9_inst *inst) result = mtk_vcodec_mem_alloc(inst->ctx, mem); if (result) { mem->size = 0; - mtk_vcodec_err(inst, "Cannot allocate seg_id_buf"); + mtk_vdec_err(inst->ctx, "Cannot allocate seg_id_buf"); return false; } /* Set the va again */ @@ -436,7 +433,7 @@ static bool vp9_add_to_fb_disp_list(struct vdec_vp9_inst *inst, struct vdec_fb_node *node; if (!fb) { - mtk_vcodec_err(inst, "fb == NULL"); + mtk_vdec_err(inst->ctx, "fb == NULL"); return false; } @@ -446,7 +443,7 @@ static bool vp9_add_to_fb_disp_list(struct vdec_vp9_inst *inst, node->fb = fb; list_move_tail(&node->list, &inst->fb_disp_list); } else { - mtk_vcodec_err(inst, "No available fb node"); + mtk_vdec_err(inst->ctx, "No available fb node"); return false; } @@ -492,10 +489,10 @@ static void vp9_swap_frm_bufs(struct vdec_vp9_inst *inst) * size */ if (frm_to_show->fb != NULL) - mtk_vcodec_err(inst, - "inst->cur_fb->base_y.size=%zu, frm_to_show->fb.base_y.size=%zu", - inst->cur_fb->base_y.size, - frm_to_show->fb->base_y.size); + mtk_vdec_err(inst->ctx, + "base_y.size=%zu, frm_to_show: base_y.size=%zu", + inst->cur_fb->base_y.size, + frm_to_show->fb->base_y.size); } if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { if (vsi->show_frame & BIT(0)) @@ -535,7 +532,7 @@ static void vp9_swap_frm_bufs(struct vdec_vp9_inst *inst) static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) { - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, @@ -547,7 +544,7 @@ static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) return false; } -static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_ctx *ctx) +static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_dec_ctx *ctx) { int result; struct mtk_vcodec_mem mem; @@ -582,20 +579,19 @@ static bool vp9_decode_end_proc(struct vdec_vp9_inst *inst) if (!vsi->show_existing_frame) { ret = vp9_wait_dec_end(inst); if (!ret) { - mtk_vcodec_err(inst, "Decode failed, Decode Timeout @[%d]", - vsi->frm_num); + mtk_vdec_err(inst->ctx, "Decode failed, Decode Timeout @[%d]", + vsi->frm_num); return false; } if (vpu_dec_end(&inst->vpu)) { - mtk_vcodec_err(inst, "vp9_dec_vpu_end failed"); + mtk_vdec_err(inst->ctx, "vp9_dec_vpu_end failed"); return false; } - mtk_vcodec_debug(inst, "Decode Ok @%d (%d/%d)", vsi->frm_num, - vsi->pic_w, vsi->pic_h); + mtk_vdec_debug(inst->ctx, "Decode Ok @%d (%d/%d)", vsi->frm_num, + vsi->pic_w, vsi->pic_h); } else { - mtk_vcodec_debug(inst, "Decode Ok @%d (show_existing_frame)", - vsi->frm_num); + mtk_vdec_debug(inst->ctx, "Decode Ok @%d (show_existing_frame)", vsi->frm_num); } vp9_swap_frm_bufs(inst); @@ -624,10 +620,9 @@ static struct vdec_fb *vp9_rm_from_fb_disp_list(struct vdec_vp9_inst *inst) fb = (struct vdec_fb *)node->fb; fb->status |= FB_ST_DISPLAY; list_move_tail(&node->list, &inst->available_fb_node_list); - mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", - node->fb, fb->status); + mtk_vdec_debug(inst->ctx, "[FB] get disp fb %p st=%d", node->fb, fb->status); } else - mtk_vcodec_debug(inst, "[FB] there is no disp fb"); + mtk_vdec_debug(inst->ctx, "[FB] there is no disp fb"); return fb; } @@ -638,7 +633,7 @@ static bool vp9_add_to_fb_use_list(struct vdec_vp9_inst *inst, struct vdec_fb_node *node; if (!fb) { - mtk_vcodec_debug(inst, "fb == NULL"); + mtk_vdec_debug(inst->ctx, "fb == NULL"); return false; } @@ -648,7 +643,7 @@ static bool vp9_add_to_fb_use_list(struct vdec_vp9_inst *inst, node->fb = fb; list_move_tail(&node->list, &inst->fb_use_list); } else { - mtk_vcodec_err(inst, "No free fb node"); + mtk_vdec_err(inst->ctx, "No free fb node"); return false; } return true; @@ -665,7 +660,7 @@ static void vp9_reset(struct vdec_vp9_inst *inst) inst->vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); if (vpu_dec_reset(&inst->vpu)) - mtk_vcodec_err(inst, "vp9_dec_vpu_reset failed"); + mtk_vdec_err(inst->ctx, "vp9_dec_vpu_reset failed"); /* Set the va again, since vpu_dec_reset will clear mv_buf in vpu */ inst->vsi->mv_buf.va = (unsigned long)inst->mv_buf.va; @@ -706,11 +701,9 @@ static void get_pic_info(struct vdec_vp9_inst *inst, struct vdec_pic_info *pic) pic->buf_w = inst->vsi->buf_w; pic->buf_h = inst->vsi->buf_h; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); - mtk_vcodec_debug(inst, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], - pic->fb_sz[1]); + mtk_vdec_debug(inst->ctx, "pic(%d, %d), buf(%d, %d)", + pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h); + mtk_vdec_debug(inst->ctx, "fb size: Y(%d), C(%d)", pic->fb_sz[0], pic->fb_sz[1]); } static void get_disp_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) @@ -732,10 +725,9 @@ static void get_free_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) list_move_tail(&node->list, &inst->available_fb_node_list); fb = (struct vdec_fb *)node->fb; fb->status |= FB_ST_FREE; - mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", - node->fb, fb->status); + mtk_vdec_debug(inst->ctx, "[FB] get free fb %p st=%d", node->fb, fb->status); } else { - mtk_vcodec_debug(inst, "[FB] there is no free fb"); + mtk_vdec_debug(inst->ctx, "[FB] there is no free fb"); } *out_fb = fb; @@ -744,18 +736,15 @@ static void get_free_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) static int validate_vsi_array_indexes(struct vdec_vp9_inst *inst, struct vdec_vp9_vsi *vsi) { if (vsi->sf_frm_idx >= VP9_MAX_FRM_BUF_NUM - 1) { - mtk_vcodec_err(inst, "Invalid vsi->sf_frm_idx=%u.", - vsi->sf_frm_idx); + mtk_vdec_err(inst->ctx, "Invalid vsi->sf_frm_idx=%u.", vsi->sf_frm_idx); return -EIO; } if (vsi->frm_to_show_idx >= VP9_MAX_FRM_BUF_NUM) { - mtk_vcodec_err(inst, "Invalid vsi->frm_to_show_idx=%u.", - vsi->frm_to_show_idx); + mtk_vdec_err(inst->ctx, "Invalid vsi->frm_to_show_idx=%u.", vsi->frm_to_show_idx); return -EIO; } if (vsi->new_fb_idx >= VP9_MAX_FRM_BUF_NUM) { - mtk_vcodec_err(inst, "Invalid vsi->new_fb_idx=%u.", - vsi->new_fb_idx); + mtk_vdec_err(inst->ctx, "Invalid vsi->new_fb_idx=%u.", vsi->new_fb_idx); return -EIO; } return 0; @@ -769,7 +758,7 @@ static void vdec_vp9_deinit(void *h_vdec) ret = vpu_dec_deinit(&inst->vpu); if (ret) - mtk_vcodec_err(inst, "vpu_dec_deinit failed"); + mtk_vdec_err(inst->ctx, "vpu_dec_deinit failed"); mem = &inst->mv_buf; if (mem->va) @@ -783,7 +772,7 @@ static void vdec_vp9_deinit(void *h_vdec) vp9_free_inst(inst); } -static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) +static int vdec_vp9_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_vp9_inst *inst; @@ -798,7 +787,7 @@ static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) inst->vpu.ctx = ctx; if (vpu_dec_init(&inst->vpu)) { - mtk_vcodec_err(inst, "vp9_dec_vpu_init failed"); + mtk_vdec_err(inst->ctx, "vp9_dec_vpu_init failed"); goto err_deinit_inst; } @@ -829,17 +818,17 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, *res_chg = false; if ((bs == NULL) && (fb == NULL)) { - mtk_vcodec_debug(inst, "[EOS]"); + mtk_vdec_debug(inst->ctx, "[EOS]"); vp9_reset(inst); return ret; } if (bs == NULL) { - mtk_vcodec_err(inst, "bs == NULL"); + mtk_vdec_err(inst->ctx, "bs == NULL"); return -EINVAL; } - mtk_vcodec_debug(inst, "Input BS Size = %zu", bs->size); + mtk_vdec_debug(inst->ctx, "Input BS Size = %zu", bs->size); while (1) { struct vdec_fb *cur_fb = NULL; @@ -882,7 +871,7 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, ret = vpu_dec_start(&inst->vpu, data, 3); if (ret) { - mtk_vcodec_err(inst, "vpu_dec_start failed"); + mtk_vdec_err(inst->ctx, "vpu_dec_start failed"); goto DECODE_ERROR; } @@ -892,7 +881,7 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (vsi->show_frame & BIT(2)) { ret = vpu_dec_start(&inst->vpu, NULL, 0); if (ret) { - mtk_vcodec_err(inst, "vpu trig decoder failed"); + mtk_vdec_err(inst->ctx, "vpu trig decoder failed"); goto DECODE_ERROR; } } @@ -900,7 +889,7 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, ret = validate_vsi_array_indexes(inst, vsi); if (ret) { - mtk_vcodec_err(inst, "Invalid values from VPU."); + mtk_vdec_err(inst->ctx, "Invalid values from VPU."); goto DECODE_ERROR; } @@ -926,18 +915,18 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) vp9_add_to_fb_use_list(inst, inst->cur_fb); - mtk_vcodec_debug(inst, "[#pic %d]", vsi->frm_num); + mtk_vdec_debug(inst->ctx, "[#pic %d]", vsi->frm_num); if (vsi->show_existing_frame) - mtk_vcodec_debug(inst, - "drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", - vsi->new_fb_idx, vsi->frm_to_show_idx); + mtk_vdec_debug(inst->ctx, + "drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", + vsi->new_fb_idx, vsi->frm_to_show_idx); if (vsi->show_existing_frame && (vsi->frm_to_show_idx < VP9_MAX_FRM_BUF_NUM)) { - mtk_vcodec_debug(inst, - "Skip Decode drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", - vsi->new_fb_idx, vsi->frm_to_show_idx); + mtk_vdec_debug(inst->ctx, + "Skip Decode drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", + vsi->new_fb_idx, vsi->frm_to_show_idx); vp9_ref_cnt_fb(inst, &vsi->new_fb_idx, vsi->frm_to_show_idx); @@ -954,14 +943,14 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (vsi->resolution_changed) { *res_chg = true; - mtk_vcodec_debug(inst, "VDEC_ST_RESOLUTION_CHANGED"); + mtk_vdec_debug(inst->ctx, "VDEC_ST_RESOLUTION_CHANGED"); ret = 0; goto DECODE_ERROR; } if (!vp9_decode_end_proc(inst)) { - mtk_vcodec_err(inst, "vp9_decode_end_proc"); + mtk_vdec_err(inst->ctx, "vp9_decode_end_proc"); ret = -EINVAL; goto DECODE_ERROR; } @@ -985,8 +974,8 @@ static void get_crop_info(struct vdec_vp9_inst *inst, struct v4l2_rect *cr) cr->top = 0; cr->width = inst->vsi->pic_w; cr->height = inst->vsi->pic_h; - mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d\n", - cr->left, cr->top, cr->width, cr->height); + mtk_vdec_debug(inst->ctx, "get crop info l=%d, t=%d, w=%d, h=%d\n", + cr->left, cr->top, cr->width, cr->height); } static int vdec_vp9_get_param(void *h_vdec, enum vdec_get_param_type type, @@ -1012,7 +1001,7 @@ static int vdec_vp9_get_param(void *h_vdec, enum vdec_get_param_type type, get_crop_info(inst, out); break; default: - mtk_vcodec_err(inst, "not supported param type %d", type); + mtk_vdec_err(inst->ctx, "not supported param type %d", type); ret = -EINVAL; break; } diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c similarity index 94% rename from drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index c2f90848f498..e393e3e668f8 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -9,9 +9,8 @@ #include #include -#include "../mtk_vcodec_util.h" #include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" +#include "../../common/mtk_vcodec_intr.h" #include "../vdec_drv_base.h" #include "../vdec_drv_if.h" #include "../vdec_vpu_if.h" @@ -445,7 +444,7 @@ struct vdec_vp9_slice_ref { * @counts_helper: counts table according to newest kernel spec */ struct vdec_vp9_slice_instance { - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vpu_inst vpu; int seq; @@ -506,7 +505,7 @@ static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance { struct vdec_vp9_slice_frame_ctx *remote_frame_ctx; struct vdec_vp9_slice_frame_ctx *frame_ctx; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; struct vdec_vp9_slice_init_vsi *vsi; int ret = 0; @@ -518,7 +517,7 @@ static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance remote_frame_ctx = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->default_frame_ctx); if (!remote_frame_ctx) { - mtk_vcodec_err(instance, "failed to map default frame ctx\n"); + mtk_vdec_err(ctx, "failed to map default frame ctx\n"); return -EINVAL; } @@ -543,7 +542,7 @@ out: static int vdec_vp9_slice_alloc_working_buffer(struct vdec_vp9_slice_instance *instance, struct vdec_vp9_slice_vsi *vsi) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; enum vdec_vp9_slice_resolution_level level; /* super blocks */ unsigned int max_sb_w; @@ -577,8 +576,8 @@ static int vdec_vp9_slice_alloc_working_buffer(struct vdec_vp9_slice_instance *i if (level == instance->level) return 0; - mtk_vcodec_debug(instance, "resolution level changed, from %u to %u, %ux%u", - instance->level, level, w, h); + mtk_vdec_debug(ctx, "resolution level changed, from %u to %u, %ux%u", + instance->level, level, w, h); max_sb_w = DIV_ROUND_UP(max_w, 64); max_sb_h = DIV_ROUND_UP(max_h, 64); @@ -635,7 +634,7 @@ err: static void vdec_vp9_slice_free_working_buffer(struct vdec_vp9_slice_instance *instance) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; int i; for (i = 0; i < ARRAY_SIZE(instance->mv); i++) { @@ -1025,9 +1024,9 @@ static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *inst uh = &vsi->frame.uh; - mtk_vcodec_debug(instance, "ctx dirty %u idx %d\n", - instance->dirty[uh->frame_context_idx], - uh->frame_context_idx); + mtk_vdec_debug(instance->ctx, "ctx dirty %u idx %d\n", + instance->dirty[uh->frame_context_idx], + uh->frame_context_idx); if (instance->dirty[uh->frame_context_idx]) frame_ctx = &instance->frame_ctx[uh->frame_context_idx]; @@ -1051,7 +1050,7 @@ static void vdec_vp9_slice_setup_seg_buffer(struct vdec_vp9_slice_instance *inst uh->error_resilient_mode || uh->frame_width != instance->width || uh->frame_height != instance->height) { - mtk_vcodec_debug(instance, "reset seg\n"); + mtk_vdec_debug(instance->ctx, "reset seg\n"); memset(buf->va, 0, buf->size); } } @@ -1093,16 +1092,14 @@ static int vdec_vp9_slice_setup_tile_buffer(struct vdec_vp9_slice_instance *inst cols = 1 << cols_log2; if (rows > 4 || cols > 64) { - mtk_vcodec_err(instance, "tile_rows %u tile_cols %u\n", - rows, cols); + mtk_vdec_err(instance->ctx, "tile_rows %u tile_cols %u\n", rows, cols); return -EINVAL; } offset = uh->uncompressed_header_size + uh->header_size_in_bytes; if (bs->size <= offset) { - mtk_vcodec_err(instance, "bs size %zu tile offset %u\n", - bs->size, offset); + mtk_vdec_err(instance->ctx, "bs size %zu tile offset %u\n", bs->size, offset); return -EINVAL; } @@ -1596,14 +1593,12 @@ static int vdec_vp9_slice_update_single(struct vdec_vp9_slice_instance *instance vsi = &pfc->vsi; memcpy(&pfc->state[0], &vsi->state, sizeof(vsi->state)); - mtk_vcodec_debug(instance, "Frame %u Y_CRC %08x %08x %08x %08x\n", - pfc->seq, - vsi->state.crc[0], vsi->state.crc[1], - vsi->state.crc[2], vsi->state.crc[3]); - mtk_vcodec_debug(instance, "Frame %u C_CRC %08x %08x %08x %08x\n", - pfc->seq, - vsi->state.crc[4], vsi->state.crc[5], - vsi->state.crc[6], vsi->state.crc[7]); + mtk_vdec_debug(instance->ctx, "Frame %u Y_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[0], vsi->state.crc[1], + vsi->state.crc[2], vsi->state.crc[3]); + mtk_vdec_debug(instance->ctx, "Frame %u C_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[4], vsi->state.crc[5], + vsi->state.crc[6], vsi->state.crc[7]); vdec_vp9_slice_update_prob(instance, vsi); @@ -1624,10 +1619,10 @@ static int vdec_vp9_slice_update_lat(struct vdec_vp9_slice_instance *instance, vsi = &pfc->vsi; memcpy(&pfc->state[0], &vsi->state, sizeof(vsi->state)); - mtk_vcodec_debug(instance, "Frame %u LAT CRC 0x%08x %lx %lx\n", - pfc->seq, vsi->state.crc[0], - (unsigned long)vsi->trans.dma_addr, - (unsigned long)vsi->trans.dma_addr_end); + mtk_vdec_debug(instance->ctx, "Frame %u LAT CRC 0x%08x %lx %lx\n", + pfc->seq, vsi->state.crc[0], + (unsigned long)vsi->trans.dma_addr, + (unsigned long)vsi->trans.dma_addr_end); /* buffer full, need to re-decode */ if (vsi->state.full) { @@ -1844,19 +1839,17 @@ static int vdec_vp9_slice_update_core(struct vdec_vp9_slice_instance *instance, vsi = &pfc->vsi; memcpy(&pfc->state[1], &vsi->state, sizeof(vsi->state)); - mtk_vcodec_debug(instance, "Frame %u Y_CRC %08x %08x %08x %08x\n", - pfc->seq, - vsi->state.crc[0], vsi->state.crc[1], - vsi->state.crc[2], vsi->state.crc[3]); - mtk_vcodec_debug(instance, "Frame %u C_CRC %08x %08x %08x %08x\n", - pfc->seq, - vsi->state.crc[4], vsi->state.crc[5], - vsi->state.crc[6], vsi->state.crc[7]); + mtk_vdec_debug(instance->ctx, "Frame %u Y_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[0], vsi->state.crc[1], + vsi->state.crc[2], vsi->state.crc[3]); + mtk_vdec_debug(instance->ctx, "Frame %u C_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[4], vsi->state.crc[5], + vsi->state.crc[6], vsi->state.crc[7]); return 0; } -static int vdec_vp9_slice_init(struct mtk_vcodec_ctx *ctx) +static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx) { struct vdec_vp9_slice_instance *instance; struct vdec_vp9_slice_init_vsi *vsi; @@ -1874,7 +1867,7 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_ctx *ctx) ret = vpu_dec_init(&instance->vpu); if (ret) { - mtk_vcodec_err(instance, "failed to init vpu dec, ret %d\n", ret); + mtk_vdec_err(ctx, "failed to init vpu dec, ret %d\n", ret); goto error_vpu_init; } @@ -1882,7 +1875,7 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_ctx *ctx) vsi = instance->vpu.vsi; if (!vsi) { - mtk_vcodec_err(instance, "failed to get VP9 vsi\n"); + mtk_vdec_err(ctx, "failed to get VP9 vsi\n"); ret = -EINVAL; goto error_vsi; } @@ -1890,7 +1883,7 @@ static int vdec_vp9_slice_init(struct mtk_vcodec_ctx *ctx) instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->core_vsi); if (!instance->core_vsi) { - mtk_vcodec_err(instance, "failed to get VP9 core vsi\n"); + mtk_vdec_err(ctx, "failed to get VP9 core vsi\n"); ret = -EINVAL; goto error_vsi; } @@ -1931,7 +1924,7 @@ static int vdec_vp9_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, { struct vdec_vp9_slice_instance *instance = h_vdec; - mtk_vcodec_debug(instance, "flush ...\n"); + mtk_vdec_debug(instance->ctx, "flush ...\n"); if (instance->ctx->dev->vdec_pdata->hw_arch != MTK_VDEC_PURE_SINGLE_CORE) vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue); return vpu_dec_reset(&instance->vpu); @@ -1939,11 +1932,10 @@ static int vdec_vp9_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, static void vdec_vp9_slice_get_pic_info(struct vdec_vp9_slice_instance *instance) { - struct mtk_vcodec_ctx *ctx = instance->ctx; + struct mtk_vcodec_dec_ctx *ctx = instance->ctx; unsigned int data[3]; - mtk_vcodec_debug(instance, "w %u h %u\n", - ctx->picinfo.pic_w, ctx->picinfo.pic_h); + mtk_vdec_debug(instance->ctx, "w %u h %u\n", ctx->picinfo.pic_w, ctx->picinfo.pic_h); data[0] = ctx->picinfo.pic_w; data[1] = ctx->picinfo.pic_h; @@ -1975,11 +1967,10 @@ static int vdec_vp9_slice_get_param(void *h_vdec, enum vdec_get_param_type type, vdec_vp9_slice_get_dpb_size(instance, out); break; case GET_PARAM_CROP_INFO: - mtk_vcodec_debug(instance, "No need to get vp9 crop information."); + mtk_vdec_debug(instance->ctx, "No need to get vp9 crop information."); break; default: - mtk_vcodec_err(instance, "invalid get parameter type=%d\n", - type); + mtk_vdec_err(instance->ctx, "invalid get parameter type=%d\n", type); return -EINVAL; } @@ -1992,7 +1983,7 @@ static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_vp9_slice_instance *instance = h_vdec; struct vdec_vp9_slice_pfc *pfc = &instance->sc_pfc; struct vdec_vp9_slice_vsi *vsi; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; int ret; if (!instance || !instance->ctx) @@ -2011,14 +2002,14 @@ static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, ret = vdec_vp9_slice_setup_single(instance, bs, fb, pfc); if (ret) { - mtk_vcodec_err(instance, "Failed to setup VP9 single ret %d\n", ret); + mtk_vdec_err(ctx, "Failed to setup VP9 single ret %d\n", ret); return ret; } vdec_vp9_slice_vsi_to_remote(vsi, instance->vsi); ret = vpu_dec_start(&instance->vpu, NULL, 0); if (ret) { - mtk_vcodec_err(instance, "Failed to dec VP9 ret %d\n", ret); + mtk_vdec_err(ctx, "Failed to dec VP9 ret %d\n", ret); return ret; } @@ -2026,7 +2017,7 @@ static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); /* update remote vsi if decode timeout */ if (ret) { - mtk_vcodec_err(instance, "VP9 decode timeout %d\n", ret); + mtk_vdec_err(ctx, "VP9 decode timeout %d\n", ret); WRITE_ONCE(instance->vsi->state.timeout, 1); } @@ -2035,7 +2026,7 @@ static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, vdec_vp9_slice_vsi_from_remote(vsi, instance->vsi, 0); ret = vdec_vp9_slice_update_single(instance, pfc); if (ret) { - mtk_vcodec_err(instance, "VP9 decode error: %d\n", ret); + mtk_vdec_err(ctx, "VP9 decode error: %d\n", ret); return ret; } @@ -2050,7 +2041,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_lat_buf *lat_buf; struct vdec_vp9_slice_pfc *pfc; struct vdec_vp9_slice_vsi *vsi; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; int ret; if (!instance || !instance->ctx) @@ -2069,7 +2060,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&instance->ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_debug(instance, "Failed to get VP9 lat buf\n"); + mtk_vdec_debug(ctx, "Failed to get VP9 lat buf\n"); return -EAGAIN; } pfc = (struct vdec_vp9_slice_pfc *)lat_buf->private_data; @@ -2081,14 +2072,14 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, ret = vdec_vp9_slice_setup_lat(instance, bs, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "Failed to setup VP9 lat ret %d\n", ret); + mtk_vdec_err(ctx, "Failed to setup VP9 lat ret %d\n", ret); goto err_free_fb_out; } vdec_vp9_slice_vsi_to_remote(vsi, instance->vsi); ret = vpu_dec_start(&instance->vpu, NULL, 0); if (ret) { - mtk_vcodec_err(instance, "Failed to dec VP9 ret %d\n", ret); + mtk_vdec_err(ctx, "Failed to dec VP9 ret %d\n", ret); goto err_free_fb_out; } @@ -2097,7 +2088,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); /* update remote vsi if decode timeout */ if (ret) { - mtk_vcodec_err(instance, "VP9 decode timeout %d pic %d\n", ret, pfc->seq); + mtk_vdec_err(ctx, "VP9 decode timeout %d pic %d\n", ret, pfc->seq); WRITE_ONCE(instance->vsi->state.timeout, 1); } vpu_dec_end(&instance->vpu); @@ -2108,13 +2099,13 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, /* LAT trans full, no more UBE or decode timeout */ if (ret) { - mtk_vcodec_err(instance, "VP9 decode error: %d\n", ret); + mtk_vdec_err(ctx, "VP9 decode error: %d\n", ret); goto err_free_fb_out; } - mtk_vcodec_debug(instance, "lat dma addr: 0x%lx 0x%lx\n", - (unsigned long)pfc->vsi.trans.dma_addr, - (unsigned long)pfc->vsi.trans.dma_addr_end); + mtk_vdec_debug(ctx, "lat dma addr: 0x%lx 0x%lx\n", + (unsigned long)pfc->vsi.trans.dma_addr, + (unsigned long)pfc->vsi.trans.dma_addr_end); vdec_msg_queue_update_ube_wptr(&ctx->msg_queue, vsi->trans.dma_addr_end + @@ -2145,7 +2136,7 @@ static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf) { struct vdec_vp9_slice_instance *instance; struct vdec_vp9_slice_pfc *pfc; - struct mtk_vcodec_ctx *ctx = NULL; + struct mtk_vcodec_dec_ctx *ctx = NULL; struct vdec_fb *fb = NULL; int ret = -EINVAL; @@ -2169,14 +2160,14 @@ static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf) ret = vdec_vp9_slice_setup_core(instance, fb, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "vdec_vp9_slice_setup_core\n"); + mtk_vdec_err(ctx, "vdec_vp9_slice_setup_core\n"); goto err; } vdec_vp9_slice_vsi_to_remote(&pfc->vsi, instance->core_vsi); ret = vpu_dec_core(&instance->vpu); if (ret) { - mtk_vcodec_err(instance, "vpu_dec_core\n"); + mtk_vdec_err(ctx, "vpu_dec_core\n"); goto err; } @@ -2185,7 +2176,7 @@ static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf) WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); /* update remote vsi if decode timeout */ if (ret) { - mtk_vcodec_err(instance, "VP9 core timeout pic %d\n", pfc->seq); + mtk_vdec_err(ctx, "VP9 core timeout pic %d\n", pfc->seq); WRITE_ONCE(instance->core_vsi->state.timeout, 1); } vpu_dec_core_end(&instance->vpu); @@ -2194,13 +2185,13 @@ static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf) vdec_vp9_slice_vsi_from_remote(&pfc->vsi, instance->core_vsi, 1); ret = vdec_vp9_slice_update_core(instance, lat_buf, pfc); if (ret) { - mtk_vcodec_err(instance, "vdec_vp9_slice_update_core\n"); + mtk_vdec_err(ctx, "vdec_vp9_slice_update_core\n"); goto err; } pfc->vsi.trans.dma_addr_end += ctx->msg_queue.wdma_addr.dma_addr; - mtk_vcodec_debug(instance, "core dma_addr_end 0x%lx\n", - (unsigned long)pfc->vsi.trans.dma_addr_end); + mtk_vdec_debug(ctx, "core dma_addr_end 0x%lx\n", + (unsigned long)pfc->vsi.trans.dma_addr_end); vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, pfc->vsi.trans.dma_addr_end); ctx->dev->vdec_pdata->cap_to_disp(ctx, 0, lat_buf->src_buf_req); diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_base.h similarity index 95% rename from drivers/media/platform/mediatek/vcodec/vdec_drv_base.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_base.h index e913f963b7db..f6abb9365234 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_base.h @@ -15,7 +15,7 @@ struct vdec_common_if { * @ctx : [in] mtk v4l2 context * @h_vdec : [out] driver handle */ - int (*init)(struct mtk_vcodec_ctx *ctx); + int (*init)(struct mtk_vcodec_dec_ctx *ctx); /** * (*decode)() - trigger decode diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.c similarity index 86% rename from drivers/media/platform/mediatek/vcodec/vdec_drv_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.c index 06d393174cc2..d0b459b1603f 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.c @@ -14,7 +14,7 @@ #include "vdec_drv_base.h" #include "mtk_vcodec_dec_pm.h" -int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +int vdec_if_init(struct mtk_vcodec_dec_ctx *ctx, unsigned int fourcc) { enum mtk_vdec_hw_arch hw_arch = ctx->dev->vdec_pdata->hw_arch; int ret = 0; @@ -68,14 +68,14 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) return ret; } -int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, +int vdec_if_decode(struct mtk_vcodec_dec_ctx *ctx, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { int ret = 0; if (bs) { if ((bs->dma_addr & 63) != 0) { - mtk_v4l2_err("bs dma_addr should 64 byte align"); + mtk_v4l2_vdec_err(ctx, "bs dma_addr should 64 byte align"); return -EINVAL; } } @@ -83,7 +83,7 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, if (fb) { if (((fb->base_y.dma_addr & 511) != 0) || ((fb->base_c.dma_addr & 511) != 0)) { - mtk_v4l2_err("frame buffer dma_addr should 512 byte align"); + mtk_v4l2_vdec_err(ctx, "frame buffer dma_addr should 512 byte align"); return -EINVAL; } } @@ -100,7 +100,7 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, return ret; } -int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, +int vdec_if_get_param(struct mtk_vcodec_dec_ctx *ctx, enum vdec_get_param_type type, void *out) { int ret = 0; @@ -115,7 +115,7 @@ int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, return ret; } -void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) +void vdec_if_deinit(struct mtk_vcodec_dec_ctx *ctx) { if (!ctx->drv_handle) return; diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.h similarity index 89% rename from drivers/media/platform/mediatek/vcodec/vdec_drv_if.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.h index a8da6a59a6a5..bfd297c96850 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.h @@ -8,9 +8,7 @@ #ifndef _VDEC_DRV_IF_H_ #define _VDEC_DRV_IF_H_ -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_dec.h" -#include "mtk_vcodec_util.h" /** @@ -69,14 +67,14 @@ extern const struct vdec_common_if vdec_av1_slice_lat_if; * @ctx : [in] v4l2 context * @fourcc : [in] video format fourcc, V4L2_PIX_FMT_H264/VP8/VP9.. */ -int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); +int vdec_if_init(struct mtk_vcodec_dec_ctx *ctx, unsigned int fourcc); /** * vdec_if_deinit() - deinitialize decode driver * @ctx : [in] v4l2 context * */ -void vdec_if_deinit(struct mtk_vcodec_ctx *ctx); +void vdec_if_deinit(struct mtk_vcodec_dec_ctx *ctx); /** * vdec_if_decode() - trigger decode @@ -90,7 +88,7 @@ void vdec_if_deinit(struct mtk_vcodec_ctx *ctx); * * Return: 0 on success. -EIO on unrecoverable error. */ -int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, +int vdec_if_decode(struct mtk_vcodec_dec_ctx *ctx, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg); /** @@ -99,7 +97,7 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, * @type : [in] input parameter type * @out : [out] buffer to store query result */ -int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, +int vdec_if_get_param(struct mtk_vcodec_dec_ctx *ctx, enum vdec_get_param_type type, void *out); #endif diff --git a/drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h similarity index 100% rename from drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.c similarity index 81% rename from drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.c index 04e6dc6cfa1d..f283c4703dc6 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.c @@ -8,8 +8,8 @@ #include #include +#include "mtk_vcodec_dec_drv.h" #include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_drv.h" #include "vdec_msg_queue.h" #define VDEC_MSG_QUEUE_TIMEOUT_MS 1500 @@ -77,7 +77,7 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf head = vdec_get_buf_list(msg_ctx->hardware_index, buf); if (!head) { - mtk_v4l2_err("fail to qbuf: %d", msg_ctx->hardware_index); + mtk_v4l2_vdec_err(buf->ctx, "fail to qbuf: %d", msg_ctx->hardware_index); return -EINVAL; } @@ -95,8 +95,8 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf } } - mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d", - msg_ctx->hardware_index, buf, msg_ctx->ready_num); + mtk_v4l2_vdec_dbg(3, buf->ctx, "enqueue buf type: %d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); spin_unlock(&msg_ctx->ready_lock); return 0; @@ -123,8 +123,6 @@ struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) spin_lock(&msg_ctx->ready_lock); if (list_empty(&msg_ctx->ready_queue)) { - mtk_v4l2_debug(3, "queue is NULL, type:%d num: %d", - msg_ctx->hardware_index, msg_ctx->ready_num); spin_unlock(&msg_ctx->ready_lock); if (msg_ctx->hardware_index == MTK_VDEC_CORE) @@ -146,15 +144,15 @@ struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) head = vdec_get_buf_list(msg_ctx->hardware_index, buf); if (!head) { spin_unlock(&msg_ctx->ready_lock); - mtk_v4l2_err("fail to dqbuf: %d", msg_ctx->hardware_index); + mtk_v4l2_vdec_err(buf->ctx, "fail to dqbuf: %d", msg_ctx->hardware_index); return NULL; } list_del(head); vdec_msg_queue_dec(&buf->ctx->msg_queue, msg_ctx->hardware_index); msg_ctx->ready_num--; - mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d", - msg_ctx->hardware_index, buf, msg_ctx->ready_num); + mtk_v4l2_vdec_dbg(3, buf->ctx, "dqueue buf type:%d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); spin_unlock(&msg_ctx->ready_lock); return buf; @@ -164,7 +162,7 @@ void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t u { spin_lock(&msg_queue->lat_ctx.ready_lock); msg_queue->wdma_rptr_addr = ube_rptr; - mtk_v4l2_debug(3, "update ube rprt (0x%llx)", ube_rptr); + mtk_v4l2_vdec_dbg(3, msg_queue->ctx, "update ube rprt (0x%llx)", ube_rptr); spin_unlock(&msg_queue->lat_ctx.ready_lock); } @@ -172,20 +170,19 @@ void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t u { spin_lock(&msg_queue->lat_ctx.ready_lock); msg_queue->wdma_wptr_addr = ube_wptr; - mtk_v4l2_debug(3, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx", - msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr, - ube_wptr); + mtk_v4l2_vdec_dbg(3, msg_queue->ctx, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx", + msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr, + ube_wptr); spin_unlock(&msg_queue->lat_ctx.ready_lock); } bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) { if (atomic_read(&msg_queue->lat_list_cnt) == NUM_BUFFER_COUNT) { - mtk_v4l2_debug(3, "wait buf full: list(%d %d) ready_num:%d status:%d", - atomic_read(&msg_queue->lat_list_cnt), - atomic_read(&msg_queue->core_list_cnt), - msg_queue->lat_ctx.ready_num, - msg_queue->status); + mtk_v4l2_vdec_dbg(3, msg_queue->ctx, "wait buf full: (%d %d) ready:%d status:%d", + atomic_read(&msg_queue->lat_list_cnt), + atomic_read(&msg_queue->core_list_cnt), + msg_queue->lat_ctx.ready_num, msg_queue->status); return true; } @@ -193,16 +190,16 @@ bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) vdec_msg_queue_qbuf(&msg_queue->core_ctx, &msg_queue->empty_lat_buf); wait_event(msg_queue->core_dec_done, msg_queue->flush_done); - mtk_v4l2_debug(3, "flush done => ready_num:%d status:%d list(%d %d)", - msg_queue->lat_ctx.ready_num, msg_queue->status, - atomic_read(&msg_queue->lat_list_cnt), - atomic_read(&msg_queue->core_list_cnt)); + mtk_v4l2_vdec_dbg(3, msg_queue->ctx, "flush done => ready_num:%d status:%d list(%d %d)", + msg_queue->lat_ctx.ready_num, msg_queue->status, + atomic_read(&msg_queue->lat_list_cnt), + atomic_read(&msg_queue->core_list_cnt)); return false; } void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx) + struct mtk_vcodec_dec_ctx *ctx) { struct vdec_lat_buf *lat_buf; struct mtk_vcodec_mem *mem; @@ -231,6 +228,7 @@ void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, mtk_vcodec_mem_free(ctx, mem); kfree(lat_buf->private_data); + lat_buf->private_data = NULL; } if (msg_queue->wdma_addr.size) @@ -241,9 +239,9 @@ static void vdec_msg_queue_core_work(struct work_struct *work) { struct vdec_msg_queue *msg_queue = container_of(work, struct vdec_msg_queue, core_work); - struct mtk_vcodec_ctx *ctx = - container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_dec_ctx *ctx = + container_of(msg_queue, struct mtk_vcodec_dec_ctx, msg_queue); + struct mtk_vcodec_dec_dev *dev = ctx->dev; struct vdec_lat_buf *lat_buf; spin_lock(&msg_queue->core_ctx.ready_lock); @@ -282,7 +280,7 @@ static void vdec_msg_queue_core_work(struct work_struct *work) } int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + struct mtk_vcodec_dec_ctx *ctx, core_decode_cb_t core_decode, int private_size) { struct vdec_lat_buf *lat_buf; @@ -306,7 +304,8 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, ctx->picinfo.buf_h); err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr); if (err) { - mtk_v4l2_err("failed to allocate wdma_addr buf"); + mtk_v4l2_vdec_err(ctx, "failed to allocate wdma_addr buf"); + msg_queue->wdma_addr.size = 0; return -ENOMEM; } msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr; @@ -316,20 +315,21 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, msg_queue->empty_lat_buf.core_decode = NULL; msg_queue->empty_lat_buf.is_last_frame = true; + msg_queue->ctx = ctx; for (i = 0; i < NUM_BUFFER_COUNT; i++) { lat_buf = &msg_queue->lat_buf[i]; lat_buf->wdma_err_addr.size = VDEC_ERR_MAP_SZ_AVC; err = mtk_vcodec_mem_alloc(ctx, &lat_buf->wdma_err_addr); if (err) { - mtk_v4l2_err("failed to allocate wdma_err_addr buf[%d]", i); + mtk_v4l2_vdec_err(ctx, "failed to allocate wdma_err_addr buf[%d]", i); goto mem_alloc_err; } lat_buf->slice_bc_addr.size = VDEC_LAT_SLICE_HEADER_SZ; err = mtk_vcodec_mem_alloc(ctx, &lat_buf->slice_bc_addr); if (err) { - mtk_v4l2_err("failed to allocate wdma_addr buf[%d]", i); + mtk_v4l2_vdec_err(ctx, "failed to allocate wdma_addr buf[%d]", i); goto mem_alloc_err; } @@ -337,15 +337,15 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, lat_buf->rd_mv_addr.size = VDEC_RD_MV_BUFFER_SZ; err = mtk_vcodec_mem_alloc(ctx, &lat_buf->rd_mv_addr); if (err) { - mtk_v4l2_err("failed to allocate rd_mv_addr buf[%d]", i); - return -ENOMEM; + mtk_v4l2_vdec_err(ctx, "failed to allocate rd_mv_addr buf[%d]", i); + goto mem_alloc_err; } lat_buf->tile_addr.size = VDEC_LAT_TILE_SZ; err = mtk_vcodec_mem_alloc(ctx, &lat_buf->tile_addr); if (err) { - mtk_v4l2_err("failed to allocate tile_addr buf[%d]", i); - return -ENOMEM; + mtk_v4l2_vdec_err(ctx, "failed to allocate tile_addr buf[%d]", i); + goto mem_alloc_err; } } @@ -360,7 +360,7 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, lat_buf->is_last_frame = false; err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf); if (err) { - mtk_v4l2_err("failed to qbuf buf[%d]", i); + mtk_v4l2_vdec_err(ctx, "failed to qbuf buf[%d]", i); goto mem_alloc_err; } } diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h similarity index 95% rename from drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h index 2a745e902ad1..1d9beb9e4a14 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h @@ -12,13 +12,11 @@ #include #include -#include "mtk_vcodec_util.h" - #define NUM_BUFFER_COUNT 3 struct vdec_lat_buf; -struct mtk_vcodec_ctx; -struct mtk_vcodec_dev; +struct mtk_vcodec_dec_ctx; +struct mtk_vcodec_dec_dev; typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); /** @@ -76,7 +74,7 @@ struct vdec_lat_buf { struct media_request *src_buf_req; void *private_data; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; core_decode_cb_t core_decode; struct list_head lat_list; struct list_head core_list; @@ -100,6 +98,7 @@ struct vdec_lat_buf { * @empty_lat_buf: the last lat buf used to flush decode * @core_dec_done: core work queue decode done event * @status: current context decode status for core hardware + * @ctx: mtk vcodec context information */ struct vdec_msg_queue { struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; @@ -118,6 +117,7 @@ struct vdec_msg_queue { struct vdec_lat_buf empty_lat_buf; wait_queue_head_t core_dec_done; int status; + struct mtk_vcodec_dec_ctx *ctx; }; /** @@ -130,7 +130,7 @@ struct vdec_msg_queue { * Return: returns 0 if init successfully, or fail. */ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + struct mtk_vcodec_dec_ctx *ctx, core_decode_cb_t core_decode, int private_size); /** @@ -186,6 +186,6 @@ bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue); * @ctx: v4l2 ctx */ void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx); + struct mtk_vcodec_dec_ctx *ctx); #endif diff --git a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c similarity index 67% rename from drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index df309e8e9379..82e57ae983d5 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -4,19 +4,17 @@ * Author: PC Chen */ -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" +#include "mtk_vcodec_dec_drv.h" #include "vdec_drv_if.h" #include "vdec_ipi_msg.h" #include "vdec_vpu_if.h" -#include "mtk_vcodec_fw.h" static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) { struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) (unsigned long)msg->ap_inst_addr; - mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); + mtk_vdec_debug(vpu->ctx, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); /* mapping VPU address to kernel virtual address */ /* the content in vsi is initialized to 0 in VPU */ @@ -24,7 +22,7 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) msg->vpu_inst_addr); vpu->inst_addr = msg->vpu_inst_addr; - mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr); + mtk_vdec_debug(vpu->ctx, "- vpu_inst_addr = 0x%x", vpu->inst_addr); /* Set default ABI version if dealing with unversioned firmware. */ vpu->fw_abi_version = 0; @@ -40,7 +38,7 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) /* Check firmware version. */ vpu->fw_abi_version = msg->vdec_abi_version; - mtk_vcodec_debug(vpu, "firmware version 0x%x\n", vpu->fw_abi_version); + mtk_vdec_debug(vpu->ctx, "firmware version 0x%x\n", vpu->fw_abi_version); switch (vpu->fw_abi_version) { case 1: break; @@ -48,8 +46,7 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) vpu->inst_id = msg->inst_id; break; default: - mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", - vpu->fw_abi_version); + mtk_vdec_err(vpu->ctx, "unhandled firmware version 0x%x\n", vpu->fw_abi_version); vpu->failure = 1; break; } @@ -60,7 +57,7 @@ static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *ms struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) (unsigned long)msg->ap_inst_addr; - mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); + mtk_vdec_debug(vpu->ctx, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); /* param_type is enum vdec_get_param_type */ switch (msg->param_type) { @@ -69,12 +66,27 @@ static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *ms vpu->fb_sz[1] = msg->data[1]; break; default: - mtk_vcodec_err(vpu, "invalid get param type=%d", msg->param_type); + mtk_vdec_err(vpu->ctx, "invalid get param type=%d", msg->param_type); vpu->failure = 1; break; } } +static bool vpu_dec_check_ap_inst(struct mtk_vcodec_dec_dev *dec_dev, struct vdec_vpu_inst *vpu) +{ + struct mtk_vcodec_dec_ctx *ctx; + int ret = false; + + list_for_each_entry(ctx, &dec_dev->ctx_list, list) { + if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { + ret = true; + break; + } + } + + return ret; +} + /* * vpu_dec_ipi_handler - Handler for VPU ipi message. * @@ -87,44 +99,51 @@ static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *ms */ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) { + struct mtk_vcodec_dec_dev *dec_dev; const struct vdec_vpu_ipi_ack *msg = data; - struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) - (unsigned long)msg->ap_inst_addr; + struct vdec_vpu_inst *vpu; - if (!vpu) { - mtk_v4l2_err("ap_inst_addr is NULL, did the SCP hang or crash?"); + dec_dev = (struct mtk_vcodec_dec_dev *)priv; + vpu = (struct vdec_vpu_inst *)(unsigned long)msg->ap_inst_addr; + if (!priv || !vpu) { + pr_err(MTK_DBG_V4L2_STR "ap_inst_addr is NULL, did the SCP hang or crash?"); return; } - mtk_vcodec_debug(vpu, "+ id=%X", msg->msg_id); - - vpu->failure = msg->status; - vpu->signaled = 1; - - if (msg->status == 0) { - switch (msg->msg_id) { - case VPU_IPIMSG_DEC_INIT_ACK: - handle_init_ack_msg(data); - break; - - case VPU_IPIMSG_DEC_START_ACK: - case VPU_IPIMSG_DEC_END_ACK: - case VPU_IPIMSG_DEC_DEINIT_ACK: - case VPU_IPIMSG_DEC_RESET_ACK: - case VPU_IPIMSG_DEC_CORE_ACK: - case VPU_IPIMSG_DEC_CORE_END_ACK: - break; - - case VPU_IPIMSG_DEC_GET_PARAM_ACK: - handle_get_param_msg_ack(data); - break; - default: - mtk_vcodec_err(vpu, "invalid msg=%X", msg->msg_id); - break; - } + if (!vpu_dec_check_ap_inst(dec_dev, vpu) || msg->msg_id < VPU_IPIMSG_DEC_INIT_ACK || + msg->msg_id > VPU_IPIMSG_DEC_GET_PARAM_ACK) { + mtk_v4l2_vdec_err(vpu->ctx, "vdec msg id not correctly => 0x%x", msg->msg_id); + vpu->failure = -EINVAL; + goto error; } - mtk_vcodec_debug(vpu, "- id=%X", msg->msg_id); + vpu->failure = msg->status; + if (msg->status != 0) + goto error; + + switch (msg->msg_id) { + case VPU_IPIMSG_DEC_INIT_ACK: + handle_init_ack_msg(data); + break; + + case VPU_IPIMSG_DEC_START_ACK: + case VPU_IPIMSG_DEC_END_ACK: + case VPU_IPIMSG_DEC_DEINIT_ACK: + case VPU_IPIMSG_DEC_RESET_ACK: + case VPU_IPIMSG_DEC_CORE_ACK: + case VPU_IPIMSG_DEC_CORE_END_ACK: + break; + + case VPU_IPIMSG_DEC_GET_PARAM_ACK: + handle_get_param_msg_ack(data); + break; + default: + mtk_vdec_err(vpu->ctx, "invalid msg=%X", msg->msg_id); + break; + } + +error: + vpu->signaled = 1; } static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) @@ -132,7 +151,7 @@ static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) int err, id, msgid; msgid = *(uint32_t *)msg; - mtk_vcodec_debug(vpu, "id=%X", msgid); + mtk_vdec_debug(vpu->ctx, "id=%X", msgid); vpu->failure = 0; vpu->signaled = 0; @@ -150,8 +169,8 @@ static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg, len, 2000); if (err) { - mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d", - id, msgid, err); + mtk_vdec_err(vpu->ctx, "send fail vpu_id=%d msg_id=%X status=%d", + id, msgid, err); return err; } @@ -163,7 +182,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) struct vdec_ap_ipi_cmd msg; int err = 0; - mtk_vcodec_debug(vpu, "+ id=%X", msg_id); + mtk_vdec_debug(vpu->ctx, "+ id=%X", msg_id); memset(&msg, 0, sizeof(msg)); msg.msg_id = msg_id; @@ -174,7 +193,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) msg.codec_type = vpu->codec_type; err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err); + mtk_vdec_debug(vpu->ctx, "- id=%X ret=%d", msg_id, err); return err; } @@ -183,24 +202,23 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) struct vdec_ap_ipi_init msg; int err; - mtk_vcodec_debug_enter(vpu); - init_waitqueue_head(&vpu->wq); vpu->handler = vpu_dec_ipi_handler; + vpu->ctx->vpu_inst = vpu; err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, - vpu->handler, "vdec", NULL); + vpu->handler, "vdec", vpu->ctx->dev); if (err) { - mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err); + mtk_vdec_err(vpu->ctx, "vpu_ipi_register fail status=%d", err); return err; } if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->core_id, vpu->handler, - "vdec", NULL); + "vdec", vpu->ctx->dev); if (err) { - mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err); + mtk_vdec_err(vpu->ctx, "vpu_ipi_register core fail status=%d", err); return err; } } @@ -210,10 +228,10 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) msg.ap_inst_addr = (unsigned long)vpu; msg.codec_type = vpu->codec_type; - mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu); + mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu); err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- ret=%d", err); + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } @@ -223,10 +241,8 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) int i; int err = 0; - mtk_vcodec_debug_enter(vpu); - if (len > ARRAY_SIZE(msg.data)) { - mtk_vcodec_err(vpu, "invalid len = %d\n", len); + mtk_vdec_err(vpu->ctx, "invalid len = %d\n", len); return -EINVAL; } @@ -242,7 +258,7 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) msg.codec_type = vpu->codec_type; err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- ret=%d", err); + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } @@ -252,10 +268,8 @@ int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data, struct vdec_ap_ipi_get_param msg; int err; - mtk_vcodec_debug_enter(vpu); - if (len > ARRAY_SIZE(msg.data)) { - mtk_vcodec_err(vpu, "invalid len = %d\n", len); + mtk_vdec_err(vpu->ctx, "invalid len = %d\n", len); return -EINVAL; } @@ -267,7 +281,7 @@ int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data, msg.codec_type = vpu->codec_type; err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- ret=%d", err); + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } diff --git a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h similarity index 97% rename from drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h rename to drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h index 0436bba91457..fbb3f34a73f0 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h @@ -7,9 +7,7 @@ #ifndef _VDEC_VPU_IF_H_ #define _VDEC_VPU_IF_H_ -#include "mtk_vcodec_fw.h" - -struct mtk_vcodec_ctx; +struct mtk_vcodec_dec_ctx; /** * struct vdec_vpu_inst - VPU instance for video codec @@ -40,7 +38,7 @@ struct vdec_vpu_inst { uint32_t fw_abi_version; uint32_t inst_id; unsigned int signaled; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_dec_ctx *ctx; wait_queue_head_t wq; mtk_vcodec_ipi_handler handler; unsigned int codec_type; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/Makefile b/drivers/media/platform/mediatek/vcodec/encoder/Makefile new file mode 100644 index 000000000000..e621b5b7e5e6 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/encoder/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o + +mtk-vcodec-enc-y := venc/venc_vp8_if.o \ + venc/venc_h264_if.o \ + mtk_vcodec_enc.o \ + mtk_vcodec_enc_drv.o \ + mtk_vcodec_enc_pm.o \ + venc_drv_if.o \ + venc_vpu_if.o \ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c similarity index 82% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c rename to drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c index 315e97a2450e..04948d3eb011 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -10,10 +10,7 @@ #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_enc.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" #include "venc_drv_if.h" #define MTK_VENC_MIN_W 160U @@ -45,69 +42,59 @@ static const struct v4l2_frmsize_stepwise mtk_venc_4k_framesizes = { static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) { - struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + struct mtk_vcodec_enc_ctx *ctx = ctrl_to_enc_ctx(ctrl); struct mtk_enc_params *p = &ctx->enc_params; int ret = 0; switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE_MODE val= %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_BITRATE_MODE val= %d", ctrl->val); if (ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) { - mtk_v4l2_err("Unsupported bitrate mode =%d", ctrl->val); + mtk_v4l2_venc_err(ctx, "Unsupported bitrate mode =%d", ctrl->val); ret = -EINVAL; } break; case V4L2_CID_MPEG_VIDEO_BITRATE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", ctrl->val); p->bitrate = ctrl->val; ctx->param_change |= MTK_ENCODE_PARAM_BITRATE; break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", ctrl->val); p->num_b_frame = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", + ctrl->val); p->rc_frame = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", ctrl->val); p->h264_max_qp = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", ctrl->val); p->seq_hdr_mode = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", ctrl->val); p->rc_mb = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", ctrl->val); p->h264_profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", ctrl->val); p->h264_level = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", ctrl->val); p->intra_period = ctrl->val; ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD; break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", - ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", ctrl->val); p->gop_size = ctrl->val; ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE; break; @@ -116,10 +103,10 @@ static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) * FIXME - what vp8 profiles are actually supported? * The ctrl is added (with only profile 0 supported) for now. */ - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_VP8_PROFILE val = %d", ctrl->val); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_VP8_PROFILE val = %d", ctrl->val); break; case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); + mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); p->force_intra = 1; ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA; break; @@ -172,7 +159,7 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(fh); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(fh); if (fsize->index != 0) return -EINVAL; @@ -196,7 +183,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { const struct mtk_vcodec_enc_pdata *pdata = - fh_to_ctx(priv)->dev->venc_pdata; + fh_to_enc_ctx(priv)->dev->venc_pdata; return vidioc_enum_fmt(f, pdata->capture_formats, pdata->num_capture_formats); @@ -206,7 +193,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { const struct mtk_vcodec_enc_pdata *pdata = - fh_to_ctx(priv)->dev->venc_pdata; + fh_to_enc_ctx(priv)->dev->venc_pdata; return vidioc_enum_fmt(f, pdata->output_formats, pdata->num_output_formats); @@ -214,7 +201,7 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, static int mtk_vcodec_enc_get_chip_name(void *priv) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-enc")) @@ -234,7 +221,7 @@ static int mtk_vcodec_enc_get_chip_name(void *priv) static int vidioc_venc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct device *dev = &ctx->dev->plat_dev->dev; int platform_name = mtk_vcodec_enc_get_chip_name(priv); @@ -247,7 +234,7 @@ static int vidioc_venc_querycap(struct file *file, void *priv, static int vidioc_venc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct v4l2_fract *timeperframe = &a->parm.output.timeperframe; if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) @@ -270,7 +257,7 @@ static int vidioc_venc_s_parm(struct file *file, void *priv, static int vidioc_venc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) return -EINVAL; @@ -284,7 +271,7 @@ static int vidioc_venc_g_parm(struct file *file, void *priv, return 0; } -static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, +static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_enc_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -304,7 +291,7 @@ static void vidioc_try_fmt_cap(struct v4l2_format *f) /* V4L2 specification suggests the driver corrects the format struct if any of * the dimensions is unsupported */ -static int vidioc_try_fmt_out(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, +static int vidioc_try_fmt_out(struct mtk_vcodec_enc_ctx *ctx, struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; @@ -341,11 +328,12 @@ static int vidioc_try_fmt_out(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, if (pix_fmt_mp->height < tmp_h && (pix_fmt_mp->height + 32) <= max_height) pix_fmt_mp->height += 32; - mtk_v4l2_debug(0, "before resize w=%d, h=%d, after resize w=%d, h=%d, sizeimage=%d %d", - tmp_w, tmp_h, pix_fmt_mp->width, - pix_fmt_mp->height, - pix_fmt_mp->plane_fmt[0].sizeimage, - pix_fmt_mp->plane_fmt[1].sizeimage); + mtk_v4l2_venc_dbg(0, ctx, + "before resize wxh=%dx%d, after resize wxh=%dx%d, sizeimage=%d %d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->plane_fmt[0].sizeimage, + pix_fmt_mp->plane_fmt[1].sizeimage); pix_fmt_mp->num_planes = fmt->num_planes; pix_fmt_mp->plane_fmt[0].sizeimage = @@ -376,8 +364,8 @@ static int vidioc_try_fmt_out(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, return 0; } -static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, - struct venc_enc_param *param) +static void mtk_venc_set_param(struct mtk_vcodec_enc_ctx *ctx, + struct venc_enc_param *param) { struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC]; struct mtk_enc_params *enc_params = &ctx->enc_params; @@ -396,7 +384,7 @@ static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, param->input_yuv_fmt = VENC_YUV_FORMAT_NV21; break; default: - mtk_v4l2_err("Unsupported fourcc =%d", q_data_src->fmt->fourcc); + mtk_v4l2_venc_err(ctx, "Unsupported fourcc =%d", q_data_src->fmt->fourcc); break; } param->h264_profile = enc_params->h264_profile; @@ -414,19 +402,19 @@ static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, param->gop_size = enc_params->gop_size; param->bitrate = enc_params->bitrate; - mtk_v4l2_debug(0, - "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d", - param->input_yuv_fmt, param->h264_profile, - param->h264_level, param->width, param->height, - param->buf_width, param->buf_height, - param->frm_rate, param->bitrate, - param->gop_size, param->intra_period); + mtk_v4l2_venc_dbg(0, ctx, + "fmt 0x%x, P/L %d/%d w/h %d/%d buf %d/%d fps/bps %d/%d gop %d i_per %d", + param->input_yuv_fmt, param->h264_profile, + param->h264_level, param->width, param->height, + param->buf_width, param->buf_height, + param->frm_rate, param->bitrate, + param->gop_size, param->intra_period); } static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; struct vb2_queue *vq; struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); @@ -435,12 +423,12 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) { - mtk_v4l2_err("fail to get vq"); + mtk_v4l2_venc_err(ctx, "fail to get vq"); return -EINVAL; } if (vb2_is_busy(vq)) { - mtk_v4l2_err("queue busy"); + mtk_v4l2_venc_err(ctx, "queue busy"); return -EBUSY; } @@ -468,8 +456,8 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, if (ctx->state == MTK_STATE_FREE) { ret = venc_if_init(ctx, q_data->fmt->fourcc); if (ret) { - mtk_v4l2_err("venc_if_init failed=%d, codec type=%x", - ret, q_data->fmt->fourcc); + mtk_v4l2_venc_err(ctx, "venc_if_init failed=%d, codec type=%x", + ret, q_data->fmt->fourcc); return -EBUSY; } ctx->state = MTK_STATE_INIT; @@ -481,7 +469,7 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, static int vidioc_venc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; struct vb2_queue *vq; struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); @@ -490,12 +478,12 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) { - mtk_v4l2_err("fail to get vq"); + mtk_v4l2_venc_err(ctx, "fail to get vq"); return -EINVAL; } if (vb2_is_busy(vq)) { - mtk_v4l2_err("queue busy"); + mtk_v4l2_venc_err(ctx, "queue busy"); return -EBUSY; } @@ -536,7 +524,7 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct vb2_queue *vq; struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); int i; @@ -569,7 +557,7 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); @@ -591,7 +579,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); @@ -612,7 +600,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, static int vidioc_venc_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -642,7 +630,7 @@ static int vidioc_venc_g_selection(struct file *file, void *priv, static int vidioc_venc_s_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -667,11 +655,11 @@ static int vidioc_venc_s_selection(struct file *file, void *priv, static int vidioc_venc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); + mtk_v4l2_venc_err(ctx, "[%d] Call on QBUF after unrecoverable error", + ctx->id); return -EIO; } @@ -681,12 +669,12 @@ static int vidioc_venc_qbuf(struct file *file, void *priv, static int vidioc_venc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); int ret; if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); + mtk_v4l2_venc_err(ctx, "[%d] Call on QBUF after unrecoverable error", + ctx->id); return -EIO; } @@ -719,13 +707,13 @@ static int vidioc_venc_dqbuf(struct file *file, void *priv, static int vidioc_encoder_cmd(struct file *file, void *priv, struct v4l2_encoder_cmd *cmd) { - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(priv); struct vb2_queue *src_vq, *dst_vq; int ret; if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call to CMD after unrecoverable error", - ctx->id); + mtk_v4l2_venc_err(ctx, "[%d] Call to CMD after unrecoverable error", + ctx->id); return -EIO; } @@ -737,7 +725,7 @@ static int vidioc_encoder_cmd(struct file *file, void *priv, if (ctx->is_flushing) return -EBUSY; - mtk_v4l2_debug(1, "encoder cmd=%u", cmd->cmd); + mtk_v4l2_venc_dbg(1, ctx, "encoder cmd=%u", cmd->cmd); dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); @@ -746,11 +734,11 @@ static int vidioc_encoder_cmd(struct file *file, void *priv, src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); if (!vb2_is_streaming(src_vq)) { - mtk_v4l2_debug(1, "Output stream is off. No need to flush."); + mtk_v4l2_venc_dbg(1, ctx, "Output stream is off. No need to flush."); return 0; } if (!vb2_is_streaming(dst_vq)) { - mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); + mtk_v4l2_venc_dbg(1, ctx, "Capture stream is off. No need to flush."); return 0; } ctx->is_flushing = true; @@ -813,7 +801,7 @@ static int vb2ops_venc_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(vq); struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vq->type); unsigned int i; @@ -837,15 +825,14 @@ static int vb2ops_venc_queue_setup(struct vb2_queue *vq, static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type); int i; for (i = 0; i < q_data->fmt->num_planes; i++) { if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { - mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", - i, vb2_plane_size(vb, i), - q_data->sizeimage[i]); + mtk_v4l2_venc_err(ctx, "data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), q_data->sizeimage[i]); return -EINVAL; } } @@ -855,7 +842,7 @@ static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); @@ -865,10 +852,8 @@ static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && (ctx->param_change != MTK_ENCODE_PARAM_NONE)) { - mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x", - ctx->id, - vb2_v4l2->vb2_buf.index, - ctx->param_change); + mtk_v4l2_venc_dbg(1, ctx, "[%d] Before id=%d encode parameter change %x", + ctx->id, vb2_v4l2->vb2_buf.index, ctx->param_change); mtk_buf->param_change = ctx->param_change; mtk_buf->enc_params = ctx->enc_params; ctx->param_change = MTK_ENCODE_PARAM_NONE; @@ -879,7 +864,7 @@ static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(q); struct venc_enc_param param; int ret, pm_ret; int i; @@ -903,14 +888,14 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) ret = pm_runtime_resume_and_get(&ctx->dev->plat_dev->dev); if (ret < 0) { - mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); + mtk_v4l2_venc_err(ctx, "pm_runtime_resume_and_get fail %d", ret); goto err_start_stream; } mtk_venc_set_param(ctx, ¶m); ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m); if (ret) { - mtk_v4l2_err("venc_if_set_param failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_set_param failed=%d", ret); ctx->state = MTK_STATE_ABORT; goto err_set_param; } @@ -923,7 +908,7 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) VENC_SET_PARAM_PREPEND_HEADER, NULL); if (ret) { - mtk_v4l2_err("venc_if_set_param failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_set_param failed=%d", ret); ctx->state = MTK_STATE_ABORT; goto err_set_param; } @@ -935,7 +920,7 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) err_set_param: pm_ret = pm_runtime_put(&ctx->dev->plat_dev->dev); if (pm_ret < 0) - mtk_v4l2_err("pm_runtime_put fail %d", pm_ret); + mtk_v4l2_venc_err(ctx, "pm_runtime_put fail %d", pm_ret); err_start_stream: for (i = 0; i < q->num_buffers; ++i) { @@ -946,9 +931,8 @@ err_start_stream: * can be marked as done. */ if (buf && buf->state == VB2_BUF_STATE_ACTIVE) { - mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", - ctx->id, i, q->type, - (int)buf->state); + mtk_v4l2_venc_dbg(0, ctx, "[%d] id=%d, type=%d, %d->VB2_BUF_STATE_QUEUED", + ctx->id, i, q->type, (int)buf->state); v4l2_m2m_buf_done(to_vb2_v4l2_buffer(buf), VB2_BUF_STATE_QUEUED); } @@ -959,11 +943,11 @@ err_start_stream: static void vb2ops_venc_stop_streaming(struct vb2_queue *q) { - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(q); struct vb2_v4l2_buffer *src_buf, *dst_buf; int ret; - mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); + mtk_v4l2_venc_dbg(2, ctx, "[%d]-> type=%d", ctx->id, q->type); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { @@ -974,7 +958,7 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) if (ctx->is_flushing) { struct v4l2_m2m_buffer *b, *n; - mtk_v4l2_debug(1, "STREAMOFF called while flushing"); + mtk_v4l2_venc_dbg(1, ctx, "STREAMOFF called while flushing"); /* * STREAMOFF could be called before the flush buffer is * dequeued. Check whether empty flush buf is still in @@ -1008,21 +992,21 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) || (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) { - mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d", - ctx->id, q->type, - vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), - vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); + mtk_v4l2_venc_dbg(1, ctx, "[%d]-> q type %d out=%d cap=%d", + ctx->id, q->type, + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); return; } /* Release the encoder if both streams are stopped. */ ret = venc_if_deinit(ctx); if (ret) - mtk_v4l2_err("venc_if_deinit failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_deinit failed=%d", ret); ret = pm_runtime_put(&ctx->dev->plat_dev->dev); if (ret < 0) - mtk_v4l2_err("pm_runtime_put fail %d", ret); + mtk_v4l2_venc_err(ctx, "pm_runtime_put fail %d", ret); ctx->state = MTK_STATE_FREE; } @@ -1048,7 +1032,7 @@ static const struct vb2_ops mtk_venc_vb2_ops = { static int mtk_venc_encode_header(void *priv) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_enc_ctx *ctx = priv; int ret; struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem bs_buf; @@ -1056,7 +1040,7 @@ static int mtk_venc_encode_header(void *priv) dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (!dst_buf) { - mtk_v4l2_debug(1, "No dst buffer"); + mtk_v4l2_venc_dbg(1, ctx, "No dst buffer"); return -EINVAL; } @@ -1064,12 +1048,10 @@ static int mtk_venc_encode_header(void *priv) bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; - mtk_v4l2_debug(1, - "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", - ctx->id, - dst_buf->vb2_buf.index, bs_buf.va, - (u64)bs_buf.dma_addr, - bs_buf.size); + mtk_v4l2_venc_dbg(1, ctx, + "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", + ctx->id, dst_buf->vb2_buf.index, bs_buf.va, + (u64)bs_buf.dma_addr, bs_buf.size); ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_SEQUENCE_HEADER, @@ -1079,7 +1061,7 @@ static int mtk_venc_encode_header(void *priv) vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); ctx->state = MTK_STATE_ABORT; v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - mtk_v4l2_err("venc_if_encode failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_encode failed=%d", ret); return -EINVAL; } src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); @@ -1087,7 +1069,7 @@ static int mtk_venc_encode_header(void *priv) dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; dst_buf->timecode = src_buf->timecode; } else { - mtk_v4l2_err("No timestamp for the header buffer."); + mtk_v4l2_venc_err(ctx, "No timestamp for the header buffer."); } ctx->state = MTK_STATE_HEADER; @@ -1097,7 +1079,7 @@ static int mtk_venc_encode_header(void *priv) return 0; } -static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) +static int mtk_venc_param_change(struct mtk_vcodec_enc_ctx *ctx) { struct venc_enc_param enc_prm; struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); @@ -1116,10 +1098,8 @@ static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) { enc_prm.bitrate = mtk_buf->enc_params.bitrate; - mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - enc_prm.bitrate); + mtk_v4l2_venc_dbg(1, ctx, "[%d] id=%d, change param br=%d", + ctx->id, vb2_v4l2->vb2_buf.index, enc_prm.bitrate); ret |= venc_if_set_param(ctx, VENC_SET_PARAM_ADJUST_BITRATE, &enc_prm); @@ -1127,27 +1107,23 @@ static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) { enc_prm.frm_rate = mtk_buf->enc_params.framerate_num / mtk_buf->enc_params.framerate_denom; - mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - enc_prm.frm_rate); + mtk_v4l2_venc_dbg(1, ctx, "[%d] id=%d, change param fr=%d", + ctx->id, vb2_v4l2->vb2_buf.index, enc_prm.frm_rate); ret |= venc_if_set_param(ctx, VENC_SET_PARAM_ADJUST_FRAMERATE, &enc_prm); } if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) { enc_prm.gop_size = mtk_buf->enc_params.gop_size; - mtk_v4l2_debug(1, "change param intra period=%d", - enc_prm.gop_size); + mtk_v4l2_venc_dbg(1, ctx, "change param intra period=%d", enc_prm.gop_size); ret |= venc_if_set_param(ctx, VENC_SET_PARAM_GOP_SIZE, &enc_prm); } if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) { - mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - mtk_buf->enc_params.force_intra); + mtk_v4l2_venc_dbg(1, ctx, "[%d] id=%d, change param force I=%d", + ctx->id, vb2_v4l2->vb2_buf.index, + mtk_buf->enc_params.force_intra); if (mtk_buf->enc_params.force_intra) ret |= venc_if_set_param(ctx, VENC_SET_PARAM_FORCE_INTRA, @@ -1158,8 +1134,8 @@ static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) if (ret) { ctx->state = MTK_STATE_ABORT; - mtk_v4l2_err("venc_if_set_param %d failed=%d", - mtk_buf->param_change, ret); + mtk_v4l2_venc_err(ctx, "venc_if_set_param %d failed=%d", + mtk_buf->param_change, ret); return -1; } @@ -1176,7 +1152,7 @@ static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) */ static void mtk_venc_worker(struct work_struct *work) { - struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, + struct mtk_vcodec_enc_ctx *ctx = container_of(work, struct mtk_vcodec_enc_ctx, encode_work); struct vb2_v4l2_buffer *src_buf, *dst_buf; struct venc_frm_buf frm_buf; @@ -1220,14 +1196,11 @@ static void mtk_venc_worker(struct work_struct *work) bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; - mtk_v4l2_debug(2, - "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", - (u64)frm_buf.fb_addr[0].dma_addr, - frm_buf.fb_addr[0].size, - (u64)frm_buf.fb_addr[1].dma_addr, - frm_buf.fb_addr[1].size, - (u64)frm_buf.fb_addr[2].dma_addr, - frm_buf.fb_addr[2].size); + mtk_v4l2_venc_dbg(2, ctx, + "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", + (u64)frm_buf.fb_addr[0].dma_addr, frm_buf.fb_addr[0].size, + (u64)frm_buf.fb_addr[1].dma_addr, frm_buf.fb_addr[1].size, + (u64)frm_buf.fb_addr[2].dma_addr, frm_buf.fb_addr[2].size); ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, &frm_buf, &bs_buf, &enc_result); @@ -1242,25 +1215,24 @@ static void mtk_venc_worker(struct work_struct *work) v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - mtk_v4l2_err("venc_if_encode failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_encode failed=%d", ret); } else { v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_result.bs_size); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - mtk_v4l2_debug(2, "venc_if_encode bs size=%d", - enc_result.bs_size); + mtk_v4l2_venc_dbg(2, ctx, "venc_if_encode bs size=%d", + enc_result.bs_size); } v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); - mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", - src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, - enc_result.bs_size); + mtk_v4l2_venc_dbg(1, ctx, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", + src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, enc_result.bs_size); } static void m2mops_venc_device_run(void *priv) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_enc_ctx *ctx = priv; if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && (ctx->state != MTK_STATE_HEADER)) { @@ -1276,11 +1248,10 @@ static void m2mops_venc_device_run(void *priv) static int m2mops_venc_job_ready(void *m2m_priv) { - struct mtk_vcodec_ctx *ctx = m2m_priv; + struct mtk_vcodec_enc_ctx *ctx = m2m_priv; if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) { - mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.", - ctx->id, ctx->state); + mtk_v4l2_venc_dbg(3, ctx, "[%d]Not ready: state=0x%x.", ctx->id, ctx->state); return 0; } @@ -1289,7 +1260,7 @@ static int m2mops_venc_job_ready(void *m2m_priv) static void m2mops_venc_job_abort(void *priv) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_enc_ctx *ctx = priv; ctx->state = MTK_STATE_ABORT; } @@ -1300,7 +1271,7 @@ const struct v4l2_m2m_ops mtk_venc_m2m_ops = { .job_abort = m2mops_venc_job_abort, }; -void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_enc_ctx *ctx) { struct mtk_q_data *q_data; @@ -1361,7 +1332,7 @@ void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) ctx->enc_params.framerate_denom = MTK_DEFAULT_FRAMERATE_DENOM; } -int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_enc_ctx *ctx) { const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops; struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; @@ -1415,8 +1386,7 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) if (handler->error) { - mtk_v4l2_err("Init control handler fail %d", - handler->error); + mtk_v4l2_venc_err(ctx, "Init control handler fail %d", handler->error); return handler->error; } @@ -1428,7 +1398,7 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { - struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_enc_ctx *ctx = priv; int ret; /* Note: VB2_USERPTR works with dma-contig because mt8173 @@ -1463,28 +1433,28 @@ int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, return vb2_queue_init(dst_vq); } -int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx) +int mtk_venc_unlock(struct mtk_vcodec_enc_ctx *ctx) { - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_enc_dev *dev = ctx->dev; mutex_unlock(&dev->enc_mutex); return 0; } -int mtk_venc_lock(struct mtk_vcodec_ctx *ctx) +int mtk_venc_lock(struct mtk_vcodec_enc_ctx *ctx) { - struct mtk_vcodec_dev *dev = ctx->dev; + struct mtk_vcodec_enc_dev *dev = ctx->dev; mutex_lock(&dev->enc_mutex); return 0; } -void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx) +void mtk_vcodec_enc_release(struct mtk_vcodec_enc_ctx *ctx) { int ret = venc_if_deinit(ctx); if (ret) - mtk_v4l2_err("venc_if_deinit failed=%d", ret); + mtk_v4l2_venc_err(ctx, "venc_if_deinit failed=%d", ret); ctx->state = MTK_STATE_FREE; } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h similarity index 78% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h rename to drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h index 513ee7993e34..82246401ed4a 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h @@ -11,6 +11,8 @@ #include #include +#include "mtk_vcodec_enc_drv.h" + #define MTK_VENC_IRQ_STATUS_SPS 0x1 #define MTK_VENC_IRQ_STATUS_PPS 0x2 #define MTK_VENC_IRQ_STATUS_FRM 0x4 @@ -39,12 +41,12 @@ struct mtk_video_enc_buf { extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops; extern const struct v4l2_m2m_ops mtk_venc_m2m_ops; -int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx); -int mtk_venc_lock(struct mtk_vcodec_ctx *ctx); +int mtk_venc_unlock(struct mtk_vcodec_enc_ctx *ctx); +int mtk_venc_lock(struct mtk_vcodec_enc_ctx *ctx); int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); -void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx); -int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx); -void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_enc_release(struct mtk_vcodec_enc_ctx *ctx); +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_enc_ctx *ctx); +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_enc_ctx *ctx); #endif /* _MTK_VCODEC_ENC_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c similarity index 85% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c rename to drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c index 5df0a22ff3b5..6319f24bc714 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c @@ -9,19 +9,16 @@ #include #include #include -#include #include +#include #include #include #include #include -#include "mtk_vcodec_drv.h" #include "mtk_vcodec_enc.h" #include "mtk_vcodec_enc_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_fw.h" +#include "../common/mtk_vcodec_intr.h" static const struct mtk_video_fmt mtk_video_formats_output[] = { { @@ -85,8 +82,8 @@ static void clean_irq_status(unsigned int irq_status, void __iomem *addr) } static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) { - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_enc_dev *dev = priv; + struct mtk_vcodec_enc_ctx *ctx; unsigned long flags; void __iomem *addr; int core_id; @@ -97,12 +94,11 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) core_id = dev->venc_pdata->core_id; if (core_id < 0 || core_id >= NUM_MAX_VCODEC_REG_BASE) { - mtk_v4l2_err("Invalid core id: %d, ctx id: %d", - core_id, ctx->id); + mtk_v4l2_venc_err(ctx, "Invalid core id: %d, ctx id: %d", core_id, ctx->id); return IRQ_HANDLED; } - mtk_v4l2_debug(1, "id: %d, core id: %d", ctx->id, core_id); + mtk_v4l2_venc_dbg(1, ctx, "id: %d, core id: %d", ctx->id, core_id); addr = dev->reg_base[core_id] + MTK_VENC_IRQ_ACK_OFFSET; @@ -111,14 +107,14 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) clean_irq_status(ctx->irq_status, addr); - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); + wake_up_enc_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); return IRQ_HANDLED; } static int fops_vcodec_open(struct file *file) { - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = NULL; + struct mtk_vcodec_enc_dev *dev = video_drvdata(file); + struct mtk_vcodec_enc_ctx *ctx = NULL; int ret = 0; struct vb2_queue *src_vq; @@ -143,16 +139,14 @@ static int fops_vcodec_open(struct file *file) ctx->type = MTK_INST_ENCODER; ret = mtk_vcodec_enc_ctrls_setup(ctx); if (ret) { - mtk_v4l2_err("Failed to setup controls() (%d)", - ret); + mtk_v4l2_venc_err(ctx, "Failed to setup controls() (%d)", ret); goto err_ctrls_setup; } ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, &mtk_vcodec_enc_queue_init); if (IS_ERR((__force void *)ctx->m2m_ctx)) { ret = PTR_ERR((__force void *)ctx->m2m_ctx); - mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", - ret); + mtk_v4l2_venc_err(ctx, "Failed to v4l2_m2m_ctx_init() (%d)", ret); goto err_m2m_ctx_init; } src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, @@ -171,23 +165,23 @@ static int fops_vcodec_open(struct file *file) * Return 0 if downloading firmware successfully, * otherwise it is failed */ - mtk_v4l2_err("vpu_load_firmware failed!"); + mtk_v4l2_venc_err(ctx, "vpu_load_firmware failed!"); goto err_load_fw; } dev->enc_capability = mtk_vcodec_fw_get_venc_capa(dev->fw_handler); - mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); + mtk_v4l2_venc_dbg(0, ctx, "encoder capability %x", dev->enc_capability); } - mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", - ctx->id, ctx, ctx->m2m_ctx); + mtk_v4l2_venc_dbg(2, ctx, "Create instance [%d]@%p m2m_ctx=%p ", + ctx->id, ctx, ctx->m2m_ctx); list_add(&ctx->list, &dev->ctx_list); mutex_unlock(&dev->dev_mutex); - mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), - ctx->id); + mtk_v4l2_venc_dbg(0, ctx, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), + ctx->id); return ret; /* Deinit when failure occurred */ @@ -206,10 +200,10 @@ err_ctrls_setup: static int fops_vcodec_release(struct file *file) { - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + struct mtk_vcodec_enc_dev *dev = video_drvdata(file); + struct mtk_vcodec_enc_ctx *ctx = fh_to_enc_ctx(file->private_data); - mtk_v4l2_debug(1, "[%d] encoder", ctx->id); + mtk_v4l2_venc_dbg(1, ctx, "[%d] encoder", ctx->id); mutex_lock(&dev->dev_mutex); v4l2_m2m_ctx_release(ctx->m2m_ctx); @@ -235,7 +229,7 @@ static const struct v4l2_file_operations mtk_vcodec_fops = { static int mtk_vcodec_probe(struct platform_device *pdev) { - struct mtk_vcodec_dev *dev; + struct mtk_vcodec_enc_dev *dev; struct video_device *vfd_enc; phandle rproc_phandle; enum mtk_vcodec_fw_type fw_type; @@ -255,7 +249,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) &rproc_phandle)) { fw_type = SCP; } else { - mtk_v4l2_err("Could not get venc IPI device"); + dev_err(&pdev->dev, "[MTK VCODEC] Could not get venc IPI device"); return -ENODEV; } dma_set_max_seg_size(&pdev->dev, UINT_MAX); @@ -267,7 +261,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) dev->venc_pdata = of_device_get_match_data(&pdev->dev); ret = mtk_vcodec_init_enc_clk(dev); if (ret < 0) { - dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!"); + dev_err(&pdev->dev, "[MTK VCODEC] Failed to get mtk vcodec clock source!"); goto err_enc_pm; } @@ -292,7 +286,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) 0, pdev->name, dev); if (ret) { dev_err(&pdev->dev, - "Failed to install dev->enc_irq %d (%d) core_id (%d)", + "[MTK VCODEC] Failed to install dev->enc_irq %d (%d) core_id (%d)", dev->enc_irq, ret, dev->venc_pdata->core_id); ret = -EINVAL; goto err_res; @@ -307,16 +301,14 @@ static int mtk_vcodec_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { - mtk_v4l2_err("v4l2_device_register err=%d", ret); + dev_err(&pdev->dev, "[MTK VCODEC] v4l2_device_register err=%d", ret); goto err_res; } - init_waitqueue_head(&dev->queue); - /* allocate video device for encoder and register it */ vfd_enc = video_device_alloc(); if (!vfd_enc) { - mtk_v4l2_err("Failed to allocate video device"); + dev_err(&pdev->dev, "[MTK VCODEC] Failed to allocate video device"); ret = -ENOMEM; goto err_enc_alloc; } @@ -337,7 +329,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); if (IS_ERR((__force void *)dev->m2m_dev_enc)) { - mtk_v4l2_err("Failed to init mem2mem enc device"); + dev_err(&pdev->dev, "[MTK VCODEC] Failed to init mem2mem enc device"); ret = PTR_ERR((__force void *)dev->m2m_dev_enc); goto err_enc_mem_init; } @@ -347,20 +339,20 @@ static int mtk_vcodec_probe(struct platform_device *pdev) WQ_MEM_RECLAIM | WQ_FREEZABLE); if (!dev->encode_workqueue) { - mtk_v4l2_err("Failed to create encode workqueue"); + dev_err(&pdev->dev, "[MTK VCODEC] Failed to create encode workqueue"); ret = -EINVAL; goto err_event_workq; } ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1); if (ret) { - mtk_v4l2_err("Failed to register video device"); + dev_err(&pdev->dev, "[MTK VCODEC] Failed to register video device"); goto err_enc_reg; } mtk_vcodec_dbgfs_init(dev, true); - mtk_v4l2_debug(0, "encoder %d registered as /dev/video%d", - dev->venc_pdata->core_id, vfd_enc->num); + dev_dbg(&pdev->dev, "[MTK VCODEC] encoder %d registered as /dev/video%d", + dev->venc_pdata->core_id, vfd_enc->num); return 0; @@ -459,9 +451,8 @@ MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); static void mtk_vcodec_enc_remove(struct platform_device *pdev) { - struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + struct mtk_vcodec_enc_dev *dev = platform_get_drvdata(pdev); - mtk_v4l2_debug_enter(); destroy_workqueue(dev->encode_workqueue); if (dev->m2m_dev_enc) v4l2_m2m_release(dev->m2m_dev_enc); @@ -469,7 +460,7 @@ static void mtk_vcodec_enc_remove(struct platform_device *pdev) if (dev->vfd_enc) video_unregister_device(dev->vfd_enc); - mtk_vcodec_dbgfs_deinit(dev); + mtk_vcodec_dbgfs_deinit(&dev->dbgfs); v4l2_device_unregister(&dev->v4l2_dev); pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h new file mode 100644 index 000000000000..a042f607ed8d --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong + */ + +#ifndef _MTK_VCODEC_ENC_DRV_H_ +#define _MTK_VCODEC_ENC_DRV_H_ + +#include "../common/mtk_vcodec_cmn_drv.h" +#include "../common/mtk_vcodec_dbgfs.h" +#include "../common/mtk_vcodec_fw_priv.h" +#include "../common/mtk_vcodec_util.h" + +#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" + +#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) +#define MTK_ENC_IOVA_IS_34BIT(ctx) ((ctx)->dev->venc_pdata->uses_34bit) + +/** + * struct mtk_vcodec_enc_pdata - compatible data for each IC + * + * @uses_ext: whether the encoder uses the extended firmware messaging format + * @min_bitrate: minimum supported encoding bitrate + * @max_bitrate: maximum supported encoding bitrate + * @capture_formats: array of supported capture formats + * @num_capture_formats: number of entries in capture_formats + * @output_formats: array of supported output formats + * @num_output_formats: number of entries in output_formats + * @core_id: stand for h264 or vp8 encode index + * @uses_34bit: whether the encoder uses 34-bit iova + */ +struct mtk_vcodec_enc_pdata { + bool uses_ext; + u64 min_bitrate; + u64 max_bitrate; + const struct mtk_video_fmt *capture_formats; + size_t num_capture_formats; + const struct mtk_video_fmt *output_formats; + size_t num_output_formats; + u8 core_id; + bool uses_34bit; +}; + +/* + * enum mtk_encode_param - General encoding parameters type + */ +enum mtk_encode_param { + MTK_ENCODE_PARAM_NONE = 0, + MTK_ENCODE_PARAM_BITRATE = (1 << 0), + MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), + MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), + MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), + MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), +}; + +/** + * struct mtk_enc_params - General encoding parameters + * @bitrate: target bitrate in bits per second + * @num_b_frame: number of b frames between p-frame + * @rc_frame: frame based rate control + * @rc_mb: macroblock based rate control + * @seq_hdr_mode: H.264 sequence header is encoded separately or joined + * with the first frame + * @intra_period: I frame period + * @gop_size: group of picture size, it's used as the intra frame period + * @framerate_num: frame rate numerator. ex: framerate_num=30 and + * framerate_denom=1 means FPS is 30 + * @framerate_denom: frame rate denominator. ex: framerate_num=30 and + * framerate_denom=1 means FPS is 30 + * @h264_max_qp: Max value for H.264 quantization parameter + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @force_intra: force/insert intra frame + */ +struct mtk_enc_params { + unsigned int bitrate; + unsigned int num_b_frame; + unsigned int rc_frame; + unsigned int rc_mb; + unsigned int seq_hdr_mode; + unsigned int intra_period; + unsigned int gop_size; + unsigned int framerate_num; + unsigned int framerate_denom; + unsigned int h264_max_qp; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int force_intra; +}; + +/** + * struct mtk_vcodec_enc_ctx - Context (instance) private data. + * + * @type: type of encoder instance + * @dev: pointer to the mtk_vcodec_enc_dev of the device + * @list: link to ctx_list of mtk_vcodec_enc_dev + * + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue of the context + * @id: index of the context that this structure describes + * @state: state of the context + * @param_change: indicate encode parameter type + * @enc_params: encoding parameters + * + * @enc_if: hooked encoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @encode_work: worker for the encoding + * @empty_flush_buf: a fake size-0 capture buffer that indicates flush. Used for encoder. + * @is_flushing: set to true if flushing is in progress. + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * + * @q_mutex: vb2_queue mutex. + * @vpu_inst: vpu instance pointer. + */ +struct mtk_vcodec_enc_ctx { + enum mtk_instance_type type; + struct mtk_vcodec_enc_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct mtk_q_data q_data[2]; + int id; + enum mtk_instance_state state; + enum mtk_encode_param param_change; + struct mtk_enc_params enc_params; + + const struct venc_common_if *enc_if; + void *drv_handle; + + int int_cond[MTK_VDEC_HW_MAX]; + int int_type[MTK_VDEC_HW_MAX]; + wait_queue_head_t queue[MTK_VDEC_HW_MAX]; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct encode_work; + struct v4l2_m2m_buffer empty_flush_buf; + bool is_flushing; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + struct mutex q_mutex; + void *vpu_inst; +}; + +/** + * struct mtk_vcodec_enc_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_enc: Video device for encoder. + * + * @m2m_dev_enc: m2m device for encoder. + * @plat_dev: platform device + * @ctx_list: list of struct mtk_vcodec_ctx + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of MTK Vcodec registers. + * @venc_pdata: encoder IC-specific data + * + * @fw_handler: used to communicate with the firmware. + * @id_counter: used to identify current opened instance + * + * @enc_mutex: encoder hardware lock. + * @dev_mutex: video_device lock + * @encode_workqueue: encode work queue + * + * @enc_irq: h264 encoder irq resource + * @irqlock: protect data access by irq handler and work thread + * + * @pm: power management control + * @enc_capability: used to identify encode capability + * @dbgfs: debug log related information + */ +struct mtk_vcodec_enc_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_enc; + + struct v4l2_m2m_dev *m2m_dev_enc; + struct platform_device *plat_dev; + struct list_head ctx_list; + struct mtk_vcodec_enc_ctx *curr_ctx; + + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + const struct mtk_vcodec_enc_pdata *venc_pdata; + + struct mtk_vcodec_fw *fw_handler; + u64 id_counter; + + /* encoder hardware mutex lock */ + struct mutex enc_mutex; + struct mutex dev_mutex; + struct workqueue_struct *encode_workqueue; + + int enc_irq; + spinlock_t irqlock; + + struct mtk_vcodec_pm pm; + unsigned int enc_capability; + struct mtk_vcodec_dbgfs dbgfs; +}; + +static inline struct mtk_vcodec_enc_ctx *fh_to_enc_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mtk_vcodec_enc_ctx, fh); +} + +static inline struct mtk_vcodec_enc_ctx *ctrl_to_enc_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_vcodec_enc_ctx, ctrl_hdl); +} + +/* Wake up context wait_queue */ +static inline void +wake_up_enc_ctx(struct mtk_vcodec_enc_ctx *ctx, unsigned int reason, unsigned int hw_id) +{ + ctx->int_cond[hw_id] = 1; + ctx->int_type[hw_id] = reason; + wake_up_interruptible(&ctx->queue[hw_id]); +} + +#define mtk_venc_err(ctx, fmt, args...) \ + mtk_vcodec_err((ctx)->id, (ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_venc_debug(ctx, fmt, args...) \ + mtk_vcodec_debug((ctx)->id, (ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_v4l2_venc_err(ctx, fmt, args...) mtk_v4l2_err((ctx)->dev->plat_dev, fmt, ##args) + +#define mtk_v4l2_venc_dbg(level, ctx, fmt, args...) \ + mtk_v4l2_debug((ctx)->dev->plat_dev, level, fmt, ##args) + +#endif /* _MTK_VCODEC_ENC_DRV_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c similarity index 82% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c rename to drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c index 7055954eb2af..3fce936e61b9 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c @@ -5,14 +5,13 @@ */ #include -#include -#include +#include #include +#include "mtk_vcodec_enc_drv.h" #include "mtk_vcodec_enc_pm.h" -#include "mtk_vcodec_util.h" -int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *mtkdev) { struct platform_device *pdev; struct mtk_vcodec_pm *pm; @@ -35,7 +34,7 @@ int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) if (!enc_clk->clk_info) return -ENOMEM; } else { - mtk_v4l2_err("Failed to get venc clock count"); + dev_err(pm->dev, "[MTK VCODEC] Failed to get venc clock count"); return -EINVAL; } @@ -44,13 +43,13 @@ int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) ret = of_property_read_string_index(pdev->dev.of_node, "clock-names", i, &clk_info->clk_name); if (ret) { - mtk_v4l2_err("venc failed to get clk name %d", i); + dev_err(pm->dev, "[MTK VCODEC] venc failed to get clk name %d", i); return ret; } clk_info->vcodec_clk = devm_clk_get(&pdev->dev, clk_info->clk_name); if (IS_ERR(clk_info->vcodec_clk)) { - mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, + dev_err(pm->dev, "[MTK VCODEC] venc devm_clk_get (%d)%s fail", i, clk_info->clk_name); return PTR_ERR(clk_info->vcodec_clk); } @@ -67,7 +66,7 @@ void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) for (i = 0; i < enc_clk->clk_num; i++) { ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk); if (ret) { - mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i, + dev_err(pm->dev, "[MTK VCODEC] venc clk_prepare_enable %d %s fail %d", i, enc_clk->clk_info[i].clk_name, ret); goto clkerr; } diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h similarity index 78% rename from drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h rename to drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h index bc455cefc0cd..e50be0575190 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h @@ -7,9 +7,9 @@ #ifndef _MTK_VCODEC_ENC_PM_H_ #define _MTK_VCODEC_ENC_PM_H_ -#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc_drv.h" -int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *dev); +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *dev); void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); diff --git a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c similarity index 88% rename from drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c rename to drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c index 60fd165c0d94..a68dac72c4e4 100644 --- a/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c @@ -10,9 +10,8 @@ #include #include -#include "../mtk_vcodec_drv.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc_drv.h" +#include "../../common/mtk_vcodec_intr.h" #include "../mtk_vcodec_enc.h" #include "../mtk_vcodec_enc_pm.h" #include "../venc_drv_base.h" @@ -221,7 +220,7 @@ struct venc_h264_inst { struct venc_vpu_inst vpu_inst; struct venc_h264_vsi *vsi; struct venc_h264_vsi_34 *vsi_34; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_enc_ctx *ctx; }; static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr) @@ -240,13 +239,13 @@ static unsigned int h264_get_profile(struct venc_h264_inst *inst, case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: return 100; case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: - mtk_vcodec_err(inst, "unsupported CONSTRAINED_BASELINE"); + mtk_venc_err(inst->ctx, "unsupported CONSTRAINED_BASELINE"); return 0; case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: - mtk_vcodec_err(inst, "unsupported EXTENDED"); + mtk_venc_err(inst->ctx, "unsupported EXTENDED"); return 0; default: - mtk_vcodec_debug(inst, "unsupported profile %d", profile); + mtk_venc_debug(inst->ctx, "unsupported profile %d", profile); return 100; } } @@ -256,7 +255,7 @@ static unsigned int h264_get_level(struct venc_h264_inst *inst, { switch (level) { case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - mtk_vcodec_err(inst, "unsupported 1B"); + mtk_venc_err(inst->ctx, "unsupported 1B"); return 0; case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: return 10; @@ -289,7 +288,7 @@ static unsigned int h264_get_level(struct venc_h264_inst *inst, case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: return 51; default: - mtk_vcodec_debug(inst, "unsupported level %d", level); + mtk_venc_debug(inst->ctx, "unsupported level %d", level); return 31; } } @@ -298,8 +297,6 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst) { int i; - mtk_vcodec_debug_enter(inst); - /* Except the SKIP_FRAME buffers, * other buffers need to be freed by AP. */ @@ -309,8 +306,6 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst) } mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); - - mtk_vcodec_debug_leave(inst); } static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) @@ -321,8 +316,6 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) u32 vpua, wb_size; int ret = 0; - mtk_vcodec_debug_enter(inst); - if (is_34bit) wb_34 = inst->vsi_34->work_bufs; else @@ -366,8 +359,7 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); if (ret) { - mtk_vcodec_err(inst, - "cannot allocate buf %d", i); + mtk_venc_err(inst->ctx, "cannot allocate buf %d", i); goto err_alloc; } /* @@ -391,23 +383,20 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit) else wb[i].iova = inst->work_bufs[i].dma_addr; - mtk_vcodec_debug(inst, - "work_buf[%d] va=0x%p iova=%pad size=%zu", - i, inst->work_bufs[i].va, - &inst->work_bufs[i].dma_addr, - inst->work_bufs[i].size); + mtk_venc_debug(inst->ctx, "work_buf[%d] va=0x%p iova=%pad size=%zu", + i, inst->work_bufs[i].va, + &inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); } /* the pps_buf is used by AP side only */ inst->pps_buf.size = 128; ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf); if (ret) { - mtk_vcodec_err(inst, "cannot allocate pps_buf"); + mtk_venc_err(inst->ctx, "cannot allocate pps_buf"); goto err_alloc; } - mtk_vcodec_debug_leave(inst); - return ret; err_alloc: @@ -419,12 +408,12 @@ err_alloc: static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) { unsigned int irq_status = 0; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + struct mtk_vcodec_enc_ctx *ctx = (struct mtk_vcodec_enc_ctx *)inst->ctx; if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, 0)) { irq_status = ctx->irq_status; - mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); + mtk_venc_debug(ctx, "irq_status %x <-", irq_status); } return irq_status; } @@ -452,21 +441,18 @@ static int h264_encode_sps(struct venc_h264_inst *inst, int ret = 0; unsigned int irq_status; - mtk_vcodec_debug_enter(inst); - ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL, bs_buf, NULL); if (ret) return ret; irq_status = h264_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_SPS) { - mtk_vcodec_err(inst, "expect irq status %d", - MTK_VENC_IRQ_STATUS_SPS); + mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_SPS); return -EINVAL; } *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); - mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + mtk_venc_debug(inst->ctx, "bs size %d <-", *bs_size); return ret; } @@ -478,21 +464,18 @@ static int h264_encode_pps(struct venc_h264_inst *inst, int ret = 0; unsigned int irq_status; - mtk_vcodec_debug_enter(inst); - ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL, bs_buf, NULL); if (ret) return ret; irq_status = h264_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_PPS) { - mtk_vcodec_err(inst, "expect irq status %d", - MTK_VENC_IRQ_STATUS_PPS); + mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_PPS); return -EINVAL; } *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); - mtk_vcodec_debug(inst, "bs size %d <-", *bs_size); + mtk_venc_debug(inst->ctx, "bs size %d <-", *bs_size); return ret; } @@ -529,10 +512,9 @@ static int h264_encode_frame(struct venc_h264_inst *inst, unsigned int intra_period; unsigned int irq_status; struct venc_frame_info frame_info; - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_enc_ctx *ctx = inst->ctx; - mtk_vcodec_debug_enter(inst); - mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt); + mtk_venc_debug(ctx, "frm_cnt = %d\n ", inst->frm_cnt); if (MTK_ENC_IOVA_IS_34BIT(ctx)) { gop_size = inst->vsi_34->config.gop_size; @@ -545,9 +527,9 @@ static int h264_encode_frame(struct venc_h264_inst *inst, frame_info.skip_frm_count = inst->skip_frm_cnt; frame_info.frm_type = h264_frame_type(inst->frm_cnt, gop_size, intra_period); - mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", - frame_info.frm_count, frame_info.skip_frm_count, - frame_info.frm_type); + mtk_venc_debug(ctx, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", + frame_info.frm_count, frame_info.skip_frm_count, + frame_info.frm_type); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, bs_buf, &frame_info); @@ -570,15 +552,15 @@ static int h264_encode_frame(struct venc_h264_inst *inst, irq_status = h264_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { - mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + mtk_venc_err(ctx, "irq_status=%d failed", irq_status); return -EIO; } *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT); ++inst->frm_cnt; - mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-", - inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm); + mtk_venc_debug(ctx, "frm %d bs_size %d key_frm %d <-", + inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm); return 0; } @@ -589,7 +571,7 @@ static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, unsigned char *p = buf; if (size < H264_FILLER_MARKER_SIZE) { - mtk_vcodec_err(inst, "filler size too small %d", size); + mtk_venc_err(inst->ctx, "filler size too small %d", size); return; } @@ -599,7 +581,7 @@ static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, memset(p, 0xff, size); } -static int h264_enc_init(struct mtk_vcodec_ctx *ctx) +static int h264_enc_init(struct mtk_vcodec_enc_ctx *ctx) { const bool is_ext = MTK_ENC_CTX_IS_EXT(ctx); int ret = 0; @@ -612,9 +594,7 @@ static int h264_enc_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu_inst.ctx = ctx; inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264; - inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); - - mtk_vcodec_debug_enter(inst); + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_SYS); ret = vpu_enc_init(&inst->vpu_inst); @@ -623,8 +603,6 @@ static int h264_enc_init(struct mtk_vcodec_ctx *ctx) else inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; - mtk_vcodec_debug_leave(inst); - if (ret) kfree(inst); else @@ -641,9 +619,9 @@ static int h264_enc_encode(void *handle, { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_enc_ctx *ctx = inst->ctx; - mtk_vcodec_debug(inst, "opt %d ->", opt); + mtk_venc_debug(ctx, "opt %d ->", opt); enable_irq(ctx->dev->enc_irq); @@ -678,7 +656,7 @@ static int h264_enc_encode(void *handle, break; } - mtk_vcodec_debug(inst, "h264_encode_frame prepend SPS/PPS"); + mtk_venc_debug(ctx, "h264_encode_frame prepend SPS/PPS"); ret = h264_encode_header(inst, bs_buf, &bs_size_hdr); if (ret) @@ -705,9 +683,8 @@ static int h264_enc_encode(void *handle, result->bs_size = hdr_sz + filler_sz + bs_size_frm; - mtk_vcodec_debug(inst, "hdr %d filler %d frame %d bs %d", - hdr_sz, filler_sz, bs_size_frm, - result->bs_size); + mtk_venc_debug(ctx, "hdr %d filler %d frame %d bs %d", + hdr_sz, filler_sz, bs_size_frm, result->bs_size); inst->prepend_hdr = 0; result->is_key_frm = inst->vpu_inst.is_key_frm; @@ -715,7 +692,7 @@ static int h264_enc_encode(void *handle, } default: - mtk_vcodec_err(inst, "venc_start_opt %d not supported", opt); + mtk_venc_err(ctx, "venc_start_opt %d not supported", opt); ret = -EINVAL; break; } @@ -723,7 +700,7 @@ static int h264_enc_encode(void *handle, encode_err: disable_irq(ctx->dev->enc_irq); - mtk_vcodec_debug(inst, "opt %d <-", opt); + mtk_venc_debug(ctx, "opt %d <-", opt); return ret; } @@ -772,10 +749,10 @@ static int h264_enc_set_param(void *handle, { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; - struct mtk_vcodec_ctx *ctx = inst->ctx; + struct mtk_vcodec_enc_ctx *ctx = inst->ctx; const bool is_34bit = MTK_ENC_IOVA_IS_34BIT(ctx); - mtk_vcodec_debug(inst, "->type=%d", type); + mtk_venc_debug(ctx, "->type=%d", type); switch (type) { case VENC_SET_PARAM_ENC: @@ -798,7 +775,7 @@ static int h264_enc_set_param(void *handle, case VENC_SET_PARAM_PREPEND_HEADER: inst->prepend_hdr = 1; - mtk_vcodec_debug(inst, "set prepend header mode"); + mtk_venc_debug(ctx, "set prepend header mode"); break; case VENC_SET_PARAM_FORCE_INTRA: case VENC_SET_PARAM_GOP_SIZE: @@ -811,8 +788,6 @@ static int h264_enc_set_param(void *handle, break; } - mtk_vcodec_debug_leave(inst); - return ret; } @@ -821,14 +796,11 @@ static int h264_enc_deinit(void *handle) int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; - mtk_vcodec_debug_enter(inst); - ret = vpu_enc_deinit(&inst->vpu_inst); if (inst->work_buf_allocated) h264_enc_free_work_buf(inst); - mtk_vcodec_debug_leave(inst); kfree(inst); return ret; diff --git a/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_vp8_if.c similarity index 88% rename from drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c rename to drivers/media/platform/mediatek/vcodec/encoder/venc/venc_vp8_if.c index 56ce58f761f1..05abca91e742 100644 --- a/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_vp8_if.c @@ -9,9 +9,8 @@ #include #include -#include "../mtk_vcodec_drv.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc_drv.h" +#include "../../common/mtk_vcodec_intr.h" #include "../mtk_vcodec_enc.h" #include "../mtk_vcodec_enc_pm.h" #include "../venc_drv_base.h" @@ -129,7 +128,7 @@ struct venc_vp8_inst { unsigned int ts_mode; struct venc_vpu_inst vpu_inst; struct venc_vp8_vsi *vsi; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_enc_ctx *ctx; }; static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr) @@ -141,16 +140,12 @@ static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst) { int i; - mtk_vcodec_debug_enter(inst); - /* Buffers need to be freed by AP. */ for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { if (inst->work_bufs[i].size == 0) continue; mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); } - - mtk_vcodec_debug_leave(inst); } static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) @@ -159,8 +154,6 @@ static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) int ret = 0; struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs; - mtk_vcodec_debug_enter(inst); - for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { if (wb[i].size == 0) continue; @@ -177,8 +170,7 @@ static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) inst->work_bufs[i].size = wb[i].size; ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); if (ret) { - mtk_vcodec_err(inst, - "cannot alloc work_bufs[%d]", i); + mtk_venc_err(inst->ctx, "cannot alloc work_bufs[%d]", i); goto err_alloc; } /* @@ -199,15 +191,12 @@ static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) } wb[i].iova = inst->work_bufs[i].dma_addr; - mtk_vcodec_debug(inst, - "work_bufs[%d] va=0x%p,iova=%pad,size=%zu", - i, inst->work_bufs[i].va, - &inst->work_bufs[i].dma_addr, - inst->work_bufs[i].size); + mtk_venc_debug(inst->ctx, "work_bufs[%d] va=0x%p,iova=%pad,size=%zu", + i, inst->work_bufs[i].va, + &inst->work_bufs[i].dma_addr, + inst->work_bufs[i].size); } - mtk_vcodec_debug_leave(inst); - return ret; err_alloc: @@ -219,12 +208,12 @@ err_alloc: static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst) { unsigned int irq_status = 0; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx; + struct mtk_vcodec_enc_ctx *ctx = (struct mtk_vcodec_enc_ctx *)inst->ctx; if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, 0)) { irq_status = ctx->irq_status; - mtk_vcodec_debug(inst, "isr return %x", irq_status); + mtk_venc_debug(ctx, "isr return %x", irq_status); } return irq_status; } @@ -269,8 +258,7 @@ static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst, } if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) { - mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)", - bs_buf->size); + mtk_venc_err(inst->ctx, "bitstream buf size is too small(%zu)", bs_buf->size); return -EINVAL; } @@ -300,7 +288,7 @@ static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, int ret = 0; unsigned int irq_status; - mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt); + mtk_venc_debug(inst->ctx, "->frm_cnt=%d", inst->frm_cnt); ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, NULL); if (ret) @@ -308,23 +296,22 @@ static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, irq_status = vp8_enc_wait_venc_done(inst); if (irq_status != MTK_VENC_IRQ_STATUS_FRM) { - mtk_vcodec_err(inst, "irq_status=%d failed", irq_status); + mtk_venc_err(inst->ctx, "irq_status=%d failed", irq_status); return -EIO; } if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) { - mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed"); + mtk_venc_err(inst->ctx, "vp8_enc_compose_one_frame failed"); return -EINVAL; } inst->frm_cnt++; - mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size, - inst->vpu_inst.is_key_frm); + mtk_venc_debug(inst->ctx, "<-size=%d key_frm=%d", *bs_size, inst->vpu_inst.is_key_frm); return ret; } -static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) +static int vp8_enc_init(struct mtk_vcodec_enc_ctx *ctx) { int ret = 0; struct venc_vp8_inst *inst; @@ -336,16 +323,12 @@ static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu_inst.ctx = ctx; inst->vpu_inst.id = IPI_VENC_VP8; - inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); - - mtk_vcodec_debug_enter(inst); + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_LT_SYS); ret = vpu_enc_init(&inst->vpu_inst); inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi; - mtk_vcodec_debug_leave(inst); - if (ret) kfree(inst); else @@ -362,9 +345,7 @@ static int vp8_enc_encode(void *handle, { int ret = 0; struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; - struct mtk_vcodec_ctx *ctx = inst->ctx; - - mtk_vcodec_debug_enter(inst); + struct mtk_vcodec_enc_ctx *ctx = inst->ctx; enable_irq(ctx->dev->enc_irq); @@ -378,7 +359,7 @@ static int vp8_enc_encode(void *handle, break; default: - mtk_vcodec_err(inst, "opt not support:%d", opt); + mtk_venc_err(ctx, "opt not support:%d", opt); ret = -EINVAL; break; } @@ -386,8 +367,6 @@ static int vp8_enc_encode(void *handle, encode_err: disable_irq(ctx->dev->enc_irq); - mtk_vcodec_debug_leave(inst); - return ret; } @@ -398,7 +377,7 @@ static int vp8_enc_set_param(void *handle, int ret = 0; struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; - mtk_vcodec_debug(inst, "->type=%d", type); + mtk_venc_debug(inst->ctx, "->type=%d", type); switch (type) { case VENC_SET_PARAM_ENC: @@ -429,7 +408,7 @@ static int vp8_enc_set_param(void *handle, */ case VENC_SET_PARAM_TS_MODE: inst->ts_mode = 1; - mtk_vcodec_debug(inst, "set ts_mode"); + mtk_venc_debug(inst->ctx, "set ts_mode"); break; default: @@ -437,8 +416,6 @@ static int vp8_enc_set_param(void *handle, break; } - mtk_vcodec_debug_leave(inst); - return ret; } @@ -447,16 +424,12 @@ static int vp8_enc_deinit(void *handle) int ret = 0; struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; - mtk_vcodec_debug_enter(inst); - ret = vpu_enc_deinit(&inst->vpu_inst); if (inst->work_buf_allocated) vp8_enc_free_work_buf(inst); - mtk_vcodec_debug_leave(inst); kfree(inst); - return ret; } diff --git a/drivers/media/platform/mediatek/vcodec/venc_drv_base.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_base.h similarity index 94% rename from drivers/media/platform/mediatek/vcodec/venc_drv_base.h rename to drivers/media/platform/mediatek/vcodec/encoder/venc_drv_base.h index 3d718411dc73..856d50151bf6 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_drv_base.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_base.h @@ -9,7 +9,7 @@ #ifndef _VENC_DRV_BASE_ #define _VENC_DRV_BASE_ -#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc_drv.h" #include "venc_drv_if.h" @@ -19,7 +19,7 @@ struct venc_common_if { * @ctx: [in] mtk v4l2 context * @handle: [out] driver handle */ - int (*init)(struct mtk_vcodec_ctx *ctx); + int (*init)(struct mtk_vcodec_enc_ctx *ctx); /** * (*encode)() - trigger encode diff --git a/drivers/media/platform/mediatek/vcodec/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c similarity index 86% rename from drivers/media/platform/mediatek/vcodec/venc_drv_if.c rename to drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c index ce0bce811615..1bdaecdd64a7 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c @@ -16,7 +16,7 @@ #include "mtk_vcodec_enc.h" #include "mtk_vcodec_enc_pm.h" -int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +int venc_if_init(struct mtk_vcodec_enc_ctx *ctx, unsigned int fourcc) { int ret = 0; @@ -40,8 +40,8 @@ int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) return ret; } -int venc_if_set_param(struct mtk_vcodec_ctx *ctx, - enum venc_set_param_type type, struct venc_enc_param *in) +int venc_if_set_param(struct mtk_vcodec_enc_ctx *ctx, + enum venc_set_param_type type, struct venc_enc_param *in) { int ret = 0; @@ -54,7 +54,7 @@ int venc_if_set_param(struct mtk_vcodec_ctx *ctx, return ret; } -int venc_if_encode(struct mtk_vcodec_ctx *ctx, +int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, struct venc_done_result *result) @@ -81,7 +81,7 @@ int venc_if_encode(struct mtk_vcodec_ctx *ctx, return ret; } -int venc_if_deinit(struct mtk_vcodec_ctx *ctx) +int venc_if_deinit(struct mtk_vcodec_enc_ctx *ctx) { int ret = 0; diff --git a/drivers/media/platform/mediatek/vcodec/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h similarity index 94% rename from drivers/media/platform/mediatek/vcodec/venc_drv_if.h rename to drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h index 0b04a1020873..d00fb68b8235 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h @@ -9,8 +9,7 @@ #ifndef _VENC_DRV_IF_H_ #define _VENC_DRV_IF_H_ -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" +#include "mtk_vcodec_enc_drv.h" /* * enum venc_yuv_fmt - The type of input yuv format @@ -132,14 +131,14 @@ extern const struct venc_common_if venc_vp8_if; * @fourcc: encoder input format * Return: 0 if creating handle successfully, otherwise it is failed. */ -int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); +int venc_if_init(struct mtk_vcodec_enc_ctx *ctx, unsigned int fourcc); /* * venc_if_deinit - Release the driver handle * @ctx: device context * Return: 0 if releasing handle successfully, otherwise it is failed. */ -int venc_if_deinit(struct mtk_vcodec_ctx *ctx); +int venc_if_deinit(struct mtk_vcodec_enc_ctx *ctx); /* * venc_if_set_param - Set parameter to driver @@ -148,7 +147,7 @@ int venc_if_deinit(struct mtk_vcodec_ctx *ctx); * @in: input parameter * Return: 0 if setting param successfully, otherwise it is failed. */ -int venc_if_set_param(struct mtk_vcodec_ctx *ctx, +int venc_if_set_param(struct mtk_vcodec_enc_ctx *ctx, enum venc_set_param_type type, struct venc_enc_param *in); @@ -161,7 +160,7 @@ int venc_if_set_param(struct mtk_vcodec_ctx *ctx, * @result: encode result * Return: 0 if encoding frame successfully, otherwise it is failed. */ -int venc_if_encode(struct mtk_vcodec_ctx *ctx, +int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, diff --git a/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h similarity index 100% rename from drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h rename to drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h diff --git a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c similarity index 75% rename from drivers/media/platform/mediatek/vcodec/venc_vpu_if.c rename to drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c index 09e7eaa25aab..d299cc2962a5 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c @@ -4,8 +4,7 @@ * Author: PoChun Lin */ -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_fw.h" +#include "mtk_vcodec_enc_drv.h" #include "venc_ipi_msg.h" #include "venc_vpu_if.h" @@ -22,14 +21,13 @@ static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data) return; /* Check firmware version. */ - mtk_vcodec_debug(vpu, "firmware version: 0x%x\n", - msg->venc_abi_version); + mtk_venc_debug(vpu->ctx, "firmware version: 0x%x\n", msg->venc_abi_version); switch (msg->venc_abi_version) { case 1: break; default: - mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", - msg->venc_abi_version); + mtk_venc_err(vpu->ctx, "unhandled firmware version 0x%x\n", + msg->venc_abi_version); vpu->failure = 1; break; } @@ -44,19 +42,47 @@ static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) vpu->is_key_frm = msg->is_key_frm; } +static bool vpu_enc_check_ap_inst(struct mtk_vcodec_enc_dev *enc_dev, struct venc_vpu_inst *vpu) +{ + struct mtk_vcodec_enc_ctx *ctx; + int ret = false; + + list_for_each_entry(ctx, &enc_dev->ctx_list, list) { + if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { + ret = true; + break; + } + } + + return ret; +} + static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) { + struct mtk_vcodec_enc_dev *enc_dev; const struct venc_vpu_ipi_msg_common *msg = data; - struct venc_vpu_inst *vpu = - (struct venc_vpu_inst *)(unsigned long)msg->venc_inst; + struct venc_vpu_inst *vpu; - mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d", - msg->msg_id, vpu, msg->status); + enc_dev = (struct mtk_vcodec_enc_dev *)priv; + vpu = (struct venc_vpu_inst *)(unsigned long)msg->venc_inst; + if (!priv || !vpu) { + pr_err(MTK_DBG_V4L2_STR "venc_inst is NULL, did the SCP hang or crash?"); + return; + } + + mtk_venc_debug(vpu->ctx, "msg_id %x inst %p status %d", msg->msg_id, vpu, msg->status); + if (!vpu_enc_check_ap_inst(enc_dev, vpu) || msg->msg_id < VPU_IPIMSG_ENC_INIT_DONE || + msg->msg_id > VPU_IPIMSG_ENC_DEINIT_DONE) { + mtk_v4l2_venc_err(vpu->ctx, "venc msg id not correctly => 0x%x", msg->msg_id); + vpu->failure = -EINVAL; + goto error; + } - vpu->signaled = 1; vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); - if (vpu->failure) - goto failure; + if (vpu->failure) { + mtk_venc_err(vpu->ctx, "vpu enc status failure %d", vpu->failure); + goto error; + } switch (msg->msg_id) { case VPU_IPIMSG_ENC_INIT_DONE: @@ -70,12 +96,12 @@ static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) case VPU_IPIMSG_ENC_DEINIT_DONE: break; default: - mtk_vcodec_err(vpu, "unknown msg id %x", msg->msg_id); + mtk_venc_err(vpu->ctx, "unknown msg id %x", msg->msg_id); break; } -failure: - mtk_vcodec_debug_leave(vpu); +error: + vpu->signaled = 1; } static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, @@ -83,25 +109,21 @@ static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, { int status; - mtk_vcodec_debug_enter(vpu); - if (!vpu->ctx->dev->fw_handler) { - mtk_vcodec_err(vpu, "inst dev is NULL"); + mtk_venc_err(vpu->ctx, "inst dev is NULL"); return -EINVAL; } status = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, len, 2000); if (status) { - mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", - *(uint32_t *)msg, len, status); + mtk_venc_err(vpu->ctx, "vpu_ipi_send msg_id %x len %d fail %d", + *(uint32_t *)msg, len, status); return -EINVAL; } if (vpu->failure) return -EINVAL; - mtk_vcodec_debug_leave(vpu); - return 0; } @@ -110,17 +132,16 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) int status; struct venc_ap_ipi_msg_init out; - mtk_vcodec_debug_enter(vpu); - init_waitqueue_head(&vpu->wq_hd); vpu->signaled = 0; vpu->failure = 0; + vpu->ctx->vpu_inst = vpu; status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, vpu_enc_ipi_handler, "venc", NULL); if (status) { - mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); + mtk_venc_err(vpu->ctx, "vpu_ipi_register fail %d", status); return -EINVAL; } @@ -128,12 +149,10 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) out.msg_id = AP_IPIMSG_ENC_INIT; out.venc_inst = (unsigned long)vpu; if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail"); + mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_INIT fail"); return -EINVAL; } - mtk_vcodec_debug_leave(vpu); - return 0; } @@ -166,7 +185,7 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, sizeof(struct venc_ap_ipi_msg_set_param); struct venc_ap_ipi_msg_set_param_ext out; - mtk_vcodec_debug(vpu, "id %d ->", id); + mtk_venc_debug(vpu->ctx, "id %d ->", id); memset(&out, 0, sizeof(out)); out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM; @@ -208,16 +227,15 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, out.base.data_item = 0; break; default: - mtk_vcodec_err(vpu, "id %d not supported", id); + mtk_venc_err(vpu->ctx, "id %d not supported", id); return -EINVAL; } if (vpu_enc_send_msg(vpu, &out, msg_size)) { - mtk_vcodec_err(vpu, - "AP_IPIMSG_ENC_SET_PARAM %d fail", id); + mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_SET_PARAM %d fail", id); return -EINVAL; } - mtk_vcodec_debug(vpu, "id %d <-", id); + mtk_venc_debug(vpu->ctx, "id %d <-", id); return 0; } @@ -234,7 +252,7 @@ static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu, sizeof(struct venc_ap_ipi_msg_enc); struct venc_ap_ipi_msg_enc_ext out; - mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode); memset(&out, 0, sizeof(out)); out.base.msg_id = AP_IPIMSG_ENC_ENCODE; @@ -248,7 +266,7 @@ static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu, out.base.input_addr[1] = frm_buf->fb_addr[1].dma_addr; out.base.input_addr[2] = frm_buf->fb_addr[2].dma_addr; } else { - mtk_vcodec_err(vpu, "dma_addr not align to 16"); + mtk_venc_err(vpu->ctx, "dma_addr not align to 16"); return -EINVAL; } } @@ -263,8 +281,7 @@ static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu, out.data[2] = frame_info->frm_type; } if (vpu_enc_send_msg(vpu, &out, msg_size)) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", - bs_mode); + mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_ENCODE %d fail", bs_mode); return -EINVAL; } @@ -280,7 +297,7 @@ static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu, struct venc_ap_ipi_msg_enc_ext_34 out; size_t msg_size = sizeof(struct venc_ap_ipi_msg_enc_ext_34); - mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode); memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_ENCODE; @@ -295,7 +312,7 @@ static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu, out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; } else { - mtk_vcodec_err(vpu, "dma_addr not align to 16"); + mtk_venc_err(vpu->ctx, "dma_addr not align to 16"); return -EINVAL; } } @@ -310,8 +327,7 @@ static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu, out.data[2] = frame_info->frm_type; } if (vpu_enc_send_msg(vpu, &out, msg_size)) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", - bs_mode); + mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_ENCODE %d fail", bs_mode); return -EINVAL; } @@ -335,8 +351,8 @@ int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, if (ret) return ret; - mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-", - bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); + mtk_venc_debug(vpu->ctx, "bs_mode %d state %d size %d key_frm %d <-", + bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm); return 0; } @@ -345,17 +361,13 @@ int vpu_enc_deinit(struct venc_vpu_inst *vpu) { struct venc_ap_ipi_msg_deinit out; - mtk_vcodec_debug_enter(vpu); - memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_DEINIT; out.vpu_inst_addr = vpu->inst_addr; if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail"); + mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_DEINIT fail"); return -EINVAL; } - mtk_vcodec_debug_leave(vpu); - return 0; } diff --git a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.h similarity index 96% rename from drivers/media/platform/mediatek/vcodec/venc_vpu_if.h rename to drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.h index f83bc1b3f2bf..ede55fc3bd07 100644 --- a/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.h @@ -7,7 +7,6 @@ #ifndef _VENC_VPU_IF_H_ #define _VENC_VPU_IF_H_ -#include "mtk_vcodec_fw.h" #include "venc_drv_if.h" /* @@ -35,7 +34,7 @@ struct venc_vpu_inst { unsigned int inst_addr; void *vsi; int id; - struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_enc_ctx *ctx; }; int vpu_enc_init(struct venc_vpu_inst *vpu); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h deleted file mode 100644 index f17d67e781c9..000000000000 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h +++ /dev/null @@ -1,548 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_DRV_H_ -#define _MTK_VCODEC_DRV_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_vcodec_dbgfs.h" -#include "mtk_vcodec_util.h" -#include "vdec_msg_queue.h" - -#define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" -#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" - -#define MTK_VCODEC_MAX_PLANES 3 -#define MTK_V4L2_BENCHMARK 0 -#define WAIT_INTR_TIMEOUT_MS 1000 -#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) -#define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) - -/* - * enum mtk_hw_reg_idx - MTK hw register base index - */ -enum mtk_hw_reg_idx { - VDEC_SYS, - VDEC_MISC, - VDEC_LD, - VDEC_TOP, - VDEC_CM, - VDEC_AD, - VDEC_AV, - VDEC_PP, - VDEC_HWD, - VDEC_HWQ, - VDEC_HWB, - VDEC_HWG, - NUM_MAX_VDEC_REG_BASE, - /* h264 encoder */ - VENC_SYS = NUM_MAX_VDEC_REG_BASE, - /* vp8 encoder */ - VENC_LT_SYS, - NUM_MAX_VCODEC_REG_BASE -}; - -/* - * enum mtk_instance_type - The type of an MTK Vcodec instance. - */ -enum mtk_instance_type { - MTK_INST_DECODER = 0, - MTK_INST_ENCODER = 1, -}; - -/** - * enum mtk_instance_state - The state of an MTK Vcodec instance. - * @MTK_STATE_FREE: default state when instance is created - * @MTK_STATE_INIT: vcodec instance is initialized - * @MTK_STATE_HEADER: vdec had sps/pps header parsed or venc - * had sps/pps header encoded - * @MTK_STATE_FLUSH: vdec is flushing. Only used by decoder - * @MTK_STATE_ABORT: vcodec should be aborted - */ -enum mtk_instance_state { - MTK_STATE_FREE = 0, - MTK_STATE_INIT = 1, - MTK_STATE_HEADER = 2, - MTK_STATE_FLUSH = 3, - MTK_STATE_ABORT = 4, -}; - -/* - * enum mtk_encode_param - General encoding parameters type - */ -enum mtk_encode_param { - MTK_ENCODE_PARAM_NONE = 0, - MTK_ENCODE_PARAM_BITRATE = (1 << 0), - MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), - MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), - MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), - MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), -}; - -enum mtk_fmt_type { - MTK_FMT_DEC = 0, - MTK_FMT_ENC = 1, - MTK_FMT_FRAME = 2, -}; - -/* - * enum mtk_vdec_hw_id - Hardware index used to separate - * different hardware - */ -enum mtk_vdec_hw_id { - MTK_VDEC_CORE, - MTK_VDEC_LAT0, - MTK_VDEC_LAT1, - MTK_VDEC_LAT_SOC, - MTK_VDEC_HW_MAX, -}; - -/* - * enum mtk_vdec_hw_count - Supported hardware count - */ -enum mtk_vdec_hw_count { - MTK_VDEC_NO_HW = 0, - MTK_VDEC_ONE_CORE, - MTK_VDEC_ONE_LAT_ONE_CORE, - MTK_VDEC_MAX_HW_COUNT, -}; - -/* - * struct mtk_video_fmt - Structure used to store information about pixelformats - */ -struct mtk_video_fmt { - u32 fourcc; - enum mtk_fmt_type type; - u32 num_planes; - u32 flags; - struct v4l2_frmsize_stepwise frmsize; -}; - -/* - * enum mtk_q_type - Type of queue - */ -enum mtk_q_type { - MTK_Q_DATA_SRC = 0, - MTK_Q_DATA_DST = 1, -}; - -/* - * struct mtk_q_data - Structure used to store information about queue - */ -struct mtk_q_data { - unsigned int visible_width; - unsigned int visible_height; - unsigned int coded_width; - unsigned int coded_height; - enum v4l2_field field; - unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; - unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; - const struct mtk_video_fmt *fmt; -}; - -/** - * struct mtk_enc_params - General encoding parameters - * @bitrate: target bitrate in bits per second - * @num_b_frame: number of b frames between p-frame - * @rc_frame: frame based rate control - * @rc_mb: macroblock based rate control - * @seq_hdr_mode: H.264 sequence header is encoded separately or joined - * with the first frame - * @intra_period: I frame period - * @gop_size: group of picture size, it's used as the intra frame period - * @framerate_num: frame rate numerator. ex: framerate_num=30 and - * framerate_denom=1 means FPS is 30 - * @framerate_denom: frame rate denominator. ex: framerate_num=30 and - * framerate_denom=1 means FPS is 30 - * @h264_max_qp: Max value for H.264 quantization parameter - * @h264_profile: V4L2 defined H.264 profile - * @h264_level: V4L2 defined H.264 level - * @force_intra: force/insert intra frame - */ -struct mtk_enc_params { - unsigned int bitrate; - unsigned int num_b_frame; - unsigned int rc_frame; - unsigned int rc_mb; - unsigned int seq_hdr_mode; - unsigned int intra_period; - unsigned int gop_size; - unsigned int framerate_num; - unsigned int framerate_denom; - unsigned int h264_max_qp; - unsigned int h264_profile; - unsigned int h264_level; - unsigned int force_intra; -}; - -/* - * struct mtk_vcodec_clk_info - Structure used to store clock name - */ -struct mtk_vcodec_clk_info { - const char *clk_name; - struct clk *vcodec_clk; -}; - -/* - * struct mtk_vcodec_clk - Structure used to store vcodec clock information - */ -struct mtk_vcodec_clk { - struct mtk_vcodec_clk_info *clk_info; - int clk_num; -}; - -/* - * struct mtk_vcodec_pm - Power management data structure - */ -struct mtk_vcodec_pm { - struct mtk_vcodec_clk vdec_clk; - struct mtk_vcodec_clk venc_clk; - struct device *dev; -}; - -/** - * struct vdec_pic_info - picture size information - * @pic_w: picture width - * @pic_h: picture height - * @buf_w: picture buffer width (64 aligned up from pic_w) - * @buf_h: picture buffer heiht (64 aligned up from pic_h) - * @fb_sz: bitstream size of each plane - * E.g. suppose picture size is 176x144, - * buffer size will be aligned to 176x160. - * @cap_fourcc: fourcc number(may changed when resolution change) - * @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os. - */ -struct vdec_pic_info { - unsigned int pic_w; - unsigned int pic_h; - unsigned int buf_w; - unsigned int buf_h; - unsigned int fb_sz[VIDEO_MAX_PLANES]; - unsigned int cap_fourcc; - unsigned int reserved; -}; - -/** - * struct mtk_vcodec_ctx - Context (instance) private data. - * - * @type: type of the instance - decoder or encoder - * @dev: pointer to the mtk_vcodec_dev of the device - * @list: link to ctx_list of mtk_vcodec_dev - * @fh: struct v4l2_fh - * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context - * @q_data: store information of input and output queue - * of the context - * @id: index of the context that this structure describes - * @state: state of the context - * @param_change: indicate encode parameter type - * @enc_params: encoding parameters - * @dec_if: hooked decoder driver interface - * @enc_if: hooked encoder driver interface - * @drv_handle: driver handle for specific decode/encode instance - * - * @picinfo: store picture info after header parsing - * @dpb_size: store dpb count after header parsing - * @int_cond: variable used by the waitqueue - * @int_type: type of the last interrupt - * @queue: waitqueue that can be used to wait for this context to - * finish - * @irq_status: irq status - * - * @ctrl_hdl: handler for v4l2 framework - * @decode_work: worker for the decoding - * @encode_work: worker for the encoding - * @last_decoded_picinfo: pic information get from latest decode - * @empty_flush_buf: a fake size-0 capture buffer that indicates flush. Only - * to be used with encoder and stateful decoder. - * @is_flushing: set to true if flushing is in progress. - * @current_codec: current set input codec, in V4L2 pixel format - * @capture_fourcc: capture queue type in V4L2 pixel format - * - * @colorspace: enum v4l2_colorspace; supplemental to pixelformat - * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding - * @quantization: enum v4l2_quantization, colorspace quantization - * @xfer_func: enum v4l2_xfer_func, colorspace transfer function - * @decoded_frame_cnt: number of decoded frames - * @lock: protect variables accessed by V4L2 threads and worker thread such as - * mtk_video_dec_buf. - * @hw_id: hardware index used to identify different hardware. - * - * @msg_queue: msg queue used to store lat buffer information. - * @q_mutex: vb2_queue mutex. - */ -struct mtk_vcodec_ctx { - enum mtk_instance_type type; - struct mtk_vcodec_dev *dev; - struct list_head list; - - struct v4l2_fh fh; - struct v4l2_m2m_ctx *m2m_ctx; - struct mtk_q_data q_data[2]; - int id; - enum mtk_instance_state state; - enum mtk_encode_param param_change; - struct mtk_enc_params enc_params; - - const struct vdec_common_if *dec_if; - const struct venc_common_if *enc_if; - void *drv_handle; - - struct vdec_pic_info picinfo; - int dpb_size; - - int int_cond[MTK_VDEC_HW_MAX]; - int int_type[MTK_VDEC_HW_MAX]; - wait_queue_head_t queue[MTK_VDEC_HW_MAX]; - unsigned int irq_status; - - struct v4l2_ctrl_handler ctrl_hdl; - struct work_struct decode_work; - struct work_struct encode_work; - struct vdec_pic_info last_decoded_picinfo; - struct v4l2_m2m_buffer empty_flush_buf; - bool is_flushing; - - u32 current_codec; - u32 capture_fourcc; - - enum v4l2_colorspace colorspace; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - enum v4l2_xfer_func xfer_func; - - int decoded_frame_cnt; - struct mutex lock; - int hw_id; - - struct vdec_msg_queue msg_queue; - - struct mutex q_mutex; -}; - -/* - * enum mtk_vdec_hw_arch - Used to separate different hardware architecture - */ -enum mtk_vdec_hw_arch { - MTK_VDEC_PURE_SINGLE_CORE, - MTK_VDEC_LAT_SINGLE_CORE, -}; - -/* - * struct mtk_vdec_format_types - Structure used to get supported - * format types according to decoder capability - */ -enum mtk_vdec_format_types { - MTK_VDEC_FORMAT_MM21 = 0x20, - MTK_VDEC_FORMAT_MT21C = 0x40, - MTK_VDEC_FORMAT_H264_SLICE = 0x100, - MTK_VDEC_FORMAT_VP8_FRAME = 0x200, - MTK_VDEC_FORMAT_VP9_FRAME = 0x400, - MTK_VDEC_FORMAT_AV1_FRAME = 0x800, - MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000, - MTK_VCODEC_INNER_RACING = 0x20000, -}; - -/** - * struct mtk_vcodec_dec_pdata - compatible data for each IC - * @init_vdec_params: init vdec params - * @ctrls_setup: init vcodec dec ctrls - * @worker: worker to start a decode job - * @flush_decoder: function that flushes the decoder - * @get_cap_buffer: get capture buffer from capture queue - * @cap_to_disp: put capture buffer to disp list for lat and core arch - * @vdec_vb2_ops: struct vb2_ops - * - * @vdec_formats: supported video decoder formats - * @num_formats: count of video decoder formats - * @default_out_fmt: default output buffer format - * @default_cap_fmt: default capture buffer format - * - * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core - * - * @is_subdev_supported: whether support parent-node architecture(subdev) - * @uses_stateless_api: whether the decoder uses the stateless API with requests - */ - -struct mtk_vcodec_dec_pdata { - void (*init_vdec_params)(struct mtk_vcodec_ctx *ctx); - int (*ctrls_setup)(struct mtk_vcodec_ctx *ctx); - void (*worker)(struct work_struct *work); - int (*flush_decoder)(struct mtk_vcodec_ctx *ctx); - struct vdec_fb *(*get_cap_buffer)(struct mtk_vcodec_ctx *ctx); - void (*cap_to_disp)(struct mtk_vcodec_ctx *ctx, int error, - struct media_request *src_buf_req); - - struct vb2_ops *vdec_vb2_ops; - - const struct mtk_video_fmt *vdec_formats; - const int *num_formats; - const struct mtk_video_fmt *default_out_fmt; - const struct mtk_video_fmt *default_cap_fmt; - - enum mtk_vdec_hw_arch hw_arch; - - bool is_subdev_supported; - bool uses_stateless_api; -}; - -/** - * struct mtk_vcodec_enc_pdata - compatible data for each IC - * - * @uses_ext: whether the encoder uses the extended firmware messaging format - * @min_bitrate: minimum supported encoding bitrate - * @max_bitrate: maximum supported encoding bitrate - * @capture_formats: array of supported capture formats - * @num_capture_formats: number of entries in capture_formats - * @output_formats: array of supported output formats - * @num_output_formats: number of entries in output_formats - * @core_id: stand for h264 or vp8 encode index - * @uses_34bit: whether the encoder uses 34-bit iova - */ -struct mtk_vcodec_enc_pdata { - bool uses_ext; - unsigned long min_bitrate; - unsigned long max_bitrate; - const struct mtk_video_fmt *capture_formats; - size_t num_capture_formats; - const struct mtk_video_fmt *output_formats; - size_t num_output_formats; - int core_id; - bool uses_34bit; -}; - -#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) -#define MTK_ENC_IOVA_IS_34BIT(ctx) ((ctx)->dev->venc_pdata->uses_34bit) - -/** - * struct mtk_vcodec_dev - driver data - * @v4l2_dev: V4L2 device to register video devices for. - * @vfd_dec: Video device for decoder - * @mdev_dec: Media device for decoder - * @vfd_enc: Video device for encoder. - * - * @m2m_dev_dec: m2m device for decoder - * @m2m_dev_enc: m2m device for encoder. - * @plat_dev: platform device - * @ctx_list: list of struct mtk_vcodec_ctx - * @irqlock: protect data access by irq handler and work thread - * @curr_ctx: The context that is waiting for codec hardware - * - * @reg_base: Mapped address of MTK Vcodec registers. - * @vdec_pdata: decoder IC-specific data - * @venc_pdata: encoder IC-specific data - * - * @fw_handler: used to communicate with the firmware. - * @id_counter: used to identify current opened instance - * - * @decode_workqueue: decode work queue - * @encode_workqueue: encode work queue - * - * @int_cond: used to identify interrupt condition happen - * @int_type: used to identify what kind of interrupt condition happen - * @dev_mutex: video_device lock - * @queue: waitqueue for waiting for completion of device commands - * - * @dec_irq: decoder irq resource - * @enc_irq: h264 encoder irq resource - * - * @dec_mutex: decoder hardware lock - * @enc_mutex: encoder hardware lock. - * - * @pm: power management control - * @dec_capability: used to identify decode capability, ex: 4k - * @enc_capability: used to identify encode capability - * - * @core_workqueue: queue used for core hardware decode - * - * @subdev_dev: subdev hardware device - * @subdev_prob_done: check whether all used hw device is prob done - * @subdev_bitmap: used to record hardware is ready or not - * - * @dec_active_cnt: used to mark whether need to record register value - * @vdec_racing_info: record register value - * @dec_racing_info_mutex: mutex lock used for inner racing mode - * @dbgfs: debug log related information - */ -struct mtk_vcodec_dev { - struct v4l2_device v4l2_dev; - struct video_device *vfd_dec; - struct media_device mdev_dec; - struct video_device *vfd_enc; - - struct v4l2_m2m_dev *m2m_dev_dec; - struct v4l2_m2m_dev *m2m_dev_enc; - struct platform_device *plat_dev; - struct list_head ctx_list; - spinlock_t irqlock; - struct mtk_vcodec_ctx *curr_ctx; - void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; - const struct mtk_vcodec_dec_pdata *vdec_pdata; - const struct mtk_vcodec_enc_pdata *venc_pdata; - - struct mtk_vcodec_fw *fw_handler; - - unsigned long id_counter; - - struct workqueue_struct *decode_workqueue; - struct workqueue_struct *encode_workqueue; - int int_cond; - int int_type; - struct mutex dev_mutex; - wait_queue_head_t queue; - - int dec_irq; - int enc_irq; - - /* decoder hardware mutex lock */ - struct mutex dec_mutex[MTK_VDEC_HW_MAX]; - struct mutex enc_mutex; - - struct mtk_vcodec_pm pm; - unsigned int dec_capability; - unsigned int enc_capability; - - struct workqueue_struct *core_workqueue; - - void *subdev_dev[MTK_VDEC_HW_MAX]; - int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev); - DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); - - atomic_t dec_active_cnt; - u32 vdec_racing_info[132]; - /* Protects access to vdec_racing_info data */ - struct mutex dec_racing_info_mutex; - - struct mtk_vcodec_dbgfs dbgfs; -}; - -static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct mtk_vcodec_ctx, fh); -} - -static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); -} - -/* Wake up context wait_queue */ -static inline void -wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason, unsigned int hw_id) -{ - ctx->int_cond[hw_id] = 1; - ctx->int_type[hw_id] = reason; - wake_up_interruptible(&ctx->queue[hw_id]); -} - -#endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c deleted file mode 100644 index 552b4c93d972..000000000000 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin -*/ - -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, - int command, unsigned int timeout_ms, - unsigned int hw_id) -{ - long timeout_jiff, ret; - int status = 0; - - timeout_jiff = msecs_to_jiffies(timeout_ms); - ret = wait_event_interruptible_timeout(ctx->queue[hw_id], - ctx->int_cond[hw_id], - timeout_jiff); - - if (!ret) { - status = -1; /* timeout */ - mtk_v4l2_err("[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)", - ctx->id, command, ctx->type, timeout_ms, - ctx->int_cond[hw_id], ctx->int_type[hw_id]); - } else if (-ERESTARTSYS == ret) { - status = -1; - mtk_v4l2_err("[%d] cmd=%d, type=%d, dec inter fail (%d %d)", - ctx->id, command, ctx->type, - ctx->int_cond[hw_id], ctx->int_type[hw_id]); - } - - ctx->int_cond[hw_id] = 0; - ctx->int_type[hw_id] = 0; - - return status; -} -EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h deleted file mode 100644 index 88d389b65f13..000000000000 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_UTIL_H_ -#define _MTK_VCODEC_UTIL_H_ - -#include -#include - -struct mtk_vcodec_mem { - size_t size; - void *va; - dma_addr_t dma_addr; -}; - -struct mtk_vcodec_fb { - size_t size; - dma_addr_t dma_addr; -}; - -struct mtk_vcodec_ctx; -struct mtk_vcodec_dev; - -#undef pr_fmt -#define pr_fmt(fmt) "%s(),%d: " fmt, __func__, __LINE__ - -#define mtk_v4l2_err(fmt, args...) \ - pr_err("[MTK_V4L2][ERROR] " fmt "\n", ##args) - -#define mtk_vcodec_err(h, fmt, args...) \ - pr_err("[MTK_VCODEC][ERROR][%d]: " fmt "\n", \ - ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) - -#if defined(CONFIG_DEBUG_FS) -extern int mtk_v4l2_dbg_level; -extern int mtk_vcodec_dbg; - -#define mtk_v4l2_debug(level, fmt, args...) \ - do { \ - if (mtk_v4l2_dbg_level >= (level)) \ - pr_debug("[MTK_V4L2] %s, %d: " fmt "\n", \ - __func__, __LINE__, ##args); \ - } while (0) - -#define mtk_vcodec_debug(h, fmt, args...) \ - do { \ - if (mtk_vcodec_dbg) \ - dev_dbg(&(((struct mtk_vcodec_ctx *)(h)->ctx)->dev->plat_dev->dev), \ - "[MTK_VCODEC][%d]: %s, %d " fmt "\n", \ - ((struct mtk_vcodec_ctx *)(h)->ctx)->id, \ - __func__, __LINE__, ##args); \ - } while (0) -#else -#define mtk_v4l2_debug(level, fmt, args...) pr_debug(fmt, ##args) - -#define mtk_vcodec_debug(h, fmt, args...) \ - pr_debug("[MTK_VCODEC][%d]: " fmt "\n", \ - ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) -#endif - -#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") -#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") - -#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") -#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") - -void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, - unsigned int reg_idx); -int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem); -void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem); -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - struct mtk_vcodec_ctx *ctx, int hw_idx); -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - unsigned int hw_idx); -void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx); - -#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c index 4c8f5296d120..7243604a82a5 100644 --- a/drivers/media/platform/mediatek/vpu/mtk_vpu.c +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c @@ -9,10 +9,10 @@ #include #include #include -#include -#include +#include #include #include +#include #include #include #include diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c index bfb3edcf018a..988c1cc1d8b6 100644 --- a/drivers/media/platform/microchip/microchip-csi2dc.c +++ b/drivers/media/platform/microchip/microchip-csi2dc.c @@ -476,7 +476,7 @@ static const struct v4l2_subdev_ops csi2dc_subdev_ops = { static int csi2dc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct csi2dc_device *csi2dc = container_of(notifier, struct csi2dc_device, notifier); @@ -520,14 +520,14 @@ static const struct v4l2_async_notifier_operations csi2dc_async_ops = { static int csi2dc_prepare_notifier(struct csi2dc_device *csi2dc, struct fwnode_handle *input_fwnode) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret = 0; - v4l2_async_nf_init(&csi2dc->notifier); + v4l2_async_subdev_nf_init(&csi2dc->notifier, &csi2dc->csi2dc_sd); asd = v4l2_async_nf_add_fwnode_remote(&csi2dc->notifier, input_fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(input_fwnode); @@ -542,8 +542,7 @@ static int csi2dc_prepare_notifier(struct csi2dc_device *csi2dc, csi2dc->notifier.ops = &csi2dc_async_ops; - ret = v4l2_async_subdev_nf_register(&csi2dc->csi2dc_sd, - &csi2dc->notifier); + ret = v4l2_async_nf_register(&csi2dc->notifier); if (ret) { dev_err(csi2dc->dev, "fail to register async notifier: %d\n", ret); diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index 4e657fad33d0..8dbf7bc1e863 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -1712,7 +1712,7 @@ static int isc_ctrl_init(struct isc_device *isc) static int isc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct isc_device *isc = container_of(notifier->v4l2_dev, struct isc_device, v4l2_dev); @@ -1741,7 +1741,7 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier, static void isc_async_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct isc_device *isc = container_of(notifier->v4l2_dev, struct isc_device, v4l2_dev); diff --git a/drivers/media/platform/microchip/microchip-isc.h b/drivers/media/platform/microchip/microchip-isc.h index e3a6c7367e70..ad4e98a1dd8f 100644 --- a/drivers/media/platform/microchip/microchip-isc.h +++ b/drivers/media/platform/microchip/microchip-isc.h @@ -44,7 +44,7 @@ struct isc_buffer { struct isc_subdev_entity { struct v4l2_subdev *sd; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *epn; struct v4l2_async_notifier notifier; diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c index 746f4a2fa9f6..5ac149cf3647 100644 --- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c +++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c @@ -409,7 +409,6 @@ static int microchip_isc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct isc_device *isc; - struct resource *res; void __iomem *io_base; struct isc_subdev_entity *subdev_entity; int irq; @@ -423,8 +422,7 @@ static int microchip_isc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, isc); isc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - io_base = devm_ioremap_resource(dev, res); + io_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(io_base)) return PTR_ERR(io_base); @@ -525,15 +523,15 @@ static int microchip_isc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode = of_fwnode_handle(subdev_entity->epn); - v4l2_async_nf_init(&subdev_entity->notifier); + v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(subdev_entity->epn); subdev_entity->epn = NULL; @@ -545,8 +543,7 @@ static int microchip_isc_probe(struct platform_device *pdev) subdev_entity->notifier.ops = µchip_isc_async_ops; - ret = v4l2_async_nf_register(&isc->v4l2_dev, - &subdev_entity->notifier); + ret = v4l2_async_nf_register(&subdev_entity->notifier); if (ret) { dev_err(dev, "fail to register async notifier\n"); goto cleanup_subdev; diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c index 79ae696764d0..73445f33d26b 100644 --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c @@ -398,7 +398,6 @@ static int microchip_xisc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct isc_device *isc; - struct resource *res; void __iomem *io_base; struct isc_subdev_entity *subdev_entity; int irq; @@ -412,8 +411,7 @@ static int microchip_xisc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, isc); isc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - io_base = devm_ioremap_resource(dev, res); + io_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(io_base)) return PTR_ERR(io_base); @@ -515,15 +513,15 @@ static int microchip_xisc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode = of_fwnode_handle(subdev_entity->epn); - v4l2_async_nf_init(&subdev_entity->notifier); + v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(subdev_entity->epn); subdev_entity->epn = NULL; @@ -535,8 +533,7 @@ static int microchip_xisc_probe(struct platform_device *pdev) subdev_entity->notifier.ops = µchip_isc_async_ops; - ret = v4l2_async_nf_register(&isc->v4l2_dev, - &subdev_entity->notifier); + ret = v4l2_async_nf_register(&subdev_entity->notifier); if (ret) { dev_err(dev, "fail to register async notifier\n"); goto cleanup_subdev; diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c index 7157734a1550..81a0d3b76b88 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.c +++ b/drivers/media/platform/nvidia/tegra-vde/vde.c @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig index a0ca6b297fb8..40e3436669e2 100644 --- a/drivers/media/platform/nxp/Kconfig +++ b/drivers/media/platform/nxp/Kconfig @@ -17,6 +17,17 @@ config VIDEO_IMX7_CSI Driver for the NXP Camera Sensor Interface (CSI) Bridge. This device is found in the i.MX6UL/L, i.MX7 and i.MX8M[MQ] SoCs. +config VIDEO_IMX8MQ_MIPI_CSI2 + tristate "NXP i.MX8MQ MIPI CSI-2 receiver" + depends on ARCH_MXC || COMPILE_TEST + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Video4Linux2 driver for the MIPI CSI-2 receiver found on the i.MX8MQ + SoC. + config VIDEO_IMX_MIPI_CSIS tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile index b8e672b75fed..4d90eb713652 100644 --- a/drivers/media/platform/nxp/Makefile +++ b/drivers/media/platform/nxp/Makefile @@ -5,6 +5,7 @@ obj-y += imx-jpeg/ obj-y += imx8-isi/ obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o +obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 9512c0a61966..b7a720198ce5 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -2742,7 +2742,6 @@ static int mxc_jpeg_probe(struct platform_device *pdev) dev_info(&pdev->dev, "choose slot %d\n", jpeg->slot_data.slot); dec_irq = platform_get_irq(pdev, 0); if (dec_irq < 0) { - dev_err(&pdev->dev, "Failed to get irq %d\n", dec_irq); ret = dec_irq; goto err_irq; } diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 05d52762e792..16f19a640130 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -1230,7 +1229,7 @@ mipi_notifier_to_csis_state(struct v4l2_async_notifier *n) static int mipi_csis_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct mipi_csis_device *csis = mipi_notifier_to_csis_state(notifier); struct media_pad *sink = &csis->sd.entity.pads[CSIS_PAD_SINK]; @@ -1247,12 +1246,12 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; unsigned int i; int ret; - v4l2_async_nf_init(&csis->notifier); + v4l2_async_subdev_nf_init(&csis->notifier, &csis->sd); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); @@ -1278,7 +1277,7 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) dev_dbg(csis->dev, "flags: 0x%08x\n", csis->bus.flags); asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto err_parse; @@ -1288,7 +1287,7 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) csis->notifier.ops = &mipi_csis_notify_ops; - ret = v4l2_async_subdev_nf_register(&csis->sd, &csis->notifier); + ret = v4l2_async_nf_register(&csis->notifier); if (ret) return ret; @@ -1365,13 +1364,6 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) sd->dev = csis->dev; - sd->fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), - 1, 0, 0); - if (!sd->fwnode) { - dev_err(csis->dev, "Unable to retrieve endpoint for port@1\n"); - return -ENOENT; - } - csis->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; csis->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index 90f319857c23..e62dc5c1a4ae 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 791bde67f439..15049c6aab37 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -1076,6 +1076,7 @@ static int imx7_csi_video_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct imx7_csi_pixfmt *cc; + u32 walign; if (fsize->index > 0) return -EINVAL; @@ -1085,16 +1086,17 @@ static int imx7_csi_video_enum_framesizes(struct file *file, void *fh, return -EINVAL; /* - * TODO: The constraints are hardware-specific and may depend on the - * pixel format. This should come from the driver using - * imx_media_capture. + * The width alignment is 8 bytes as indicated by the + * CSI_IMAG_PARA.IMAGE_WIDTH documentation. Convert it to pixels. */ + walign = 8 * 8 / cc->bpp; + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = 1; - fsize->stepwise.max_width = 65535; + fsize->stepwise.min_width = walign; + fsize->stepwise.max_width = round_down(65535U, walign); fsize->stepwise.min_height = 1; fsize->stepwise.max_height = 65535; - fsize->stepwise.step_width = 1; + fsize->stepwise.step_width = walign; fsize->stepwise.step_height = 1; return 0; @@ -2035,7 +2037,7 @@ static const struct media_entity_operations imx7_csi_entity_ops = { static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK]; @@ -2060,11 +2062,11 @@ static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = { static int imx7_csi_async_register(struct imx7_csi *csi) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int ret; - v4l2_async_nf_init(&csi->notifier); + v4l2_async_nf_init(&csi->notifier, &csi->v4l2_dev); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); @@ -2075,7 +2077,7 @@ static int imx7_csi_async_register(struct imx7_csi *csi) } asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); @@ -2087,7 +2089,7 @@ static int imx7_csi_async_register(struct imx7_csi *csi) csi->notifier.ops = &imx7_csi_notify_ops; - ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier); + ret = v4l2_async_nf_register(&csi->notifier); if (ret) goto error; diff --git a/drivers/media/platform/nxp/imx8-isi/Makefile b/drivers/media/platform/nxp/imx8-isi/Makefile index 9bff9297686d..4713c4e8b64b 100644 --- a/drivers/media/platform/nxp/imx8-isi/Makefile +++ b/drivers/media/platform/nxp/imx8-isi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -imx8-isi-y := imx8-isi-core.o imx8-isi-crossbar.o imx8-isi-hw.o \ - imx8-isi-pipe.o imx8-isi-video.o +imx8-isi-y := imx8-isi-core.o imx8-isi-crossbar.o imx8-isi-gasket.o \ + imx8-isi-hw.o imx8-isi-pipe.o imx8-isi-video.o imx8-isi-$(CONFIG_DEBUG_FS) += imx8-isi-debug.o imx8-isi-$(CONFIG_VIDEO_IMX8_ISI_M2M) += imx8-isi-m2m.o diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 253e77189b69..81be744e9f1b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,12 +30,12 @@ */ struct mxc_isi_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; unsigned int port; }; static inline struct mxc_isi_async_subdev * -asd_to_mxc_isi_async_subdev(struct v4l2_async_subdev *asd) +asd_to_mxc_isi_async_subdev(struct v4l2_async_connection *asd) { return container_of(asd, struct mxc_isi_async_subdev, asd); }; @@ -48,12 +48,12 @@ notifier_to_mxc_isi_dev(struct v4l2_async_notifier *n) static int mxc_isi_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier); - struct mxc_isi_async_subdev *masd = asd_to_mxc_isi_async_subdev(asd); + struct mxc_isi_async_subdev *masd = asd_to_mxc_isi_async_subdev(asc); struct media_pad *pad = &isi->crossbar.pads[masd->port]; struct device_link *link; @@ -175,7 +175,7 @@ static int mxc_isi_v4l2_init(struct mxc_isi_dev *isi) } /* Initialize, fill and register the async notifier. */ - v4l2_async_nf_init(&isi->notifier); + v4l2_async_nf_init(&isi->notifier, v4l2_dev); isi->notifier.ops = &mxc_isi_async_notifier_ops; for (i = 0; i < isi->pdata->num_ports; ++i) { @@ -200,7 +200,7 @@ static int mxc_isi_v4l2_init(struct mxc_isi_dev *isi) masd->port = i; } - ret = v4l2_async_nf_register(v4l2_dev, &isi->notifier); + ret = v4l2_async_nf_register(&isi->notifier); if (ret < 0) { dev_err(isi->dev, "Failed to register async notifier: %d\n", ret); @@ -289,7 +289,7 @@ static const struct mxc_isi_plat_data mxc_imx8mn_data = { .clks = mxc_imx8mn_clks, .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = false, - .has_gasket = true, + .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = false, }; @@ -303,10 +303,24 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .clks = mxc_imx8mn_clks, .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = true, - .has_gasket = true, + .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = true, }; +static const struct mxc_isi_plat_data mxc_imx93_data = { + .model = MXC_ISI_IMX93, + .num_ports = 1, + .num_channels = 1, + .reg_offset = 0, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .clks = mxc_imx8mn_clks, + .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), + .buf_active_reverse = true, + .gasket_ops = &mxc_imx93_gasket_ops, + .has_36bit_dma = false, +}; + /* ----------------------------------------------------------------------------- * Power management */ @@ -443,7 +457,7 @@ static int mxc_isi_probe(struct platform_device *pdev) return PTR_ERR(isi->regs); } - if (isi->pdata->has_gasket) { + if (isi->pdata->gasket_ops) { isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,blk-ctrl"); if (IS_ERR(isi->gasket)) { @@ -518,6 +532,7 @@ static int mxc_isi_remove(struct platform_device *pdev) static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, + { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, mxc_isi_of_match); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index e469788a9e6c..2810ebe9b5f7 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -147,9 +147,18 @@ struct mxc_isi_set_thd { struct mxc_isi_panic_thd panic_set_thd_v; }; +struct mxc_gasket_ops { + void (*enable)(struct mxc_isi_dev *isi, + const struct v4l2_mbus_frame_desc *fd, + const struct v4l2_mbus_framefmt *fmt, + const unsigned int port); + void (*disable)(struct mxc_isi_dev *isi, const unsigned int port); +}; + enum model { MXC_ISI_IMX8MN, MXC_ISI_IMX8MP, + MXC_ISI_IMX93, }; struct mxc_isi_plat_data { @@ -159,10 +168,10 @@ struct mxc_isi_plat_data { unsigned int reg_offset; const struct mxc_isi_ier_reg *ier_reg; const struct mxc_isi_set_thd *set_thd; + const struct mxc_gasket_ops *gasket_ops; const struct clk_bulk_data *clks; unsigned int num_clks; bool buf_active_reverse; - bool has_gasket; bool has_36bit_dma; }; @@ -286,6 +295,9 @@ struct mxc_isi_dev { struct dentry *debugfs_root; }; +extern const struct mxc_gasket_ops mxc_imx8_gasket_ops; +extern const struct mxc_gasket_ops mxc_imx93_gasket_ops; + int mxc_isi_crossbar_init(struct mxc_isi_dev *isi); void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar); int mxc_isi_crossbar_register(struct mxc_isi_crossbar *xbar); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index f7447b2f4d77..792f031e032a 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -15,7 +15,6 @@ #include #include -#include #include #include "imx8-isi-core.h" @@ -25,32 +24,18 @@ static inline struct mxc_isi_crossbar *to_isi_crossbar(struct v4l2_subdev *sd) return container_of(sd, struct mxc_isi_crossbar, sd); } -/* ----------------------------------------------------------------------------- - * Media block control (i.MX8MN and i.MX8MP only) - */ -#define GASKET_BASE(n) (0x0060 + (n) * 0x30) - -#define GASKET_CTRL 0x0000 -#define GASKET_CTRL_DATA_TYPE(dt) ((dt) << 8) -#define GASKET_CTRL_DATA_TYPE_MASK (0x3f << 8) -#define GASKET_CTRL_DUAL_COMP_ENABLE BIT(1) -#define GASKET_CTRL_ENABLE BIT(0) - -#define GASKET_HSIZE 0x0004 -#define GASKET_VSIZE 0x0008 - static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, struct v4l2_subdev_state *state, struct v4l2_subdev *remote_sd, u32 remote_pad, unsigned int port) { struct mxc_isi_dev *isi = xbar->isi; + const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops; const struct v4l2_mbus_framefmt *fmt; struct v4l2_mbus_frame_desc fd; - u32 val; int ret; - if (!isi->pdata->has_gasket) + if (!gasket_ops) return 0; /* @@ -77,17 +62,7 @@ static int mxc_isi_crossbar_gasket_enable(struct mxc_isi_crossbar *xbar, if (!fmt) return -EINVAL; - regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_HSIZE, fmt->width); - regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_VSIZE, fmt->height); - - val = GASKET_CTRL_DATA_TYPE(fd.entry[0].bus.csi2.dt) - | GASKET_CTRL_ENABLE; - - if (fd.entry[0].bus.csi2.dt == MIPI_CSI2_DT_YUV422_8B) - val |= GASKET_CTRL_DUAL_COMP_ENABLE; - - regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, val); - + gasket_ops->enable(isi, &fd, fmt, port); return 0; } @@ -95,11 +70,12 @@ static void mxc_isi_crossbar_gasket_disable(struct mxc_isi_crossbar *xbar, unsigned int port) { struct mxc_isi_dev *isi = xbar->isi; + const struct mxc_gasket_ops *gasket_ops = isi->pdata->gasket_ops; - if (!isi->pdata->has_gasket) + if (!gasket_ops) return; - regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, 0); + gasket_ops->disable(isi, port); } /* ----------------------------------------------------------------------------- @@ -483,7 +459,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) xbar->inputs = kcalloc(xbar->num_sinks, sizeof(*xbar->inputs), GFP_KERNEL); - if (!xbar->pads) { + if (!xbar->inputs) { ret = -ENOMEM; goto err_free; } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c new file mode 100644 index 000000000000..f69c3b5d4782 --- /dev/null +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019-2023 NXP + */ + +#include + +#include + +#include "imx8-isi-core.h" + +/* ----------------------------------------------------------------------------- + * i.MX8MN and i.MX8MP gasket + */ + +#define GASKET_BASE(n) (0x0060 + (n) * 0x30) + +#define GASKET_CTRL 0x0000 +#define GASKET_CTRL_DATA_TYPE(dt) ((dt) << 8) +#define GASKET_CTRL_DATA_TYPE_MASK (0x3f << 8) +#define GASKET_CTRL_DUAL_COMP_ENABLE BIT(1) +#define GASKET_CTRL_ENABLE BIT(0) + +#define GASKET_HSIZE 0x0004 +#define GASKET_VSIZE 0x0008 + +static void mxc_imx8_gasket_enable(struct mxc_isi_dev *isi, + const struct v4l2_mbus_frame_desc *fd, + const struct v4l2_mbus_framefmt *fmt, + const unsigned int port) +{ + u32 val; + + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_HSIZE, fmt->width); + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_VSIZE, fmt->height); + + val = GASKET_CTRL_DATA_TYPE(fd->entry[0].bus.csi2.dt); + if (fd->entry[0].bus.csi2.dt == MIPI_CSI2_DT_YUV422_8B) + val |= GASKET_CTRL_DUAL_COMP_ENABLE; + + val |= GASKET_CTRL_ENABLE; + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, val); +} + +static void mxc_imx8_gasket_disable(struct mxc_isi_dev *isi, + const unsigned int port) +{ + regmap_write(isi->gasket, GASKET_BASE(port) + GASKET_CTRL, 0); +} + +const struct mxc_gasket_ops mxc_imx8_gasket_ops = { + .enable = mxc_imx8_gasket_enable, + .disable = mxc_imx8_gasket_disable, +}; + +/* ----------------------------------------------------------------------------- + * i.MX93 gasket + */ + +#define DISP_MIX_CAMERA_MUX 0x30 +#define DISP_MIX_CAMERA_MUX_DATA_TYPE(x) (((x) & 0x3f) << 3) +#define DISP_MIX_CAMERA_MUX_GASKET_ENABLE BIT(16) + +static void mxc_imx93_gasket_enable(struct mxc_isi_dev *isi, + const struct v4l2_mbus_frame_desc *fd, + const struct v4l2_mbus_framefmt *fmt, + const unsigned int port) +{ + u32 val; + + val = DISP_MIX_CAMERA_MUX_DATA_TYPE(fd->entry[0].bus.csi2.dt); + val |= DISP_MIX_CAMERA_MUX_GASKET_ENABLE; + regmap_write(isi->gasket, DISP_MIX_CAMERA_MUX, val); +} + +static void mxc_imx93_gasket_disable(struct mxc_isi_dev *isi, + unsigned int port) +{ + regmap_write(isi->gasket, DISP_MIX_CAMERA_MUX, 0); +} + +const struct mxc_gasket_ops mxc_imx93_gasket_ops = { + .enable = mxc_imx93_gasket_enable, + .disable = mxc_imx93_gasket_disable, +}; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index c4454aa1cb34..65d20e9bae69 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -791,7 +791,6 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) irq = platform_get_irq(to_platform_device(isi->dev), id); if (irq < 0) { - dev_err(pipe->isi->dev, "Failed to get IRQ (%d)\n", irq); ret = irq; goto error; } diff --git a/drivers/staging/media/imx/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c similarity index 99% rename from drivers/staging/media/imx/imx8mq-mipi-csi2.c rename to drivers/media/platform/nxp/imx8mq-mipi-csi2.c index c84b6dceece2..ed048f73c982 100644 --- a/drivers/staging/media/imx/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -566,7 +566,7 @@ mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n) static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct csi_state *state = mipi_notifier_to_csi2_state(notifier); struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK]; @@ -586,12 +586,12 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; unsigned int i; int ret; - v4l2_async_nf_init(&state->notifier); + v4l2_async_subdev_nf_init(&state->notifier, &state->sd); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); @@ -618,7 +618,7 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) state->bus.flags); asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto err_parse; @@ -628,7 +628,7 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) state->notifier.ops = &imx8mq_mipi_csi_notify_ops; - ret = v4l2_async_subdev_nf_register(&state->sd, &state->notifier); + ret = v4l2_async_nf_register(&state->notifier); if (ret) return ret; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 1ef26aea3eae..f11dc59135a5 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1383,7 +1383,7 @@ static void camss_unregister_entities(struct camss *camss) static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct camss *camss = container_of(async, struct camss, notifier); struct camss_async_subdev *csd = @@ -1615,14 +1615,6 @@ static int camss_probe(struct platform_device *pdev) if (!camss->vfe) return -ENOMEM; - v4l2_async_nf_init(&camss->notifier); - - num_subdevs = camss_of_parse_ports(camss); - if (num_subdevs < 0) { - ret = num_subdevs; - goto err_cleanup; - } - ret = camss_icc_get(camss); if (ret < 0) goto err_cleanup; @@ -1648,15 +1640,22 @@ static int camss_probe(struct platform_device *pdev) goto err_cleanup; } + v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev); + + num_subdevs = camss_of_parse_ports(camss); + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cleanup; + } + ret = camss_register_entities(camss); if (ret < 0) - goto err_register_entities; + goto err_cleanup; if (num_subdevs) { camss->notifier.ops = &camss_subdev_notifier_ops; - ret = v4l2_async_nf_register(&camss->v4l2_dev, - &camss->notifier); + ret = v4l2_async_nf_register(&camss->notifier); if (ret) { dev_err(dev, "Failed to register async subdev nodes: %d\n", @@ -1691,9 +1690,8 @@ static int camss_probe(struct platform_device *pdev) err_register_subdevs: camss_unregister_entities(camss); -err_register_entities: - v4l2_device_unregister(&camss->v4l2_dev); err_cleanup: + v4l2_device_unregister(&camss->v4l2_dev); v4l2_async_nf_cleanup(&camss->notifier); return ret; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 3acd2b3403e8..f6c326cb853b 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -113,7 +113,7 @@ struct camss_camera_interface { }; struct camss_async_subdev { - struct v4l2_async_subdev asd; /* must be first */ + struct v4l2_async_connection asd; /* must be first */ struct camss_camera_interface interface; }; diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 2ae867cb4c48..054b8e74ba4f 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -246,7 +247,7 @@ err: static void venus_assign_register_offsets(struct venus_core *core) { - if (IS_V6(core)) { + if (IS_IRIS2(core) || IS_IRIS2_1(core)) { core->vbif_base = core->base + VBIF_BASE; core->cpu_base = core->base + CPU_BASE_V6; core->cpu_cs_base = core->base + CPU_CS_BASE_V6; @@ -684,6 +685,7 @@ static const struct venus_resources sdm845_res = { .vcodec_clks_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, + .vpu_version = VPU_VERSION_AR50, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, .vmem_addr = 0, @@ -709,6 +711,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, + .vpu_version = VPU_VERSION_AR50, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, .vmem_addr = 0, @@ -756,10 +759,15 @@ static const struct venus_resources sc7180_res = { .opp_pmdomain = (const char *[]) { "cx", NULL }, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, + .vpu_version = VPU_VERSION_AR50, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, + .cp_start = 0, + .cp_size = 0x70800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.4/venus.mdt", }; @@ -809,12 +817,13 @@ static const struct venus_resources sm8250_res = { .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, + .vpu_version = VPU_VERSION_IRIS2, .num_vpp_pipes = 4, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mdt", + .fwname = "qcom/vpu-1.0/venus.mbn", }; static const struct freq_tbl sc7280_freq_table[] = { @@ -866,6 +875,7 @@ static const struct venus_resources sc7280_res = { .opp_pmdomain = (const char *[]) { "cx", NULL }, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, + .vpu_version = VPU_VERSION_IRIS2_1, .num_vpp_pipes = 1, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 320bde0f83cb..4a633261ece4 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -48,6 +48,14 @@ struct bw_tbl { u32 peak_10bit; }; +enum vpu_version { + VPU_VERSION_AR50, + VPU_VERSION_AR50_LITE, + VPU_VERSION_IRIS1, + VPU_VERSION_IRIS2, + VPU_VERSION_IRIS2_1, +}; + struct venus_resources { u64 dma_mask; const struct freq_tbl *freq_tbl; @@ -71,6 +79,7 @@ struct venus_resources { const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; enum hfi_version hfi_version; + enum vpu_version vpu_version; u8 num_vpp_pipes; u32 max_load; unsigned int vmem_id; @@ -160,6 +169,7 @@ struct venus_format { * @core0_usage_count: usage counter for core0 * @core1_usage_count: usage counter for core1 * @root: debugfs root directory + * @venus_ver: the venus firmware version */ struct venus_core { void __iomem *base; @@ -386,7 +396,8 @@ enum venus_inst_modes { * @ycbcr_enc: current YCbCr encoding * @quantization: current quantization * @xfer_func: current xfer function - * @codec_state: current codec API state (see DEC/ENC_STATE_) + * @codec_state: current decoder API state (see DEC_STATE_) + * @enc_state: current encoder API state (see ENC_STATE_) * @reconf_wait: wait queue for resolution change event * @subscriptions: used to hold current events subscriptions * @buf_count: used to count number of buffers (reqbuf(0)) @@ -505,6 +516,12 @@ struct venus_inst { #define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) #define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX) +#define IS_AR50(core) ((core)->res->vpu_version == VPU_VERSION_AR50) +#define IS_AR50_LITE(core) ((core)->res->vpu_version == VPU_VERSION_AR50_LITE) +#define IS_IRIS1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS1) +#define IS_IRIS2(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2) +#define IS_IRIS2_1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2_1) + #define ctrl_to_inst(ctrl) \ container_of((ctrl)->handler, struct venus_inst, ctrl_handler) diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index cfb11c551167..fe7da2b30482 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,7 @@ static void venus_reset_cpu(struct venus_core *core) u32 fw_size = core->fw.mapped_mem_size; void __iomem *wrapper_base; - if (IS_V6(core)) + if (IS_IRIS2_1(core)) wrapper_base = core->wrapper_tz_base; else wrapper_base = core->wrapper_base; @@ -41,7 +42,7 @@ static void venus_reset_cpu(struct venus_core *core) writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR); writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR); - if (IS_V6(core)) { + if (IS_IRIS2_1(core)) { /* Bring XTSS out of reset */ writel(0, wrapper_base + WRAPPER_TZ_XTSS_SW_RESET); } else { @@ -67,7 +68,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume) if (resume) { venus_reset_cpu(core); } else { - if (IS_V6(core)) + if (IS_IRIS2_1(core)) writel(WRAPPER_XTSS_SW_RESET_BIT, core->wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET); else @@ -82,9 +83,9 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, phys_addr_t *mem_phys, size_t *mem_size) { const struct firmware *mdt; + struct reserved_mem *rmem; struct device_node *node; struct device *dev; - struct resource r; ssize_t fw_size; void *mem_va; int ret; @@ -99,13 +100,16 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); - if (ret) - goto err_put_node; + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(dev, "failed to lookup reserved memory-region\n"); + return -EINVAL; + } ret = request_firmware(&mdt, fwname, dev); if (ret < 0) - goto err_put_node; + return ret; fw_size = qcom_mdt_get_size(mdt); if (fw_size < 0) { @@ -113,17 +117,17 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, goto err_release_fw; } - *mem_phys = r.start; - *mem_size = resource_size(&r); + *mem_phys = rmem->base; + *mem_size = rmem->size; if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) { ret = -EINVAL; goto err_release_fw; } - mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); + mem_va = memremap(*mem_phys, *mem_size, MEMREMAP_WC); if (!mem_va) { - dev_err(dev, "unable to map memory region: %pR\n", &r); + dev_err(dev, "unable to map memory region %pa size %#zx\n", mem_phys, *mem_size); ret = -ENOMEM; goto err_release_fw; } @@ -138,8 +142,6 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, memunmap(mem_va); err_release_fw: release_firmware(mdt); -err_put_node: - of_node_put(node); return ret; } @@ -179,7 +181,7 @@ static int venus_shutdown_no_tz(struct venus_core *core) void __iomem *wrapper_base = core->wrapper_base; void __iomem *wrapper_tz_base = core->wrapper_tz_base; - if (IS_V6(core)) { + if (IS_IRIS2_1(core)) { /* Assert the reset to XTSS */ reg = readl(wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET); reg |= WRAPPER_XTSS_SW_RESET_BIT; @@ -241,6 +243,16 @@ int venus_boot(struct venus_core *core) return ret; if (core->use_tz && res->cp_size) { + /* + * Clues for porting using downstream data: + * cp_start = 0 + * cp_size = venus_ns/virtual-addr-pool[0] - yes, address and not size! + * This works, as the non-secure context bank is placed + * contiguously right after the Content Protection region. + * + * cp_nonpixel_start = venus_sec_non_pixel/virtual-addr-pool[0] + * cp_nonpixel_size = venus_sec_non_pixel/virtual-addr-pool[1] + */ ret = qcom_scm_mem_protect_video_var(res->cp_start, res->cp_size, res->cp_nonpixel_start, diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 1822e85ab6bf..8295542e1a7c 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -189,7 +189,7 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) if (ret) return ret; - count = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + count = hfi_bufreq_get_count_min(&bufreq, ver); for (i = 0; i < count; i++) { buf = kzalloc(sizeof(*buf), GFP_KERNEL); @@ -668,6 +668,7 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { u32 ptype = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS; + enum hfi_version ver = inst->core->res->hfi_version; union hfi_get_property hprop; unsigned int i; int ret; @@ -675,12 +676,12 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, memset(req, 0, sizeof(*req)); if (type == HFI_BUFFER_OUTPUT || type == HFI_BUFFER_OUTPUT2) - req->count_min = inst->fw_min_cnt; + hfi_bufreq_set_count_min(req, ver, inst->fw_min_cnt); ret = platform_get_bufreq(inst, type, req); if (!ret) { if (type == HFI_BUFFER_OUTPUT || type == HFI_BUFFER_OUTPUT2) - inst->fw_min_cnt = req->count_min; + inst->fw_min_cnt = hfi_bufreq_get_count_min(req, ver); return 0; } diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 0abbc50c5864..e4c05d62cfc7 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -1170,14 +1170,6 @@ struct hfi_buffer_display_hold_count_actual { u32 hold_count; }; -/* HFI 4XX reorder the fields, use these macros */ -#define HFI_BUFREQ_HOLD_COUNT(bufreq, ver) \ - ((ver) == HFI_VERSION_4XX ? 0 : (bufreq)->hold_count) -#define HFI_BUFREQ_COUNT_MIN(bufreq, ver) \ - ((ver) == HFI_VERSION_4XX ? (bufreq)->hold_count : (bufreq)->count_min) -#define HFI_BUFREQ_COUNT_MIN_HOST(bufreq, ver) \ - ((ver) == HFI_VERSION_4XX ? (bufreq)->count_min : 0) - struct hfi_buffer_requirements { u32 type; u32 size; @@ -1189,6 +1181,59 @@ struct hfi_buffer_requirements { u32 alignment; }; +/* On HFI 4XX, some of the struct members have been swapped. */ +static inline u32 hfi_bufreq_get_hold_count(struct hfi_buffer_requirements *req, + u32 ver) +{ + if (ver == HFI_VERSION_4XX) + return 0; + + return req->hold_count; +}; + +static inline u32 hfi_bufreq_get_count_min(struct hfi_buffer_requirements *req, + u32 ver) +{ + if (ver == HFI_VERSION_4XX) + return req->hold_count; + + return req->count_min; +}; + +static inline u32 hfi_bufreq_get_count_min_host(struct hfi_buffer_requirements *req, + u32 ver) +{ + if (ver == HFI_VERSION_4XX) + return req->count_min; + + return 0; +}; + +static inline void hfi_bufreq_set_hold_count(struct hfi_buffer_requirements *req, + u32 ver, u32 val) +{ + if (ver == HFI_VERSION_4XX) + return; + + req->hold_count = val; +}; + +static inline void hfi_bufreq_set_count_min(struct hfi_buffer_requirements *req, + u32 ver, u32 val) +{ + if (ver == HFI_VERSION_4XX) + req->hold_count = val; + + req->count_min = val; +}; + +static inline void hfi_bufreq_set_count_min_host(struct hfi_buffer_requirements *req, + u32 ver, u32 val) +{ + if (ver == HFI_VERSION_4XX) + req->count_min = val; +}; + struct hfi_data_payload { u32 size; u8 data[1]; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 3d5dadfa1900..7cab685a2ec8 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -99,7 +99,7 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: data_ptr += sizeof(u32); bufreq = (struct hfi_buffer_requirements *)data_ptr; - event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver); + event.buf_count = hfi_bufreq_get_count_min(bufreq, ver); data_ptr += sizeof(*bufreq); break; case HFI_INDEX_EXTRADATA_INPUT_CROP: diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c index e97ff8cf6d64..f5a655973c08 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -1215,24 +1215,24 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, out_min_count = output_buffer_count(VIDC_SESSION_TYPE_DEC, codec); /* Max of driver and FW count */ - out_min_count = max(out_min_count, bufreq->count_min); + out_min_count = max(out_min_count, hfi_bufreq_get_count_min(bufreq, version)); bufreq->type = buftype; bufreq->region_size = 0; - bufreq->count_min = 1; bufreq->count_actual = 1; - bufreq->hold_count = 1; + hfi_bufreq_set_count_min(bufreq, version, 1); + hfi_bufreq_set_hold_count(bufreq, version, 1); bufreq->contiguous = 1; bufreq->alignment = 256; if (buftype == HFI_BUFFER_INPUT) { - bufreq->count_min = MIN_INPUT_BUFFERS; + hfi_bufreq_set_count_min(bufreq, version, MIN_INPUT_BUFFERS); bufreq->size = calculate_dec_input_frame_size(width, height, codec, max_mbs_per_frame, buffer_size_limit); } else if (buftype == HFI_BUFFER_OUTPUT || buftype == HFI_BUFFER_OUTPUT2) { - bufreq->count_min = out_min_count; + hfi_bufreq_set_count_min(bufreq, version, out_min_count); bufreq->size = venus_helper_get_framesz_raw(params->hfi_color_fmt, out_width, out_height); @@ -1269,7 +1269,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, u32 work_mode = params->enc.work_mode; u32 rc_type = params->enc.rc_type; u32 num_vpp_pipes = params->num_vpp_pipes; - u32 num_ref; + u32 num_ref, count_min; switch (codec) { case V4L2_PIX_FMT_H264: @@ -1289,21 +1289,21 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, bufreq->type = buftype; bufreq->region_size = 0; - bufreq->count_min = 1; bufreq->count_actual = 1; - bufreq->hold_count = 1; + hfi_bufreq_set_count_min(bufreq, version, 1); + hfi_bufreq_set_hold_count(bufreq, version, 1); bufreq->contiguous = 1; bufreq->alignment = 256; if (buftype == HFI_BUFFER_INPUT) { - bufreq->count_min = MIN_INPUT_BUFFERS; + hfi_bufreq_set_count_min(bufreq, version, MIN_INPUT_BUFFERS); bufreq->size = venus_helper_get_framesz_raw(params->hfi_color_fmt, width, height); } else if (buftype == HFI_BUFFER_OUTPUT || buftype == HFI_BUFFER_OUTPUT2) { - bufreq->count_min = - output_buffer_count(VIDC_SESSION_TYPE_ENC, codec); + count_min = output_buffer_count(VIDC_SESSION_TYPE_ENC, codec); + hfi_bufreq_set_count_min(bufreq, version, count_min); bufreq->size = calculate_enc_output_frame_size(width, height, rc_type); } else if (buftype == HFI_BUFFER_INTERNAL_SCRATCH(version)) { diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index f07f554bc5fe..643e5aa138f5 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include +#include #include "hfi_platform.h" #include "core.h" @@ -80,7 +80,7 @@ hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, u32 *dec_codec if (plat->codecs) plat->codecs(enc_codecs, dec_codecs, count); - if (of_device_is_compatible(core->dev->of_node, "qcom,sc7280-venus")) { + if (IS_IRIS2_1(core)) { *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; } diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index f0b46389e8d5..19fc6575a489 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -131,7 +131,6 @@ struct venus_hfi_device { static bool venus_pkt_debug; int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL; -static bool venus_sys_idle_indicator; static bool venus_fw_low_power_mode = true; static int venus_hw_rsp_timeout = 1000; static bool venus_fw_coverage; @@ -448,23 +447,25 @@ static int venus_boot_core(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; static const unsigned int max_tries = 100; - u32 ctrl_status = 0, mask_val; + u32 ctrl_status = 0, mask_val = 0; unsigned int count = 0; void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; void __iomem *wrapper_base = hdev->core->wrapper_base; int ret = 0; - writel(BIT(VIDC_CTRL_INIT_CTRL_SHIFT), cpu_cs_base + VIDC_CTRL_INIT); - if (IS_V6(hdev->core)) { + if (IS_IRIS2(hdev->core) || IS_IRIS2_1(hdev->core)) { mask_val = readl(wrapper_base + WRAPPER_INTR_MASK); mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BASK_V6 | WRAPPER_INTR_MASK_A2HCPU_MASK); } else { mask_val = WRAPPER_INTR_MASK_A2HVCODEC_MASK; } - writel(mask_val, wrapper_base + WRAPPER_INTR_MASK); - writel(1, cpu_cs_base + CPU_CS_SCIACMDARG3); + writel(mask_val, wrapper_base + WRAPPER_INTR_MASK); + if (IS_V1(hdev->core)) + writel(1, cpu_cs_base + CPU_CS_SCIACMDARG3); + + writel(BIT(VIDC_CTRL_INIT_CTRL_SHIFT), cpu_cs_base + VIDC_CTRL_INIT); while (!ctrl_status && count < max_tries) { ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) { @@ -480,7 +481,7 @@ static int venus_boot_core(struct venus_hfi_device *hdev) if (count >= max_tries) ret = -ETIMEDOUT; - if (IS_V6(hdev->core)) { + if (IS_IRIS2(hdev->core) || IS_IRIS2_1(hdev->core)) { writel(0x1, cpu_cs_base + CPU_CS_H2XSOFTINTEN_V6); writel(0x0, cpu_cs_base + CPU_CS_X2RPMH_V6); } @@ -548,10 +549,10 @@ static int venus_halt_axi(struct venus_hfi_device *hdev) u32 mask_val; int ret; - if (IS_V6(hdev->core)) { + if (IS_IRIS2(hdev->core) || IS_IRIS2_1(hdev->core)) { writel(0x3, cpu_cs_base + CPU_CS_X2RPMH_V6); - if (hdev->core->res->num_vpp_pipes == 1) + if (IS_IRIS2_1(hdev->core)) goto skip_aon_mvp_noc; writel(0x1, aon_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); @@ -927,17 +928,12 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) if (ret) dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); - /* - * Idle indicator is disabled by default on some 4xx firmware versions, - * enable it explicitly in order to make suspend functional by checking - * WFI (wait-for-interrupt) bit. - */ - if (IS_V4(hdev->core) || IS_V6(hdev->core)) - venus_sys_idle_indicator = true; - - ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); - if (ret) - dev_warn(dev, "setting idle response ON failed (%d)\n", ret); + /* HFI_PROPERTY_SYS_IDLE_INDICATOR is not supported beyond 8916 (HFI V1) */ + if (IS_V1(hdev->core)) { + ret = venus_sys_set_idle_message(hdev, false); + if (ret) + dev_warn(dev, "setting idle response ON failed (%d)\n", ret); + } ret = venus_sys_set_power_control(hdev, venus_fw_low_power_mode); if (ret) @@ -1114,7 +1110,7 @@ static irqreturn_t venus_isr(struct venus_core *core) wrapper_base = hdev->core->wrapper_base; status = readl(wrapper_base + WRAPPER_INTR_STATUS); - if (IS_V6(core)) { + if (IS_IRIS2(core) || IS_IRIS2_1(core)) { if (status & WRAPPER_INTR_STATUS_A2H_MASK || status & WRAPPER_INTR_STATUS_A2HWD_MASK_V6 || status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) @@ -1126,7 +1122,7 @@ static irqreturn_t venus_isr(struct venus_core *core) hdev->irq_status = status; } writel(1, cpu_cs_base + CPU_CS_A2HSOFTINTCLR); - if (!IS_V6(core)) + if (!(IS_IRIS2(core) || IS_IRIS2_1(core))) writel(status, wrapper_base + WRAPPER_INTR_CLEAR); return IRQ_WAKE_THREAD; @@ -1521,7 +1517,7 @@ static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status, cpu_status; - if (IS_V6(hdev->core)) + if (IS_IRIS2(hdev->core) || IS_IRIS2_1(hdev->core)) cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6); else cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS); @@ -1541,7 +1537,7 @@ static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev) void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status, cpu_status; - if (IS_V6(hdev->core)) + if (IS_IRIS2(hdev->core) || IS_IRIS2_1(hdev->core)) cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6); else cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index f5676440dd36..dbf305cec120 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -727,7 +727,7 @@ static int vdec_set_work_route(struct venus_inst *inst) u32 ptype = HFI_PROPERTY_PARAM_WORK_ROUTE; struct hfi_video_work_route wr; - if (!IS_V6(inst->core)) + if (!(IS_IRIS2(inst->core) || IS_IRIS2_1(inst->core))) return 0; wr.video_work_route = inst->core->res->num_vpp_pipes; @@ -899,13 +899,13 @@ static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, if (ret) return ret; - *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + *in_num = hfi_bufreq_get_count_min(&bufreq, ver); ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); if (ret) return ret; - *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + *out_num = hfi_bufreq_get_count_min(&bufreq, ver); return 0; } @@ -1019,14 +1019,14 @@ static int vdec_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) + inst->num_output_bufs < hfi_bufreq_get_count_min(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) + if (inst->num_input_bufs < hfi_bufreq_get_count_min(&bufreq, ver)) return -EINVAL; return 0; diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index fbe12a608b21..7e0f29bf7fae 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -79,7 +79,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); if (!ret) - ctrl->val = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + ctrl->val = hfi_bufreq_get_count_min(&bufreq, ver); break; default: return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6d773b000e8a..44b13696cf82 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1207,7 +1207,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) + inst->num_output_bufs < hfi_bufreq_get_count_min(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); @@ -1215,7 +1215,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_input_bufs < bufreq.count_actual || - inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) + inst->num_input_bufs < hfi_bufreq_get_count_min(&bufreq, ver)) return -EINVAL; return 0; diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 7468e43800a9..d9d2a293f3ef 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -358,7 +358,7 @@ static int venc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (!ret) - ctrl->val = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + ctrl->val = hfi_bufreq_get_count_min(&bufreq, ver); break; default: return -EINVAL; diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index fee1a066f56b..7360cf3863f2 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -326,7 +326,7 @@ static const struct v4l2_subdev_ops rcar_isp_subdev_ops = { static int risp_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rcar_isp *isp = notifier_to_isp(notifier); int pad; @@ -350,7 +350,7 @@ static int risp_notify_bound(struct v4l2_async_notifier *notifier, static void risp_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rcar_isp *isp = notifier_to_isp(notifier); @@ -366,7 +366,7 @@ static const struct v4l2_async_notifier_operations risp_notify_ops = { static int risp_parse_dt(struct rcar_isp *isp) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode; struct fwnode_handle *ep; unsigned int id; @@ -392,16 +392,16 @@ static int risp_parse_dt(struct rcar_isp *isp) dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode)); - v4l2_async_nf_init(&isp->notifier); + v4l2_async_subdev_nf_init(&isp->notifier, &isp->subdev); isp->notifier.ops = &risp_notify_ops; asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); - ret = v4l2_async_subdev_nf_register(&isp->subdev, &isp->notifier); + ret = v4l2_async_nf_register(&isp->notifier); if (ret) v4l2_async_nf_cleanup(&isp->notifier); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 3c4f5eb93be1..809c3a38cc4a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -252,7 +251,7 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; @@ -264,7 +263,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, mutex_lock(&vin->group->lock); for (i = 0; i < RVIN_CSI_MAX; i++) { - if (vin->group->remotes[i].asd != asd) + if (vin->group->remotes[i].asc != asc) continue; vin->group->remotes[i].subdev = NULL; vin_dbg(vin, "Unbind %s from slot %u\n", subdev->name, i); @@ -278,7 +277,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; @@ -286,7 +285,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, mutex_lock(&vin->group->lock); for (i = 0; i < RVIN_CSI_MAX; i++) { - if (vin->group->remotes[i].asd != asd) + if (vin->group->remotes[i].asc != asc) continue; vin->group->remotes[i].subdev = subdev; vin_dbg(vin, "Bound %s to slot %u\n", subdev->name, i); @@ -311,7 +310,7 @@ static int rvin_group_parse_of(struct rvin_dev *vin, unsigned int port, struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; int ret; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), port, id, 0); @@ -327,14 +326,14 @@ static int rvin_group_parse_of(struct rvin_dev *vin, unsigned int port, goto out; } - asd = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode, - struct v4l2_async_subdev); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); goto out; } - vin->group->remotes[vep.base.id].asd = asd; + vin->group->remotes[vep.base.id].asc = asc; vin_dbg(vin, "Add group OF device %pOF to slot %u\n", to_of_node(fwnode), vep.base.id); @@ -376,7 +375,7 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, mutex_unlock(&vin->group->lock); - v4l2_async_nf_init(&vin->group->notifier); + v4l2_async_nf_init(&vin->group->notifier, &vin->v4l2_dev); /* * Some subdevices may overlap but the parser function can handle it and @@ -387,7 +386,7 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, continue; for (id = 0; id < max_id; id++) { - if (vin->group->remotes[id].asd) + if (vin->group->remotes[id].asc) continue; ret = rvin_group_parse_of(vin->group->vin[i], port, id); @@ -396,11 +395,11 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, } } - if (list_empty(&vin->group->notifier.asd_list)) + if (list_empty(&vin->group->notifier.waiting_list)) return 0; vin->group->notifier.ops = &rvin_group_notify_ops; - ret = v4l2_async_nf_register(&vin->v4l2_dev, &vin->group->notifier); + ret = v4l2_async_nf_register(&vin->group->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); v4l2_async_nf_cleanup(&vin->group->notifier); @@ -611,7 +610,7 @@ static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); @@ -624,7 +623,7 @@ static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; @@ -656,7 +655,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_UNKNOWN, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; int ret; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0); @@ -687,14 +686,14 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin) goto out; } - asd = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode, - struct v4l2_async_subdev); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); + asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); goto out; } - vin->parallel.asd = asd; + vin->parallel.asc = asc; vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode)); out: @@ -713,20 +712,20 @@ static int rvin_parallel_init(struct rvin_dev *vin) { int ret; - v4l2_async_nf_init(&vin->notifier); + v4l2_async_nf_init(&vin->notifier, &vin->v4l2_dev); ret = rvin_parallel_parse_of(vin); if (ret) return ret; - if (!vin->parallel.asd) + if (!vin->parallel.asc) return -ENODEV; vin_dbg(vin, "Found parallel subdevice %pOF\n", - to_of_node(vin->parallel.asd->match.fwnode)); + to_of_node(vin->parallel.asc->match.fwnode)); vin->notifier.ops = &rvin_parallel_notify_ops; - ret = v4l2_async_nf_register(&vin->v4l2_dev, &vin->notifier); + ret = v4l2_async_nf_register(&vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); v4l2_async_nf_cleanup(&vin->notifier); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index 7a134c0eff57..f6326df0b09b 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -133,6 +132,111 @@ struct rcar_csi2; #define PHYFRX_FORCERX_MODE_1 BIT(1) #define PHYFRX_FORCERX_MODE_0 BIT(0) +/* V4H BASE registers */ +#define V4H_N_LANES_REG 0x0004 +#define V4H_CSI2_RESETN_REG 0x0008 +#define V4H_PHY_MODE_REG 0x001c +#define V4H_PHY_SHUTDOWNZ_REG 0x0040 +#define V4H_DPHY_RSTZ_REG 0x0044 +#define V4H_FLDC_REG 0x0804 +#define V4H_FLDD_REG 0x0808 +#define V4H_IDIC_REG 0x0810 +#define V4H_PHY_EN_REG 0x2000 + +#define V4H_ST_PHYST_REG 0x2814 +#define V4H_ST_PHYST_ST_PHY_READY BIT(31) +#define V4H_ST_PHYST_ST_STOPSTATE_3 BIT(3) +#define V4H_ST_PHYST_ST_STOPSTATE_2 BIT(2) +#define V4H_ST_PHYST_ST_STOPSTATE_1 BIT(1) +#define V4H_ST_PHYST_ST_STOPSTATE_0 BIT(0) + +/* V4H PPI registers */ +#define V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(n) (0x21800 + ((n) * 2)) /* n = 0 - 9 */ +#define V4H_PPI_STARTUP_RW_COMMON_STARTUP_1_1_REG 0x21822 +#define V4H_PPI_CALIBCTRL_RW_COMMON_BG_0_REG 0x2184c +#define V4H_PPI_RW_LPDCOCAL_TIMEBASE_REG 0x21c02 +#define V4H_PPI_RW_LPDCOCAL_NREF_REG 0x21c04 +#define V4H_PPI_RW_LPDCOCAL_NREF_RANGE_REG 0x21c06 +#define V4H_PPI_RW_LPDCOCAL_TWAIT_CONFIG_REG 0x21c0a +#define V4H_PPI_RW_LPDCOCAL_VT_CONFIG_REG 0x21c0c +#define V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG 0x21c10 +#define V4H_PPI_RW_COMMON_CFG_REG 0x21c6c +#define V4H_PPI_RW_TERMCAL_CFG_0_REG 0x21c80 +#define V4H_PPI_RW_OFFSETCAL_CFG_0_REG 0x21ca0 + +/* V4H CORE registers */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(n) (0x22040 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(n) (0x22440 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(n) (0x22840 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(n) (0x22c40 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(n) (0x23040 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */ +#define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */ +#define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 +#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c + +/* V4H C-PHY */ +#define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_RW_TRIO1_REG(n) (0x22500 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_RW_TRIO2_REG(n) (0x22900 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_CLANE_0_RW_LP_0_REG 0x2a080 +#define V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(n) (0x2a100 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480 +#define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880 +#define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */ + +struct rcsi2_cphy_setting { + u16 msps; + u16 rx2; + u16 trio0; + u16 trio1; + u16 trio2; + u16 lane27; + u16 lane29; +}; + +static const struct rcsi2_cphy_setting cphy_setting_table_r8a779g0[] = { + { .msps = 80, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0134, .trio2 = 0x6a, .lane27 = 0x0000, .lane29 = 0x0a24 }, + { .msps = 100, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x00f5, .trio2 = 0x55, .lane27 = 0x0000, .lane29 = 0x0a24 }, + { .msps = 200, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0077, .trio2 = 0x2b, .lane27 = 0x0000, .lane29 = 0x0a44 }, + { .msps = 300, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x004d, .trio2 = 0x1d, .lane27 = 0x0000, .lane29 = 0x0a44 }, + { .msps = 400, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0038, .trio2 = 0x16, .lane27 = 0x0000, .lane29 = 0x0a64 }, + { .msps = 500, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x002b, .trio2 = 0x12, .lane27 = 0x0000, .lane29 = 0x0a64 }, + { .msps = 600, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0023, .trio2 = 0x0f, .lane27 = 0x0000, .lane29 = 0x0a64 }, + { .msps = 700, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x001d, .trio2 = 0x0d, .lane27 = 0x0000, .lane29 = 0x0a84 }, + { .msps = 800, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0018, .trio2 = 0x0c, .lane27 = 0x0000, .lane29 = 0x0a84 }, + { .msps = 900, .rx2 = 0x38, .trio0 = 0x024a, .trio1 = 0x0015, .trio2 = 0x0b, .lane27 = 0x0000, .lane29 = 0x0a84 }, + { .msps = 1000, .rx2 = 0x3e, .trio0 = 0x024a, .trio1 = 0x0012, .trio2 = 0x0a, .lane27 = 0x0400, .lane29 = 0x0a84 }, + { .msps = 1100, .rx2 = 0x44, .trio0 = 0x024a, .trio1 = 0x000f, .trio2 = 0x09, .lane27 = 0x0800, .lane29 = 0x0a84 }, + { .msps = 1200, .rx2 = 0x4a, .trio0 = 0x024a, .trio1 = 0x000e, .trio2 = 0x08, .lane27 = 0x0c00, .lane29 = 0x0a84 }, + { .msps = 1300, .rx2 = 0x51, .trio0 = 0x024a, .trio1 = 0x000c, .trio2 = 0x08, .lane27 = 0x0c00, .lane29 = 0x0aa4 }, + { .msps = 1400, .rx2 = 0x57, .trio0 = 0x024a, .trio1 = 0x000b, .trio2 = 0x07, .lane27 = 0x1000, .lane29 = 0x0aa4 }, + { .msps = 1500, .rx2 = 0x5d, .trio0 = 0x044a, .trio1 = 0x0009, .trio2 = 0x07, .lane27 = 0x1000, .lane29 = 0x0aa4 }, + { .msps = 1600, .rx2 = 0x63, .trio0 = 0x044a, .trio1 = 0x0008, .trio2 = 0x07, .lane27 = 0x1400, .lane29 = 0x0aa4 }, + { .msps = 1700, .rx2 = 0x6a, .trio0 = 0x044a, .trio1 = 0x0007, .trio2 = 0x06, .lane27 = 0x1400, .lane29 = 0x0aa4 }, + { .msps = 1800, .rx2 = 0x70, .trio0 = 0x044a, .trio1 = 0x0007, .trio2 = 0x06, .lane27 = 0x1400, .lane29 = 0x0aa4 }, + { .msps = 1900, .rx2 = 0x76, .trio0 = 0x044a, .trio1 = 0x0006, .trio2 = 0x06, .lane27 = 0x1400, .lane29 = 0x0aa4 }, + { .msps = 2000, .rx2 = 0x7c, .trio0 = 0x044a, .trio1 = 0x0005, .trio2 = 0x06, .lane27 = 0x1800, .lane29 = 0x0aa4 }, + { .msps = 2100, .rx2 = 0x83, .trio0 = 0x044a, .trio1 = 0x0005, .trio2 = 0x05, .lane27 = 0x1800, .lane29 = 0x0aa4 }, + { .msps = 2200, .rx2 = 0x89, .trio0 = 0x064a, .trio1 = 0x0004, .trio2 = 0x05, .lane27 = 0x1800, .lane29 = 0x0aa4 }, + { .msps = 2300, .rx2 = 0x8f, .trio0 = 0x064a, .trio1 = 0x0003, .trio2 = 0x05, .lane27 = 0x1800, .lane29 = 0x0aa4 }, + { .msps = 2400, .rx2 = 0x95, .trio0 = 0x064a, .trio1 = 0x0003, .trio2 = 0x05, .lane27 = 0x1800, .lane29 = 0x0aa4 }, + { .msps = 2500, .rx2 = 0x9c, .trio0 = 0x064a, .trio1 = 0x0003, .trio2 = 0x05, .lane27 = 0x1c00, .lane29 = 0x0aa4 }, + { .msps = 2600, .rx2 = 0xa2, .trio0 = 0x064a, .trio1 = 0x0002, .trio2 = 0x05, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 2700, .rx2 = 0xa8, .trio0 = 0x064a, .trio1 = 0x0002, .trio2 = 0x05, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 2800, .rx2 = 0xae, .trio0 = 0x064a, .trio1 = 0x0002, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 2900, .rx2 = 0xb5, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3000, .rx2 = 0xbb, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3100, .rx2 = 0xc1, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3200, .rx2 = 0xc7, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3300, .rx2 = 0xce, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3400, .rx2 = 0xd4, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { .msps = 3500, .rx2 = 0xda, .trio0 = 0x084a, .trio1 = 0x0001, .trio2 = 0x04, .lane27 = 0x1c00, .lane29 = 0x0ad4 }, + { /* sentinel */ }, +}; + struct phtw_value { u16 data; u16 code; @@ -538,6 +642,11 @@ static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data) iowrite32(data, priv->base + reg); } +static void rcsi2_write16(struct rcar_csi2 *priv, unsigned int reg, u16 data) +{ + iowrite16(data, priv->base + reg); +} + static void rcsi2_enter_standby_gen3(struct rcar_csi2 *priv) { rcsi2_write(priv, PHYCNT_REG, 0); @@ -645,6 +754,10 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; do_div(mbps, lanes * 1000000); + /* Adjust for C-PHY, divide by 2.8. */ + if (priv->cphy) + mbps = div_u64(mbps * 5, 14); + return mbps; } @@ -834,6 +947,173 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) return 0; } +static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match) +{ + unsigned int timeout; + u32 status; + + for (timeout = 0; timeout <= 10; timeout++) { + status = rcsi2_read(priv, V4H_ST_PHYST_REG); + if ((status & match) == match) + return 0; + + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) +{ + const struct rcsi2_cphy_setting *conf; + + for (conf = cphy_setting_table_r8a779g0; conf->msps != 0; conf++) { + if (conf->msps > msps) + break; + } + + if (!conf->msps) { + dev_err(priv->dev, "Unsupported PHY speed for msps setting (%u Msps)", msps); + return -ERANGE; + } + + /* C-PHY specific */ + rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(7), 0x0155); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(7), 0x0068); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(8), 0x0010); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_LP_0_REG, 0x463c); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_LP_0_REG, 0x463c); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_LP_0_REG, 0x463c); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(0), 0x00d5); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(0), 0x00d5); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(0), 0x00d5); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(1), 0x0013); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(1), 0x0013); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(1), 0x0013); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(5), 0x0013); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(5), 0x0013); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(5), 0x0013); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(6), 0x000a); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(6), 0x000a); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(6), 0x000a); + + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(2), conf->rx2); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(2), conf->rx2); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(2), conf->rx2); + + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(2), 0); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(2), 0); + + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(0), conf->trio0); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(0), conf->trio0); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(0), conf->trio0); + + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(2), conf->trio2); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(2), conf->trio2); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(2), conf->trio2); + + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(1), conf->trio1); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1); + rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1); + + /* + * Configure pin-swap. + * TODO: This registers is not documented yet, the values should depend + * on the 'clock-lanes' and 'data-lanes' devicetree properties. + */ + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5); + rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000); + + /* Leave Shutdown mode */ + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0)); + + /* Wait for calibration */ + if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) { + dev_err(priv->dev, "PHY calibration failed\n"); + return -ETIMEDOUT; + } + + /* C-PHY setting - analog programing*/ + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9), conf->lane29); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(7), conf->lane27); + + return 0; +} + +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) +{ + const struct rcar_csi2_format *format; + unsigned int lanes; + int msps; + int ret; + + /* Calculate parameters */ + format = rcsi2_code_to_fmt(priv->mf.code); + if (!format) + return -EINVAL; + + ret = rcsi2_get_active_lanes(priv, &lanes); + if (ret) + return ret; + + msps = rcsi2_calc_mbps(priv, format->bpp, lanes); + if (msps < 0) + return msps; + + /* Reset LINK and PHY*/ + rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0); + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); + + /* PHY static setting */ + rcsi2_write(priv, V4H_PHY_EN_REG, BIT(0)); + rcsi2_write(priv, V4H_FLDC_REG, 0); + rcsi2_write(priv, V4H_FLDD_REG, 0); + rcsi2_write(priv, V4H_IDIC_REG, 0); + rcsi2_write(priv, V4H_PHY_MODE_REG, BIT(0)); + rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); + + /* Reset CSI2 */ + rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0)); + + /* Registers static setting through APB */ + /* Common setting */ + rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(0), 0x1bfd); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_STARTUP_1_1_REG, 0x0233); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(6), 0x0027); + rcsi2_write16(priv, V4H_PPI_CALIBCTRL_RW_COMMON_BG_0_REG, 0x01f4); + rcsi2_write16(priv, V4H_PPI_RW_TERMCAL_CFG_0_REG, 0x0013); + rcsi2_write16(priv, V4H_PPI_RW_OFFSETCAL_CFG_0_REG, 0x0003); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_TIMEBASE_REG, 0x004f); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_NREF_REG, 0x0320); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_NREF_RANGE_REG, 0x000f); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_TWAIT_CONFIG_REG, 0xfe18); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_VT_CONFIG_REG, 0x0c3c); + rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG, 0x0105); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x1000); + rcsi2_write16(priv, V4H_PPI_RW_COMMON_CFG_REG, 0x0003); + + /* C-PHY settings */ + ret = rcsi2_c_phy_setting_v4h(priv, msps); + if (ret) + return ret; + + rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 | + V4H_ST_PHYST_ST_STOPSTATE_1 | + V4H_ST_PHYST_ST_STOPSTATE_2); + + return 0; +} + static int rcsi2_start(struct rcar_csi2 *priv) { int ret; @@ -989,12 +1269,12 @@ static irqreturn_t rcsi2_irq_thread(int irq, void *data) static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rcar_csi2 *priv = notifier_to_csi2(notifier); int pad; - pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, + pad = media_entity_get_fwnode_pad(&subdev->entity, asc->match.fwnode, MEDIA_PAD_FL_SOURCE); if (pad < 0) { dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name); @@ -1014,7 +1294,7 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, static void rcsi2_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rcar_csi2 *priv = notifier_to_csi2(notifier); @@ -1091,7 +1371,7 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, static int rcsi2_parse_dt(struct rcar_csi2 *priv) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; struct fwnode_handle *fwnode; struct fwnode_handle *ep; struct v4l2_fwnode_endpoint v4l2_ep = { @@ -1123,16 +1403,16 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) dev_dbg(priv->dev, "Found '%pOF'\n", to_of_node(fwnode)); - v4l2_async_nf_init(&priv->notifier); + v4l2_async_subdev_nf_init(&priv->notifier, &priv->subdev); priv->notifier.ops = &rcar_csi2_notify_ops; - asd = v4l2_async_nf_add_fwnode(&priv->notifier, fwnode, - struct v4l2_async_subdev); + asc = v4l2_async_nf_add_fwnode(&priv->notifier, fwnode, + struct v4l2_async_connection); fwnode_handle_put(fwnode); - if (IS_ERR(asd)) - return PTR_ERR(asd); + if (IS_ERR(asc)) + return PTR_ERR(asc); - ret = v4l2_async_subdev_nf_register(&priv->subdev, &priv->notifier); + ret = v4l2_async_nf_register(&priv->notifier); if (ret) v4l2_async_nf_cleanup(&priv->notifier); @@ -1496,6 +1776,12 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = { .support_dphy = true, }; +static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = { + .start_receiver = rcsi2_start_receiver_v4h, + .use_isp = true, + .support_cphy = true, +}; + static const struct of_device_id rcar_csi2_of_table[] = { { .compatible = "renesas,r8a774a1-csi2", @@ -1545,6 +1831,10 @@ static const struct of_device_id rcar_csi2_of_table[] = { .compatible = "renesas,r8a779a0-csi2", .data = &rcar_csi2_info_r8a779a0, }, + { + .compatible = "renesas,r8a779g0-csi2", + .data = &rcar_csi2_info_r8a779g0, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index cb206d3976dd..792336dada44 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -106,7 +106,7 @@ struct rvin_video_format { /** * struct rvin_parallel_entity - Parallel video input endpoint descriptor - * @asd: sub-device descriptor for async framework + * @asc: async connection descriptor for async framework * @subdev: subdevice matched using async framework * @mbus_type: media bus type * @bus: media bus parallel configuration @@ -115,7 +115,7 @@ struct rvin_video_format { * */ struct rvin_parallel_entity { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; struct v4l2_subdev *subdev; enum v4l2_mbus_type mbus_type; @@ -272,10 +272,10 @@ struct rvin_dev { * * @lock: protects the count, notifier, vin and csi members * @count: number of enabled VIN instances found in DT - * @notifier: group notifier for CSI-2 async subdevices + * @notifier: group notifier for CSI-2 async connections * @vin: VIN instances which are part of the group * @link_setup: Callback to create all links for the media graph - * @remotes: array of pairs of fwnode and subdev pointers + * @remotes: array of pairs of async connection and subdev pointers * to all remote subdevices. */ struct rvin_group { @@ -291,7 +291,7 @@ struct rvin_group { int (*link_setup)(struct rvin_dev *vin); struct { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; struct v4l2_subdev *subdev; } remotes[RVIN_REMOTES_MAX]; }; diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 3a92f4535c18..163a4ba61c17 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -44,8 +44,9 @@ #include #include #include +#include #include -#include +#include #include #include #include @@ -1097,7 +1098,7 @@ static void rcar_drif_sdr_unregister(struct rcar_drif_sdr *sdr) /* Sub-device bound callback */ static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rcar_drif_sdr *sdr = container_of(notifier, struct rcar_drif_sdr, notifier); @@ -1112,7 +1113,7 @@ static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier, /* Sub-device unbind callback */ static void rcar_drif_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rcar_drif_sdr *sdr = container_of(notifier, struct rcar_drif_sdr, notifier); @@ -1205,9 +1206,9 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) { struct v4l2_async_notifier *notifier = &sdr->notifier; struct fwnode_handle *fwnode, *ep; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; - v4l2_async_nf_init(notifier); + v4l2_async_nf_init(&sdr->notifier, &sdr->v4l2_dev); ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(sdr->dev->of_node), NULL); @@ -1225,7 +1226,7 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) } asd = v4l2_async_nf_add_fwnode(notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); @@ -1341,7 +1342,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) sdr->notifier.ops = &rcar_drif_notify_ops; /* Register notifier */ - ret = v4l2_async_nf_register(&sdr->v4l2_dev, &sdr->notifier); + ret = v4l2_async_nf_register(&sdr->notifier); if (ret < 0) { dev_err(sdr->dev, "failed: notifier register ret %d\n", ret); goto cleanup; diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c index ab39cd2201c8..a2565b269f3b 100644 --- a/drivers/media/platform/renesas/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index 2b8cb50f54de..fff349e45067 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -1601,10 +1600,8 @@ static int jpu_probe(struct platform_device *pdev) /* interrupt service routine registration */ jpu->irq = ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); + if (ret < 0) return ret; - } ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0, dev_name(&pdev->dev), jpu); diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 5c9e27f8c94b..ec631c6e2a57 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -152,7 +151,7 @@ static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf) * ceu_subdev - Wraps v4l2 sub-device and provides async subdevice. */ struct ceu_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; struct v4l2_subdev *v4l2_sd; /* per-subdevice mbus configuration options */ @@ -160,7 +159,7 @@ struct ceu_subdev { struct ceu_mbus_fmt mbus_fmt; }; -static struct ceu_subdev *to_ceu_subdev(struct v4l2_async_subdev *asd) +static struct ceu_subdev *to_ceu_subdev(struct v4l2_async_connection *asd) { return container_of(asd, struct ceu_subdev, asd); } @@ -1375,7 +1374,7 @@ static void ceu_vdev_release(struct video_device *vdev) static int ceu_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *v4l2_sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct v4l2_device *v4l2_dev = notifier->v4l2_dev; struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev); @@ -1658,7 +1657,7 @@ static int ceu_probe(struct platform_device *pdev) if (ret) goto error_pm_disable; - v4l2_async_nf_init(&ceudev->notifier); + v4l2_async_nf_init(&ceudev->notifier, &ceudev->v4l2_dev); if (IS_ENABLED(CONFIG_OF) && dev->of_node) { ceu_data = of_device_get_match_data(dev); @@ -1680,7 +1679,7 @@ static int ceu_probe(struct platform_device *pdev) ceudev->notifier.v4l2_dev = &ceudev->v4l2_dev; ceudev->notifier.ops = &ceu_notify_ops; - ret = v4l2_async_nf_register(&ceudev->v4l2_dev, &ceudev->notifier); + ret = v4l2_async_nf_register(&ceudev->notifier); if (ret) goto error_cleanup; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 7a71370fcc32..280efd2a8185 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -93,7 +92,7 @@ static int rzg2l_cru_group_notify_complete(struct v4l2_async_notifier *notifier) static void rzg2l_cru_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); @@ -111,7 +110,7 @@ static void rzg2l_cru_group_notify_unbind(struct v4l2_async_notifier *notifier, static int rzg2l_cru_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rzg2l_cru_dev *cru = notifier_to_cru(notifier); @@ -139,7 +138,7 @@ static int rzg2l_cru_mc_parse_of(struct rzg2l_cru_dev *cru) .bus_type = V4L2_MBUS_CSI2_DPHY, }; struct fwnode_handle *ep, *fwnode; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(cru->dev), 1, 0, 0); @@ -163,7 +162,7 @@ static int rzg2l_cru_mc_parse_of(struct rzg2l_cru_dev *cru) } asd = v4l2_async_nf_add_fwnode(&cru->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; @@ -183,7 +182,7 @@ static int rzg2l_cru_mc_parse_of_graph(struct rzg2l_cru_dev *cru) { int ret; - v4l2_async_nf_init(&cru->notifier); + v4l2_async_nf_init(&cru->notifier, &cru->v4l2_dev); ret = rzg2l_cru_mc_parse_of(cru); if (ret) @@ -191,10 +190,10 @@ static int rzg2l_cru_mc_parse_of_graph(struct rzg2l_cru_dev *cru) cru->notifier.ops = &rzg2l_cru_async_ops; - if (list_empty(&cru->notifier.asd_list)) + if (list_empty(&cru->notifier.waiting_list)) return 0; - ret = v4l2_async_nf_register(&cru->v4l2_dev, &cru->notifier); + ret = v4l2_async_nf_register(&cru->notifier); if (ret < 0) { dev_err(cru->dev, "Notifier registration failed\n"); v4l2_async_nf_cleanup(&cru->notifier); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 0b682cbae3eb..811603f18af0 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -45,7 +45,7 @@ enum rzg2l_cru_dma_state { }; struct rzg2l_cru_csi { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; u32 channel; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index d6489c62b081..ad2bd71037ab 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -600,7 +599,7 @@ static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { static int rzg2l_csi2_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rzg2l_csi2 *csi2 = notifier_to_csi2(notifier); @@ -616,7 +615,7 @@ static int rzg2l_csi2_notify_bound(struct v4l2_async_notifier *notifier, static void rzg2l_csi2_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct rzg2l_csi2 *csi2 = notifier_to_csi2(notifier); @@ -647,7 +646,7 @@ static int rzg2l_csi2_parse_dt(struct rzg2l_csi2 *csi2) struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode; struct fwnode_handle *ep; int ret; @@ -674,16 +673,16 @@ static int rzg2l_csi2_parse_dt(struct rzg2l_csi2 *csi2) fwnode = fwnode_graph_get_remote_endpoint(ep); fwnode_handle_put(ep); - v4l2_async_nf_init(&csi2->notifier); + v4l2_async_subdev_nf_init(&csi2->notifier, &csi2->subdev); csi2->notifier.ops = &rzg2l_csi2_notify_ops; asd = v4l2_async_nf_add_fwnode(&csi2->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(fwnode); if (IS_ERR(asd)) return PTR_ERR(asd); - ret = v4l2_async_subdev_nf_register(&csi2->subdev, &csi2->notifier); + ret = v4l2_async_nf_register(&csi2->notifier); if (ret) v4l2_async_nf_cleanup(&csi2->notifier); diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index 8fe3272a541f..f792aedc9d82 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -1223,19 +1223,19 @@ static int sh_vou_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct video_device *vdev; struct sh_vou_device *vou_dev; - struct resource *reg_res; struct v4l2_subdev *subdev; struct vb2_queue *q; int irq, ret; - reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - - if (!vou_pdata || !reg_res || irq <= 0) { + if (!vou_pdata) { dev_err(&pdev->dev, "Insufficient VOU platform information.\n"); return -ENODEV; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL); if (!vou_dev) return -ENOMEM; @@ -1264,7 +1264,7 @@ static int sh_vou_probe(struct platform_device *pdev) pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - vou_dev->base = devm_ioremap_resource(&pdev->dev, reg_res); + vou_dev->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vou_dev->base)) return PTR_ERR(vou_dev->base); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index a9db84be4822..1aac44d68731 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index a1293c45aae1..d30f0ecb1bfd 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -148,7 +148,7 @@ struct rkisp1_info { * @port: port number (0: MIPI, 1: Parallel) */ struct rkisp1_sensor_async { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; unsigned int index; struct fwnode_handle *source_ep; unsigned int lanes; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index d7acc94e10f8..fdff3d0da4e5 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -381,6 +381,7 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) struct rkisp1_csi *csi = to_rkisp1_csi(sd); struct rkisp1_device *rkisp1 = csi->rkisp1; struct rkisp1_sensor_async *source_asd; + struct v4l2_async_connection *asc; struct media_pad *source_pad; struct v4l2_subdev *source; int ret; @@ -406,7 +407,11 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) return -EPIPE; } - source_asd = container_of(source->asd, struct rkisp1_sensor_async, asd); + asc = v4l2_async_connection_unique(source); + if (!asc) + return -EPIPE; + + source_asd = container_of(asc, struct rkisp1_sensor_async, asd); if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY) return -EINVAL; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 4762cb32353d..c41abd2833f1 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,12 +122,12 @@ struct rkisp1_isr_data { static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct rkisp1_device *rkisp1 = container_of(notifier, struct rkisp1_device, notifier); struct rkisp1_sensor_async *s_asd = - container_of(asd, struct rkisp1_sensor_async, asd); + container_of(asc, struct rkisp1_sensor_async, asd); int source_pad; int ret; @@ -165,10 +165,10 @@ static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); } -static void rkisp1_subdev_notifier_destroy(struct v4l2_async_subdev *asd) +static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc) { struct rkisp1_sensor_async *rk_asd = - container_of(asd, struct rkisp1_sensor_async, asd); + container_of(asc, struct rkisp1_sensor_async, asd); fwnode_handle_put(rk_asd->source_ep); } @@ -187,7 +187,7 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) unsigned int index = 0; int ret = 0; - v4l2_async_nf_init(ntf); + v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev); ntf->ops = &rkisp1_subdev_notifier_ops; @@ -287,7 +287,7 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) if (!index) dev_dbg(rkisp1->dev, "no remote subdevice found\n"); - ret = v4l2_async_nf_register(&rkisp1->v4l2_dev, ntf); + ret = v4l2_async_nf_register(ntf); if (ret) { v4l2_async_nf_cleanup(ntf); return ret; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 585cf3f53469..07fbb77ce234 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -868,9 +868,13 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) mbus_flags = 0; } else { const struct rkisp1_sensor_async *asd; + struct v4l2_async_connection *asc; - asd = container_of(rkisp1->source->asd, - struct rkisp1_sensor_async, asd); + asc = v4l2_async_connection_unique(rkisp1->source); + if (!asc) + return -EPIPE; + + asd = container_of(asc, struct rkisp1_sensor_async, asd); mbus_type = asd->mbus_type; mbus_flags = asd->mbus_flags; diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index 1fb34de70649..618ae55fe396 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "gsc-core.h" diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index 976b4f747ad4..97908778e1c8 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -924,7 +923,6 @@ static int fimc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; u32 lclk_freq = 0; struct fimc_dev *fimc; - struct resource *res; int ret = 0; int irq; @@ -961,8 +959,7 @@ static int fimc_probe(struct platform_device *pdev) return PTR_ERR(fimc->sysreg); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(dev, res); + fimc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index c3146ae08447..9396b10b5b1c 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -1450,7 +1450,6 @@ static int fimc_lite_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *of_id; struct fimc_lite *fimc; - struct resource *res; int ret; int irq; @@ -1479,8 +1478,7 @@ static int fimc_lite_probe(struct platform_device *pdev) spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(dev, res); + fimc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index c9cb9a216fae..5f10bb4eb4f7 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -401,7 +400,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, int index = fmd->num_sensors; struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *np; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; int ret; @@ -466,7 +465,7 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, asd = v4l2_async_nf_add_fwnode_remote(&fmd->subdev_notifier, of_fwnode_handle(ep), - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep); @@ -1372,7 +1371,7 @@ err: static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct fimc_md *fmd = notifier_to_fimc_md(notifier); struct fimc_sensor_info *si = NULL; @@ -1479,7 +1478,7 @@ static int fimc_md_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fmd); - v4l2_async_nf_init(&fmd->subdev_notifier); + v4l2_async_nf_init(&fmd->subdev_notifier, &fmd->v4l2_dev); ret = fimc_md_register_platform_entities(fmd, dev->of_node); if (ret) @@ -1507,8 +1506,7 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->subdev_notifier.ops = &subdev_notifier_ops; fmd->num_sensors = 0; - ret = v4l2_async_nf_register(&fmd->v4l2_dev, - &fmd->subdev_notifier); + ret = v4l2_async_nf_register(&fmd->subdev_notifier); if (ret) goto err_clk_p; } diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h index 079105d88bab..786264cf79dc 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.h +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h @@ -82,7 +82,7 @@ struct fimc_camclk_info { */ struct fimc_sensor_info { struct fimc_source_info pdata; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; struct fimc_dev *host; }; diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index afe1fcc37354..e4529f666e20 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -381,8 +381,8 @@ static int camif_request_irqs(struct platform_device *pdev, init_waitqueue_head(&vp->irq_queue); irq = platform_get_irq(pdev, i); - if (irq <= 0) - return -ENXIO; + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, s3c_camif_irq_handler, 0, dev_name(&pdev->dev), vp); diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c index c3c7e48f1b6e..d2c4a0178b3c 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -2870,10 +2870,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) /* interrupt service routine registration */ jpeg->irq = ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); + if (ret < 0) return ret; - } ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq, 0, dev_name(&pdev->dev), jpeg); @@ -3164,7 +3162,7 @@ static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, .remove_new = s5p_jpeg_remove, .driver = { - .of_match_table = of_match_ptr(samsung_jpeg_match), + .of_match_table = samsung_jpeg_match, .name = S5P_JPEG_M2M_NAME, .pm = &s5p_jpeg_pm_ops, }, diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index dad6e22e4ce4..8cb4fdcae137 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -134,6 +134,7 @@ struct stm32_dcmi { struct video_device *vdev; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; + struct v4l2_subdev *s_subdev; struct v4l2_format fmt; struct v4l2_rect crop; bool do_crop; @@ -692,51 +693,6 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, return 0; } -static int dcmi_pipeline_s_stream(struct stm32_dcmi *dcmi, int state) -{ - struct media_entity *entity = &dcmi->vdev->entity; - struct v4l2_subdev *subdev; - struct media_pad *pad; - int ret; - - /* Start/stop all entities within pipeline */ - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - pad = media_pad_remote_pad_first(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - ret = v4l2_subdev_call(subdev, video, s_stream, state); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_err(dcmi->dev, "%s: \"%s\" failed to %s streaming (%d)\n", - __func__, subdev->name, - state ? "start" : "stop", ret); - return ret; - } - - dev_dbg(dcmi->dev, "\"%s\" is %s\n", - subdev->name, state ? "started" : "stopped"); - } - - return 0; -} - -static int dcmi_pipeline_start(struct stm32_dcmi *dcmi) -{ - return dcmi_pipeline_s_stream(dcmi, 1); -} - -static void dcmi_pipeline_stop(struct stm32_dcmi *dcmi) -{ - dcmi_pipeline_s_stream(dcmi, 0); -} - static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); @@ -758,9 +714,12 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) goto err_pm_put; } - ret = dcmi_pipeline_start(dcmi); - if (ret) + ret = v4l2_subdev_call(dcmi->s_subdev, video, s_stream, 1); + if (ret < 0) { + dev_err(dcmi->dev, "%s: Failed to start source subdev, error (%d)\n", + __func__, ret); goto err_media_pipeline_stop; + } spin_lock_irq(&dcmi->irqlock); @@ -862,7 +821,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err_pipeline_stop: - dcmi_pipeline_stop(dcmi); + v4l2_subdev_call(dcmi->s_subdev, video, s_stream, 0); err_media_pipeline_stop: video_device_pipeline_stop(dcmi->vdev); @@ -889,8 +848,12 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); struct dcmi_buf *buf, *node; + int ret; - dcmi_pipeline_stop(dcmi); + ret = v4l2_subdev_call(dcmi->s_subdev, video, s_stream, 0); + if (ret < 0) + dev_err(dcmi->dev, "%s: Failed to stop source subdev, error (%d)\n", + __func__, ret); video_device_pipeline_stop(dcmi->vdev); @@ -1837,7 +1800,7 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); @@ -1849,7 +1812,7 @@ static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier, static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); unsigned int ret; @@ -1876,6 +1839,8 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n", subdev->name); + dcmi->s_subdev = subdev; + return ret; } @@ -1887,7 +1852,7 @@ static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { static int dcmi_graph_init(struct stm32_dcmi *dcmi) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *ep; int ret; @@ -1897,11 +1862,11 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) return -EINVAL; } - v4l2_async_nf_init(&dcmi->notifier); + v4l2_async_nf_init(&dcmi->notifier, &dcmi->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&dcmi->notifier, of_fwnode_handle(ep), - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep); @@ -1912,7 +1877,7 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) dcmi->notifier.ops = &dcmi_graph_notify_ops; - ret = v4l2_async_nf_register(&dcmi->v4l2_dev, &dcmi->notifier); + ret = v4l2_async_nf_register(&dcmi->notifier); if (ret < 0) { dev_err(dcmi->dev, "Failed to register notifier\n"); v4l2_async_nf_cleanup(&dcmi->notifier); @@ -1932,7 +1897,6 @@ static int dcmi_probe(struct platform_device *pdev) struct dma_chan *chan; struct dma_slave_caps caps; struct clk *mclk; - int irq; int ret = 0; match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev); @@ -1981,19 +1945,11 @@ static int dcmi_probe(struct platform_device *pdev) dcmi->bus.data_shift = ep.bus.parallel.data_shift; dcmi->bus_type = ep.bus_type; - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return irq ? irq : -ENXIO; + dcmi->irq = platform_get_irq(pdev, 0); + if (dcmi->irq < 0) + return dcmi->irq; - dcmi->irq = irq; - - dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!dcmi->res) { - dev_err(&pdev->dev, "Could not get resource\n"); - return -ENODEV; - } - - dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res); + dcmi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &dcmi->res); if (IS_ERR(dcmi->regs)) return PTR_ERR(dcmi->regs); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index d6e7d1b36083..ad13d447d483 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -42,7 +41,7 @@ static const struct media_entity_operations sun4i_csi_video_entity_ops = { static int sun4i_csi_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi, notifier); @@ -118,11 +117,11 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int ret; - v4l2_async_nf_init(&csi->notifier); + v4l2_async_nf_init(&csi->notifier, &csi->v4l); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); @@ -136,7 +135,7 @@ static int sun4i_csi_notifier_init(struct sun4i_csi *csi) csi->bus = vep.bus.parallel; asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); goto out; @@ -240,7 +239,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) if (ret) goto err_unregister_media; - ret = v4l2_async_nf_register(&csi->v4l, &csi->notifier); + ret = v4l2_async_nf_register(&csi->notifier); if (ret) { dev_err(csi->dev, "Couldn't register our notifier.\n"); goto err_unregister_media; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index e2723cfa4515..c6ba385c0c86 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -427,7 +426,7 @@ static struct platform_driver sun6i_csi_platform_driver = { .remove_new = sun6i_csi_remove, .driver = { .name = SUN6I_CSI_NAME, - .of_match_table = of_match_ptr(sun6i_csi_of_match), + .of_match_table = sun6i_csi_of_match, .pm = &sun6i_csi_pm_ops, }, }; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c index 4db950973ce2..e573413123b9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -642,7 +642,7 @@ static int sun6i_csi_bridge_link(struct sun6i_csi_device *csi_dev, static int sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *remote_subdev, - struct v4l2_async_subdev *async_subdev) + struct v4l2_async_connection *async_subdev) { struct sun6i_csi_device *csi_dev = container_of(notifier, struct sun6i_csi_device, @@ -819,7 +819,10 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) /* V4L2 Async */ - v4l2_async_nf_init(notifier); + if (csi_dev->isp_available) + v4l2_async_subdev_nf_init(notifier, subdev); + else + v4l2_async_nf_init(notifier, v4l2_dev); notifier->ops = &sun6i_csi_bridge_notifier_ops; sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel, @@ -828,10 +831,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2, SUN6I_CSI_PORT_MIPI_CSI2, NULL); - if (csi_dev->isp_available) - ret = v4l2_async_subdev_nf_register(subdev, notifier); - else - ret = v4l2_async_nf_register(v4l2_dev, notifier); + ret = v4l2_async_nf_register(notifier); if (ret) { dev_err(dev, "failed to register v4l2 async notifier: %d\n", ret); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h index ee592a14b9c5..44653b38f722 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h @@ -34,7 +34,7 @@ struct sun6i_csi_bridge_source { }; struct sun6i_csi_bridge_async_subdev { - struct v4l2_async_subdev async_subdev; + struct v4l2_async_connection async_subdev; struct sun6i_csi_bridge_source *source; }; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c index dce130b4c9f6..08d86c17b284 100644 --- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -408,7 +407,7 @@ static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { static int sun6i_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *remote_subdev, - struct v4l2_async_subdev *async_subdev) + struct v4l2_async_connection *async_subdev) { struct v4l2_subdev *subdev = notifier->sd; struct sun6i_mipi_csi2_device *csi2_dev = @@ -462,7 +461,7 @@ sun6i_mipi_csi2_bridge_source_setup(struct sun6i_mipi_csi2_device *csi2_dev) { struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; struct v4l2_fwnode_endpoint *endpoint = &csi2_dev->bridge.endpoint; - struct v4l2_async_subdev *subdev_async; + struct v4l2_async_connection *subdev_async; struct fwnode_handle *handle; struct device *dev = csi2_dev->dev; int ret; @@ -480,7 +479,7 @@ sun6i_mipi_csi2_bridge_source_setup(struct sun6i_mipi_csi2_device *csi2_dev) subdev_async = v4l2_async_nf_add_fwnode_remote(notifier, handle, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(subdev_async)) ret = PTR_ERR(subdev_async); @@ -531,7 +530,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* V4L2 Async */ - v4l2_async_nf_init(notifier); + v4l2_async_subdev_nf_init(notifier, subdev); notifier->ops = &sun6i_mipi_csi2_notifier_ops; ret = sun6i_mipi_csi2_bridge_source_setup(csi2_dev); @@ -540,7 +539,7 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) /* Only register the notifier when a sensor is connected. */ if (ret != -ENODEV) { - ret = v4l2_async_subdev_nf_register(subdev, notifier); + ret = v4l2_async_nf_register(notifier); if (ret < 0) goto error_v4l2_notifier_cleanup; @@ -757,7 +756,7 @@ static struct platform_driver sun6i_mipi_csi2_platform_driver = { .remove_new = sun6i_mipi_csi2_remove, .driver = { .name = SUN6I_MIPI_CSI2_NAME, - .of_match_table = of_match_ptr(sun6i_mipi_csi2_of_match), + .of_match_table = sun6i_mipi_csi2_of_match, .pm = &sun6i_mipi_csi2_pm_ops, }, }; diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c index 23d32e198aaa..14a1844812c0 100644 --- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -445,7 +444,7 @@ static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { static int sun8i_a83t_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *remote_subdev, - struct v4l2_async_subdev *async_subdev) + struct v4l2_async_connection *async_subdev) { struct v4l2_subdev *subdev = notifier->sd; struct sun8i_a83t_mipi_csi2_device *csi2_dev = @@ -499,7 +498,7 @@ sun8i_a83t_mipi_csi2_bridge_source_setup(struct sun8i_a83t_mipi_csi2_device *csi { struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; struct v4l2_fwnode_endpoint *endpoint = &csi2_dev->bridge.endpoint; - struct v4l2_async_subdev *subdev_async; + struct v4l2_async_connection *subdev_async; struct fwnode_handle *handle; struct device *dev = csi2_dev->dev; int ret; @@ -517,7 +516,7 @@ sun8i_a83t_mipi_csi2_bridge_source_setup(struct sun8i_a83t_mipi_csi2_device *csi subdev_async = v4l2_async_nf_add_fwnode_remote(notifier, handle, - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(subdev_async)) ret = PTR_ERR(subdev_async); @@ -569,7 +568,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* V4L2 Async */ - v4l2_async_nf_init(notifier); + v4l2_async_subdev_nf_init(notifier, subdev); notifier->ops = &sun8i_a83t_mipi_csi2_notifier_ops; ret = sun8i_a83t_mipi_csi2_bridge_source_setup(csi2_dev); @@ -578,7 +577,7 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) /* Only register the notifier when a sensor is connected. */ if (ret != -ENODEV) { - ret = v4l2_async_subdev_nf_register(subdev, notifier); + ret = v4l2_async_nf_register(notifier); if (ret < 0) goto error_v4l2_notifier_cleanup; @@ -824,7 +823,7 @@ static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = { .remove_new = sun8i_a83t_mipi_csi2_remove, .driver = { .name = SUN8I_A83T_MIPI_CSI2_NAME, - .of_match_table = of_match_ptr(sun8i_a83t_mipi_csi2_of_match), + .of_match_table = sun8i_a83t_mipi_csi2_of_match, .pm = &sun8i_a83t_mipi_csi2_pm_ops, }, }; diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index e4b0fd793f55..90ab1d77b6a5 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -11,9 +11,9 @@ #include #include #include +#include #include -#include -#include +#include #include #include diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index bd0c4257bbff..0b025ec91826 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -9,9 +9,9 @@ #include #include #include +#include #include -#include -#include +#include #include #include diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index ffe1887cc429..63092013d476 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -2144,7 +2144,7 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { static int vpfe_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, struct vpfe_device, v4l2_dev); @@ -2300,7 +2300,7 @@ vpfe_get_pdata(struct vpfe_device *vpfe) dev_dbg(dev, "vpfe_get_pdata\n"); - v4l2_async_nf_init(&vpfe->notifier); + v4l2_async_nf_init(&vpfe->notifier, &vpfe->v4l2_dev); if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) return dev->platform_data; @@ -2370,8 +2370,7 @@ vpfe_get_pdata(struct vpfe_device *vpfe) pdata->asd[i] = v4l2_async_nf_add_fwnode(&vpfe->notifier, of_fwnode_handle(rem), - struct - v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(rem); if (IS_ERR(pdata->asd[i])) goto cleanup; @@ -2404,10 +2403,17 @@ static int vpfe_probe(struct platform_device *pdev) vpfe->pdev = &pdev->dev; + ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); + if (ret) { + vpfe_err(vpfe, "Unable to register v4l2 device.\n"); + return ret; + } + vpfe_cfg = vpfe_get_pdata(vpfe); if (!vpfe_cfg) { dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; + ret = -EINVAL; + goto probe_out_cleanup; } vpfe->cfg = vpfe_cfg; @@ -2420,10 +2426,8 @@ static int vpfe_probe(struct platform_device *pdev) } ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - ret = -ENODEV; + if (ret < 0) goto probe_out_cleanup; - } vpfe->irq = ret; ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, @@ -2434,13 +2438,6 @@ static int vpfe_probe(struct platform_device *pdev) goto probe_out_cleanup; } - ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); - if (ret) { - vpfe_err(vpfe, - "Unable to register v4l2 device.\n"); - goto probe_out_cleanup; - } - /* set the driver data in platform device */ platform_set_drvdata(pdev, vpfe); /* Enabling module functional clock */ @@ -2450,7 +2447,7 @@ static int vpfe_probe(struct platform_device *pdev) ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { vpfe_err(vpfe, "Unable to resume device.\n"); - goto probe_out_v4l2_unregister; + goto probe_out_cleanup; } vpfe_ccdc_config_defaults(ccdc); @@ -2463,23 +2460,22 @@ static int vpfe_probe(struct platform_device *pdev) GFP_KERNEL); if (!vpfe->sd) { ret = -ENOMEM; - goto probe_out_v4l2_unregister; + goto probe_out_cleanup; } vpfe->notifier.ops = &vpfe_async_ops; - ret = v4l2_async_nf_register(&vpfe->v4l2_dev, &vpfe->notifier); + ret = v4l2_async_nf_register(&vpfe->notifier); if (ret) { vpfe_err(vpfe, "Error registering async notifier\n"); ret = -EINVAL; - goto probe_out_v4l2_unregister; + goto probe_out_cleanup; } return 0; -probe_out_v4l2_unregister: - v4l2_device_unregister(&vpfe->v4l2_dev); probe_out_cleanup: v4l2_async_nf_cleanup(&vpfe->notifier); + v4l2_device_unregister(&vpfe->v4l2_dev); return ret; } @@ -2494,8 +2490,8 @@ static void vpfe_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&vpfe->notifier); v4l2_async_nf_cleanup(&vpfe->notifier); - v4l2_device_unregister(&vpfe->v4l2_dev); video_unregister_device(&vpfe->video_dev); + v4l2_device_unregister(&vpfe->v4l2_dev); } #ifdef CONFIG_PM_SLEEP @@ -2630,7 +2626,7 @@ static struct platform_driver vpfe_driver = { .driver = { .name = VPFE_MODULE_NAME, .pm = &vpfe_pm_ops, - .of_match_table = of_match_ptr(vpfe_of_match), + .of_match_table = vpfe_of_match, }, }; diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.h b/drivers/media/platform/ti/am437x/am437x-vpfe.h index f8b4e917b91a..50c3c793b370 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.h +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.h @@ -84,7 +84,7 @@ struct vpfe_config { /* information about each subdev */ struct vpfe_subdev_info sub_devs[VPFE_MAX_SUBDEV]; /* Flat array, arranged in groups */ - struct v4l2_async_subdev *asd[VPFE_MAX_SUBDEV]; + struct v4l2_async_connection *asd[VPFE_MAX_SUBDEV]; }; struct vpfe_cap_buffer { diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 16ae52879a79..1a4273bbe752 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -50,10 +50,16 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; const struct cal_format_info *fmtinfo; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; u32 bpp; s64 freq; - fmtinfo = cal_format_by_code(phy->formats[CAL_CAMERARX_PAD_SINK].code); + state = v4l2_subdev_get_locked_active_state(&phy->subdev); + + fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK); + + fmtinfo = cal_format_by_code(fmt->code); if (!fmtinfo) return -EINVAL; @@ -583,33 +589,6 @@ done: return ret; } -int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, - struct v4l2_mbus_frame_desc *desc) -{ - struct media_pad *pad; - int ret; - - if (!phy->source) - return -EPIPE; - - pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); - if (!pad) - return -EPIPE; - - ret = v4l2_subdev_call(phy->source, pad, get_frame_desc, pad->index, - desc); - if (ret) - return ret; - - if (desc->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { - dev_err(phy->cal->dev, - "Frame descriptor does not describe CSI-2 link"); - return -EINVAL; - } - - return 0; -} - /* ------------------------------------------------------------------ * V4L2 Subdev Operations * ------------------------------------------------------------------ @@ -620,34 +599,20 @@ static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) return container_of(sd, struct cal_camerarx, subdev); } -static struct v4l2_mbus_framefmt * -cal_camerarx_get_pad_format(struct cal_camerarx *phy, - struct v4l2_subdev_state *state, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&phy->subdev, state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &phy->formats[pad]; - default: - return NULL; - } -} - static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) { struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&phy->mutex); + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) ret = cal_camerarx_start(phy); else cal_camerarx_stop(phy); - mutex_unlock(&phy->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -657,62 +622,44 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct cal_camerarx *phy = to_cal_camerarx(sd); - int ret = 0; - - mutex_lock(&phy->mutex); /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; - if (code->index > 0) { - ret = -EINVAL; - goto out; - } + if (code->index > 0) + return -EINVAL; - fmt = cal_camerarx_get_pad_format(phy, state, - CAL_CAMERARX_PAD_SINK, - code->which); + fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, + CAL_CAMERARX_PAD_SINK); code->code = fmt->code; } else { - if (code->index >= cal_num_formats) { - ret = -EINVAL; - goto out; - } + if (code->index >= cal_num_formats) + return -EINVAL; code->code = cal_formats[code->index].code; } -out: - mutex_unlock(&phy->mutex); - - return ret; + return 0; } static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { - struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; - int ret = 0; if (fse->index > 0) return -EINVAL; - mutex_lock(&phy->mutex); - /* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = cal_camerarx_get_pad_format(phy, state, - CAL_CAMERARX_PAD_SINK, - fse->which); - if (fse->code != fmt->code) { - ret = -EINVAL; - goto out; - } + fmt = v4l2_subdev_get_pad_format(sd, state, + CAL_CAMERARX_PAD_SINK); + if (fse->code != fmt->code) + return -EINVAL; fse->min_width = fmt->width; fse->max_width = fmt->width; @@ -720,10 +667,8 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, fse->max_height = fmt->height; } else { fmtinfo = cal_format_by_code(fse->code); - if (!fmtinfo) { - ret = -EINVAL; - goto out; - } + if (!fmtinfo) + return -EINVAL; fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); @@ -731,27 +676,6 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, fse->max_height = CAL_MAX_HEIGHT_LINES; } -out: - mutex_unlock(&phy->mutex); - - return ret; -} - -static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - struct cal_camerarx *phy = to_cal_camerarx(sd); - struct v4l2_mbus_framefmt *fmt; - - mutex_lock(&phy->mutex); - - fmt = cal_camerarx_get_pad_format(phy, state, format->pad, - format->which); - format->format = *fmt; - - mutex_unlock(&phy->mutex); - return 0; } @@ -759,14 +683,13 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { - struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; struct v4l2_mbus_framefmt *fmt; unsigned int bpp; /* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(format->pad)) - return cal_camerarx_sd_get_fmt(sd, state, format); + return v4l2_subdev_get_fmt(sd, state, format); /* * Default to the first format if the requested media bus code isn't @@ -790,20 +713,13 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - mutex_lock(&phy->mutex); - - fmt = cal_camerarx_get_pad_format(phy, state, - CAL_CAMERARX_PAD_SINK, - format->which); + fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK); *fmt = format->format; - fmt = cal_camerarx_get_pad_format(phy, state, - CAL_CAMERARX_PAD_FIRST_SOURCE, - format->which); + fmt = v4l2_subdev_get_pad_format(sd, state, + CAL_CAMERARX_PAD_FIRST_SOURCE); *fmt = format->format; - mutex_unlock(&phy->mutex); - return 0; } @@ -817,7 +733,7 @@ static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, .format = { .width = 640, .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_2X8, + .code = MEDIA_BUS_FMT_UYVY8_1X16, .field = V4L2_FIELD_NONE, .colorspace = V4L2_COLORSPACE_SRGB, .ycbcr_enc = V4L2_YCBCR_ENC_601, @@ -829,6 +745,40 @@ static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, return cal_camerarx_sd_set_fmt(sd, state, &format); } +static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + struct v4l2_mbus_frame_desc remote_desc; + const struct media_pad *remote_pad; + int ret; + + remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); + if (!remote_pad) + return -EPIPE; + + ret = v4l2_subdev_call(phy->source, pad, get_frame_desc, + remote_pad->index, &remote_desc); + if (ret) + return ret; + + if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + cal_err(phy->cal, + "Frame descriptor does not describe CSI-2 link"); + return -EINVAL; + } + + if (remote_desc.num_entries > 1) + cal_err(phy->cal, + "Multiple streams not supported in remote frame descriptor, using the first one\n"); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + fd->entry[0] = remote_desc.entry[0]; + + return 0; +} + static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { .s_stream = cal_camerarx_sd_s_stream, }; @@ -837,8 +787,9 @@ static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, - .get_fmt = cal_camerarx_sd_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = cal_camerarx_sd_set_fmt, + .get_frame_desc = cal_camerarx_get_frame_desc, }; static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { @@ -864,7 +815,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int i; int ret; - phy = kzalloc(sizeof(*phy), GFP_KERNEL); + phy = devm_kzalloc(cal->dev, sizeof(*phy), GFP_KERNEL); if (!phy) return ERR_PTR(-ENOMEM); @@ -872,7 +823,6 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->instance = instance; spin_lock_init(&phy->vc_lock); - mutex_init(&phy->mutex); phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? @@ -881,8 +831,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->base = devm_ioremap_resource(cal->dev, phy->res); if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); - ret = PTR_ERR(phy->base); - goto error; + return ERR_CAST(phy->base); } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -890,11 +839,11 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = cal_camerarx_regmap_init(cal, phy); if (ret) - goto error; + return ERR_PTR(ret); ret = cal_camerarx_parse_dt(phy); if (ret) - goto error; + return ERR_PTR(ret); /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; @@ -911,21 +860,25 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), phy->pads); if (ret) - goto error; + goto err_node_put; - ret = cal_camerarx_sd_init_cfg(sd, NULL); + ret = v4l2_subdev_init_finalize(sd); if (ret) - goto error; + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); if (ret) - goto error; + goto err_free_state; return phy; -error: +err_free_state: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&phy->subdev.entity); - kfree(phy); +err_node_put: + of_node_put(phy->source_ep_node); + of_node_put(phy->source_node); return ERR_PTR(ret); } @@ -935,9 +888,8 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) return; v4l2_device_unregister_subdev(&phy->subdev); + v4l2_subdev_cleanup(&phy->subdev); media_entity_cleanup(&phy->subdev.entity); of_node_put(phy->source_ep_node); of_node_put(phy->source_node); - mutex_destroy(&phy->mutex); - kfree(phy); } diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index ca906a9e4222..a8abcd0fee17 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -687,21 +687,34 @@ static void cal_release_buffers(struct cal_ctx *ctx, static int cal_video_check_format(struct cal_ctx *ctx) { const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; struct media_pad *remote_pad; + int ret = 0; remote_pad = media_pad_remote_pad_first(&ctx->pad); if (!remote_pad) return -ENODEV; - format = &ctx->phy->formats[remote_pad->index]; + state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); + + format = v4l2_subdev_get_pad_format(&ctx->phy->subdev, state, remote_pad->index); + if (!format) { + ret = -EINVAL; + goto out; + } if (ctx->fmtinfo->code != format->code || ctx->v_fmt.fmt.pix.height != format->height || ctx->v_fmt.fmt.pix.width != format->width || - ctx->v_fmt.fmt.pix.field != format->field) - return -EPIPE; + ctx->v_fmt.fmt.pix.field != format->field) { + ret = -EPIPE; + goto out; + } - return 0; +out: + v4l2_subdev_unlock_state(state); + + return ret; } static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -894,7 +907,7 @@ static int cal_ctx_v4l2_init_mc_format(struct cal_ctx *ctx) const struct cal_format_info *fmtinfo; struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; - fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_2X8); + fmtinfo = cal_format_by_code(MEDIA_BUS_FMT_UYVY8_1X16); if (!fmtinfo) return -EINVAL; diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 9c5105223d6b..528909ae4bd6 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,48 +61,24 @@ MODULE_PARM_DESC(mc_api, "activates the MC API"); const struct cal_format_info cal_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, + .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, }, { .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, + .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, }, { .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, + .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, }, { .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, + .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .fourcc = V4L2_PIX_FMT_RGB565, + .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .code = MEDIA_BUS_FMT_SBGGR8_1X8, @@ -470,30 +446,24 @@ static bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) } static int -cal_get_remote_frame_desc_entry(struct cal_camerarx *phy, +cal_get_remote_frame_desc_entry(struct cal_ctx *ctx, struct v4l2_mbus_frame_desc_entry *entry) { struct v4l2_mbus_frame_desc fd; + struct media_pad *phy_source_pad; int ret; - ret = cal_camerarx_get_remote_frame_desc(phy, &fd); - if (ret) { - if (ret != -ENOIOCTLCMD) - dev_err(phy->cal->dev, - "Failed to get remote frame desc: %d\n", ret); - return ret; - } - - if (fd.num_entries == 0) { - dev_err(phy->cal->dev, - "No streams found in the remote frame descriptor\n"); - + phy_source_pad = media_pad_remote_pad_first(&ctx->pad); + if (!phy_source_pad) return -ENODEV; - } - if (fd.num_entries > 1) - dev_dbg(phy->cal->dev, - "Multiple streams not supported in remote frame descriptor, using the first one\n"); + ret = v4l2_subdev_call(&ctx->phy->subdev, pad, get_frame_desc, + phy_source_pad->index, &fd); + if (ret) + return ret; + + if (fd.num_entries != 1) + return -EINVAL; *entry = fd.entry[0]; @@ -505,7 +475,7 @@ int cal_ctx_prepare(struct cal_ctx *ctx) struct v4l2_mbus_frame_desc_entry entry; int ret; - ret = cal_get_remote_frame_desc_entry(ctx->phy, &entry); + ret = cal_get_remote_frame_desc_entry(ctx, &entry); if (ret == -ENOIOCTLCMD) { ctx->vc = 0; @@ -804,19 +774,19 @@ static irqreturn_t cal_irq(int irq_cal, void *data) */ struct cal_v4l2_async_subdev { - struct v4l2_async_subdev asd; /* Must be first */ + struct v4l2_async_connection asd; /* Must be first */ struct cal_camerarx *phy; }; static inline struct cal_v4l2_async_subdev * -to_cal_asd(struct v4l2_async_subdev *asd) +to_cal_asd(struct v4l2_async_connection *asd) { return container_of(asd, struct cal_v4l2_async_subdev, asd); } static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct cal_camerarx *phy = to_cal_asd(asd)->phy; int pad; @@ -895,7 +865,7 @@ static int cal_async_notifier_register(struct cal_dev *cal) unsigned int i; int ret; - v4l2_async_nf_init(&cal->notifier); + v4l2_async_nf_init(&cal->notifier, &cal->v4l2_dev); cal->notifier.ops = &cal_async_notifier_ops; for (i = 0; i < cal->data->num_csi2_phy; ++i) { @@ -919,7 +889,7 @@ static int cal_async_notifier_register(struct cal_dev *cal) casd->phy = phy; } - ret = v4l2_async_nf_register(&cal->v4l2_dev, &cal->notifier); + ret = v4l2_async_nf_register(&cal->notifier); if (ret) { cal_err(cal, "Error registering async notifier\n"); goto error; diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h index de73d6d21b6f..0856297adc0b 100644 --- a/drivers/media/platform/ti/cal/cal.h +++ b/drivers/media/platform/ti/cal/cal.h @@ -177,7 +177,6 @@ struct cal_camerarx { struct v4l2_subdev subdev; struct media_pad pads[CAL_CAMERARX_NUM_PADS]; - struct v4l2_mbus_framefmt formats[CAL_CAMERARX_NUM_PADS]; /* protects the vc_* fields below */ spinlock_t vc_lock; @@ -185,13 +184,6 @@ struct cal_camerarx { u16 vc_frame_number[4]; u32 vc_sequence[4]; - /* - * Lock for camerarx ops. Protects: - * - formats - * - enable_count - */ - struct mutex mutex; - unsigned int enable_count; }; @@ -327,8 +319,6 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); -int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, - struct v4l2_mbus_frame_desc *desc); void cal_camerarx_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 44d269d6038c..99fae8830c41 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1363,12 +1363,12 @@ static inline void free_vpif_objs(void) static int vpif_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { int i; for (i = 0; i < vpif_obj.config->asd_sizes[0]; i++) { - struct v4l2_async_subdev *_asd = vpif_obj.config->asd[i]; + struct v4l2_async_connection *_asd = vpif_obj.config->asd[i]; const struct fwnode_handle *fwnode = _asd->match.fwnode; if (fwnode == subdev->fwnode) { @@ -1483,7 +1483,8 @@ static const struct v4l2_async_notifier_operations vpif_async_ops = { }; static struct vpif_capture_config * -vpif_capture_get_pdata(struct platform_device *pdev) +vpif_capture_get_pdata(struct platform_device *pdev, + struct v4l2_device *v4l2_dev) { struct device_node *endpoint = NULL; struct device_node *rem = NULL; @@ -1492,7 +1493,7 @@ vpif_capture_get_pdata(struct platform_device *pdev) struct vpif_capture_chan_config *chan; unsigned int i; - v4l2_async_nf_init(&vpif_obj.notifier); + v4l2_async_nf_init(&vpif_obj.notifier, v4l2_dev); /* * DT boot: OF node from parent device contains @@ -1570,8 +1571,7 @@ vpif_capture_get_pdata(struct platform_device *pdev) pdata->asd[i] = v4l2_async_nf_add_fwnode(&vpif_obj.notifier, of_fwnode_handle(rem), - struct - v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(pdata->asd[i])) goto err_cleanup; @@ -1609,18 +1609,12 @@ static __init int vpif_probe(struct platform_device *pdev) int res_idx = 0; int i, err; - pdev->dev.platform_data = vpif_capture_get_pdata(pdev); - if (!pdev->dev.platform_data) { - dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); - return -EINVAL; - } - vpif_dev = &pdev->dev; err = initialize_vpif(); if (err) { v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); - goto cleanup; + return err; } err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); @@ -1647,13 +1641,21 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } while (++res_idx); + pdev->dev.platform_data = + vpif_capture_get_pdata(pdev, &vpif_obj.v4l2_dev); + if (!pdev->dev.platform_data) { + err = -EINVAL; + dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); + goto vpif_unregister; + } + vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; vpif_obj.sd = kcalloc(subdev_count, sizeof(*vpif_obj.sd), GFP_KERNEL); if (!vpif_obj.sd) { err = -ENOMEM; - goto vpif_unregister; + goto probe_subdev_out; } if (!vpif_obj.config->asd_sizes[0]) { @@ -1684,8 +1686,7 @@ static __init int vpif_probe(struct platform_device *pdev) goto probe_subdev_out; } else { vpif_obj.notifier.ops = &vpif_async_ops; - err = v4l2_async_nf_register(&vpif_obj.v4l2_dev, - &vpif_obj.notifier); + err = v4l2_async_nf_register(&vpif_obj.notifier); if (err) { vpif_err("Error registering async notifier\n"); err = -EINVAL; @@ -1696,14 +1697,13 @@ static __init int vpif_probe(struct platform_device *pdev) return 0; probe_subdev_out: + v4l2_async_nf_cleanup(&vpif_obj.notifier); /* free sub devices memory */ kfree(vpif_obj.sd); vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); vpif_free: free_vpif_objs(); -cleanup: - v4l2_async_nf_cleanup(&vpif_obj.notifier); return err; } diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index f3aaa9e76492..1cda23244c7b 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -2002,6 +2002,7 @@ static void isp_remove(struct platform_device *pdev) struct isp_device *isp = platform_get_drvdata(pdev); v4l2_async_nf_unregister(&isp->notifier); + v4l2_async_nf_cleanup(&isp->notifier); isp_unregister_entities(isp); isp_cleanup_modules(isp); isp_xclk_cleanup(isp); @@ -2011,7 +2012,6 @@ static void isp_remove(struct platform_device *pdev) __omap3isp_put(isp, false); media_entity_enum_cleanup(&isp->crashed); - v4l2_async_nf_cleanup(&isp->notifier); kfree(isp); } @@ -2022,35 +2022,34 @@ enum isp_of_phy { ISP_OF_PHY_CSIPHY2, }; +static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct isp_device *isp = container_of(async, struct isp_device, + notifier); + struct isp_bus_cfg *bus_cfg = + &container_of(asc, struct isp_async_subdev, asd)->bus; + int ret; + + mutex_lock(&isp->media_dev.graph_mutex); + ret = isp_link_entity(isp, &sd->entity, bus_cfg->interface); + mutex_unlock(&isp->media_dev.graph_mutex); + + return ret; +} + static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) { struct isp_device *isp = container_of(async, struct isp_device, notifier); - struct v4l2_device *v4l2_dev = &isp->v4l2_dev; - struct v4l2_subdev *sd; int ret; mutex_lock(&isp->media_dev.graph_mutex); - ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); - if (ret) { - mutex_unlock(&isp->media_dev.graph_mutex); - return ret; - } - - list_for_each_entry(sd, &v4l2_dev->subdevs, list) { - if (sd->notifier != &isp->notifier) - continue; - - ret = isp_link_entity(isp, &sd->entity, - v4l2_subdev_to_bus_cfg(sd)->interface); - if (ret < 0) { - mutex_unlock(&isp->media_dev.graph_mutex); - return ret; - } - } - mutex_unlock(&isp->media_dev.graph_mutex); + if (ret) + return ret; ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); if (ret < 0) @@ -2240,6 +2239,7 @@ static int isp_parse_of_endpoints(struct isp_device *isp) } static const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { + .bound = isp_subdev_notifier_bound, .complete = isp_subdev_notifier_complete, }; @@ -2288,13 +2288,8 @@ static int isp_probe(struct platform_device *pdev) mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); - v4l2_async_nf_init(&isp->notifier); isp->dev = &pdev->dev; - ret = isp_parse_of_endpoints(isp); - if (ret < 0) - goto error; - isp->ref_count = 0; ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); @@ -2329,9 +2324,8 @@ static int isp_probe(struct platform_device *pdev) for (i = 0; i < 2; i++) { unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0; - mem = platform_get_resource(pdev, IORESOURCE_MEM, i); isp->mmio_base[map_idx] = - devm_ioremap_resource(isp->dev, mem); + devm_platform_get_and_ioremap_resource(pdev, i, &mem); if (IS_ERR(isp->mmio_base[map_idx])) { ret = PTR_ERR(isp->mmio_base[map_idx]); goto error; @@ -2398,10 +2392,8 @@ static int isp_probe(struct platform_device *pdev) /* Interrupt */ ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - ret = -ENODEV; + if (ret < 0) goto error_iommu; - } isp->irq_num = ret; if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED, @@ -2426,7 +2418,13 @@ static int isp_probe(struct platform_device *pdev) isp->notifier.ops = &isp_subdev_notifier_ops; - ret = v4l2_async_nf_register(&isp->v4l2_dev, &isp->notifier); + v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev); + + ret = isp_parse_of_endpoints(isp); + if (ret < 0) + goto error_register_entities; + + ret = v4l2_async_nf_register(&isp->notifier); if (ret) goto error_register_entities; @@ -2436,6 +2434,7 @@ static int isp_probe(struct platform_device *pdev) return 0; error_register_entities: + v4l2_async_nf_cleanup(&isp->notifier); isp_unregister_entities(isp); error_modules: isp_cleanup_modules(isp); @@ -2445,7 +2444,6 @@ error_isp: isp_xclk_cleanup(isp); __omap3isp_put(isp, false); error: - v4l2_async_nf_cleanup(&isp->notifier); mutex_destroy(&isp->isp_mutex); error_release_isp: kfree(isp); diff --git a/drivers/media/platform/ti/omap3isp/isp.h b/drivers/media/platform/ti/omap3isp/isp.h index a9d760fbf349..b4793631ad97 100644 --- a/drivers/media/platform/ti/omap3isp/isp.h +++ b/drivers/media/platform/ti/omap3isp/isp.h @@ -220,12 +220,21 @@ struct isp_device { }; struct isp_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; struct isp_bus_cfg bus; }; -#define v4l2_subdev_to_bus_cfg(sd) \ - (&container_of((sd)->asd, struct isp_async_subdev, asd)->bus) +static inline struct isp_bus_cfg * +v4l2_subdev_to_bus_cfg(struct v4l2_subdev *sd) +{ + struct v4l2_async_connection *asc; + + asc = v4l2_async_connection_unique(sd); + if (!asc) + return NULL; + + return &container_of(asc, struct isp_async_subdev, asd)->bus; +} #define v4l2_dev_to_isp_device(dev) \ container_of(dev, struct isp_device, v4l2_dev) diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index fdcdffe5fecb..2fe42aa91800 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1140,8 +1140,13 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) if (ccdc->input == CCDC_INPUT_PARALLEL) { struct v4l2_subdev *sd = to_isp_pipeline(&ccdc->subdev.entity)->external; + struct isp_bus_cfg *bus_cfg; - parcfg = &v4l2_subdev_to_bus_cfg(sd)->bus.parallel; + bus_cfg = v4l2_subdev_to_bus_cfg(sd); + if (WARN_ON(!bus_cfg)) + return; + + parcfg = &bus_cfg->bus.parallel; ccdc->bt656 = parcfg->bt656; } @@ -2436,7 +2441,11 @@ static int ccdc_link_validate(struct v4l2_subdev *sd, if (ccdc->input == CCDC_INPUT_PARALLEL) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->source->entity); - struct isp_bus_cfg *bus_cfg = v4l2_subdev_to_bus_cfg(sd); + struct isp_bus_cfg *bus_cfg; + + bus_cfg = v4l2_subdev_to_bus_cfg(sd); + if (WARN_ON(!bus_cfg)) + return -EPIPE; parallel_shift = bus_cfg->bus.parallel.data_lane_shift; } else { diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index fc90ff88464f..da5f0176ec78 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -360,6 +360,8 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) pad = media_pad_remote_pad_first(&ccp2->pads[CCP2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); buscfg = v4l2_subdev_to_bus_cfg(pipe->external); + if (WARN_ON(!buscfg)) + return -EPIPE; ret = ccp2_phyif_config(ccp2, &buscfg->bus.ccp2); if (ret < 0) diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 6870980a2fa9..0f9a54b11f98 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -564,6 +564,8 @@ static int csi2_configure(struct isp_csi2_device *csi2) pad = media_pad_remote_pad_first(&csi2->pads[CSI2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); buscfg = v4l2_subdev_to_bus_cfg(pipe->external); + if (WARN_ON(!buscfg)) + return -EPIPE; csi2->frame_skip = 0; v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); diff --git a/drivers/media/platform/ti/omap3isp/ispcsiphy.c b/drivers/media/platform/ti/omap3isp/ispcsiphy.c index 1bde76c0adbe..29a84d8ca0df 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/ti/omap3isp/ispcsiphy.c @@ -163,13 +163,17 @@ static int csiphy_set_power(struct isp_csiphy *phy, u32 power) static int omap3isp_csiphy_config(struct isp_csiphy *phy) { struct isp_pipeline *pipe = to_isp_pipeline(phy->entity); - struct isp_bus_cfg *buscfg = v4l2_subdev_to_bus_cfg(pipe->external); + struct isp_bus_cfg *buscfg; struct isp_csiphy_lanes_cfg *lanes; int csi2_ddrclk_khz; unsigned int num_data_lanes, used_lanes = 0; unsigned int i; u32 reg; + buscfg = v4l2_subdev_to_bus_cfg(pipe->external); + if (WARN_ON(!buscfg)) + return -EPIPE; + if (buscfg->interface == ISP_INTERFACE_CCP2B_PHY1 || buscfg->interface == ISP_INTERFACE_CCP2B_PHY2) { lanes = &buscfg->bus.ccp2.lanecfg; @@ -306,8 +310,13 @@ void omap3isp_csiphy_release(struct isp_csiphy *phy) mutex_lock(&phy->mutex); if (phy->entity) { struct isp_pipeline *pipe = to_isp_pipeline(phy->entity); - struct isp_bus_cfg *buscfg = - v4l2_subdev_to_bus_cfg(pipe->external); + struct isp_bus_cfg *buscfg; + + buscfg = v4l2_subdev_to_bus_cfg(pipe->external); + if (WARN_ON(!buscfg)) { + mutex_unlock(&phy->mutex); + return; + } csiphy_routing_cfg(phy, buscfg->interface, false, buscfg->bus.ccp2.phy_layer); diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index c0a368bacf88..423fc85d79ee 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -986,7 +986,6 @@ static int hantro_probe(struct platform_device *pdev) { const struct of_device_id *match; struct hantro_dev *vpu; - struct resource *res; int num_bases; int i, ret; @@ -1047,11 +1046,9 @@ static int hantro_probe(struct platform_device *pdev) return -ENOMEM; for (i = 0; i < num_bases; i++) { - res = vpu->variant->reg_names ? - platform_get_resource_byname(vpu->pdev, IORESOURCE_MEM, - vpu->variant->reg_names[i]) : - platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); - vpu->reg_bases[i] = devm_ioremap_resource(vpu->dev, res); + vpu->reg_bases[i] = vpu->variant->reg_names ? + devm_platform_ioremap_resource_byname(pdev, vpu->variant->reg_names[i]) : + devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vpu->reg_bases[i])) return PTR_ERR(vpu->reg_bases[i]); } @@ -1088,8 +1085,8 @@ static int hantro_probe(struct platform_device *pdev) irq_name = "default"; irq = platform_get_irq(vpu->pdev, 0); } - if (irq <= 0) - return -ENXIO; + if (irq < 0) + return irq; ret = devm_request_irq(vpu->dev, irq, vpu->variant->irqs[i].handler, 0, @@ -1225,7 +1222,7 @@ static struct platform_driver hantro_driver = { .remove_new = hantro_remove, .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(of_hantro_match), + .of_match_table = of_hantro_match, .pm = &hantro_pm_ops, }, }; diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index e871c078dd59..b3ae037a50f6 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -297,6 +297,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, enum v4l2_buf_type type) { const struct hantro_fmt *fmt; + const struct hantro_fmt *vpu_fmt; bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; @@ -316,19 +317,23 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, if (coded) { pix_mp->num_planes = 1; - } else if (!ctx->is_encoder) { + vpu_fmt = fmt; + } else if (ctx->is_encoder) { + vpu_fmt = hantro_find_format(ctx, ctx->dst_fmt.pixelformat); + } else { /* * Width/height on the CAPTURE end of a decoder are ignored and * replaced by the OUTPUT ones. */ pix_mp->width = ctx->src_fmt.width; pix_mp->height = ctx->src_fmt.height; + vpu_fmt = fmt; } pix_mp->field = V4L2_FIELD_NONE; v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, - &fmt->frmsize); + &vpu_fmt->frmsize); if (!coded) { /* Fill remaining fields */ diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 6d273abfe16c..5de6b6694f53 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -314,7 +314,7 @@ static const struct v4l2_subdev_ops video_mux_subdev_ops = { static int video_mux_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct video_mux *vmux = notifier_to_video_mux(notifier); @@ -331,10 +331,10 @@ static int video_mux_async_register(struct video_mux *vmux, unsigned int i; int ret; - v4l2_async_nf_init(&vmux->notifier); + v4l2_async_subdev_nf_init(&vmux->notifier, &vmux->subdev); for (i = 0; i < num_input_pads; i++) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep, *remote_ep; ep = fwnode_graph_get_endpoint_by_id( @@ -352,7 +352,7 @@ static int video_mux_async_register(struct video_mux *vmux, fwnode_handle_put(remote_ep); asd = v4l2_async_nf_add_fwnode_remote(&vmux->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); @@ -366,7 +366,7 @@ static int video_mux_async_register(struct video_mux *vmux, vmux->notifier.ops = &video_mux_notify_ops; - ret = v4l2_async_subdev_nf_register(&vmux->subdev, &vmux->notifier); + ret = v4l2_async_nf_register(&vmux->notifier); if (ret) goto err_nf_cleanup; diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index 3123216b3f70..4285770fde18 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -34,13 +34,13 @@ * @subdev: V4L2 subdev */ struct xvip_graph_entity { - struct v4l2_async_subdev asd; /* must be first */ + struct v4l2_async_connection asd; /* must be first */ struct media_entity *entity; struct v4l2_subdev *subdev; }; static inline struct xvip_graph_entity * -to_xvip_entity(struct v4l2_async_subdev *asd) +to_xvip_entity(struct v4l2_async_connection *asd) { return container_of(asd, struct xvip_graph_entity, asd); } @@ -54,9 +54,9 @@ xvip_graph_find_entity(struct xvip_composite_device *xdev, const struct fwnode_handle *fwnode) { struct xvip_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; - list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &xdev->notifier.done_list, asc_entry) { entity = to_xvip_entity(asd); if (entity->asd.match.fwnode == fwnode) return entity; @@ -285,13 +285,13 @@ static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) struct xvip_composite_device *xdev = container_of(notifier, struct xvip_composite_device, notifier); struct xvip_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret; dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); /* Create links for every entity. */ - list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &xdev->notifier.done_list, asc_entry) { entity = to_xvip_entity(asd); ret = xvip_graph_build_one(xdev, entity); if (ret < 0) @@ -312,36 +312,14 @@ static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *unused) + struct v4l2_async_connection *asc) { - struct xvip_composite_device *xdev = - container_of(notifier, struct xvip_composite_device, notifier); - struct xvip_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct xvip_graph_entity *entity = to_xvip_entity(asc); - /* Locate the entity corresponding to the bound subdev and store the - * subdev pointer. - */ - list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { - entity = to_xvip_entity(asd); + entity->entity = &subdev->entity; + entity->subdev = subdev; - if (entity->asd.match.fwnode != subdev->fwnode) - continue; - - if (entity->subdev) { - dev_err(xdev->dev, "duplicate subdev for node %p\n", - entity->asd.match.fwnode); - return -EINVAL; - } - - dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name); - entity->entity = &subdev->entity; - entity->subdev = subdev; - return 0; - } - - dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name); - return -EINVAL; + return 0; } static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = { @@ -402,7 +380,7 @@ err_notifier_cleanup: static int xvip_graph_parse(struct xvip_composite_device *xdev) { struct xvip_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret; /* @@ -415,7 +393,7 @@ static int xvip_graph_parse(struct xvip_composite_device *xdev) if (ret < 0) return 0; - list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &xdev->notifier.waiting_list, asc_entry) { entity = to_xvip_entity(asd); ret = xvip_graph_parse_one(xdev, entity->asd.match.fwnode); if (ret < 0) { @@ -516,6 +494,8 @@ static int xvip_graph_init(struct xvip_composite_device *xdev) goto done; } + v4l2_async_nf_init(&xdev->notifier, &xdev->v4l2_dev); + /* Parse the graph to extract a list of subdevice DT nodes. */ ret = xvip_graph_parse(xdev); if (ret < 0) { @@ -523,7 +503,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev) goto done; } - if (list_empty(&xdev->notifier.asd_list)) { + if (list_empty(&xdev->notifier.waiting_list)) { dev_err(xdev->dev, "no subdev found in graph\n"); ret = -ENOENT; goto done; @@ -532,7 +512,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev) /* Register the subdevices notifier. */ xdev->notifier.ops = &xvip_graph_notify_ops; - ret = v4l2_async_nf_register(&xdev->v4l2_dev, &xdev->notifier); + ret = v4l2_async_nf_register(&xdev->notifier); if (ret < 0) { dev_err(xdev->dev, "notifier registration failed\n"); goto done; @@ -596,7 +576,6 @@ static int xvip_composite_probe(struct platform_device *pdev) xdev->dev = &pdev->dev; INIT_LIST_HEAD(&xdev->dmas); - v4l2_async_nf_init(&xdev->notifier); ret = xvip_composite_v4l2_init(xdev); if (ret < 0) diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index b31b7ed60bbe..3da8e5102bec 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1282,7 +1282,8 @@ static int fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name) fw_data += (sizeof(struct bts_action) + (action->size)); fw_len -= (sizeof(struct bts_action) + (action->size)); } - fmdbg("Transfered only %d of %d bytes of the firmware to chip\n", fw_entry->size - fw_len, fw_entry->size); + fmdbg("Transferred only %d of %d bytes of the firmware to chip\n", + fw_entry->size - fw_len, fw_entry->size); rel_fw: release_firmware(fw_entry); clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 922c790b577e..07bdf649c60d 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -274,6 +274,7 @@ config IR_MCEUSB config IR_MESON tristate "Amlogic Meson IR remote receiver" depends on ARCH_MESON || COMPILE_TEST + select REGMAP_MMIO help Say Y if you want to use the IR remote receiver available on Amlogic Meson SoCs. diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 27f55ea966c6..41eeec648803 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -205,7 +205,7 @@ static struct platform_driver gpio_ir_recv_driver = { .remove_new = gpio_ir_recv_remove, .driver = { .name = KBUILD_MODNAME, - .of_match_table = of_match_ptr(gpio_ir_recv_of_match), + .of_match_table = gpio_ir_recv_of_match, #ifdef CONFIG_PM .pm = &gpio_ir_recv_pm_ops, #endif diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c index 2b829c146db1..1a8fea357f14 100644 --- a/drivers/media/rc/gpio-ir-tx.c +++ b/drivers/media/rc/gpio-ir-tx.c @@ -199,7 +199,7 @@ static struct platform_driver gpio_ir_tx_driver = { .probe = gpio_ir_tx_probe, .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(gpio_ir_tx_of_match), + .of_match_table = gpio_ir_tx_of_match, }, }; module_platform_driver(gpio_ir_tx_driver); diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index adbbe639a261..13e81bf8005d 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -275,7 +275,7 @@ static struct platform_driver ir_rx51_platform_driver = { .resume = ir_rx51_resume, .driver = { .name = KBUILD_MODNAME, - .of_match_table = of_match_ptr(ir_rx51_match), + .of_match_table = ir_rx51_match, }, }; module_platform_driver(ir_rx51_platform_driver); diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 49aa309d1a8c..70322fab34ac 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -10,71 +10,57 @@ #include #include #include -#include +#include #include #include #include +#include #include #define DRIVER_NAME "meson-ir" -/* valid on all Meson platforms */ #define IR_DEC_LDR_ACTIVE 0x00 #define IR_DEC_LDR_IDLE 0x04 #define IR_DEC_LDR_REPEAT 0x08 #define IR_DEC_BIT_0 0x0c #define IR_DEC_REG0 0x10 +#define IR_DEC_REG0_BASE_TIME GENMASK(11, 0) #define IR_DEC_FRAME 0x14 #define IR_DEC_STATUS 0x18 +#define IR_DEC_STATUS_PULSE BIT(8) #define IR_DEC_REG1 0x1c -/* only available on Meson 8b and newer */ +#define IR_DEC_REG1_TIME_IV GENMASK(28, 16) +#define IR_DEC_REG1_ENABLE BIT(15) +#define IR_DEC_REG1_MODE GENMASK(8, 7) +#define IR_DEC_REG1_IRQSEL GENMASK(3, 2) +#define IR_DEC_REG1_RESET BIT(0) +/* The following regs are only available on Meson 8b and newer */ #define IR_DEC_REG2 0x20 +#define IR_DEC_REG2_MODE GENMASK(3, 0) -#define REG0_RATE_MASK GENMASK(11, 0) +#define DEC_MODE_NEC 0x0 +#define DEC_MODE_RAW 0x2 -#define DECODE_MODE_NEC 0x0 -#define DECODE_MODE_RAW 0x2 +#define IRQSEL_NEC_MODE 0 +#define IRQSEL_RISE_FALL 1 +#define IRQSEL_FALL 2 +#define IRQSEL_RISE 3 -/* Meson 6b uses REG1 to configure the mode */ -#define REG1_MODE_MASK GENMASK(8, 7) -#define REG1_MODE_SHIFT 7 - -/* Meson 8b / GXBB use REG2 to configure the mode */ -#define REG2_MODE_MASK GENMASK(3, 0) -#define REG2_MODE_SHIFT 0 - -#define REG1_TIME_IV_MASK GENMASK(28, 16) - -#define REG1_IRQSEL_MASK GENMASK(3, 2) -#define REG1_IRQSEL_NEC_MODE 0 -#define REG1_IRQSEL_RISE_FALL 1 -#define REG1_IRQSEL_FALL 2 -#define REG1_IRQSEL_RISE 3 - -#define REG1_RESET BIT(0) -#define REG1_ENABLE BIT(15) - -#define STATUS_IR_DEC_IN BIT(8) - -#define MESON_TRATE 10 /* us */ +#define MESON_RAW_TRATE 10 /* us */ +#define MESON_HW_TRATE 20 /* us */ struct meson_ir { - void __iomem *reg; + struct regmap *reg; struct rc_dev *rc; spinlock_t lock; }; -static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg, - u32 mask, u32 value) -{ - u32 data; - - data = readl(ir->reg + reg); - data &= ~mask; - data |= (value & mask); - writel(data, ir->reg + reg); -} +static const struct regmap_config meson_ir_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; static irqreturn_t meson_ir_irq(int irqno, void *dev_id) { @@ -84,12 +70,12 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) spin_lock(&ir->lock); - duration = readl_relaxed(ir->reg + IR_DEC_REG1); - duration = FIELD_GET(REG1_TIME_IV_MASK, duration); - rawir.duration = duration * MESON_TRATE; + regmap_read(ir->reg, IR_DEC_REG1, &duration); + duration = FIELD_GET(IR_DEC_REG1_TIME_IV, duration); + rawir.duration = duration * MESON_RAW_TRATE; - status = readl_relaxed(ir->reg + IR_DEC_STATUS); - rawir.pulse = !!(status & STATUS_IR_DEC_IN); + regmap_read(ir->reg, IR_DEC_STATUS, &status); + rawir.pulse = !!(status & IR_DEC_STATUS_PULSE); ir_raw_event_store_with_timeout(ir->rc, &rawir); @@ -102,6 +88,7 @@ static int meson_ir_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; + void __iomem *res_start; const char *map_name; struct meson_ir *ir; int irq, ret; @@ -110,7 +97,12 @@ static int meson_ir_probe(struct platform_device *pdev) if (!ir) return -ENOMEM; - ir->reg = devm_platform_ioremap_resource(pdev, 0); + res_start = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(res_start)) + return PTR_ERR(res_start); + + ir->reg = devm_regmap_init_mmio(&pdev->dev, res_start, + &meson_ir_regmap_config); if (IS_ERR(ir->reg)) return PTR_ERR(ir->reg); @@ -131,7 +123,7 @@ static int meson_ir_probe(struct platform_device *pdev) map_name = of_get_property(node, "linux,rc-map-name", NULL); ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - ir->rc->rx_resolution = MESON_TRATE; + ir->rc->rx_resolution = MESON_RAW_TRATE; ir->rc->min_timeout = 1; ir->rc->timeout = IR_DEFAULT_TIMEOUT; ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; @@ -153,24 +145,28 @@ static int meson_ir_probe(struct platform_device *pdev) } /* Reset the decoder */ - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET); - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET, + IR_DEC_REG1_RESET); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET, 0); /* Set general operation mode (= raw/software decoding) */ if (of_device_is_compatible(node, "amlogic,meson6-ir")) - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, - FIELD_PREP(REG1_MODE_MASK, DECODE_MODE_RAW)); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE, + FIELD_PREP(IR_DEC_REG1_MODE, DEC_MODE_RAW)); else - meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK, - FIELD_PREP(REG2_MODE_MASK, DECODE_MODE_RAW)); + regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE, + FIELD_PREP(IR_DEC_REG2_MODE, DEC_MODE_RAW)); /* Set rate */ - meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1); + regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME, + FIELD_PREP(IR_DEC_REG0_BASE_TIME, + MESON_RAW_TRATE - 1)); /* IRQ on rising and falling edges */ - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK, - FIELD_PREP(REG1_IRQSEL_MASK, REG1_IRQSEL_RISE_FALL)); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_IRQSEL, + FIELD_PREP(IR_DEC_REG1_IRQSEL, IRQSEL_RISE_FALL)); /* Enable the decoder */ - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, + IR_DEC_REG1_ENABLE); dev_info(dev, "receiver initialized\n"); @@ -184,7 +180,7 @@ static void meson_ir_remove(struct platform_device *pdev) /* Disable the decoder */ spin_lock_irqsave(&ir->lock, flags); - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0); spin_unlock_irqrestore(&ir->lock, flags); } @@ -202,14 +198,16 @@ static void meson_ir_shutdown(struct platform_device *pdev) * bootloader a chance to power the system back on */ if (of_device_is_compatible(node, "amlogic,meson6-ir")) - meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, - DECODE_MODE_NEC << REG1_MODE_SHIFT); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE, + FIELD_PREP(IR_DEC_REG1_MODE, DEC_MODE_NEC)); else - meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK, - DECODE_MODE_NEC << REG2_MODE_SHIFT); + regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE, + FIELD_PREP(IR_DEC_REG2_MODE, DEC_MODE_NEC)); /* Set rate to default value */ - meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, 0x13); + regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME, + FIELD_PREP(IR_DEC_REG0_BASE_TIME, + MESON_HW_TRATE - 1)); spin_unlock_irqrestore(&ir->lock, flags); } diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c index df9349330a93..4e294e59d3cb 100644 --- a/drivers/media/rc/mtk-cir.c +++ b/drivers/media/rc/mtk-cir.c @@ -8,7 +8,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 75b7aed1579c..bf58c965ead8 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index c2167ccfd222..e95bdccfc18e 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -628,7 +628,6 @@ static int vivid_fop_release(struct file *file) v4l2_info(&dev->v4l2_dev, "reconnect\n"); vivid_reconnect(dev); } - mutex_unlock(&dev->mutex); if (file->private_data == dev->radio_rx_rds_owner) { dev->radio_rx_rds_last_block = 0; dev->radio_rx_rds_owner = NULL; @@ -637,6 +636,7 @@ static int vivid_fop_release(struct file *file) dev->radio_tx_rds_last_block = 0; dev->radio_tx_rds_owner = NULL; } + mutex_unlock(&dev->mutex); if (vdev->queue) return vb2_fop_release(file); return v4l2_fh_release(file); diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c index 3853a3d43d4f..a7b19863f489 100644 --- a/drivers/media/tuners/qt1010.c +++ b/drivers/media/tuners/qt1010.c @@ -345,11 +345,12 @@ static int qt1010_init(struct dvb_frontend *fe) else valptr = &tmpval; - BUG_ON(i >= ARRAY_SIZE(i2c_data) - 1); - - err = qt1010_init_meas1(priv, i2c_data[i+1].reg, - i2c_data[i].reg, - i2c_data[i].val, valptr); + if (i >= ARRAY_SIZE(i2c_data) - 1) + err = -EIO; + else + err = qt1010_init_meas1(priv, i2c_data[i + 1].reg, + i2c_data[i].reg, + i2c_data[i].val, valptr); i++; break; } diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 1e9c8d01523b..33a2aa8907e6 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -322,6 +322,8 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || (msg[0].addr == state->af9033_i2c_addr[1])) { + if (msg[0].len < 3 || msg[1].len < 1) + return -EOPNOTSUPP; /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; @@ -381,6 +383,8 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || (msg[0].addr == state->af9033_i2c_addr[1])) { + if (msg[0].len < 3) + return -EOPNOTSUPP; /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; @@ -388,10 +392,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].addr == state->af9033_i2c_addr[1]) reg |= 0x100000; - ret = (msg[0].len >= 3) ? af9035_wr_regs(d, reg, - &msg[0].buf[3], - msg[0].len - 3) - : -EOPNOTSUPP; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { /* I2C write */ u8 buf[MAX_XFER_SIZE]; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index aa45b5d263f6..a1235d0cce92 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -202,7 +202,7 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, while (i < num) { if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { - if (msg[i].len > 2 || msg[i+1].len > 60) { + if (msg[i].len != 2 || msg[i + 1].len > 60) { ret = -EOPNOTSUPP; break; } diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 2dcbb49d66da..2410054ddb2c 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -788,6 +788,10 @@ static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (az6007_xfer_debug) printk(KERN_DEBUG "az6007: I2C W addr=0x%x len=%d\n", addr, msgs[i].len); + if (msgs[i].len < 1) { + ret = -EIO; + goto err; + } req = AZ6007_I2C_WR; index = msgs[i].buf[0]; value = addr | (1 << 8); @@ -802,6 +806,10 @@ static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (az6007_xfer_debug) printk(KERN_DEBUG "az6007: I2C R addr=0x%x len=%d\n", addr, msgs[i].len); + if (msgs[i].len < 1) { + ret = -EIO; + goto err; + } req = AZ6007_I2C_RD; index = msgs[i].buf[0]; value = addr; diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index 0c434259c36f..c71e7b93476d 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -120,7 +120,7 @@ static int gl861_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], } else if (num == 2 && !(msg[0].flags & I2C_M_RD) && (msg[1].flags & I2C_M_RD)) { /* I2C write + read */ - if (msg[0].len > 1 || msg[1].len > sizeof(ctx->buf)) { + if (msg[0].len != 1 || msg[1].len > sizeof(ctx->buf)) { ret = -EOPNOTSUPP; goto err; } diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index 0827bf3d4e8c..13604e6acdb8 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -422,6 +422,10 @@ static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (ret == 0) ret = 2; } else { + if (msg[0].len < 2) { + ret = -EOPNOTSUPP; + goto unlock; + } /* write one or more registers */ reg = msg[0].buf[0]; addr = msg[0].addr; @@ -431,6 +435,7 @@ static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], ret = 1; } +unlock: mutex_unlock(&d->i2c_mutex); return ret; } diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 970b84c3f0b5..b3bb1805829a 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -128,6 +128,10 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], switch (num) { case 2: + if (msg[0].len < 1) { + num = -EOPNOTSUPP; + break; + } /* read stv0299 register */ value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { @@ -139,6 +143,10 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], case 1: switch (msg[0].addr) { case 0x68: + if (msg[0].len < 2) { + num = -EOPNOTSUPP; + break; + } /* write to stv0299 register */ buf6[0] = 0x2a; buf6[1] = msg[0].buf[0]; @@ -148,6 +156,10 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], break; case 0x60: if (msg[0].flags == 0) { + if (msg[0].len < 4) { + num = -EOPNOTSUPP; + break; + } /* write to tuner pll */ buf6[0] = 0x2c; buf6[1] = 5; @@ -159,6 +171,10 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 7, DW210X_WRITE_MSG); } else { + if (msg[0].len < 1) { + num = -EOPNOTSUPP; + break; + } /* read from tuner */ dw210x_op_rw(d->udev, 0xb5, 0, 0, buf6, 1, DW210X_READ_MSG); @@ -166,12 +182,20 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } break; case (DW2102_RC_QUERY): + if (msg[0].len < 2) { + num = -EOPNOTSUPP; + break; + } dw210x_op_rw(d->udev, 0xb8, 0, 0, buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; break; case (DW2102_VOLTAGE_CTRL): + if (msg[0].len < 1) { + num = -EOPNOTSUPP; + break; + } buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; dw210x_op_rw(d->udev, 0xb2, 0, 0, diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index fea5bcf72a31..c88a202daf5f 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -277,7 +277,6 @@ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int nu char *read = kmalloc(1, GFP_KERNEL); if (!read) { ret = -ENOMEM; - kfree(read); goto unlock; } @@ -288,8 +287,10 @@ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int nu if ((ret = m920x_read(d->udev, M9206_I2C, 0x0, 0x20 | stop, - read, 1)) != 0) + read, 1)) != 0) { + kfree(read); goto unlock; + } msg[i].buf[j] = read[0]; } diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c index 98b2177667d2..d269f8bb2dee 100644 --- a/drivers/media/usb/dvb-usb/opera1.c +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -439,9 +439,14 @@ MODULE_DEVICE_TABLE(usb, opera1_table); static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { + int ret; u8 command[] = { READ_MAC_ADDR }; - opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); - opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); + ret = opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); + if (ret) + return ret; + ret = opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); + if (ret) + return ret; return 0; } static int opera1_xilinx_load_firmware(struct usb_device *dev, diff --git a/drivers/media/usb/go7007/go7007-i2c.c b/drivers/media/usb/go7007/go7007-i2c.c index 38339dd2f83f..2880370e45c8 100644 --- a/drivers/media/usb/go7007/go7007-i2c.c +++ b/drivers/media/usb/go7007/go7007-i2c.c @@ -165,8 +165,6 @@ static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, } else if (msgs[i].len == 3) { if (msgs[i].flags & I2C_M_RD) return -EIO; - if (msgs[i].len != 3) - return -EIO; if (go7007_i2c_xfer(go, msgs[i].addr, 0, (msgs[i].buf[0] << 8) | msgs[i].buf[1], 0x01, &msgs[i].buf[2]) < 0) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 75c89b07e86a..29cc207194b9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -3285,12 +3285,14 @@ int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) /* Return information about the tuner */ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { - LOCK_TAKE(hdw->big_lock); do { + LOCK_TAKE(hdw->big_lock); + do { if (hdw->tuner_signal_stale) { pvr2_hdw_status_poll(hdw); } memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); - } while (0); LOCK_GIVE(hdw->big_lock); + } while (0); + LOCK_GIVE(hdw->big_lock); return 0; } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c index a8c0b513e58e..3077399901aa 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -77,7 +77,7 @@ static ssize_t show_name(struct device *class_dev, pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", cip->chptr, cip->ctl_id, name); if (!name) return -EINVAL; - return scnprintf(buf, PAGE_SIZE, "%s\n", name); + return sysfs_emit(buf, "%s\n", name); } static ssize_t show_type(struct device *class_dev, @@ -98,7 +98,7 @@ static ssize_t show_type(struct device *class_dev, } pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", cip->chptr, cip->ctl_id, name); - return scnprintf(buf, PAGE_SIZE, "%s\n", name); + return sysfs_emit(buf, "%s\n", name); } static ssize_t show_min(struct device *class_dev, @@ -111,7 +111,7 @@ static ssize_t show_min(struct device *class_dev, val = pvr2_ctrl_get_min(cip->cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", cip->chptr, cip->ctl_id, val); - return scnprintf(buf, PAGE_SIZE, "%ld\n", val); + return sysfs_emit(buf, "%ld\n", val); } static ssize_t show_max(struct device *class_dev, @@ -124,7 +124,7 @@ static ssize_t show_max(struct device *class_dev, val = pvr2_ctrl_get_max(cip->cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", cip->chptr, cip->ctl_id, val); - return scnprintf(buf, PAGE_SIZE, "%ld\n", val); + return sysfs_emit(buf, "%ld\n", val); } static ssize_t show_def(struct device *class_dev, @@ -544,7 +544,7 @@ static ssize_t v4l_minor_number_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", + return sysfs_emit(buf, "%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, pvr2_v4l_type_video)); } @@ -556,7 +556,7 @@ static ssize_t bus_info_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", + return sysfs_emit(buf, "%s\n", pvr2_hdw_get_bus_info(sfp->channel.hdw)); } @@ -567,7 +567,7 @@ static ssize_t hdw_name_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", + return sysfs_emit(buf, "%s\n", pvr2_hdw_get_type(sfp->channel.hdw)); } @@ -578,7 +578,7 @@ static ssize_t hdw_desc_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%s\n", + return sysfs_emit(buf, "%s\n", pvr2_hdw_get_desc(sfp->channel.hdw)); } @@ -590,7 +590,7 @@ static ssize_t v4l_radio_minor_number_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", + return sysfs_emit(buf, "%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, pvr2_v4l_type_radio)); } @@ -602,7 +602,7 @@ static ssize_t unit_number_show(struct device *class_dev, struct pvr2_sysfs *sfp; sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; - return scnprintf(buf,PAGE_SIZE,"%d\n", + return sysfs_emit(buf, "%d\n", pvr2_hdw_get_unit_number(sfp->channel.hdw)); } diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 640737d3b8ae..8a39cac76c58 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -455,12 +455,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) rc = smscore_register_device(¶ms, &dev->coredev, 0, mdev); if (rc < 0) { pr_err("smscore_register_device(...) failed, rc %d\n", rc); - smsusb_term_device(intf); -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - media_device_unregister(mdev); -#endif - kfree(mdev); - return rc; + goto err_unregister_device; } smscore_set_board_id(dev->coredev, board_id); @@ -477,8 +472,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) rc = smsusb_start_streaming(dev); if (rc < 0) { pr_err("smsusb_start_streaming(...) failed\n"); - smsusb_term_device(intf); - return rc; + goto err_unregister_device; } dev->state = SMSUSB_ACTIVE; @@ -486,13 +480,20 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) rc = smscore_start_device(dev->coredev); if (rc < 0) { pr_err("smscore_start_device(...) failed\n"); - smsusb_term_device(intf); - return rc; + goto err_unregister_device; } pr_debug("device 0x%p created\n", dev); return rc; + +err_unregister_device: + smsusb_term_device(intf); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + media_device_unregister(mdev); +#endif + kfree(mdev); + return rc; } static int smsusb_probe(struct usb_interface *intf, diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c index ea25b96b8bbf..dff6bf532ce3 100644 --- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -76,7 +76,7 @@ static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe, static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; u8 b[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, @@ -103,7 +103,7 @@ static int ttusbdecfe_dvbt_get_tune_settings(struct dvb_frontend* fe, static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; u8 b[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, @@ -137,7 +137,7 @@ static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend *fe) static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; u8 b[] = { 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -158,7 +158,7 @@ static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struc static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; state->hi_band = (SEC_TONE_ON == tone); @@ -169,7 +169,7 @@ static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend *fe, static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; switch (voltage) { case SEC_VOLTAGE_13: @@ -187,7 +187,7 @@ static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend *fe, static void ttusbdecfe_release(struct dvb_frontend* fe) { - struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; + struct ttusbdecfe_state *state = fe->demodulator_priv; kfree(state); } diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 348559bc2468..f77ebd688cde 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -74,6 +74,15 @@ config V4L2_FWNODE config V4L2_ASYNC tristate +config V4L2_CCI + tristate + +config V4L2_CCI_I2C + tristate + depends on I2C + select REGMAP_I2C + select V4L2_CCI + # Used by drivers that need Videobuf modules config VIDEOBUF_GEN tristate diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 41d91bd10cf2..be2551705755 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o # (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o +obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o obj-$(CONFIG_V4L2_H264) += v4l2-h264.o diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index b16b5f4cb91e..091e8cf4114b 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -28,22 +28,22 @@ static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { if (!n->ops || !n->ops->bound) return 0; - return n->ops->bound(n, subdev, asd); + return n->ops->bound(n, subdev, asc); } static void v4l2_async_nf_call_unbind(struct v4l2_async_notifier *n, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { if (!n->ops || !n->ops->unbind) return; - n->ops->unbind(n, subdev, asd); + n->ops->unbind(n, subdev, asc); } static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n) @@ -55,131 +55,142 @@ static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n) } static void v4l2_async_nf_call_destroy(struct v4l2_async_notifier *n, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { if (!n->ops || !n->ops->destroy) return; - n->ops->destroy(asd); + n->ops->destroy(asc); } static bool match_i2c(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) + struct v4l2_subdev *sd, + struct v4l2_async_match_desc *match) { #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(sd->dev); return client && - asd->match.i2c.adapter_id == client->adapter->nr && - asd->match.i2c.address == client->addr; + match->i2c.adapter_id == client->adapter->nr && + match->i2c.address == client->addr; #else return false; #endif } +static struct device *notifier_dev(struct v4l2_async_notifier *notifier) +{ + if (notifier->sd) + return notifier->sd->dev; + + if (notifier->v4l2_dev) + return notifier->v4l2_dev->dev; + + return NULL; +} + static bool match_fwnode_one(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode, - struct v4l2_async_subdev *asd) + struct v4l2_async_match_desc *match) { - struct fwnode_handle *other_fwnode; - struct fwnode_handle *dev_fwnode; - bool asd_fwnode_is_ep; - bool sd_fwnode_is_ep; - struct device *dev; + struct fwnode_handle *asd_dev_fwnode; + bool ret; - /* - * Both the subdev and the async subdev can provide either an endpoint - * fwnode or a device fwnode. Start with the simple case of direct - * fwnode matching. - */ - if (sd_fwnode == asd->match.fwnode) + dev_dbg(notifier_dev(notifier), + "v4l2-async: fwnode match: need %pfw, trying %pfw\n", + sd_fwnode, match->fwnode); + + if (sd_fwnode == match->fwnode) { + dev_dbg(notifier_dev(notifier), + "v4l2-async: direct match found\n"); return true; - - /* - * Otherwise, check if the sd fwnode and the asd fwnode refer to an - * endpoint or a device. If they're of the same type, there's no match. - * Technically speaking this checks if the nodes refer to a connected - * endpoint, which is the simplest check that works for both OF and - * ACPI. This won't make a difference, as drivers should not try to - * match unconnected endpoints. - */ - sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd_fwnode); - asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); - - if (sd_fwnode_is_ep == asd_fwnode_is_ep) - return false; - - /* - * The sd and asd fwnodes are of different types. Get the device fwnode - * parent of the endpoint fwnode, and compare it with the other fwnode. - */ - if (sd_fwnode_is_ep) { - dev_fwnode = fwnode_graph_get_port_parent(sd_fwnode); - other_fwnode = asd->match.fwnode; - } else { - dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); - other_fwnode = sd_fwnode; } - fwnode_handle_put(dev_fwnode); - - if (dev_fwnode != other_fwnode) + if (!fwnode_graph_is_endpoint(match->fwnode)) { + dev_dbg(notifier_dev(notifier), + "v4l2-async: direct match not found\n"); return false; - - /* - * We have a heterogeneous match. Retrieve the struct device of the side - * that matched on a device fwnode to print its driver name. - */ - if (sd_fwnode_is_ep) - dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev - : notifier->sd->dev; - else - dev = sd->dev; - - if (dev && dev->driver) { - if (sd_fwnode_is_ep) - dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n", - dev->driver->name); - dev_notice(dev, "Consider updating driver %s to match on endpoints\n", - dev->driver->name); } - return true; + asd_dev_fwnode = fwnode_graph_get_port_parent(match->fwnode); + + ret = sd_fwnode == asd_dev_fwnode; + + fwnode_handle_put(asd_dev_fwnode); + + dev_dbg(notifier_dev(notifier), + "v4l2-async: device--endpoint match %sfound\n", + ret ? "" : "not "); + + return ret; } static bool match_fwnode(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) + struct v4l2_subdev *sd, + struct v4l2_async_match_desc *match) { - if (match_fwnode_one(notifier, sd, sd->fwnode, asd)) + dev_dbg(notifier_dev(notifier), + "v4l2-async: matching for notifier %pfw, sd fwnode %pfw\n", + dev_fwnode(notifier_dev(notifier)), sd->fwnode); + + if (!list_empty(&sd->async_subdev_endpoint_list)) { + struct v4l2_async_subdev_endpoint *ase; + + dev_dbg(sd->dev, + "v4l2-async: endpoint fwnode list available, looking for %pfw\n", + match->fwnode); + + list_for_each_entry(ase, &sd->async_subdev_endpoint_list, + async_subdev_endpoint_entry) { + bool matched = ase->endpoint == match->fwnode; + + dev_dbg(sd->dev, + "v4l2-async: endpoint-endpoint match %sfound with %pfw\n", + matched ? "" : "not ", ase->endpoint); + + if (matched) + return true; + } + + dev_dbg(sd->dev, "async: no endpoint matched\n"); + + return false; + } + + if (match_fwnode_one(notifier, sd, sd->fwnode, match)) return true; /* Also check the secondary fwnode. */ if (IS_ERR_OR_NULL(sd->fwnode->secondary)) return false; - return match_fwnode_one(notifier, sd, sd->fwnode->secondary, asd); + dev_dbg(notifier_dev(notifier), + "v4l2-async: trying secondary fwnode match\n"); + + return match_fwnode_one(notifier, sd, sd->fwnode->secondary, match); } static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); -static struct v4l2_async_subdev * +static struct v4l2_async_connection * v4l2_async_find_match(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) { bool (*match)(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); - struct v4l2_async_subdev *asd; + struct v4l2_subdev *sd, + struct v4l2_async_match_desc *match); + struct v4l2_async_connection *asc; - list_for_each_entry(asd, ¬ifier->waiting, list) { + list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) { /* bus_type has been verified valid before */ - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_I2C: + switch (asc->match.type) { + case V4L2_ASYNC_MATCH_TYPE_I2C: match = match_i2c; break; - case V4L2_ASYNC_MATCH_FWNODE: + case V4L2_ASYNC_MATCH_TYPE_FWNODE: match = match_fwnode; break; default: @@ -189,28 +200,26 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, } /* match cannot be NULL here */ - if (match(notifier, sd, asd)) - return asd; + if (match(notifier, sd, &asc->match)) + return asc; } return NULL; } -/* Compare two async sub-device descriptors for equivalence */ -static bool asd_equal(struct v4l2_async_subdev *asd_x, - struct v4l2_async_subdev *asd_y) +/* Compare two async match descriptors for equivalence */ +static bool v4l2_async_match_equal(struct v4l2_async_match_desc *match1, + struct v4l2_async_match_desc *match2) { - if (asd_x->match_type != asd_y->match_type) + if (match1->type != match2->type) return false; - switch (asd_x->match_type) { - case V4L2_ASYNC_MATCH_I2C: - return asd_x->match.i2c.adapter_id == - asd_y->match.i2c.adapter_id && - asd_x->match.i2c.address == - asd_y->match.i2c.address; - case V4L2_ASYNC_MATCH_FWNODE: - return asd_x->match.fwnode == asd_y->match.fwnode; + switch (match1->type) { + case V4L2_ASYNC_MATCH_TYPE_I2C: + return match1->i2c.adapter_id == match2->i2c.adapter_id && + match1->i2c.address == match2->i2c.address; + case V4L2_ASYNC_MATCH_TYPE_FWNODE: + return match1->fwnode == match2->fwnode; default: break; } @@ -224,7 +233,7 @@ v4l2_async_find_subdev_notifier(struct v4l2_subdev *sd) { struct v4l2_async_notifier *n; - list_for_each_entry(n, ¬ifier_list, list) + list_for_each_entry(n, ¬ifier_list, notifier_entry) if (n->sd == sd) return n; @@ -247,14 +256,14 @@ v4l2_async_nf_find_v4l2_dev(struct v4l2_async_notifier *notifier) static bool v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier) { - struct v4l2_subdev *sd; + struct v4l2_async_connection *asc; - if (!list_empty(¬ifier->waiting)) + if (!list_empty(¬ifier->waiting_list)) return false; - list_for_each_entry(sd, ¬ifier->done, async_list) { + list_for_each_entry(asc, ¬ifier->done_list, asc_entry) { struct v4l2_async_notifier *subdev_notifier = - v4l2_async_find_subdev_notifier(sd); + v4l2_async_find_subdev_notifier(asc->sd); if (subdev_notifier && !v4l2_async_nf_can_complete(subdev_notifier)) @@ -271,22 +280,33 @@ v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier) static int v4l2_async_nf_try_complete(struct v4l2_async_notifier *notifier) { + struct v4l2_async_notifier *__notifier = notifier; + /* Quick check whether there are still more sub-devices here. */ - if (!list_empty(¬ifier->waiting)) + if (!list_empty(¬ifier->waiting_list)) return 0; + if (notifier->sd) + dev_dbg(notifier_dev(notifier), + "v4l2-async: trying to complete\n"); + /* Check the entire notifier tree; find the root notifier first. */ while (notifier->parent) notifier = notifier->parent; /* This is root if it has v4l2_dev. */ - if (!notifier->v4l2_dev) + if (!notifier->v4l2_dev) { + dev_dbg(notifier_dev(__notifier), + "v4l2-async: V4L2 device not available\n"); return 0; + } /* Is everything ready? */ if (!v4l2_async_nf_can_complete(notifier)) return 0; + dev_dbg(notifier_dev(__notifier), "v4l2-async: complete\n"); + return v4l2_async_nf_call_complete(notifier); } @@ -314,41 +334,53 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n, static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { struct v4l2_async_notifier *subdev_notifier; + bool registered = false; int ret; - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret < 0) - return ret; + if (list_empty(&sd->asc_list)) { + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) + return ret; + registered = true; + } - ret = v4l2_async_nf_call_bound(notifier, sd, asd); + ret = v4l2_async_nf_call_bound(notifier, sd, asc); if (ret < 0) { - v4l2_device_unregister_subdev(sd); - return ret; + if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE) + dev_dbg(notifier_dev(notifier), + "failed binding %pfw (%d)\n", + asc->match.fwnode, ret); + goto err_unregister_subdev; } - /* - * Depending of the function of the entities involved, we may want to - * create links between them (for example between a sensor and its lens - * or between a sensor's source pad and the connected device's sink - * pad). - */ - ret = v4l2_async_create_ancillary_links(notifier, sd); - if (ret) { - v4l2_async_nf_call_unbind(notifier, sd, asd); - v4l2_device_unregister_subdev(sd); - return ret; + if (registered) { + /* + * Depending of the function of the entities involved, we may + * want to create links between them (for example between a + * sensor and its lens or between a sensor's source pad and the + * connected device's sink pad). + */ + ret = v4l2_async_create_ancillary_links(notifier, sd); + if (ret) { + if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE) + dev_dbg(notifier_dev(notifier), + "failed creating links for %pfw (%d)\n", + asc->match.fwnode, ret); + goto err_call_unbind; + } } - /* Remove from the waiting list */ - list_del(&asd->list); - sd->asd = asd; - sd->notifier = notifier; + list_add(&asc->asc_subdev_entry, &sd->asc_list); + asc->sd = sd; - /* Move from the global subdevice list to notifier's done */ - list_move(&sd->async_list, ¬ifier->done); + /* Move from the waiting list to notifier's done */ + list_move(&asc->asc_entry, ¬ifier->done_list); + + dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n", + dev_name(sd->dev), ret); /* * See if the sub-device has a notifier. If not, return here. @@ -365,6 +397,16 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, subdev_notifier->parent = notifier; return v4l2_async_nf_try_all_subdevs(subdev_notifier); + +err_call_unbind: + v4l2_async_nf_call_unbind(notifier, sd, asc); + list_del(&asc->asc_subdev_entry); + +err_unregister_subdev: + if (registered) + v4l2_device_unregister_subdev(sd); + + return ret; } /* Test all async sub-devices in a notifier for a match. */ @@ -378,16 +420,21 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier) if (!v4l2_dev) return 0; + dev_dbg(notifier_dev(notifier), "v4l2-async: trying all sub-devices\n"); + again: list_for_each_entry(sd, &subdev_list, async_list) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; int ret; - asd = v4l2_async_find_match(notifier, sd); - if (!asd) + asc = v4l2_async_find_match(notifier, sd); + if (!asc) continue; - ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); + dev_dbg(notifier_dev(notifier), + "v4l2-async: match found, subdev %s\n", sd->name); + + ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asc); if (ret < 0) return ret; @@ -403,37 +450,33 @@ again: return 0; } -static void v4l2_async_cleanup(struct v4l2_subdev *sd) +static void v4l2_async_unbind_subdev_one(struct v4l2_async_notifier *notifier, + struct v4l2_async_connection *asc) { - v4l2_device_unregister_subdev(sd); - /* - * Subdevice driver will reprobe and put the subdev back - * onto the list - */ - list_del_init(&sd->async_list); - sd->asd = NULL; + list_move_tail(&asc->asc_entry, ¬ifier->waiting_list); + if (list_is_singular(&asc->asc_subdev_entry)) { + v4l2_async_nf_call_unbind(notifier, asc->sd, asc); + v4l2_device_unregister_subdev(asc->sd); + asc->sd = NULL; + } + list_del(&asc->asc_subdev_entry); } /* Unbind all sub-devices in the notifier tree. */ static void -v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier, - bool readd) +v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier) { - struct v4l2_subdev *sd, *tmp; + struct v4l2_async_connection *asc, *asc_tmp; - list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { + list_for_each_entry_safe(asc, asc_tmp, ¬ifier->done_list, + asc_entry) { struct v4l2_async_notifier *subdev_notifier = - v4l2_async_find_subdev_notifier(sd); + v4l2_async_find_subdev_notifier(asc->sd); if (subdev_notifier) - v4l2_async_nf_unbind_all_subdevs(subdev_notifier, true); + v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - v4l2_async_nf_call_unbind(notifier, sd, sd->asd); - if (readd) - list_add_tail(&sd->asd->list, ¬ifier->waiting); - v4l2_async_cleanup(sd); - - list_move(&sd->async_list, &subdev_list); + v4l2_async_unbind_subdev_one(notifier, asc); } notifier->parent = NULL; @@ -441,106 +484,109 @@ v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier, /* See if an async sub-device can be found in a notifier's lists. */ static bool -__v4l2_async_nf_has_async_subdev(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev *asd) +v4l2_async_nf_has_async_match_entry(struct v4l2_async_notifier *notifier, + struct v4l2_async_match_desc *match) { - struct v4l2_async_subdev *asd_y; - struct v4l2_subdev *sd; + struct v4l2_async_connection *asc; - list_for_each_entry(asd_y, ¬ifier->waiting, list) - if (asd_equal(asd, asd_y)) + list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) + if (v4l2_async_match_equal(&asc->match, match)) return true; - list_for_each_entry(sd, ¬ifier->done, async_list) { - if (WARN_ON(!sd->asd)) - continue; - - if (asd_equal(asd, sd->asd)) + list_for_each_entry(asc, ¬ifier->done_list, asc_entry) + if (v4l2_async_match_equal(&asc->match, match)) return true; - } return false; } /* - * Find out whether an async sub-device was set up already or - * whether it exists in a given notifier before @this_index. - * If @this_index < 0, search the notifier's entire @asd_list. + * Find out whether an async sub-device was set up already or whether it exists + * in a given notifier. */ static bool -v4l2_async_nf_has_async_subdev(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev *asd, int this_index) +v4l2_async_nf_has_async_match(struct v4l2_async_notifier *notifier, + struct v4l2_async_match_desc *match) { - struct v4l2_async_subdev *asd_y; - int j = 0; + struct list_head *heads[] = { + ¬ifier->waiting_list, + ¬ifier->done_list, + }; + unsigned int i; lockdep_assert_held(&list_lock); /* Check that an asd is not being added more than once. */ - list_for_each_entry(asd_y, ¬ifier->asd_list, asd_list) { - if (this_index >= 0 && j++ >= this_index) - break; - if (asd_equal(asd, asd_y)) - return true; + for (i = 0; i < ARRAY_SIZE(heads); i++) { + struct v4l2_async_connection *asc; + + list_for_each_entry(asc, heads[i], asc_entry) { + if (&asc->match == match) + continue; + if (v4l2_async_match_equal(&asc->match, match)) + return true; + } } - /* Check that an asd does not exist in other notifiers. */ - list_for_each_entry(notifier, ¬ifier_list, list) - if (__v4l2_async_nf_has_async_subdev(notifier, asd)) + /* Check that an asc does not exist in other notifiers. */ + list_for_each_entry(notifier, ¬ifier_list, notifier_entry) + if (v4l2_async_nf_has_async_match_entry(notifier, match)) return true; return false; } -static int v4l2_async_nf_asd_valid(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev *asd, - int this_index) +static int v4l2_async_nf_match_valid(struct v4l2_async_notifier *notifier, + struct v4l2_async_match_desc *match) { - struct device *dev = - notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL; + struct device *dev = notifier_dev(notifier); - if (!asd) - return -EINVAL; - - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_I2C: - case V4L2_ASYNC_MATCH_FWNODE: - if (v4l2_async_nf_has_async_subdev(notifier, asd, this_index)) { - dev_dbg(dev, "subdev descriptor already listed in this or other notifiers\n"); + switch (match->type) { + case V4L2_ASYNC_MATCH_TYPE_I2C: + case V4L2_ASYNC_MATCH_TYPE_FWNODE: + if (v4l2_async_nf_has_async_match(notifier, match)) { + dev_dbg(dev, "v4l2-async: match descriptor already listed in a notifier\n"); return -EEXIST; } break; default: - dev_err(dev, "Invalid match type %u on %p\n", - asd->match_type, asd); + dev_err(dev, "v4l2-async: Invalid match type %u on %p\n", + match->type, match); return -EINVAL; } return 0; } -void v4l2_async_nf_init(struct v4l2_async_notifier *notifier) +void v4l2_async_nf_init(struct v4l2_async_notifier *notifier, + struct v4l2_device *v4l2_dev) { - INIT_LIST_HEAD(¬ifier->asd_list); + INIT_LIST_HEAD(¬ifier->waiting_list); + INIT_LIST_HEAD(¬ifier->done_list); + notifier->v4l2_dev = v4l2_dev; } EXPORT_SYMBOL(v4l2_async_nf_init); +void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd) +{ + INIT_LIST_HEAD(¬ifier->waiting_list); + INIT_LIST_HEAD(¬ifier->done_list); + notifier->sd = sd; +} +EXPORT_SYMBOL_GPL(v4l2_async_subdev_nf_init); + static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev *asd; - int ret, i = 0; - - INIT_LIST_HEAD(¬ifier->waiting); - INIT_LIST_HEAD(¬ifier->done); + struct v4l2_async_connection *asc; + int ret; mutex_lock(&list_lock); - list_for_each_entry(asd, ¬ifier->asd_list, asd_list) { - ret = v4l2_async_nf_asd_valid(notifier, asd, i++); + list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) { + ret = v4l2_async_nf_match_valid(notifier, &asc->match); if (ret) goto err_unlock; - - list_add_tail(&asd->list, ¬ifier->waiting); } ret = v4l2_async_nf_try_all_subdevs(notifier); @@ -552,7 +598,7 @@ static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier) goto err_unbind; /* Keep also completed notifiers on the list */ - list_add(¬ifier->list, ¬ifier_list); + list_add(¬ifier->notifier_entry, ¬ifier_list); mutex_unlock(&list_lock); @@ -562,7 +608,7 @@ err_unbind: /* * On failure, unbind all sub-devices registered through this notifier. */ - v4l2_async_nf_unbind_all_subdevs(notifier, false); + v4l2_async_nf_unbind_all_subdevs(notifier); err_unlock: mutex_unlock(&list_lock); @@ -570,16 +616,13 @@ err_unlock: return ret; } -int v4l2_async_nf_register(struct v4l2_device *v4l2_dev, - struct v4l2_async_notifier *notifier) +int v4l2_async_nf_register(struct v4l2_async_notifier *notifier) { int ret; - if (WARN_ON(!v4l2_dev || notifier->sd)) + if (WARN_ON(!notifier->v4l2_dev == !notifier->sd)) return -EINVAL; - notifier->v4l2_dev = v4l2_dev; - ret = __v4l2_async_nf_register(notifier); if (ret) notifier->v4l2_dev = NULL; @@ -588,36 +631,15 @@ int v4l2_async_nf_register(struct v4l2_device *v4l2_dev, } EXPORT_SYMBOL(v4l2_async_nf_register); -int v4l2_async_subdev_nf_register(struct v4l2_subdev *sd, - struct v4l2_async_notifier *notifier) -{ - int ret; - - if (WARN_ON(!sd || notifier->v4l2_dev)) - return -EINVAL; - - notifier->sd = sd; - - ret = __v4l2_async_nf_register(notifier); - if (ret) - notifier->sd = NULL; - - return ret; -} -EXPORT_SYMBOL(v4l2_async_subdev_nf_register); - static void __v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier) { if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) return; - v4l2_async_nf_unbind_all_subdevs(notifier, false); + v4l2_async_nf_unbind_all_subdevs(notifier); - notifier->sd = NULL; - notifier->v4l2_dev = NULL; - - list_del(¬ifier->list); + list_del(¬ifier->notifier_entry); } void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier) @@ -632,24 +654,25 @@ EXPORT_SYMBOL(v4l2_async_nf_unregister); static void __v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev *asd, *tmp; + struct v4l2_async_connection *asc, *tmp; - if (!notifier || !notifier->asd_list.next) + if (!notifier || !notifier->waiting_list.next) return; - list_for_each_entry_safe(asd, tmp, ¬ifier->asd_list, asd_list) { - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_FWNODE: - fwnode_handle_put(asd->match.fwnode); - break; - default: - break; - } + WARN_ON(!list_empty(¬ifier->done_list)); - list_del(&asd->asd_list); - v4l2_async_nf_call_destroy(notifier, asd); - kfree(asd); + list_for_each_entry_safe(asc, tmp, ¬ifier->waiting_list, asc_entry) { + list_del(&asc->asc_entry); + v4l2_async_nf_call_destroy(notifier, asc); + + if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE) + fwnode_handle_put(asc->match.fwnode); + + kfree(asc); } + + notifier->sd = NULL; + notifier->v4l2_dev = NULL; } void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier) @@ -662,143 +685,156 @@ void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier) } EXPORT_SYMBOL_GPL(v4l2_async_nf_cleanup); -int __v4l2_async_nf_add_subdev(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev *asd) +static void __v4l2_async_nf_add_connection(struct v4l2_async_notifier *notifier, + struct v4l2_async_connection *asc) { - int ret; - mutex_lock(&list_lock); - ret = v4l2_async_nf_asd_valid(notifier, asd, -1); - if (ret) - goto unlock; + list_add_tail(&asc->asc_entry, ¬ifier->waiting_list); - list_add_tail(&asd->asd_list, ¬ifier->asd_list); - -unlock: mutex_unlock(&list_lock); - return ret; } -EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_subdev); -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_fwnode(struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode, - unsigned int asd_struct_size) + unsigned int asc_struct_size) { - struct v4l2_async_subdev *asd; - int ret; + struct v4l2_async_connection *asc; - asd = kzalloc(asd_struct_size, GFP_KERNEL); - if (!asd) + asc = kzalloc(asc_struct_size, GFP_KERNEL); + if (!asc) return ERR_PTR(-ENOMEM); - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = fwnode_handle_get(fwnode); + asc->notifier = notifier; + asc->match.type = V4L2_ASYNC_MATCH_TYPE_FWNODE; + asc->match.fwnode = fwnode_handle_get(fwnode); - ret = __v4l2_async_nf_add_subdev(notifier, asd); - if (ret) { - fwnode_handle_put(fwnode); - kfree(asd); - return ERR_PTR(ret); - } + __v4l2_async_nf_add_connection(notifier, asc); - return asd; + return asc; } EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode); -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_fwnode_remote(struct v4l2_async_notifier *notif, struct fwnode_handle *endpoint, - unsigned int asd_struct_size) + unsigned int asc_struct_size) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; struct fwnode_handle *remote; remote = fwnode_graph_get_remote_endpoint(endpoint); if (!remote) return ERR_PTR(-ENOTCONN); - asd = __v4l2_async_nf_add_fwnode(notif, remote, asd_struct_size); + asc = __v4l2_async_nf_add_fwnode(notif, remote, asc_struct_size); /* * Calling __v4l2_async_nf_add_fwnode grabs a refcount, * so drop the one we got in fwnode_graph_get_remote_port_parent. */ fwnode_handle_put(remote); - return asd; + return asc; } EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode_remote); -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_i2c(struct v4l2_async_notifier *notifier, int adapter_id, - unsigned short address, unsigned int asd_struct_size) + unsigned short address, unsigned int asc_struct_size) { - struct v4l2_async_subdev *asd; - int ret; + struct v4l2_async_connection *asc; - asd = kzalloc(asd_struct_size, GFP_KERNEL); - if (!asd) + asc = kzalloc(asc_struct_size, GFP_KERNEL); + if (!asc) return ERR_PTR(-ENOMEM); - asd->match_type = V4L2_ASYNC_MATCH_I2C; - asd->match.i2c.adapter_id = adapter_id; - asd->match.i2c.address = address; + asc->notifier = notifier; + asc->match.type = V4L2_ASYNC_MATCH_TYPE_I2C; + asc->match.i2c.adapter_id = adapter_id; + asc->match.i2c.address = address; - ret = __v4l2_async_nf_add_subdev(notifier, asd); - if (ret) { - kfree(asd); - return ERR_PTR(ret); - } + __v4l2_async_nf_add_connection(notifier, asc); - return asd; + return asc; } EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_i2c); +int v4l2_async_subdev_endpoint_add(struct v4l2_subdev *sd, + struct fwnode_handle *fwnode) +{ + struct v4l2_async_subdev_endpoint *ase; + + ase = kmalloc(sizeof(*ase), GFP_KERNEL); + if (!ase) + return -ENOMEM; + + ase->endpoint = fwnode; + list_add(&ase->async_subdev_endpoint_entry, + &sd->async_subdev_endpoint_list); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_async_subdev_endpoint_add); + +struct v4l2_async_connection * +v4l2_async_connection_unique(struct v4l2_subdev *sd) +{ + if (!list_is_singular(&sd->asc_list)) + return NULL; + + return list_first_entry(&sd->asc_list, + struct v4l2_async_connection, asc_subdev_entry); +} +EXPORT_SYMBOL_GPL(v4l2_async_connection_unique); + int v4l2_async_register_subdev(struct v4l2_subdev *sd) { struct v4l2_async_notifier *subdev_notifier; struct v4l2_async_notifier *notifier; + struct v4l2_async_connection *asc; int ret; + INIT_LIST_HEAD(&sd->asc_list); + /* - * No reference taken. The reference is held by the device - * (struct v4l2_subdev.dev), and async sub-device does not - * exist independently of the device at any point of time. + * No reference taken. The reference is held by the device (struct + * v4l2_subdev.dev), and async sub-device does not exist independently + * of the device at any point of time. + * + * The async sub-device shall always be registered for its device node, + * not the endpoint node. */ - if (!sd->fwnode && sd->dev) + if (!sd->fwnode && sd->dev) { sd->fwnode = dev_fwnode(sd->dev); + } else if (fwnode_graph_is_endpoint(sd->fwnode)) { + dev_warn(sd->dev, "sub-device fwnode is an endpoint!\n"); + return -EINVAL; + } mutex_lock(&list_lock); - INIT_LIST_HEAD(&sd->async_list); - - list_for_each_entry(notifier, ¬ifier_list, list) { + list_for_each_entry(notifier, ¬ifier_list, notifier_entry) { struct v4l2_device *v4l2_dev = v4l2_async_nf_find_v4l2_dev(notifier); - struct v4l2_async_subdev *asd; if (!v4l2_dev) continue; - asd = v4l2_async_find_match(notifier, sd); - if (!asd) - continue; + while ((asc = v4l2_async_find_match(notifier, sd))) { + ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, + asc); + if (ret) + goto err_unbind; - ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); - if (ret) - goto err_unbind; - - ret = v4l2_async_nf_try_complete(notifier); - if (ret) - goto err_unbind; - - goto out_unlock; + ret = v4l2_async_nf_try_complete(notifier); + if (ret) + goto err_unbind; + } } /* None matched, wait for hot-plugging */ list_add(&sd->async_list, &subdev_list); -out_unlock: mutex_unlock(&list_lock); return 0; @@ -810,11 +846,10 @@ err_unbind: */ subdev_notifier = v4l2_async_find_subdev_notifier(sd); if (subdev_notifier) - v4l2_async_nf_unbind_all_subdevs(subdev_notifier, false); + v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - if (sd->asd) - v4l2_async_nf_call_unbind(notifier, sd, sd->asd); - v4l2_async_cleanup(sd); + if (asc) + v4l2_async_unbind_subdev_one(notifier, asc); mutex_unlock(&list_lock); @@ -824,6 +859,8 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { + struct v4l2_async_connection *asc, *asc_tmp; + if (!sd->async_list.next) return; @@ -836,30 +873,34 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) kfree(sd->subdev_notifier); sd->subdev_notifier = NULL; - if (sd->asd) { - struct v4l2_async_notifier *notifier = sd->notifier; + if (sd->asc_list.next) { + list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list, + asc_subdev_entry) { + list_move(&asc->asc_entry, + &asc->notifier->waiting_list); - list_add(&sd->asd->list, ¬ifier->waiting); - - v4l2_async_nf_call_unbind(notifier, sd, sd->asd); + v4l2_async_unbind_subdev_one(asc->notifier, asc); + list_del(&asc->asc_subdev_entry); + } } - v4l2_async_cleanup(sd); + list_del(&sd->async_list); + sd->async_list.next = NULL; mutex_unlock(&list_lock); } EXPORT_SYMBOL(v4l2_async_unregister_subdev); -static void print_waiting_subdev(struct seq_file *s, - struct v4l2_async_subdev *asd) +static void print_waiting_match(struct seq_file *s, + struct v4l2_async_match_desc *match) { - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_I2C: - seq_printf(s, " [i2c] dev=%d-%04x\n", asd->match.i2c.adapter_id, - asd->match.i2c.address); + switch (match->type) { + case V4L2_ASYNC_MATCH_TYPE_I2C: + seq_printf(s, " [i2c] dev=%d-%04x\n", match->i2c.adapter_id, + match->i2c.address); break; - case V4L2_ASYNC_MATCH_FWNODE: { - struct fwnode_handle *devnode, *fwnode = asd->match.fwnode; + case V4L2_ASYNC_MATCH_TYPE_FWNODE: { + struct fwnode_handle *devnode, *fwnode = match->fwnode; devnode = fwnode_graph_is_endpoint(fwnode) ? fwnode_graph_get_port_parent(fwnode) : @@ -889,14 +930,14 @@ v4l2_async_nf_name(struct v4l2_async_notifier *notifier) static int pending_subdevs_show(struct seq_file *s, void *data) { struct v4l2_async_notifier *notif; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asc; mutex_lock(&list_lock); - list_for_each_entry(notif, ¬ifier_list, list) { + list_for_each_entry(notif, ¬ifier_list, notifier_entry) { seq_printf(s, "%s:\n", v4l2_async_nf_name(notif)); - list_for_each_entry(asd, ¬if->waiting, list) - print_waiting_subdev(s, asd); + list_for_each_entry(asc, ¬if->waiting_list, asc_entry) + print_waiting_match(s, &asc->match); } mutex_unlock(&list_lock); diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c new file mode 100644 index 000000000000..bc2dbec019b0 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-cci.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MIPI Camera Control Interface (CCI) register access helpers. + * + * Copyright (C) 2023 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +int cci_read(struct regmap *map, u32 reg, u64 *val, int *err) +{ + unsigned int len; + u8 buf[8]; + int ret; + + if (err && *err) + return *err; + + len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); + reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); + + ret = regmap_bulk_read(map, reg, buf, len); + if (ret) { + dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", + reg, ret); + goto out; + } + + switch (len) { + case 1: + *val = buf[0]; + break; + case 2: + *val = get_unaligned_be16(buf); + break; + case 3: + *val = get_unaligned_be24(buf); + break; + case 4: + *val = get_unaligned_be32(buf); + break; + case 8: + *val = get_unaligned_be64(buf); + break; + default: + dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", + len, reg); + ret = -EINVAL; + break; + } + +out: + if (ret && err) + *err = ret; + + return ret; +} +EXPORT_SYMBOL_GPL(cci_read); + +int cci_write(struct regmap *map, u32 reg, u64 val, int *err) +{ + unsigned int len; + u8 buf[8]; + int ret; + + if (err && *err) + return *err; + + len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); + reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); + + switch (len) { + case 1: + buf[0] = val; + break; + case 2: + put_unaligned_be16(val, buf); + break; + case 3: + put_unaligned_be24(val, buf); + break; + case 4: + put_unaligned_be32(val, buf); + break; + case 8: + put_unaligned_be64(val, buf); + break; + default: + dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", + len, reg); + ret = -EINVAL; + goto out; + } + + ret = regmap_bulk_write(map, reg, buf, len); + if (ret) + dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", + reg, ret); + +out: + if (ret && err) + *err = ret; + + return ret; +} +EXPORT_SYMBOL_GPL(cci_write); + +int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err) +{ + u64 readval; + int ret; + + ret = cci_read(map, reg, &readval, err); + if (ret) + return ret; + + val = (readval & ~mask) | (val & mask); + + return cci_write(map, reg, val, err); +} +EXPORT_SYMBOL_GPL(cci_update_bits); + +int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs, + unsigned int num_regs, int *err) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_regs; i++) { + ret = cci_write(map, regs[i].reg, regs[i].val, err); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cci_multi_reg_write); + +#if IS_ENABLED(CONFIG_V4L2_CCI_I2C) +struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client, + int reg_addr_bits) +{ + struct regmap_config config = { + .reg_bits = reg_addr_bits, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .disable_locking = true, + }; + + return devm_regmap_init_i2c(client, &config); +} +EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support"); diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index bee1535b04d3..3a4b15a98e02 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -262,6 +262,10 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, + { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, + .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, /* YUV planar formats */ { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 049c2f2001ea..7f181fbbb140 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -568,19 +568,29 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, link->local_id = fwep.id; link->local_port = fwep.port; link->local_node = fwnode_graph_get_port_parent(fwnode); + if (!link->local_node) + return -ENOLINK; fwnode = fwnode_graph_get_remote_endpoint(fwnode); - if (!fwnode) { - fwnode_handle_put(fwnode); - return -ENOLINK; - } + if (!fwnode) + goto err_put_local_node; fwnode_graph_parse_endpoint(fwnode, &fwep); link->remote_id = fwep.id; link->remote_port = fwep.port; link->remote_node = fwnode_graph_get_port_parent(fwnode); + if (!link->remote_node) + goto err_put_remote_endpoint; return 0; + +err_put_remote_endpoint: + fwnode_handle_put(fwnode); + +err_put_local_node: + fwnode_handle_put(link->local_node); + + return -ENOLINK; } EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); @@ -798,103 +808,6 @@ int v4l2_fwnode_device_parse(struct device *dev, } EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse); -static int -v4l2_async_nf_fwnode_parse_endpoint(struct device *dev, - struct v4l2_async_notifier *notifier, - struct fwnode_handle *endpoint, - unsigned int asd_struct_size, - parse_endpoint_func parse_endpoint) -{ - struct v4l2_fwnode_endpoint vep = { .bus_type = 0 }; - struct v4l2_async_subdev *asd; - int ret; - - asd = kzalloc(asd_struct_size, GFP_KERNEL); - if (!asd) - return -ENOMEM; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = - fwnode_graph_get_remote_port_parent(endpoint); - if (!asd->match.fwnode) { - dev_dbg(dev, "no remote endpoint found\n"); - ret = -ENOTCONN; - goto out_err; - } - - ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep); - if (ret) { - dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n", - ret); - goto out_err; - } - - ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0; - if (ret == -ENOTCONN) - dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port, - vep.base.id); - else if (ret < 0) - dev_warn(dev, - "driver could not parse port@%u/endpoint@%u (%d)\n", - vep.base.port, vep.base.id, ret); - v4l2_fwnode_endpoint_free(&vep); - if (ret < 0) - goto out_err; - - ret = __v4l2_async_nf_add_subdev(notifier, asd); - if (ret < 0) { - /* not an error if asd already exists */ - if (ret == -EEXIST) - ret = 0; - goto out_err; - } - - return 0; - -out_err: - fwnode_handle_put(asd->match.fwnode); - kfree(asd); - - return ret == -ENOTCONN ? 0 : ret; -} - -int -v4l2_async_nf_parse_fwnode_endpoints(struct device *dev, - struct v4l2_async_notifier *notifier, - size_t asd_struct_size, - parse_endpoint_func parse_endpoint) -{ - struct fwnode_handle *fwnode; - int ret = 0; - - if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) - return -EINVAL; - - fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) { - struct fwnode_handle *dev_fwnode; - bool is_available; - - dev_fwnode = fwnode_graph_get_port_parent(fwnode); - is_available = fwnode_device_is_available(dev_fwnode); - fwnode_handle_put(dev_fwnode); - if (!is_available) - continue; - - - ret = v4l2_async_nf_fwnode_parse_endpoint(dev, notifier, - fwnode, - asd_struct_size, - parse_endpoint); - if (ret < 0) - break; - } - - fwnode_handle_put(fwnode); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_async_nf_parse_fwnode_endpoints); - /* * v4l2_fwnode_reference_parse - parse references for async sub-devices * @dev: the device node the properties of which are parsed for references @@ -918,10 +831,10 @@ static int v4l2_fwnode_reference_parse(struct device *dev, !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), prop, NULL, 0, index, &args)); index++) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; asd = v4l2_async_nf_add_fwnode(notifier, args.fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(args.fwnode); if (IS_ERR(asd)) { /* not an error if asd already exists */ @@ -1223,10 +1136,10 @@ v4l2_fwnode_reference_parse_int_props(struct device *dev, props, nprops))); index++) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; asd = v4l2_async_nf_add_fwnode(notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(fwnode); if (IS_ERR(asd)) { ret = PTR_ERR(asd); @@ -1302,7 +1215,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (!notifier) return -ENOMEM; - v4l2_async_nf_init(notifier); + v4l2_async_subdev_nf_init(notifier, sd); ret = v4l2_subdev_get_privacy_led(sd); if (ret < 0) @@ -1312,7 +1225,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (ret < 0) goto out_cleanup; - ret = v4l2_async_subdev_nf_register(sd, notifier); + ret = v4l2_async_nf_register(notifier); if (ret < 0) goto out_cleanup; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 01ba27f2ef87..f4d9d6279094 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1508,6 +1508,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break; case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break; case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break; + case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break; + case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break; default: if (fmt->description[0]) return; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2ec179cd1264..b92348ad61f6 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -200,9 +200,6 @@ static inline int check_format(struct v4l2_subdev *sd, if (!format) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - format->stream = 0; - return check_which(format->which) ? : check_pad(sd, format->pad) ? : check_state(sd, state, format->which, format->pad, format->stream); } @@ -230,9 +227,6 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, if (!code) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - code->stream = 0; - return check_which(code->which) ? : check_pad(sd, code->pad) ? : check_state(sd, state, code->which, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); @@ -245,9 +239,6 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, if (!fse) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - fse->stream = 0; - return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : check_state(sd, state, fse->which, fse->pad, fse->stream) ? : sd->ops->pad->enum_frame_size(sd, state, fse); @@ -283,9 +274,6 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, if (!fie) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - fie->stream = 0; - return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : check_state(sd, state, fie->which, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); @@ -298,9 +286,6 @@ static inline int check_selection(struct v4l2_subdev *sd, if (!sel) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - sel->stream = 0; - return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : check_state(sd, state, sel->which, sel->pad, sel->stream); } @@ -1467,8 +1452,20 @@ EXPORT_SYMBOL_GPL(__v4l2_subdev_init_finalize); void v4l2_subdev_cleanup(struct v4l2_subdev *sd) { + struct v4l2_async_subdev_endpoint *ase, *ase_tmp; + __v4l2_subdev_state_free(sd->active_state); sd->active_state = NULL; + + if (list_empty(&sd->async_subdev_endpoint_list)) + return; + + list_for_each_entry_safe(ase, ase_tmp, &sd->async_subdev_endpoint_list, + async_subdev_endpoint_entry) { + list_del(&ase->async_subdev_endpoint_entry); + + kfree(ase); + } } EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); @@ -1605,7 +1602,7 @@ EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing, + const struct v4l2_subdev_krouting *routing, const struct v4l2_mbus_framefmt *fmt) { struct v4l2_subdev_stream_configs *stream_configs; @@ -1992,11 +1989,16 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); + /* Call the .enable_streams() operation. */ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, streams_mask); - if (ret) + if (ret) { + dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, + streams_mask, ret); goto done; + } /* Mark the streams as enabled. */ for (i = 0; i < state->stream_configs.num_configs; ++i) { @@ -2104,11 +2106,16 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); + /* Call the .disable_streams() operation. */ ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, streams_mask); - if (ret) + if (ret) { + dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad, + streams_mask, ret); goto done; + } /* Mark the streams as disabled. */ for (i = 0; i < state->stream_configs.num_configs; ++i) { @@ -2182,6 +2189,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) sd->dev_priv = NULL; sd->host_priv = NULL; sd->privacy_led = NULL; + INIT_LIST_HEAD(&sd->async_subdev_endpoint_list); #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.name = sd->name; sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index f178ed9899a9..3ae8e8af8ab3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -3721,6 +3721,60 @@ struct hwrm_func_backing_store_qcaps_v2_output { u8 valid; }; +/* hwrm_func_dbr_pacing_qcfg_input (size:128b/16B) */ +struct hwrm_func_dbr_pacing_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* hwrm_func_dbr_pacing_qcfg_output (size:512b/64B) */ +struct hwrm_func_dbr_pacing_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 flags; +#define FUNC_DBR_PACING_QCFG_RESP_FLAGS_DBR_NQ_EVENT_ENABLED 0x1UL + u8 unused_0[7]; + __le32 dbr_stat_db_fifo_reg; +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_SFT 0 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_PCIE_CFG 0x0UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_GRC 0x1UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR0 0x2UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR1 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_LAST \ + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR1 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_MASK 0xfffffffcUL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SFT 2 + __le32 dbr_stat_db_fifo_reg_watermark_mask; + u8 dbr_stat_db_fifo_reg_watermark_shift; + u8 unused_1[3]; + __le32 dbr_stat_db_fifo_reg_fifo_room_mask; + u8 dbr_stat_db_fifo_reg_fifo_room_shift; + u8 unused_2[3]; + __le32 dbr_throttling_aeq_arm_reg; +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_MASK 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_SFT 0 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_PCIE_CFG 0x0UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_GRC 0x1UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR0 0x2UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR1 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_LAST \ + FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR1 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_MASK 0xfffffffcUL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SFT 2 + u8 dbr_throttling_aeq_arm_reg_val; + u8 unused_3[7]; + __le32 primary_nq_id; + __le32 pacing_threshold; + u8 unused_4[7]; + u8 valid; +}; + /* hwrm_func_drv_if_change_input (size:192b/24B) */ struct hwrm_func_drv_if_change_input { __le16 req_type; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index 852eb449ccae..6ba2b9398633 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -345,7 +345,7 @@ static void bnxt_set_edev_info(struct bnxt_en_dev *edev, struct bnxt *bp) edev->hw_ring_stats_size = bp->hw_ring_stats_size; edev->pf_port_id = bp->pf.port_id; edev->en_state = bp->state; - + edev->bar0 = bp->bar0; edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h index 80cbc4b6130a..6ff77f082e6c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h @@ -81,6 +81,7 @@ struct bnxt_en_dev { * mode only. Will be * updated in resume. */ + void __iomem *bar0; }; static inline bool bnxt_ulp_registered(struct bnxt_en_dev *edev) diff --git a/drivers/staging/media/atomisp/Kconfig b/drivers/staging/media/atomisp/Kconfig index e9b168ba97bf..5d8917160d41 100644 --- a/drivers/staging/media/atomisp/Kconfig +++ b/drivers/staging/media/atomisp/Kconfig @@ -12,9 +12,12 @@ menuconfig INTEL_ATOMISP config VIDEO_ATOMISP tristate "Intel Atom Image Signal Processor Driver" depends on VIDEO_DEV && INTEL_ATOMISP + depends on MEDIA_PCI_SUPPORT depends on PMIC_OPREGION + depends on I2C select V4L2_FWNODE select IOSF_MBI + select IPU_BRIDGE select VIDEOBUF2_VMALLOC select VIDEO_V4L2_SUBDEV_API help diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig index e726101b24e4..2d4165cda2f1 100644 --- a/drivers/staging/media/atomisp/i2c/Kconfig +++ b/drivers/staging/media/atomisp/i2c/Kconfig @@ -57,18 +57,6 @@ config VIDEO_ATOMISP_GC0310 This is a Video4Linux2 sensor-level driver for the Galaxycore GC0310 0.3MP sensor. -config VIDEO_ATOMISP_OV2680 - tristate "Omnivision OV2680 sensor support" - depends on ACPI - depends on I2C && VIDEO_DEV - help - This is a Video4Linux2 sensor-level driver for the Omnivision - OV2680 raw camera. - - ov2680 is a 2M raw sensor. - - It currently only works with the atomisp driver. - config VIDEO_ATOMISP_OV5693 tristate "Omnivision ov5693 sensor support" depends on ACPI diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile index 8d022986e199..fc55af5f3422 100644 --- a/drivers/staging/media/atomisp/i2c/Makefile +++ b/drivers/staging/media/atomisp/i2c/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_VIDEO_ATOMISP_OV5693) += ov5693/ obj-$(CONFIG_VIDEO_ATOMISP_MT9M114) += atomisp-mt9m114.o obj-$(CONFIG_VIDEO_ATOMISP_GC2235) += atomisp-gc2235.o obj-$(CONFIG_VIDEO_ATOMISP_OV2722) += atomisp-ov2722.o -obj-$(CONFIG_VIDEO_ATOMISP_OV2680) += atomisp-ov2680.o obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o obj-$(CONFIG_VIDEO_ATOMISP_MSRLIST_HELPER) += atomisp-libmsrlisthelper.o diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c deleted file mode 100644 index 4cc2839937af..000000000000 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c +++ /dev/null @@ -1,849 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for OmniVision OV2680 1080p HD camera sensor. - * - * Copyright (c) 2013 Intel Corporation. All Rights Reserved. - * Copyright (c) 2023 Hans de Goede - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ov2680.h" - -static const struct v4l2_rect ov2680_default_crop = { - .left = OV2680_ACTIVE_START_LEFT, - .top = OV2680_ACTIVE_START_TOP, - .width = OV2680_ACTIVE_WIDTH, - .height = OV2680_ACTIVE_HEIGHT, -}; - -static int ov2680_write_reg_array(struct i2c_client *client, - const struct ov2680_reg *reglist) -{ - const struct ov2680_reg *next = reglist; - int ret; - - for (; next->reg != 0; next++) { - ret = ov_write_reg8(client, next->reg, next->val); - if (ret) - return ret; - } - - return 0; -} - -static void ov2680_set_bayer_order(struct ov2680_dev *sensor, struct v4l2_mbus_framefmt *fmt) -{ - static const int ov2680_hv_flip_bayer_order[] = { - MEDIA_BUS_FMT_SBGGR10_1X10, - MEDIA_BUS_FMT_SGRBG10_1X10, - MEDIA_BUS_FMT_SGBRG10_1X10, - MEDIA_BUS_FMT_SRGGB10_1X10, - }; - int hv_flip = 0; - - if (sensor->ctrls.vflip->val) - hv_flip += 1; - - if (sensor->ctrls.hflip->val) - hv_flip += 2; - - fmt->code = ov2680_hv_flip_bayer_order[hv_flip]; -} - -static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val) -{ - int ret; - - if (sensor->is_streaming) - return -EBUSY; - - ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT1, BIT(2), val ? BIT(2) : 0); - if (ret < 0) - return ret; - - ov2680_set_bayer_order(sensor, &sensor->mode.fmt); - return 0; -} - -static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val) -{ - int ret; - - if (sensor->is_streaming) - return -EBUSY; - - ret = ov_update_reg(sensor->client, OV2680_REG_FORMAT2, BIT(2), val ? BIT(2) : 0); - if (ret < 0) - return ret; - - ov2680_set_bayer_order(sensor, &sensor->mode.fmt); - return 0; -} - -static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp) -{ - return ov_write_reg24(sensor->client, OV2680_REG_EXPOSURE_PK_HIGH, exp << 4); -} - -static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain) -{ - return ov_write_reg16(sensor->client, OV2680_REG_GAIN_PK, gain); -} - -static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) -{ - int ret; - - if (!value) - return ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), 0); - - ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, 0x03, value - 1); - if (ret < 0) - return ret; - - ret = ov_update_reg(sensor->client, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); - if (ret < 0) - return ret; - - return 0; -} - -static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - int ret; - - /* Only apply changes to the controls if the device is powered up */ - if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { - ov2680_set_bayer_order(sensor, &sensor->mode.fmt); - return 0; - } - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ret = ov2680_set_vflip(sensor, ctrl->val); - break; - case V4L2_CID_HFLIP: - ret = ov2680_set_hflip(sensor, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - ret = ov2680_exposure_set(sensor, ctrl->val); - break; - case V4L2_CID_GAIN: - ret = ov2680_gain_set(sensor, ctrl->val); - break; - case V4L2_CID_TEST_PATTERN: - ret = ov2680_test_pattern_set(sensor, ctrl->val); - break; - default: - ret = -EINVAL; - } - - pm_runtime_put(sensor->sd.dev); - return ret; -} - -static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { - .s_ctrl = ov2680_s_ctrl, -}; - -static int ov2680_init_registers(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = ov_write_reg8(client, OV2680_SW_RESET, 0x01); - - /* Wait for sensor reset */ - usleep_range(1000, 2000); - - ret |= ov2680_write_reg_array(client, ov2680_global_setting); - - return ret; -} - -static struct v4l2_mbus_framefmt * -__ov2680_get_pad_format(struct ov2680_dev *sensor, struct v4l2_subdev_state *state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&sensor->sd, state, pad); - - return &sensor->mode.fmt; -} - -static struct v4l2_rect * -__ov2680_get_pad_crop(struct ov2680_dev *sensor, struct v4l2_subdev_state *state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); - - return &sensor->mode.crop; -} - -static void ov2680_fill_format(struct ov2680_dev *sensor, - struct v4l2_mbus_framefmt *fmt, - unsigned int width, unsigned int height) -{ - memset(fmt, 0, sizeof(*fmt)); - fmt->width = width; - fmt->height = height; - fmt->field = V4L2_FIELD_NONE; - ov2680_set_bayer_order(sensor, fmt); -} - -static void ov2680_calc_mode(struct ov2680_dev *sensor) -{ - int width = sensor->mode.fmt.width; - int height = sensor->mode.fmt.height; - int orig_width = width; - int orig_height = height; - - if (width <= (sensor->mode.crop.width / 2) && - height <= (sensor->mode.crop.height / 2)) { - sensor->mode.binning = true; - width *= 2; - height *= 2; - } else { - sensor->mode.binning = false; - } - - sensor->mode.h_start = - (sensor->mode.crop.left + (sensor->mode.crop.width - width) / 2) & ~1; - sensor->mode.v_start = - (sensor->mode.crop.top + (sensor->mode.crop.height - height) / 2) & ~1; - sensor->mode.h_end = min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, - OV2680_NATIVE_WIDTH - 1); - sensor->mode.v_end = min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, - OV2680_NATIVE_HEIGHT - 1); - sensor->mode.h_output_size = orig_width; - sensor->mode.v_output_size = orig_height; - sensor->mode.hts = OV2680_PIXELS_PER_LINE; - sensor->mode.vts = OV2680_LINES_PER_FRAME; -} - -static int ov2680_set_mode(struct ov2680_dev *sensor) -{ - struct i2c_client *client = sensor->client; - u8 sensor_ctrl_0a, inc, fmt1, fmt2; - int ret; - - if (sensor->mode.binning) { - sensor_ctrl_0a = 0x23; - inc = 0x31; - fmt1 = 0xc2; - fmt2 = 0x01; - } else { - sensor_ctrl_0a = 0x21; - inc = 0x11; - fmt1 = 0xc0; - fmt2 = 0x00; - } - - ret = ov_write_reg8(client, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_HORIZONTAL_START_H, sensor->mode.h_start); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_VERTICAL_START_H, sensor->mode.v_start); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_HORIZONTAL_END_H, sensor->mode.h_end); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_VERTICAL_END_H, sensor->mode.v_end); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_HORIZONTAL_OUTPUT_SIZE_H, - sensor->mode.h_output_size); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_VERTICAL_OUTPUT_SIZE_H, - sensor->mode.v_output_size); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_HTS, sensor->mode.hts); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_VTS, sensor->mode.vts); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_ISP_X_WIN, 0); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_ISP_Y_WIN, 0); - if (ret) - return ret; - - ret = ov_write_reg8(client, OV2680_X_INC, inc); - if (ret) - return ret; - - ret = ov_write_reg8(client, OV2680_Y_INC, inc); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_X_WIN, sensor->mode.h_output_size); - if (ret) - return ret; - - ret = ov_write_reg16(client, OV2680_Y_WIN, sensor->mode.v_output_size); - if (ret) - return ret; - - ret = ov_write_reg8(client, OV2680_REG_FORMAT1, fmt1); - if (ret) - return ret; - - ret = ov_write_reg8(client, OV2680_REG_FORMAT2, fmt2); - if (ret) - return ret; - - return 0; -} - -static int ov2680_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - struct v4l2_mbus_framefmt *fmt; - const struct v4l2_rect *crop; - unsigned int width, height; - - crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, format->which); - - /* Limit set_fmt max size to crop width / height */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - OV2680_MIN_CROP_WIDTH, crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - OV2680_MIN_CROP_HEIGHT, crop->height); - - fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, format->which); - ov2680_fill_format(sensor, fmt, width, height); - - format->format = *fmt; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - return 0; - - mutex_lock(&sensor->lock); - ov2680_calc_mode(sensor); - mutex_unlock(&sensor->lock); - return 0; -} - -static int ov2680_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - struct v4l2_mbus_framefmt *fmt; - - fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, format->which); - format->format = *fmt; - return 0; -} - -static int ov2680_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_selection *sel) -{ - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - mutex_lock(&sensor->lock); - sel->r = *__ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); - mutex_unlock(&sensor->lock); - break; - case V4L2_SEL_TGT_NATIVE_SIZE: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = OV2680_NATIVE_WIDTH; - sel->r.height = OV2680_NATIVE_HEIGHT; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = OV2680_ACTIVE_START_TOP; - sel->r.left = OV2680_ACTIVE_START_LEFT; - sel->r.width = OV2680_ACTIVE_WIDTH; - sel->r.height = OV2680_ACTIVE_HEIGHT; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ov2680_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_selection *sel) -{ - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* - * Clamp the boundaries of the crop rectangle to the size of the sensor - * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't - * disrupted. - */ - rect.left = clamp(ALIGN(sel->r.left, 2), OV2680_NATIVE_START_LEFT, - OV2680_NATIVE_WIDTH); - rect.top = clamp(ALIGN(sel->r.top, 2), OV2680_NATIVE_START_TOP, - OV2680_NATIVE_HEIGHT); - rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), - OV2680_MIN_CROP_WIDTH, OV2680_NATIVE_WIDTH); - rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), - OV2680_MIN_CROP_HEIGHT, OV2680_NATIVE_HEIGHT); - - /* Make sure the crop rectangle isn't outside the bounds of the array */ - rect.width = min_t(unsigned int, rect.width, - OV2680_NATIVE_WIDTH - rect.left); - rect.height = min_t(unsigned int, rect.height, - OV2680_NATIVE_HEIGHT - rect.top); - - __crop = __ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* - * Reset the output image size if the crop rectangle size has - * been modified. - */ - format = __ov2680_get_pad_format(sensor, state, sel->pad, sel->which); - format->width = rect.width; - format->height = rect.height; - } - - *__crop = rect; - sel->r = rect; - - return 0; -} - -static int ov2680_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_subdev_format fmt = { - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .width = 800, - .height = 600, - }, - }; - - sd_state->pads[0].try_crop = ov2680_default_crop; - - return ov2680_set_fmt(sd, sd_state, &fmt); -} - -static int ov2680_detect(struct i2c_client *client) -{ - struct i2c_adapter *adapter = client->adapter; - u32 high = 0, low = 0; - int ret; - u16 id; - u8 revision; - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) - return -ENODEV; - - ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_H, &high); - if (ret) { - dev_err(&client->dev, "sensor_id_high read failed (%d)\n", ret); - return -ENODEV; - } - ret = ov_read_reg8(client, OV2680_SC_CMMN_CHIP_ID_L, &low); - id = ((((u16)high) << 8) | (u16)low); - - if (id != OV2680_ID) { - dev_err(&client->dev, "sensor ID error 0x%x\n", id); - return -ENODEV; - } - - ret = ov_read_reg8(client, OV2680_SC_CMMN_SUB_ID, &high); - revision = (u8)high & 0x0f; - - dev_info(&client->dev, "sensor_revision id = 0x%x, rev= %d\n", - id, revision); - - return 0; -} - -static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - if (sensor->is_streaming == enable) { - dev_warn(&client->dev, "stream already %s\n", enable ? "started" : "stopped"); - goto error_unlock; - } - - if (enable) { - ret = pm_runtime_get_sync(sensor->sd.dev); - if (ret < 0) - goto error_power_down; - - ret = ov2680_set_mode(sensor); - if (ret) - goto error_power_down; - - /* Restore value of all ctrls */ - ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret) - goto error_power_down; - - ret = ov_write_reg8(client, OV2680_SW_STREAM, OV2680_START_STREAMING); - if (ret) - goto error_power_down; - } else { - ov_write_reg8(client, OV2680_SW_STREAM, OV2680_STOP_STREAMING); - pm_runtime_put(sensor->sd.dev); - } - - sensor->is_streaming = enable; - v4l2_ctrl_activate(sensor->ctrls.vflip, !enable); - v4l2_ctrl_activate(sensor->ctrls.hflip, !enable); - - mutex_unlock(&sensor->lock); - return 0; - -error_power_down: - pm_runtime_put(sensor->sd.dev); - sensor->is_streaming = false; -error_unlock: - mutex_unlock(&sensor->lock); - return ret; -} - -static int ov2680_s_config(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - dev_err(&client->dev, "ov2680 power-up err.\n"); - goto fail_power_on; - } - - /* config & detect sensor */ - ret = ov2680_detect(client); - if (ret) - dev_err(&client->dev, "ov2680_detect err s_config.\n"); - -fail_power_on: - pm_runtime_put(&client->dev); - return ret; -} - -static int ov2680_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *interval) -{ - interval->interval.numerator = 1; - interval->interval.denominator = OV2680_FPS; - return 0; -} - -static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - /* We support only a single format */ - if (code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int ov2680_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - static const struct v4l2_frmsize_discrete ov2680_frame_sizes[] = { - { 1616, 1216 }, - { 1616, 1096 }, - { 1616, 916 }, - { 1456, 1096 }, - { 1296, 976 }, - { 1296, 736 }, - { 784, 592 }, - { 656, 496 }, - }; - int index = fse->index; - - if (index >= ARRAY_SIZE(ov2680_frame_sizes)) - return -EINVAL; - - fse->min_width = ov2680_frame_sizes[index].width; - fse->min_height = ov2680_frame_sizes[index].height; - fse->max_width = ov2680_frame_sizes[index].width; - fse->max_height = ov2680_frame_sizes[index].height; - - return 0; -} - -static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval_enum *fie) -{ - /* Only 1 framerate */ - if (fie->index) - return -EINVAL; - - fie->interval.numerator = 1; - fie->interval.denominator = OV2680_FPS; - return 0; -} - -static int ov2680_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) -{ - *frames = OV2680_SKIP_FRAMES; - return 0; -} - -static const struct v4l2_subdev_video_ops ov2680_video_ops = { - .s_stream = ov2680_s_stream, - .g_frame_interval = ov2680_g_frame_interval, -}; - -static const struct v4l2_subdev_sensor_ops ov2680_sensor_ops = { - .g_skip_frames = ov2680_g_skip_frames, -}; - -static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { - .init_cfg = ov2680_init_cfg, - .enum_mbus_code = ov2680_enum_mbus_code, - .enum_frame_size = ov2680_enum_frame_size, - .enum_frame_interval = ov2680_enum_frame_interval, - .get_fmt = ov2680_get_fmt, - .set_fmt = ov2680_set_fmt, - .get_selection = ov2680_get_selection, - .set_selection = ov2680_set_selection, -}; - -static const struct v4l2_subdev_ops ov2680_ops = { - .video = &ov2680_video_ops, - .pad = &ov2680_pad_ops, - .sensor = &ov2680_sensor_ops, -}; - -static int ov2680_init_controls(struct ov2680_dev *sensor) -{ - static const char * const test_pattern_menu[] = { - "Disabled", - "Color Bars", - "Random Data", - "Square", - "Black Image", - }; - const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; - struct ov2680_ctrls *ctrls = &sensor->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN; - - v4l2_ctrl_handler_init(hdl, 4); - - hdl->lock = &sensor->lock; - - ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, exp_max, 1, exp_max); - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 1023, 1, 250); - ctrls->test_pattern = - v4l2_ctrl_new_std_menu_items(hdl, - &ov2680_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(test_pattern_menu) - 1, - 0, 0, test_pattern_menu); - - ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - - if (hdl->error) - return hdl->error; - - sensor->sd.ctrl_handler = hdl; - return 0; -} - -static void ov2680_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - - dev_dbg(&client->dev, "ov2680_remove...\n"); - - v4l2_async_unregister_subdev(&sensor->sd); - media_entity_cleanup(&sensor->sd.entity); - v4l2_ctrl_handler_free(&sensor->ctrls.handler); - mutex_destroy(&sensor->lock); - fwnode_handle_put(sensor->ep_fwnode); - pm_runtime_disable(&client->dev); -} - -static int ov2680_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct ov2680_dev *sensor; - int ret; - - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); - if (!sensor) - return -ENOMEM; - - mutex_init(&sensor->lock); - - sensor->client = client; - v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_ops); - - /* - * Sometimes the fwnode graph is initialized by the bridge driver. - * Bridge drivers doing this may also add GPIO mappings, wait for this. - */ - sensor->ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); - if (!sensor->ep_fwnode) - return dev_err_probe(dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); - - sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); - if (IS_ERR(sensor->powerdown)) { - fwnode_handle_put(sensor->ep_fwnode); - return dev_err_probe(dev, PTR_ERR(sensor->powerdown), "getting powerdown GPIO\n"); - } - - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); - pm_runtime_set_autosuspend_delay(dev, 1000); - pm_runtime_use_autosuspend(dev); - - ret = ov2680_s_config(&sensor->sd); - if (ret) { - ov2680_remove(client); - return ret; - } - - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - sensor->sd.fwnode = sensor->ep_fwnode; - - ret = ov2680_init_controls(sensor); - if (ret) { - ov2680_remove(client); - return ret; - } - - ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); - if (ret) { - ov2680_remove(client); - return ret; - } - - sensor->mode.crop = ov2680_default_crop; - ov2680_fill_format(sensor, &sensor->mode.fmt, OV2680_NATIVE_WIDTH, OV2680_NATIVE_HEIGHT); - ov2680_calc_mode(sensor); - - ret = v4l2_async_register_subdev_sensor(&sensor->sd); - if (ret) { - ov2680_remove(client); - return ret; - } - - return 0; -} - -static int ov2680_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - - gpiod_set_value_cansleep(sensor->powerdown, 1); - return 0; -} - -static int ov2680_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2680_dev *sensor = to_ov2680_sensor(sd); - - /* according to DS, at least 5ms is needed after DOVDD (enabled by ACPI) */ - usleep_range(5000, 6000); - - gpiod_set_value_cansleep(sensor->powerdown, 0); - - /* according to DS, 20ms is needed between PWDN and i2c access */ - msleep(20); - - ov2680_init_registers(sd); - return 0; -} - -static DEFINE_RUNTIME_DEV_PM_OPS(ov2680_pm_ops, ov2680_suspend, ov2680_resume, NULL); - -static const struct acpi_device_id ov2680_acpi_match[] = { - {"XXOV2680"}, - {"OVTI2680"}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, ov2680_acpi_match); - -static struct i2c_driver ov2680_driver = { - .driver = { - .name = "ov2680", - .pm = pm_sleep_ptr(&ov2680_pm_ops), - .acpi_match_table = ov2680_acpi_match, - }, - .probe = ov2680_probe, - .remove = ov2680_remove, -}; -module_i2c_driver(ov2680_driver); - -MODULE_AUTHOR("Jacky Wang "); -MODULE_DESCRIPTION("A low-level driver for OmniVision 2680 sensors"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h deleted file mode 100644 index d032af245674..000000000000 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ /dev/null @@ -1,249 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for OmniVision OV2680 5M camera sensor. - * - * Copyright (c) 2013 Intel Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - */ - -#ifndef __OV2680_H__ -#define __OV2680_H__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define OV2680_NATIVE_WIDTH 1616 -#define OV2680_NATIVE_HEIGHT 1216 -#define OV2680_NATIVE_START_LEFT 0 -#define OV2680_NATIVE_START_TOP 0 -#define OV2680_ACTIVE_WIDTH 1600 -#define OV2680_ACTIVE_HEIGHT 1200 -#define OV2680_ACTIVE_START_LEFT 8 -#define OV2680_ACTIVE_START_TOP 8 -#define OV2680_MIN_CROP_WIDTH 2 -#define OV2680_MIN_CROP_HEIGHT 2 - -/* 1704 * 1294 * 30fps = 66MHz pixel clock */ -#define OV2680_PIXELS_PER_LINE 1704 -#define OV2680_LINES_PER_FRAME 1294 -#define OV2680_FPS 30 -#define OV2680_SKIP_FRAMES 3 - -/* If possible send 16 extra rows / lines to the ISP as padding */ -#define OV2680_END_MARGIN 16 - -#define OV2680_FOCAL_LENGTH_NUM 334 /*3.34mm*/ - -#define OV2680_INTEGRATION_TIME_MARGIN 8 -#define OV2680_ID 0x2680 - -/* - * OV2680 System control registers - */ -#define OV2680_SW_SLEEP 0x0100 -#define OV2680_SW_RESET 0x0103 -#define OV2680_SW_STREAM 0x0100 - -#define OV2680_SC_CMMN_CHIP_ID_H 0x300A -#define OV2680_SC_CMMN_CHIP_ID_L 0x300B -#define OV2680_SC_CMMN_SCCB_ID 0x302B /* 0x300C*/ -#define OV2680_SC_CMMN_SUB_ID 0x302A /* process, version*/ - -#define OV2680_GROUP_ACCESS 0x3208 /*Bit[7:4] Group control, Bit[3:0] Group ID*/ - -#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 -#define OV2680_REG_GAIN_PK 0x350a - -#define OV2680_REG_SENSOR_CTRL_0A 0x370a - -#define OV2680_HORIZONTAL_START_H 0x3800 /* Bit[11:8] */ -#define OV2680_HORIZONTAL_START_L 0x3801 /* Bit[7:0] */ -#define OV2680_VERTICAL_START_H 0x3802 /* Bit[11:8] */ -#define OV2680_VERTICAL_START_L 0x3803 /* Bit[7:0] */ -#define OV2680_HORIZONTAL_END_H 0x3804 /* Bit[11:8] */ -#define OV2680_HORIZONTAL_END_L 0x3805 /* Bit[7:0] */ -#define OV2680_VERTICAL_END_H 0x3806 /* Bit[11:8] */ -#define OV2680_VERTICAL_END_L 0x3807 /* Bit[7:0] */ -#define OV2680_HORIZONTAL_OUTPUT_SIZE_H 0x3808 /* Bit[11:8] */ -#define OV2680_HORIZONTAL_OUTPUT_SIZE_L 0x3809 /* Bit[7:0] */ -#define OV2680_VERTICAL_OUTPUT_SIZE_H 0x380a /* Bit[11:8] */ -#define OV2680_VERTICAL_OUTPUT_SIZE_L 0x380b /* Bit[7:0] */ -#define OV2680_HTS 0x380c -#define OV2680_VTS 0x380e -#define OV2680_ISP_X_WIN 0x3810 -#define OV2680_ISP_Y_WIN 0x3812 -#define OV2680_X_INC 0x3814 -#define OV2680_Y_INC 0x3815 - -#define OV2680_FRAME_OFF_NUM 0x4202 - -/*Flip/Mirror*/ -#define OV2680_REG_FORMAT1 0x3820 -#define OV2680_REG_FORMAT2 0x3821 - -#define OV2680_MWB_RED_GAIN_H 0x5004/*0x3400*/ -#define OV2680_MWB_GREEN_GAIN_H 0x5006/*0x3402*/ -#define OV2680_MWB_BLUE_GAIN_H 0x5008/*0x3404*/ -#define OV2680_MWB_GAIN_MAX 0x0fff - -#define OV2680_REG_ISP_CTRL00 0x5080 - -#define OV2680_X_WIN 0x5704 -#define OV2680_Y_WIN 0x5706 -#define OV2680_WIN_CONTROL 0x5708 - -#define OV2680_START_STREAMING 0x01 -#define OV2680_STOP_STREAMING 0x00 - -/* - * ov2680 device structure. - */ -struct ov2680_dev { - struct v4l2_subdev sd; - struct media_pad pad; - /* Protect against concurrent changes to controls */ - struct mutex lock; - struct i2c_client *client; - struct gpio_desc *powerdown; - struct fwnode_handle *ep_fwnode; - bool is_streaming; - - struct ov2680_mode { - struct v4l2_rect crop; - struct v4l2_mbus_framefmt fmt; - bool binning; - u16 h_start; - u16 v_start; - u16 h_end; - u16 v_end; - u16 h_output_size; - u16 v_output_size; - u16 hts; - u16 vts; - } mode; - - struct ov2680_ctrls { - struct v4l2_ctrl_handler handler; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - struct v4l2_ctrl *test_pattern; - } ctrls; -}; - -/** - * struct ov2680_reg - MI sensor register format - * @type: type of the register - * @reg: 16-bit offset to register - * @val: 8/16/32-bit register value - * - * Define a structure for sensor register initialization values - */ -struct ov2680_reg { - u16 reg; - u32 val; /* @set value for read/mod/write, @mask */ -}; - -#define to_ov2680_sensor(x) container_of(x, struct ov2680_dev, sd) - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - struct ov2680_dev *sensor = - container_of(ctrl->handler, struct ov2680_dev, ctrls.handler); - - return &sensor->sd; -} - -static struct ov2680_reg const ov2680_global_setting[] = { - /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */ - {0x3016, 0x1c}, - - /* PLL MULT bits 0-7, datasheet default 0x37 for 24MHz extclk, use 0x45 for 19.2 Mhz extclk */ - {0x3082, 0x45}, - - /* R MANUAL set exposure (0x01) and gain (0x02) to manual (hw does not do auto) */ - {0x3503, 0x03}, - - /* Analog control register tweaks */ - {0x3603, 0x39}, /* Reset value 0x99 */ - {0x3604, 0x24}, /* Reset value 0x74 */ - {0x3621, 0x37}, /* Reset value 0x44 */ - - /* Sensor control register tweaks */ - {0x3701, 0x64}, /* Reset value 0x61 */ - {0x3705, 0x3c}, /* Reset value 0x21 */ - {0x370c, 0x50}, /* Reset value 0x10 */ - {0x370d, 0xc0}, /* Reset value 0x00 */ - {0x3718, 0x88}, /* Reset value 0x80 */ - - /* PSRAM tweaks */ - {0x3781, 0x80}, /* Reset value 0x00 */ - {0x3784, 0x0c}, /* Reset value 0x00, based on OV2680_R1A_AM10.ovt */ - {0x3789, 0x60}, /* Reset value 0x50 */ - - /* BLC CTRL00 0x01 -> 0x81 set avg_weight to 8 */ - {0x4000, 0x81}, - - /* Set black level compensation range to 0 - 3 (default 0 - 11) */ - {0x4008, 0x00}, - {0x4009, 0x03}, - - /* VFIFO R2 0x00 -> 0x02 set Frame reset enable */ - {0x4602, 0x02}, - - /* MIPI ctrl CLK PREPARE MIN change from 0x26 (38) -> 0x36 (54) */ - {0x481f, 0x36}, - - /* MIPI ctrl CLK LPX P MIN change from 0x32 (50) -> 0x36 (54) */ - {0x4825, 0x36}, - - /* R ISP CTRL2 0x20 -> 0x30, set sof_sel bit */ - {0x5002, 0x30}, - - /* - * Window CONTROL 0x00 -> 0x01, enable manual window control, - * this is necessary for full size flip and mirror support. - */ - {0x5708, 0x01}, - - /* - * DPC CTRL0 0x14 -> 0x3e, set enable_tail, enable_3x3_cluster - * and enable_general_tail bits based OV2680_R1A_AM10.ovt. - */ - {0x5780, 0x3e}, - - /* DPC MORE CONNECTION CASE THRE 0x0c (12) -> 0x02 (2) */ - {0x5788, 0x02}, - - /* DPC GAIN LIST1 0x0f (15) -> 0x08 (8) */ - {0x578e, 0x08}, - - /* DPC GAIN LIST2 0x3f (63) -> 0x0c (12) */ - {0x578f, 0x0c}, - - /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */ - {0x5792, 0x00}, - - {} -}; - -#endif diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index e27f9dc8e7aa..0803b296e9ac 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3001,12 +3001,6 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) bool need_to_enqueue_buffer = false; int i; - if (!asd) { - dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, pipe->vdev.name); - return; - } - lockdep_assert_held(&asd->isp->mutex); /* @@ -3068,12 +3062,6 @@ int atomisp_set_parameters(struct video_device *vdev, struct atomisp_css_params *css_param = &asd->params.css_param; int ret; - if (!asd) { - dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - lockdep_assert_held(&asd->isp->mutex); if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) { @@ -4067,12 +4055,6 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, const struct atomisp_in_fmt_conv *fc = NULL; int ret, i; - if (!asd) { - dev_err(isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - isp_sink_crop = atomisp_subdev_get_rect( &asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); @@ -4280,12 +4262,6 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p (struct atomisp_input_stream_info *)ffmt->reserved; int ret; - if (!asd) { - dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - format = atomisp_get_format_bridge(f->pixelformat); if (!format) return -EINVAL; diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index b13d1cb4668d..b97ec85aa0ba 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -613,9 +613,6 @@ static void __apply_additional_pipe_config( static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd, enum ia_css_pipe_id pipe_id) { - if (!asd) - return false; - if (pipe_id == IA_CSS_PIPE_ID_YUVPP) return true; diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h index 16ddb3ab2963..8a112acba1e0 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h @@ -30,9 +30,6 @@ #define CSI2_PAD_SOURCE 1 #define CSI2_PADS_NUM 2 -#define CSI2_MAX_LANES 4 -#define CSI2_MAX_LINK_FREQS 3 - #define CSI2_MAX_ACPI_GPIOS 2u struct acpi_device; @@ -55,70 +52,6 @@ struct atomisp_csi2_acpi_gpio_parsing_data { unsigned int map_count; }; -enum atomisp_csi2_sensor_swnodes { - SWNODE_SENSOR, - SWNODE_SENSOR_PORT, - SWNODE_SENSOR_ENDPOINT, - SWNODE_CSI2_PORT, - SWNODE_CSI2_ENDPOINT, - SWNODE_COUNT -}; - -struct atomisp_csi2_property_names { - char clock_frequency[16]; - char rotation[9]; - char bus_type[9]; - char data_lanes[11]; - char remote_endpoint[16]; - char link_frequencies[17]; -}; - -struct atomisp_csi2_node_names { - char port[7]; - char endpoint[11]; - char remote_port[7]; -}; - -struct atomisp_csi2_sensor_config { - const char *hid; - int lanes; - int nr_link_freqs; - u64 link_freqs[CSI2_MAX_LINK_FREQS]; -}; - -struct atomisp_csi2_sensor { - /* Append port in "-%u" format as suffix of HID */ - char name[ACPI_ID_LEN + 4]; - struct acpi_device *adev; - int port; - int lanes; - - /* SWNODE_COUNT + 1 for terminating NULL */ - const struct software_node *group[SWNODE_COUNT + 1]; - struct software_node swnodes[SWNODE_COUNT]; - struct atomisp_csi2_node_names node_names; - struct atomisp_csi2_property_names prop_names; - /* "clock-frequency", "rotation" + terminating entry */ - struct property_entry dev_properties[3]; - /* "bus-type", "data-lanes", "remote-endpoint" + "link-freq" + terminating entry */ - struct property_entry ep_properties[5]; - /* "data-lanes", "remote-endpoint" + terminating entry */ - struct property_entry csi2_properties[3]; - struct software_node_ref_args local_ref[1]; - struct software_node_ref_args remote_ref[1]; - struct software_node_ref_args vcm_ref[1]; - /* GPIO mappings storage */ - struct atomisp_csi2_acpi_gpio_map gpio_map; -}; - -struct atomisp_csi2_bridge { - struct software_node csi2_node; - char csi2_node_name[14]; - u32 data_lanes[CSI2_MAX_LANES]; - unsigned int n_sensors; - struct atomisp_csi2_sensor sensors[ATOMISP_CAMERA_NR_PORTS]; -}; - struct atomisp_mipi_csi2_device { struct v4l2_subdev subdev; struct media_pad pads[CSI2_PADS_NUM]; diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index 0d12ba78d9c1..03940c11505f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -14,31 +14,14 @@ #include #include #include + +#include #include #include "atomisp_cmd.h" #include "atomisp_csi2.h" #include "atomisp_internal.h" -#define NODE_SENSOR(_HID, _PROPS) \ - ((const struct software_node) { \ - .name = _HID, \ - .properties = _PROPS, \ - }) - -#define NODE_PORT(_PORT, _SENSOR_NODE) \ - ((const struct software_node) { \ - .name = _PORT, \ - .parent = _SENSOR_NODE, \ - }) - -#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \ - ((const struct software_node) { \ - .name = _EP, \ - .parent = _PORT, \ - .properties = _PROPS, \ - }) - #define PMC_CLK_RATE_19_2MHZ 19200000 /* @@ -84,20 +67,27 @@ static const guid_t atomisp_dsm_guid = 0x97, 0xb9, 0x88, 0x2a, 0x68, 0x60, 0xa4, 0xbe); /* - * Extend this array with ACPI Hardware IDs of sensors known to be working - * plus the default number of links + link-frequencies. - * - * Do not add an entry for a sensor that is not actually supported, - * or which have not yet been converted to work without atomisp_gmin - * power-management and with v4l2-async probing. + * 75c9a639-5c8a-4a00-9f48-a9c3b5da789f + * This _DSM GUID returns a string giving the VCM type e.g. "AD5823". */ -static const struct atomisp_csi2_sensor_config supported_sensors[] = { - /* GalaxyCore GC0310 */ - { "INT0310", 1 }, - /* Omnivision OV2680 */ - { "OVTI2680", 1 }, +static const guid_t vcm_dsm_guid = + GUID_INIT(0x75c9a639, 0x5c8a, 0x4a00, + 0x9f, 0x48, 0xa9, 0xc3, 0xb5, 0xda, 0x78, 0x9f); + +struct atomisp_sensor_config { + int lanes; + bool vcm; }; +#define ATOMISP_SENSOR_CONFIG(_HID, _LANES, _VCM) \ +{ \ + .id = _HID, \ + .driver_data = (long)&((const struct atomisp_sensor_config) { \ + .lanes = _LANES, \ + .vcm = _VCM, \ + }) \ +} + /* * gmin_cfg parsing code. This is a cleaned up version of the gmin_cfg parsing * code from atomisp_gmin_platform.c. @@ -151,7 +141,8 @@ static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key) if (!val) break; - acpi_handle_info(adev->handle, "Using DSM entry %s=%s\n", key, val); + acpi_handle_info(adev->handle, "%s: Using DSM entry %s=%s\n", + dev_name(&adev->dev), key, val); break; } } @@ -176,7 +167,8 @@ static char *gmin_cfg_get_dmi_override(struct acpi_device *adev, const char *key if (strcmp(key, gv->key)) continue; - acpi_handle_info(adev->handle, "Using DMI entry %s=%s\n", key, gv->val); + acpi_handle_info(adev->handle, "%s: Using DMI entry %s=%s\n", + dev_name(&adev->dev), key, gv->val); return kstrdup(gv->val, GFP_KERNEL); } @@ -212,7 +204,8 @@ static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int defau return int_val; out_use_default: - acpi_handle_info(adev->handle, "Using default %s=%d\n", key, default_val); + acpi_handle_info(adev->handle, "%s: Using default %s=%d\n", + dev_name(&adev->dev), key, default_val); return default_val; } @@ -255,7 +248,8 @@ static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev) ACPI_FREE(buffer.pointer); if (ret < 0) - acpi_handle_warn(adev->handle, "Could not find PMC clk in _PR0\n"); + acpi_handle_warn(adev->handle, "%s: Could not find PMC clk in _PR0\n", + dev_name(&adev->dev)); return ret; } @@ -274,7 +268,8 @@ static int atomisp_csi2_set_pmc_clk_freq(struct acpi_device *adev, int clock_num clk = clk_get(NULL, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - acpi_handle_err(adev->handle, "Error getting clk %s:%d\n", name, ret); + acpi_handle_err(adev->handle, "%s: Error getting clk %s: %d\n", + dev_name(&adev->dev), name, ret); return ret; } @@ -288,7 +283,8 @@ static int atomisp_csi2_set_pmc_clk_freq(struct acpi_device *adev, int clock_num if (!ret) ret = clk_set_rate(clk, PMC_CLK_RATE_19_2MHZ); if (ret) - acpi_handle_err(adev->handle, "Error setting clk-rate for %s:%d\n", name, ret); + acpi_handle_err(adev->handle, "%s: Error setting clk-rate for %s: %d\n", + dev_name(&adev->dev), name, ret); clk_put(clk); return ret; @@ -337,7 +333,8 @@ static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_ if (i == data->settings_count) { acpi_handle_warn(data->adev->handle, - "Could not find DSM GPIO settings for pin %u\n", pin); + "%s: Could not find DSM GPIO settings for pin %u\n", + dev_name(&data->adev->dev), pin); return 1; } @@ -349,7 +346,8 @@ static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_ name = "powerdown-gpios"; break; default: - acpi_handle_warn(data->adev->handle, "Unknown GPIO type 0x%02lx for pin %u\n", + acpi_handle_warn(data->adev->handle, "%s: Unknown GPIO type 0x%02lx for pin %u\n", + dev_name(&data->adev->dev), INTEL_GPIO_DSM_TYPE(settings), pin); return 1; } @@ -374,7 +372,8 @@ static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_ data->map->mapping[i].size = 1; data->map_count++; - acpi_handle_info(data->adev->handle, "%s crs %d %s pin %u active-%s\n", name, + acpi_handle_info(data->adev->handle, "%s: %s crs %d %s pin %u active-%s\n", + dev_name(&data->adev->dev), name, data->res_count - 1, agpio->resource_source.string_ptr, pin, active_low ? "low" : "high"); @@ -400,8 +399,7 @@ static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_ * the INT3472 discrete.c code and there is some overlap, but there are * enough differences that it is difficult to share the code. */ -static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, - struct acpi_device *adev) +static int atomisp_csi2_add_gpio_mappings(struct acpi_device *adev) { struct atomisp_csi2_acpi_gpio_parsing_data data = { }; LIST_HEAD(resource_list); @@ -412,7 +410,8 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid, 0x00, 1, NULL, ACPI_TYPE_STRING); if (obj) { - acpi_handle_info(adev->handle, "Sensor module id: '%s'\n", obj->string.pointer); + acpi_handle_info(adev->handle, "%s: Sensor module id: '%s'\n", + dev_name(&adev->dev), obj->string.pointer); ACPI_FREE(obj); } @@ -426,7 +425,8 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, &intel_sensor_gpio_info_guid, 0x00, 1, NULL, ACPI_TYPE_INTEGER); if (!obj) { - acpi_handle_err(adev->handle, "No _DSM entry for GPIO pin count\n"); + acpi_handle_err(adev->handle, "%s: No _DSM entry for GPIO pin count\n", + dev_name(&adev->dev)); return -EIO; } @@ -434,7 +434,9 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, ACPI_FREE(obj); if (data.settings_count > CSI2_MAX_ACPI_GPIOS) { - acpi_handle_err(adev->handle, "Too many GPIOs %u > %u\n", data.settings_count, CSI2_MAX_ACPI_GPIOS); + acpi_handle_err(adev->handle, "%s: Too many GPIOs %u > %u\n", + dev_name(&adev->dev), data.settings_count, + CSI2_MAX_ACPI_GPIOS); return -EOVERFLOW; } @@ -448,7 +450,8 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, 0x00, i + 2, NULL, ACPI_TYPE_INTEGER); if (!obj) { - acpi_handle_err(adev->handle, "No _DSM entry for pin %u\n", i); + acpi_handle_err(adev->handle, "%s: No _DSM entry for pin %u\n", + dev_name(&adev->dev), i); return -EIO; } @@ -463,15 +466,19 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, INTEL_GPIO_DSM_PIN(data.settings[j])) continue; - acpi_handle_err(adev->handle, "Duplicate pin number %lu\n", + acpi_handle_err(adev->handle, "%s: Duplicate pin number %lu\n", + dev_name(&adev->dev), INTEL_GPIO_DSM_PIN(data.settings[i])); return -EIO; } } + data.map = kzalloc(sizeof(*data.map), GFP_KERNEL); + if (!data.map) + return -ENOMEM; + /* Now parse the ACPI resources and build the lookup table */ data.adev = adev; - data.map = &sensor->gpio_map; ret = acpi_dev_get_resources(adev, &resource_list, atomisp_csi2_handle_acpi_gpio_res, &data); if (ret < 0) @@ -481,230 +488,105 @@ static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, if (data.map_count != data.settings_count || data.res_count != data.settings_count) - acpi_handle_warn(adev->handle, "ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n", - data.settings_count, data.res_count, data.map_count); + acpi_handle_warn(adev->handle, "%s: ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n", + dev_name(&adev->dev), data.settings_count, + data.res_count, data.map_count); ret = acpi_dev_add_driver_gpios(adev, data.map->mapping); if (ret) - acpi_handle_err(adev->handle, "Error adding driver GPIOs: %d\n", ret); + acpi_handle_err(adev->handle, "%s: Error adding driver GPIOs: %d\n", + dev_name(&adev->dev), ret); return ret; } -static const struct atomisp_csi2_property_names prop_names = { - .clock_frequency = "clock-frequency", - .rotation = "rotation", - .bus_type = "bus-type", - .data_lanes = "data-lanes", - .remote_endpoint = "remote-endpoint", - .link_frequencies = "link-frequencies", +static char *atomisp_csi2_get_vcm_type(struct acpi_device *adev) +{ + union acpi_object *obj; + char *vcm_type; + + obj = acpi_evaluate_dsm_typed(adev->handle, &vcm_dsm_guid, 0, 0, + NULL, ACPI_TYPE_STRING); + if (!obj) + return NULL; + + vcm_type = kstrdup(obj->string.pointer, GFP_KERNEL); + ACPI_FREE(obj); + + if (!vcm_type) + return NULL; + + string_lower(vcm_type, vcm_type); + return vcm_type; +} + +static const struct acpi_device_id atomisp_sensor_configs[] = { + ATOMISP_SENSOR_CONFIG("INT33BE", 2, true), /* OV5693 */ + {} }; -static void atomisp_csi2_create_fwnode_properties(struct atomisp_csi2_sensor *sensor, - struct atomisp_csi2_bridge *bridge, - const struct atomisp_csi2_sensor_config *cfg) +static int atomisp_csi2_parse_sensor_fwnode(struct acpi_device *adev, + struct ipu_sensor *sensor) { - sensor->prop_names = prop_names; - - sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CSI2_ENDPOINT]); - sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]); - - sensor->dev_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.clock_frequency, - PMC_CLK_RATE_19_2MHZ); - sensor->dev_properties[1] = PROPERTY_ENTRY_U32(sensor->prop_names.rotation, 0); - - sensor->ep_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.bus_type, - V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); - sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes, - bridge->data_lanes, - sensor->lanes); - sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint, - sensor->local_ref); - if (cfg->nr_link_freqs > 0) - sensor->ep_properties[3] = - PROPERTY_ENTRY_U64_ARRAY_LEN(sensor->prop_names.link_frequencies, - cfg->link_freqs, cfg->nr_link_freqs); - - sensor->csi2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes, - bridge->data_lanes, - sensor->lanes); - sensor->csi2_properties[1] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint, - sensor->remote_ref); -} - -static void atomisp_csi2_init_swnode_names(struct atomisp_csi2_sensor *sensor) -{ - snprintf(sensor->node_names.remote_port, - sizeof(sensor->node_names.remote_port), - SWNODE_GRAPH_PORT_NAME_FMT, sensor->port); - snprintf(sensor->node_names.port, - sizeof(sensor->node_names.port), - SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ - snprintf(sensor->node_names.endpoint, - sizeof(sensor->node_names.endpoint), - SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ -} - -static void atomisp_csi2_init_swnode_group(struct atomisp_csi2_sensor *sensor) -{ - struct software_node *nodes = sensor->swnodes; - - sensor->group[SWNODE_SENSOR] = &nodes[SWNODE_SENSOR]; - sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; - sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; - sensor->group[SWNODE_CSI2_PORT] = &nodes[SWNODE_CSI2_PORT]; - sensor->group[SWNODE_CSI2_ENDPOINT] = &nodes[SWNODE_CSI2_ENDPOINT]; -} - -static void atomisp_csi2_create_connection_swnodes(struct atomisp_csi2_bridge *bridge, - struct atomisp_csi2_sensor *sensor) -{ - struct software_node *nodes = sensor->swnodes; - - atomisp_csi2_init_swnode_names(sensor); - - nodes[SWNODE_SENSOR] = NODE_SENSOR(sensor->name, - sensor->dev_properties); - nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, - &nodes[SWNODE_SENSOR]); - nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint, - &nodes[SWNODE_SENSOR_PORT], - sensor->ep_properties); - nodes[SWNODE_CSI2_PORT] = NODE_PORT(sensor->node_names.remote_port, - &bridge->csi2_node); - nodes[SWNODE_CSI2_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint, - &nodes[SWNODE_CSI2_PORT], - sensor->csi2_properties); - - atomisp_csi2_init_swnode_group(sensor); -} - -static void atomisp_csi2_unregister_sensors(struct atomisp_csi2_bridge *bridge) -{ - struct atomisp_csi2_sensor *sensor; - unsigned int i; - - for (i = 0; i < bridge->n_sensors; i++) { - sensor = &bridge->sensors[i]; - software_node_unregister_node_group(sensor->group); - acpi_dev_remove_driver_gpios(sensor->adev); - acpi_dev_put(sensor->adev); - } -} - -static int atomisp_csi2_connect_sensor(const struct atomisp_csi2_sensor_config *cfg, - struct atomisp_csi2_bridge *bridge, - struct atomisp_device *isp) -{ - struct fwnode_handle *fwnode, *primary; - struct atomisp_csi2_sensor *sensor; - struct acpi_device *adev; + const struct acpi_device_id *id; int ret, clock_num; + bool vcm = false; + int lanes = 1; - for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { - if (!adev->status.enabled) - continue; + id = acpi_match_acpi_device(atomisp_sensor_configs, adev); + if (id) { + struct atomisp_sensor_config *cfg = + (struct atomisp_sensor_config *)id->driver_data; - if (bridge->n_sensors >= ATOMISP_CAMERA_NR_PORTS) { - dev_err(isp->dev, "Exceeded available CSI2 ports\n"); - ret = -EOVERFLOW; - goto err_put_adev; - } - - sensor = &bridge->sensors[bridge->n_sensors]; - - /* - * ACPI takes care of turning the PMC clock on and off, but on BYT - * the clock defaults to 25 MHz instead of the expected 19.2 MHz. - * Get the PMC-clock number from ACPI _PR0 method and set it to 19.2 MHz. - * The PMC-clock number is also used to determine the default CSI port. - */ - clock_num = atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(adev); - - ret = atomisp_csi2_set_pmc_clk_freq(adev, clock_num); - if (ret) - goto err_put_adev; - - sensor->port = atomisp_csi2_get_port(adev, clock_num); - if (sensor->port >= ATOMISP_CAMERA_NR_PORTS) { - acpi_handle_err(adev->handle, "Invalid port: %d\n", sensor->port); - ret = -EINVAL; - goto err_put_adev; - } - - sensor->lanes = gmin_cfg_get_int(adev, "CsiLanes", cfg->lanes); - if (sensor->lanes > CSI2_MAX_LANES) { - acpi_handle_err(adev->handle, "Invalid number of lanes: %d\n", sensor->lanes); - ret = -EINVAL; - goto err_put_adev; - } - - ret = atomisp_csi2_add_gpio_mappings(sensor, adev); - if (ret) - goto err_put_adev; - - snprintf(sensor->name, sizeof(sensor->name), "%s-%u", - cfg->hid, sensor->port); - - atomisp_csi2_create_fwnode_properties(sensor, bridge, cfg); - atomisp_csi2_create_connection_swnodes(bridge, sensor); - - ret = software_node_register_node_group(sensor->group); - if (ret) - goto err_remove_mappings; - - fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR]); - if (!fwnode) { - ret = -ENODEV; - goto err_free_swnodes; - } - - sensor->adev = acpi_dev_get(adev); - - primary = acpi_fwnode_handle(adev); - primary->secondary = fwnode; - - bridge->n_sensors++; + lanes = cfg->lanes; + vcm = cfg->vcm; } - return 0; + /* + * ACPI takes care of turning the PMC clock on and off, but on BYT + * the clock defaults to 25 MHz instead of the expected 19.2 MHz. + * Get the PMC-clock number from ACPI PR0 method and set it to 19.2 MHz. + * The PMC-clock number is also used to determine the default CSI port. + */ + clock_num = atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(adev); -err_free_swnodes: - software_node_unregister_node_group(sensor->group); -err_remove_mappings: - acpi_dev_remove_driver_gpios(adev); -err_put_adev: - acpi_dev_put(adev); - return ret; -} + ret = atomisp_csi2_set_pmc_clk_freq(adev, clock_num); + if (ret) + return ret; -static int atomisp_csi2_connect_sensors(struct atomisp_csi2_bridge *bridge, - struct atomisp_device *isp) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(supported_sensors); i++) { - const struct atomisp_csi2_sensor_config *cfg = &supported_sensors[i]; - - ret = atomisp_csi2_connect_sensor(cfg, bridge, isp); - if (ret) - goto err_unregister_sensors; + sensor->link = atomisp_csi2_get_port(adev, clock_num); + if (sensor->link >= ATOMISP_CAMERA_NR_PORTS) { + acpi_handle_err(adev->handle, "%s: Invalid port: %u\n", + dev_name(&adev->dev), sensor->link); + return -EINVAL; } - return 0; + sensor->lanes = gmin_cfg_get_int(adev, "CsiLanes", lanes); + if (sensor->lanes > IPU_MAX_LANES) { + acpi_handle_err(adev->handle, "%s: Invalid lane-count: %d\n", + dev_name(&adev->dev), sensor->lanes); + return -EINVAL; + } -err_unregister_sensors: - atomisp_csi2_unregister_sensors(bridge); - return ret; + ret = atomisp_csi2_add_gpio_mappings(adev); + if (ret) + return ret; + + sensor->mclkspeed = PMC_CLK_RATE_19_2MHZ; + sensor->rotation = 0; + sensor->orientation = (sensor->link == 1) ? + V4L2_FWNODE_ORIENTATION_BACK : V4L2_FWNODE_ORIENTATION_FRONT; + + if (vcm) + sensor->vcm_type = atomisp_csi2_get_vcm_type(adev); + + return 0; } int atomisp_csi2_bridge_init(struct atomisp_device *isp) { - struct atomisp_csi2_bridge *bridge; struct device *dev = isp->dev; struct fwnode_handle *fwnode; - int i, ret; /* * This function is intended to run only once and then leave @@ -716,58 +598,13 @@ int atomisp_csi2_bridge_init(struct atomisp_device *isp) if (fwnode && fwnode->secondary) return 0; - bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return -ENOMEM; - - strscpy(bridge->csi2_node_name, "atomisp-csi2", sizeof(bridge->csi2_node_name)); - bridge->csi2_node.name = bridge->csi2_node_name; - - ret = software_node_register(&bridge->csi2_node); - if (ret < 0) { - dev_err(dev, "Failed to register the CSI2 HID node\n"); - goto err_free_bridge; - } - - /* - * Map the lane arrangement, which is fixed for the ISP2 (meaning we - * only need one, rather than one per sensor). We include it as a - * member of the bridge struct rather than a global variable so - * that it survives if the module is unloaded along with the rest of - * the struct. - */ - for (i = 0; i < CSI2_MAX_LANES; i++) - bridge->data_lanes[i] = i + 1; - - ret = atomisp_csi2_connect_sensors(bridge, isp); - if (ret || bridge->n_sensors == 0) - goto err_unregister_csi2; - - fwnode = software_node_fwnode(&bridge->csi2_node); - if (!fwnode) { - dev_err(dev, "Error getting fwnode from csi2 software_node\n"); - ret = -ENODEV; - goto err_unregister_sensors; - } - - set_secondary_fwnode(dev, fwnode); - - return 0; - -err_unregister_sensors: - atomisp_csi2_unregister_sensors(bridge); -err_unregister_csi2: - software_node_unregister(&bridge->csi2_node); -err_free_bridge: - kfree(bridge); - - return ret; + return ipu_bridge_init(dev, atomisp_csi2_parse_sensor_fwnode); } /******* V4L2 sub-device asynchronous registration callbacks***********/ struct sensor_async_subdev { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; int port; }; @@ -777,10 +614,11 @@ struct sensor_async_subdev { /* .bound() notifier callback when a match is found */ static int atomisp_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct atomisp_device *isp = notifier_to_atomisp(notifier); struct sensor_async_subdev *s_asd = to_sensor_asd(asd); + int ret; if (s_asd->port >= ATOMISP_CAMERA_NR_PORTS) { dev_err(isp->dev, "port %d not supported\n", s_asd->port); @@ -792,6 +630,10 @@ static int atomisp_notifier_bound(struct v4l2_async_notifier *notifier, return -EBUSY; } + ret = ipu_bridge_instantiate_vcm(sd->dev); + if (ret) + return ret; + isp->sensor_subdevs[s_asd->port] = sd; return 0; } @@ -799,7 +641,7 @@ static int atomisp_notifier_bound(struct v4l2_async_notifier *notifier, /* The .unbind callback */ static void atomisp_notifier_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct atomisp_device *isp = notifier_to_atomisp(notifier); struct sensor_async_subdev *s_asd = to_sensor_asd(asd); @@ -825,7 +667,7 @@ int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp) { int i, mipi_port, ret; - v4l2_async_nf_init(&isp->notifier); + v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev); isp->notifier.ops = &atomisp_async_ops; for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index c43b916a006e..0d0329f5e4ad 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -1508,7 +1508,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i isp->firmware = NULL; isp->css_env.isp_css_fw.data = NULL; - err = v4l2_async_nf_register(&isp->v4l2_dev, &isp->notifier); + err = v4l2_async_nf_register(&isp->notifier); if (err) { dev_err(isp->dev, "failed to register async notifier : %d\n", err); goto css_init_fail; @@ -1615,3 +1615,4 @@ MODULE_AUTHOR("Wen Wang "); MODULE_AUTHOR("Xiaolin Zhang "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel ATOM Platform ISP Driver"); +MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/sh_css_mipi.c index b20acaab0595..ced21dedf7ac 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_mipi.c +++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.c @@ -351,15 +351,6 @@ allocate_mipi_frames(struct ia_css_pipe *pipe, ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "allocate_mipi_frames(%p) enter:\n", pipe); - assert(pipe); - assert(pipe->stream); - if ((!pipe) || (!pipe->stream)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "allocate_mipi_frames(%p) exit: pipe or stream is null.\n", - pipe); - return -EINVAL; - } - if (IS_ISP2401 && pipe->stream->config.online) { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "allocate_mipi_frames(%p) exit: no buffers needed for 2401 pipe mode.\n", @@ -557,13 +548,6 @@ send_mipi_frames(struct ia_css_pipe *pipe) IA_CSS_ENTER_PRIVATE("pipe=%p", pipe); - assert(pipe); - assert(pipe->stream); - if (!pipe || !pipe->stream) { - IA_CSS_ERROR("pipe or stream is null"); - return -EINVAL; - } - /* multi stream video needs mipi buffers */ /* nothing to be done in other cases. */ if (pipe->stream->config.mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index ff0082d02af3..5174bc210ae1 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -202,9 +202,6 @@ generate_dvs_6axis_table_from_config(struct ia_css_dvs_6axis_config void free_dvs_6axis_table(struct ia_css_dvs_6axis_config **dvs_6axis_config) { - assert(dvs_6axis_config); - assert(*dvs_6axis_config); - if ((dvs_6axis_config) && (*dvs_6axis_config)) { IA_CSS_ENTER_PRIVATE("dvs_6axis_config %p", (*dvs_6axis_config)); if ((*dvs_6axis_config)->xcoords_y) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 297e1b981720..f35c745c22c0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -51,6 +51,7 @@ #include "ia_css_event.h" #include "mmu_device.h" #include "ia_css_spctrl.h" +#include "atomisp_internal.h" #ifndef offsetof #define offsetof(T, x) ((unsigned int)&(((T *)0)->x)) @@ -1212,14 +1213,15 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me, struct ia_css_binary *first_binary = NULL; struct ia_css_pipe *pipe = NULL; unsigned int num; - enum ia_css_pipe_id pipe_id = id; unsigned int thread_id; u8 if_config_index, tmp_if_config_index; - assert(me); - - assert(me->stages); + if (!me->stages) { + dev_err(atomisp_dev, "%s called on a pipeline without stages\n", + __func__); + return; /* FIXME should be able to return an error */ + } first_binary = me->stages->binary; @@ -1252,8 +1254,8 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me, } /* if (first_binary != NULL) */ /* Signal the host immediately after start for SP_ISYS_COPY only */ - if ((me->num_stages == 1) && me->stages && - (me->stages->sp_func == IA_CSS_PIPELINE_ISYS_COPY)) + if (me->num_stages == 1 && + me->stages->sp_func == IA_CSS_PIPELINE_ISYS_COPY) sh_css_sp_group.config.no_isp_sync = true; /* Init stage data */ diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c index 61c5afa58142..f5d963904201 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c @@ -1727,7 +1727,7 @@ static int isc_ctrl_init(struct isc_device *isc) static int isc_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct isc_device *isc = container_of(notifier->v4l2_dev, struct isc_device, v4l2_dev); @@ -1746,7 +1746,7 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier, static void isc_async_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct isc_device *isc = container_of(notifier->v4l2_dev, struct isc_device, v4l2_dev); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h index dfc030b5a08f..31767ea74be6 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-isc.h +++ b/drivers/staging/media/deprecated/atmel/atmel-isc.h @@ -44,7 +44,7 @@ struct isc_buffer { struct isc_subdev_entity { struct v4l2_subdev *sd; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *epn; struct v4l2_async_notifier notifier; diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c index cc86ebcc76af..31b2b48085c5 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c @@ -503,15 +503,15 @@ static int atmel_isc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode = of_fwnode_handle(subdev_entity->epn); - v4l2_async_nf_init(&subdev_entity->notifier); + v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(subdev_entity->epn); subdev_entity->epn = NULL; @@ -523,8 +523,7 @@ static int atmel_isc_probe(struct platform_device *pdev) subdev_entity->notifier.ops = &atmel_isc_async_ops; - ret = v4l2_async_nf_register(&isc->v4l2_dev, - &subdev_entity->notifier); + ret = v4l2_async_nf_register(&subdev_entity->notifier); if (ret) { dev_err(dev, "fail to register async notifier\n"); goto cleanup_subdev; diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c index 68ef3374d25e..020034f631f5 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c @@ -493,15 +493,15 @@ static int microchip_xisc_probe(struct platform_device *pdev) } list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *fwnode = of_fwnode_handle(subdev_entity->epn); - v4l2_async_nf_init(&subdev_entity->notifier); + v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, fwnode, - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(subdev_entity->epn); subdev_entity->epn = NULL; @@ -513,8 +513,7 @@ static int microchip_xisc_probe(struct platform_device *pdev) subdev_entity->notifier.ops = &atmel_isc_async_ops; - ret = v4l2_async_nf_register(&isc->v4l2_dev, - &subdev_entity->notifier); + ret = v4l2_async_nf_register(&subdev_entity->notifier); if (ret) { dev_err(dev, "fail to register async notifier\n"); goto cleanup_subdev; diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig index 21fd79515042..426310e1ea5b 100644 --- a/drivers/staging/media/imx/Kconfig +++ b/drivers/staging/media/imx/Kconfig @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_IMX_MEDIA - tristate "i.MX5/6 V4L2 media core driver" + tristate "i.MX5/6 V4L2 media drivers" depends on ARCH_MXC || COMPILE_TEST depends on HAS_DMA depends on VIDEO_DEV depends on VIDEO_DEV + depends on IMX_IPUV3_CORE select MEDIA_CONTROLLER select V4L2_FWNODE select V4L2_MEM2MEM_DEV @@ -12,26 +13,4 @@ config VIDEO_IMX_MEDIA select VIDEO_V4L2_SUBDEV_API help Say yes here to enable support for video4linux media controller - driver for the i.MX5/6 SOC. - -if VIDEO_IMX_MEDIA -menu "i.MX5/6/7/8 Media Sub devices" - -config VIDEO_IMX_CSI - tristate "i.MX5/6 Camera Sensor Interface driver" - depends on IMX_IPUV3_CORE - default y - help - A video4linux camera sensor interface driver for i.MX5/6. -endmenu -endif - -config VIDEO_IMX8MQ_MIPI_CSI2 - tristate "NXP i.MX8MQ MIPI CSI-2 receiver" - depends on ARCH_MXC || COMPILE_TEST - depends on VIDEO_DEV - select MEDIA_CONTROLLER - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - help - V4L2 driver for the MIPI CSI-2 receiver found in the i.MX8MQ SoC. + drivers for the i.MX5/6 SOC. diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 906a422aa656..330e0825f506 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -9,9 +9,6 @@ imx6-media-objs := imx-media-dev.o imx-media-internal-sd.o \ imx6-media-csi-objs := imx-media-csi.o imx-media-fim.o obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o - -obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media.o -obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o -obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o - -obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-media.o +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-media-csi.o +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-mipi-csi2.o diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 097171bb930d..dda1ebc34692 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1892,7 +1892,7 @@ static const struct v4l2_subdev_internal_ops csi_internal_ops = { static int imx_csi_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct csi_priv *priv = notifier_to_dev(notifier); struct media_pad *sink = &priv->sd.entity.pads[CSI_SINK_PAD]; @@ -1913,12 +1913,12 @@ static const struct v4l2_async_notifier_operations csi_notify_ops = { static int imx_csi_async_register(struct csi_priv *priv) { - struct v4l2_async_subdev *asd = NULL; + struct v4l2_async_connection *asd = NULL; struct fwnode_handle *ep; unsigned int port; int ret; - v4l2_async_nf_init(&priv->notifier); + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); /* get this CSI's port id */ ret = fwnode_property_read_u32(dev_fwnode(priv->dev), "reg", &port); @@ -1930,7 +1930,7 @@ static int imx_csi_async_register(struct csi_priv *priv) FWNODE_GRAPH_ENDPOINT_NEXT); if (ep) { asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); @@ -1944,7 +1944,7 @@ static int imx_csi_async_register(struct csi_priv *priv) priv->notifier.ops = &csi_notify_ops; - ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); + ret = v4l2_async_nf_register(&priv->notifier); if (ret) return ret; diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c index 7b7cbec08326..0d0ee8627a2d 100644 --- a/drivers/staging/media/imx/imx-media-dev-common.c +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -365,7 +365,7 @@ struct imx_media_dev *imx_media_dev_init(struct device *dev, INIT_LIST_HEAD(&imxmd->vdev_list); - v4l2_async_nf_init(&imxmd->notifier); + v4l2_async_nf_init(&imxmd->notifier, &imxmd->v4l2_dev); return imxmd; @@ -382,14 +382,14 @@ int imx_media_dev_notifier_register(struct imx_media_dev *imxmd, int ret; /* no subdevs? just bail */ - if (list_empty(&imxmd->notifier.asd_list)) { + if (list_empty(&imxmd->notifier.waiting_list)) { v4l2_err(&imxmd->v4l2_dev, "no subdevs\n"); return -ENODEV; } /* prepare the async subdev notifier and register it */ imxmd->notifier.ops = ops ? ops : &imx_media_notifier_ops; - ret = v4l2_async_nf_register(&imxmd->v4l2_dev, &imxmd->notifier); + ret = v4l2_async_nf_register(&imxmd->notifier); if (ret) { v4l2_err(&imxmd->v4l2_dev, "v4l2_async_nf_register failed with %d\n", ret); diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index c80113905069..be54dca11465 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -20,7 +20,7 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) /* async subdev bound notifier */ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 92a99010c150..118bff988bc7 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -19,7 +19,7 @@ static int imx_media_of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) { - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; int ret = 0; if (!of_device_is_available(csi_np)) { @@ -31,7 +31,7 @@ static int imx_media_of_add_csi(struct imx_media_dev *imxmd, /* add CSI fwnode to async notifier */ asd = v4l2_async_nf_add_fwnode(&imxmd->notifier, of_fwnode_handle(csi_np), - struct v4l2_async_subdev); + struct v4l2_async_connection); if (IS_ERR(asd)) { ret = PTR_ERR(asd); if (ret == -EEXIST) diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index ab565b4e29ec..b2d8476d83a0 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -636,7 +636,7 @@ static const struct v4l2_subdev_internal_ops csi2_internal_ops = { static int csi2_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct csi2_dev *csi2 = notifier_to_dev(notifier); struct media_pad *sink = &csi2->sd.entity.pads[CSI2_SINK_PAD]; @@ -659,7 +659,7 @@ static int csi2_notify_bound(struct v4l2_async_notifier *notifier, static void csi2_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct csi2_dev *csi2 = notifier_to_dev(notifier); @@ -676,11 +676,11 @@ static int csi2_async_register(struct csi2_dev *csi2) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int ret; - v4l2_async_nf_init(&csi2->notifier); + v4l2_async_subdev_nf_init(&csi2->notifier, &csi2->sd); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi2->dev), 0, 0, FWNODE_GRAPH_ENDPOINT_NEXT); @@ -697,7 +697,7 @@ static int csi2_async_register(struct csi2_dev *csi2) dev_dbg(csi2->dev, "flags: 0x%08x\n", vep.bus.mipi_csi2.flags); asd = v4l2_async_nf_add_fwnode_remote(&csi2->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); if (IS_ERR(asd)) @@ -705,7 +705,7 @@ static int csi2_async_register(struct csi2_dev *csi2) csi2->notifier.ops = &csi2_notify_ops; - ret = v4l2_async_subdev_nf_register(&csi2->sd, &csi2->notifier); + ret = v4l2_async_nf_register(&csi2->notifier); if (ret) return ret; diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index 8c70497d744c..9c10f1474c35 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -1193,14 +1193,14 @@ static int imgu_css_binary_preallocate(struct imgu_css *css, unsigned int pipe) for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) if (!imgu_dmamap_alloc(imgu, - &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF]. - mem[i], CSS_BDS_SIZE)) + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_REF].mem[i], + CSS_BDS_SIZE)) goto out_of_memory; for (i = 0; i < IPU3_CSS_AUX_FRAMES; i++) if (!imgu_dmamap_alloc(imgu, - &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR]. - mem[i], CSS_GDC_SIZE)) + &css_pipe->aux_frames[IPU3_CSS_AUX_FRAME_TNR].mem[i], + CSS_GDC_SIZE)) goto out_of_memory; return 0; @@ -1428,13 +1428,11 @@ static int imgu_css_map_init(struct imgu_css *css, unsigned int pipe) for (p = 0; p < IPU3_CSS_PIPE_ID_NUM; p++) for (i = 0; i < IMGU_ABI_MAX_STAGES; i++) { if (!imgu_dmamap_alloc(imgu, - &css_pipe-> - xmem_sp_stage_ptrs[p][i], + &css_pipe->xmem_sp_stage_ptrs[p][i], sizeof(struct imgu_abi_sp_stage))) return -ENOMEM; if (!imgu_dmamap_alloc(imgu, - &css_pipe-> - xmem_isp_stage_ptrs[p][i], + &css_pipe->xmem_isp_stage_ptrs[p][i], sizeof(struct imgu_abi_isp_stage))) return -ENOMEM; } diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c index 88c9d72e1c83..70c9fd7c8bc5 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.c +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -280,3 +280,12 @@ const struct vdec_platform vdec_platform_sm1 = { .num_formats = ARRAY_SIZE(vdec_formats_sm1), .revision = VDEC_REVISION_SM1, }; + +MODULE_FIRMWARE("meson/vdec/g12a_h264.bin"); +MODULE_FIRMWARE("meson/vdec/g12a_vp9.bin"); +MODULE_FIRMWARE("meson/vdec/gxbb_h264.bin"); +MODULE_FIRMWARE("meson/vdec/gxl_h264.bin"); +MODULE_FIRMWARE("meson/vdec/gxl_mpeg12.bin"); +MODULE_FIRMWARE("meson/vdec/gxl_vp9.bin"); +MODULE_FIRMWARE("meson/vdec/gxm_h264.bin"); +MODULE_FIRMWARE("meson/vdec/sm1_vp9_mmu.bin"); diff --git a/drivers/staging/media/rkvdec/rkvdec-vp9.c b/drivers/staging/media/rkvdec/rkvdec-vp9.c index cfae99b40ccb..0e7e16f20eeb 100644 --- a/drivers/staging/media/rkvdec/rkvdec-vp9.c +++ b/drivers/staging/media/rkvdec/rkvdec-vp9.c @@ -227,7 +227,6 @@ static void init_intra_only_probs(struct rkvdec_ctx *ctx, } } } - } for (i = 0; i < sizeof(v4l2_vp9_kf_uv_mode_prob); ++i) { diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 134e2b9fa7d9..84a41792cb4b 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -120,7 +120,7 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .max_width = 4096, .step_width = 16, .min_height = 48, - .max_height = 2304, + .max_height = 2560, .step_height = 16, }, .ctrls = &rkvdec_h264_ctrls, diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c index 8337dc487047..5c0a45394cba 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c @@ -540,7 +540,7 @@ static struct platform_driver sun6i_isp_platform_driver = { .remove_new = sun6i_isp_remove, .driver = { .name = SUN6I_ISP_NAME, - .of_match_table = of_match_ptr(sun6i_isp_of_match), + .of_match_table = sun6i_isp_of_match, .pm = &sun6i_isp_pm_ops, }, }; diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c index 1ca4673df2b3..ccbb530aa2e2 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -395,7 +395,7 @@ static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev, static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *remote_subdev, - struct v4l2_async_subdev *async_subdev) + struct v4l2_async_connection *async_subdev) { struct sun6i_isp_device *isp_dev = container_of(notifier, struct sun6i_isp_device, proc.notifier); @@ -536,7 +536,7 @@ int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) /* V4L2 Async */ - v4l2_async_nf_init(notifier); + v4l2_async_nf_init(notifier, v4l2_dev); notifier->ops = &sun6i_isp_proc_notifier_ops; sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0, @@ -544,7 +544,7 @@ int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1, SUN6I_ISP_PORT_CSI1); - ret = v4l2_async_nf_register(v4l2_dev, notifier); + ret = v4l2_async_nf_register(notifier); if (ret) { v4l2_err(v4l2_dev, "failed to register v4l2 async notifier: %d\n", ret); diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h index c5c274e21ad5..db6738a39147 100644 --- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h @@ -34,7 +34,7 @@ struct sun6i_isp_proc_source { }; struct sun6i_isp_proc_async_subdev { - struct v4l2_async_subdev async_subdev; + struct v4l2_async_connection async_subdev; struct sun6i_isp_proc_source *source; }; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 4add037537a2..e98b3010520e 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -40,7 +40,7 @@ * @subdev: V4L2 subdev */ struct tegra_vi_graph_entity { - struct v4l2_async_subdev asd; + struct v4l2_async_connection asd; struct media_entity *entity; struct v4l2_subdev *subdev; }; @@ -58,7 +58,7 @@ to_tegra_channel_buffer(struct vb2_v4l2_buffer *vb) } static inline struct tegra_vi_graph_entity * -to_tegra_vi_graph_entity(struct v4l2_async_subdev *asd) +to_tegra_vi_graph_entity(struct v4l2_async_connection *asd) { return container_of(asd, struct tegra_vi_graph_entity, asd); } @@ -1181,7 +1181,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) } if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) - v4l2_async_nf_init(&chan->notifier); + v4l2_async_nf_init(&chan->notifier, &vid->v4l2_dev); return 0; @@ -1462,9 +1462,9 @@ tegra_vi_graph_find_entity(struct tegra_vi_channel *chan, const struct fwnode_handle *fwnode) { struct tegra_vi_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; - list_for_each_entry(asd, &chan->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &chan->notifier.done_list, asc_entry) { entity = to_tegra_vi_graph_entity(asd); if (entity->asd.match.fwnode == fwnode) return entity; @@ -1578,7 +1578,7 @@ create_link: static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) { struct tegra_vi_graph_entity *entity; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; struct tegra_vi_channel *chan; struct tegra_vi *vi; @@ -1608,7 +1608,7 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) } /* create links between the entities */ - list_for_each_entry(asd, &chan->notifier.asd_list, asd_list) { + list_for_each_entry(asd, &chan->notifier.done_list, asc_entry) { entity = to_tegra_vi_graph_entity(asd); ret = tegra_vi_graph_build(chan, entity); if (ret < 0) @@ -1651,7 +1651,7 @@ unregister_video: static int tegra_vi_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct tegra_vi_graph_entity *entity; struct tegra_vi *vi; @@ -1748,7 +1748,6 @@ cleanup: static int tegra_vi_graph_init(struct tegra_vi *vi) { - struct tegra_video_device *vid = dev_get_drvdata(vi->client.host); struct tegra_vi_channel *chan; struct fwnode_handle *fwnode = dev_fwnode(vi->dev); int ret; @@ -1775,11 +1774,11 @@ static int tegra_vi_graph_init(struct tegra_vi *vi) ret = tegra_vi_graph_parse_one(chan, remote); fwnode_handle_put(remote); - if (ret < 0 || list_empty(&chan->notifier.asd_list)) + if (ret < 0 || list_empty(&chan->notifier.waiting_list)) continue; chan->notifier.ops = &tegra_vi_async_ops; - ret = v4l2_async_nf_register(&vid->v4l2_dev, &chan->notifier); + ret = v4l2_async_nf_register(&chan->notifier); if (ret < 0) { dev_err(vi->dev, "failed to register channel %d notifier: %d\n", diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 4cf3fde7e034..191ecd19a6a7 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -19,6 +19,7 @@ #include #include "vip.h" +#include "video.h" static inline struct tegra_vip *host1x_client_to_vip(struct host1x_client *client) { diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5d93ecc01f6a..eac0ba39581e 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1887,17 +1887,6 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. -config FB_MX3 - tristate "MX3 Framebuffer support" - depends on FB && MX3_IPU - select BACKLIGHT_CLASS_DEVICE - select FB_IOMEM_HELPERS - default y - help - This is a framebuffer device for the i.MX31 LCD Controller. So - far only synchronous displays are supported. If you plan to use - an LCD display with your i.MX31 system, say Y here. - config FB_BROADSHEET tristate "E-Ink Broadsheet/Epson S1D13521 controller support" depends on FB && (ARCH_PXA || COMPILE_TEST) diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index e6b0ae094b8b..70569f7027ed 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -122,7 +122,6 @@ obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_EFI) += efifb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o obj-$(CONFIG_FB_OF) += offb.o -obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o obj-$(CONFIG_FB_SIMPLE) += simplefb.o diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index fafe574398b0..1b3c9958ef5c 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -61,14 +61,12 @@ static ssize_t store_mode(struct device *device, struct device_attribute *attr, struct fb_var_screeninfo var; struct fb_modelist *modelist; struct fb_videomode *mode; - struct list_head *pos; size_t i; int err; memset(&var, 0, sizeof(var)); - list_for_each(pos, &fb_info->modelist) { - modelist = list_entry(pos, struct fb_modelist, list); + list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; i = mode_string(mstr, 0, mode); if (strncmp(mstr, buf, max(count, i)) == 0) { @@ -129,13 +127,11 @@ static ssize_t show_modes(struct device *device, struct device_attribute *attr, { struct fb_info *fb_info = dev_get_drvdata(device); unsigned int i; - struct list_head *pos; struct fb_modelist *modelist; const struct fb_videomode *mode; i = 0; - list_for_each(pos, &fb_info->modelist) { - modelist = list_entry(pos, struct fb_modelist, list); + list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; i += mode_string(buf, i, mode); } diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index f7e019dded0f..7196b055f2bd 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -963,15 +963,12 @@ int fb_mode_is_equal(const struct fb_videomode *mode1, const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var, struct list_head *head) { - struct list_head *pos; struct fb_modelist *modelist; struct fb_videomode *mode, *best = NULL; u32 diff = -1; - list_for_each(pos, head) { + list_for_each_entry(modelist, head, list) { u32 d; - - modelist = list_entry(pos, struct fb_modelist, list); mode = &modelist->mode; if (mode->xres >= var->xres && mode->yres >= var->yres) { @@ -1001,15 +998,12 @@ const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode, struct list_head *head) { - struct list_head *pos; struct fb_modelist *modelist; struct fb_videomode *cmode, *best = NULL; u32 diff = -1, diff_refresh = -1; - list_for_each(pos, head) { + list_for_each_entry(modelist, head, list) { u32 d; - - modelist = list_entry(pos, struct fb_modelist, list); cmode = &modelist->mode; d = abs(cmode->xres - mode->xres) + @@ -1041,13 +1035,11 @@ const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode, const struct fb_videomode *fb_match_mode(const struct fb_var_screeninfo *var, struct list_head *head) { - struct list_head *pos; struct fb_modelist *modelist; struct fb_videomode *m, mode; fb_var_to_videomode(&mode, var); - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); + list_for_each_entry(modelist, head, list) { m = &modelist->mode; if (fb_mode_is_equal(m, &mode)) return m; @@ -1065,13 +1057,11 @@ const struct fb_videomode *fb_match_mode(const struct fb_var_screeninfo *var, */ int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head) { - struct list_head *pos; struct fb_modelist *modelist; struct fb_videomode *m; int found = 0; - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); + list_for_each_entry(modelist, head, list) { m = &modelist->mode; if (fb_mode_is_equal(m, mode)) { found = 1; @@ -1152,7 +1142,6 @@ void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num, const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs, struct list_head *head) { - struct list_head *pos; struct fb_modelist *modelist; const struct fb_videomode *m, *m1 = NULL, *md = NULL, *best = NULL; int first = 0; @@ -1161,8 +1150,7 @@ const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs, goto finished; /* get the first detailed mode and the very first mode */ - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); + list_for_each_entry(modelist, head, list) { m = &modelist->mode; if (!first) { diff --git a/drivers/video/fbdev/mx3fb.c b/drivers/video/fbdev/mx3fb.c deleted file mode 100644 index e6bafaba3460..000000000000 --- a/drivers/video/fbdev/mx3fb.c +++ /dev/null @@ -1,1695 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - * - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define MX3FB_NAME "mx3_sdc_fb" - -#define MX3FB_REG_OFFSET 0xB4 - -/* SDC Registers */ -#define SDC_COM_CONF (0xB4 - MX3FB_REG_OFFSET) -#define SDC_GW_CTRL (0xB8 - MX3FB_REG_OFFSET) -#define SDC_FG_POS (0xBC - MX3FB_REG_OFFSET) -#define SDC_BG_POS (0xC0 - MX3FB_REG_OFFSET) -#define SDC_CUR_POS (0xC4 - MX3FB_REG_OFFSET) -#define SDC_PWM_CTRL (0xC8 - MX3FB_REG_OFFSET) -#define SDC_CUR_MAP (0xCC - MX3FB_REG_OFFSET) -#define SDC_HOR_CONF (0xD0 - MX3FB_REG_OFFSET) -#define SDC_VER_CONF (0xD4 - MX3FB_REG_OFFSET) -#define SDC_SHARP_CONF_1 (0xD8 - MX3FB_REG_OFFSET) -#define SDC_SHARP_CONF_2 (0xDC - MX3FB_REG_OFFSET) - -/* Register bits */ -#define SDC_COM_TFT_COLOR 0x00000001UL -#define SDC_COM_FG_EN 0x00000010UL -#define SDC_COM_GWSEL 0x00000020UL -#define SDC_COM_GLB_A 0x00000040UL -#define SDC_COM_KEY_COLOR_G 0x00000080UL -#define SDC_COM_BG_EN 0x00000200UL -#define SDC_COM_SHARP 0x00001000UL - -#define SDC_V_SYNC_WIDTH_L 0x00000001UL - -/* Display Interface registers */ -#define DI_DISP_IF_CONF (0x0124 - MX3FB_REG_OFFSET) -#define DI_DISP_SIG_POL (0x0128 - MX3FB_REG_OFFSET) -#define DI_SER_DISP1_CONF (0x012C - MX3FB_REG_OFFSET) -#define DI_SER_DISP2_CONF (0x0130 - MX3FB_REG_OFFSET) -#define DI_HSP_CLK_PER (0x0134 - MX3FB_REG_OFFSET) -#define DI_DISP0_TIME_CONF_1 (0x0138 - MX3FB_REG_OFFSET) -#define DI_DISP0_TIME_CONF_2 (0x013C - MX3FB_REG_OFFSET) -#define DI_DISP0_TIME_CONF_3 (0x0140 - MX3FB_REG_OFFSET) -#define DI_DISP1_TIME_CONF_1 (0x0144 - MX3FB_REG_OFFSET) -#define DI_DISP1_TIME_CONF_2 (0x0148 - MX3FB_REG_OFFSET) -#define DI_DISP1_TIME_CONF_3 (0x014C - MX3FB_REG_OFFSET) -#define DI_DISP2_TIME_CONF_1 (0x0150 - MX3FB_REG_OFFSET) -#define DI_DISP2_TIME_CONF_2 (0x0154 - MX3FB_REG_OFFSET) -#define DI_DISP2_TIME_CONF_3 (0x0158 - MX3FB_REG_OFFSET) -#define DI_DISP3_TIME_CONF (0x015C - MX3FB_REG_OFFSET) -#define DI_DISP0_DB0_MAP (0x0160 - MX3FB_REG_OFFSET) -#define DI_DISP0_DB1_MAP (0x0164 - MX3FB_REG_OFFSET) -#define DI_DISP0_DB2_MAP (0x0168 - MX3FB_REG_OFFSET) -#define DI_DISP0_CB0_MAP (0x016C - MX3FB_REG_OFFSET) -#define DI_DISP0_CB1_MAP (0x0170 - MX3FB_REG_OFFSET) -#define DI_DISP0_CB2_MAP (0x0174 - MX3FB_REG_OFFSET) -#define DI_DISP1_DB0_MAP (0x0178 - MX3FB_REG_OFFSET) -#define DI_DISP1_DB1_MAP (0x017C - MX3FB_REG_OFFSET) -#define DI_DISP1_DB2_MAP (0x0180 - MX3FB_REG_OFFSET) -#define DI_DISP1_CB0_MAP (0x0184 - MX3FB_REG_OFFSET) -#define DI_DISP1_CB1_MAP (0x0188 - MX3FB_REG_OFFSET) -#define DI_DISP1_CB2_MAP (0x018C - MX3FB_REG_OFFSET) -#define DI_DISP2_DB0_MAP (0x0190 - MX3FB_REG_OFFSET) -#define DI_DISP2_DB1_MAP (0x0194 - MX3FB_REG_OFFSET) -#define DI_DISP2_DB2_MAP (0x0198 - MX3FB_REG_OFFSET) -#define DI_DISP2_CB0_MAP (0x019C - MX3FB_REG_OFFSET) -#define DI_DISP2_CB1_MAP (0x01A0 - MX3FB_REG_OFFSET) -#define DI_DISP2_CB2_MAP (0x01A4 - MX3FB_REG_OFFSET) -#define DI_DISP3_B0_MAP (0x01A8 - MX3FB_REG_OFFSET) -#define DI_DISP3_B1_MAP (0x01AC - MX3FB_REG_OFFSET) -#define DI_DISP3_B2_MAP (0x01B0 - MX3FB_REG_OFFSET) -#define DI_DISP_ACC_CC (0x01B4 - MX3FB_REG_OFFSET) -#define DI_DISP_LLA_CONF (0x01B8 - MX3FB_REG_OFFSET) -#define DI_DISP_LLA_DATA (0x01BC - MX3FB_REG_OFFSET) - -/* DI_DISP_SIG_POL bits */ -#define DI_D3_VSYNC_POL_SHIFT 28 -#define DI_D3_HSYNC_POL_SHIFT 27 -#define DI_D3_DRDY_SHARP_POL_SHIFT 26 -#define DI_D3_CLK_POL_SHIFT 25 -#define DI_D3_DATA_POL_SHIFT 24 - -/* DI_DISP_IF_CONF bits */ -#define DI_D3_CLK_IDLE_SHIFT 26 -#define DI_D3_CLK_SEL_SHIFT 25 -#define DI_D3_DATAMSK_SHIFT 24 - -enum ipu_panel { - IPU_PANEL_SHARP_TFT, - IPU_PANEL_TFT, -}; - -struct ipu_di_signal_cfg { - unsigned datamask_en:1; - unsigned clksel_en:1; - unsigned clkidle_en:1; - unsigned data_pol:1; /* true = inverted */ - unsigned clk_pol:1; /* true = rising edge */ - unsigned enable_pol:1; - unsigned Hsync_pol:1; /* true = active high */ - unsigned Vsync_pol:1; -}; - -static const struct fb_videomode mx3fb_modedb[] = { - { - /* 240x320 @ 60 Hz */ - .name = "Sharp-QVGA", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 185925, - .left_margin = 9, - .right_margin = 16, - .upper_margin = 7, - .lower_margin = 9, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | - FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | - FB_SYNC_CLK_IDLE_EN, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* 240x33 @ 60 Hz */ - .name = "Sharp-CLI", - .refresh = 60, - .xres = 240, - .yres = 33, - .pixclock = 185925, - .left_margin = 9, - .right_margin = 16, - .upper_margin = 7, - .lower_margin = 9 + 287, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | - FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | - FB_SYNC_CLK_IDLE_EN, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* 640x480 @ 60 Hz */ - .name = "NEC-VGA", - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = 38255, - .left_margin = 144, - .right_margin = 0, - .upper_margin = 34, - .lower_margin = 40, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* NTSC TV output */ - .name = "TV-NTSC", - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = 37538, - .left_margin = 38, - .right_margin = 858 - 640 - 38 - 3, - .upper_margin = 36, - .lower_margin = 518 - 480 - 36 - 1, - .hsync_len = 3, - .vsync_len = 1, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* PAL TV output */ - .name = "TV-PAL", - .refresh = 50, - .xres = 640, - .yres = 480, - .pixclock = 37538, - .left_margin = 38, - .right_margin = 960 - 640 - 38 - 32, - .upper_margin = 32, - .lower_margin = 555 - 480 - 32 - 3, - .hsync_len = 32, - .vsync_len = 3, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* TV output VGA mode, 640x480 @ 65 Hz */ - .name = "TV-VGA", - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = 40574, - .left_margin = 35, - .right_margin = 45, - .upper_margin = 9, - .lower_margin = 1, - .hsync_len = 46, - .vsync_len = 5, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, -}; - -struct mx3fb_data { - struct fb_info *fbi; - int backlight_level; - void __iomem *reg_base; - spinlock_t lock; - struct device *dev; - struct backlight_device *bl; - - uint32_t h_start_width; - uint32_t v_start_width; - enum disp_data_mapping disp_data_fmt; -}; - -struct dma_chan_request { - struct mx3fb_data *mx3fb; - enum ipu_channel id; -}; - -/* MX3 specific framebuffer information. */ -struct mx3fb_info { - int blank; - enum ipu_channel ipu_ch; - uint32_t cur_ipu_buf; - - u32 pseudo_palette[16]; - - struct completion flip_cmpl; - struct mutex mutex; /* Protects fb-ops */ - struct mx3fb_data *mx3fb; - struct idmac_channel *idmac_channel; - struct dma_async_tx_descriptor *txd; - dma_cookie_t cookie; - struct scatterlist sg[2]; - - struct fb_var_screeninfo cur_var; /* current var info */ -}; - -static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value); -static u32 sdc_get_brightness(struct mx3fb_data *mx3fb); - -static int mx3fb_bl_get_brightness(struct backlight_device *bl) -{ - struct mx3fb_data *fbd = bl_get_data(bl); - - return sdc_get_brightness(fbd); -} - -static int mx3fb_bl_update_status(struct backlight_device *bl) -{ - struct mx3fb_data *fbd = bl_get_data(bl); - int brightness = backlight_get_brightness(bl); - - fbd->backlight_level = (fbd->backlight_level & ~0xFF) | brightness; - - sdc_set_brightness(fbd, fbd->backlight_level); - - return 0; -} - -static const struct backlight_ops mx3fb_lcdc_bl_ops = { - .update_status = mx3fb_bl_update_status, - .get_brightness = mx3fb_bl_get_brightness, -}; - -static void mx3fb_init_backlight(struct mx3fb_data *fbd) -{ - struct backlight_properties props; - struct backlight_device *bl; - - if (fbd->bl) - return; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 0xff; - props.type = BACKLIGHT_RAW; - sdc_set_brightness(fbd, fbd->backlight_level); - - bl = backlight_device_register("mx3fb-bl", fbd->dev, fbd, - &mx3fb_lcdc_bl_ops, &props); - if (IS_ERR(bl)) { - dev_err(fbd->dev, "error %ld on backlight register\n", - PTR_ERR(bl)); - return; - } - - fbd->bl = bl; - bl->props.power = FB_BLANK_UNBLANK; - bl->props.fb_blank = FB_BLANK_UNBLANK; - bl->props.brightness = mx3fb_bl_get_brightness(bl); -} - -static void mx3fb_exit_backlight(struct mx3fb_data *fbd) -{ - backlight_device_unregister(fbd->bl); -} - -static void mx3fb_dma_done(void *); - -/* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */ -static const char *fb_mode; -static unsigned long default_bpp = 16; - -static u32 mx3fb_read_reg(struct mx3fb_data *mx3fb, unsigned long reg) -{ - return __raw_readl(mx3fb->reg_base + reg); -} - -static void mx3fb_write_reg(struct mx3fb_data *mx3fb, u32 value, unsigned long reg) -{ - __raw_writel(value, mx3fb->reg_base + reg); -} - -struct di_mapping { - uint32_t b0, b1, b2; -}; - -static const struct di_mapping di_mappings[] = { - [IPU_DISP_DATA_MAPPING_RGB666] = { 0x0005000f, 0x000b000f, 0x0011000f }, - [IPU_DISP_DATA_MAPPING_RGB565] = { 0x0004003f, 0x000a000f, 0x000f003f }, - [IPU_DISP_DATA_MAPPING_RGB888] = { 0x00070000, 0x000f0000, 0x00170000 }, -}; - -static void sdc_fb_init(struct mx3fb_info *fbi) -{ - struct mx3fb_data *mx3fb = fbi->mx3fb; - uint32_t reg; - - reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); - - mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF); -} - -/* Returns enabled flag before uninit */ -static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi) -{ - struct mx3fb_data *mx3fb = fbi->mx3fb; - uint32_t reg; - - reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); - - mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF); - - return reg & SDC_COM_BG_EN; -} - -static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) -{ - struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; - struct idmac_channel *ichan = mx3_fbi->idmac_channel; - struct dma_chan *dma_chan = &ichan->dma_chan; - unsigned long flags; - dma_cookie_t cookie; - - if (mx3_fbi->txd) - dev_dbg(mx3fb->dev, "mx3fbi %p, desc %p, sg %p\n", mx3_fbi, - to_tx_desc(mx3_fbi->txd), to_tx_desc(mx3_fbi->txd)->sg); - else - dev_dbg(mx3fb->dev, "mx3fbi %p, txd = NULL\n", mx3_fbi); - - /* This enables the channel */ - if (mx3_fbi->cookie < 0) { - mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan, - &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!mx3_fbi->txd) { - dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", - dma_chan->chan_id); - return; - } - - mx3_fbi->txd->callback_param = mx3_fbi->txd; - mx3_fbi->txd->callback = mx3fb_dma_done; - - cookie = mx3_fbi->txd->tx_submit(mx3_fbi->txd); - dev_dbg(mx3fb->dev, "%d: Submit %p #%d [%c]\n", __LINE__, - mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); - } else { - if (!mx3_fbi->txd || !mx3_fbi->txd->tx_submit) { - dev_err(mx3fb->dev, "Cannot enable channel %d\n", - dma_chan->chan_id); - return; - } - - /* Just re-activate the same buffer */ - dma_async_issue_pending(dma_chan); - cookie = mx3_fbi->cookie; - dev_dbg(mx3fb->dev, "%d: Re-submit %p #%d [%c]\n", __LINE__, - mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); - } - - if (cookie >= 0) { - spin_lock_irqsave(&mx3fb->lock, flags); - sdc_fb_init(mx3_fbi); - mx3_fbi->cookie = cookie; - spin_unlock_irqrestore(&mx3fb->lock, flags); - } - - /* - * Attention! Without this msleep the channel keeps generating - * interrupts. Next sdc_set_brightness() is going to be called - * from mx3fb_blank(). - */ - msleep(2); -} - -static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) -{ - struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; - unsigned long flags; - - if (mx3_fbi->txd == NULL) - return; - - spin_lock_irqsave(&mx3fb->lock, flags); - - sdc_fb_uninit(mx3_fbi); - - spin_unlock_irqrestore(&mx3fb->lock, flags); - - dmaengine_terminate_all(mx3_fbi->txd->chan); - mx3_fbi->txd = NULL; - mx3_fbi->cookie = -EINVAL; -} - -/** - * sdc_set_window_pos() - set window position of the respective plane. - * @mx3fb: mx3fb context. - * @channel: IPU DMAC channel ID. - * @x_pos: X coordinate relative to the top left corner to place window at. - * @y_pos: Y coordinate relative to the top left corner to place window at. - * @return: 0 on success or negative error code on failure. - */ -static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel, - int16_t x_pos, int16_t y_pos) -{ - if (channel != IDMAC_SDC_0) - return -EINVAL; - - x_pos += mx3fb->h_start_width; - y_pos += mx3fb->v_start_width; - - mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); - return 0; -} - -/** - * sdc_init_panel() - initialize a synchronous LCD panel. - * @mx3fb: mx3fb context. - * @panel: panel type. - * @pixel_clk: desired pixel clock frequency in Hz. - * @width: width of panel in pixels. - * @height: height of panel in pixels. - * @h_start_width: number of pixel clocks between the HSYNC signal pulse - * and the start of valid data. - * @h_sync_width: width of the HSYNC signal in units of pixel clocks. - * @h_end_width: number of pixel clocks between the end of valid data - * and the HSYNC signal for next line. - * @v_start_width: number of lines between the VSYNC signal pulse and the - * start of valid data. - * @v_sync_width: width of the VSYNC signal in units of lines - * @v_end_width: number of lines between the end of valid data and the - * VSYNC signal for next frame. - * @sig: bitfield of signal polarities for LCD interface. - * @return: 0 on success or negative error code on failure. - */ -static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, - uint32_t pixel_clk, - uint16_t width, uint16_t height, - uint16_t h_start_width, uint16_t h_sync_width, - uint16_t h_end_width, uint16_t v_start_width, - uint16_t v_sync_width, uint16_t v_end_width, - const struct ipu_di_signal_cfg *sig) -{ - unsigned long lock_flags; - uint32_t reg; - uint32_t old_conf; - uint32_t div; - struct clk *ipu_clk; - const struct di_mapping *map; - - dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); - - if (v_sync_width == 0 || h_sync_width == 0) - return -EINVAL; - - /* Init panel size and blanking periods */ - reg = ((uint32_t) (h_sync_width - 1) << 26) | - ((uint32_t) (width + h_start_width + h_end_width - 1) << 16); - mx3fb_write_reg(mx3fb, reg, SDC_HOR_CONF); - -#ifdef DEBUG - printk(KERN_CONT " hor_conf %x,", reg); -#endif - - reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L | - ((uint32_t) (height + v_start_width + v_end_width - 1) << 16); - mx3fb_write_reg(mx3fb, reg, SDC_VER_CONF); - -#ifdef DEBUG - printk(KERN_CONT " ver_conf %x\n", reg); -#endif - - mx3fb->h_start_width = h_start_width; - mx3fb->v_start_width = v_start_width; - - switch (panel) { - case IPU_PANEL_SHARP_TFT: - mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1); - mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2); - mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); - break; - case IPU_PANEL_TFT: - mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF); - break; - default: - return -EINVAL; - } - - /* Init clocking */ - - /* - * Calculate divider: fractional part is 4 bits so simply multiple by - * 2^4 to get fractional part, as long as we stay under ~250MHz and on - * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz - */ - ipu_clk = clk_get(mx3fb->dev, NULL); - if (!IS_ERR(ipu_clk)) { - div = clk_get_rate(ipu_clk) * 16 / pixel_clk; - clk_put(ipu_clk); - } else { - div = 0; - } - - if (div < 0x40) { /* Divider less than 4 */ - dev_dbg(mx3fb->dev, - "InitPanel() - Pixel clock divider less than 4\n"); - div = 0x40; - } - - dev_dbg(mx3fb->dev, "pixel clk = %u, divider %u.%u\n", - pixel_clk, div >> 4, (div & 7) * 125); - - spin_lock_irqsave(&mx3fb->lock, lock_flags); - - /* - * DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits - * fewer. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing - * debug. DISP3_IF_CLK_UP_WR is 0 - */ - mx3fb_write_reg(mx3fb, (((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); - - /* DI settings */ - old_conf = mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF) & 0x78FFFFFF; - old_conf |= sig->datamask_en << DI_D3_DATAMSK_SHIFT | - sig->clksel_en << DI_D3_CLK_SEL_SHIFT | - sig->clkidle_en << DI_D3_CLK_IDLE_SHIFT; - mx3fb_write_reg(mx3fb, old_conf, DI_DISP_IF_CONF); - - old_conf = mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL) & 0xE0FFFFFF; - old_conf |= sig->data_pol << DI_D3_DATA_POL_SHIFT | - sig->clk_pol << DI_D3_CLK_POL_SHIFT | - sig->enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT | - sig->Hsync_pol << DI_D3_HSYNC_POL_SHIFT | - sig->Vsync_pol << DI_D3_VSYNC_POL_SHIFT; - mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); - - map = &di_mappings[mx3fb->disp_data_fmt]; - mx3fb_write_reg(mx3fb, map->b0, DI_DISP3_B0_MAP); - mx3fb_write_reg(mx3fb, map->b1, DI_DISP3_B1_MAP); - mx3fb_write_reg(mx3fb, map->b2, DI_DISP3_B2_MAP); - - spin_unlock_irqrestore(&mx3fb->lock, lock_flags); - - dev_dbg(mx3fb->dev, "DI_DISP_IF_CONF = 0x%08X\n", - mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF)); - dev_dbg(mx3fb->dev, "DI_DISP_SIG_POL = 0x%08X\n", - mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL)); - dev_dbg(mx3fb->dev, "DI_DISP3_TIME_CONF = 0x%08X\n", - mx3fb_read_reg(mx3fb, DI_DISP3_TIME_CONF)); - - return 0; -} - -/** - * sdc_set_color_key() - set the transparent color key for SDC graphic plane. - * @mx3fb: mx3fb context. - * @channel: IPU DMAC channel ID. - * @enable: boolean to enable or disable color keyl. - * @color_key: 24-bit RGB color to use as transparent color key. - * @return: 0 on success or negative error code on failure. - */ -static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel, - bool enable, uint32_t color_key) -{ - uint32_t reg, sdc_conf; - unsigned long lock_flags; - - spin_lock_irqsave(&mx3fb->lock, lock_flags); - - sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF); - if (channel == IDMAC_SDC_0) - sdc_conf &= ~SDC_COM_GWSEL; - else - sdc_conf |= SDC_COM_GWSEL; - - if (enable) { - reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L; - mx3fb_write_reg(mx3fb, reg | (color_key & 0x00FFFFFFL), - SDC_GW_CTRL); - - sdc_conf |= SDC_COM_KEY_COLOR_G; - } else { - sdc_conf &= ~SDC_COM_KEY_COLOR_G; - } - mx3fb_write_reg(mx3fb, sdc_conf, SDC_COM_CONF); - - spin_unlock_irqrestore(&mx3fb->lock, lock_flags); - - return 0; -} - -/** - * sdc_set_global_alpha() - set global alpha blending modes. - * @mx3fb: mx3fb context. - * @enable: boolean to enable or disable global alpha blending. If disabled, - * per pixel blending is used. - * @alpha: global alpha value. - * @return: 0 on success or negative error code on failure. - */ -static int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t alpha) -{ - uint32_t reg; - unsigned long lock_flags; - - spin_lock_irqsave(&mx3fb->lock, lock_flags); - - if (enable) { - reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0x00FFFFFFL; - mx3fb_write_reg(mx3fb, reg | ((uint32_t) alpha << 24), SDC_GW_CTRL); - - reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); - mx3fb_write_reg(mx3fb, reg | SDC_COM_GLB_A, SDC_COM_CONF); - } else { - reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); - mx3fb_write_reg(mx3fb, reg & ~SDC_COM_GLB_A, SDC_COM_CONF); - } - - spin_unlock_irqrestore(&mx3fb->lock, lock_flags); - - return 0; -} - -static u32 sdc_get_brightness(struct mx3fb_data *mx3fb) -{ - u32 brightness; - - brightness = mx3fb_read_reg(mx3fb, SDC_PWM_CTRL); - brightness = (brightness >> 16) & 0xFF; - - return brightness; -} - -static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value) -{ - dev_dbg(mx3fb->dev, "%s: value = %d\n", __func__, value); - /* This might be board-specific */ - mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); - return; -} - -static uint32_t bpp_to_pixfmt(int bpp) -{ - uint32_t pixfmt = 0; - switch (bpp) { - case 24: - pixfmt = IPU_PIX_FMT_BGR24; - break; - case 32: - pixfmt = IPU_PIX_FMT_BGR32; - break; - case 16: - pixfmt = IPU_PIX_FMT_RGB565; - break; - } - return pixfmt; -} - -static int mx3fb_blank(int blank, struct fb_info *fbi); -static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, - bool lock); -static int mx3fb_unmap_video_memory(struct fb_info *fbi); - -/** - * mx3fb_set_fix() - set fixed framebuffer parameters from variable settings. - * @fbi: framebuffer information pointer - * @return: 0 on success or negative error code on failure. - */ -static int mx3fb_set_fix(struct fb_info *fbi) -{ - struct fb_fix_screeninfo *fix = &fbi->fix; - struct fb_var_screeninfo *var = &fbi->var; - - memcpy(fix->id, "DISP3 BG", 8); - - fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; - - fix->type = FB_TYPE_PACKED_PIXELS; - fix->accel = FB_ACCEL_NONE; - fix->visual = FB_VISUAL_TRUECOLOR; - fix->xpanstep = 1; - fix->ypanstep = 1; - - return 0; -} - -static void mx3fb_dma_done(void *arg) -{ - struct idmac_tx_desc *tx_desc = to_tx_desc(arg); - struct dma_chan *chan = tx_desc->txd.chan; - struct idmac_channel *ichannel = to_idmac_chan(chan); - struct mx3fb_data *mx3fb = ichannel->client; - struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; - - dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq); - - /* We only need one interrupt, it will be re-enabled as needed */ - disable_irq_nosync(ichannel->eof_irq); - - complete(&mx3_fbi->flip_cmpl); -} - -static bool mx3fb_must_set_par(struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - struct fb_var_screeninfo old_var = mx3_fbi->cur_var; - struct fb_var_screeninfo new_var = fbi->var; - - if ((fbi->var.activate & FB_ACTIVATE_FORCE) && - (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) - return true; - - /* - * Ignore xoffset and yoffset update, - * because pan display handles this case. - */ - old_var.xoffset = new_var.xoffset; - old_var.yoffset = new_var.yoffset; - - return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo)); -} - -static int __set_par(struct fb_info *fbi, bool lock) -{ - u32 mem_len, cur_xoffset, cur_yoffset; - struct ipu_di_signal_cfg sig_cfg; - enum ipu_panel mode = IPU_PANEL_TFT; - struct mx3fb_info *mx3_fbi = fbi->par; - struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; - struct idmac_channel *ichan = mx3_fbi->idmac_channel; - struct idmac_video_param *video = &ichan->params.video; - struct scatterlist *sg = mx3_fbi->sg; - - /* Total cleanup */ - if (mx3_fbi->txd) - sdc_disable_channel(mx3_fbi); - - mx3fb_set_fix(fbi); - - mem_len = fbi->var.yres_virtual * fbi->fix.line_length; - if (mem_len > fbi->fix.smem_len) { - if (fbi->fix.smem_start) - mx3fb_unmap_video_memory(fbi); - - if (mx3fb_map_video_memory(fbi, mem_len, lock) < 0) - return -ENOMEM; - } - - sg_init_table(&sg[0], 1); - sg_init_table(&sg[1], 1); - - sg_dma_address(&sg[0]) = fbi->fix.smem_start; - sg_set_page(&sg[0], virt_to_page(fbi->screen_base), - fbi->fix.smem_len, - offset_in_page(fbi->screen_base)); - - if (mx3_fbi->ipu_ch == IDMAC_SDC_0) { - memset(&sig_cfg, 0, sizeof(sig_cfg)); - if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) - sig_cfg.Hsync_pol = true; - if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) - sig_cfg.Vsync_pol = true; - if (fbi->var.sync & FB_SYNC_CLK_INVERT) - sig_cfg.clk_pol = true; - if (fbi->var.sync & FB_SYNC_DATA_INVERT) - sig_cfg.data_pol = true; - if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH) - sig_cfg.enable_pol = true; - if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) - sig_cfg.clkidle_en = true; - if (fbi->var.sync & FB_SYNC_CLK_SEL_EN) - sig_cfg.clksel_en = true; - if (fbi->var.sync & FB_SYNC_SHARP_MODE) - mode = IPU_PANEL_SHARP_TFT; - - dev_dbg(fbi->device, "pixclock = %u Hz\n", - (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); - - if (sdc_init_panel(mx3fb, mode, - (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, - fbi->var.xres, fbi->var.yres, - fbi->var.left_margin, - fbi->var.hsync_len, - fbi->var.right_margin + - fbi->var.hsync_len, - fbi->var.upper_margin, - fbi->var.vsync_len, - fbi->var.lower_margin + - fbi->var.vsync_len, &sig_cfg) != 0) { - dev_err(fbi->device, - "mx3fb: Error initializing panel.\n"); - return -EINVAL; - } - } - - sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, 0, 0); - - mx3_fbi->cur_ipu_buf = 0; - - video->out_pixel_fmt = bpp_to_pixfmt(fbi->var.bits_per_pixel); - video->out_width = fbi->var.xres; - video->out_height = fbi->var.yres; - video->out_stride = fbi->var.xres_virtual; - - if (mx3_fbi->blank == FB_BLANK_UNBLANK) { - sdc_enable_channel(mx3_fbi); - /* - * sg[0] points to fb smem_start address - * and is actually active in controller. - */ - mx3_fbi->cur_var.xoffset = 0; - mx3_fbi->cur_var.yoffset = 0; - } - - /* - * Preserve xoffset and yoffest in case they are - * inactive in controller as fb is blanked. - */ - cur_xoffset = mx3_fbi->cur_var.xoffset; - cur_yoffset = mx3_fbi->cur_var.yoffset; - mx3_fbi->cur_var = fbi->var; - mx3_fbi->cur_var.xoffset = cur_xoffset; - mx3_fbi->cur_var.yoffset = cur_yoffset; - - return 0; -} - -/** - * mx3fb_set_par() - set framebuffer parameters and change the operating mode. - * @fbi: framebuffer information pointer. - * @return: 0 on success or negative error code on failure. - */ -static int mx3fb_set_par(struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; - struct idmac_channel *ichan = mx3_fbi->idmac_channel; - int ret; - - dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+'); - - mutex_lock(&mx3_fbi->mutex); - - ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0; - - mutex_unlock(&mx3_fbi->mutex); - - return ret; -} - -/** - * mx3fb_check_var() - check and adjust framebuffer variable parameters. - * @var: framebuffer variable parameters - * @fbi: framebuffer information pointer - */ -static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - u32 vtotal; - u32 htotal; - - dev_dbg(fbi->device, "%s\n", __func__); - - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; - if (var->yres_virtual < var->yres) - var->yres_virtual = var->yres; - - if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && - (var->bits_per_pixel != 16)) - var->bits_per_pixel = default_bpp; - - switch (var->bits_per_pixel) { - case 16: - var->red.length = 5; - var->red.offset = 11; - var->red.msb_right = 0; - - var->green.length = 6; - var->green.offset = 5; - var->green.msb_right = 0; - - var->blue.length = 5; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 0; - var->transp.offset = 0; - var->transp.msb_right = 0; - break; - case 24: - var->red.length = 8; - var->red.offset = 16; - var->red.msb_right = 0; - - var->green.length = 8; - var->green.offset = 8; - var->green.msb_right = 0; - - var->blue.length = 8; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 0; - var->transp.offset = 0; - var->transp.msb_right = 0; - break; - case 32: - var->red.length = 8; - var->red.offset = 16; - var->red.msb_right = 0; - - var->green.length = 8; - var->green.offset = 8; - var->green.msb_right = 0; - - var->blue.length = 8; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 8; - var->transp.offset = 24; - var->transp.msb_right = 0; - break; - } - - if (var->pixclock < 1000) { - htotal = var->xres + var->right_margin + var->hsync_len + - var->left_margin; - vtotal = var->yres + var->lower_margin + var->vsync_len + - var->upper_margin; - var->pixclock = (vtotal * htotal * 6UL) / 100UL; - var->pixclock = KHZ2PICOS(var->pixclock); - dev_dbg(fbi->device, "pixclock set for 60Hz refresh = %u ps\n", - var->pixclock); - } - - var->height = -1; - var->width = -1; - var->grayscale = 0; - - /* Preserve sync flags */ - var->sync |= mx3_fbi->cur_var.sync; - mx3_fbi->cur_var.sync |= var->sync; - - return 0; -} - -static u32 chan_to_field(unsigned int chan, struct fb_bitfield *bf) -{ - chan &= 0xffff; - chan >>= 16 - bf->length; - return chan << bf->offset; -} - -static int mx3fb_setcolreg(unsigned int regno, unsigned int red, - unsigned int green, unsigned int blue, - unsigned int trans, struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - u32 val; - int ret = 1; - - dev_dbg(fbi->device, "%s, regno = %u\n", __func__, regno); - - mutex_lock(&mx3_fbi->mutex); - /* - * If greyscale is true, then we convert the RGB value - * to greyscale no matter what visual we are using. - */ - if (fbi->var.grayscale) - red = green = blue = (19595 * red + 38470 * green + - 7471 * blue) >> 16; - switch (fbi->fix.visual) { - case FB_VISUAL_TRUECOLOR: - /* - * 16-bit True Colour. We encode the RGB value - * according to the RGB bitfield information. - */ - if (regno < 16) { - u32 *pal = fbi->pseudo_palette; - - val = chan_to_field(red, &fbi->var.red); - val |= chan_to_field(green, &fbi->var.green); - val |= chan_to_field(blue, &fbi->var.blue); - - pal[regno] = val; - - ret = 0; - } - break; - - case FB_VISUAL_STATIC_PSEUDOCOLOR: - case FB_VISUAL_PSEUDOCOLOR: - break; - } - mutex_unlock(&mx3_fbi->mutex); - - return ret; -} - -static void __blank(int blank, struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; - int was_blank = mx3_fbi->blank; - - mx3_fbi->blank = blank; - - /* Attention! - * Do not call sdc_disable_channel() for a channel that is disabled - * already! This will result in a kernel NULL pointer dereference - * (mx3_fbi->txd is NULL). Hide the fact, that all blank modes are - * handled equally by this driver. - */ - if (blank > FB_BLANK_UNBLANK && was_blank > FB_BLANK_UNBLANK) - return; - - switch (blank) { - case FB_BLANK_POWERDOWN: - case FB_BLANK_VSYNC_SUSPEND: - case FB_BLANK_HSYNC_SUSPEND: - case FB_BLANK_NORMAL: - sdc_set_brightness(mx3fb, 0); - memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); - /* Give LCD time to update - enough for 50 and 60 Hz */ - msleep(25); - sdc_disable_channel(mx3_fbi); - break; - case FB_BLANK_UNBLANK: - sdc_enable_channel(mx3_fbi); - sdc_set_brightness(mx3fb, mx3fb->backlight_level); - break; - } -} - -/** - * mx3fb_blank() - blank the display. - * @blank: blank value for the panel - * @fbi: framebuffer information pointer - */ -static int mx3fb_blank(int blank, struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - - dev_dbg(fbi->device, "%s, blank = %d, base %p, len %u\n", __func__, - blank, fbi->screen_base, fbi->fix.smem_len); - - if (mx3_fbi->blank == blank) - return 0; - - mutex_lock(&mx3_fbi->mutex); - __blank(blank, fbi); - mutex_unlock(&mx3_fbi->mutex); - - return 0; -} - -/** - * mx3fb_pan_display() - pan or wrap the display - * @var: variable screen buffer information. - * @fbi: framebuffer information pointer. - * - * We look only at xoffset, yoffset and the FB_VMODE_YWRAP flag - */ -static int mx3fb_pan_display(struct fb_var_screeninfo *var, - struct fb_info *fbi) -{ - struct mx3fb_info *mx3_fbi = fbi->par; - u32 y_bottom; - unsigned long base; - off_t offset; - dma_cookie_t cookie; - struct scatterlist *sg = mx3_fbi->sg; - struct dma_chan *dma_chan = &mx3_fbi->idmac_channel->dma_chan; - struct dma_async_tx_descriptor *txd; - int ret; - - dev_dbg(fbi->device, "%s [%c]\n", __func__, - list_empty(&mx3_fbi->idmac_channel->queue) ? '-' : '+'); - - if (var->xoffset > 0) { - dev_dbg(fbi->device, "x panning not supported\n"); - return -EINVAL; - } - - if (mx3_fbi->cur_var.xoffset == var->xoffset && - mx3_fbi->cur_var.yoffset == var->yoffset) - return 0; /* No change, do nothing */ - - y_bottom = var->yoffset; - - if (!(var->vmode & FB_VMODE_YWRAP)) - y_bottom += fbi->var.yres; - - if (y_bottom > fbi->var.yres_virtual) - return -EINVAL; - - mutex_lock(&mx3_fbi->mutex); - - offset = var->yoffset * fbi->fix.line_length - + var->xoffset * (fbi->var.bits_per_pixel / 8); - base = fbi->fix.smem_start + offset; - - dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n", - mx3_fbi->cur_ipu_buf, base); - - /* - * We enable the End of Frame interrupt, which will free a tx-descriptor, - * which we will need for the next dmaengine_prep_slave_sg(). The - * IRQ-handler will disable the IRQ again. - */ - init_completion(&mx3_fbi->flip_cmpl); - enable_irq(mx3_fbi->idmac_channel->eof_irq); - - ret = wait_for_completion_timeout(&mx3_fbi->flip_cmpl, HZ / 10); - if (ret <= 0) { - mutex_unlock(&mx3_fbi->mutex); - dev_info(fbi->device, "Panning failed due to %s\n", ret < 0 ? - "user interrupt" : "timeout"); - disable_irq(mx3_fbi->idmac_channel->eof_irq); - return ret ? : -ETIMEDOUT; - } - - mx3_fbi->cur_ipu_buf = !mx3_fbi->cur_ipu_buf; - - sg_dma_address(&sg[mx3_fbi->cur_ipu_buf]) = base; - sg_set_page(&sg[mx3_fbi->cur_ipu_buf], - virt_to_page(fbi->screen_base + offset), fbi->fix.smem_len, - offset_in_page(fbi->screen_base + offset)); - - if (mx3_fbi->txd) - async_tx_ack(mx3_fbi->txd); - - txd = dmaengine_prep_slave_sg(dma_chan, sg + - mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!txd) { - dev_err(fbi->device, - "Error preparing a DMA transaction descriptor.\n"); - mutex_unlock(&mx3_fbi->mutex); - return -EIO; - } - - txd->callback_param = txd; - txd->callback = mx3fb_dma_done; - - /* - * Emulate original mx3fb behaviour: each new call to idmac_tx_submit() - * should switch to another buffer - */ - cookie = txd->tx_submit(txd); - dev_dbg(fbi->device, "%d: Submit %p #%d\n", __LINE__, txd, cookie); - if (cookie < 0) { - dev_err(fbi->device, - "Error updating SDC buf %d to address=0x%08lX\n", - mx3_fbi->cur_ipu_buf, base); - mutex_unlock(&mx3_fbi->mutex); - return -EIO; - } - - mx3_fbi->txd = txd; - - fbi->var.xoffset = var->xoffset; - fbi->var.yoffset = var->yoffset; - - if (var->vmode & FB_VMODE_YWRAP) - fbi->var.vmode |= FB_VMODE_YWRAP; - else - fbi->var.vmode &= ~FB_VMODE_YWRAP; - - mx3_fbi->cur_var = fbi->var; - - mutex_unlock(&mx3_fbi->mutex); - - dev_dbg(fbi->device, "Update complete\n"); - - return 0; -} - -/* - * This structure contains the pointers to the control functions that are - * invoked by the core framebuffer driver to perform operations like - * blitting, rectangle filling, copy regions and cursor definition. - */ -static const struct fb_ops mx3fb_ops = { - .owner = THIS_MODULE, - FB_DEFAULT_IOMEM_OPS, - .fb_set_par = mx3fb_set_par, - .fb_check_var = mx3fb_check_var, - .fb_setcolreg = mx3fb_setcolreg, - .fb_pan_display = mx3fb_pan_display, - .fb_blank = mx3fb_blank, -}; - -#ifdef CONFIG_PM -/* - * Power management hooks. Note that we won't be called from IRQ context, - * unlike the blank functions above, so we may sleep. - */ - -/* - * Suspends the framebuffer and blanks the screen. Power management support - */ -static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); - struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; - - console_lock(); - fb_set_suspend(mx3fb->fbi, 1); - console_unlock(); - - if (mx3_fbi->blank == FB_BLANK_UNBLANK) { - sdc_disable_channel(mx3_fbi); - sdc_set_brightness(mx3fb, 0); - - } - return 0; -} - -/* - * Resumes the framebuffer and unblanks the screen. Power management support - */ -static int mx3fb_resume(struct platform_device *pdev) -{ - struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); - struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; - - if (mx3_fbi->blank == FB_BLANK_UNBLANK) { - sdc_enable_channel(mx3_fbi); - sdc_set_brightness(mx3fb, mx3fb->backlight_level); - } - - console_lock(); - fb_set_suspend(mx3fb->fbi, 0); - console_unlock(); - - return 0; -} -#else -#define mx3fb_suspend NULL -#define mx3fb_resume NULL -#endif - -/* - * Main framebuffer functions - */ - -/** - * mx3fb_map_video_memory() - allocates the DRAM memory for the frame buffer. - * @fbi: framebuffer information pointer - * @mem_len: length of mapped memory - * @lock: do not lock during initialisation - * @return: Error code indicating success or failure - * - * This buffer is remapped into a non-cached, non-buffered, memory region to - * allow palette and pixel writes to occur without flushing the cache. Once this - * area is remapped, all virtual memory access to the video memory should occur - * at the new region. - */ -static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, - bool lock) -{ - int retval = 0; - dma_addr_t addr; - - fbi->screen_base = dma_alloc_wc(fbi->device, mem_len, &addr, - GFP_DMA | GFP_KERNEL); - - if (!fbi->screen_base) { - dev_err(fbi->device, "Cannot allocate %u bytes framebuffer memory\n", - mem_len); - retval = -EBUSY; - goto err0; - } - - if (lock) - mutex_lock(&fbi->mm_lock); - fbi->fix.smem_start = addr; - fbi->fix.smem_len = mem_len; - if (lock) - mutex_unlock(&fbi->mm_lock); - - dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n", - (uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len); - - fbi->screen_size = fbi->fix.smem_len; - - /* Clear the screen */ - memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); - - return 0; - -err0: - fbi->fix.smem_len = 0; - fbi->fix.smem_start = 0; - fbi->screen_base = NULL; - return retval; -} - -/** - * mx3fb_unmap_video_memory() - de-allocate frame buffer memory. - * @fbi: framebuffer information pointer - * @return: error code indicating success or failure - */ -static int mx3fb_unmap_video_memory(struct fb_info *fbi) -{ - dma_free_wc(fbi->device, fbi->fix.smem_len, fbi->screen_base, - fbi->fix.smem_start); - - fbi->screen_base = NULL; - mutex_lock(&fbi->mm_lock); - fbi->fix.smem_start = 0; - fbi->fix.smem_len = 0; - mutex_unlock(&fbi->mm_lock); - return 0; -} - -/** - * mx3fb_init_fbinfo() - initialize framebuffer information object. - * @dev: the device - * @ops: framebuffer device operations - * @return: initialized framebuffer structure. - */ -static struct fb_info *mx3fb_init_fbinfo(struct device *dev, - const struct fb_ops *ops) -{ - struct fb_info *fbi; - struct mx3fb_info *mx3fbi; - int ret; - - /* Allocate sufficient memory for the fb structure */ - fbi = framebuffer_alloc(sizeof(struct mx3fb_info), dev); - if (!fbi) - return NULL; - - mx3fbi = fbi->par; - mx3fbi->cookie = -EINVAL; - mx3fbi->cur_ipu_buf = 0; - - fbi->var.activate = FB_ACTIVATE_NOW; - - fbi->fbops = ops; - fbi->pseudo_palette = mx3fbi->pseudo_palette; - - mutex_init(&mx3fbi->mutex); - - /* Allocate colormap */ - ret = fb_alloc_cmap(&fbi->cmap, 16, 0); - if (ret < 0) { - framebuffer_release(fbi); - return NULL; - } - - return fbi; -} - -static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) -{ - struct device *dev = mx3fb->dev; - struct mx3fb_platform_data *mx3fb_pdata = dev_get_platdata(dev); - const char *name = mx3fb_pdata->name; - struct fb_info *fbi; - struct mx3fb_info *mx3fbi; - const struct fb_videomode *mode; - int ret, num_modes; - - if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) { - dev_err(dev, "Illegal display data format %d\n", - mx3fb_pdata->disp_data_fmt); - return -EINVAL; - } - - ichan->client = mx3fb; - - if (ichan->dma_chan.chan_id != IDMAC_SDC_0) - return -EINVAL; - - fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops); - if (!fbi) - return -ENOMEM; - - if (!fb_mode) - fb_mode = name; - - if (!fb_mode) { - ret = -EINVAL; - goto emode; - } - - if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) { - mode = mx3fb_pdata->mode; - num_modes = mx3fb_pdata->num_modes; - } else { - mode = mx3fb_modedb; - num_modes = ARRAY_SIZE(mx3fb_modedb); - } - - if (!fb_find_mode(&fbi->var, fbi, fb_mode, mode, - num_modes, NULL, default_bpp)) { - ret = -EBUSY; - goto emode; - } - - fb_videomode_to_modelist(mode, num_modes, &fbi->modelist); - - /* Default Y virtual size is 2x panel size */ - fbi->var.yres_virtual = fbi->var.yres * 2; - - mx3fb->fbi = fbi; - - /* set Display Interface clock period */ - mx3fb_write_reg(mx3fb, 0x00100010L, DI_HSP_CLK_PER); - /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ - - sdc_set_brightness(mx3fb, 255); - sdc_set_global_alpha(mx3fb, true, 0xFF); - sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0); - - mx3fbi = fbi->par; - mx3fbi->idmac_channel = ichan; - mx3fbi->ipu_ch = ichan->dma_chan.chan_id; - mx3fbi->mx3fb = mx3fb; - mx3fbi->blank = FB_BLANK_NORMAL; - - mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt; - - init_completion(&mx3fbi->flip_cmpl); - disable_irq(ichan->eof_irq); - dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); - ret = __set_par(fbi, false); - if (ret < 0) - goto esetpar; - - __blank(FB_BLANK_UNBLANK, fbi); - - dev_info(dev, "registered, using mode %s\n", fb_mode); - - ret = register_framebuffer(fbi); - if (ret < 0) - goto erfb; - - return 0; - -erfb: -esetpar: -emode: - fb_dealloc_cmap(&fbi->cmap); - framebuffer_release(fbi); - - return ret; -} - -static bool chan_filter(struct dma_chan *chan, void *arg) -{ - struct dma_chan_request *rq = arg; - struct device *dev; - struct mx3fb_platform_data *mx3fb_pdata; - - if (!imx_dma_is_ipu(chan)) - return false; - - if (!rq) - return false; - - dev = rq->mx3fb->dev; - mx3fb_pdata = dev_get_platdata(dev); - - return rq->id == chan->chan_id && - mx3fb_pdata->dma_dev == chan->device->dev; -} - -static void release_fbi(struct fb_info *fbi) -{ - mx3fb_unmap_video_memory(fbi); - - fb_dealloc_cmap(&fbi->cmap); - - unregister_framebuffer(fbi); - framebuffer_release(fbi); -} - -static int mx3fb_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int ret; - struct resource *sdc_reg; - struct mx3fb_data *mx3fb; - dma_cap_mask_t mask; - struct dma_chan *chan; - struct dma_chan_request rq; - - /* - * Display Interface (DI) and Synchronous Display Controller (SDC) - * registers - */ - sdc_reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!sdc_reg) - return -EINVAL; - - mx3fb = devm_kzalloc(&pdev->dev, sizeof(*mx3fb), GFP_KERNEL); - if (!mx3fb) - return -ENOMEM; - - spin_lock_init(&mx3fb->lock); - - mx3fb->reg_base = ioremap(sdc_reg->start, resource_size(sdc_reg)); - if (!mx3fb->reg_base) { - ret = -ENOMEM; - goto eremap; - } - - pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); - - /* IDMAC interface */ - dmaengine_get(); - - mx3fb->dev = dev; - platform_set_drvdata(pdev, mx3fb); - - rq.mx3fb = mx3fb; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - rq.id = IDMAC_SDC_0; - chan = dma_request_channel(mask, chan_filter, &rq); - if (!chan) { - ret = -EBUSY; - goto ersdc0; - } - - mx3fb->backlight_level = 255; - - ret = init_fb_chan(mx3fb, to_idmac_chan(chan)); - if (ret < 0) - goto eisdc0; - - mx3fb_init_backlight(mx3fb); - - return 0; - -eisdc0: - dma_release_channel(chan); -ersdc0: - dmaengine_put(); - iounmap(mx3fb->reg_base); -eremap: - dev_err(dev, "mx3fb: failed to register fb\n"); - return ret; -} - -static void mx3fb_remove(struct platform_device *dev) -{ - struct mx3fb_data *mx3fb = platform_get_drvdata(dev); - struct fb_info *fbi = mx3fb->fbi; - struct mx3fb_info *mx3_fbi = fbi->par; - struct dma_chan *chan; - - chan = &mx3_fbi->idmac_channel->dma_chan; - release_fbi(fbi); - - mx3fb_exit_backlight(mx3fb); - - dma_release_channel(chan); - dmaengine_put(); - - iounmap(mx3fb->reg_base); -} - -static struct platform_driver mx3fb_driver = { - .driver = { - .name = MX3FB_NAME, - }, - .probe = mx3fb_probe, - .remove_new = mx3fb_remove, - .suspend = mx3fb_suspend, - .resume = mx3fb_resume, -}; - -/* - * Parse user specified options (`video=mx3fb:') - * example: - * video=mx3fb:bpp=16 - */ -static int __init mx3fb_setup(void) -{ -#ifndef MODULE - char *opt, *options = NULL; - - if (fb_get_options("mx3fb", &options)) - return -ENODEV; - - if (!options || !*options) - return 0; - - while ((opt = strsep(&options, ",")) != NULL) { - if (!*opt) - continue; - if (!strncmp(opt, "bpp=", 4)) - default_bpp = simple_strtoul(opt + 4, NULL, 0); - else - fb_mode = opt; - } -#endif - - return 0; -} - -static int __init mx3fb_init(void) -{ - int ret = mx3fb_setup(); - - if (ret < 0) - return ret; - - ret = platform_driver_register(&mx3fb_driver); - return ret; -} - -static void __exit mx3fb_exit(void) -{ - platform_driver_unregister(&mx3fb_driver); -} - -module_init(mx3fb_init); -module_exit(mx3fb_exit); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("MX3 framebuffer driver"); -MODULE_ALIAS("platform:" MX3FB_NAME); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c index d2f622b4c372..b58b11015c0c 100644 --- a/drivers/video/fbdev/neofb.c +++ b/drivers/video/fbdev/neofb.c @@ -1948,49 +1948,40 @@ static struct fb_info *neo_alloc_fb_info(struct pci_dev *dev, switch (info->fix.accel) { case FB_ACCEL_NEOMAGIC_NM2070: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 128"); + strscpy(info->fix.id, "MagicGraph128", sizeof(info->fix.id)); break; case FB_ACCEL_NEOMAGIC_NM2090: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 128V"); + strscpy(info->fix.id, "MagicGraph128V", sizeof(info->fix.id)); break; case FB_ACCEL_NEOMAGIC_NM2093: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 128ZV"); + strscpy(info->fix.id, "MagicGraph128ZV", sizeof(info->fix.id)); break; case FB_ACCEL_NEOMAGIC_NM2097: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 128ZV+"); + strscpy(info->fix.id, "Mag.Graph128ZV+", sizeof(info->fix.id)); break; case FB_ACCEL_NEOMAGIC_NM2160: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 128XD"); + strscpy(info->fix.id, "MagicGraph128XD", sizeof(info->fix.id)); break; case FB_ACCEL_NEOMAGIC_NM2200: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 256AV"); + strscpy(info->fix.id, "MagicGraph256AV", sizeof(info->fix.id)); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2230: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 256AV+"); + strscpy(info->fix.id, "Mag.Graph256AV+", sizeof(info->fix.id)); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2360: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 256ZX"); + strscpy(info->fix.id, "MagicGraph256ZX", sizeof(info->fix.id)); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; break; case FB_ACCEL_NEOMAGIC_NM2380: - snprintf(info->fix.id, sizeof(info->fix.id), - "MagicGraph 256XL+"); + strscpy(info->fix.id, "Mag.Graph256XL+", sizeof(info->fix.id)); info->flags |= FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 86dd24022871..5ae48e36fccb 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -52,8 +52,8 @@ struct ssd1307fb_deviceinfo { u32 default_vcomh; u32 default_dclk_div; u32 default_dclk_frq; - int need_pwm; - int need_chargepump; + bool need_pwm; + bool need_chargepump; }; struct ssd1307fb_par { diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile index 7c35a282b484..73c56da8e284 100644 --- a/fs/tracefs/Makefile +++ b/fs/tracefs/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only tracefs-objs := inode.o +tracefs-objs += event_inode.o obj-$(CONFIG_TRACING) += tracefs.o diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c new file mode 100644 index 000000000000..237c6f370ad9 --- /dev/null +++ b/fs/tracefs/event_inode.c @@ -0,0 +1,807 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * event_inode.c - part of tracefs, a pseudo file system for activating tracing + * + * Copyright (C) 2020-23 VMware Inc, author: Steven Rostedt (VMware) + * Copyright (C) 2020-23 VMware Inc, author: Ajay Kaher + * + * eventfs is used to dynamically create inodes and dentries based on the + * meta data provided by the tracing system. + * + * eventfs stores the meta-data of files/dirs and holds off on creating + * inodes/dentries of the files. When accessed, the eventfs will create the + * inodes/dentries in a just-in-time (JIT) manner. The eventfs will clean up + * and delete the inodes/dentries when they are no longer referenced. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +struct eventfs_inode { + struct list_head e_top_files; +}; + +/* + * struct eventfs_file - hold the properties of the eventfs files and + * directories. + * @name: the name of the file or directory to create + * @d_parent: holds parent's dentry + * @dentry: once accessed holds dentry + * @list: file or directory to be added to parent directory + * @ei: list of files and directories within directory + * @fop: file_operations for file or directory + * @iop: inode_operations for file or directory + * @data: something that the caller will want to get to later on + * @mode: the permission that the file or directory should have + */ +struct eventfs_file { + const char *name; + struct dentry *d_parent; + struct dentry *dentry; + struct list_head list; + struct eventfs_inode *ei; + const struct file_operations *fop; + const struct inode_operations *iop; + /* + * Union - used for deletion + * @del_list: list of eventfs_file to delete + * @rcu: eventfs_file to delete in RCU + * @is_freed: node is freed if one of the above is set + */ + union { + struct list_head del_list; + struct rcu_head rcu; + unsigned long is_freed; + }; + void *data; + umode_t mode; +}; + +static DEFINE_MUTEX(eventfs_mutex); +DEFINE_STATIC_SRCU(eventfs_srcu); + +static struct dentry *eventfs_root_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags); +static int dcache_dir_open_wrapper(struct inode *inode, struct file *file); +static int eventfs_release(struct inode *inode, struct file *file); + +static const struct inode_operations eventfs_root_dir_inode_operations = { + .lookup = eventfs_root_lookup, +}; + +static const struct file_operations eventfs_file_operations = { + .open = dcache_dir_open_wrapper, + .read = generic_read_dir, + .iterate_shared = dcache_readdir, + .llseek = generic_file_llseek, + .release = eventfs_release, +}; + +/** + * create_file - create a file in the tracefs filesystem + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This is the basic "create a file" function for tracefs. It allows for a + * wide range of flexibility in creating a file. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +static struct dentry *create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fop) +{ + struct tracefs_inode *ti; + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + if (WARN_ON_ONCE(!S_ISREG(mode))) + return NULL; + + dentry = eventfs_start_creating(name, parent); + + if (IS_ERR(dentry)) + return dentry; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return eventfs_failed_creating(dentry); + + inode->i_mode = mode; + inode->i_fop = fop; + inode->i_private = data; + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE; + d_instantiate(dentry, inode); + fsnotify_create(dentry->d_parent->d_inode, dentry); + return eventfs_end_creating(dentry); +}; + +/** + * create_dir - create a dir in the tracefs filesystem + * @name: the name of the file to create. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * + * This is the basic "create a dir" function for eventfs. It allows for a + * wide range of flexibility in creating a dir. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +static struct dentry *create_dir(const char *name, struct dentry *parent, void *data) +{ + struct tracefs_inode *ti; + struct dentry *dentry; + struct inode *inode; + + dentry = eventfs_start_creating(name, parent); + if (IS_ERR(dentry)) + return dentry; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return eventfs_failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_fop = &eventfs_file_operations; + inode->i_private = data; + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE; + + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return eventfs_end_creating(dentry); +} + +/** + * eventfs_set_ef_status_free - set the ef->status to free + * @dentry: dentry who's status to be freed + * + * eventfs_set_ef_status_free will be called if no more + * references remain + */ +void eventfs_set_ef_status_free(struct dentry *dentry) +{ + struct tracefs_inode *ti_parent; + struct eventfs_file *ef; + + mutex_lock(&eventfs_mutex); + ti_parent = get_tracefs(dentry->d_parent->d_inode); + if (!ti_parent || !(ti_parent->flags & TRACEFS_EVENT_INODE)) + goto out; + + ef = dentry->d_fsdata; + if (!ef) + goto out; + + /* + * If ef was freed, then the LSB bit is set for d_fsdata. + * But this should not happen, as it should still have a + * ref count that prevents it. Warn in case it does. + */ + if (WARN_ON_ONCE((unsigned long)ef & 1)) + goto out; + + dentry->d_fsdata = NULL; + ef->dentry = NULL; +out: + mutex_unlock(&eventfs_mutex); +} + +/** + * eventfs_post_create_dir - post create dir routine + * @ef: eventfs_file of recently created dir + * + * Map the meta-data of files within an eventfs dir to their parent dentry + */ +static void eventfs_post_create_dir(struct eventfs_file *ef) +{ + struct eventfs_file *ef_child; + struct tracefs_inode *ti; + + /* srcu lock already held */ + /* fill parent-child relation */ + list_for_each_entry_srcu(ef_child, &ef->ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + ef_child->d_parent = ef->dentry; + } + + ti = get_tracefs(ef->dentry->d_inode); + ti->private = ef->ei; +} + +/** + * create_dentry - helper function to create dentry + * @ef: eventfs_file of file or directory to create + * @parent: parent dentry + * @lookup: true if called from lookup routine + * + * Used to create a dentry for file/dir, executes post dentry creation routine + */ +static struct dentry * +create_dentry(struct eventfs_file *ef, struct dentry *parent, bool lookup) +{ + bool invalidate = false; + struct dentry *dentry; + + mutex_lock(&eventfs_mutex); + if (ef->is_freed) { + mutex_unlock(&eventfs_mutex); + return NULL; + } + if (ef->dentry) { + dentry = ef->dentry; + /* On dir open, up the ref count */ + if (!lookup) + dget(dentry); + mutex_unlock(&eventfs_mutex); + return dentry; + } + mutex_unlock(&eventfs_mutex); + + if (!lookup) + inode_lock(parent->d_inode); + + if (ef->ei) + dentry = create_dir(ef->name, parent, ef->data); + else + dentry = create_file(ef->name, ef->mode, parent, + ef->data, ef->fop); + + if (!lookup) + inode_unlock(parent->d_inode); + + mutex_lock(&eventfs_mutex); + if (IS_ERR_OR_NULL(dentry)) { + /* If the ef was already updated get it */ + dentry = ef->dentry; + if (dentry && !lookup) + dget(dentry); + mutex_unlock(&eventfs_mutex); + return dentry; + } + + if (!ef->dentry && !ef->is_freed) { + ef->dentry = dentry; + if (ef->ei) + eventfs_post_create_dir(ef); + dentry->d_fsdata = ef; + } else { + /* A race here, should try again (unless freed) */ + invalidate = true; + + /* + * Should never happen unless we get here due to being freed. + * Otherwise it means two dentries exist with the same name. + */ + WARN_ON_ONCE(!ef->is_freed); + } + mutex_unlock(&eventfs_mutex); + if (invalidate) + d_invalidate(dentry); + + if (lookup || invalidate) + dput(dentry); + + return invalidate ? NULL : dentry; +} + +static bool match_event_file(struct eventfs_file *ef, const char *name) +{ + bool ret; + + mutex_lock(&eventfs_mutex); + ret = !ef->is_freed && strcmp(ef->name, name) == 0; + mutex_unlock(&eventfs_mutex); + + return ret; +} + +/** + * eventfs_root_lookup - lookup routine to create file/dir + * @dir: in which a lookup is being done + * @dentry: file/dir dentry + * @flags: to pass as flags parameter to simple lookup + * + * Used to create a dynamic file/dir within @dir. Use the eventfs_inode + * list of meta data to find the information needed to create the file/dir. + */ +static struct dentry *eventfs_root_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + struct dentry *ret = NULL; + int idx; + + ti = get_tracefs(dir); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return NULL; + + ei = ti->private; + idx = srcu_read_lock(&eventfs_srcu); + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + if (!match_event_file(ef, dentry->d_name.name)) + continue; + ret = simple_lookup(dir, dentry, flags); + create_dentry(ef, ef->d_parent, true); + break; + } + srcu_read_unlock(&eventfs_srcu, idx); + return ret; +} + +/** + * eventfs_release - called to release eventfs file/dir + * @inode: inode to be released + * @file: file to be released (not used) + */ +static int eventfs_release(struct inode *inode, struct file *file) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + struct dentry *dentry; + int idx; + + ti = get_tracefs(inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + ei = ti->private; + idx = srcu_read_lock(&eventfs_srcu); + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + mutex_lock(&eventfs_mutex); + dentry = ef->dentry; + mutex_unlock(&eventfs_mutex); + if (dentry) + dput(dentry); + } + srcu_read_unlock(&eventfs_srcu, idx); + return dcache_dir_close(inode, file); +} + +/** + * dcache_dir_open_wrapper - eventfs open wrapper + * @inode: not used + * @file: dir to be opened (to create its child) + * + * Used to dynamically create the file/dir within @file. @file is really a + * directory and all the files/dirs of the children within @file will be + * created. If any of the files/dirs have already been created, their + * reference count will be incremented. + */ +static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + struct dentry *dentry = file_dentry(file); + struct inode *f_inode = file_inode(file); + int idx; + + ti = get_tracefs(f_inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + ei = ti->private; + idx = srcu_read_lock(&eventfs_srcu); + list_for_each_entry_rcu(ef, &ei->e_top_files, list) { + create_dentry(ef, dentry, false); + } + srcu_read_unlock(&eventfs_srcu, idx); + return dcache_dir_open(inode, file); +} + +/** + * eventfs_prepare_ef - helper function to prepare eventfs_file + * @name: the name of the file/directory to create. + * @mode: the permission that the file should have. + * @fop: struct file_operations that should be used for this file/directory. + * @iop: struct inode_operations that should be used for this file/directory. + * @data: something that the caller will want to get to later on. The + * inode.i_private pointer will point to this value on the open() call. + * + * This function allocates and fills the eventfs_file structure. + */ +static struct eventfs_file *eventfs_prepare_ef(const char *name, umode_t mode, + const struct file_operations *fop, + const struct inode_operations *iop, + void *data) +{ + struct eventfs_file *ef; + + ef = kzalloc(sizeof(*ef), GFP_KERNEL); + if (!ef) + return ERR_PTR(-ENOMEM); + + ef->name = kstrdup(name, GFP_KERNEL); + if (!ef->name) { + kfree(ef); + return ERR_PTR(-ENOMEM); + } + + if (S_ISDIR(mode)) { + ef->ei = kzalloc(sizeof(*ef->ei), GFP_KERNEL); + if (!ef->ei) { + kfree(ef->name); + kfree(ef); + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&ef->ei->e_top_files); + } else { + ef->ei = NULL; + } + + ef->iop = iop; + ef->fop = fop; + ef->mode = mode; + ef->data = data; + return ef; +} + +/** + * eventfs_create_events_dir - create the trace event structure + * @name: the name of the directory to create. + * @parent: parent dentry for this file. This should be a directory dentry + * if set. If this parameter is NULL, then the directory will be + * created in the root of the tracefs filesystem. + * + * This function creates the top of the trace event directory. + */ +struct dentry *eventfs_create_events_dir(const char *name, + struct dentry *parent) +{ + struct dentry *dentry = tracefs_start_creating(name, parent); + struct eventfs_inode *ei; + struct tracefs_inode *ti; + struct inode *inode; + + if (IS_ERR(dentry)) + return dentry; + + ei = kzalloc(sizeof(*ei), GFP_KERNEL); + if (!ei) + return ERR_PTR(-ENOMEM); + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) { + kfree(ei); + tracefs_failed_creating(dentry); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&ei->e_top_files); + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE; + ti->private = ei; + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_fop = &eventfs_file_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return tracefs_end_creating(dentry); +} + +/** + * eventfs_add_subsystem_dir - add eventfs subsystem_dir to list to create later + * @name: the name of the file to create. + * @parent: parent dentry for this dir. + * + * This function adds eventfs subsystem dir to list. + * And all these dirs are created on the fly when they are looked up, + * and the dentry and inodes will be removed when they are done. + */ +struct eventfs_file *eventfs_add_subsystem_dir(const char *name, + struct dentry *parent) +{ + struct tracefs_inode *ti_parent; + struct eventfs_inode *ei_parent; + struct eventfs_file *ef; + + if (!parent) + return ERR_PTR(-EINVAL); + + ti_parent = get_tracefs(parent->d_inode); + ei_parent = ti_parent->private; + + ef = eventfs_prepare_ef(name, S_IFDIR, NULL, NULL, NULL); + if (IS_ERR(ef)) + return ef; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ei_parent->e_top_files); + ef->d_parent = parent; + mutex_unlock(&eventfs_mutex); + return ef; +} + +/** + * eventfs_add_dir - add eventfs dir to list to create later + * @name: the name of the file to create. + * @ef_parent: parent eventfs_file for this dir. + * + * This function adds eventfs dir to list. + * And all these dirs are created on the fly when they are looked up, + * and the dentry and inodes will be removed when they are done. + */ +struct eventfs_file *eventfs_add_dir(const char *name, + struct eventfs_file *ef_parent) +{ + struct eventfs_file *ef; + + if (!ef_parent) + return ERR_PTR(-EINVAL); + + ef = eventfs_prepare_ef(name, S_IFDIR, NULL, NULL, NULL); + if (IS_ERR(ef)) + return ef; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ef_parent->ei->e_top_files); + ef->d_parent = ef_parent->dentry; + mutex_unlock(&eventfs_mutex); + return ef; +} + +/** + * eventfs_add_events_file - add the data needed to create a file for later reference + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This function is used to add the information needed to create a + * dentry/inode within the top level events directory. The file created + * will have the @mode permissions. The @data will be used to fill the + * inode.i_private when the open() call is done. The dentry and inodes are + * all created when they are referenced, and removed when they are no + * longer referenced. + */ +int eventfs_add_events_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fop) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + + if (!parent) + return -EINVAL; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + if (!parent->d_inode) + return -EINVAL; + + ti = get_tracefs(parent->d_inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + ei = ti->private; + ef = eventfs_prepare_ef(name, mode, fop, NULL, data); + + if (IS_ERR(ef)) + return -ENOMEM; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ei->e_top_files); + ef->d_parent = parent; + mutex_unlock(&eventfs_mutex); + return 0; +} + +/** + * eventfs_add_file - add eventfs file to list to create later + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @ef_parent: parent eventfs_file for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This function is used to add the information needed to create a + * file within a subdirectory of the events directory. The file created + * will have the @mode permissions. The @data will be used to fill the + * inode.i_private when the open() call is done. The dentry and inodes are + * all created when they are referenced, and removed when they are no + * longer referenced. + */ +int eventfs_add_file(const char *name, umode_t mode, + struct eventfs_file *ef_parent, + void *data, + const struct file_operations *fop) +{ + struct eventfs_file *ef; + + if (!ef_parent) + return -EINVAL; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + ef = eventfs_prepare_ef(name, mode, fop, NULL, data); + if (IS_ERR(ef)) + return -ENOMEM; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ef_parent->ei->e_top_files); + ef->d_parent = ef_parent->dentry; + mutex_unlock(&eventfs_mutex); + return 0; +} + +static void free_ef(struct rcu_head *head) +{ + struct eventfs_file *ef = container_of(head, struct eventfs_file, rcu); + + kfree(ef->name); + kfree(ef->ei); + kfree(ef); +} + +/** + * eventfs_remove_rec - remove eventfs dir or file from list + * @ef: eventfs_file to be removed. + * @head: to create list of eventfs_file to be deleted + * @level: to check recursion depth + * + * The helper function eventfs_remove_rec() is used to clean up and free the + * associated data from eventfs for both of the added functions. + */ +static void eventfs_remove_rec(struct eventfs_file *ef, struct list_head *head, int level) +{ + struct eventfs_file *ef_child; + + if (!ef) + return; + /* + * Check recursion depth. It should never be greater than 3: + * 0 - events/ + * 1 - events/group/ + * 2 - events/group/event/ + * 3 - events/group/event/file + */ + if (WARN_ON_ONCE(level > 3)) + return; + + if (ef->ei) { + /* search for nested folders or files */ + list_for_each_entry_srcu(ef_child, &ef->ei->e_top_files, list, + lockdep_is_held(&eventfs_mutex)) { + eventfs_remove_rec(ef_child, head, level + 1); + } + } + + list_del_rcu(&ef->list); + list_add_tail(&ef->del_list, head); +} + +/** + * eventfs_remove - remove eventfs dir or file from list + * @ef: eventfs_file to be removed. + * + * This function acquire the eventfs_mutex lock and call eventfs_remove_rec() + */ +void eventfs_remove(struct eventfs_file *ef) +{ + struct eventfs_file *tmp; + LIST_HEAD(ef_del_list); + struct dentry *dentry_list = NULL; + struct dentry *dentry; + + if (!ef) + return; + + mutex_lock(&eventfs_mutex); + eventfs_remove_rec(ef, &ef_del_list, 0); + list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) { + if (ef->dentry) { + unsigned long ptr = (unsigned long)dentry_list; + + /* Keep the dentry from being freed yet */ + dget(ef->dentry); + + /* + * Paranoid: The dget() above should prevent the dentry + * from being freed and calling eventfs_set_ef_status_free(). + * But just in case, set the link list LSB pointer to 1 + * and have eventfs_set_ef_status_free() check that to + * make sure that if it does happen, it will not think + * the d_fsdata is an event_file. + * + * For this to work, no event_file should be allocated + * on a odd space, as the ef should always be allocated + * to be at least word aligned. Check for that too. + */ + WARN_ON_ONCE(ptr & 1); + + ef->dentry->d_fsdata = (void *)(ptr | 1); + dentry_list = ef->dentry; + ef->dentry = NULL; + } + call_srcu(&eventfs_srcu, &ef->rcu, free_ef); + } + mutex_unlock(&eventfs_mutex); + + while (dentry_list) { + unsigned long ptr; + + dentry = dentry_list; + ptr = (unsigned long)dentry->d_fsdata & ~1UL; + dentry_list = (struct dentry *)ptr; + dentry->d_fsdata = NULL; + d_invalidate(dentry); + mutex_lock(&eventfs_mutex); + /* dentry should now have at least a single reference */ + WARN_ONCE((int)d_count(dentry) < 1, + "dentry %p less than one reference (%d) after invalidate\n", + dentry, d_count(dentry)); + mutex_unlock(&eventfs_mutex); + dput(dentry); + } +} + +/** + * eventfs_remove_events_dir - remove eventfs dir or file from list + * @dentry: events's dentry to be removed. + * + * This function remove events main directory + */ +void eventfs_remove_events_dir(struct dentry *dentry) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + + if (!dentry || !dentry->d_inode) + return; + + ti = get_tracefs(dentry->d_inode); + if (!ti || !(ti->flags & TRACEFS_EVENT_INODE)) + return; + + ei = ti->private; + d_invalidate(dentry); + dput(dentry); + kfree(ei); +} diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 2feb6c58648c..de5b72216b1a 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -21,13 +21,33 @@ #include #include #include +#include "internal.h" #define TRACEFS_DEFAULT_MODE 0700 +static struct kmem_cache *tracefs_inode_cachep __ro_after_init; static struct vfsmount *tracefs_mount; static int tracefs_mount_count; static bool tracefs_registered; +static struct inode *tracefs_alloc_inode(struct super_block *sb) +{ + struct tracefs_inode *ti; + + ti = kmem_cache_alloc(tracefs_inode_cachep, GFP_KERNEL); + if (!ti) + return NULL; + + ti->flags = 0; + + return &ti->vfs_inode; +} + +static void tracefs_free_inode(struct inode *inode) +{ + kmem_cache_free(tracefs_inode_cachep, get_tracefs(inode)); +} + static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -127,7 +147,7 @@ static const struct inode_operations tracefs_dir_inode_operations = { .rmdir = tracefs_syscall_rmdir, }; -static struct inode *tracefs_get_inode(struct super_block *sb) +struct inode *tracefs_get_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); if (inode) { @@ -290,6 +310,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) struct tracefs_fs_info *fsi = sb->s_fs_info; struct inode *inode = d_inode(sb->s_root); struct tracefs_mount_opts *opts = &fsi->mount_opts; + umode_t tmp_mode; /* * On remount, only reset mode/uid/gid if they were provided as mount @@ -297,8 +318,9 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) */ if (!remount || opts->opts & BIT(Opt_mode)) { - inode->i_mode &= ~S_IALLUGO; - inode->i_mode |= opts->mode; + tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO; + tmp_mode |= opts->mode; + WRITE_ONCE(inode->i_mode, tmp_mode); } if (!remount || opts->opts & BIT(Opt_uid)) @@ -346,11 +368,31 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root) } static const struct super_operations tracefs_super_operations = { + .alloc_inode = tracefs_alloc_inode, + .free_inode = tracefs_free_inode, + .drop_inode = generic_delete_inode, .statfs = simple_statfs, .remount_fs = tracefs_remount, .show_options = tracefs_show_options, }; +static void tracefs_dentry_iput(struct dentry *dentry, struct inode *inode) +{ + struct tracefs_inode *ti; + + if (!dentry || !inode) + return; + + ti = get_tracefs(inode); + if (ti && ti->flags & TRACEFS_EVENT_INODE) + eventfs_set_ef_status_free(dentry); + iput(inode); +} + +static const struct dentry_operations tracefs_dentry_operations = { + .d_iput = tracefs_dentry_iput, +}; + static int trace_fill_super(struct super_block *sb, void *data, int silent) { static const struct tree_descr trace_files[] = {{""}}; @@ -373,6 +415,7 @@ static int trace_fill_super(struct super_block *sb, void *data, int silent) goto fail; sb->s_op = &tracefs_super_operations; + sb->s_d_op = &tracefs_dentry_operations; tracefs_apply_options(sb, false); @@ -399,7 +442,7 @@ static struct file_system_type trace_fs_type = { }; MODULE_ALIAS_FS("tracefs"); -static struct dentry *start_creating(const char *name, struct dentry *parent) +struct dentry *tracefs_start_creating(const char *name, struct dentry *parent) { struct dentry *dentry; int error; @@ -437,7 +480,7 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) return dentry; } -static struct dentry *failed_creating(struct dentry *dentry) +struct dentry *tracefs_failed_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); dput(dentry); @@ -445,12 +488,86 @@ static struct dentry *failed_creating(struct dentry *dentry) return NULL; } -static struct dentry *end_creating(struct dentry *dentry) +struct dentry *tracefs_end_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); return dentry; } +/** + * eventfs_start_creating - start the process of creating a dentry + * @name: Name of the file created for the dentry + * @parent: The parent dentry where this dentry will be created + * + * This is a simple helper function for the dynamically created eventfs + * files. When the directory of the eventfs files are accessed, their + * dentries are created on the fly. This function is used to start that + * process. + */ +struct dentry *eventfs_start_creating(const char *name, struct dentry *parent) +{ + struct dentry *dentry; + int error; + + error = simple_pin_fs(&trace_fs_type, &tracefs_mount, + &tracefs_mount_count); + if (error) + return ERR_PTR(error); + + /* + * If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent) + parent = tracefs_mount->mnt_root; + + if (unlikely(IS_DEADDIR(parent->d_inode))) + dentry = ERR_PTR(-ENOENT); + else + dentry = lookup_one_len(name, parent, strlen(name)); + + if (!IS_ERR(dentry) && dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-EEXIST); + } + + if (IS_ERR(dentry)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + + return dentry; +} + +/** + * eventfs_failed_creating - clean up a failed eventfs dentry creation + * @dentry: The dentry to clean up + * + * If after calling eventfs_start_creating(), a failure is detected, the + * resources created by eventfs_start_creating() needs to be cleaned up. In + * that case, this function should be called to perform that clean up. + */ +struct dentry *eventfs_failed_creating(struct dentry *dentry) +{ + dput(dentry); + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + return NULL; +} + +/** + * eventfs_end_creating - Finish the process of creating a eventfs dentry + * @dentry: The dentry that has successfully been created. + * + * This function is currently just a place holder to match + * eventfs_start_creating(). In case any synchronization needs to be added, + * this function will be used to implement that without having to modify + * the callers of eventfs_start_creating(). + */ +struct dentry *eventfs_end_creating(struct dentry *dentry) +{ + return dentry; +} + /** * tracefs_create_file - create a file in the tracefs filesystem * @name: a pointer to a string containing the name of the file to create. @@ -490,14 +607,14 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, if (!(mode & S_IFMT)) mode |= S_IFREG; BUG_ON(!S_ISREG(mode)); - dentry = start_creating(name, parent); + dentry = tracefs_start_creating(name, parent); if (IS_ERR(dentry)) return NULL; inode = tracefs_get_inode(dentry->d_sb); if (unlikely(!inode)) - return failed_creating(dentry); + return tracefs_failed_creating(dentry); inode->i_mode = mode; inode->i_fop = fops ? fops : &tracefs_file_operations; @@ -506,13 +623,13 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, inode->i_gid = d_inode(dentry->d_parent)->i_gid; d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return tracefs_end_creating(dentry); } static struct dentry *__create_dir(const char *name, struct dentry *parent, const struct inode_operations *ops) { - struct dentry *dentry = start_creating(name, parent); + struct dentry *dentry = tracefs_start_creating(name, parent); struct inode *inode; if (IS_ERR(dentry)) @@ -520,7 +637,7 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, inode = tracefs_get_inode(dentry->d_sb); if (unlikely(!inode)) - return failed_creating(dentry); + return tracefs_failed_creating(dentry); /* Do not set bits for OTH */ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; @@ -534,7 +651,7 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, d_instantiate(dentry, inode); inc_nlink(d_inode(dentry->d_parent)); fsnotify_mkdir(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return tracefs_end_creating(dentry); } /** @@ -628,10 +745,26 @@ bool tracefs_initialized(void) return tracefs_registered; } +static void init_once(void *foo) +{ + struct tracefs_inode *ti = (struct tracefs_inode *) foo; + + inode_init_once(&ti->vfs_inode); +} + static int __init tracefs_init(void) { int retval; + tracefs_inode_cachep = kmem_cache_create("tracefs_inode_cache", + sizeof(struct tracefs_inode), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD| + SLAB_ACCOUNT), + init_once); + if (!tracefs_inode_cachep) + return -ENOMEM; + retval = sysfs_create_mount_point(kernel_kobj, "tracing"); if (retval) return -EINVAL; diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h new file mode 100644 index 000000000000..69c2b1d87c46 --- /dev/null +++ b/fs/tracefs/internal.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TRACEFS_INTERNAL_H +#define _TRACEFS_INTERNAL_H + +enum { + TRACEFS_EVENT_INODE = BIT(1), +}; + +struct tracefs_inode { + unsigned long flags; + void *private; + struct inode vfs_inode; +}; + +static inline struct tracefs_inode *get_tracefs(const struct inode *inode) +{ + return container_of(inode, struct tracefs_inode, vfs_inode); +} + +struct dentry *tracefs_start_creating(const char *name, struct dentry *parent); +struct dentry *tracefs_end_creating(struct dentry *dentry); +struct dentry *tracefs_failed_creating(struct dentry *dentry); +struct inode *tracefs_get_inode(struct super_block *sb); +struct dentry *eventfs_start_creating(const char *name, struct dentry *parent); +struct dentry *eventfs_failed_creating(struct dentry *dentry); +struct dentry *eventfs_end_creating(struct dentry *dentry); +void eventfs_set_ef_status_free(struct dentry *dentry); + +#endif /* _TRACEFS_INTERNAL_H */ diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index ae20dbb885d6..f1b3151ac30b 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -341,6 +341,20 @@ struct cgroup_rstat_cpu { */ struct cgroup_base_stat last_bstat; + /* + * This field is used to record the cumulative per-cpu time of + * the cgroup and its descendants. Currently it can be read via + * eBPF/drgn etc, and we are still trying to determine how to + * expose it in the cgroupfs interface. + */ + struct cgroup_base_stat subtree_bstat; + + /* + * Snapshots at the last reading. These are used to calculate the + * deltas to propagate to the per-cpu subtree_bstat. + */ + struct cgroup_base_stat last_subtree_bstat; + /* * Child cgroups with stat updates on this cpu since the last read * are linked on the parent's ->updated_children through diff --git a/include/linux/fb.h b/include/linux/fb.h index 16c3e6d6c55d..c14576458228 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -588,7 +588,7 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, .fb_copyarea = sys_copyarea, \ .fb_imageblit = sys_imageblit -/* drivers/video/fbmem.c */ +/* fbmem.c */ extern int register_framebuffer(struct fb_info *fb_info); extern void unregister_framebuffer(struct fb_info *fb_info); extern int fb_prepare_logo(struct fb_info *fb_info, int rotate); @@ -631,7 +631,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, } } -/* drivers/video/fb_defio.c */ +/* fb_defio.c */ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma); extern int fb_deferred_io_init(struct fb_info *info); extern void fb_deferred_io_open(struct fb_info *info, @@ -734,7 +734,7 @@ extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); extern void framebuffer_release(struct fb_info *info); extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max); -/* drivers/video/fbmon.c */ +/* fbmon.c */ #define FB_MAXTIMINGS 0 #define FB_VSYNCTIMINGS 1 #define FB_HSYNCTIMINGS 2 @@ -768,7 +768,7 @@ extern int of_get_fb_videomode(struct device_node *np, extern int fb_videomode_from_videomode(const struct videomode *vm, struct fb_videomode *fbmode); -/* drivers/video/modedb.c */ +/* modedb.c */ #define VESA_MODEDB_SIZE 43 #define DMT_SIZE 0x50 @@ -794,7 +794,7 @@ extern void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num, extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs, struct list_head *head); -/* drivers/video/fbcmap.c */ +/* fbcmap.c */ extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); extern int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags); extern void fb_dealloc_cmap(struct fb_cmap *cmap); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index aad9cf8876b5..e8921871ef9a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -862,13 +862,8 @@ extern int skip_trace(unsigned long ip); extern void ftrace_module_init(struct module *mod); extern void ftrace_module_enable(struct module *mod); extern void ftrace_release_mod(struct module *mod); - -extern void ftrace_disable_daemon(void); -extern void ftrace_enable_daemon(void); #else /* CONFIG_DYNAMIC_FTRACE */ static inline int skip_trace(unsigned long ip) { return 0; } -static inline void ftrace_disable_daemon(void) { } -static inline void ftrace_enable_daemon(void) { } static inline void ftrace_module_init(struct module *mod) { } static inline void ftrace_module_enable(struct module *mod) { } static inline void ftrace_release_mod(struct module *mod) { } diff --git a/include/linux/hid.h b/include/linux/hid.h index 39e21e3815ad..964ca1f15e3f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -341,6 +341,29 @@ struct hid_item { */ #define MAX_USBHID_BOOT_QUIRKS 4 +/** + * DOC: HID quirks + * | @HID_QUIRK_NOTOUCH: + * | @HID_QUIRK_IGNORE: ignore this device + * | @HID_QUIRK_NOGET: + * | @HID_QUIRK_HIDDEV_FORCE: + * | @HID_QUIRK_BADPAD: + * | @HID_QUIRK_MULTI_INPUT: + * | @HID_QUIRK_HIDINPUT_FORCE: + * | @HID_QUIRK_ALWAYS_POLL: + * | @HID_QUIRK_INPUT_PER_APP: + * | @HID_QUIRK_X_INVERT: + * | @HID_QUIRK_Y_INVERT: + * | @HID_QUIRK_SKIP_OUTPUT_REPORTS: + * | @HID_QUIRK_SKIP_OUTPUT_REPORT_ID: + * | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP: + * | @HID_QUIRK_HAVE_SPECIAL_DRIVER: + * | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE: + * | @HID_QUIRK_FULLSPEED_INTERVAL: + * | @HID_QUIRK_NO_INIT_REPORTS: + * | @HID_QUIRK_NO_IGNORE: + * | @HID_QUIRK_NO_INPUT_SYNC: + */ /* BIT(0) reserved for backward compatibility, was HID_QUIRK_INVERT */ #define HID_QUIRK_NOTOUCH BIT(1) #define HID_QUIRK_IGNORE BIT(2) @@ -360,6 +383,7 @@ struct hid_item { #define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP BIT(18) #define HID_QUIRK_HAVE_SPECIAL_DRIVER BIT(19) #define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20) +#define HID_QUIRK_NOINVERT BIT(21) #define HID_QUIRK_FULLSPEED_INTERVAL BIT(28) #define HID_QUIRK_NO_INIT_REPORTS BIT(29) #define HID_QUIRK_NO_IGNORE BIT(30) @@ -555,9 +579,9 @@ struct hid_input { struct hid_report *report; struct input_dev *input; const char *name; - bool registered; struct list_head reports; /* the list of reports */ unsigned int application; /* application usage for this input */ + bool registered; }; enum hid_type { diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h new file mode 100644 index 000000000000..4d5da161c225 --- /dev/null +++ b/include/linux/i2c-atr.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * I2C Address Translator + * + * Copyright (c) 2019,2022 Luca Ceresoli + * Copyright (c) 2022,2023 Tomi Valkeinen + * + * Based on i2c-mux.h + */ + +#ifndef _LINUX_I2C_ATR_H +#define _LINUX_I2C_ATR_H + +#include +#include + +struct device; +struct fwnode_handle; +struct i2c_atr; + +/** + * struct i2c_atr_ops - Callbacks from ATR to the device driver. + * @attach_client: Notify the driver of a new device connected on a child + * bus, with the alias assigned to it. The driver must + * configure the hardware to use the alias. + * @detach_client: Notify the driver of a device getting disconnected. The + * driver must configure the hardware to stop using the + * alias. + * + * All these functions return 0 on success, a negative error code otherwise. + */ +struct i2c_atr_ops { + int (*attach_client)(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client, u16 alias); + void (*detach_client)(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client); +}; + +/** + * i2c_atr_new() - Allocate and initialize an I2C ATR helper. + * @parent: The parent (upstream) adapter + * @dev: The device acting as an ATR + * @ops: Driver-specific callbacks + * @max_adapters: Maximum number of child adapters + * + * The new ATR helper is connected to the parent adapter but has no child + * adapters. Call i2c_atr_add_adapter() to add some. + * + * Call i2c_atr_delete() to remove. + * + * Return: pointer to the new ATR helper object, or ERR_PTR + */ +struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, + const struct i2c_atr_ops *ops, int max_adapters); + +/** + * i2c_atr_delete - Delete an I2C ATR helper. + * @atr: I2C ATR helper to be deleted. + * + * Precondition: all the adapters added with i2c_atr_add_adapter() must be + * removed by calling i2c_atr_del_adapter(). + */ +void i2c_atr_delete(struct i2c_atr *atr); + +/** + * i2c_atr_add_adapter - Create a child ("downstream") I2C bus. + * @atr: The I2C ATR + * @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is + * passed to the callbacks in `struct i2c_atr_ops`. + * @adapter_parent: The device used as the parent of the new i2c adapter, or NULL + * to use the i2c-atr device as the parent. + * @bus_handle: The fwnode handle that points to the adapter's i2c + * peripherals, or NULL. + * + * After calling this function a new i2c bus will appear. Adding and removing + * devices on the downstream bus will result in calls to the + * &i2c_atr_ops->attach_client and &i2c_atr_ops->detach_client callbacks for the + * driver to assign an alias to the device. + * + * The adapter's fwnode is set to @bus_handle, or if @bus_handle is NULL the + * function looks for a child node whose 'reg' property matches the chan_id + * under the i2c-atr device's 'i2c-atr' node. + * + * Call i2c_atr_del_adapter() to remove the adapter. + * + * Return: 0 on success, a negative error code otherwise. + */ +int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id, + struct device *adapter_parent, + struct fwnode_handle *bus_handle); + +/** + * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by + * i2c_atr_add_adapter(). If no I2C bus has been added + * this function is a no-op. + * @atr: The I2C ATR + * @chan_id: Index of the adapter to be removed (0 .. max_adapters-1) + */ +void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id); + +/** + * i2c_atr_set_driver_data - Set private driver data to the i2c-atr instance. + * @atr: The I2C ATR + * @data: Pointer to the data to store + */ +void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data); + +/** + * i2c_atr_get_driver_data - Get the stored drive data. + * @atr: The I2C ATR + * + * Return: Pointer to the stored data + */ +void *i2c_atr_get_driver_data(struct i2c_atr *atr); + +#endif /* _LINUX_I2C_ATR_H */ diff --git a/include/linux/misc_cgroup.h b/include/linux/misc_cgroup.h index c238207d1615..e799b1f8d05b 100644 --- a/include/linux/misc_cgroup.h +++ b/include/linux/misc_cgroup.h @@ -31,17 +31,18 @@ struct misc_cg; * struct misc_res: Per cgroup per misc type resource * @max: Maximum limit on the resource. * @usage: Current usage of the resource. - * @failed: True if charged failed for the resource in a cgroup. + * @events: Number of times, the resource limit exceeded. */ struct misc_res { - unsigned long max; - atomic_long_t usage; - atomic_long_t events; + u64 max; + atomic64_t usage; + atomic64_t events; }; /** * struct misc_cg - Miscellaneous controller's cgroup structure. * @css: cgroup subsys state object. + * @events_file: Handle for the misc resources events file. * @res: Array of misc resources usage in the cgroup. */ struct misc_cg { @@ -53,12 +54,10 @@ struct misc_cg { struct misc_res res[MISC_CG_RES_TYPES]; }; -unsigned long misc_cg_res_total_usage(enum misc_res_type type); -int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity); -int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount); -void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount); +u64 misc_cg_res_total_usage(enum misc_res_type type); +int misc_cg_set_capacity(enum misc_res_type type, u64 capacity); +int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount); +void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount); /** * css_misc() - Get misc cgroup from the css. @@ -99,27 +98,26 @@ static inline void put_misc_cg(struct misc_cg *cg) #else /* !CONFIG_CGROUP_MISC */ -static inline unsigned long misc_cg_res_total_usage(enum misc_res_type type) +static inline u64 misc_cg_res_total_usage(enum misc_res_type type) { return 0; } -static inline int misc_cg_set_capacity(enum misc_res_type type, - unsigned long capacity) +static inline int misc_cg_set_capacity(enum misc_res_type type, u64 capacity) { return 0; } static inline int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { return 0; } static inline void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { } diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 75b73c83bc9d..d01351b1526f 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -30,17 +30,28 @@ struct percpu_counter { extern int percpu_counter_batch; -int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp, - struct lock_class_key *key); +int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, + gfp_t gfp, u32 nr_counters, + struct lock_class_key *key); -#define percpu_counter_init(fbc, value, gfp) \ +#define percpu_counter_init_many(fbc, value, gfp, nr_counters) \ ({ \ static struct lock_class_key __key; \ \ - __percpu_counter_init(fbc, value, gfp, &__key); \ + __percpu_counter_init_many(fbc, value, gfp, nr_counters,\ + &__key); \ }) -void percpu_counter_destroy(struct percpu_counter *fbc); + +#define percpu_counter_init(fbc, value, gfp) \ + percpu_counter_init_many(fbc, value, gfp, 1) + +void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters); +static inline void percpu_counter_destroy(struct percpu_counter *fbc) +{ + percpu_counter_destroy_many(fbc, 1); +} + void percpu_counter_set(struct percpu_counter *fbc, s64 amount); void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch); @@ -116,11 +127,27 @@ struct percpu_counter { s64 count; }; +static inline int percpu_counter_init_many(struct percpu_counter *fbc, + s64 amount, gfp_t gfp, + u32 nr_counters) +{ + u32 i; + + for (i = 0; i < nr_counters; i++) + fbc[i].count = amount; + + return 0; +} + static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp) { - fbc->count = amount; - return 0; + return percpu_counter_init_many(fbc, amount, gfp, 1); +} + +static inline void percpu_counter_destroy_many(struct percpu_counter *fbc, + u32 nr_counters) +{ } static inline void percpu_counter_destroy(struct percpu_counter *fbc) diff --git a/include/linux/platform_data/video-mx3fb.h b/include/linux/platform_data/video-mx3fb.h deleted file mode 100644 index d03dc322a616..000000000000 --- a/include/linux/platform_data/video-mx3fb.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - */ - -#ifndef __ASM_ARCH_MX3FB_H__ -#define __ASM_ARCH_MX3FB_H__ - -#include -#include - -/* Proprietary FB_SYNC_ flags */ -#define FB_SYNC_OE_ACT_HIGH 0x80000000 -#define FB_SYNC_CLK_INVERT 0x40000000 -#define FB_SYNC_DATA_INVERT 0x20000000 -#define FB_SYNC_CLK_IDLE_EN 0x10000000 -#define FB_SYNC_SHARP_MODE 0x08000000 -#define FB_SYNC_SWAP_RGB 0x04000000 -#define FB_SYNC_CLK_SEL_EN 0x02000000 - -/* - * Specify the way your display is connected. The IPU can arbitrarily - * map the internal colors to the external data lines. We only support - * the following mappings at the moment. - */ -enum disp_data_mapping { - /* blue -> d[0..5], green -> d[6..11], red -> d[12..17] */ - IPU_DISP_DATA_MAPPING_RGB666, - /* blue -> d[0..4], green -> d[5..10], red -> d[11..15] */ - IPU_DISP_DATA_MAPPING_RGB565, - /* blue -> d[0..7], green -> d[8..15], red -> d[16..23] */ - IPU_DISP_DATA_MAPPING_RGB888, -}; - -/** - * struct mx3fb_platform_data - mx3fb platform data - * - * @dma_dev: pointer to the dma-device, used for dma-slave connection - * @mode: pointer to a platform-provided per mxc_register_fb() videomode - */ -struct mx3fb_platform_data { - struct device *dma_dev; - const char *name; - const struct fb_videomode *mode; - int num_modes; - enum disp_data_mapping disp_data_fmt; -}; - -#endif diff --git a/include/linux/string_choices.h b/include/linux/string_choices.h index 48120222b9b2..3c1091941eb8 100644 --- a/include/linux/string_choices.h +++ b/include/linux/string_choices.h @@ -30,6 +30,7 @@ static inline const char *str_read_write(bool v) { return v ? "read" : "write"; } +#define str_write_read(v) str_read_write(!(v)) static inline const char *str_on_off(bool v) { diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index c1a0a19d80fb..eb5c3add939b 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -649,6 +649,7 @@ struct trace_event_file { struct list_head list; struct trace_event_call *event_call; struct event_filter __rcu *filter; + struct eventfs_file *ef; struct dentry *dir; struct trace_array *tr; struct trace_subsystem_dir *system; @@ -824,6 +825,7 @@ enum { FILTER_RDYN_STRING, FILTER_PTR_STRING, FILTER_TRACE_FN, + FILTER_CPUMASK, FILTER_COMM, FILTER_CPU, FILTER_STACKTRACE, diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h index 99912445974c..009072792fa3 100644 --- a/include/linux/tracefs.h +++ b/include/linux/tracefs.h @@ -21,6 +21,29 @@ struct file_operations; #ifdef CONFIG_TRACING +struct eventfs_file; + +struct dentry *eventfs_create_events_dir(const char *name, + struct dentry *parent); + +struct eventfs_file *eventfs_add_subsystem_dir(const char *name, + struct dentry *parent); + +struct eventfs_file *eventfs_add_dir(const char *name, + struct eventfs_file *ef_parent); + +int eventfs_add_file(const char *name, umode_t mode, + struct eventfs_file *ef_parent, void *data, + const struct file_operations *fops); + +int eventfs_add_events_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +void eventfs_remove(struct eventfs_file *ef); + +void eventfs_remove_events_dir(struct dentry *dentry); + struct dentry *tracefs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 683efe29fa69..1c1d06804d45 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -125,6 +125,17 @@ struct rcu_work { struct workqueue_struct *wq; }; +enum wq_affn_scope { + WQ_AFFN_DFL, /* use system default */ + WQ_AFFN_CPU, /* one pod per CPU */ + WQ_AFFN_SMT, /* one pod poer SMT */ + WQ_AFFN_CACHE, /* one pod per LLC */ + WQ_AFFN_NUMA, /* one pod per NUMA node */ + WQ_AFFN_SYSTEM, /* one pod across the whole system */ + + WQ_AFFN_NR_TYPES, +}; + /** * struct workqueue_attrs - A struct for workqueue attributes. * @@ -138,17 +149,58 @@ struct workqueue_attrs { /** * @cpumask: allowed CPUs + * + * Work items in this workqueue are affine to these CPUs and not allowed + * to execute on other CPUs. A pool serving a workqueue must have the + * same @cpumask. */ cpumask_var_t cpumask; /** - * @no_numa: disable NUMA affinity + * @__pod_cpumask: internal attribute used to create per-pod pools * - * Unlike other fields, ``no_numa`` isn't a property of a worker_pool. It - * only modifies how :c:func:`apply_workqueue_attrs` select pools and thus - * doesn't participate in pool hash calculations or equality comparisons. + * Internal use only. + * + * Per-pod unbound worker pools are used to improve locality. Always a + * subset of ->cpumask. A workqueue can be associated with multiple + * worker pools with disjoint @__pod_cpumask's. Whether the enforcement + * of a pool's @__pod_cpumask is strict depends on @affn_strict. */ - bool no_numa; + cpumask_var_t __pod_cpumask; + + /** + * @affn_strict: affinity scope is strict + * + * If clear, workqueue will make a best-effort attempt at starting the + * worker inside @__pod_cpumask but the scheduler is free to migrate it + * outside. + * + * If set, workers are only allowed to run inside @__pod_cpumask. + */ + bool affn_strict; + + /* + * Below fields aren't properties of a worker_pool. They only modify how + * :c:func:`apply_workqueue_attrs` select pools and thus don't + * participate in pool hash calculations or equality comparisons. + */ + + /** + * @affn_scope: unbound CPU affinity scope + * + * CPU pods are used to improve execution locality of unbound work + * items. There are multiple pod types, one for each wq_affn_scope, and + * every CPU in the system belongs to one pod in every pod type. CPUs + * that belong to the same pod share the worker pool. For example, + * selecting %WQ_AFFN_NUMA makes the workqueue use a separate worker + * pool for each NUMA node. + */ + enum wq_affn_scope affn_scope; + + /** + * @ordered: work items must be executed one by one in queueing order + */ + bool ordered; }; static inline struct delayed_work *to_delayed_work(struct work_struct *work) @@ -343,14 +395,10 @@ enum { __WQ_ORDERED_EXPLICIT = 1 << 19, /* internal: alloc_ordered_workqueue() */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ - WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ + WQ_UNBOUND_MAX_ACTIVE = WQ_MAX_ACTIVE, WQ_DFL_ACTIVE = WQ_MAX_ACTIVE / 2, }; -/* unbound wq's aren't per-cpu, scale max_active according to #cpus */ -#define WQ_UNBOUND_MAX_ACTIVE \ - max_t(int, WQ_MAX_ACTIVE, num_possible_cpus() * WQ_MAX_UNBOUND_PER_CPU) - /* * System-wide workqueues which are always present. * @@ -391,7 +439,7 @@ extern struct workqueue_struct *system_freezable_power_efficient_wq; * alloc_workqueue - allocate a workqueue * @fmt: printf format for the name of the workqueue * @flags: WQ_* flags - * @max_active: max in-flight work items, 0 for default + * @max_active: max in-flight work items per CPU, 0 for default * remaining args: args for @fmt * * Allocate a workqueue with the specified parameters. For detailed @@ -569,6 +617,7 @@ static inline bool schedule_work(struct work_struct *work) /* * Detect attempt to flush system-wide workqueues at compile time when possible. + * Warn attempt to flush system-wide workqueues at runtime. * * See https://lkml.kernel.org/r/49925af7-78a8-a3dd-bce6-cfc02e1a9236@I-love.SAKURA.ne.jp * for reasons and steps for converting system-wide workqueues into local workqueues. @@ -576,52 +625,13 @@ static inline bool schedule_work(struct work_struct *work) extern void __warn_flushing_systemwide_wq(void) __compiletime_warning("Please avoid flushing system-wide workqueues."); -/** - * flush_scheduled_work - ensure that any scheduled work has run to completion. - * - * Forces execution of the kernel-global workqueue and blocks until its - * completion. - * - * It's very easy to get into trouble if you don't take great care. - * Either of the following situations will lead to deadlock: - * - * One of the work items currently on the workqueue needs to acquire - * a lock held by your code or its caller. - * - * Your code is running in the context of a work routine. - * - * They will be detected by lockdep when they occur, but the first might not - * occur very often. It depends on what work items are on the workqueue and - * what locks they need, which you have no control over. - * - * In most situations flushing the entire workqueue is overkill; you merely - * need to know that a particular work item isn't queued and isn't running. - * In such cases you should use cancel_delayed_work_sync() or - * cancel_work_sync() instead. - * - * Please stop calling this function! A conversion to stop flushing system-wide - * workqueues is in progress. This function will be removed after all in-tree - * users stopped calling this function. - */ -/* - * The background of commit 771c035372a036f8 ("deprecate the - * '__deprecated' attribute warnings entirely and for good") is that, - * since Linus builds all modules between every single pull he does, - * the standard kernel build needs to be _clean_ in order to be able to - * notice when new problems happen. Therefore, don't emit warning while - * there are in-tree users. - */ +/* Please stop using this function, for this function will be removed in near future. */ #define flush_scheduled_work() \ ({ \ - if (0) \ - __warn_flushing_systemwide_wq(); \ + __warn_flushing_systemwide_wq(); \ __flush_workqueue(system_wq); \ }) -/* - * Although there is no longer in-tree caller, for now just emit warning - * in order to give out-of-tree callers time to update. - */ #define flush_workqueue(wq) \ ({ \ struct workqueue_struct *_wq = (wq); \ @@ -714,5 +724,6 @@ int workqueue_offline_cpu(unsigned int cpu); void __init workqueue_init_early(void); void __init workqueue_init(void); +void __init workqueue_init_topology(void); #endif diff --git a/include/media/cec.h b/include/media/cec.h index abee41ae02d0..9c007f83569a 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -113,22 +113,25 @@ struct cec_fh { #define CEC_FREE_TIME_TO_USEC(ft) ((ft) * 2400) struct cec_adap_ops { - /* Low-level callbacks */ + /* Low-level callbacks, called with adap->lock held */ int (*adap_enable)(struct cec_adapter *adap, bool enable); int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable); int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); - void (*adap_configured)(struct cec_adapter *adap, bool configured); + void (*adap_unconfigured)(struct cec_adapter *adap); int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg); + void (*adap_nb_transmit_canceled)(struct cec_adapter *adap, + const struct cec_msg *msg); void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); void (*adap_free)(struct cec_adapter *adap); - /* Error injection callbacks */ + /* Error injection callbacks, called without adap->lock held */ int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf); bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line); - /* High-level CEC message callback */ + /* High-level CEC message callback, called without adap->lock held */ + void (*configured)(struct cec_adapter *adap); int (*received)(struct cec_adapter *adap, struct cec_msg *msg); }; diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index d03e5c54347a..6cce1f09c721 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -72,7 +72,7 @@ struct vpif_capture_config { int i2c_adapter_id; const char *card_name; - struct v4l2_async_subdev *asd[VPIF_CAPTURE_MAX_CHANNELS]; + struct v4l2_async_connection *asd[VPIF_CAPTURE_MAX_CHANNELS]; int asd_sizes[VPIF_CAPTURE_MAX_CHANNELS]; }; #endif /* _VPIF_TYPES_H */ diff --git a/include/media/i2c/ds90ub9xx.h b/include/media/i2c/ds90ub9xx.h new file mode 100644 index 000000000000..0245198469ec --- /dev/null +++ b/include/media/i2c/ds90ub9xx.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __MEDIA_I2C_DS90UB9XX_H__ +#define __MEDIA_I2C_DS90UB9XX_H__ + +#include + +struct i2c_atr; + +/** + * struct ds90ub9xx_platform_data - platform data for FPD-Link Serializers. + * @port: Deserializer RX port for this Serializer + * @atr: I2C ATR + * @bc_rate: back-channel clock rate + */ +struct ds90ub9xx_platform_data { + u32 port; + struct i2c_atr *atr; + unsigned long bc_rate; +}; + +#endif /* __MEDIA_I2C_DS90UB9XX_H__ */ diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.h b/include/media/ipu-bridge.h similarity index 51% rename from drivers/media/pci/intel/ipu3/cio2-bridge.h rename to include/media/ipu-bridge.h index b76ed8a641e2..bdc654a45521 100644 --- a/drivers/media/pci/intel/ipu3/cio2-bridge.h +++ b/include/media/ipu-bridge.h @@ -1,25 +1,23 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Author: Dan Scally */ -#ifndef __CIO2_BRIDGE_H -#define __CIO2_BRIDGE_H +#ifndef __IPU_BRIDGE_H +#define __IPU_BRIDGE_H #include #include +#include -#include "ipu3-cio2.h" - -struct i2c_client; - -#define CIO2_HID "INT343E" -#define CIO2_MAX_LANES 4 +#define IPU_HID "INT343E" +#define IPU_MAX_LANES 4 +#define IPU_MAX_PORTS 4 #define MAX_NUM_LINK_FREQS 3 /* Values are educated guesses as we don't have a spec */ -#define CIO2_SENSOR_ROTATION_NORMAL 0 -#define CIO2_SENSOR_ROTATION_INVERTED 1 +#define IPU_SENSOR_ROTATION_NORMAL 0 +#define IPU_SENSOR_ROTATION_INVERTED 1 -#define CIO2_SENSOR_CONFIG(_HID, _NR, ...) \ - (const struct cio2_sensor_config) { \ +#define IPU_SENSOR_CONFIG(_HID, _NR, ...) \ + (const struct ipu_sensor_config) { \ .hid = _HID, \ .nr_link_freqs = _NR, \ .link_freqs = { __VA_ARGS__ } \ @@ -49,19 +47,24 @@ struct i2c_client; .name = _TYPE, \ } -enum cio2_sensor_swnodes { +enum ipu_sensor_swnodes { SWNODE_SENSOR_HID, SWNODE_SENSOR_PORT, SWNODE_SENSOR_ENDPOINT, - SWNODE_CIO2_PORT, - SWNODE_CIO2_ENDPOINT, - /* Must be last because it is optional / maybe empty */ + SWNODE_IPU_PORT, + SWNODE_IPU_ENDPOINT, + /* below are optional / maybe empty */ + SWNODE_IVSC_HID, + SWNODE_IVSC_SENSOR_PORT, + SWNODE_IVSC_SENSOR_ENDPOINT, + SWNODE_IVSC_IPU_PORT, + SWNODE_IVSC_IPU_ENDPOINT, SWNODE_VCM, SWNODE_COUNT }; /* Data representation as it is in ACPI SSDB buffer */ -struct cio2_sensor_ssdb { +struct ipu_sensor_ssdb { u8 version; u8 sku; u8 guid_csi2[16]; @@ -90,7 +93,7 @@ struct cio2_sensor_ssdb { u8 reserved2[13]; } __packed; -struct cio2_property_names { +struct ipu_property_names { char clock_frequency[16]; char rotation[9]; char orientation[12]; @@ -100,47 +103,79 @@ struct cio2_property_names { char link_frequencies[17]; }; -struct cio2_node_names { +struct ipu_node_names { char port[7]; + char ivsc_sensor_port[7]; + char ivsc_ipu_port[7]; char endpoint[11]; char remote_port[7]; + char vcm[16]; }; -struct cio2_sensor_config { +struct ipu_sensor_config { const char *hid; const u8 nr_link_freqs; const u64 link_freqs[MAX_NUM_LINK_FREQS]; }; -struct cio2_sensor { +struct ipu_sensor { /* append ssdb.link(u8) in "-%u" format as suffix of HID */ char name[ACPI_ID_LEN + 4]; struct acpi_device *adev; - struct i2c_client *vcm_i2c_client; + + struct device *csi_dev; + struct acpi_device *ivsc_adev; + char ivsc_name[ACPI_ID_LEN + 4]; /* SWNODE_COUNT + 1 for terminating NULL */ const struct software_node *group[SWNODE_COUNT + 1]; struct software_node swnodes[SWNODE_COUNT]; - struct cio2_node_names node_names; + struct ipu_node_names node_names; - struct cio2_sensor_ssdb ssdb; - struct acpi_pld_info *pld; + u8 link; + u8 lanes; + u32 mclkspeed; + u32 rotation; + enum v4l2_fwnode_orientation orientation; + const char *vcm_type; - struct cio2_property_names prop_names; + struct ipu_property_names prop_names; struct property_entry ep_properties[5]; struct property_entry dev_properties[5]; - struct property_entry cio2_properties[3]; + struct property_entry ipu_properties[3]; + struct property_entry ivsc_properties[1]; + struct property_entry ivsc_sensor_ep_properties[4]; + struct property_entry ivsc_ipu_ep_properties[4]; + struct software_node_ref_args local_ref[1]; struct software_node_ref_args remote_ref[1]; struct software_node_ref_args vcm_ref[1]; + struct software_node_ref_args ivsc_sensor_ref[1]; + struct software_node_ref_args ivsc_ipu_ref[1]; }; -struct cio2_bridge { - char cio2_node_name[ACPI_ID_LEN]; - struct software_node cio2_hid_node; +typedef int (*ipu_parse_sensor_fwnode_t)(struct acpi_device *adev, + struct ipu_sensor *sensor); + +struct ipu_bridge { + struct device *dev; + ipu_parse_sensor_fwnode_t parse_sensor_fwnode; + char ipu_node_name[ACPI_ID_LEN]; + struct software_node ipu_hid_node; u32 data_lanes[4]; unsigned int n_sensors; - struct cio2_sensor sensors[CIO2_NUM_PORTS]; + struct ipu_sensor sensors[IPU_MAX_PORTS]; }; +#if IS_ENABLED(CONFIG_IPU_BRIDGE) +int ipu_bridge_init(struct device *dev, + ipu_parse_sensor_fwnode_t parse_sensor_fwnode); +int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor); +int ipu_bridge_instantiate_vcm(struct device *sensor); +#else +/* Use a define to avoid the @parse_sensor_fwnode argument getting evaluated */ +#define ipu_bridge_init(dev, parse_sensor_fwnode) (0) +static inline int ipu_bridge_instantiate_vcm(struct device *s) { return 0; } +#endif + #endif diff --git a/include/media/ov_16bit_addr_reg_helpers.h b/include/media/ov_16bit_addr_reg_helpers.h deleted file mode 100644 index 1c60a50bd795..000000000000 --- a/include/media/ov_16bit_addr_reg_helpers.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * I2C register access helpers for Omnivision OVxxxx image sensors which expect - * a 16 bit register address in big-endian format and which have 1-3 byte - * wide registers, in big-endian format (for the higher width registers). - * - * Based on the register helpers from drivers/media/i2c/ov2680.c which is: - * Copyright (C) 2018 Linaro Ltd - */ -#ifndef __OV_16BIT_ADDR_REG_HELPERS_H -#define __OV_16BIT_ADDR_REG_HELPERS_H - -#include -#include -#include - -static inline int ov_read_reg(struct i2c_client *client, u16 reg, - unsigned int len, u32 *val) -{ - u8 addr_buf[2], data_buf[4] = { }; - struct i2c_msg msgs[2]; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -#define ov_read_reg8(s, r, v) ov_read_reg(s, r, 1, v) -#define ov_read_reg16(s, r, v) ov_read_reg(s, r, 2, v) -#define ov_read_reg24(s, r, v) ov_read_reg(s, r, 3, v) - -static inline int ov_write_reg(struct i2c_client *client, u16 reg, - unsigned int len, u32 val) -{ - u8 buf[6]; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - ret = i2c_master_send(client, buf, len + 2); - if (ret != len + 2) { - dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } - - return 0; -} - -#define ov_write_reg8(s, r, v) ov_write_reg(s, r, 1, v) -#define ov_write_reg16(s, r, v) ov_write_reg(s, r, 2, v) -#define ov_write_reg24(s, r, v) ov_write_reg(s, r, 3, v) - -static inline int ov_update_reg(struct i2c_client *client, u16 reg, u8 mask, u8 val) -{ - u32 readval; - int ret; - - ret = ov_read_reg8(client, reg, &readval); - if (ret < 0) - return ret; - - val = (readval & ~mask) | (val & mask); - - return ov_write_reg8(client, reg, val); -} - -#endif diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 25eb1d138c06..9bd326d31181 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -22,76 +22,85 @@ struct v4l2_async_notifier; * enum v4l2_async_match_type - type of asynchronous subdevice logic to be used * in order to identify a match * - * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address - * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node + * @V4L2_ASYNC_MATCH_TYPE_I2C: Match will check for I2C adapter ID and address + * @V4L2_ASYNC_MATCH_TYPE_FWNODE: Match will use firmware node * - * This enum is used by the asynchronous sub-device logic to define the + * This enum is used by the asynchronous connection logic to define the * algorithm that will be used to match an asynchronous device. */ enum v4l2_async_match_type { - V4L2_ASYNC_MATCH_I2C, - V4L2_ASYNC_MATCH_FWNODE, + V4L2_ASYNC_MATCH_TYPE_I2C, + V4L2_ASYNC_MATCH_TYPE_FWNODE, }; /** - * struct v4l2_async_subdev - sub-device descriptor, as known to a bridge + * struct v4l2_async_match_desc - async connection match information * - * @match_type: type of match that will be used - * @match: union of per-bus type matching data sets - * @match.fwnode: - * pointer to &struct fwnode_handle to be matched. - * Used if @match_type is %V4L2_ASYNC_MATCH_FWNODE. - * @match.i2c: embedded struct with I2C parameters to be matched. + * @type: type of match that will be used + * @fwnode: pointer to &struct fwnode_handle to be matched. + * Used if @match_type is %V4L2_ASYNC_MATCH_TYPE_FWNODE. + * @i2c: embedded struct with I2C parameters to be matched. * Both @match.i2c.adapter_id and @match.i2c.address * should be matched. - * Used if @match_type is %V4L2_ASYNC_MATCH_I2C. - * @match.i2c.adapter_id: + * Used if @match_type is %V4L2_ASYNC_MATCH_TYPE_I2C. + * @i2c.adapter_id: * I2C adapter ID to be matched. - * Used if @match_type is %V4L2_ASYNC_MATCH_I2C. - * @match.i2c.address: + * Used if @match_type is %V4L2_ASYNC_MATCH_TYPE_I2C. + * @i2c.address: * I2C address to be matched. - * Used if @match_type is %V4L2_ASYNC_MATCH_I2C. - * @asd_list: used to add struct v4l2_async_subdev objects to the - * master notifier @asd_list - * @list: used to link struct v4l2_async_subdev objects, waiting to be - * probed, to a notifier->waiting list - * - * When this struct is used as a member in a driver specific struct, - * the driver specific struct shall contain the &struct - * v4l2_async_subdev as its first member. + * Used if @match_type is %V4L2_ASYNC_MATCH_TYPE_I2C. */ -struct v4l2_async_subdev { - enum v4l2_async_match_type match_type; +struct v4l2_async_match_desc { + enum v4l2_async_match_type type; union { struct fwnode_handle *fwnode; struct { int adapter_id; unsigned short address; } i2c; - } match; + }; +}; - /* v4l2-async core private: not to be used by drivers */ - struct list_head list; - struct list_head asd_list; +/** + * struct v4l2_async_connection - sub-device connection descriptor, as known to + * a bridge + * + * @match: struct of match type and per-bus type matching data sets + * @notifier: the async notifier the connection is related to + * @asc_entry: used to add struct v4l2_async_connection objects to the + * notifier @waiting_list or @done_list + * @asc_subdev_entry: entry in struct v4l2_async_subdev.asc_list list + * @sd: the related sub-device + * + * When this struct is used as a member in a driver specific struct, the driver + * specific struct shall contain the &struct v4l2_async_connection as its first + * member. + */ +struct v4l2_async_connection { + struct v4l2_async_match_desc match; + struct v4l2_async_notifier *notifier; + struct list_head asc_entry; + struct list_head asc_subdev_entry; + struct v4l2_subdev *sd; }; /** * struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations - * @bound: a subdevice driver has successfully probed one of the subdevices - * @complete: All subdevices have been probed successfully. The complete + * @bound: a sub-device has been bound by the given connection + * @complete: All connections have been bound successfully. The complete * callback is only executed for the root notifier. * @unbind: a subdevice is leaving - * @destroy: the asd is about to be freed + * @destroy: the asc is about to be freed */ struct v4l2_async_notifier_operations { int (*bound)(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd); + struct v4l2_async_connection *asc); int (*complete)(struct v4l2_async_notifier *notifier); void (*unbind)(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd); - void (*destroy)(struct v4l2_async_subdev *asd); + struct v4l2_async_connection *asc); + void (*destroy)(struct v4l2_async_connection *asc); }; /** @@ -101,20 +110,31 @@ struct v4l2_async_notifier_operations { * @v4l2_dev: v4l2_device of the root notifier, NULL otherwise * @sd: sub-device that registered the notifier, NULL otherwise * @parent: parent notifier - * @asd_list: master list of struct v4l2_async_subdev - * @waiting: list of struct v4l2_async_subdev, waiting for their drivers - * @done: list of struct v4l2_subdev, already probed - * @list: member in a global list of notifiers + * @waiting_list: list of struct v4l2_async_connection, waiting for their + * drivers + * @done_list: list of struct v4l2_subdev, already probed + * @notifier_entry: member in a global list of notifiers */ struct v4l2_async_notifier { const struct v4l2_async_notifier_operations *ops; struct v4l2_device *v4l2_dev; struct v4l2_subdev *sd; struct v4l2_async_notifier *parent; - struct list_head asd_list; - struct list_head waiting; - struct list_head done; - struct list_head list; + struct list_head waiting_list; + struct list_head done_list; + struct list_head notifier_entry; +}; + +/** + * struct v4l2_async_subdev_endpoint - Entry in sub-device's fwnode list + * + * @async_subdev_endpoint_entry: An entry in async_subdev_endpoint_list of + * &struct v4l2_subdev + * @endpoint: Endpoint fwnode agains which to match the sub-device + */ +struct v4l2_async_subdev_endpoint { + struct list_head async_subdev_endpoint_entry; + struct fwnode_handle *endpoint; }; /** @@ -128,76 +148,71 @@ void v4l2_async_debug_init(struct dentry *debugfs_dir); * v4l2_async_nf_init - Initialize a notifier. * * @notifier: pointer to &struct v4l2_async_notifier + * @v4l2_dev: pointer to &struct v4l2_device * - * This function initializes the notifier @asd_list. It must be called + * This function initializes the notifier @asc_entry. It must be called * before adding a subdevice to a notifier, using one of: * v4l2_async_nf_add_fwnode_remote(), - * v4l2_async_nf_add_fwnode(), - * v4l2_async_nf_add_i2c(), - * __v4l2_async_nf_add_subdev() or - * v4l2_async_nf_parse_fwnode_endpoints(). + * v4l2_async_nf_add_fwnode() or + * v4l2_async_nf_add_i2c(). */ -void v4l2_async_nf_init(struct v4l2_async_notifier *notifier); +void v4l2_async_nf_init(struct v4l2_async_notifier *notifier, + struct v4l2_device *v4l2_dev); /** - * __v4l2_async_nf_add_subdev - Add an async subdev to the - * notifier's master asd list. + * v4l2_async_subdev_nf_init - Initialize a sub-device notifier. * * @notifier: pointer to &struct v4l2_async_notifier - * @asd: pointer to &struct v4l2_async_subdev + * @sd: pointer to &struct v4l2_subdev * - * \warning: Drivers should avoid using this function and instead use one of: - * v4l2_async_nf_add_fwnode(), - * v4l2_async_nf_add_fwnode_remote() or + * This function initializes the notifier @asc_list. It must be called + * before adding a subdevice to a notifier, using one of: + * v4l2_async_nf_add_fwnode_remote(), v4l2_async_nf_add_fwnode() or * v4l2_async_nf_add_i2c(). - * - * Call this function before registering a notifier to link the provided @asd to - * the notifiers master @asd_list. The @asd must be allocated with k*alloc() as - * it will be freed by the framework when the notifier is destroyed. */ -int __v4l2_async_nf_add_subdev(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev *asd); +void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd); -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_fwnode(struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode, - unsigned int asd_struct_size); + unsigned int asc_struct_size); /** * v4l2_async_nf_add_fwnode - Allocate and add a fwnode async - * subdev to the notifier's master asd_list. + * subdev to the notifier's master asc_list. * * @notifier: pointer to &struct v4l2_async_notifier * @fwnode: fwnode handle of the sub-device to be matched, pointer to * &struct fwnode_handle - * @type: Type of the driver's async sub-device struct. The &struct - * v4l2_async_subdev shall be the first member of the driver's async - * sub-device struct, i.e. both begin at the same memory address. + * @type: Type of the driver's async sub-device or connection struct. The + * &struct v4l2_async_connection shall be the first member of the + * driver's async struct, i.e. both begin at the same memory address. * - * Allocate a fwnode-matched asd of size asd_struct_size, and add it to the - * notifiers @asd_list. The function also gets a reference of the fwnode which + * Allocate a fwnode-matched asc of size asc_struct_size, and add it to the + * notifiers @asc_list. The function also gets a reference of the fwnode which * is released later at notifier cleanup time. */ #define v4l2_async_nf_add_fwnode(notifier, fwnode, type) \ ((type *)__v4l2_async_nf_add_fwnode(notifier, fwnode, sizeof(type))) -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_fwnode_remote(struct v4l2_async_notifier *notif, struct fwnode_handle *endpoint, - unsigned int asd_struct_size); + unsigned int asc_struct_size); /** * v4l2_async_nf_add_fwnode_remote - Allocate and add a fwnode * remote async subdev to the - * notifier's master asd_list. + * notifier's master asc_list. * * @notifier: pointer to &struct v4l2_async_notifier - * @ep: local endpoint pointing to the remote sub-device to be matched, + * @ep: local endpoint pointing to the remote connection to be matched, * pointer to &struct fwnode_handle - * @type: Type of the driver's async sub-device struct. The &struct - * v4l2_async_subdev shall be the first member of the driver's async - * sub-device struct, i.e. both begin at the same memory address. + * @type: Type of the driver's async connection struct. The &struct + * v4l2_async_connection shall be the first member of the driver's async + * connection struct, i.e. both begin at the same memory address. * * Gets the remote endpoint of a given local endpoint, set it up for fwnode - * matching and adds the async sub-device to the notifier's @asd_list. The + * matching and adds the async connection to the notifier's @asc_list. The * function also gets a reference of the fwnode which is released later at * notifier cleanup time. * @@ -207,46 +222,63 @@ __v4l2_async_nf_add_fwnode_remote(struct v4l2_async_notifier *notif, #define v4l2_async_nf_add_fwnode_remote(notifier, ep, type) \ ((type *)__v4l2_async_nf_add_fwnode_remote(notifier, ep, sizeof(type))) -struct v4l2_async_subdev * +struct v4l2_async_connection * __v4l2_async_nf_add_i2c(struct v4l2_async_notifier *notifier, int adapter_id, unsigned short address, - unsigned int asd_struct_size); + unsigned int asc_struct_size); /** * v4l2_async_nf_add_i2c - Allocate and add an i2c async - * subdev to the notifier's master asd_list. + * subdev to the notifier's master asc_list. * * @notifier: pointer to &struct v4l2_async_notifier * @adapter: I2C adapter ID to be matched - * @address: I2C address of sub-device to be matched - * @type: Type of the driver's async sub-device struct. The &struct - * v4l2_async_subdev shall be the first member of the driver's async - * sub-device struct, i.e. both begin at the same memory address. + * @address: I2C address of connection to be matched + * @type: Type of the driver's async connection struct. The &struct + * v4l2_async_connection shall be the first member of the driver's async + * connection struct, i.e. both begin at the same memory address. * * Same as v4l2_async_nf_add_fwnode() but for I2C matched - * sub-devices. + * connections. */ #define v4l2_async_nf_add_i2c(notifier, adapter, address, type) \ ((type *)__v4l2_async_nf_add_i2c(notifier, adapter, address, \ sizeof(type))) /** - * v4l2_async_nf_register - registers a subdevice asynchronous notifier + * v4l2_async_subdev_endpoint_add - Add an endpoint fwnode to async sub-device + * matching list * - * @v4l2_dev: pointer to &struct v4l2_device - * @notifier: pointer to &struct v4l2_async_notifier + * @sd: the sub-device + * @fwnode: the endpoint fwnode to match + * + * Add a fwnode to the async sub-device's matching list. This allows registering + * multiple async sub-devices from a single device. + * + * Note that calling v4l2_subdev_cleanup() as part of the sub-device's cleanup + * if endpoints have been added to the sub-device's fwnode matching list. + * + * Returns an error on failure, 0 on success. */ -int v4l2_async_nf_register(struct v4l2_device *v4l2_dev, - struct v4l2_async_notifier *notifier); +int v4l2_async_subdev_endpoint_add(struct v4l2_subdev *sd, + struct fwnode_handle *fwnode); /** - * v4l2_async_subdev_nf_register - registers a subdevice asynchronous - * notifier for a sub-device + * v4l2_async_connection_unique - return a unique &struct v4l2_async_connection + * for a sub-device + * @sd: the sub-device + * + * Return an async connection for a sub-device, when there is a single + * one only. + */ +struct v4l2_async_connection * +v4l2_async_connection_unique(struct v4l2_subdev *sd); + +/** + * v4l2_async_nf_register - registers a subdevice asynchronous notifier * - * @sd: pointer to &struct v4l2_subdev * @notifier: pointer to &struct v4l2_async_notifier */ -int v4l2_async_subdev_nf_register(struct v4l2_subdev *sd, - struct v4l2_async_notifier *notifier); +int v4l2_async_nf_register(struct v4l2_async_notifier *notifier); /** * v4l2_async_nf_unregister - unregisters a subdevice @@ -261,14 +293,10 @@ void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier); * @notifier: the notifier the resources of which are to be cleaned up * * Release memory resources related to a notifier, including the async - * sub-devices allocated for the purposes of the notifier but not the notifier + * connections allocated for the purposes of the notifier but not the notifier * itself. The user is responsible for calling this function to clean up the - * notifier after calling - * v4l2_async_nf_add_fwnode_remote(), - * v4l2_async_nf_add_fwnode(), - * v4l2_async_nf_add_i2c(), - * __v4l2_async_nf_add_subdev() or - * v4l2_async_nf_parse_fwnode_endpoints(). + * notifier after calling v4l2_async_nf_add_fwnode_remote(), + * v4l2_async_nf_add_fwnode() or v4l2_async_nf_add_i2c(). * * There is no harm from calling v4l2_async_nf_cleanup() in other * cases as long as its memory has been zeroed after it has been diff --git a/include/media/v4l2-cci.h b/include/media/v4l2-cci.h new file mode 100644 index 000000000000..0f6803e4b17e --- /dev/null +++ b/include/media/v4l2-cci.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MIPI Camera Control Interface (CCI) register access helpers. + * + * Copyright (C) 2023 Hans de Goede + */ +#ifndef _V4L2_CCI_H +#define _V4L2_CCI_H + +#include + +struct i2c_client; +struct regmap; + +/** + * struct cci_reg_sequence - An individual write from a sequence of CCI writes + * + * @reg: Register address, use CCI_REG#() macros to encode reg width + * @val: Register value + * + * Register/value pairs for sequences of writes. + */ +struct cci_reg_sequence { + u32 reg; + u64 val; +}; + +/* + * Macros to define register address with the register width encoded + * into the higher bits. + */ +#define CCI_REG_ADDR_MASK GENMASK(15, 0) +#define CCI_REG_WIDTH_SHIFT 16 +#define CCI_REG_WIDTH_MASK GENMASK(19, 16) + +#define CCI_REG8(x) ((1 << CCI_REG_WIDTH_SHIFT) | (x)) +#define CCI_REG16(x) ((2 << CCI_REG_WIDTH_SHIFT) | (x)) +#define CCI_REG24(x) ((3 << CCI_REG_WIDTH_SHIFT) | (x)) +#define CCI_REG32(x) ((4 << CCI_REG_WIDTH_SHIFT) | (x)) +#define CCI_REG64(x) ((8 << CCI_REG_WIDTH_SHIFT) | (x)) + +/** + * cci_read() - Read a value from a single CCI register + * + * @map: Register map to read from + * @reg: Register address to read, use CCI_REG#() macros to encode reg width + * @val: Pointer to store read value + * @err: Optional pointer to store errors, if a previous error is set + * then the read will be skipped + * + * Return: %0 on success or a negative error code on failure. + */ +int cci_read(struct regmap *map, u32 reg, u64 *val, int *err); + +/** + * cci_write() - Write a value to a single CCI register + * + * @map: Register map to write to + * @reg: Register address to write, use CCI_REG#() macros to encode reg width + * @val: Value to be written + * @err: Optional pointer to store errors, if a previous error is set + * then the write will be skipped + * + * Return: %0 on success or a negative error code on failure. + */ +int cci_write(struct regmap *map, u32 reg, u64 val, int *err); + +/** + * cci_update_bits() - Perform a read/modify/write cycle on + * a single CCI register + * + * @map: Register map to update + * @reg: Register address to update, use CCI_REG#() macros to encode reg width + * @mask: Bitmask to change + * @val: New value for bitmask + * @err: Optional pointer to store errors, if a previous error is set + * then the update will be skipped + * + * Note this uses read-modify-write to update the bits, atomicity with regards + * to other cci_*() register access functions is NOT guaranteed. + * + * Return: %0 on success or a negative error code on failure. + */ +int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err); + +/** + * cci_multi_reg_write() - Write multiple registers to the device + * + * @map: Register map to write to + * @regs: Array of structures containing register-address, -value pairs to be + * written, register-addresses use CCI_REG#() macros to encode reg width + * @num_regs: Number of registers to write + * @err: Optional pointer to store errors, if a previous error is set + * then the write will be skipped + * + * Write multiple registers to the device where the set of register, value + * pairs are supplied in any order, possibly not all in a single range. + * + * Use of the CCI_REG#() macros to encode reg width is mandatory. + * + * For raw lists of register-address, -value pairs with only 8 bit + * wide writes regmap_multi_reg_write() can be used instead. + * + * Return: %0 on success or a negative error code on failure. + */ +int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs, + unsigned int num_regs, int *err); + +#if IS_ENABLED(CONFIG_V4L2_CCI_I2C) +/** + * devm_cci_regmap_init_i2c() - Create regmap to use with cci_*() register + * access functions + * + * @client: i2c_client to create the regmap for + * @reg_addr_bits: register address width to use (8 or 16) + * + * Note the memory for the created regmap is devm() managed, tied to the client. + * + * Return: %0 on success or a negative error code on failure. + */ +struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client, + int reg_addr_bits); +#endif + +#endif diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 394d798f3dfa..f7c57c776589 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -21,10 +21,6 @@ #include -struct fwnode_handle; -struct v4l2_async_notifier; -struct v4l2_async_subdev; - /** * struct v4l2_fwnode_endpoint - the endpoint data structure * @base: fwnode endpoint of the v4l2_fwnode @@ -393,72 +389,6 @@ int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, int v4l2_fwnode_device_parse(struct device *dev, struct v4l2_fwnode_device_properties *props); -/** - * typedef parse_endpoint_func - Driver's callback function to be called on - * each V4L2 fwnode endpoint. - * - * @dev: pointer to &struct device - * @vep: pointer to &struct v4l2_fwnode_endpoint - * @asd: pointer to &struct v4l2_async_subdev - * - * Return: - * * %0 on success - * * %-ENOTCONN if the endpoint is to be skipped but this - * should not be considered as an error - * * %-EINVAL if the endpoint configuration is invalid - */ -typedef int (*parse_endpoint_func)(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd); - -/** - * v4l2_async_nf_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a - * device node - * @dev: the device the endpoints of which are to be parsed - * @notifier: notifier for @dev - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. - * @parse_endpoint: Driver's callback function called on each V4L2 fwnode - * endpoint. Optional. - * - * DEPRECATED! This function is deprecated. Don't use it in new drivers. - * Instead see an example in cio2_parse_firmware() function in - * drivers/media/pci/intel/ipu3/ipu3-cio2.c . - * - * Parse the fwnode endpoints of the @dev device and populate the async sub- - * devices list in the notifier. The @parse_endpoint callback function is - * called for each endpoint with the corresponding async sub-device pointer to - * let the caller initialize the driver-specific part of the async sub-device - * structure. - * - * The notifier memory shall be zeroed before this function is called on the - * notifier. - * - * This function may not be called on a registered notifier and may be called on - * a notifier only once. - * - * The &struct v4l2_fwnode_endpoint passed to the callback function - * @parse_endpoint is released once the function is finished. If there is a need - * to retain that configuration, the user needs to allocate memory for it. - * - * Any notifier populated using this function must be released with a call to - * v4l2_async_nf_cleanup() after it has been unregistered and the async - * sub-devices are no longer in use, even if the function returned an error. - * - * Return: %0 on success, including when no async sub-devices are found - * %-ENOMEM if memory allocation failed - * %-EINVAL if graph or endpoint parsing failed - * Other error codes as returned by @parse_endpoint - */ -int -v4l2_async_nf_parse_fwnode_endpoints(struct device *dev, - struct v4l2_async_notifier *notifier, - size_t asd_struct_size, - parse_endpoint_func parse_endpoint); - /* Helper macros to access the connector links. */ /** v4l2_connector_last_link - Helper macro to get the first diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b325df0d54d6..d9fca929c10b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1020,12 +1020,14 @@ struct v4l2_subdev_platform_data { * @dev: pointer to the physical device, if any * @fwnode: The fwnode_handle of the subdev, usually the same as * either dev->of_node->fwnode or dev->fwnode (whichever is non-NULL). - * @async_list: Links this subdev to a global subdev_list or @notifier->done - * list. - * @asd: Pointer to respective &struct v4l2_async_subdev. - * @notifier: Pointer to the managing notifier. + * @async_list: Links this subdev to a global subdev_list or + * @notifier->done_list list. + * @async_subdev_endpoint_list: List entry in async_subdev_endpoint_entry of + * &struct v4l2_async_subdev_endpoint. * @subdev_notifier: A sub-device notifier implicitly registered for the sub- * device using v4l2_async_register_subdev_sensor(). + * @asc_list: Async connection list, of &struct + * v4l2_async_connection.subdev_entry. * @pdata: common part of subdevice platform data * @state_lock: A pointer to a lock used for all the subdev's states, set by the * driver. This is optional. If NULL, each state instance will get @@ -1065,9 +1067,9 @@ struct v4l2_subdev { struct device *dev; struct fwnode_handle *fwnode; struct list_head async_list; - struct v4l2_async_subdev *asd; - struct v4l2_async_notifier *notifier; + struct list_head async_subdev_endpoint_list; struct v4l2_async_notifier *subdev_notifier; + struct list_head asc_list; struct v4l2_subdev_platform_data *pdata; struct mutex *state_lock; @@ -1383,8 +1385,9 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, * v4l2_subdev_cleanup() - Releases the resources allocated by the subdevice * @sd: The subdevice * - * This function will release the resources allocated in - * v4l2_subdev_init_finalize. + * Clean up a V4L2 async sub-device. Must be called for a sub-device as part of + * its release if resources have been associated with it using + * v4l2_async_subdev_endpoint_add() or v4l2_subdev_init_finalize(). */ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); @@ -1532,7 +1535,7 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, */ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing, + const struct v4l2_subdev_krouting *routing, const struct v4l2_mbus_framefmt *fmt); /** diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 1e7774ac808f..533ab92684d8 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4440,8 +4440,6 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, u32 port, const struct sockaddr *addr); int ib_device_set_netdev(struct ib_device *ib_dev, struct net_device *ndev, unsigned int port); -struct net_device *ib_device_netdev(struct ib_device *dev, u32 port); - struct ib_wq *ib_create_wq(struct ib_pd *pd, struct ib_wq_init_attr *init_attr); int ib_destroy_wq_user(struct ib_wq *wq, struct ib_udata *udata); diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 03abd30e6c8c..2b22f153ef63 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -114,27 +114,6 @@ struct iw_cm_id *iw_create_cm_id(struct ib_device *device, */ void iw_destroy_cm_id(struct iw_cm_id *cm_id); -/** - * iw_cm_bind_qp - Unbind the specified IW CM identifier and QP - * - * @cm_id: The IW CM idenfier to unbind from the QP. - * @qp: The QP - * - * This is called by the provider when destroying the QP to ensure - * that any references held by the IWCM are released. It may also - * be called by the IWCM when destroying a CM_ID to that any - * references held by the provider are released. - */ -void iw_cm_unbind_qp(struct iw_cm_id *cm_id, struct ib_qp *qp); - -/** - * iw_cm_get_qp - Return the ib_qp associated with a QPN - * - * @ib_device: The IB device - * @qpn: The queue pair number - */ -struct ib_qp *iw_cm_get_qp(struct ib_device *device, int qpn); - /** * iw_cm_listen - Listen for incoming connection requests on the * specified IW CM id. diff --git a/include/uapi/linux/cgroupstats.h b/include/uapi/linux/cgroupstats.h index aa306e4cd6c1..80b2c8594480 100644 --- a/include/uapi/linux/cgroupstats.h +++ b/include/uapi/linux/cgroupstats.h @@ -24,8 +24,6 @@ * basis. This data is shared using taskstats. * * Most of these states are derived by looking at the task->state value - * For the nr_io_wait state, a flag in the delay accounting structure - * indicates that the task is waiting on IO * * Each member is aligned to a 8 byte boundary. */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 164815adbf16..84c83ce5e9b4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -796,6 +796,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_Z16 v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */ #define V4L2_PIX_FMT_MT21C v4l2_fourcc('M', 'T', '2', '1') /* Mediatek compressed block mode */ #define V4L2_PIX_FMT_MM21 v4l2_fourcc('M', 'M', '2', '1') /* Mediatek 8-bit block mode, two non-contiguous planes */ +#define V4L2_PIX_FMT_MT2110T v4l2_fourcc('M', 'T', '2', 'T') /* Mediatek 10-bit block tile mode */ +#define V4L2_PIX_FMT_MT2110R v4l2_fourcc('M', 'T', '2', 'R') /* Mediatek 10-bit block raster mode */ #define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */ #define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */ diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index 8a2a1d4f6b29..6e7c67a0cca3 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -53,6 +53,7 @@ enum { BNXT_RE_UCNTX_CMASK_HAVE_CCTX = 0x1ULL, BNXT_RE_UCNTX_CMASK_HAVE_MODE = 0x02ULL, BNXT_RE_UCNTX_CMASK_WC_DPI_ENABLED = 0x04ULL, + BNXT_RE_UCNTX_CMASK_DBR_PACING_ENABLED = 0x08ULL, }; enum bnxt_re_wqe_mode { @@ -131,10 +132,13 @@ enum bnxt_re_shpg_offt { enum bnxt_re_objects { BNXT_RE_OBJECT_ALLOC_PAGE = (1U << UVERBS_ID_NS_SHIFT), + BNXT_RE_OBJECT_NOTIFY_DRV, }; enum bnxt_re_alloc_page_type { BNXT_RE_ALLOC_WC_PAGE = 0, + BNXT_RE_ALLOC_DBR_BAR_PAGE, + BNXT_RE_ALLOC_DBR_PAGE, }; enum bnxt_re_var_alloc_page_attrs { @@ -154,4 +158,7 @@ enum bnxt_re_alloc_page_methods { BNXT_RE_METHOD_DESTROY_PAGE, }; +enum bnxt_re_notify_drv_methods { + BNXT_RE_METHOD_NOTIFY_DRV = (1U << UVERBS_ID_NS_SHIFT), +}; #endif /* __BNXT_RE_UVERBS_ABI_H__*/ diff --git a/include/uapi/rdma/irdma-abi.h b/include/uapi/rdma/irdma-abi.h index a7085e092d34..bb18f15489e3 100644 --- a/include/uapi/rdma/irdma-abi.h +++ b/include/uapi/rdma/irdma-abi.h @@ -22,10 +22,16 @@ enum irdma_memreg_type { IRDMA_MEMREG_TYPE_CQ = 2, }; +enum { + IRDMA_ALLOC_UCTX_USE_RAW_ATTR = 1 << 0, + IRDMA_ALLOC_UCTX_MIN_HW_WQ_SIZE = 1 << 1, +}; + struct irdma_alloc_ucontext_req { __u32 rsvd32; __u8 userspace_ver; __u8 rsvd8[3]; + __aligned_u64 comp_mask; }; struct irdma_alloc_ucontext_resp { @@ -46,6 +52,9 @@ struct irdma_alloc_ucontext_resp { __u16 max_hw_sq_chunk; __u8 hw_rev; __u8 rsvd2; + __aligned_u64 comp_mask; + __u16 min_hw_wq_size; + __u8 rsvd3[6]; }; struct irdma_alloc_pd_resp { diff --git a/init/main.c b/init/main.c index b9b4621cd7e8..66c754c83971 100644 --- a/init/main.c +++ b/init/main.c @@ -1538,6 +1538,7 @@ static noinline void __init kernel_init_freeable(void) smp_init(); sched_init_smp(); + workqueue_init_topology(); padata_init(); page_alloc_init_late(); diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 53ac5d9a7f75..9beb9b4cfc43 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -432,7 +432,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos) if (l->list[mid] == pid) { index = mid; break; - } else if (l->list[mid] <= pid) + } else if (l->list[mid] < pid) index = mid + 1; else end = mid; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 103043613941..ecab9062e7b9 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -495,28 +495,6 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, return &cgrp->self; } -/** - * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem - * @cgrp: the cgroup of interest - * @ss: the subsystem of interest - * - * Find and get @cgrp's css associated with @ss. If the css doesn't exist - * or is offline, %NULL is returned. - */ -static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp, - struct cgroup_subsys *ss) -{ - struct cgroup_subsys_state *css; - - rcu_read_lock(); - css = cgroup_css(cgrp, ss); - if (css && !css_tryget_online(css)) - css = NULL; - rcu_read_unlock(); - - return css; -} - /** * cgroup_e_css_by_mask - obtain a cgroup's effective css for the specified ss * @cgrp: the cgroup of interest @@ -682,7 +660,7 @@ EXPORT_SYMBOL_GPL(of_css); * @ssid: the index of the subsystem, CGROUP_SUBSYS_COUNT after reaching the end * @cgrp: the target cgroup to iterate css's of * - * Should be called under cgroup_[tree_]mutex. + * Should be called under cgroup_mutex. */ #define for_each_css(css, ssid, cgrp) \ for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \ @@ -932,7 +910,7 @@ static void css_set_move_task(struct task_struct *task, #define CSS_SET_HASH_BITS 7 static DEFINE_HASHTABLE(css_set_table, CSS_SET_HASH_BITS); -static unsigned long css_set_hash(struct cgroup_subsys_state *css[]) +static unsigned long css_set_hash(struct cgroup_subsys_state **css) { unsigned long key = 0UL; struct cgroup_subsys *ss; @@ -1073,7 +1051,7 @@ static bool compare_css_sets(struct css_set *cset, */ static struct css_set *find_existing_css_set(struct css_set *old_cset, struct cgroup *cgrp, - struct cgroup_subsys_state *template[]) + struct cgroup_subsys_state **template) { struct cgroup_root *root = cgrp->root; struct cgroup_subsys *ss; @@ -1739,7 +1717,7 @@ static int css_populate_dir(struct cgroup_subsys_state *css) struct cftype *cfts, *failed_cfts; int ret; - if ((css->flags & CSS_VISIBLE) || !cgrp->kn) + if (css->flags & CSS_VISIBLE) return 0; if (!css->ss) { @@ -2503,7 +2481,7 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset, /* * This function may be called both before and - * after cgroup_taskset_migrate(). The two cases + * after cgroup_migrate_execute(). The two cases * can be distinguished by looking at whether @cset * has its ->mg_dst_cset set. */ @@ -3660,9 +3638,32 @@ static int cgroup_stat_show(struct seq_file *seq, void *v) return 0; } -static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, - struct cgroup *cgrp, int ssid) +#ifdef CONFIG_CGROUP_SCHED +/** + * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem + * @cgrp: the cgroup of interest + * @ss: the subsystem of interest + * + * Find and get @cgrp's css associated with @ss. If the css doesn't exist + * or is offline, %NULL is returned. + */ +static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp, + struct cgroup_subsys *ss) { + struct cgroup_subsys_state *css; + + rcu_read_lock(); + css = cgroup_css(cgrp, ss); + if (css && !css_tryget_online(css)) + css = NULL; + rcu_read_unlock(); + + return css; +} + +static int cgroup_extra_stat_show(struct seq_file *seq, int ssid) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup_subsys *ss = cgroup_subsys[ssid]; struct cgroup_subsys_state *css; int ret; @@ -3678,15 +3679,15 @@ static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, css_put(css); return ret; } +#endif static int cpu_stat_show(struct seq_file *seq, void *v) { - struct cgroup __maybe_unused *cgrp = seq_css(seq)->cgroup; int ret = 0; cgroup_base_stat_cputime_show(seq); #ifdef CONFIG_CGROUP_SCHED - ret = cgroup_extra_stat_show(seq, cgrp, cpu_cgrp_id); + ret = cgroup_extra_stat_show(seq, cpu_cgrp_id); #endif return ret; } @@ -4356,14 +4357,13 @@ static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) return ret; } -static int cgroup_rm_cftypes_locked(struct cftype *cfts) +static void cgroup_rm_cftypes_locked(struct cftype *cfts) { lockdep_assert_held(&cgroup_mutex); list_del(&cfts->node); cgroup_apply_cftypes(cfts, false); cgroup_exit_cftypes(cfts); - return 0; } /** @@ -4379,8 +4379,6 @@ static int cgroup_rm_cftypes_locked(struct cftype *cfts) */ int cgroup_rm_cftypes(struct cftype *cfts) { - int ret; - if (!cfts || cfts[0].name[0] == '\0') return 0; @@ -4388,9 +4386,9 @@ int cgroup_rm_cftypes(struct cftype *cfts) return -ENOENT; cgroup_lock(); - ret = cgroup_rm_cftypes_locked(cfts); + cgroup_rm_cftypes_locked(cfts); cgroup_unlock(); - return ret; + return 0; } /** @@ -5344,7 +5342,7 @@ static struct cftype cgroup_psi_files[] = { * RCU callback. * * 4. After the grace period, the css can be freed. Implemented in - * css_free_work_fn(). + * css_free_rwork_fn(). * * It is actually hairier because both step 2 and 4 require process context * and thus involve punting to css->destroy_work adding two additional @@ -5588,8 +5586,7 @@ err_free_css: /* * The returned cgroup is fully initialized including its control mask, but - * it isn't associated with its kernfs_node and doesn't have the control - * mask applied. + * it doesn't have the control mask applied. */ static struct cgroup *cgroup_create(struct cgroup *parent, const char *name, umode_t mode) @@ -5915,7 +5912,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * Mark @cgrp and the associated csets dead. The former prevents * further task migration and child creation by disabling - * cgroup_lock_live_group(). The latter makes the csets ignored by + * cgroup_kn_lock_live(). The latter makes the csets ignored by * the migration path. */ cgrp->self.flags &= ~CSS_ONLINE; @@ -5937,7 +5934,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) parent->nr_threaded_children--; spin_lock_irq(&css_set_lock); - for (tcgrp = cgroup_parent(cgrp); tcgrp; tcgrp = cgroup_parent(tcgrp)) { + for (tcgrp = parent; tcgrp; tcgrp = cgroup_parent(tcgrp)) { tcgrp->nr_descendants--; tcgrp->nr_dying_descendants++; /* @@ -6130,8 +6127,8 @@ int __init cgroup_init(void) continue; if (cgroup1_ssid_disabled(ssid)) - printk(KERN_INFO "Disabling %s control group subsystem in v1 mounts\n", - ss->name); + pr_info("Disabling %s control group subsystem in v1 mounts\n", + ss->name); cgrp_dfl_root.subsys_mask |= 1 << ss->id; diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index ef0564ca9d5d..fc05e0a9c1fb 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1231,7 +1231,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) /* * Percpu kthreads in top_cpuset are ignored */ - if ((task->flags & PF_KTHREAD) && kthread_is_per_cpu(task)) + if (kthread_is_per_cpu(task)) continue; cpumask_andnot(new_cpus, possible_mask, cs->subparts_cpus); } else { @@ -1256,7 +1256,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) static void compute_effective_cpumask(struct cpumask *new_cpus, struct cpuset *cs, struct cpuset *parent) { - if (parent->nr_subparts_cpus) { + if (parent->nr_subparts_cpus && is_partition_valid(cs)) { cpumask_or(new_cpus, parent->effective_cpus, parent->subparts_cpus); cpumask_and(new_cpus, new_cpus, cs->cpus_allowed); @@ -1278,6 +1278,52 @@ enum subparts_cmd { static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, int turning_on); +static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, + struct tmpmasks *tmp); + +/* + * Update partition exclusive flag + * + * Return: 0 if successful, an error code otherwise + */ +static int update_partition_exclusive(struct cpuset *cs, int new_prs) +{ + bool exclusive = (new_prs > 0); + + if (exclusive && !is_cpu_exclusive(cs)) { + if (update_flag(CS_CPU_EXCLUSIVE, cs, 1)) + return PERR_NOTEXCL; + } else if (!exclusive && is_cpu_exclusive(cs)) { + /* Turning off CS_CPU_EXCLUSIVE will not return error */ + update_flag(CS_CPU_EXCLUSIVE, cs, 0); + } + return 0; +} + +/* + * Update partition load balance flag and/or rebuild sched domain + * + * Changing load balance flag will automatically call + * rebuild_sched_domains_locked(). + */ +static void update_partition_sd_lb(struct cpuset *cs, int old_prs) +{ + int new_prs = cs->partition_root_state; + bool new_lb = (new_prs != PRS_ISOLATED); + bool rebuild_domains = (new_prs > 0) || (old_prs > 0); + + if (new_lb != !!is_sched_load_balance(cs)) { + rebuild_domains = true; + if (new_lb) + set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + else + clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + } + + if (rebuild_domains) + rebuild_sched_domains_locked(); +} + /** * update_parent_subparts_cpumask - update subparts_cpus mask of parent cpuset * @cs: The cpuset that requests change in partition root state @@ -1337,8 +1383,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, return is_partition_invalid(parent) ? PERR_INVPARENT : PERR_NOTPART; } - if ((newmask && cpumask_empty(newmask)) || - (!newmask && cpumask_empty(cs->cpus_allowed))) + if (!newmask && cpumask_empty(cs->cpus_allowed)) return PERR_CPUSEMPTY; /* @@ -1404,11 +1449,16 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, cpumask_and(tmp->addmask, newmask, parent->cpus_allowed); adding = cpumask_andnot(tmp->addmask, tmp->addmask, parent->subparts_cpus); + /* + * Empty cpumask is not allowed + */ + if (cpumask_empty(newmask)) { + part_error = PERR_CPUSEMPTY; /* * Make partition invalid if parent's effective_cpus could * become empty and there are tasks in the parent. */ - if (adding && + } else if (adding && cpumask_subset(parent->effective_cpus, tmp->addmask) && !cpumask_intersects(tmp->delmask, cpu_active_mask) && partition_is_populated(parent, cs)) { @@ -1481,14 +1531,13 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, /* * Transitioning between invalid to valid or vice versa may require - * changing CS_CPU_EXCLUSIVE and CS_SCHED_LOAD_BALANCE. + * changing CS_CPU_EXCLUSIVE. */ if (old_prs != new_prs) { - if (is_prs_invalid(old_prs) && !is_cpu_exclusive(cs) && - (update_flag(CS_CPU_EXCLUSIVE, cs, 1) < 0)) - return PERR_NOTEXCL; - if (is_prs_invalid(new_prs) && is_cpu_exclusive(cs)) - update_flag(CS_CPU_EXCLUSIVE, cs, 0); + int err = update_partition_exclusive(cs, new_prs); + + if (err) + return err; } /* @@ -1521,23 +1570,33 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, spin_unlock_irq(&callback_lock); - if (adding || deleting) + if (adding || deleting) { update_tasks_cpumask(parent, tmp->addmask); + if (parent->child_ecpus_count) + update_sibling_cpumasks(parent, cs, tmp); + } /* - * Set or clear CS_SCHED_LOAD_BALANCE when partcmd_update, if necessary. - * rebuild_sched_domains_locked() may be called. + * For partcmd_update without newmask, it is being called from + * cpuset_hotplug_workfn() where cpus_read_lock() wasn't taken. + * Update the load balance flag and scheduling domain if + * cpus_read_trylock() is successful. */ - if (old_prs != new_prs) { - if (old_prs == PRS_ISOLATED) - update_flag(CS_SCHED_LOAD_BALANCE, cs, 1); - else if (new_prs == PRS_ISOLATED) - update_flag(CS_SCHED_LOAD_BALANCE, cs, 0); + if ((cmd == partcmd_update) && !newmask && cpus_read_trylock()) { + update_partition_sd_lb(cs, old_prs); + cpus_read_unlock(); } + notify_partition_change(cs, old_prs); return 0; } +/* + * update_cpumasks_hier() flags + */ +#define HIER_CHECKALL 0x01 /* Check all cpusets with no skipping */ +#define HIER_NO_SD_REBUILD 0x02 /* Don't rebuild sched domains */ + /* * update_cpumasks_hier - Update effective cpumasks and tasks in the subtree * @cs: the cpuset to consider @@ -1552,7 +1611,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, * Called with cpuset_mutex held */ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, - bool force) + int flags) { struct cpuset *cp; struct cgroup_subsys_state *pos_css; @@ -1589,11 +1648,16 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, } /* - * Skip the whole subtree if the cpumask remains the same - * and has no partition root state and force flag not set. + * Skip the whole subtree if + * 1) the cpumask remains the same, + * 2) has no partition root state, + * 3) HIER_CHECKALL flag not set, and + * 4) for v2 load balance state same as its parent. */ - if (!cp->partition_root_state && !force && - cpumask_equal(tmp->new_cpus, cp->effective_cpus)) { + if (!cp->partition_root_state && !(flags & HIER_CHECKALL) && + cpumask_equal(tmp->new_cpus, cp->effective_cpus) && + (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) || + (is_sched_load_balance(parent) == is_sched_load_balance(cp)))) { pos_css = css_rightmost_descendant(pos_css); continue; } @@ -1676,6 +1740,20 @@ update_parent_subparts: update_tasks_cpumask(cp, tmp->new_cpus); + /* + * On default hierarchy, inherit the CS_SCHED_LOAD_BALANCE + * from parent if current cpuset isn't a valid partition root + * and their load balance states differ. + */ + if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) && + !is_partition_valid(cp) && + (is_sched_load_balance(parent) != is_sched_load_balance(cp))) { + if (is_sched_load_balance(parent)) + set_bit(CS_SCHED_LOAD_BALANCE, &cp->flags); + else + clear_bit(CS_SCHED_LOAD_BALANCE, &cp->flags); + } + /* * On legacy hierarchy, if the effective cpumask of any non- * empty cpuset is changed, we need to rebuild sched domains. @@ -1693,7 +1771,7 @@ update_parent_subparts: } rcu_read_unlock(); - if (need_rebuild_sched_domains) + if (need_rebuild_sched_domains && !(flags & HIER_NO_SD_REBUILD)) rebuild_sched_domains_locked(); } @@ -1717,7 +1795,9 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, * to use the right effective_cpus value. * * The update_cpumasks_hier() function may sleep. So we have to - * release the RCU read lock before calling it. + * release the RCU read lock before calling it. HIER_NO_SD_REBUILD + * flag is used to suppress rebuild of sched domains as the callers + * will take care of that. */ rcu_read_lock(); cpuset_for_each_child(sibling, pos_css, parent) { @@ -1729,7 +1809,7 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, continue; rcu_read_unlock(); - update_cpumasks_hier(sibling, tmp, false); + update_cpumasks_hier(sibling, tmp, HIER_NO_SD_REBUILD); rcu_read_lock(); css_put(&sibling->css); } @@ -1748,6 +1828,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, int retval; struct tmpmasks tmp; bool invalidate = false; + int old_prs = cs->partition_root_state; /* top_cpuset.cpus_allowed tracks cpu_online_mask; it's read-only */ if (cs == &top_cpuset) @@ -1775,18 +1856,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed)) return 0; -#ifdef CONFIG_CPUMASK_OFFSTACK - /* - * Use the cpumasks in trialcs for tmpmasks when they are pointers - * to allocated cpumasks. - * - * Note that update_parent_subparts_cpumask() uses only addmask & - * delmask, but not new_cpus. - */ - tmp.addmask = trialcs->subparts_cpus; - tmp.delmask = trialcs->effective_cpus; - tmp.new_cpus = NULL; -#endif + if (alloc_cpumasks(NULL, &tmp)) + return -ENOMEM; retval = validate_change(cs, trialcs); @@ -1815,7 +1886,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, retval = 0; } if (retval < 0) - return retval; + goto out_free; if (cs->partition_root_state) { if (invalidate) @@ -1850,13 +1921,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, } spin_unlock_irq(&callback_lock); -#ifdef CONFIG_CPUMASK_OFFSTACK - /* Now trialcs->cpus_allowed is available */ - tmp.new_cpus = trialcs->cpus_allowed; -#endif - /* effective_cpus will be updated here */ - update_cpumasks_hier(cs, &tmp, false); + update_cpumasks_hier(cs, &tmp, 0); if (cs->partition_root_state) { struct cpuset *parent = parent_cs(cs); @@ -1867,7 +1933,12 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, */ if (parent->child_ecpus_count) update_sibling_cpumasks(parent, cs, &tmp); + + /* Update CS_SCHED_LOAD_BALANCE and/or sched_domains */ + update_partition_sd_lb(cs, old_prs); } +out_free: + free_cpumasks(NULL, &tmp); return 0; } @@ -2243,7 +2314,6 @@ out: static int update_prstate(struct cpuset *cs, int new_prs) { int err = PERR_NONE, old_prs = cs->partition_root_state; - bool sched_domain_rebuilt = false; struct cpuset *parent = parent_cs(cs); struct tmpmasks tmpmask; @@ -2262,45 +2332,26 @@ static int update_prstate(struct cpuset *cs, int new_prs) if (alloc_cpumasks(NULL, &tmpmask)) return -ENOMEM; + err = update_partition_exclusive(cs, new_prs); + if (err) + goto out; + if (!old_prs) { /* - * Turning on partition root requires setting the - * CS_CPU_EXCLUSIVE bit implicitly as well and cpus_allowed - * cannot be empty. + * cpus_allowed cannot be empty. */ if (cpumask_empty(cs->cpus_allowed)) { err = PERR_CPUSEMPTY; goto out; } - err = update_flag(CS_CPU_EXCLUSIVE, cs, 1); - if (err) { - err = PERR_NOTEXCL; - goto out; - } - err = update_parent_subparts_cpumask(cs, partcmd_enable, NULL, &tmpmask); - if (err) { - update_flag(CS_CPU_EXCLUSIVE, cs, 0); - goto out; - } - - if (new_prs == PRS_ISOLATED) { - /* - * Disable the load balance flag should not return an - * error unless the system is running out of memory. - */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, 0); - sched_domain_rebuilt = true; - } } else if (old_prs && new_prs) { /* * A change in load balance state only, no change in cpumasks. */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, (new_prs != PRS_ISOLATED)); - sched_domain_rebuilt = true; - goto out; /* Sched domain is rebuilt in update_flag() */ + ; } else { /* * Switching back to member is always allowed even if it @@ -2319,40 +2370,31 @@ static int update_prstate(struct cpuset *cs, int new_prs) compute_effective_cpumask(cs->effective_cpus, cs, parent); spin_unlock_irq(&callback_lock); } - - /* Turning off CS_CPU_EXCLUSIVE will not return error */ - update_flag(CS_CPU_EXCLUSIVE, cs, 0); - - if (!is_sched_load_balance(cs)) { - /* Make sure load balance is on */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, 1); - sched_domain_rebuilt = true; - } } - - update_tasks_cpumask(parent, tmpmask.new_cpus); - - if (parent->child_ecpus_count) - update_sibling_cpumasks(parent, cs, &tmpmask); - - if (!sched_domain_rebuilt) - rebuild_sched_domains_locked(); out: /* - * Make partition invalid if an error happen + * Make partition invalid & disable CS_CPU_EXCLUSIVE if an error + * happens. */ - if (err) + if (err) { new_prs = -new_prs; + update_partition_exclusive(cs, new_prs); + } + spin_lock_irq(&callback_lock); cs->partition_root_state = new_prs; WRITE_ONCE(cs->prs_err, err); spin_unlock_irq(&callback_lock); + /* * Update child cpusets, if present. * Force update if switching back to member. */ if (!list_empty(&cs->css.children)) - update_cpumasks_hier(cs, &tmpmask, !new_prs); + update_cpumasks_hier(cs, &tmpmask, !new_prs ? HIER_CHECKALL : 0); + + /* Update sched domains and load balance flag */ + update_partition_sd_lb(cs, old_prs); notify_partition_change(cs, old_prs); free_cpumasks(NULL, &tmpmask); @@ -2488,6 +2530,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) struct cgroup_subsys_state *css; struct cpuset *cs, *oldcs; struct task_struct *task; + bool cpus_updated, mems_updated; int ret; /* used later by cpuset_attach() */ @@ -2502,13 +2545,25 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) if (ret) goto out_unlock; + cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus); + mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems); + cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) goto out_unlock; - ret = security_task_setscheduler(task); - if (ret) - goto out_unlock; + + /* + * Skip rights over task check in v2 when nothing changes, + * migration permission derives from hierarchy ownership in + * cgroup_procs_write_permission()). + */ + if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) || + (cpus_updated || mems_updated)) { + ret = security_task_setscheduler(task); + if (ret) + goto out_unlock; + } if (dl_task(task)) { cs->nr_migrate_dl_tasks++; @@ -3223,6 +3278,14 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) cs->use_parent_ecpus = true; parent->child_ecpus_count++; } + + /* + * For v2, clear CS_SCHED_LOAD_BALANCE if parent is isolated + */ + if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) && + !is_sched_load_balance(parent)) + clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + spin_unlock_irq(&callback_lock); if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags)) @@ -3522,17 +3585,16 @@ hotplug_update_tasks_legacy(struct cpuset *cs, is_empty = cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed); - mutex_unlock(&cpuset_mutex); - /* * Move tasks to the nearest ancestor with execution resources, * This is full cgroup operation which will also call back into * cpuset. Should be done outside any lock. */ - if (is_empty) + if (is_empty) { + mutex_unlock(&cpuset_mutex); remove_tasks_in_empty_cpuset(cs); - - mutex_lock(&cpuset_mutex); + mutex_lock(&cpuset_mutex); + } } static void @@ -3692,6 +3754,7 @@ unlock: /** * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset + * @work: unused * * This function is called after either CPU or memory configuration has * changed and updates cpuset accordingly. The top_cpuset is always @@ -4074,6 +4137,7 @@ bool cpuset_node_allowed(int node, gfp_t gfp_mask) /** * cpuset_spread_node() - On which node to begin search for a page + * @rotor: round robin rotor * * If a task is marked PF_SPREAD_PAGE or PF_SPREAD_SLAB (as for * tasks in a cpuset with is_spread_page or is_spread_slab set), diff --git a/kernel/cgroup/misc.c b/kernel/cgroup/misc.c index ae2f4dd47508..79a3717a5803 100644 --- a/kernel/cgroup/misc.c +++ b/kernel/cgroup/misc.c @@ -14,7 +14,7 @@ #include #define MAX_STR "max" -#define MAX_NUM ULONG_MAX +#define MAX_NUM U64_MAX /* Miscellaneous res name, keep it in sync with enum misc_res_type */ static const char *const misc_res_name[] = { @@ -37,7 +37,7 @@ static struct misc_cg root_cg; * more than the actual capacity. We are using Limits resource distribution * model of cgroup for miscellaneous controller. */ -static unsigned long misc_res_capacity[MISC_CG_RES_TYPES]; +static u64 misc_res_capacity[MISC_CG_RES_TYPES]; /** * parent_misc() - Get the parent of the passed misc cgroup. @@ -74,10 +74,10 @@ static inline bool valid_type(enum misc_res_type type) * Context: Any context. * Return: Current total usage of the resource. */ -unsigned long misc_cg_res_total_usage(enum misc_res_type type) +u64 misc_cg_res_total_usage(enum misc_res_type type) { if (valid_type(type)) - return atomic_long_read(&root_cg.res[type].usage); + return atomic64_read(&root_cg.res[type].usage); return 0; } @@ -95,7 +95,7 @@ EXPORT_SYMBOL_GPL(misc_cg_res_total_usage); * * %0 - Successfully registered the capacity. * * %-EINVAL - If @type is invalid. */ -int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity) +int misc_cg_set_capacity(enum misc_res_type type, u64 capacity) { if (!valid_type(type)) return -EINVAL; @@ -114,9 +114,9 @@ EXPORT_SYMBOL_GPL(misc_cg_set_capacity); * Context: Any context. */ static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { - WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage), + WARN_ONCE(atomic64_add_negative(-amount, &cg->res[type].usage), "misc cgroup resource %s became less than 0", misc_res_name[type]); } @@ -137,13 +137,12 @@ static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg, * * -EBUSY - If max limit will be crossed or total usage will be more than the * capacity. */ -int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) +int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount) { struct misc_cg *i, *j; int ret; struct misc_res *res; - int new_usage; + u64 new_usage; if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type]))) return -EINVAL; @@ -154,7 +153,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, for (i = cg; i; i = parent_misc(i)) { res = &i->res[type]; - new_usage = atomic_long_add_return(amount, &res->usage); + new_usage = atomic64_add_return(amount, &res->usage); if (new_usage > READ_ONCE(res->max) || new_usage > READ_ONCE(misc_res_capacity[type])) { ret = -EBUSY; @@ -165,7 +164,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, err_charge: for (j = i; j; j = parent_misc(j)) { - atomic_long_inc(&j->res[type].events); + atomic64_inc(&j->res[type].events); cgroup_file_notify(&j->events_file); } @@ -184,8 +183,7 @@ EXPORT_SYMBOL_GPL(misc_cg_try_charge); * * Context: Any context. */ -void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) +void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount) { struct misc_cg *i; @@ -209,7 +207,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) { int i; struct misc_cg *cg = css_misc(seq_css(sf)); - unsigned long max; + u64 max; for (i = 0; i < MISC_CG_RES_TYPES; i++) { if (READ_ONCE(misc_res_capacity[i])) { @@ -217,7 +215,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) if (max == MAX_NUM) seq_printf(sf, "%s max\n", misc_res_name[i]); else - seq_printf(sf, "%s %lu\n", misc_res_name[i], + seq_printf(sf, "%s %llu\n", misc_res_name[i], max); } } @@ -241,13 +239,13 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) * Return: * * >= 0 - Number of bytes processed in the input. * * -EINVAL - If buf is not valid. - * * -ERANGE - If number is bigger than the unsigned long capacity. + * * -ERANGE - If number is bigger than the u64 capacity. */ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct misc_cg *cg; - unsigned long max; + u64 max; int ret = 0, i; enum misc_res_type type = MISC_CG_RES_TYPES; char *token; @@ -271,7 +269,7 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, if (!strcmp(MAX_STR, buf)) { max = MAX_NUM; } else { - ret = kstrtoul(buf, 0, &max); + ret = kstrtou64(buf, 0, &max); if (ret) return ret; } @@ -297,13 +295,13 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, static int misc_cg_current_show(struct seq_file *sf, void *v) { int i; - unsigned long usage; + u64 usage; struct misc_cg *cg = css_misc(seq_css(sf)); for (i = 0; i < MISC_CG_RES_TYPES; i++) { - usage = atomic_long_read(&cg->res[i].usage); + usage = atomic64_read(&cg->res[i].usage); if (READ_ONCE(misc_res_capacity[i]) || usage) - seq_printf(sf, "%s %lu\n", misc_res_name[i], usage); + seq_printf(sf, "%s %llu\n", misc_res_name[i], usage); } return 0; @@ -322,12 +320,12 @@ static int misc_cg_current_show(struct seq_file *sf, void *v) static int misc_cg_capacity_show(struct seq_file *sf, void *v) { int i; - unsigned long cap; + u64 cap; for (i = 0; i < MISC_CG_RES_TYPES; i++) { cap = READ_ONCE(misc_res_capacity[i]); if (cap) - seq_printf(sf, "%s %lu\n", misc_res_name[i], cap); + seq_printf(sf, "%s %llu\n", misc_res_name[i], cap); } return 0; @@ -336,12 +334,13 @@ static int misc_cg_capacity_show(struct seq_file *sf, void *v) static int misc_events_show(struct seq_file *sf, void *v) { struct misc_cg *cg = css_misc(seq_css(sf)); - unsigned long events, i; + u64 events; + int i; for (i = 0; i < MISC_CG_RES_TYPES; i++) { - events = atomic_long_read(&cg->res[i].events); + events = atomic64_read(&cg->res[i].events); if (READ_ONCE(misc_res_capacity[i]) || events) - seq_printf(sf, "%s.max %lu\n", misc_res_name[i], events); + seq_printf(sf, "%s.max %llu\n", misc_res_name[i], events); } return 0; } @@ -397,7 +396,7 @@ misc_cg_alloc(struct cgroup_subsys_state *parent_css) for (i = 0; i < MISC_CG_RES_TYPES; i++) { WRITE_ONCE(cg->res[i].max, MAX_NUM); - atomic_long_set(&cg->res[i].usage, 0); + atomic64_set(&cg->res[i].usage, 0); } return &cg->css; diff --git a/kernel/cgroup/namespace.c b/kernel/cgroup/namespace.c index 0d5c29879a50..144a464e45c6 100644 --- a/kernel/cgroup/namespace.c +++ b/kernel/cgroup/namespace.c @@ -149,9 +149,3 @@ const struct proc_ns_operations cgroupns_operations = { .install = cgroupns_install, .owner = cgroupns_owner, }; - -static __init int cgroup_namespaces_init(void) -{ - return 0; -} -subsys_initcall(cgroup_namespaces_init); diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index 2542c21b6b6d..d80d7a608141 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -344,6 +344,7 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) { struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); struct cgroup *parent = cgroup_parent(cgrp); + struct cgroup_rstat_cpu *prstatc; struct cgroup_base_stat delta; unsigned seq; @@ -357,17 +358,24 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) delta = rstatc->bstat; } while (__u64_stats_fetch_retry(&rstatc->bsync, seq)); - /* propagate percpu delta to global */ + /* propagate per-cpu delta to cgroup and per-cpu global statistics */ cgroup_base_stat_sub(&delta, &rstatc->last_bstat); cgroup_base_stat_add(&cgrp->bstat, &delta); cgroup_base_stat_add(&rstatc->last_bstat, &delta); + cgroup_base_stat_add(&rstatc->subtree_bstat, &delta); - /* propagate global delta to parent (unless that's root) */ + /* propagate cgroup and per-cpu global delta to parent (unless that's root) */ if (cgroup_parent(parent)) { delta = cgrp->bstat; cgroup_base_stat_sub(&delta, &cgrp->last_bstat); cgroup_base_stat_add(&parent->bstat, &delta); cgroup_base_stat_add(&cgrp->last_bstat, &delta); + + delta = rstatc->subtree_bstat; + prstatc = cgroup_rstat_cpu(parent, cpu); + cgroup_base_stat_sub(&delta, &rstatc->last_subtree_bstat); + cgroup_base_stat_add(&prstatc->subtree_bstat, &delta); + cgroup_base_stat_add(&rstatc->last_subtree_bstat, &delta); } } diff --git a/kernel/fork.c b/kernel/fork.c index c770553f6d69..d1a46bac0745 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -917,8 +917,6 @@ static void cleanup_lazy_tlbs(struct mm_struct *mm) */ void __mmdrop(struct mm_struct *mm) { - int i; - BUG_ON(mm == &init_mm); WARN_ON_ONCE(mm == current->mm); @@ -933,9 +931,8 @@ void __mmdrop(struct mm_struct *mm) put_user_ns(mm->user_ns); mm_pasid_drop(mm); mm_destroy_cid(mm); + percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS); - for (i = 0; i < NR_MM_COUNTERS; i++) - percpu_counter_destroy(&mm->rss_stat[i]); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); @@ -1271,8 +1268,6 @@ static void mm_init_uprobes_state(struct mm_struct *mm) static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, struct user_namespace *user_ns) { - int i; - mt_init_flags(&mm->mm_mt, MM_MT_FLAGS); mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock); atomic_set(&mm->mm_users, 1); @@ -1320,17 +1315,15 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, if (mm_alloc_cid(mm)) goto fail_cid; - for (i = 0; i < NR_MM_COUNTERS; i++) - if (percpu_counter_init(&mm->rss_stat[i], 0, GFP_KERNEL_ACCOUNT)) - goto fail_pcpu; + if (percpu_counter_init_many(mm->rss_stat, 0, GFP_KERNEL_ACCOUNT, + NR_MM_COUNTERS)) + goto fail_pcpu; mm->user_ns = get_user_ns(user_ns); lru_gen_init_mm(mm); return mm; fail_pcpu: - while (i > 0) - percpu_counter_destroy(&mm->rss_stat[--i]); mm_destroy_cid(mm); fail_cid: destroy_context(mm); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 52dea5dd5362..78502d4c7214 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -692,10 +692,7 @@ static void rb_time_set(rb_time_t *t, u64 val) static inline bool rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set) { - unsigned long ret; - - ret = local_cmpxchg(l, expect, set); - return ret == expect; + return local_try_cmpxchg(l, &expect, set); } static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) @@ -752,9 +749,7 @@ static void rb_time_set(rb_time_t *t, u64 val) static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) { - u64 val; - val = local64_cmpxchg(&t->time, expect, set); - return val == expect; + return local64_try_cmpxchg(&t->time, &expect, set); } #endif @@ -1494,14 +1489,11 @@ static bool rb_head_page_replace(struct buffer_page *old, { unsigned long *ptr = (unsigned long *)&old->list.prev->next; unsigned long val; - unsigned long ret; val = *ptr & ~RB_FLAG_MASK; val |= RB_PAGE_HEAD; - ret = cmpxchg(ptr, val, (unsigned long)&new->list); - - return ret == val; + return try_cmpxchg(ptr, &val, (unsigned long)&new->list); } /* @@ -3003,7 +2995,6 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, { unsigned long new_index, old_index; struct buffer_page *bpage; - unsigned long index; unsigned long addr; u64 write_stamp; u64 delta; @@ -3060,8 +3051,9 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, */ old_index += write_mask; new_index += write_mask; - index = local_cmpxchg(&bpage->write, old_index, new_index); - if (index == old_index) { + + /* caution: old_index gets updated on cmpxchg failure */ + if (local_try_cmpxchg(&bpage->write, &old_index, new_index)) { /* update counters */ local_sub(event_length, &cpu_buffer->entries_bytes); return true; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8e64aaad5361..3e55375f47e0 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3119,7 +3119,6 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer, struct ftrace_stack *fstack; struct stack_entry *entry; int stackidx; - void *ptr; /* * Add one, for this function and the call to save_stack_trace() @@ -3157,32 +3156,16 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer, nr_entries = stack_trace_save(fstack->calls, size, skip); } - size = nr_entries * sizeof(unsigned long); event = __trace_buffer_lock_reserve(buffer, TRACE_STACK, - (sizeof(*entry) - sizeof(entry->caller)) + size, + struct_size(entry, caller, nr_entries), trace_ctx); if (!event) goto out; - ptr = ring_buffer_event_data(event); - entry = ptr; - - /* - * For backward compatibility reasons, the entry->caller is an - * array of 8 slots to store the stack. This is also exported - * to user space. The amount allocated on the ring buffer actually - * holds enough for the stack specified by nr_entries. This will - * go into the location of entry->caller. Due to string fortifiers - * checking the size of the destination of memcpy() it triggers - * when it detects that size is greater than 8. To hide this from - * the fortifiers, we use "ptr" and pointer arithmetic to assign caller. - * - * The below is really just: - * memcpy(&entry->caller, fstack->calls, size); - */ - ptr += offsetof(typeof(*entry), caller); - memcpy(ptr, fstack->calls, size); + entry = ring_buffer_event_data(event); entry->size = nr_entries; + memcpy(&entry->caller, fstack->calls, + flex_array_size(entry, caller, nr_entries)); if (!call_filter_check_discard(call, entry, buffer, event)) __buffer_unlock_commit(buffer, event); @@ -4206,18 +4189,12 @@ static void *s_start(struct seq_file *m, loff_t *pos) loff_t l = 0; int cpu; - /* - * copy the tracer to avoid using a global lock all around. - * iter->trace is a copy of current_trace, the pointer to the - * name may be used instead of a strcmp(), as iter->trace->name - * will point to the same string as current_trace->name. - */ mutex_lock(&trace_types_lock); - if (unlikely(tr->current_trace && iter->trace->name != tr->current_trace->name)) { + if (unlikely(tr->current_trace != iter->trace)) { /* Close iter->trace before switching to the new current tracer */ if (iter->trace->close) iter->trace->close(iter); - *iter->trace = *tr->current_trace; + iter->trace = tr->current_trace; /* Reopen the new current tracer */ if (iter->trace->open) iter->trace->open(iter); @@ -4829,6 +4806,25 @@ static const struct seq_operations tracer_seq_ops = { .show = s_show, }; +/* + * Note, as iter itself can be allocated and freed in different + * ways, this function is only used to free its content, and not + * the iterator itself. The only requirement to all the allocations + * is that it must zero all fields (kzalloc), as freeing works with + * ethier allocated content or NULL. + */ +static void free_trace_iter_content(struct trace_iterator *iter) +{ + /* The fmt is either NULL, allocated or points to static_fmt_buf */ + if (iter->fmt != static_fmt_buf) + kfree(iter->fmt); + + kfree(iter->temp); + kfree(iter->buffer_iter); + mutex_destroy(&iter->mutex); + free_cpumask_var(iter->started); +} + static struct trace_iterator * __tracing_open(struct inode *inode, struct file *file, bool snapshot) { @@ -4870,16 +4866,8 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) iter->fmt = NULL; iter->fmt_size = 0; - /* - * We make a copy of the current tracer to avoid concurrent - * changes on it while we are reading. - */ mutex_lock(&trace_types_lock); - iter->trace = kzalloc(sizeof(*iter->trace), GFP_KERNEL); - if (!iter->trace) - goto fail; - - *iter->trace = *tr->current_trace; + iter->trace = tr->current_trace; if (!zalloc_cpumask_var(&iter->started, GFP_KERNEL)) goto fail; @@ -4944,9 +4932,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) fail: mutex_unlock(&trace_types_lock); - kfree(iter->trace); - kfree(iter->temp); - kfree(iter->buffer_iter); + free_trace_iter_content(iter); release: seq_release_private(inode, file); return ERR_PTR(-ENOMEM); @@ -5025,12 +5011,7 @@ static int tracing_release(struct inode *inode, struct file *file) mutex_unlock(&trace_types_lock); - mutex_destroy(&iter->mutex); - free_cpumask_var(iter->started); - kfree(iter->fmt); - kfree(iter->temp); - kfree(iter->trace); - kfree(iter->buffer_iter); + free_trace_iter_content(iter); seq_release_private(inode, file); return 0; @@ -6318,6 +6299,15 @@ static void set_buffer_entries(struct array_buffer *buf, unsigned long val) per_cpu_ptr(buf->data, cpu)->entries = val; } +static void update_buffer_entries(struct array_buffer *buf, int cpu) +{ + if (cpu == RING_BUFFER_ALL_CPUS) { + set_buffer_entries(buf, ring_buffer_size(buf->buffer, 0)); + } else { + per_cpu_ptr(buf->data, cpu)->entries = ring_buffer_size(buf->buffer, cpu); + } +} + #ifdef CONFIG_TRACER_MAX_TRACE /* resize @tr's buffer to the size of @size_tr's entries */ static int resize_buffer_duplicate_size(struct array_buffer *trace_buf, @@ -6396,18 +6386,12 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr, return ret; } - if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(&tr->max_buffer, size); - else - per_cpu_ptr(tr->max_buffer.data, cpu)->entries = size; + update_buffer_entries(&tr->max_buffer, cpu); out: #endif /* CONFIG_TRACER_MAX_TRACE */ - if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(&tr->array_buffer, size); - else - per_cpu_ptr(tr->array_buffer.data, cpu)->entries = size; + update_buffer_entries(&tr->array_buffer, cpu); return ret; } @@ -6825,10 +6809,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) close_pipe_on_cpu(tr, iter->cpu_file); mutex_unlock(&trace_types_lock); - free_cpumask_var(iter->started); - kfree(iter->fmt); - kfree(iter->temp); - mutex_destroy(&iter->mutex); + free_trace_iter_content(iter); kfree(iter); trace_array_put(tr); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 73eaec158473..5669dd1f90d9 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -77,6 +77,16 @@ enum trace_type { #undef __array #define __array(type, item, size) type item[size]; +/* + * For backward compatibility, older user space expects to see the + * kernel_stack event with a fixed size caller field. But today the fix + * size is ignored by the kernel, and the real structure is dynamic. + * Expose to user space: "unsigned long caller[8];" but the real structure + * will be "unsigned long caller[] __counted_by(size)" + */ +#undef __stack_array +#define __stack_array(type, item, size, field) type item[] __counted_by(field); + #undef __array_desc #define __array_desc(type, container, item, size) @@ -596,7 +606,6 @@ trace_buffer_iter(struct trace_iterator *iter, int cpu) int tracer_init(struct tracer *t, struct trace_array *tr); int tracing_is_enabled(void); void tracing_reset_online_cpus(struct array_buffer *buf); -void tracing_reset_current(int cpu); void tracing_reset_all_online_cpus(void); void tracing_reset_all_online_cpus_unlocked(void); int tracing_open_generic(struct inode *inode, struct file *filp); @@ -697,7 +706,6 @@ void trace_filter_add_remove_task(struct trace_pid_list *pid_list, void *trace_pid_next(struct trace_pid_list *pid_list, void *v, loff_t *pos); void *trace_pid_start(struct trace_pid_list *pid_list, loff_t *pos); int trace_pid_show(struct seq_file *m, void *v); -void trace_free_pid_list(struct trace_pid_list *pid_list); int trace_pid_write(struct trace_pid_list *filtered_pids, struct trace_pid_list **new_pid_list, const char __user *ubuf, size_t cnt); @@ -1334,7 +1342,7 @@ struct trace_subsystem_dir { struct list_head list; struct event_subsystem *subsystem; struct trace_array *tr; - struct dentry *entry; + struct eventfs_file *ef; int ref_count; int nr_events; }; diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 340b2fa98218..c47422b20908 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -190,7 +190,7 @@ FTRACE_ENTRY(kernel_stack, stack_entry, F_STRUCT( __field( int, size ) - __array( unsigned long, caller, FTRACE_STACK_ENTRIES ) + __stack_array( unsigned long, caller, FTRACE_STACK_ENTRIES, size) ), F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n" diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 578f1f7d49a6..ed367d713be0 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -984,7 +984,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir) return; if (!--dir->nr_events) { - tracefs_remove(dir->entry); + eventfs_remove(dir->ef); list_del(&dir->list); __put_system_dir(dir); } @@ -1005,7 +1005,7 @@ static void remove_event_file_dir(struct trace_event_file *file) tracefs_remove(dir); } - + eventfs_remove(file->ef); list_del(&file->list); remove_subsystem(file->system); free_event_filter(file->filter); @@ -2291,13 +2291,13 @@ create_new_subsystem(const char *name) return NULL; } -static struct dentry * +static struct eventfs_file * event_subsystem_dir(struct trace_array *tr, const char *name, struct trace_event_file *file, struct dentry *parent) { struct event_subsystem *system, *iter; struct trace_subsystem_dir *dir; - struct dentry *entry; + int res; /* First see if we did not already create this dir */ list_for_each_entry(dir, &tr->systems, list) { @@ -2305,7 +2305,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name, if (strcmp(system->name, name) == 0) { dir->nr_events++; file->system = dir; - return dir->entry; + return dir->ef; } } @@ -2329,8 +2329,8 @@ event_subsystem_dir(struct trace_array *tr, const char *name, } else __get_system(system); - dir->entry = tracefs_create_dir(name, parent); - if (!dir->entry) { + dir->ef = eventfs_add_subsystem_dir(name, parent); + if (IS_ERR(dir->ef)) { pr_warn("Failed to create system directory %s\n", name); __put_system(system); goto out_free; @@ -2345,22 +2345,22 @@ event_subsystem_dir(struct trace_array *tr, const char *name, /* the ftrace system is special, do not create enable or filter files */ if (strcmp(name, "ftrace") != 0) { - entry = tracefs_create_file("filter", TRACE_MODE_WRITE, - dir->entry, dir, + res = eventfs_add_file("filter", TRACE_MODE_WRITE, + dir->ef, dir, &ftrace_subsystem_filter_fops); - if (!entry) { + if (res) { kfree(system->filter); system->filter = NULL; pr_warn("Could not create tracefs '%s/filter' entry\n", name); } - trace_create_file("enable", TRACE_MODE_WRITE, dir->entry, dir, + eventfs_add_file("enable", TRACE_MODE_WRITE, dir->ef, dir, &ftrace_system_enable_fops); } list_add(&dir->list, &tr->systems); - return dir->entry; + return dir->ef; out_free: kfree(dir); @@ -2413,36 +2413,37 @@ static int event_create_dir(struct dentry *parent, struct trace_event_file *file) { struct trace_event_call *call = file->event_call; + struct eventfs_file *ef_subsystem = NULL; struct trace_array *tr = file->tr; - struct dentry *d_events; const char *name; int ret; /* * If the trace point header did not define TRACE_SYSTEM - * then the system would be called "TRACE_SYSTEM". + * then the system would be called "TRACE_SYSTEM". This should + * never happen. */ - if (strcmp(call->class->system, TRACE_SYSTEM) != 0) { - d_events = event_subsystem_dir(tr, call->class->system, file, parent); - if (!d_events) - return -ENOMEM; - } else - d_events = parent; + if (WARN_ON_ONCE(strcmp(call->class->system, TRACE_SYSTEM) == 0)) + return -ENODEV; + + ef_subsystem = event_subsystem_dir(tr, call->class->system, file, parent); + if (!ef_subsystem) + return -ENOMEM; name = trace_event_name(call); - file->dir = tracefs_create_dir(name, d_events); - if (!file->dir) { + file->ef = eventfs_add_dir(name, ef_subsystem); + if (IS_ERR(file->ef)) { pr_warn("Could not create tracefs '%s' directory\n", name); return -1; } if (call->class->reg && !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) - trace_create_file("enable", TRACE_MODE_WRITE, file->dir, file, + eventfs_add_file("enable", TRACE_MODE_WRITE, file->ef, file, &ftrace_enable_fops); #ifdef CONFIG_PERF_EVENTS if (call->event.type && call->class->reg) - trace_create_file("id", TRACE_MODE_READ, file->dir, + eventfs_add_file("id", TRACE_MODE_READ, file->ef, (void *)(long)call->event.type, &ftrace_event_id_fops); #endif @@ -2458,27 +2459,27 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) * triggers or filters. */ if (!(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) { - trace_create_file("filter", TRACE_MODE_WRITE, file->dir, + eventfs_add_file("filter", TRACE_MODE_WRITE, file->ef, file, &ftrace_event_filter_fops); - trace_create_file("trigger", TRACE_MODE_WRITE, file->dir, + eventfs_add_file("trigger", TRACE_MODE_WRITE, file->ef, file, &event_trigger_fops); } #ifdef CONFIG_HIST_TRIGGERS - trace_create_file("hist", TRACE_MODE_READ, file->dir, file, + eventfs_add_file("hist", TRACE_MODE_READ, file->ef, file, &event_hist_fops); #endif #ifdef CONFIG_HIST_TRIGGERS_DEBUG - trace_create_file("hist_debug", TRACE_MODE_READ, file->dir, file, + eventfs_add_file("hist_debug", TRACE_MODE_READ, file->ef, file, &event_hist_debug_fops); #endif - trace_create_file("format", TRACE_MODE_READ, file->dir, call, + eventfs_add_file("format", TRACE_MODE_READ, file->ef, call, &ftrace_event_format_fops); #ifdef CONFIG_TRACE_EVENT_INJECT if (call->event.type && call->class->reg) - trace_create_file("inject", 0200, file->dir, file, + eventfs_add_file("inject", 0200, file->ef, file, &event_inject_fops); #endif @@ -3631,21 +3632,22 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr) { struct dentry *d_events; struct dentry *entry; + int error = 0; entry = trace_create_file("set_event", TRACE_MODE_WRITE, parent, tr, &ftrace_set_event_fops); if (!entry) return -ENOMEM; - d_events = tracefs_create_dir("events", parent); - if (!d_events) { + d_events = eventfs_create_events_dir("events", parent); + if (IS_ERR(d_events)) { pr_warn("Could not create tracefs 'events' directory\n"); return -ENOMEM; } - entry = trace_create_file("enable", TRACE_MODE_WRITE, d_events, + error = eventfs_add_events_file("enable", TRACE_MODE_WRITE, d_events, tr, &ftrace_tr_enable_fops); - if (!entry) + if (error) return -ENOMEM; /* There are not as crucial, just warn if they are not created */ @@ -3658,11 +3660,11 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr) &ftrace_set_event_notrace_pid_fops); /* ring buffer internal formats */ - trace_create_file("header_page", TRACE_MODE_READ, d_events, + eventfs_add_events_file("header_page", TRACE_MODE_READ, d_events, ring_buffer_print_page_header, &ftrace_show_header_fops); - trace_create_file("header_event", TRACE_MODE_READ, d_events, + eventfs_add_events_file("header_event", TRACE_MODE_READ, d_events, ring_buffer_print_entry_header, &ftrace_show_header_fops); @@ -3750,7 +3752,7 @@ int event_trace_del_tracer(struct trace_array *tr) down_write(&trace_event_sem); __trace_remove_event_dirs(tr); - tracefs_remove(tr->event_dir); + eventfs_remove_events_dir(tr->event_dir); up_write(&trace_event_sem); tr->event_dir = NULL; diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 1dad64267878..3a529214a21b 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -46,15 +46,19 @@ static const char * ops[] = { OPS }; enum filter_pred_fn { FILTER_PRED_FN_NOP, FILTER_PRED_FN_64, + FILTER_PRED_FN_64_CPUMASK, FILTER_PRED_FN_S64, FILTER_PRED_FN_U64, FILTER_PRED_FN_32, + FILTER_PRED_FN_32_CPUMASK, FILTER_PRED_FN_S32, FILTER_PRED_FN_U32, FILTER_PRED_FN_16, + FILTER_PRED_FN_16_CPUMASK, FILTER_PRED_FN_S16, FILTER_PRED_FN_U16, FILTER_PRED_FN_8, + FILTER_PRED_FN_8_CPUMASK, FILTER_PRED_FN_S8, FILTER_PRED_FN_U8, FILTER_PRED_FN_COMM, @@ -64,21 +68,25 @@ enum filter_pred_fn { FILTER_PRED_FN_PCHAR_USER, FILTER_PRED_FN_PCHAR, FILTER_PRED_FN_CPU, + FILTER_PRED_FN_CPU_CPUMASK, + FILTER_PRED_FN_CPUMASK, + FILTER_PRED_FN_CPUMASK_CPU, FILTER_PRED_FN_FUNCTION, FILTER_PRED_FN_, FILTER_PRED_TEST_VISITED, }; struct filter_pred { - enum filter_pred_fn fn_num; - u64 val; - u64 val2; - struct regex regex; + struct regex *regex; + struct cpumask *mask; unsigned short *ops; struct ftrace_event_field *field; - int offset; + u64 val; + u64 val2; + enum filter_pred_fn fn_num; + int offset; int not; - int op; + int op; }; /* @@ -94,6 +102,8 @@ struct filter_pred { C(TOO_MANY_OPEN, "Too many '('"), \ C(TOO_MANY_CLOSE, "Too few '('"), \ C(MISSING_QUOTE, "Missing matching quote"), \ + C(MISSING_BRACE_OPEN, "Missing '{'"), \ + C(MISSING_BRACE_CLOSE, "Missing '}'"), \ C(OPERAND_TOO_LONG, "Operand too long"), \ C(EXPECT_STRING, "Expecting string field"), \ C(EXPECT_DIGIT, "Expecting numeric field"), \ @@ -103,6 +113,7 @@ struct filter_pred { C(BAD_SUBSYS_FILTER, "Couldn't find or set field in one of a subsystem's events"), \ C(TOO_MANY_PREDS, "Too many terms in predicate expression"), \ C(INVALID_FILTER, "Meaningless filter expression"), \ + C(INVALID_CPULIST, "Invalid cpulist"), \ C(IP_FIELD_ONLY, "Only 'ip' field is supported for function trace"), \ C(INVALID_VALUE, "Invalid value (did you forget quotes)?"), \ C(NO_FUNCTION, "Function not found"), \ @@ -186,6 +197,15 @@ enum { PROCESS_OR = 4, }; +static void free_predicate(struct filter_pred *pred) +{ + if (pred) { + kfree(pred->regex); + kfree(pred->mask); + kfree(pred); + } +} + /* * Without going into a formal proof, this explains the method that is used in * parsing the logical expressions. @@ -623,12 +643,64 @@ out_free: kfree(inverts); if (prog_stack) { for (i = 0; prog_stack[i].pred; i++) - kfree(prog_stack[i].pred); + free_predicate(prog_stack[i].pred); kfree(prog_stack); } return ERR_PTR(ret); } +static inline int +do_filter_cpumask(int op, const struct cpumask *mask, const struct cpumask *cmp) +{ + switch (op) { + case OP_EQ: + return cpumask_equal(mask, cmp); + case OP_NE: + return !cpumask_equal(mask, cmp); + case OP_BAND: + return cpumask_intersects(mask, cmp); + default: + return 0; + } +} + +/* Optimisation of do_filter_cpumask() for scalar fields */ +static inline int +do_filter_scalar_cpumask(int op, unsigned int cpu, const struct cpumask *mask) +{ + /* + * Per the weight-of-one cpumask optimisations, the mask passed in this + * function has a weight >= 2, so it is never equal to a single scalar. + */ + switch (op) { + case OP_EQ: + return false; + case OP_NE: + return true; + case OP_BAND: + return cpumask_test_cpu(cpu, mask); + default: + return 0; + } +} + +static inline int +do_filter_cpumask_scalar(int op, const struct cpumask *mask, unsigned int cpu) +{ + switch (op) { + case OP_EQ: + return cpumask_test_cpu(cpu, mask) && + cpumask_nth(1, mask) >= nr_cpu_ids; + case OP_NE: + return !cpumask_test_cpu(cpu, mask) || + cpumask_nth(1, mask) < nr_cpu_ids; + case OP_BAND: + return cpumask_test_cpu(cpu, mask); + default: + return 0; + } +} + enum pred_cmp_types { PRED_CMP_TYPE_NOP, PRED_CMP_TYPE_LT, @@ -672,6 +744,18 @@ static int filter_pred_##type(struct filter_pred *pred, void *event) \ } \ } +#define DEFINE_CPUMASK_COMPARISON_PRED(size) \ +static int filter_pred_##size##_cpumask(struct filter_pred *pred, void *event) \ +{ \ + u##size *addr = (u##size *)(event + pred->offset); \ + unsigned int cpu = *addr; \ + \ + if (cpu >= nr_cpu_ids) \ + return 0; \ + \ + return do_filter_scalar_cpumask(pred->op, cpu, pred->mask); \ +} + #define DEFINE_EQUALITY_PRED(size) \ static int filter_pred_##size(struct filter_pred *pred, void *event) \ { \ @@ -693,6 +777,11 @@ DEFINE_COMPARISON_PRED(u16); DEFINE_COMPARISON_PRED(s8); DEFINE_COMPARISON_PRED(u8); +DEFINE_CPUMASK_COMPARISON_PRED(64); +DEFINE_CPUMASK_COMPARISON_PRED(32); +DEFINE_CPUMASK_COMPARISON_PRED(16); +DEFINE_CPUMASK_COMPARISON_PRED(8); + DEFINE_EQUALITY_PRED(64); DEFINE_EQUALITY_PRED(32); DEFINE_EQUALITY_PRED(16); @@ -750,7 +839,7 @@ static int filter_pred_string(struct filter_pred *pred, void *event) char *addr = (char *)(event + pred->offset); int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, pred->regex.field_len); + cmp = pred->regex->match(addr, pred->regex, pred->regex->field_len); match = cmp ^ pred->not; @@ -763,7 +852,7 @@ static __always_inline int filter_pchar(struct filter_pred *pred, char *str) int len; len = strlen(str) + 1; /* including tailing '\0' */ - cmp = pred->regex.match(str, &pred->regex, len); + cmp = pred->regex->match(str, pred->regex, len); match = cmp ^ pred->not; @@ -813,7 +902,7 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event) char *addr = (char *)(event + str_loc); int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, str_len); + cmp = pred->regex->match(addr, pred->regex, str_len); match = cmp ^ pred->not; @@ -836,7 +925,7 @@ static int filter_pred_strrelloc(struct filter_pred *pred, void *event) char *addr = (char *)(&item[1]) + str_loc; int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, str_len); + cmp = pred->regex->match(addr, pred->regex, str_len); match = cmp ^ pred->not; @@ -869,12 +958,42 @@ static int filter_pred_cpu(struct filter_pred *pred, void *event) } } +/* Filter predicate for current CPU vs user-provided cpumask */ +static int filter_pred_cpu_cpumask(struct filter_pred *pred, void *event) +{ + int cpu = raw_smp_processor_id(); + + return do_filter_scalar_cpumask(pred->op, cpu, pred->mask); +} + +/* Filter predicate for cpumask field vs user-provided cpumask */ +static int filter_pred_cpumask(struct filter_pred *pred, void *event) +{ + u32 item = *(u32 *)(event + pred->offset); + int loc = item & 0xffff; + const struct cpumask *mask = (event + loc); + const struct cpumask *cmp = pred->mask; + + return do_filter_cpumask(pred->op, mask, cmp); +} + +/* Filter predicate for cpumask field vs user-provided scalar */ +static int filter_pred_cpumask_cpu(struct filter_pred *pred, void *event) +{ + u32 item = *(u32 *)(event + pred->offset); + int loc = item & 0xffff; + const struct cpumask *mask = (event + loc); + unsigned int cpu = pred->val; + + return do_filter_cpumask_scalar(pred->op, mask, cpu); +} + /* Filter predicate for COMM. */ static int filter_pred_comm(struct filter_pred *pred, void *event) { int cmp; - cmp = pred->regex.match(current->comm, &pred->regex, + cmp = pred->regex->match(current->comm, pred->regex, TASK_COMM_LEN); return cmp ^ pred->not; } @@ -1004,7 +1123,7 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not) static void filter_build_regex(struct filter_pred *pred) { - struct regex *r = &pred->regex; + struct regex *r = pred->regex; char *search; enum regex_type type = MATCH_FULL; @@ -1169,7 +1288,7 @@ static void free_prog(struct event_filter *filter) return; for (i = 0; prog[i].pred; i++) - kfree(prog[i].pred); + free_predicate(prog[i].pred); kfree(prog); } @@ -1236,8 +1355,12 @@ static void filter_free_subsystem_filters(struct trace_subsystem_dir *dir, int filter_assign_type(const char *type) { - if (strstr(type, "__data_loc") && strstr(type, "char")) - return FILTER_DYN_STRING; + if (strstr(type, "__data_loc")) { + if (strstr(type, "char")) + return FILTER_DYN_STRING; + if (strstr(type, "cpumask_t")) + return FILTER_CPUMASK; + } if (strstr(type, "__rel_loc") && strstr(type, "char")) return FILTER_RDYN_STRING; @@ -1313,24 +1436,32 @@ static int filter_pred_fn_call(struct filter_pred *pred, void *event) switch (pred->fn_num) { case FILTER_PRED_FN_64: return filter_pred_64(pred, event); + case FILTER_PRED_FN_64_CPUMASK: + return filter_pred_64_cpumask(pred, event); case FILTER_PRED_FN_S64: return filter_pred_s64(pred, event); case FILTER_PRED_FN_U64: return filter_pred_u64(pred, event); case FILTER_PRED_FN_32: return filter_pred_32(pred, event); + case FILTER_PRED_FN_32_CPUMASK: + return filter_pred_32_cpumask(pred, event); case FILTER_PRED_FN_S32: return filter_pred_s32(pred, event); case FILTER_PRED_FN_U32: return filter_pred_u32(pred, event); case FILTER_PRED_FN_16: return filter_pred_16(pred, event); + case FILTER_PRED_FN_16_CPUMASK: + return filter_pred_16_cpumask(pred, event); case FILTER_PRED_FN_S16: return filter_pred_s16(pred, event); case FILTER_PRED_FN_U16: return filter_pred_u16(pred, event); case FILTER_PRED_FN_8: return filter_pred_8(pred, event); + case FILTER_PRED_FN_8_CPUMASK: + return filter_pred_8_cpumask(pred, event); case FILTER_PRED_FN_S8: return filter_pred_s8(pred, event); case FILTER_PRED_FN_U8: @@ -1349,6 +1480,12 @@ static int filter_pred_fn_call(struct filter_pred *pred, void *event) return filter_pred_pchar(pred, event); case FILTER_PRED_FN_CPU: return filter_pred_cpu(pred, event); + case FILTER_PRED_FN_CPU_CPUMASK: + return filter_pred_cpu_cpumask(pred, event); + case FILTER_PRED_FN_CPUMASK: + return filter_pred_cpumask(pred, event); + case FILTER_PRED_FN_CPUMASK_CPU: + return filter_pred_cpumask_cpu(pred, event); case FILTER_PRED_FN_FUNCTION: return filter_pred_function(pred, event); case FILTER_PRED_TEST_VISITED: @@ -1553,9 +1690,117 @@ static int parse_pred(const char *str, void *data, goto err_free; } - pred->regex.len = len; - strncpy(pred->regex.pattern, str + s, len); - pred->regex.pattern[len] = 0; + pred->regex = kzalloc(sizeof(*pred->regex), GFP_KERNEL); + if (!pred->regex) + goto err_mem; + pred->regex->len = len; + strncpy(pred->regex->pattern, str + s, len); + pred->regex->pattern[len] = 0; + + } else if (!strncmp(str + i, "CPUS", 4)) { + unsigned int maskstart; + bool single; + char *tmp; + + switch (field->filter_type) { + case FILTER_CPUMASK: + case FILTER_CPU: + case FILTER_OTHER: + break; + default: + parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + i); + goto err_free; + } + + switch (op) { + case OP_EQ: + case OP_NE: + case OP_BAND: + break; + default: + parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + i); + goto err_free; + } + + /* Skip CPUS */ + i += 4; + if (str[i++] != '{') { + parse_error(pe, FILT_ERR_MISSING_BRACE_OPEN, pos + i); + goto err_free; + } + maskstart = i; + + /* Walk the cpulist until closing } */ + for (; str[i] && str[i] != '}'; i++); + if (str[i] != '}') { + parse_error(pe, FILT_ERR_MISSING_BRACE_CLOSE, pos + i); + goto err_free; + } + + if (maskstart == i) { + parse_error(pe, FILT_ERR_INVALID_CPULIST, pos + i); + goto err_free; + } + + /* Copy the cpulist between { and } */ + tmp = kmalloc((i - maskstart) + 1, GFP_KERNEL); + strscpy(tmp, str + maskstart, (i - maskstart) + 1); + + pred->mask = kzalloc(cpumask_size(), GFP_KERNEL); + if (!pred->mask) + goto err_mem; + + /* Now parse it */ + if (cpulist_parse(tmp, pred->mask)) { + parse_error(pe, FILT_ERR_INVALID_CPULIST, pos + i); + goto err_free; + } + + /* Move along */ + i++; + + /* + * Optimisation: if the user-provided mask has a weight of one + * then we can treat it as a scalar input. + */ + single = cpumask_weight(pred->mask) == 1; + if (single) { + pred->val = cpumask_first(pred->mask); + kfree(pred->mask); + } + + if (field->filter_type == FILTER_CPUMASK) { + pred->fn_num = single ? + FILTER_PRED_FN_CPUMASK_CPU : + FILTER_PRED_FN_CPUMASK; + } else if (field->filter_type == FILTER_CPU) { + if (single) { + pred->op = pred->op == OP_BAND ? OP_EQ : pred->op; + pred->fn_num = FILTER_PRED_FN_CPU; + } else { + pred->fn_num = FILTER_PRED_FN_CPU_CPUMASK; + } + } else if (single) { + pred->op = pred->op == OP_BAND ? OP_EQ : pred->op; + pred->fn_num = select_comparison_fn(pred->op, field->size, false); + if (pred->op == OP_NE) + pred->not = 1; + } else { + switch (field->size) { + case 8: + pred->fn_num = FILTER_PRED_FN_64_CPUMASK; + break; + case 4: + pred->fn_num = FILTER_PRED_FN_32_CPUMASK; + break; + case 2: + pred->fn_num = FILTER_PRED_FN_16_CPUMASK; + break; + case 1: + pred->fn_num = FILTER_PRED_FN_8_CPUMASK; + break; + } + } /* This is either a string, or an integer */ } else if (str[i] == '\'' || str[i] == '"') { @@ -1597,9 +1842,12 @@ static int parse_pred(const char *str, void *data, goto err_free; } - pred->regex.len = len; - strncpy(pred->regex.pattern, str + s, len); - pred->regex.pattern[len] = 0; + pred->regex = kzalloc(sizeof(*pred->regex), GFP_KERNEL); + if (!pred->regex) + goto err_mem; + pred->regex->len = len; + strncpy(pred->regex->pattern, str + s, len); + pred->regex->pattern[len] = 0; filter_build_regex(pred); @@ -1608,7 +1856,7 @@ static int parse_pred(const char *str, void *data, } else if (field->filter_type == FILTER_STATIC_STRING) { pred->fn_num = FILTER_PRED_FN_STRING; - pred->regex.field_len = field->size; + pred->regex->field_len = field->size; } else if (field->filter_type == FILTER_DYN_STRING) { pred->fn_num = FILTER_PRED_FN_STRLOC; @@ -1691,10 +1939,10 @@ static int parse_pred(const char *str, void *data, return i; err_free: - kfree(pred); + free_predicate(pred); return -EINVAL; err_mem: - kfree(pred); + free_predicate(pred); return -ENOMEM; } @@ -2287,8 +2535,8 @@ static int ftrace_function_set_filter_pred(struct filter_pred *pred, return ret; return __ftrace_function_set_filter(pred->op == OP_EQ, - pred->regex.pattern, - pred->regex.len, + pred->regex->pattern, + pred->regex->len, data); } diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index 33cb6af31f39..6f046650e527 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -1328,14 +1328,14 @@ static int user_field_set_string(struct ftrace_event_field *field, static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) { - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head = &user->fields; int pos = 0, depth = 0; const char *str_func; pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (depth != 0) pos += snprintf(buf + pos, LEN_OR_ZERO, " "); @@ -1347,7 +1347,7 @@ static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (user_field_is_dyn_string(field->type, &str_func)) pos += snprintf(buf + pos, LEN_OR_ZERO, ", %s(%s)", str_func, field->name); @@ -1732,7 +1732,7 @@ static int user_event_create(const char *raw_command) static int user_event_show(struct seq_file *m, struct dyn_event *ev) { struct user_event *user = container_of(ev, struct user_event, devent); - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head; int depth = 0; @@ -1740,7 +1740,7 @@ static int user_event_show(struct seq_file *m, struct dyn_event *ev) head = trace_get_fields(&user->call); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (depth == 0) seq_puts(m, " "); else @@ -1816,13 +1816,14 @@ out: static bool user_fields_match(struct user_event *user, int argc, const char **argv) { - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head = &user->fields; int i = 0; - list_for_each_entry_safe_reverse(field, next, head, link) + list_for_each_entry_reverse(field, head, link) { if (!user_field_match(field, argc, argv, &i)) return false; + } if (i != argc) return false; diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index 58f3946081e2..1698fc22afa0 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -51,6 +51,9 @@ static int ftrace_event_register(struct trace_event_call *call, #undef __array #define __array(type, item, size) type item[size]; +#undef __stack_array +#define __stack_array(type, item, size, field) __array(type, item, size) + #undef __array_desc #define __array_desc(type, container, item, size) type item[size]; @@ -114,6 +117,9 @@ static void __always_unused ____ftrace_check_##name(void) \ is_signed_type(_type), .filter_type = FILTER_OTHER, \ .len = _len }, +#undef __stack_array +#define __stack_array(_type, _item, _len, _field) __array(_type, _item, _len) + #undef __array_desc #define __array_desc(_type, _container, _item, _len) __array(_type, _item, _len) @@ -149,6 +155,9 @@ static struct trace_event_fields ftrace_event_fields_##name[] = { \ #undef __array #define __array(type, item, len) +#undef __stack_array +#define __stack_array(type, item, len, field) + #undef __array_desc #define __array_desc(type, container, item, len) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 184ecf45cb5a..beac2e18046c 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -126,11 +126,6 @@ enum { * * L: pool->lock protected. Access with pool->lock held. * - * X: During normal operation, modification requires pool->lock and should - * be done only from local cpu. Either disabling preemption on local - * cpu or grabbing pool->lock is enough for read access. If - * POOL_DISASSOCIATED is set, it's identical to L. - * * K: Only modified by worker while holding pool->lock. Can be safely read by * self, while holding pool->lock or from IRQ context if %current is the * kworker. @@ -164,7 +159,7 @@ struct worker_pool { int cpu; /* I: the associated cpu */ int node; /* I: the associated node ID */ int id; /* I: pool ID */ - unsigned int flags; /* X: flags */ + unsigned int flags; /* L: flags */ unsigned long watchdog_ts; /* L: watchdog timestamp */ bool cpu_stall; /* WD: stalled cpu bound pool */ @@ -220,6 +215,7 @@ enum pool_workqueue_stats { PWQ_STAT_CPU_TIME, /* total CPU time consumed */ PWQ_STAT_CPU_INTENSIVE, /* wq_cpu_intensive_thresh_us violations */ PWQ_STAT_CM_WAKEUP, /* concurrency-management worker wakeups */ + PWQ_STAT_REPATRIATED, /* unbound workers brought back into scope */ PWQ_STAT_MAYDAY, /* maydays to rescuer */ PWQ_STAT_RESCUED, /* linked work items executed by rescuer */ @@ -266,12 +262,12 @@ struct pool_workqueue { u64 stats[PWQ_NR_STATS]; /* - * Release of unbound pwq is punted to system_wq. See put_pwq() - * and pwq_unbound_release_workfn() for details. pool_workqueue - * itself is also RCU protected so that the first pwq can be - * determined without grabbing wq->mutex. + * Release of unbound pwq is punted to a kthread_worker. See put_pwq() + * and pwq_release_workfn() for details. pool_workqueue itself is also + * RCU protected so that the first pwq can be determined without + * grabbing wq->mutex. */ - struct work_struct unbound_release_work; + struct kthread_work release_work; struct rcu_head rcu; } __aligned(1 << WORK_STRUCT_FLAG_BITS); @@ -330,14 +326,33 @@ struct workqueue_struct { /* hot fields used during command issue, aligned to cacheline */ unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */ - struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */ - struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */ + struct pool_workqueue __percpu __rcu **cpu_pwq; /* I: per-cpu pwqs */ }; static struct kmem_cache *pwq_cache; -static cpumask_var_t *wq_numa_possible_cpumask; - /* possible CPUs of each node */ +/* + * Each pod type describes how CPUs should be grouped for unbound workqueues. + * See the comment above workqueue_attrs->affn_scope. + */ +struct wq_pod_type { + int nr_pods; /* number of pods */ + cpumask_var_t *pod_cpus; /* pod -> cpus */ + int *pod_node; /* pod -> node */ + int *cpu_pod; /* cpu -> pod */ +}; + +static struct wq_pod_type wq_pod_types[WQ_AFFN_NR_TYPES]; +static enum wq_affn_scope wq_affn_dfl = WQ_AFFN_CACHE; + +static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = { + [WQ_AFFN_DFL] = "default", + [WQ_AFFN_CPU] = "cpu", + [WQ_AFFN_SMT] = "smt", + [WQ_AFFN_CACHE] = "cache", + [WQ_AFFN_NUMA] = "numa", + [WQ_AFFN_SYSTEM] = "system", +}; /* * Per-cpu work items which run for longer than the following threshold are @@ -349,19 +364,14 @@ static cpumask_var_t *wq_numa_possible_cpumask; static unsigned long wq_cpu_intensive_thresh_us = ULONG_MAX; module_param_named(cpu_intensive_thresh_us, wq_cpu_intensive_thresh_us, ulong, 0644); -static bool wq_disable_numa; -module_param_named(disable_numa, wq_disable_numa, bool, 0444); - /* see the comment above the definition of WQ_POWER_EFFICIENT */ static bool wq_power_efficient = IS_ENABLED(CONFIG_WQ_POWER_EFFICIENT_DEFAULT); module_param_named(power_efficient, wq_power_efficient, bool, 0444); static bool wq_online; /* can kworkers be created yet? */ -static bool wq_numa_enabled; /* unbound NUMA affinity enabled */ - -/* buf for wq_update_unbound_numa_attrs(), protected by CPU hotplug exclusion */ -static struct workqueue_attrs *wq_update_unbound_numa_attrs_buf; +/* buf for wq_update_unbound_pod_attrs(), protected by CPU hotplug exclusion */ +static struct workqueue_attrs *wq_update_pod_attrs_buf; static DEFINE_MUTEX(wq_pool_mutex); /* protects pools and workqueues list */ static DEFINE_MUTEX(wq_pool_attach_mutex); /* protects worker attach/detach */ @@ -375,6 +385,9 @@ static bool workqueue_freezing; /* PL: have wqs started freezing? */ /* PL&A: allowable cpus for unbound wqs and work items */ static cpumask_var_t wq_unbound_cpumask; +/* for further constrain wq_unbound_cpumask by cmdline parameter*/ +static struct cpumask wq_cmdline_cpumask __initdata; + /* CPU where unbound work was last round robin scheduled from this CPU */ static DEFINE_PER_CPU(int, wq_rr_cpu_last); @@ -404,6 +417,13 @@ static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS]; /* I: attributes used when instantiating ordered pools on demand */ static struct workqueue_attrs *ordered_wq_attrs[NR_STD_WORKER_POOLS]; +/* + * I: kthread_worker to release pwq's. pwq release needs to be bounced to a + * process context while holding a pool lock. Bounce to a dedicated kthread + * worker to avoid A-A deadlocks. + */ +static struct kthread_worker *pwq_release_worker; + struct workqueue_struct *system_wq __read_mostly; EXPORT_SYMBOL(system_wq); struct workqueue_struct *system_highpri_wq __read_mostly; @@ -610,35 +630,6 @@ static int worker_pool_assign_id(struct worker_pool *pool) return ret; } -/** - * unbound_pwq_by_node - return the unbound pool_workqueue for the given node - * @wq: the target workqueue - * @node: the node ID - * - * This must be called with any of wq_pool_mutex, wq->mutex or RCU - * read locked. - * If the pwq needs to be used beyond the locking in effect, the caller is - * responsible for guaranteeing that the pwq stays online. - * - * Return: The unbound pool_workqueue for @node. - */ -static struct pool_workqueue *unbound_pwq_by_node(struct workqueue_struct *wq, - int node) -{ - assert_rcu_or_wq_mutex_or_pool_mutex(wq); - - /* - * XXX: @node can be NUMA_NO_NODE if CPU goes offline while a - * delayed item is pending. The plan is to keep CPU -> NODE - * mapping valid and stable across CPU on/offlines. Once that - * happens, this workaround can be removed. - */ - if (unlikely(node == NUMA_NO_NODE)) - return wq->dfl_pwq; - - return rcu_dereference_raw(wq->numa_pwq_tbl[node]); -} - static unsigned int work_color_to_flags(int color) { return color << WORK_STRUCT_COLOR_SHIFT; @@ -829,11 +820,6 @@ static bool work_is_canceling(struct work_struct *work) * they're being called with pool->lock held. */ -static bool __need_more_worker(struct worker_pool *pool) -{ - return !pool->nr_running; -} - /* * Need to wake up a worker? Called from anything but currently * running workers. @@ -844,7 +830,7 @@ static bool __need_more_worker(struct worker_pool *pool) */ static bool need_more_worker(struct worker_pool *pool) { - return !list_empty(&pool->worklist) && __need_more_worker(pool); + return !list_empty(&pool->worklist) && !pool->nr_running; } /* Can I start working? Called from busy but !running workers. */ @@ -875,51 +861,18 @@ static bool too_many_workers(struct worker_pool *pool) return nr_idle > 2 && (nr_idle - 2) * MAX_IDLE_WORKERS_RATIO >= nr_busy; } -/* - * Wake up functions. - */ - -/* Return the first idle worker. Called with pool->lock held. */ -static struct worker *first_idle_worker(struct worker_pool *pool) -{ - if (unlikely(list_empty(&pool->idle_list))) - return NULL; - - return list_first_entry(&pool->idle_list, struct worker, entry); -} - -/** - * wake_up_worker - wake up an idle worker - * @pool: worker pool to wake worker from - * - * Wake up the first idle worker of @pool. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - */ -static void wake_up_worker(struct worker_pool *pool) -{ - struct worker *worker = first_idle_worker(pool); - - if (likely(worker)) - wake_up_process(worker->task); -} - /** * worker_set_flags - set worker flags and adjust nr_running accordingly * @worker: self * @flags: flags to set * * Set @flags in @worker->flags and adjust nr_running accordingly. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock) */ static inline void worker_set_flags(struct worker *worker, unsigned int flags) { struct worker_pool *pool = worker->pool; - WARN_ON_ONCE(worker->task != current); + lockdep_assert_held(&pool->lock); /* If transitioning into NOT_RUNNING, adjust nr_running. */ if ((flags & WORKER_NOT_RUNNING) && @@ -936,16 +889,13 @@ static inline void worker_set_flags(struct worker *worker, unsigned int flags) * @flags: flags to clear * * Clear @flags in @worker->flags and adjust nr_running accordingly. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock) */ static inline void worker_clr_flags(struct worker *worker, unsigned int flags) { struct worker_pool *pool = worker->pool; unsigned int oflags = worker->flags; - WARN_ON_ONCE(worker->task != current); + lockdep_assert_held(&pool->lock); worker->flags &= ~flags; @@ -959,6 +909,244 @@ static inline void worker_clr_flags(struct worker *worker, unsigned int flags) pool->nr_running++; } +/* Return the first idle worker. Called with pool->lock held. */ +static struct worker *first_idle_worker(struct worker_pool *pool) +{ + if (unlikely(list_empty(&pool->idle_list))) + return NULL; + + return list_first_entry(&pool->idle_list, struct worker, entry); +} + +/** + * worker_enter_idle - enter idle state + * @worker: worker which is entering idle state + * + * @worker is entering idle state. Update stats and idle timer if + * necessary. + * + * LOCKING: + * raw_spin_lock_irq(pool->lock). + */ +static void worker_enter_idle(struct worker *worker) +{ + struct worker_pool *pool = worker->pool; + + if (WARN_ON_ONCE(worker->flags & WORKER_IDLE) || + WARN_ON_ONCE(!list_empty(&worker->entry) && + (worker->hentry.next || worker->hentry.pprev))) + return; + + /* can't use worker_set_flags(), also called from create_worker() */ + worker->flags |= WORKER_IDLE; + pool->nr_idle++; + worker->last_active = jiffies; + + /* idle_list is LIFO */ + list_add(&worker->entry, &pool->idle_list); + + if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) + mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); + + /* Sanity check nr_running. */ + WARN_ON_ONCE(pool->nr_workers == pool->nr_idle && pool->nr_running); +} + +/** + * worker_leave_idle - leave idle state + * @worker: worker which is leaving idle state + * + * @worker is leaving idle state. Update stats. + * + * LOCKING: + * raw_spin_lock_irq(pool->lock). + */ +static void worker_leave_idle(struct worker *worker) +{ + struct worker_pool *pool = worker->pool; + + if (WARN_ON_ONCE(!(worker->flags & WORKER_IDLE))) + return; + worker_clr_flags(worker, WORKER_IDLE); + pool->nr_idle--; + list_del_init(&worker->entry); +} + +/** + * find_worker_executing_work - find worker which is executing a work + * @pool: pool of interest + * @work: work to find worker for + * + * Find a worker which is executing @work on @pool by searching + * @pool->busy_hash which is keyed by the address of @work. For a worker + * to match, its current execution should match the address of @work and + * its work function. This is to avoid unwanted dependency between + * unrelated work executions through a work item being recycled while still + * being executed. + * + * This is a bit tricky. A work item may be freed once its execution + * starts and nothing prevents the freed area from being recycled for + * another work item. If the same work item address ends up being reused + * before the original execution finishes, workqueue will identify the + * recycled work item as currently executing and make it wait until the + * current execution finishes, introducing an unwanted dependency. + * + * This function checks the work item address and work function to avoid + * false positives. Note that this isn't complete as one may construct a + * work function which can introduce dependency onto itself through a + * recycled work item. Well, if somebody wants to shoot oneself in the + * foot that badly, there's only so much we can do, and if such deadlock + * actually occurs, it should be easy to locate the culprit work function. + * + * CONTEXT: + * raw_spin_lock_irq(pool->lock). + * + * Return: + * Pointer to worker which is executing @work if found, %NULL + * otherwise. + */ +static struct worker *find_worker_executing_work(struct worker_pool *pool, + struct work_struct *work) +{ + struct worker *worker; + + hash_for_each_possible(pool->busy_hash, worker, hentry, + (unsigned long)work) + if (worker->current_work == work && + worker->current_func == work->func) + return worker; + + return NULL; +} + +/** + * move_linked_works - move linked works to a list + * @work: start of series of works to be scheduled + * @head: target list to append @work to + * @nextp: out parameter for nested worklist walking + * + * Schedule linked works starting from @work to @head. Work series to be + * scheduled starts at @work and includes any consecutive work with + * WORK_STRUCT_LINKED set in its predecessor. See assign_work() for details on + * @nextp. + * + * CONTEXT: + * raw_spin_lock_irq(pool->lock). + */ +static void move_linked_works(struct work_struct *work, struct list_head *head, + struct work_struct **nextp) +{ + struct work_struct *n; + + /* + * Linked worklist will always end before the end of the list, + * use NULL for list head. + */ + list_for_each_entry_safe_from(work, n, NULL, entry) { + list_move_tail(&work->entry, head); + if (!(*work_data_bits(work) & WORK_STRUCT_LINKED)) + break; + } + + /* + * If we're already inside safe list traversal and have moved + * multiple works to the scheduled queue, the next position + * needs to be updated. + */ + if (nextp) + *nextp = n; +} + +/** + * assign_work - assign a work item and its linked work items to a worker + * @work: work to assign + * @worker: worker to assign to + * @nextp: out parameter for nested worklist walking + * + * Assign @work and its linked work items to @worker. If @work is already being + * executed by another worker in the same pool, it'll be punted there. + * + * If @nextp is not NULL, it's updated to point to the next work of the last + * scheduled work. This allows assign_work() to be nested inside + * list_for_each_entry_safe(). + * + * Returns %true if @work was successfully assigned to @worker. %false if @work + * was punted to another worker already executing it. + */ +static bool assign_work(struct work_struct *work, struct worker *worker, + struct work_struct **nextp) +{ + struct worker_pool *pool = worker->pool; + struct worker *collision; + + lockdep_assert_held(&pool->lock); + + /* + * A single work shouldn't be executed concurrently by multiple workers. + * __queue_work() ensures that @work doesn't jump to a different pool + * while still running in the previous pool. Here, we should ensure that + * @work is not executed concurrently by multiple workers from the same + * pool. Check whether anyone is already processing the work. If so, + * defer the work to the currently executing one. + */ + collision = find_worker_executing_work(pool, work); + if (unlikely(collision)) { + move_linked_works(work, &collision->scheduled, nextp); + return false; + } + + move_linked_works(work, &worker->scheduled, nextp); + return true; +} + +/** + * kick_pool - wake up an idle worker if necessary + * @pool: pool to kick + * + * @pool may have pending work items. Wake up worker if necessary. Returns + * whether a worker was woken up. + */ +static bool kick_pool(struct worker_pool *pool) +{ + struct worker *worker = first_idle_worker(pool); + struct task_struct *p; + + lockdep_assert_held(&pool->lock); + + if (!need_more_worker(pool) || !worker) + return false; + + p = worker->task; + +#ifdef CONFIG_SMP + /* + * Idle @worker is about to execute @work and waking up provides an + * opportunity to migrate @worker at a lower cost by setting the task's + * wake_cpu field. Let's see if we want to move @worker to improve + * execution locality. + * + * We're waking the worker that went idle the latest and there's some + * chance that @worker is marked idle but hasn't gone off CPU yet. If + * so, setting the wake_cpu won't do anything. As this is a best-effort + * optimization and the race window is narrow, let's leave as-is for + * now. If this becomes pronounced, we can skip over workers which are + * still on cpu when picking an idle worker. + * + * If @pool has non-strict affinity, @worker might have ended up outside + * its affinity scope. Repatriate. + */ + if (!pool->attrs->affn_strict && + !cpumask_test_cpu(p->wake_cpu, pool->attrs->__pod_cpumask)) { + struct work_struct *work = list_first_entry(&pool->worklist, + struct work_struct, entry); + p->wake_cpu = cpumask_any_distribute(pool->attrs->__pod_cpumask); + get_work_pwq(work)->stats[PWQ_STAT_REPATRIATED]++; + } +#endif + wake_up_process(p); + return true; +} + #ifdef CONFIG_WQ_CPU_INTENSIVE_REPORT /* @@ -1124,10 +1312,9 @@ void wq_worker_sleeping(struct task_struct *task) } pool->nr_running--; - if (need_more_worker(pool)) { + if (kick_pool(pool)) worker->current_pwq->stats[PWQ_STAT_CM_WAKEUP]++; - wake_up_worker(pool); - } + raw_spin_unlock_irq(&pool->lock); } @@ -1175,10 +1362,8 @@ void wq_worker_tick(struct task_struct *task) wq_cpu_intensive_report(worker->current_func); pwq->stats[PWQ_STAT_CPU_INTENSIVE]++; - if (need_more_worker(pool)) { + if (kick_pool(pool)) pwq->stats[PWQ_STAT_CM_WAKEUP]++; - wake_up_worker(pool); - } raw_spin_unlock(&pool->lock); } @@ -1214,94 +1399,6 @@ work_func_t wq_worker_last_func(struct task_struct *task) return worker->last_func; } -/** - * find_worker_executing_work - find worker which is executing a work - * @pool: pool of interest - * @work: work to find worker for - * - * Find a worker which is executing @work on @pool by searching - * @pool->busy_hash which is keyed by the address of @work. For a worker - * to match, its current execution should match the address of @work and - * its work function. This is to avoid unwanted dependency between - * unrelated work executions through a work item being recycled while still - * being executed. - * - * This is a bit tricky. A work item may be freed once its execution - * starts and nothing prevents the freed area from being recycled for - * another work item. If the same work item address ends up being reused - * before the original execution finishes, workqueue will identify the - * recycled work item as currently executing and make it wait until the - * current execution finishes, introducing an unwanted dependency. - * - * This function checks the work item address and work function to avoid - * false positives. Note that this isn't complete as one may construct a - * work function which can introduce dependency onto itself through a - * recycled work item. Well, if somebody wants to shoot oneself in the - * foot that badly, there's only so much we can do, and if such deadlock - * actually occurs, it should be easy to locate the culprit work function. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - * - * Return: - * Pointer to worker which is executing @work if found, %NULL - * otherwise. - */ -static struct worker *find_worker_executing_work(struct worker_pool *pool, - struct work_struct *work) -{ - struct worker *worker; - - hash_for_each_possible(pool->busy_hash, worker, hentry, - (unsigned long)work) - if (worker->current_work == work && - worker->current_func == work->func) - return worker; - - return NULL; -} - -/** - * move_linked_works - move linked works to a list - * @work: start of series of works to be scheduled - * @head: target list to append @work to - * @nextp: out parameter for nested worklist walking - * - * Schedule linked works starting from @work to @head. Work series to - * be scheduled starts at @work and includes any consecutive work with - * WORK_STRUCT_LINKED set in its predecessor. - * - * If @nextp is not NULL, it's updated to point to the next work of - * the last scheduled work. This allows move_linked_works() to be - * nested inside outer list_for_each_entry_safe(). - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - */ -static void move_linked_works(struct work_struct *work, struct list_head *head, - struct work_struct **nextp) -{ - struct work_struct *n; - - /* - * Linked worklist will always end before the end of the list, - * use NULL for list head. - */ - list_for_each_entry_safe_from(work, n, NULL, entry) { - list_move_tail(&work->entry, head); - if (!(*work_data_bits(work) & WORK_STRUCT_LINKED)) - break; - } - - /* - * If we're already inside safe list traversal and have moved - * multiple works to the scheduled queue, the next position - * needs to be updated. - */ - if (nextp) - *nextp = n; -} - /** * get_pwq - get an extra reference on the specified pool_workqueue * @pwq: pool_workqueue to get @@ -1328,17 +1425,11 @@ static void put_pwq(struct pool_workqueue *pwq) lockdep_assert_held(&pwq->pool->lock); if (likely(--pwq->refcnt)) return; - if (WARN_ON_ONCE(!(pwq->wq->flags & WQ_UNBOUND))) - return; /* - * @pwq can't be released under pool->lock, bounce to - * pwq_unbound_release_workfn(). This never recurses on the same - * pool->lock as this path is taken only for unbound workqueues and - * the release work item is scheduled on a per-cpu workqueue. To - * avoid lockdep warning, unbound pool->locks are given lockdep - * subclass of 1 in get_unbound_pool(). + * @pwq can't be released under pool->lock, bounce to a dedicated + * kthread_worker to avoid A-A deadlocks. */ - schedule_work(&pwq->unbound_release_work); + kthread_queue_work(pwq_release_worker, &pwq->release_work); } /** @@ -1554,7 +1645,7 @@ fail: static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, struct list_head *head, unsigned int extra_flags) { - struct worker_pool *pool = pwq->pool; + debug_work_activate(work); /* record the work call stack in order to print it in KASAN reports */ kasan_record_aux_stack_noalloc(work); @@ -1563,9 +1654,6 @@ static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, set_work_pwq(work, pwq, extra_flags); list_add_tail(&work->entry, head); get_pwq(pwq); - - if (__need_more_worker(pool)) - wake_up_worker(pool); } /* @@ -1619,8 +1707,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) { struct pool_workqueue *pwq; - struct worker_pool *last_pool; - struct list_head *worklist; + struct worker_pool *last_pool, *pool; unsigned int work_flags; unsigned int req_cpu = cpu; @@ -1644,23 +1731,23 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, rcu_read_lock(); retry: /* pwq which will be used unless @work is executing elsewhere */ - if (wq->flags & WQ_UNBOUND) { - if (req_cpu == WORK_CPU_UNBOUND) + if (req_cpu == WORK_CPU_UNBOUND) { + if (wq->flags & WQ_UNBOUND) cpu = wq_select_unbound_cpu(raw_smp_processor_id()); - pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); - } else { - if (req_cpu == WORK_CPU_UNBOUND) + else cpu = raw_smp_processor_id(); - pwq = per_cpu_ptr(wq->cpu_pwqs, cpu); } + pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu)); + pool = pwq->pool; + /* * If @work was previously on a different pool, it might still be * running there, in which case the work needs to be queued on that * pool to guarantee non-reentrancy. */ last_pool = get_work_pool(work); - if (last_pool && last_pool != pwq->pool) { + if (last_pool && last_pool != pool) { struct worker *worker; raw_spin_lock(&last_pool->lock); @@ -1669,26 +1756,27 @@ retry: if (worker && worker->current_pwq->wq == wq) { pwq = worker->current_pwq; + pool = pwq->pool; + WARN_ON_ONCE(pool != last_pool); } else { /* meh... not running there, queue here */ raw_spin_unlock(&last_pool->lock); - raw_spin_lock(&pwq->pool->lock); + raw_spin_lock(&pool->lock); } } else { - raw_spin_lock(&pwq->pool->lock); + raw_spin_lock(&pool->lock); } /* - * pwq is determined and locked. For unbound pools, we could have - * raced with pwq release and it could already be dead. If its - * refcnt is zero, repeat pwq selection. Note that pwqs never die - * without another pwq replacing it in the numa_pwq_tbl or while - * work items are executing on it, so the retrying is guaranteed to - * make forward-progress. + * pwq is determined and locked. For unbound pools, we could have raced + * with pwq release and it could already be dead. If its refcnt is zero, + * repeat pwq selection. Note that unbound pwqs never die without + * another pwq replacing it in cpu_pwq or while work items are executing + * on it, so the retrying is guaranteed to make forward-progress. */ if (unlikely(!pwq->refcnt)) { if (wq->flags & WQ_UNBOUND) { - raw_spin_unlock(&pwq->pool->lock); + raw_spin_unlock(&pool->lock); cpu_relax(); goto retry; } @@ -1707,21 +1795,20 @@ retry: work_flags = work_color_to_flags(pwq->work_color); if (likely(pwq->nr_active < pwq->max_active)) { + if (list_empty(&pool->worklist)) + pool->watchdog_ts = jiffies; + trace_workqueue_activate_work(work); pwq->nr_active++; - worklist = &pwq->pool->worklist; - if (list_empty(worklist)) - pwq->pool->watchdog_ts = jiffies; + insert_work(pwq, work, &pool->worklist, work_flags); + kick_pool(pool); } else { work_flags |= WORK_STRUCT_INACTIVE; - worklist = &pwq->inactive_works; + insert_work(pwq, work, &pwq->inactive_works, work_flags); } - debug_work_activate(work); - insert_work(pwq, work, worklist, work_flags); - out: - raw_spin_unlock(&pwq->pool->lock); + raw_spin_unlock(&pool->lock); rcu_read_unlock(); } @@ -1758,7 +1845,7 @@ bool queue_work_on(int cpu, struct workqueue_struct *wq, EXPORT_SYMBOL(queue_work_on); /** - * workqueue_select_cpu_near - Select a CPU based on NUMA node + * select_numa_node_cpu - Select a CPU based on NUMA node * @node: NUMA node ID that we want to select a CPU from * * This function will attempt to find a "random" cpu available on a given @@ -1766,14 +1853,10 @@ EXPORT_SYMBOL(queue_work_on); * WORK_CPU_UNBOUND indicating that we should just schedule to any * available CPU if we need to schedule this work. */ -static int workqueue_select_cpu_near(int node) +static int select_numa_node_cpu(int node) { int cpu; - /* No point in doing this if NUMA isn't enabled for workqueues */ - if (!wq_numa_enabled) - return WORK_CPU_UNBOUND; - /* Delay binding to CPU if node is not valid or online */ if (node < 0 || node >= MAX_NUMNODES || !node_online(node)) return WORK_CPU_UNBOUND; @@ -1830,7 +1913,7 @@ bool queue_work_node(int node, struct workqueue_struct *wq, local_irq_save(flags); if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { - int cpu = workqueue_select_cpu_near(node); + int cpu = select_numa_node_cpu(node); __queue_work(cpu, wq, work); ret = true; @@ -1985,60 +2068,6 @@ bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork) } EXPORT_SYMBOL(queue_rcu_work); -/** - * worker_enter_idle - enter idle state - * @worker: worker which is entering idle state - * - * @worker is entering idle state. Update stats and idle timer if - * necessary. - * - * LOCKING: - * raw_spin_lock_irq(pool->lock). - */ -static void worker_enter_idle(struct worker *worker) -{ - struct worker_pool *pool = worker->pool; - - if (WARN_ON_ONCE(worker->flags & WORKER_IDLE) || - WARN_ON_ONCE(!list_empty(&worker->entry) && - (worker->hentry.next || worker->hentry.pprev))) - return; - - /* can't use worker_set_flags(), also called from create_worker() */ - worker->flags |= WORKER_IDLE; - pool->nr_idle++; - worker->last_active = jiffies; - - /* idle_list is LIFO */ - list_add(&worker->entry, &pool->idle_list); - - if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) - mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); - - /* Sanity check nr_running. */ - WARN_ON_ONCE(pool->nr_workers == pool->nr_idle && pool->nr_running); -} - -/** - * worker_leave_idle - leave idle state - * @worker: worker which is leaving idle state - * - * @worker is leaving idle state. Update stats. - * - * LOCKING: - * raw_spin_lock_irq(pool->lock). - */ -static void worker_leave_idle(struct worker *worker) -{ - struct worker_pool *pool = worker->pool; - - if (WARN_ON_ONCE(!(worker->flags & WORKER_IDLE))) - return; - worker_clr_flags(worker, WORKER_IDLE); - pool->nr_idle--; - list_del_init(&worker->entry); -} - static struct worker *alloc_worker(int node) { struct worker *worker; @@ -2054,6 +2083,14 @@ static struct worker *alloc_worker(int node) return worker; } +static cpumask_t *pool_allowed_cpus(struct worker_pool *pool) +{ + if (pool->cpu < 0 && pool->attrs->affn_strict) + return pool->attrs->__pod_cpumask; + else + return pool->attrs->cpumask; +} + /** * worker_attach_to_pool() - attach a worker to a pool * @worker: worker to be attached @@ -2079,7 +2116,7 @@ static void worker_attach_to_pool(struct worker *worker, kthread_set_per_cpu(worker->task, pool->cpu); if (worker->rescue_wq) - set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask); + set_cpus_allowed_ptr(worker->task, pool_allowed_cpus(pool)); list_add_tail(&worker->node, &pool->workers); worker->pool = pool; @@ -2171,16 +2208,25 @@ static struct worker *create_worker(struct worker_pool *pool) } set_user_nice(worker->task, pool->attrs->nice); - kthread_bind_mask(worker->task, pool->attrs->cpumask); + kthread_bind_mask(worker->task, pool_allowed_cpus(pool)); /* successful, attach the worker to the pool */ worker_attach_to_pool(worker, pool); /* start the newly created worker */ raw_spin_lock_irq(&pool->lock); + worker->pool->nr_workers++; worker_enter_idle(worker); + kick_pool(pool); + + /* + * @worker is waiting on a completion in kthread() and will trigger hung + * check if not woken up soon. As kick_pool() might not have waken it + * up, wake it up explicitly once more. + */ wake_up_process(worker->task); + raw_spin_unlock_irq(&pool->lock); return worker; @@ -2308,9 +2354,8 @@ static void idle_worker_timeout(struct timer_list *t) static void idle_cull_fn(struct work_struct *work) { struct worker_pool *pool = container_of(work, struct worker_pool, idle_cull_work); - struct list_head cull_list; + LIST_HEAD(cull_list); - INIT_LIST_HEAD(&cull_list); /* * Grabbing wq_pool_attach_mutex here ensures an already-running worker * cannot proceed beyong worker_detach_from_pool() in its self-destruct @@ -2499,7 +2544,6 @@ __acquires(&pool->lock) struct pool_workqueue *pwq = get_work_pwq(work); struct worker_pool *pool = worker->pool; unsigned long work_data; - struct worker *collision; #ifdef CONFIG_LOCKDEP /* * It is permissible to free the struct work_struct from @@ -2516,18 +2560,6 @@ __acquires(&pool->lock) WARN_ON_ONCE(!(pool->flags & POOL_DISASSOCIATED) && raw_smp_processor_id() != pool->cpu); - /* - * A single work shouldn't be executed concurrently by - * multiple workers on a single cpu. Check whether anyone is - * already processing the work. If so, defer the work to the - * currently executing one. - */ - collision = find_worker_executing_work(pool, work); - if (unlikely(collision)) { - move_linked_works(work, &collision->scheduled, NULL); - return; - } - /* claim and dequeue */ debug_work_deactivate(work); hash_add(pool->busy_hash, &worker->hentry, (unsigned long)work); @@ -2556,14 +2588,12 @@ __acquires(&pool->lock) worker_set_flags(worker, WORKER_CPU_INTENSIVE); /* - * Wake up another worker if necessary. The condition is always - * false for normal per-cpu workers since nr_running would always - * be >= 1 at this point. This is used to chain execution of the - * pending work items for WORKER_NOT_RUNNING workers such as the - * UNBOUND and CPU_INTENSIVE ones. + * Kick @pool if necessary. It's always noop for per-cpu worker pools + * since nr_running would always be >= 1 at this point. This is used to + * chain execution of the pending work items for WORKER_NOT_RUNNING + * workers such as the UNBOUND and CPU_INTENSIVE ones. */ - if (need_more_worker(pool)) - wake_up_worker(pool); + kick_pool(pool); /* * Record the last pool and clear PENDING which should be the last @@ -2573,6 +2603,7 @@ __acquires(&pool->lock) */ set_work_pool_and_clear_pending(work, pool->id); + pwq->stats[PWQ_STAT_STARTED]++; raw_spin_unlock_irq(&pool->lock); lock_map_acquire(&pwq->wq->lockdep_map); @@ -2599,7 +2630,6 @@ __acquires(&pool->lock) * workqueues), so hiding them isn't a problem. */ lockdep_invariant_state(true); - pwq->stats[PWQ_STAT_STARTED]++; trace_workqueue_execute_start(work); worker->current_func(work); /* @@ -2665,9 +2695,15 @@ __acquires(&pool->lock) */ static void process_scheduled_works(struct worker *worker) { - while (!list_empty(&worker->scheduled)) { - struct work_struct *work = list_first_entry(&worker->scheduled, - struct work_struct, entry); + struct work_struct *work; + bool first = true; + + while ((work = list_first_entry_or_null(&worker->scheduled, + struct work_struct, entry))) { + if (first) { + worker->pool->watchdog_ts = jiffies; + first = false; + } process_one_work(worker, work); } } @@ -2748,17 +2784,8 @@ recheck: list_first_entry(&pool->worklist, struct work_struct, entry); - pool->watchdog_ts = jiffies; - - if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) { - /* optimization path, not strictly necessary */ - process_one_work(worker, work); - if (unlikely(!list_empty(&worker->scheduled))) - process_scheduled_works(worker); - } else { - move_linked_works(work, &worker->scheduled, NULL); + if (assign_work(work, worker, NULL)) process_scheduled_works(worker); - } } while (keep_working(pool)); worker_set_flags(worker, WORKER_PREP); @@ -2802,7 +2829,6 @@ static int rescuer_thread(void *__rescuer) { struct worker *rescuer = __rescuer; struct workqueue_struct *wq = rescuer->rescue_wq; - struct list_head *scheduled = &rescuer->scheduled; bool should_stop; set_user_nice(current, RESCUER_NICE_LEVEL); @@ -2833,7 +2859,6 @@ repeat: struct pool_workqueue, mayday_node); struct worker_pool *pool = pwq->pool; struct work_struct *work, *n; - bool first = true; __set_current_state(TASK_RUNNING); list_del_init(&pwq->mayday_node); @@ -2848,18 +2873,14 @@ repeat: * Slurp in all works issued via this workqueue and * process'em. */ - WARN_ON_ONCE(!list_empty(scheduled)); + WARN_ON_ONCE(!list_empty(&rescuer->scheduled)); list_for_each_entry_safe(work, n, &pool->worklist, entry) { - if (get_work_pwq(work) == pwq) { - if (first) - pool->watchdog_ts = jiffies; - move_linked_works(work, scheduled, &n); + if (get_work_pwq(work) == pwq && + assign_work(work, rescuer, &n)) pwq->stats[PWQ_STAT_RESCUED]++; - } - first = false; } - if (!list_empty(scheduled)) { + if (!list_empty(&rescuer->scheduled)) { process_scheduled_works(rescuer); /* @@ -2892,12 +2913,10 @@ repeat: put_pwq(pwq); /* - * Leave this pool. If need_more_worker() is %true, notify a - * regular worker; otherwise, we end up with 0 concurrency - * and stalling the execution. + * Leave this pool. Notify regular workers; otherwise, we end up + * with 0 concurrency and stalling the execution. */ - if (need_more_worker(pool)) - wake_up_worker(pool); + kick_pool(pool); raw_spin_unlock_irq(&pool->lock); @@ -3032,7 +3051,6 @@ static void insert_wq_barrier(struct pool_workqueue *pwq, pwq->nr_in_flight[work_color]++; work_flags |= work_color_to_flags(work_color); - debug_work_activate(&barr->work); insert_work(pwq, &barr->work, head, work_flags); } @@ -3695,6 +3713,7 @@ void free_workqueue_attrs(struct workqueue_attrs *attrs) { if (attrs) { free_cpumask_var(attrs->cpumask); + free_cpumask_var(attrs->__pod_cpumask); kfree(attrs); } } @@ -3716,8 +3735,11 @@ struct workqueue_attrs *alloc_workqueue_attrs(void) goto fail; if (!alloc_cpumask_var(&attrs->cpumask, GFP_KERNEL)) goto fail; + if (!alloc_cpumask_var(&attrs->__pod_cpumask, GFP_KERNEL)) + goto fail; cpumask_copy(attrs->cpumask, cpu_possible_mask); + attrs->affn_scope = WQ_AFFN_DFL; return attrs; fail: free_workqueue_attrs(attrs); @@ -3729,12 +3751,26 @@ static void copy_workqueue_attrs(struct workqueue_attrs *to, { to->nice = from->nice; cpumask_copy(to->cpumask, from->cpumask); + cpumask_copy(to->__pod_cpumask, from->__pod_cpumask); + to->affn_strict = from->affn_strict; + /* - * Unlike hash and equality test, this function doesn't ignore - * ->no_numa as it is used for both pool and wq attrs. Instead, - * get_unbound_pool() explicitly clears ->no_numa after copying. + * Unlike hash and equality test, copying shouldn't ignore wq-only + * fields as copying is used for both pool and wq attrs. Instead, + * get_unbound_pool() explicitly clears the fields. */ - to->no_numa = from->no_numa; + to->affn_scope = from->affn_scope; + to->ordered = from->ordered; +} + +/* + * Some attrs fields are workqueue-only. Clear them for worker_pool's. See the + * comments in 'struct workqueue_attrs' definition. + */ +static void wqattrs_clear_for_pool(struct workqueue_attrs *attrs) +{ + attrs->affn_scope = WQ_AFFN_NR_TYPES; + attrs->ordered = false; } /* hash value of the content of @attr */ @@ -3745,6 +3781,9 @@ static u32 wqattrs_hash(const struct workqueue_attrs *attrs) hash = jhash_1word(attrs->nice, hash); hash = jhash(cpumask_bits(attrs->cpumask), BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long), hash); + hash = jhash(cpumask_bits(attrs->__pod_cpumask), + BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long), hash); + hash = jhash_1word(attrs->affn_strict, hash); return hash; } @@ -3756,9 +3795,57 @@ static bool wqattrs_equal(const struct workqueue_attrs *a, return false; if (!cpumask_equal(a->cpumask, b->cpumask)) return false; + if (!cpumask_equal(a->__pod_cpumask, b->__pod_cpumask)) + return false; + if (a->affn_strict != b->affn_strict) + return false; return true; } +/* Update @attrs with actually available CPUs */ +static void wqattrs_actualize_cpumask(struct workqueue_attrs *attrs, + const cpumask_t *unbound_cpumask) +{ + /* + * Calculate the effective CPU mask of @attrs given @unbound_cpumask. If + * @attrs->cpumask doesn't overlap with @unbound_cpumask, we fallback to + * @unbound_cpumask. + */ + cpumask_and(attrs->cpumask, attrs->cpumask, unbound_cpumask); + if (unlikely(cpumask_empty(attrs->cpumask))) + cpumask_copy(attrs->cpumask, unbound_cpumask); +} + +/* find wq_pod_type to use for @attrs */ +static const struct wq_pod_type * +wqattrs_pod_type(const struct workqueue_attrs *attrs) +{ + enum wq_affn_scope scope; + struct wq_pod_type *pt; + + /* to synchronize access to wq_affn_dfl */ + lockdep_assert_held(&wq_pool_mutex); + + if (attrs->affn_scope == WQ_AFFN_DFL) + scope = wq_affn_dfl; + else + scope = attrs->affn_scope; + + pt = &wq_pod_types[scope]; + + if (!WARN_ON_ONCE(attrs->affn_scope == WQ_AFFN_NR_TYPES) && + likely(pt->nr_pods)) + return pt; + + /* + * Before workqueue_init_topology(), only SYSTEM is available which is + * initialized in workqueue_init_early(). + */ + pt = &wq_pod_types[WQ_AFFN_SYSTEM]; + BUG_ON(!pt->nr_pods); + return pt; +} + /** * init_worker_pool - initialize a newly zalloc'd worker_pool * @pool: worker_pool to initialize @@ -3797,6 +3884,9 @@ static int init_worker_pool(struct worker_pool *pool) pool->attrs = alloc_workqueue_attrs(); if (!pool->attrs) return -ENOMEM; + + wqattrs_clear_for_pool(pool->attrs); + return 0; } @@ -3844,12 +3934,8 @@ static void rcu_free_wq(struct rcu_head *rcu) container_of(rcu, struct workqueue_struct, rcu); wq_free_lockdep(wq); - - if (!(wq->flags & WQ_UNBOUND)) - free_percpu(wq->cpu_pwqs); - else - free_workqueue_attrs(wq->unbound_attrs); - + free_percpu(wq->cpu_pwq); + free_workqueue_attrs(wq->unbound_attrs); kfree(wq); } @@ -3876,10 +3962,8 @@ static void rcu_free_pool(struct rcu_head *rcu) static void put_unbound_pool(struct worker_pool *pool) { DECLARE_COMPLETION_ONSTACK(detach_completion); - struct list_head cull_list; struct worker *worker; - - INIT_LIST_HEAD(&cull_list); + LIST_HEAD(cull_list); lockdep_assert_held(&wq_pool_mutex); @@ -3963,10 +4047,10 @@ static void put_unbound_pool(struct worker_pool *pool) */ static struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs) { + struct wq_pod_type *pt = &wq_pod_types[WQ_AFFN_NUMA]; u32 hash = wqattrs_hash(attrs); struct worker_pool *pool; - int node; - int target_node = NUMA_NO_NODE; + int pod, node = NUMA_NO_NODE; lockdep_assert_held(&wq_pool_mutex); @@ -3978,31 +4062,22 @@ static struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs) } } - /* if cpumask is contained inside a NUMA node, we belong to that node */ - if (wq_numa_enabled) { - for_each_node(node) { - if (cpumask_subset(attrs->cpumask, - wq_numa_possible_cpumask[node])) { - target_node = node; - break; - } + /* If __pod_cpumask is contained inside a NUMA pod, that's our node */ + for (pod = 0; pod < pt->nr_pods; pod++) { + if (cpumask_subset(attrs->__pod_cpumask, pt->pod_cpus[pod])) { + node = pt->pod_node[pod]; + break; } } /* nope, create a new one */ - pool = kzalloc_node(sizeof(*pool), GFP_KERNEL, target_node); + pool = kzalloc_node(sizeof(*pool), GFP_KERNEL, node); if (!pool || init_worker_pool(pool) < 0) goto fail; - lockdep_set_subclass(&pool->lock, 1); /* see put_pwq() */ + pool->node = node; copy_workqueue_attrs(pool->attrs, attrs); - pool->node = target_node; - - /* - * no_numa isn't a worker_pool attribute, always clear it. See - * 'struct workqueue_attrs' comments for detail. - */ - pool->attrs->no_numa = false; + wqattrs_clear_for_pool(pool->attrs); if (worker_pool_assign_id(pool) < 0) goto fail; @@ -4028,34 +4103,33 @@ static void rcu_free_pwq(struct rcu_head *rcu) } /* - * Scheduled on system_wq by put_pwq() when an unbound pwq hits zero refcnt - * and needs to be destroyed. + * Scheduled on pwq_release_worker by put_pwq() when an unbound pwq hits zero + * refcnt and needs to be destroyed. */ -static void pwq_unbound_release_workfn(struct work_struct *work) +static void pwq_release_workfn(struct kthread_work *work) { struct pool_workqueue *pwq = container_of(work, struct pool_workqueue, - unbound_release_work); + release_work); struct workqueue_struct *wq = pwq->wq; struct worker_pool *pool = pwq->pool; bool is_last = false; /* - * when @pwq is not linked, it doesn't hold any reference to the + * When @pwq is not linked, it doesn't hold any reference to the * @wq, and @wq is invalid to access. */ if (!list_empty(&pwq->pwqs_node)) { - if (WARN_ON_ONCE(!(wq->flags & WQ_UNBOUND))) - return; - mutex_lock(&wq->mutex); list_del_rcu(&pwq->pwqs_node); is_last = list_empty(&wq->pwqs); mutex_unlock(&wq->mutex); } - mutex_lock(&wq_pool_mutex); - put_unbound_pool(pool); - mutex_unlock(&wq_pool_mutex); + if (wq->flags & WQ_UNBOUND) { + mutex_lock(&wq_pool_mutex); + put_unbound_pool(pool); + mutex_unlock(&wq_pool_mutex); + } call_rcu(&pwq->rcu, rcu_free_pwq); @@ -4099,24 +4173,13 @@ static void pwq_adjust_max_active(struct pool_workqueue *pwq) * is updated and visible. */ if (!freezable || !workqueue_freezing) { - bool kick = false; - pwq->max_active = wq->saved_max_active; while (!list_empty(&pwq->inactive_works) && - pwq->nr_active < pwq->max_active) { + pwq->nr_active < pwq->max_active) pwq_activate_first_inactive(pwq); - kick = true; - } - /* - * Need to kick a worker after thawed or an unbound wq's - * max_active is bumped. In realtime scenarios, always kicking a - * worker will cause interference on the isolated cpu cores, so - * let's kick iff work items were activated. - */ - if (kick) - wake_up_worker(pwq->pool); + kick_pool(pwq->pool); } else { pwq->max_active = 0; } @@ -4139,7 +4202,7 @@ static void init_pwq(struct pool_workqueue *pwq, struct workqueue_struct *wq, INIT_LIST_HEAD(&pwq->inactive_works); INIT_LIST_HEAD(&pwq->pwqs_node); INIT_LIST_HEAD(&pwq->mayday_node); - INIT_WORK(&pwq->unbound_release_work, pwq_unbound_release_workfn); + kthread_init_work(&pwq->release_work, pwq_release_workfn); } /* sync @pwq with the current state of its associated wq and link it */ @@ -4187,61 +4250,49 @@ static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq, } /** - * wq_calc_node_cpumask - calculate a wq_attrs' cpumask for the specified node + * wq_calc_pod_cpumask - calculate a wq_attrs' cpumask for a pod * @attrs: the wq_attrs of the default pwq of the target workqueue - * @node: the target NUMA node + * @cpu: the target CPU * @cpu_going_down: if >= 0, the CPU to consider as offline - * @cpumask: outarg, the resulting cpumask * - * Calculate the cpumask a workqueue with @attrs should use on @node. If - * @cpu_going_down is >= 0, that cpu is considered offline during - * calculation. The result is stored in @cpumask. + * Calculate the cpumask a workqueue with @attrs should use on @pod. If + * @cpu_going_down is >= 0, that cpu is considered offline during calculation. + * The result is stored in @attrs->__pod_cpumask. * - * If NUMA affinity is not enabled, @attrs->cpumask is always used. If - * enabled and @node has online CPUs requested by @attrs, the returned - * cpumask is the intersection of the possible CPUs of @node and - * @attrs->cpumask. + * If pod affinity is not enabled, @attrs->cpumask is always used. If enabled + * and @pod has online CPUs requested by @attrs, the returned cpumask is the + * intersection of the possible CPUs of @pod and @attrs->cpumask. * - * The caller is responsible for ensuring that the cpumask of @node stays - * stable. - * - * Return: %true if the resulting @cpumask is different from @attrs->cpumask, - * %false if equal. + * The caller is responsible for ensuring that the cpumask of @pod stays stable. */ -static bool wq_calc_node_cpumask(const struct workqueue_attrs *attrs, int node, - int cpu_going_down, cpumask_t *cpumask) +static void wq_calc_pod_cpumask(struct workqueue_attrs *attrs, int cpu, + int cpu_going_down) { - if (!wq_numa_enabled || attrs->no_numa) - goto use_dfl; + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int pod = pt->cpu_pod[cpu]; - /* does @node have any online CPUs @attrs wants? */ - cpumask_and(cpumask, cpumask_of_node(node), attrs->cpumask); + /* does @pod have any online CPUs @attrs wants? */ + cpumask_and(attrs->__pod_cpumask, pt->pod_cpus[pod], attrs->cpumask); + cpumask_and(attrs->__pod_cpumask, attrs->__pod_cpumask, cpu_online_mask); if (cpu_going_down >= 0) - cpumask_clear_cpu(cpu_going_down, cpumask); + cpumask_clear_cpu(cpu_going_down, attrs->__pod_cpumask); - if (cpumask_empty(cpumask)) - goto use_dfl; - - /* yeap, return possible CPUs in @node that @attrs wants */ - cpumask_and(cpumask, attrs->cpumask, wq_numa_possible_cpumask[node]); - - if (cpumask_empty(cpumask)) { - pr_warn_once("WARNING: workqueue cpumask: online intersect > " - "possible intersect\n"); - return false; + if (cpumask_empty(attrs->__pod_cpumask)) { + cpumask_copy(attrs->__pod_cpumask, attrs->cpumask); + return; } - return !cpumask_equal(cpumask, attrs->cpumask); + /* yeap, return possible CPUs in @pod that @attrs wants */ + cpumask_and(attrs->__pod_cpumask, attrs->cpumask, pt->pod_cpus[pod]); -use_dfl: - cpumask_copy(cpumask, attrs->cpumask); - return false; + if (cpumask_empty(attrs->__pod_cpumask)) + pr_warn_once("WARNING: workqueue cpumask: online intersect > " + "possible intersect\n"); } -/* install @pwq into @wq's numa_pwq_tbl[] for @node and return the old pwq */ -static struct pool_workqueue *numa_pwq_tbl_install(struct workqueue_struct *wq, - int node, - struct pool_workqueue *pwq) +/* install @pwq into @wq's cpu_pwq and return the old pwq */ +static struct pool_workqueue *install_unbound_pwq(struct workqueue_struct *wq, + int cpu, struct pool_workqueue *pwq) { struct pool_workqueue *old_pwq; @@ -4251,8 +4302,8 @@ static struct pool_workqueue *numa_pwq_tbl_install(struct workqueue_struct *wq, /* link_pwq() can handle duplicate calls */ link_pwq(pwq); - old_pwq = rcu_access_pointer(wq->numa_pwq_tbl[node]); - rcu_assign_pointer(wq->numa_pwq_tbl[node], pwq); + old_pwq = rcu_access_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu)); + rcu_assign_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu), pwq); return old_pwq; } @@ -4269,10 +4320,10 @@ struct apply_wqattrs_ctx { static void apply_wqattrs_cleanup(struct apply_wqattrs_ctx *ctx) { if (ctx) { - int node; + int cpu; - for_each_node(node) - put_pwq_unlocked(ctx->pwq_tbl[node]); + for_each_possible_cpu(cpu) + put_pwq_unlocked(ctx->pwq_tbl[cpu]); put_pwq_unlocked(ctx->dfl_pwq); free_workqueue_attrs(ctx->attrs); @@ -4288,76 +4339,64 @@ apply_wqattrs_prepare(struct workqueue_struct *wq, const cpumask_var_t unbound_cpumask) { struct apply_wqattrs_ctx *ctx; - struct workqueue_attrs *new_attrs, *tmp_attrs; - int node; + struct workqueue_attrs *new_attrs; + int cpu; lockdep_assert_held(&wq_pool_mutex); - ctx = kzalloc(struct_size(ctx, pwq_tbl, nr_node_ids), GFP_KERNEL); + if (WARN_ON(attrs->affn_scope < 0 || + attrs->affn_scope >= WQ_AFFN_NR_TYPES)) + return ERR_PTR(-EINVAL); + + ctx = kzalloc(struct_size(ctx, pwq_tbl, nr_cpu_ids), GFP_KERNEL); new_attrs = alloc_workqueue_attrs(); - tmp_attrs = alloc_workqueue_attrs(); - if (!ctx || !new_attrs || !tmp_attrs) + if (!ctx || !new_attrs) goto out_free; - /* - * Calculate the attrs of the default pwq with unbound_cpumask - * which is wq_unbound_cpumask or to set to wq_unbound_cpumask. - * If the user configured cpumask doesn't overlap with the - * wq_unbound_cpumask, we fallback to the wq_unbound_cpumask. - */ - copy_workqueue_attrs(new_attrs, attrs); - cpumask_and(new_attrs->cpumask, new_attrs->cpumask, unbound_cpumask); - if (unlikely(cpumask_empty(new_attrs->cpumask))) - cpumask_copy(new_attrs->cpumask, unbound_cpumask); - - /* - * We may create multiple pwqs with differing cpumasks. Make a - * copy of @new_attrs which will be modified and used to obtain - * pools. - */ - copy_workqueue_attrs(tmp_attrs, new_attrs); - /* * If something goes wrong during CPU up/down, we'll fall back to * the default pwq covering whole @attrs->cpumask. Always create * it even if we don't use it immediately. */ + copy_workqueue_attrs(new_attrs, attrs); + wqattrs_actualize_cpumask(new_attrs, unbound_cpumask); + cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask); ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs); if (!ctx->dfl_pwq) goto out_free; - for_each_node(node) { - if (wq_calc_node_cpumask(new_attrs, node, -1, tmp_attrs->cpumask)) { - ctx->pwq_tbl[node] = alloc_unbound_pwq(wq, tmp_attrs); - if (!ctx->pwq_tbl[node]) - goto out_free; - } else { + for_each_possible_cpu(cpu) { + if (new_attrs->ordered) { ctx->dfl_pwq->refcnt++; - ctx->pwq_tbl[node] = ctx->dfl_pwq; + ctx->pwq_tbl[cpu] = ctx->dfl_pwq; + } else { + wq_calc_pod_cpumask(new_attrs, cpu, -1); + ctx->pwq_tbl[cpu] = alloc_unbound_pwq(wq, new_attrs); + if (!ctx->pwq_tbl[cpu]) + goto out_free; } } /* save the user configured attrs and sanitize it. */ copy_workqueue_attrs(new_attrs, attrs); cpumask_and(new_attrs->cpumask, new_attrs->cpumask, cpu_possible_mask); + cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask); ctx->attrs = new_attrs; ctx->wq = wq; - free_workqueue_attrs(tmp_attrs); return ctx; out_free: - free_workqueue_attrs(tmp_attrs); free_workqueue_attrs(new_attrs); apply_wqattrs_cleanup(ctx); - return NULL; + return ERR_PTR(-ENOMEM); } /* set attrs and install prepared pwqs, @ctx points to old pwqs on return */ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) { - int node; + int cpu; /* all pwqs have been created successfully, let's install'em */ mutex_lock(&ctx->wq->mutex); @@ -4365,9 +4404,9 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) copy_workqueue_attrs(ctx->wq->unbound_attrs, ctx->attrs); /* save the previous pwq and install the new one */ - for_each_node(node) - ctx->pwq_tbl[node] = numa_pwq_tbl_install(ctx->wq, node, - ctx->pwq_tbl[node]); + for_each_possible_cpu(cpu) + ctx->pwq_tbl[cpu] = install_unbound_pwq(ctx->wq, cpu, + ctx->pwq_tbl[cpu]); /* @dfl_pwq might not have been used, ensure it's linked */ link_pwq(ctx->dfl_pwq); @@ -4407,8 +4446,8 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq, } ctx = apply_wqattrs_prepare(wq, attrs, wq_unbound_cpumask); - if (!ctx) - return -ENOMEM; + if (IS_ERR(ctx)) + return PTR_ERR(ctx); /* the ctx has been prepared successfully, let's commit it */ apply_wqattrs_commit(ctx); @@ -4422,12 +4461,11 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq, * @wq: the target workqueue * @attrs: the workqueue_attrs to apply, allocated with alloc_workqueue_attrs() * - * Apply @attrs to an unbound workqueue @wq. Unless disabled, on NUMA - * machines, this function maps a separate pwq to each NUMA node with - * possibles CPUs in @attrs->cpumask so that work items are affine to the - * NUMA node it was issued on. Older pwqs are released as in-flight work - * items finish. Note that a work item which repeatedly requeues itself - * back-to-back will stay on its current pwq. + * Apply @attrs to an unbound workqueue @wq. Unless disabled, this function maps + * a separate pwq to each CPU pod with possibles CPUs in @attrs->cpumask so that + * work items are affine to the pod it was issued on. Older pwqs are released as + * in-flight work items finish. Note that a work item which repeatedly requeues + * itself back-to-back will stay on its current pwq. * * Performs GFP_KERNEL allocations. * @@ -4450,40 +4488,37 @@ int apply_workqueue_attrs(struct workqueue_struct *wq, } /** - * wq_update_unbound_numa - update NUMA affinity of a wq for CPU hot[un]plug + * wq_update_pod - update pod affinity of a wq for CPU hot[un]plug * @wq: the target workqueue - * @cpu: the CPU coming up or going down + * @cpu: the CPU to update pool association for + * @hotplug_cpu: the CPU coming up or going down * @online: whether @cpu is coming up or going down * * This function is to be called from %CPU_DOWN_PREPARE, %CPU_ONLINE and - * %CPU_DOWN_FAILED. @cpu is being hot[un]plugged, update NUMA affinity of + * %CPU_DOWN_FAILED. @cpu is being hot[un]plugged, update pod affinity of * @wq accordingly. * - * If NUMA affinity can't be adjusted due to memory allocation failure, it - * falls back to @wq->dfl_pwq which may not be optimal but is always - * correct. * - * Note that when the last allowed CPU of a NUMA node goes offline for a - * workqueue with a cpumask spanning multiple nodes, the workers which were - * already executing the work items for the workqueue will lose their CPU - * affinity and may execute on any CPU. This is similar to how per-cpu - * workqueues behave on CPU_DOWN. If a workqueue user wants strict - * affinity, it's the user's responsibility to flush the work item from - * CPU_DOWN_PREPARE. + * If pod affinity can't be adjusted due to memory allocation failure, it falls + * back to @wq->dfl_pwq which may not be optimal but is always correct. + * + * Note that when the last allowed CPU of a pod goes offline for a workqueue + * with a cpumask spanning multiple pods, the workers which were already + * executing the work items for the workqueue will lose their CPU affinity and + * may execute on any CPU. This is similar to how per-cpu workqueues behave on + * CPU_DOWN. If a workqueue user wants strict affinity, it's the user's + * responsibility to flush the work item from CPU_DOWN_PREPARE. */ -static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, - bool online) +static void wq_update_pod(struct workqueue_struct *wq, int cpu, + int hotplug_cpu, bool online) { - int node = cpu_to_node(cpu); - int cpu_off = online ? -1 : cpu; + int off_cpu = online ? -1 : hotplug_cpu; struct pool_workqueue *old_pwq = NULL, *pwq; struct workqueue_attrs *target_attrs; - cpumask_t *cpumask; lockdep_assert_held(&wq_pool_mutex); - if (!wq_numa_enabled || !(wq->flags & WQ_UNBOUND) || - wq->unbound_attrs->no_numa) + if (!(wq->flags & WQ_UNBOUND) || wq->unbound_attrs->ordered) return; /* @@ -4491,36 +4526,29 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, * Let's use a preallocated one. The following buf is protected by * CPU hotplug exclusion. */ - target_attrs = wq_update_unbound_numa_attrs_buf; - cpumask = target_attrs->cpumask; + target_attrs = wq_update_pod_attrs_buf; copy_workqueue_attrs(target_attrs, wq->unbound_attrs); - pwq = unbound_pwq_by_node(wq, node); + wqattrs_actualize_cpumask(target_attrs, wq_unbound_cpumask); - /* - * Let's determine what needs to be done. If the target cpumask is - * different from the default pwq's, we need to compare it to @pwq's - * and create a new one if they don't match. If the target cpumask - * equals the default pwq's, the default pwq should be used. - */ - if (wq_calc_node_cpumask(wq->dfl_pwq->pool->attrs, node, cpu_off, cpumask)) { - if (cpumask_equal(cpumask, pwq->pool->attrs->cpumask)) - return; - } else { - goto use_dfl_pwq; - } + /* nothing to do if the target cpumask matches the current pwq */ + wq_calc_pod_cpumask(target_attrs, cpu, off_cpu); + pwq = rcu_dereference_protected(*per_cpu_ptr(wq->cpu_pwq, cpu), + lockdep_is_held(&wq_pool_mutex)); + if (wqattrs_equal(target_attrs, pwq->pool->attrs)) + return; /* create a new pwq */ pwq = alloc_unbound_pwq(wq, target_attrs); if (!pwq) { - pr_warn("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n", + pr_warn("workqueue: allocation failed while updating CPU pod affinity of \"%s\"\n", wq->name); goto use_dfl_pwq; } /* Install the new pwq. */ mutex_lock(&wq->mutex); - old_pwq = numa_pwq_tbl_install(wq, node, pwq); + old_pwq = install_unbound_pwq(wq, cpu, pwq); goto out_unlock; use_dfl_pwq: @@ -4528,7 +4556,7 @@ use_dfl_pwq: raw_spin_lock_irq(&wq->dfl_pwq->pool->lock); get_pwq(wq->dfl_pwq); raw_spin_unlock_irq(&wq->dfl_pwq->pool->lock); - old_pwq = numa_pwq_tbl_install(wq, node, wq->dfl_pwq); + old_pwq = install_unbound_pwq(wq, cpu, wq->dfl_pwq); out_unlock: mutex_unlock(&wq->mutex); put_pwq_unlocked(old_pwq); @@ -4539,21 +4567,26 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) bool highpri = wq->flags & WQ_HIGHPRI; int cpu, ret; + wq->cpu_pwq = alloc_percpu(struct pool_workqueue *); + if (!wq->cpu_pwq) + goto enomem; + if (!(wq->flags & WQ_UNBOUND)) { - wq->cpu_pwqs = alloc_percpu(struct pool_workqueue); - if (!wq->cpu_pwqs) - return -ENOMEM; - for_each_possible_cpu(cpu) { - struct pool_workqueue *pwq = - per_cpu_ptr(wq->cpu_pwqs, cpu); - struct worker_pool *cpu_pools = - per_cpu(cpu_worker_pools, cpu); + struct pool_workqueue **pwq_p = + per_cpu_ptr(wq->cpu_pwq, cpu); + struct worker_pool *pool = + &(per_cpu_ptr(cpu_worker_pools, cpu)[highpri]); - init_pwq(pwq, wq, &cpu_pools[highpri]); + *pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, + pool->node); + if (!*pwq_p) + goto enomem; + + init_pwq(*pwq_p, wq, pool); mutex_lock(&wq->mutex); - link_pwq(pwq); + link_pwq(*pwq_p); mutex_unlock(&wq->mutex); } return 0; @@ -4572,18 +4605,25 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) cpus_read_unlock(); return ret; + +enomem: + if (wq->cpu_pwq) { + for_each_possible_cpu(cpu) + kfree(*per_cpu_ptr(wq->cpu_pwq, cpu)); + free_percpu(wq->cpu_pwq); + wq->cpu_pwq = NULL; + } + return -ENOMEM; } static int wq_clamp_max_active(int max_active, unsigned int flags, const char *name) { - int lim = flags & WQ_UNBOUND ? WQ_UNBOUND_MAX_ACTIVE : WQ_MAX_ACTIVE; - - if (max_active < 1 || max_active > lim) + if (max_active < 1 || max_active > WQ_MAX_ACTIVE) pr_warn("workqueue: max_active %d requested for %s is out of range, clamping between %d and %d\n", - max_active, name, 1, lim); + max_active, name, 1, WQ_MAX_ACTIVE); - return clamp_val(max_active, 1, lim); + return clamp_val(max_active, 1, WQ_MAX_ACTIVE); } /* @@ -4606,7 +4646,7 @@ static int init_rescuer(struct workqueue_struct *wq) } rescuer->rescue_wq = wq; - rescuer->task = kthread_create(rescuer_thread, rescuer, "%s", wq->name); + rescuer->task = kthread_create(rescuer_thread, rescuer, "kworker/R-%s", wq->name); if (IS_ERR(rescuer->task)) { ret = PTR_ERR(rescuer->task); pr_err("workqueue: Failed to create a rescuer kthread for wq \"%s\": %pe", @@ -4627,17 +4667,15 @@ struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...) { - size_t tbl_size = 0; va_list args; struct workqueue_struct *wq; struct pool_workqueue *pwq; /* - * Unbound && max_active == 1 used to imply ordered, which is no - * longer the case on NUMA machines due to per-node pools. While + * Unbound && max_active == 1 used to imply ordered, which is no longer + * the case on many machines due to per-pod pools. While * alloc_ordered_workqueue() is the right way to create an ordered - * workqueue, keep the previous behavior to avoid subtle breakages - * on NUMA. + * workqueue, keep the previous behavior to avoid subtle breakages. */ if ((flags & WQ_UNBOUND) && max_active == 1) flags |= __WQ_ORDERED; @@ -4647,10 +4685,7 @@ struct workqueue_struct *alloc_workqueue(const char *fmt, flags |= WQ_UNBOUND; /* allocate wq and format name */ - if (flags & WQ_UNBOUND) - tbl_size = nr_node_ids * sizeof(wq->numa_pwq_tbl[0]); - - wq = kzalloc(sizeof(*wq) + tbl_size, GFP_KERNEL); + wq = kzalloc(sizeof(*wq), GFP_KERNEL); if (!wq) return NULL; @@ -4745,7 +4780,7 @@ static bool pwq_busy(struct pool_workqueue *pwq) void destroy_workqueue(struct workqueue_struct *wq) { struct pool_workqueue *pwq; - int node; + int cpu; /* * Remove it from sysfs first so that sanity check failure doesn't @@ -4804,33 +4839,23 @@ void destroy_workqueue(struct workqueue_struct *wq) list_del_rcu(&wq->list); mutex_unlock(&wq_pool_mutex); - if (!(wq->flags & WQ_UNBOUND)) { - wq_unregister_lockdep(wq); - /* - * The base ref is never dropped on per-cpu pwqs. Directly - * schedule RCU free. - */ - call_rcu(&wq->rcu, rcu_free_wq); - } else { - /* - * We're the sole accessor of @wq at this point. Directly - * access numa_pwq_tbl[] and dfl_pwq to put the base refs. - * @wq will be freed when the last pwq is released. - */ - for_each_node(node) { - pwq = rcu_access_pointer(wq->numa_pwq_tbl[node]); - RCU_INIT_POINTER(wq->numa_pwq_tbl[node], NULL); - put_pwq_unlocked(pwq); - } + /* + * We're the sole accessor of @wq. Directly access cpu_pwq and dfl_pwq + * to put the base refs. @wq will be auto-destroyed from the last + * pwq_put. RCU read lock prevents @wq from going away from under us. + */ + rcu_read_lock(); - /* - * Put dfl_pwq. @wq may be freed any time after dfl_pwq is - * put. Don't access it afterwards. - */ - pwq = wq->dfl_pwq; - wq->dfl_pwq = NULL; + for_each_possible_cpu(cpu) { + pwq = rcu_access_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu)); + RCU_INIT_POINTER(*per_cpu_ptr(wq->cpu_pwq, cpu), NULL); put_pwq_unlocked(pwq); } + + put_pwq_unlocked(wq->dfl_pwq); + wq->dfl_pwq = NULL; + + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(destroy_workqueue); @@ -4907,10 +4932,11 @@ bool current_is_workqueue_rescuer(void) * unreliable and only useful as advisory hints or for debugging. * * If @cpu is WORK_CPU_UNBOUND, the test is performed on the local CPU. - * Note that both per-cpu and unbound workqueues may be associated with - * multiple pool_workqueues which have separate congested states. A - * workqueue being congested on one CPU doesn't mean the workqueue is also - * contested on other CPUs / NUMA nodes. + * + * With the exception of ordered workqueues, all workqueues have per-cpu + * pool_workqueues, each with its own congested state. A workqueue being + * congested on one CPU doesn't mean that the workqueue is contested on any + * other CPUs. * * Return: * %true if congested, %false otherwise. @@ -4926,12 +4952,9 @@ bool workqueue_congested(int cpu, struct workqueue_struct *wq) if (cpu == WORK_CPU_UNBOUND) cpu = smp_processor_id(); - if (!(wq->flags & WQ_UNBOUND)) - pwq = per_cpu_ptr(wq->cpu_pwqs, cpu); - else - pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); - + pwq = *per_cpu_ptr(wq->cpu_pwq, cpu); ret = !list_empty(&pwq->inactive_works); + preempt_enable(); rcu_read_unlock(); @@ -5406,7 +5429,7 @@ static void unbind_workers(int cpu) * worker blocking could lead to lengthy stalls. Kick off * unbound chain execution of currently pending work items. */ - wake_up_worker(pool); + kick_pool(pool); raw_spin_unlock_irq(&pool->lock); @@ -5439,7 +5462,7 @@ static void rebind_workers(struct worker_pool *pool) for_each_pool_worker(worker, pool) { kthread_set_per_cpu(worker->task, pool->cpu); WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, - pool->attrs->cpumask) < 0); + pool_allowed_cpus(pool)) < 0); } raw_spin_lock_irq(&pool->lock); @@ -5533,9 +5556,18 @@ int workqueue_online_cpu(unsigned int cpu) mutex_unlock(&wq_pool_attach_mutex); } - /* update NUMA affinity of unbound workqueues */ - list_for_each_entry(wq, &workqueues, list) - wq_update_unbound_numa(wq, cpu, true); + /* update pod affinity of unbound workqueues */ + list_for_each_entry(wq, &workqueues, list) { + struct workqueue_attrs *attrs = wq->unbound_attrs; + + if (attrs) { + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int tcpu; + + for_each_cpu(tcpu, pt->pod_cpus[pt->cpu_pod[cpu]]) + wq_update_pod(wq, tcpu, cpu, true); + } + } mutex_unlock(&wq_pool_mutex); return 0; @@ -5551,10 +5583,19 @@ int workqueue_offline_cpu(unsigned int cpu) unbind_workers(cpu); - /* update NUMA affinity of unbound workqueues */ + /* update pod affinity of unbound workqueues */ mutex_lock(&wq_pool_mutex); - list_for_each_entry(wq, &workqueues, list) - wq_update_unbound_numa(wq, cpu, false); + list_for_each_entry(wq, &workqueues, list) { + struct workqueue_attrs *attrs = wq->unbound_attrs; + + if (attrs) { + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int tcpu; + + for_each_cpu(tcpu, pt->pod_cpus[pt->cpu_pod[cpu]]) + wq_update_pod(wq, tcpu, cpu, false); + } + } mutex_unlock(&wq_pool_mutex); return 0; @@ -5750,8 +5791,8 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask) continue; ctx = apply_wqattrs_prepare(wq, wq->unbound_attrs, unbound_cpumask); - if (!ctx) { - ret = -ENOMEM; + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); break; } @@ -5809,21 +5850,72 @@ out_unlock: return ret; } +static int parse_affn_scope(const char *val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wq_affn_names); i++) { + if (!strncasecmp(val, wq_affn_names[i], strlen(wq_affn_names[i]))) + return i; + } + return -EINVAL; +} + +static int wq_affn_dfl_set(const char *val, const struct kernel_param *kp) +{ + struct workqueue_struct *wq; + int affn, cpu; + + affn = parse_affn_scope(val); + if (affn < 0) + return affn; + if (affn == WQ_AFFN_DFL) + return -EINVAL; + + cpus_read_lock(); + mutex_lock(&wq_pool_mutex); + + wq_affn_dfl = affn; + + list_for_each_entry(wq, &workqueues, list) { + for_each_online_cpu(cpu) { + wq_update_pod(wq, cpu, cpu, true); + } + } + + mutex_unlock(&wq_pool_mutex); + cpus_read_unlock(); + + return 0; +} + +static int wq_affn_dfl_get(char *buffer, const struct kernel_param *kp) +{ + return scnprintf(buffer, PAGE_SIZE, "%s\n", wq_affn_names[wq_affn_dfl]); +} + +static const struct kernel_param_ops wq_affn_dfl_ops = { + .set = wq_affn_dfl_set, + .get = wq_affn_dfl_get, +}; + +module_param_cb(default_affinity_scope, &wq_affn_dfl_ops, NULL, 0644); + #ifdef CONFIG_SYSFS /* * Workqueues with WQ_SYSFS flag set is visible to userland via * /sys/bus/workqueue/devices/WQ_NAME. All visible workqueues have the * following attributes. * - * per_cpu RO bool : whether the workqueue is per-cpu or unbound - * max_active RW int : maximum number of in-flight work items + * per_cpu RO bool : whether the workqueue is per-cpu or unbound + * max_active RW int : maximum number of in-flight work items * * Unbound workqueues have the following extra attributes. * - * pool_ids RO int : the associated pool IDs for each node - * nice RW int : nice value of the workers - * cpumask RW mask : bitmask of allowed CPUs for the workers - * numa RW bool : whether enable NUMA affinity + * nice RW int : nice value of the workers + * cpumask RW mask : bitmask of allowed CPUs for the workers + * affinity_scope RW str : worker CPU affinity scope (cache, numa, none) + * affinity_strict RW bool : worker CPU affinity is strict */ struct wq_device { struct workqueue_struct *wq; @@ -5876,28 +5968,6 @@ static struct attribute *wq_sysfs_attrs[] = { }; ATTRIBUTE_GROUPS(wq_sysfs); -static ssize_t wq_pool_ids_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct workqueue_struct *wq = dev_to_wq(dev); - const char *delim = ""; - int node, written = 0; - - cpus_read_lock(); - rcu_read_lock(); - for_each_node(node) { - written += scnprintf(buf + written, PAGE_SIZE - written, - "%s%d:%d", delim, node, - unbound_pwq_by_node(wq, node)->pool->id); - delim = " "; - } - written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); - rcu_read_unlock(); - cpus_read_unlock(); - - return written; -} - static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -5988,50 +6058,84 @@ out_unlock: return ret ?: count; } -static ssize_t wq_numa_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t wq_affn_scope_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct workqueue_struct *wq = dev_to_wq(dev); int written; mutex_lock(&wq->mutex); - written = scnprintf(buf, PAGE_SIZE, "%d\n", - !wq->unbound_attrs->no_numa); + if (wq->unbound_attrs->affn_scope == WQ_AFFN_DFL) + written = scnprintf(buf, PAGE_SIZE, "%s (%s)\n", + wq_affn_names[WQ_AFFN_DFL], + wq_affn_names[wq_affn_dfl]); + else + written = scnprintf(buf, PAGE_SIZE, "%s\n", + wq_affn_names[wq->unbound_attrs->affn_scope]); mutex_unlock(&wq->mutex); return written; } -static ssize_t wq_numa_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t wq_affn_scope_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct workqueue_attrs *attrs; + int affn, ret = -ENOMEM; + + affn = parse_affn_scope(buf); + if (affn < 0) + return affn; + + apply_wqattrs_lock(); + attrs = wq_sysfs_prep_attrs(wq); + if (attrs) { + attrs->affn_scope = affn; + ret = apply_workqueue_attrs_locked(wq, attrs); + } + apply_wqattrs_unlock(); + free_workqueue_attrs(attrs); + return ret ?: count; +} + +static ssize_t wq_affinity_strict_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + wq->unbound_attrs->affn_strict); +} + +static ssize_t wq_affinity_strict_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct workqueue_struct *wq = dev_to_wq(dev); struct workqueue_attrs *attrs; int v, ret = -ENOMEM; + if (sscanf(buf, "%d", &v) != 1) + return -EINVAL; + apply_wqattrs_lock(); - attrs = wq_sysfs_prep_attrs(wq); - if (!attrs) - goto out_unlock; - - ret = -EINVAL; - if (sscanf(buf, "%d", &v) == 1) { - attrs->no_numa = !v; + if (attrs) { + attrs->affn_strict = (bool)v; ret = apply_workqueue_attrs_locked(wq, attrs); } - -out_unlock: apply_wqattrs_unlock(); free_workqueue_attrs(attrs); return ret ?: count; } static struct device_attribute wq_sysfs_unbound_attrs[] = { - __ATTR(pool_ids, 0444, wq_pool_ids_show, NULL), __ATTR(nice, 0644, wq_nice_show, wq_nice_store), __ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store), - __ATTR(numa, 0644, wq_numa_show, wq_numa_store), + __ATTR(affinity_scope, 0644, wq_affn_scope_show, wq_affn_scope_store), + __ATTR(affinity_strict, 0644, wq_affinity_strict_show, wq_affinity_strict_store), __ATTR_NULL, }; @@ -6398,62 +6502,19 @@ static inline void wq_watchdog_init(void) { } #endif /* CONFIG_WQ_WATCHDOG */ -static void __init wq_numa_init(void) -{ - cpumask_var_t *tbl; - int node, cpu; - - if (num_possible_nodes() <= 1) - return; - - if (wq_disable_numa) { - pr_info("workqueue: NUMA affinity support disabled\n"); - return; - } - - for_each_possible_cpu(cpu) { - if (WARN_ON(cpu_to_node(cpu) == NUMA_NO_NODE)) { - pr_warn("workqueue: NUMA node mapping not available for cpu%d, disabling NUMA support\n", cpu); - return; - } - } - - wq_update_unbound_numa_attrs_buf = alloc_workqueue_attrs(); - BUG_ON(!wq_update_unbound_numa_attrs_buf); - - /* - * We want masks of possible CPUs of each node which isn't readily - * available. Build one from cpu_to_node() which should have been - * fully initialized by now. - */ - tbl = kcalloc(nr_node_ids, sizeof(tbl[0]), GFP_KERNEL); - BUG_ON(!tbl); - - for_each_node(node) - BUG_ON(!zalloc_cpumask_var_node(&tbl[node], GFP_KERNEL, - node_online(node) ? node : NUMA_NO_NODE)); - - for_each_possible_cpu(cpu) { - node = cpu_to_node(cpu); - cpumask_set_cpu(cpu, tbl[node]); - } - - wq_numa_possible_cpumask = tbl; - wq_numa_enabled = true; -} - /** * workqueue_init_early - early init for workqueue subsystem * - * This is the first half of two-staged workqueue subsystem initialization - * and invoked as soon as the bare basics - memory allocation, cpumasks and - * idr are up. It sets up all the data structures and system workqueues - * and allows early boot code to create workqueues and queue/cancel work - * items. Actual work item execution starts only after kthreads can be - * created and scheduled right before early initcalls. + * This is the first step of three-staged workqueue subsystem initialization and + * invoked as soon as the bare basics - memory allocation, cpumasks and idr are + * up. It sets up all the data structures and system workqueues and allows early + * boot code to create workqueues and queue/cancel work items. Actual work item + * execution starts only after kthreads can be created and scheduled right + * before early initcalls. */ void __init workqueue_init_early(void) { + struct wq_pod_type *pt = &wq_pod_types[WQ_AFFN_SYSTEM]; int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL }; int i, cpu; @@ -6463,8 +6524,30 @@ void __init workqueue_init_early(void) cpumask_copy(wq_unbound_cpumask, housekeeping_cpumask(HK_TYPE_WQ)); cpumask_and(wq_unbound_cpumask, wq_unbound_cpumask, housekeeping_cpumask(HK_TYPE_DOMAIN)); + if (!cpumask_empty(&wq_cmdline_cpumask)) + cpumask_and(wq_unbound_cpumask, wq_unbound_cpumask, &wq_cmdline_cpumask); + pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC); + wq_update_pod_attrs_buf = alloc_workqueue_attrs(); + BUG_ON(!wq_update_pod_attrs_buf); + + /* initialize WQ_AFFN_SYSTEM pods */ + pt->pod_cpus = kcalloc(1, sizeof(pt->pod_cpus[0]), GFP_KERNEL); + pt->pod_node = kcalloc(1, sizeof(pt->pod_node[0]), GFP_KERNEL); + pt->cpu_pod = kcalloc(nr_cpu_ids, sizeof(pt->cpu_pod[0]), GFP_KERNEL); + BUG_ON(!pt->pod_cpus || !pt->pod_node || !pt->cpu_pod); + + BUG_ON(!zalloc_cpumask_var_node(&pt->pod_cpus[0], GFP_KERNEL, NUMA_NO_NODE)); + + wq_update_pod_attrs_buf = alloc_workqueue_attrs(); + BUG_ON(!wq_update_pod_attrs_buf); + + pt->nr_pods = 1; + cpumask_copy(pt->pod_cpus[0], cpu_possible_mask); + pt->pod_node[0] = NUMA_NO_NODE; + pt->cpu_pod[0] = 0; + /* initialize CPU pools */ for_each_possible_cpu(cpu) { struct worker_pool *pool; @@ -6474,7 +6557,9 @@ void __init workqueue_init_early(void) BUG_ON(init_worker_pool(pool)); pool->cpu = cpu; cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu)); + cpumask_copy(pool->attrs->__pod_cpumask, cpumask_of(cpu)); pool->attrs->nice = std_nice[i++]; + pool->attrs->affn_strict = true; pool->node = cpu_to_node(cpu); /* alloc pool ID */ @@ -6495,11 +6580,10 @@ void __init workqueue_init_early(void) /* * An ordered wq should have only one pwq as ordering is * guaranteed by max_active which is enforced by pwqs. - * Turn off NUMA so that dfl_pwq is used for all nodes. */ BUG_ON(!(attrs = alloc_workqueue_attrs())); attrs->nice = std_nice[i]; - attrs->no_numa = true; + attrs->ordered = true; ordered_wq_attrs[i] = attrs; } @@ -6507,7 +6591,7 @@ void __init workqueue_init_early(void) system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0); system_long_wq = alloc_workqueue("events_long", 0, 0); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, - WQ_UNBOUND_MAX_ACTIVE); + WQ_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE, 0); system_power_efficient_wq = alloc_workqueue("events_power_efficient", @@ -6530,6 +6614,9 @@ static void __init wq_cpu_intensive_thresh_init(void) if (wq_cpu_intensive_thresh_us != ULONG_MAX) return; + pwq_release_worker = kthread_create_worker(0, "pool_workqueue_release"); + BUG_ON(IS_ERR(pwq_release_worker)); + /* * The default of 10ms is derived from the fact that most modern (as of * 2023) processors can do a lot in 10ms and that it's just below what @@ -6560,11 +6647,11 @@ static void __init wq_cpu_intensive_thresh_init(void) /** * workqueue_init - bring workqueue subsystem fully online * - * This is the latter half of two-staged workqueue subsystem initialization - * and invoked as soon as kthreads can be created and scheduled. - * Workqueues have been created and work items queued on them, but there - * are no kworkers executing the work items yet. Populate the worker pools - * with the initial workers and enable future kworker creations. + * This is the second step of three-staged workqueue subsystem initialization + * and invoked as soon as kthreads can be created and scheduled. Workqueues have + * been created and work items queued on them, but there are no kworkers + * executing the work items yet. Populate the worker pools with the initial + * workers and enable future kworker creations. */ void __init workqueue_init(void) { @@ -6574,19 +6661,12 @@ void __init workqueue_init(void) wq_cpu_intensive_thresh_init(); - /* - * It'd be simpler to initialize NUMA in workqueue_init_early() but - * CPU to node mapping may not be available that early on some - * archs such as power and arm64. As per-cpu pools created - * previously could be missing node hint and unbound pools NUMA - * affinity, fix them up. - * - * Also, while iterating workqueues, create rescuers if requested. - */ - wq_numa_init(); - mutex_lock(&wq_pool_mutex); + /* + * Per-cpu pools created earlier could be missing node hint. Fix them + * up. Also, create a rescuer for workqueues that requested it. + */ for_each_possible_cpu(cpu) { for_each_cpu_worker_pool(pool, cpu) { pool->node = cpu_to_node(cpu); @@ -6594,7 +6674,6 @@ void __init workqueue_init(void) } list_for_each_entry(wq, &workqueues, list) { - wq_update_unbound_numa(wq, smp_processor_id(), true); WARN(init_rescuer(wq), "workqueue: failed to create early rescuer for %s", wq->name); @@ -6618,9 +6697,114 @@ void __init workqueue_init(void) } /* - * Despite the naming, this is a no-op function which is here only for avoiding - * link error. Since compile-time warning may fail to catch, we will need to - * emit run-time warning from __flush_workqueue(). + * Initialize @pt by first initializing @pt->cpu_pod[] with pod IDs according to + * @cpu_shares_pod(). Each subset of CPUs that share a pod is assigned a unique + * and consecutive pod ID. The rest of @pt is initialized accordingly. */ -void __warn_flushing_systemwide_wq(void) { } +static void __init init_pod_type(struct wq_pod_type *pt, + bool (*cpus_share_pod)(int, int)) +{ + int cur, pre, cpu, pod; + + pt->nr_pods = 0; + + /* init @pt->cpu_pod[] according to @cpus_share_pod() */ + pt->cpu_pod = kcalloc(nr_cpu_ids, sizeof(pt->cpu_pod[0]), GFP_KERNEL); + BUG_ON(!pt->cpu_pod); + + for_each_possible_cpu(cur) { + for_each_possible_cpu(pre) { + if (pre >= cur) { + pt->cpu_pod[cur] = pt->nr_pods++; + break; + } + if (cpus_share_pod(cur, pre)) { + pt->cpu_pod[cur] = pt->cpu_pod[pre]; + break; + } + } + } + + /* init the rest to match @pt->cpu_pod[] */ + pt->pod_cpus = kcalloc(pt->nr_pods, sizeof(pt->pod_cpus[0]), GFP_KERNEL); + pt->pod_node = kcalloc(pt->nr_pods, sizeof(pt->pod_node[0]), GFP_KERNEL); + BUG_ON(!pt->pod_cpus || !pt->pod_node); + + for (pod = 0; pod < pt->nr_pods; pod++) + BUG_ON(!zalloc_cpumask_var(&pt->pod_cpus[pod], GFP_KERNEL)); + + for_each_possible_cpu(cpu) { + cpumask_set_cpu(cpu, pt->pod_cpus[pt->cpu_pod[cpu]]); + pt->pod_node[pt->cpu_pod[cpu]] = cpu_to_node(cpu); + } +} + +static bool __init cpus_dont_share(int cpu0, int cpu1) +{ + return false; +} + +static bool __init cpus_share_smt(int cpu0, int cpu1) +{ +#ifdef CONFIG_SCHED_SMT + return cpumask_test_cpu(cpu0, cpu_smt_mask(cpu1)); +#else + return false; +#endif +} + +static bool __init cpus_share_numa(int cpu0, int cpu1) +{ + return cpu_to_node(cpu0) == cpu_to_node(cpu1); +} + +/** + * workqueue_init_topology - initialize CPU pods for unbound workqueues + * + * This is the third step of there-staged workqueue subsystem initialization and + * invoked after SMP and topology information are fully initialized. It + * initializes the unbound CPU pods accordingly. + */ +void __init workqueue_init_topology(void) +{ + struct workqueue_struct *wq; + int cpu; + + init_pod_type(&wq_pod_types[WQ_AFFN_CPU], cpus_dont_share); + init_pod_type(&wq_pod_types[WQ_AFFN_SMT], cpus_share_smt); + init_pod_type(&wq_pod_types[WQ_AFFN_CACHE], cpus_share_cache); + init_pod_type(&wq_pod_types[WQ_AFFN_NUMA], cpus_share_numa); + + mutex_lock(&wq_pool_mutex); + + /* + * Workqueues allocated earlier would have all CPUs sharing the default + * worker pool. Explicitly call wq_update_pod() on all workqueue and CPU + * combinations to apply per-pod sharing. + */ + list_for_each_entry(wq, &workqueues, list) { + for_each_online_cpu(cpu) { + wq_update_pod(wq, cpu, cpu, true); + } + } + + mutex_unlock(&wq_pool_mutex); +} + +void __warn_flushing_systemwide_wq(void) +{ + pr_warn("WARNING: Flushing system-wide workqueues will be prohibited in near future.\n"); + dump_stack(); +} EXPORT_SYMBOL(__warn_flushing_systemwide_wq); + +static int __init workqueue_unbound_cpus_setup(char *str) +{ + if (cpulist_parse(str, &wq_cmdline_cpumask) < 0) { + cpumask_clear(&wq_cmdline_cpumask); + pr_warn("workqueue.unbound_cpus: incorrect CPU range, using default\n"); + } + + return 1; +} +__setup("workqueue.unbound_cpus=", workqueue_unbound_cpus_setup); diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h index 6b1d66e28269..f6275944ada7 100644 --- a/kernel/workqueue_internal.h +++ b/kernel/workqueue_internal.h @@ -48,7 +48,7 @@ struct worker { /* A: runs through worker->node */ unsigned long last_active; /* K: last active timestamp */ - unsigned int flags; /* X: flags */ + unsigned int flags; /* L: flags */ int id; /* I: worker id */ /* diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 5004463c4f9f..9073430dc865 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -151,48 +151,72 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc) } EXPORT_SYMBOL(__percpu_counter_sum); -int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp, - struct lock_class_key *key) +int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, + gfp_t gfp, u32 nr_counters, + struct lock_class_key *key) { unsigned long flags __maybe_unused; + size_t counter_size; + s32 __percpu *counters; + u32 i; - raw_spin_lock_init(&fbc->lock); - lockdep_set_class(&fbc->lock, key); - fbc->count = amount; - fbc->counters = alloc_percpu_gfp(s32, gfp); - if (!fbc->counters) + counter_size = ALIGN(sizeof(*counters), __alignof__(*counters)); + counters = __alloc_percpu_gfp(nr_counters * counter_size, + __alignof__(*counters), gfp); + if (!counters) { + fbc[0].counters = NULL; return -ENOMEM; + } - debug_percpu_counter_activate(fbc); + for (i = 0; i < nr_counters; i++) { + raw_spin_lock_init(&fbc[i].lock); + lockdep_set_class(&fbc[i].lock, key); +#ifdef CONFIG_HOTPLUG_CPU + INIT_LIST_HEAD(&fbc[i].list); +#endif + fbc[i].count = amount; + fbc[i].counters = (void *)counters + (i * counter_size); + + debug_percpu_counter_activate(&fbc[i]); + } #ifdef CONFIG_HOTPLUG_CPU - INIT_LIST_HEAD(&fbc->list); spin_lock_irqsave(&percpu_counters_lock, flags); - list_add(&fbc->list, &percpu_counters); + for (i = 0; i < nr_counters; i++) + list_add(&fbc[i].list, &percpu_counters); spin_unlock_irqrestore(&percpu_counters_lock, flags); #endif return 0; } -EXPORT_SYMBOL(__percpu_counter_init); +EXPORT_SYMBOL(__percpu_counter_init_many); -void percpu_counter_destroy(struct percpu_counter *fbc) +void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters) { unsigned long flags __maybe_unused; + u32 i; - if (!fbc->counters) + if (WARN_ON_ONCE(!fbc)) return; - debug_percpu_counter_deactivate(fbc); + if (!fbc[0].counters) + return; + + for (i = 0; i < nr_counters; i++) + debug_percpu_counter_deactivate(&fbc[i]); #ifdef CONFIG_HOTPLUG_CPU spin_lock_irqsave(&percpu_counters_lock, flags); - list_del(&fbc->list); + for (i = 0; i < nr_counters; i++) + list_del(&fbc[i].list); spin_unlock_irqrestore(&percpu_counters_lock, flags); #endif - free_percpu(fbc->counters); - fbc->counters = NULL; + + free_percpu(fbc[0].counters); + + for (i = 0; i < nr_counters; i++) + fbc[i].counters = NULL; } -EXPORT_SYMBOL(percpu_counter_destroy); +EXPORT_SYMBOL(percpu_counter_destroy_many); int percpu_counter_batch __read_mostly = 32; EXPORT_SYMBOL(percpu_counter_batch); diff --git a/mm/percpu.c b/mm/percpu.c index 6b726ba08ce2..26d9736468c0 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1890,13 +1890,15 @@ fail_unlock: fail: trace_percpu_alloc_percpu_fail(reserved, is_atomic, size, align); - if (!is_atomic && do_warn && warn_limit) { + if (do_warn && warn_limit) { pr_warn("allocation failed, size=%zu align=%zu atomic=%d, %s\n", size, align, is_atomic, err); - dump_stack(); + if (!is_atomic) + dump_stack(); if (!--warn_limit) pr_info("limit reached, disable warning\n"); } + if (is_atomic) { /* see the flag handling in pcpu_balance_workfn() */ pcpu_atomic_alloc_failed = true; @@ -2582,14 +2584,12 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, { size_t size_sum = ai->static_size + ai->reserved_size + ai->dyn_size; size_t static_size, dyn_size; - struct pcpu_chunk *chunk; unsigned long *group_offsets; size_t *group_sizes; unsigned long *unit_off; unsigned int cpu; int *unit_map; int group, unit, i; - int map_size; unsigned long tmp_addr; size_t alloc_size; @@ -2616,7 +2616,6 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, PCPU_SETUP_BUG_ON(ai->unit_size < PCPU_MIN_UNIT_SIZE); PCPU_SETUP_BUG_ON(!IS_ALIGNED(ai->unit_size, PCPU_BITMAP_BLOCK_SIZE)); PCPU_SETUP_BUG_ON(ai->dyn_size < PERCPU_DYNAMIC_EARLY_SIZE); - PCPU_SETUP_BUG_ON(!ai->dyn_size); PCPU_SETUP_BUG_ON(!IS_ALIGNED(ai->reserved_size, PCPU_MIN_ALLOC_SIZE)); PCPU_SETUP_BUG_ON(!(IS_ALIGNED(PCPU_BITMAP_BLOCK_SIZE, PAGE_SIZE) || IS_ALIGNED(PAGE_SIZE, PCPU_BITMAP_BLOCK_SIZE))); @@ -2699,7 +2698,7 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, pcpu_unit_pages = ai->unit_size >> PAGE_SHIFT; pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; pcpu_atom_size = ai->atom_size; - pcpu_chunk_struct_size = struct_size(chunk, populated, + pcpu_chunk_struct_size = struct_size((struct pcpu_chunk *)0, populated, BITS_TO_LONGS(pcpu_unit_pages)); pcpu_stats_save_ai(ai); @@ -2736,29 +2735,23 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, dyn_size = ai->dyn_size - (static_size - ai->static_size); /* - * Initialize first chunk. - * If the reserved_size is non-zero, this initializes the reserved - * chunk. If the reserved_size is zero, the reserved chunk is NULL - * and the dynamic region is initialized here. The first chunk, - * pcpu_first_chunk, will always point to the chunk that serves - * the dynamic region. + * Initialize first chunk: + * This chunk is broken up into 3 parts: + * < static | [reserved] | dynamic > + * - static - there is no backing chunk because these allocations can + * never be freed. + * - reserved (pcpu_reserved_chunk) - exists primarily to serve + * allocations from module load. + * - dynamic (pcpu_first_chunk) - serves the dynamic part of the first + * chunk. */ tmp_addr = (unsigned long)base_addr + static_size; - map_size = ai->reserved_size ?: dyn_size; - chunk = pcpu_alloc_first_chunk(tmp_addr, map_size); + if (ai->reserved_size) + pcpu_reserved_chunk = pcpu_alloc_first_chunk(tmp_addr, + ai->reserved_size); + tmp_addr = (unsigned long)base_addr + static_size + ai->reserved_size; + pcpu_first_chunk = pcpu_alloc_first_chunk(tmp_addr, dyn_size); - /* init dynamic chunk if necessary */ - if (ai->reserved_size) { - pcpu_reserved_chunk = chunk; - - tmp_addr = (unsigned long)base_addr + static_size + - ai->reserved_size; - map_size = dyn_size; - chunk = pcpu_alloc_first_chunk(tmp_addr, map_size); - } - - /* link the first chunk in */ - pcpu_first_chunk = chunk; pcpu_nr_empty_pop_pages = pcpu_first_chunk->nr_empty_pop_pages; pcpu_chunk_relocate(pcpu_first_chunk, -1); @@ -3190,32 +3183,26 @@ void __init __weak pcpu_populate_pte(unsigned long addr) pmd_t *pmd; if (pgd_none(*pgd)) { - p4d_t *new; - - new = memblock_alloc(P4D_TABLE_SIZE, P4D_TABLE_SIZE); - if (!new) + p4d = memblock_alloc(P4D_TABLE_SIZE, P4D_TABLE_SIZE); + if (!p4d) goto err_alloc; - pgd_populate(&init_mm, pgd, new); + pgd_populate(&init_mm, pgd, p4d); } p4d = p4d_offset(pgd, addr); if (p4d_none(*p4d)) { - pud_t *new; - - new = memblock_alloc(PUD_TABLE_SIZE, PUD_TABLE_SIZE); - if (!new) + pud = memblock_alloc(PUD_TABLE_SIZE, PUD_TABLE_SIZE); + if (!pud) goto err_alloc; - p4d_populate(&init_mm, p4d, new); + p4d_populate(&init_mm, p4d, pud); } pud = pud_offset(p4d, addr); if (pud_none(*pud)) { - pmd_t *new; - - new = memblock_alloc(PMD_TABLE_SIZE, PMD_TABLE_SIZE); - if (!new) + pmd = memblock_alloc(PMD_TABLE_SIZE, PMD_TABLE_SIZE); + if (!pmd) goto err_alloc; - pud_populate(&init_mm, pud, new); + pud_populate(&init_mm, pud, pmd); } pmd = pmd_offset(pud, addr); diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore index 4d556df4f77b..af8c3f30b9c1 100644 --- a/tools/testing/selftests/cgroup/.gitignore +++ b/tools/testing/selftests/cgroup/.gitignore @@ -5,5 +5,6 @@ test_freezer test_kmem test_kill test_cpu +test_cpuset test_zswap wait_inotify diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index 27dbdd7bb4bb..c27f05f6ce9b 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -12,6 +12,7 @@ TEST_GEN_PROGS += test_core TEST_GEN_PROGS += test_freezer TEST_GEN_PROGS += test_kill TEST_GEN_PROGS += test_cpu +TEST_GEN_PROGS += test_cpuset TEST_GEN_PROGS += test_zswap LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h @@ -24,4 +25,5 @@ $(OUTPUT)/test_core: cgroup_util.c $(OUTPUT)/test_freezer: cgroup_util.c $(OUTPUT)/test_kill: cgroup_util.c $(OUTPUT)/test_cpu: cgroup_util.c +$(OUTPUT)/test_cpuset: cgroup_util.c $(OUTPUT)/test_zswap: cgroup_util.c diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c index e8bbbdb77e0d..0340d4ca8f51 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.c +++ b/tools/testing/selftests/cgroup/cgroup_util.c @@ -286,6 +286,8 @@ int cg_destroy(const char *cgroup) { int ret; + if (!cgroup) + return 0; retry: ret = rmdir(cgroup); if (ret && errno == EBUSY) { diff --git a/tools/testing/selftests/cgroup/cgroup_util.h b/tools/testing/selftests/cgroup/cgroup_util.h index c92df4e5d395..1df7f202214a 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.h +++ b/tools/testing/selftests/cgroup/cgroup_util.h @@ -11,6 +11,8 @@ #define USEC_PER_SEC 1000000L #define NSEC_PER_SEC 1000000000L +#define TEST_UID 65534 /* usually nobody, any !root is fine */ + /* * Checks if two given values differ by less than err% of their sum. */ diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index 600123503063..80aa6b2373b9 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -683,7 +683,7 @@ cleanup: */ static int test_cgcore_lesser_euid_open(const char *root) { - const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ + const uid_t test_euid = TEST_UID; int ret = KSFT_FAIL; char *cg_test_a = NULL, *cg_test_b = NULL; char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; diff --git a/tools/testing/selftests/cgroup/test_cpuset.c b/tools/testing/selftests/cgroup/test_cpuset.c new file mode 100644 index 000000000000..b061ed1e05b4 --- /dev/null +++ b/tools/testing/selftests/cgroup/test_cpuset.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "../kselftest.h" +#include "cgroup_util.h" + +static int idle_process_fn(const char *cgroup, void *arg) +{ + (void)pause(); + return 0; +} + +static int do_migration_fn(const char *cgroup, void *arg) +{ + int object_pid = (int)(size_t)arg; + + if (setuid(TEST_UID)) + return EXIT_FAILURE; + + // XXX checking /proc/$pid/cgroup would be quicker than wait + if (cg_enter(cgroup, object_pid) || + cg_wait_for_proc_count(cgroup, 1)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static int do_controller_fn(const char *cgroup, void *arg) +{ + const char *child = cgroup; + const char *parent = arg; + + if (setuid(TEST_UID)) + return EXIT_FAILURE; + + if (!cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + if (cg_write(parent, "cgroup.subtree_control", "+cpuset")) + return EXIT_FAILURE; + + if (cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + if (cg_write(parent, "cgroup.subtree_control", "-cpuset")) + return EXIT_FAILURE; + + if (!cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +/* + * Migrate a process between two sibling cgroups. + * The success should only depend on the parent cgroup permissions and not the + * migrated process itself (cpuset controller is in place because it uses + * security_task_setscheduler() in cgroup v1). + * + * Deliberately don't set cpuset.cpus in children to avoid definining migration + * permissions between two different cpusets. + */ +static int test_cpuset_perms_object(const char *root, bool allow) +{ + char *parent = NULL, *child_src = NULL, *child_dst = NULL; + char *parent_procs = NULL, *child_src_procs = NULL, *child_dst_procs = NULL; + const uid_t test_euid = TEST_UID; + int object_pid = 0; + int ret = KSFT_FAIL; + + parent = cg_name(root, "cpuset_test_0"); + if (!parent) + goto cleanup; + parent_procs = cg_name(parent, "cgroup.procs"); + if (!parent_procs) + goto cleanup; + if (cg_create(parent)) + goto cleanup; + + child_src = cg_name(parent, "cpuset_test_1"); + if (!child_src) + goto cleanup; + child_src_procs = cg_name(child_src, "cgroup.procs"); + if (!child_src_procs) + goto cleanup; + if (cg_create(child_src)) + goto cleanup; + + child_dst = cg_name(parent, "cpuset_test_2"); + if (!child_dst) + goto cleanup; + child_dst_procs = cg_name(child_dst, "cgroup.procs"); + if (!child_dst_procs) + goto cleanup; + if (cg_create(child_dst)) + goto cleanup; + + if (cg_write(parent, "cgroup.subtree_control", "+cpuset")) + goto cleanup; + + if (cg_read_strstr(child_src, "cgroup.controllers", "cpuset") || + cg_read_strstr(child_dst, "cgroup.controllers", "cpuset")) + goto cleanup; + + /* Enable permissions along src->dst tree path */ + if (chown(child_src_procs, test_euid, -1) || + chown(child_dst_procs, test_euid, -1)) + goto cleanup; + + if (allow && chown(parent_procs, test_euid, -1)) + goto cleanup; + + /* Fork a privileged child as a test object */ + object_pid = cg_run_nowait(child_src, idle_process_fn, NULL); + if (object_pid < 0) + goto cleanup; + + /* Carry out migration in a child process that can drop all privileges + * (including capabilities), the main process must remain privileged for + * cleanup. + * Child process's cgroup is irrelevant but we place it into child_dst + * as hacky way to pass information about migration target to the child. + */ + if (allow ^ (cg_run(child_dst, do_migration_fn, (void *)(size_t)object_pid) == EXIT_SUCCESS)) + goto cleanup; + + ret = KSFT_PASS; + +cleanup: + if (object_pid > 0) { + (void)kill(object_pid, SIGTERM); + (void)clone_reap(object_pid, WEXITED); + } + + cg_destroy(child_dst); + free(child_dst_procs); + free(child_dst); + + cg_destroy(child_src); + free(child_src_procs); + free(child_src); + + cg_destroy(parent); + free(parent_procs); + free(parent); + + return ret; +} + +static int test_cpuset_perms_object_allow(const char *root) +{ + return test_cpuset_perms_object(root, true); +} + +static int test_cpuset_perms_object_deny(const char *root) +{ + return test_cpuset_perms_object(root, false); +} + +/* + * Migrate a process between parent and child implicitely + * Implicit migration happens when a controller is enabled/disabled. + * + */ +static int test_cpuset_perms_subtree(const char *root) +{ + char *parent = NULL, *child = NULL; + char *parent_procs = NULL, *parent_subctl = NULL, *child_procs = NULL; + const uid_t test_euid = TEST_UID; + int object_pid = 0; + int ret = KSFT_FAIL; + + parent = cg_name(root, "cpuset_test_0"); + if (!parent) + goto cleanup; + parent_procs = cg_name(parent, "cgroup.procs"); + if (!parent_procs) + goto cleanup; + parent_subctl = cg_name(parent, "cgroup.subtree_control"); + if (!parent_subctl) + goto cleanup; + if (cg_create(parent)) + goto cleanup; + + child = cg_name(parent, "cpuset_test_1"); + if (!child) + goto cleanup; + child_procs = cg_name(child, "cgroup.procs"); + if (!child_procs) + goto cleanup; + if (cg_create(child)) + goto cleanup; + + /* Enable permissions as in a delegated subtree */ + if (chown(parent_procs, test_euid, -1) || + chown(parent_subctl, test_euid, -1) || + chown(child_procs, test_euid, -1)) + goto cleanup; + + /* Put a privileged child in the subtree and modify controller state + * from an unprivileged process, the main process remains privileged + * for cleanup. + * The unprivileged child runs in subtree too to avoid parent and + * internal-node constraing violation. + */ + object_pid = cg_run_nowait(child, idle_process_fn, NULL); + if (object_pid < 0) + goto cleanup; + + if (cg_run(child, do_controller_fn, parent) != EXIT_SUCCESS) + goto cleanup; + + ret = KSFT_PASS; + +cleanup: + if (object_pid > 0) { + (void)kill(object_pid, SIGTERM); + (void)clone_reap(object_pid, WEXITED); + } + + cg_destroy(child); + free(child_procs); + free(child); + + cg_destroy(parent); + free(parent_subctl); + free(parent_procs); + free(parent); + + return ret; +} + + +#define T(x) { x, #x } +struct cpuset_test { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_cpuset_perms_object_allow), + T(test_cpuset_perms_object_deny), + T(test_cpuset_perms_subtree), +}; +#undef T + +int main(int argc, char *argv[]) +{ + char root[PATH_MAX]; + int i, ret = EXIT_SUCCESS; + + if (cg_find_unified_root(root, sizeof(root))) + ksft_exit_skip("cgroup v2 isn't mounted\n"); + + if (cg_read_strstr(root, "cgroup.subtree_control", "cpuset")) + if (cg_write(root, "cgroup.subtree_control", "+cpuset")) + ksft_exit_skip("Failed to set cpuset controller\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + case KSFT_SKIP: + ksft_test_result_skip("%s\n", tests[i].name); + break; + default: + ret = EXIT_FAILURE; + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + return ret; +} diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index 2b5215cc599f..4afb132e4e4f 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -10,7 +10,7 @@ skip_test() { echo "$1" echo "Test SKIPPED" - exit 0 + exit 4 # ksft_skip } [[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!" diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc index 285b4770efad..ff7499eb98d6 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc @@ -34,14 +34,19 @@ mips*) esac : "Test get argument (1)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char" > kprobe_events +if grep -q eventfs_add_dir available_filter_functions; then + DIR_NAME="eventfs_add_dir" +else + DIR_NAME="tracefs_create_dir" +fi +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):char" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1='t'" trace echo 0 > events/kprobes/testprobe/enable : "Test get argument (2)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char arg2=+0(${ARG1}):char[4]" > kprobe_events +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):char arg2=+0(${ARG1}):char[4]" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1='t' arg2={'t','e','s','t'}" trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc index a4f8e7c53c1f..a202b2ea4baf 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc @@ -37,14 +37,19 @@ loongarch*) esac : "Test get argument (1)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string" > kprobe_events +if grep -q eventfs_add_dir available_filter_functions; then + DIR_NAME="eventfs_add_dir" +else + DIR_NAME="tracefs_create_dir" +fi +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):string" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1=\"test\"" trace echo 0 > events/kprobes/testprobe/enable : "Test get argument (2)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string arg2=+0(${ARG1}):string" > kprobe_events +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):string arg2=+0(${ARG1}):string" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1=\"test\" arg2=\"test\"" trace diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py new file mode 100644 index 000000000000..d0df5833f2c1 --- /dev/null +++ b/tools/workqueue/wq_dump.py @@ -0,0 +1,177 @@ +#!/usr/bin/env drgn +# +# Copyright (C) 2023 Tejun Heo +# Copyright (C) 2023 Meta Platforms, Inc. and affiliates. + +desc = """ +This is a drgn script to show the current workqueue configuration. For more +info on drgn, visit https://github.com/osandov/drgn. + +Affinity Scopes +=============== + +Shows the CPUs that can be used for unbound workqueues and how they will be +grouped by each available affinity type. For each type: + + nr_pods number of CPU pods in the affinity type + pod_cpus CPUs in each pod + pod_node NUMA node for memory allocation for each pod + cpu_pod pod that each CPU is associated to + +Worker Pools +============ + +Lists all worker pools indexed by their ID. For each pool: + + ref number of pool_workqueue's associated with this pool + nice nice value of the worker threads in the pool + idle number of idle workers + workers number of all workers + cpu CPU the pool is associated with (per-cpu pool) + cpus CPUs the workers in the pool can run on (unbound pool) + +Workqueue CPU -> pool +===================== + +Lists all workqueues along with their type and worker pool association. For +each workqueue: + + NAME TYPE[,FLAGS] POOL_ID... + + NAME name of the workqueue + TYPE percpu, unbound or ordered + FLAGS S: strict affinity scope + POOL_ID worker pool ID associated with each possible CPU +""" + +import sys + +import drgn +from drgn.helpers.linux.list import list_for_each_entry,list_empty +from drgn.helpers.linux.percpu import per_cpu_ptr +from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu +from drgn.helpers.linux.idr import idr_for_each + +import argparse +parser = argparse.ArgumentParser(description=desc, + formatter_class=argparse.RawTextHelpFormatter) +args = parser.parse_args() + +def err(s): + print(s, file=sys.stderr, flush=True) + sys.exit(1) + +def cpumask_str(cpumask): + output = "" + base = 0 + v = 0 + for cpu in for_each_cpu(cpumask[0]): + while cpu - base >= 32: + output += f'{hex(v)} ' + base += 32 + v = 0 + v |= 1 << (cpu - base) + if v > 0: + output += f'{v:08x}' + return output.strip() + +worker_pool_idr = prog['worker_pool_idr'] +workqueues = prog['workqueues'] +wq_unbound_cpumask = prog['wq_unbound_cpumask'] +wq_pod_types = prog['wq_pod_types'] +wq_affn_dfl = prog['wq_affn_dfl'] +wq_affn_names = prog['wq_affn_names'] + +WQ_UNBOUND = prog['WQ_UNBOUND'] +WQ_ORDERED = prog['__WQ_ORDERED'] +WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] + +WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] +WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] +WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] +WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] +WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] + +print('Affinity Scopes') +print('===============') + +print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') + +def print_pod_type(pt): + print(f' nr_pods {pt.nr_pods.value_()}') + + print(' pod_cpus', end='') + for pod in range(pt.nr_pods): + print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') + print('') + + print(' pod_node', end='') + for pod in range(pt.nr_pods): + print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') + print('') + + print(f' cpu_pod ', end='') + for cpu in for_each_possible_cpu(prog): + print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') + print('') + +for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: + print('') + print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}') + print_pod_type(wq_pod_types[affn]) + +print('') +print('Worker Pools') +print('============') + +max_pool_id_len = 0 +max_ref_len = 0 +for pi, pool in idr_for_each(worker_pool_idr): + pool = drgn.Object(prog, 'struct worker_pool', address=pool) + max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) + max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) + +for pi, pool in idr_for_each(worker_pool_idr): + pool = drgn.Object(prog, 'struct worker_pool', address=pool) + print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='') + print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') + if pool.cpu >= 0: + print(f'cpu={pool.cpu.value_():3}', end='') + else: + print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') + print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='') + if pool.attrs.affn_strict: + print(' strict', end='') + print('') + +print('') +print('Workqueue CPU -> pool') +print('=====================') + +print('[ workqueue \ type CPU', end='') +for cpu in for_each_possible_cpu(prog): + print(f' {cpu:{max_pool_id_len}}', end='') +print(' dfl]') + +for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): + print(f'{wq.name.string_().decode()[-24:]:24}', end='') + if wq.flags & WQ_UNBOUND: + if wq.flags & WQ_ORDERED: + print(' ordered ', end='') + else: + print(' unbound', end='') + if wq.unbound_attrs.affn_strict: + print(',S ', end='') + else: + print(' ', end='') + else: + print(' percpu ', end='') + + for cpu in for_each_possible_cpu(prog): + pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() + field_len = max(len(str(cpu)), max_pool_id_len) + print(f' {pool_id:{field_len}}', end='') + + if wq.flags & WQ_UNBOUND: + print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') + print('') diff --git a/tools/workqueue/wq_monitor.py b/tools/workqueue/wq_monitor.py index 6e258d123e8c..a8856a9c45dc 100644 --- a/tools/workqueue/wq_monitor.py +++ b/tools/workqueue/wq_monitor.py @@ -20,8 +20,11 @@ https://github.com/osandov/drgn. and got excluded from concurrency management to avoid stalling other work items. - CMwake The number of concurrency-management wake-ups while executing a - work item of the workqueue. + CMW/RPR For per-cpu workqueues, the number of concurrency-management + wake-ups while executing a work item of the workqueue. For + unbound workqueues, the number of times a worker was repatriated + to its affinity scope after being migrated to an off-scope CPU by + the scheduler. mayday The number of times the rescuer was requested while waiting for new worker creation. @@ -65,6 +68,7 @@ PWQ_STAT_COMPLETED = prog['PWQ_STAT_COMPLETED'] # work items completed exec PWQ_STAT_CPU_TIME = prog['PWQ_STAT_CPU_TIME'] # total CPU time consumed PWQ_STAT_CPU_INTENSIVE = prog['PWQ_STAT_CPU_INTENSIVE'] # wq_cpu_intensive_thresh_us violations PWQ_STAT_CM_WAKEUP = prog['PWQ_STAT_CM_WAKEUP'] # concurrency-management worker wakeups +PWQ_STAT_REPATRIATED = prog['PWQ_STAT_REPATRIATED'] # unbound workers brought back into scope PWQ_STAT_MAYDAY = prog['PWQ_STAT_MAYDAY'] # maydays to rescuer PWQ_STAT_RESCUED = prog['PWQ_STAT_RESCUED'] # linked work items executed by rescuer PWQ_NR_STATS = prog['PWQ_NR_STATS'] @@ -89,22 +93,25 @@ class WqStats: 'cpu_time' : self.stats[PWQ_STAT_CPU_TIME], 'cpu_intensive' : self.stats[PWQ_STAT_CPU_INTENSIVE], 'cm_wakeup' : self.stats[PWQ_STAT_CM_WAKEUP], + 'repatriated' : self.stats[PWQ_STAT_REPATRIATED], 'mayday' : self.stats[PWQ_STAT_MAYDAY], 'rescued' : self.stats[PWQ_STAT_RESCUED], } def table_header_str(): return f'{"":>24} {"total":>8} {"infl":>5} {"CPUtime":>8} '\ - f'{"CPUitsv":>7} {"CMwake":>7} {"mayday":>7} {"rescued":>7}' + f'{"CPUitsv":>7} {"CMW/RPR":>7} {"mayday":>7} {"rescued":>7}' def table_row_str(self): cpu_intensive = '-' - cm_wakeup = '-' + cmw_rpr = '-' mayday = '-' rescued = '-' - if not self.unbound: + if self.unbound: + cmw_rpr = str(self.stats[PWQ_STAT_REPATRIATED]); + else: cpu_intensive = str(self.stats[PWQ_STAT_CPU_INTENSIVE]) - cm_wakeup = str(self.stats[PWQ_STAT_CM_WAKEUP]) + cmw_rpr = str(self.stats[PWQ_STAT_CM_WAKEUP]) if self.mem_reclaim: mayday = str(self.stats[PWQ_STAT_MAYDAY]) @@ -115,7 +122,7 @@ class WqStats: f'{max(self.stats[PWQ_STAT_STARTED] - self.stats[PWQ_STAT_COMPLETED], 0):5} ' \ f'{self.stats[PWQ_STAT_CPU_TIME] / 1000000:8.1f} ' \ f'{cpu_intensive:>7} ' \ - f'{cm_wakeup:>7} ' \ + f'{cmw_rpr:>7} ' \ f'{mayday:>7} ' \ f'{rescued:>7} ' return out.rstrip(':')