[GIT PULL for v7.3] media updates

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+QmuaPwR3wnBdVwACF8+vY7k4RUFAmqFXqYACgkQCF8+vY7k
 4RWNtw//T8GAlfl1BDlqxcWQHPFLkGqogsZQUx8aITayUBXpOzWHK4neGM7QF2Za
 9kmBi3OzWgW3hIfbW4/vQCBSdDmTLSjoYHjYsc2Y3Cr0RvxIoEFw0GILYzygE3gp
 y9sUFHs2S43Xh1n3LuTcu0woL8YcY/3SaMrvEA6uoSGmOETiIUIuNkLpUAOEWhrl
 hkpRHt160yFiADSr8Ve9435xnkf5GvC0rmf/jF/KSUuNXrQuEx+5LIWzmadQRhph
 2o+LuOJClKenglguU+yMA/DNEJuxxT1LOUgEiw05U2I03N8DqpiCO8Mpp2/l0iXH
 fgYFpObz6LdkusUSePamR2XaA3+ViNM48ddg1xmo+1gYQ/ggipsVVWifZpGFs2hq
 lbHEPV2Va/hlVj5EZJh4E6PHzH3UkGii2zgiHw0W3XWtTXS/qVlRjQwlo4aZTKok
 KPlOCNY69XADqOCmSB+kEmuCULfqoiJChk0qeRJX+kETc7gT+Qx4Ej0WT9LdF+NV
 7+gxMOgUJFuTr1HkhO5ZXWezikBsJUvLUFud8mV+4iAJldby23I4UEXUEPK7od+W
 8RXyWvf9/0owE2E3dOqkoM2sz2WW17nWZe8bPV+NTX39qw5WBOpaPpZGxVzOWinS
 KUTHJkp/O/1ymWtMz4+B+8QTO7n6KUnVDdL4qkO8cOrJGZizxoU=
 =8fcn
 -----END PGP SIGNATURE-----

Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab:

 - v4l2-core: added ISP statistics support and per-block validation

 - v4l2-core: Allow unknown HDR10 white point and luminance

 - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor

 - New codec: Milos: VPU v2.0 codec support

 - isp driver: gained support for Dreamchip RPPX1 ISP framework

 - vsp1 driver: gained support for RZ/T2H and RZ/N2H

 - Novalake driver: gained CVS support for new NVL hardware

 - dvb-core: fix feed leak on failed DMX_ADD_PID

 - several driver fixes, cleanups and minor improvements

* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
  media: ipu-bridge: check all DMI entries when overriding sensor rotation
  media: v4l2-async: avoid deleting unlinked ASC entry on link error
  media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
  media: intel/ipu6: fix async notifier cleanup leak on parse error
  media: staging/ipu7: fix async notifier UAF on probe error path
  media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
  media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
  media: rcar-isp: Fix VSPX reference leaks
  media: rcar-isp: Release ISPCORE resources
  media: i2c: imx415: Release runtime PM reference on VBLANK error
  media: i2c: imx415: Return test pattern write errors
  media: renesas: vsp1: Declare index variables in for loop statement
  media: renesas: vsp1: Make reset control optional to support platforms without a reset line
  media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
  media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
  media: nxp: imx8-isi: Add additional 32-bit RGB format support
  media: nxp: imx8-isi: Add 16-bit raw Bayer format support
  media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
  media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
  media: nxp: imx8-isi: Correct color map between V4L2 and ISI
  ...
This commit is contained in:
Linus Torvalds 2026-08-19 10:09:22 -07:00
commit f4cdf7ca9a
351 changed files with 14587 additions and 4610 deletions

View File

@ -640,6 +640,10 @@ S: 25-29 St Giles
S: Oxford
S: United Kingdom
N: Bingbu Cao
E: bingbu.cao@amd.com
D: Ipu6, ipu7 and camera sensor drivers
N: Luiz Fernando N. Capitulino
E: lcapitulino@mandriva.com.br
E: lcapitulino@gmail.com
@ -3334,6 +3338,9 @@ E: rpurdie@rpsys.net
D: Backlight subsystem maintainer
S: United Kingdom
N: Tian Shu Qiu
D: Ipu6, ipu7 and camera sensor drivers
N: Daniel Quinlan
E: quinlan@pathname.com
W: https://www.pathname.com/~quinlan/

View File

@ -446,3 +446,7 @@ EM28xx cards list
- MyGica UTV3 Analog USB2.0 TV Box
- em2860
- eb1a:2860
* - 113
- StarTech SVID2USB232
- em28281
- eb1a:8286

View File

@ -50,8 +50,15 @@ properties:
properties:
data-lanes:
minItems: 4
maxItems: 4
oneOf:
- items:
- const: 1
- const: 2
- const: 3
- const: 4
- items:
- const: 1
- const: 2
required:
- link-frequencies

View File

@ -0,0 +1,130 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2026 Ideas on Board Oy
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/i2c/sony,imx678.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sony IMX678 Sensor
maintainers:
- Jai Luthra <jai.luthra@ideasonboard.com>
description:
Sony IMX678 diagonal 8.86 mm (Type 1/1.8) CMOS active pixel type solid-state
image sensor with a square pixel array and 8.40M (3856x2180) effective pixels.
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
items:
- enum:
- sony,imx678-aamr
- sony,imx678-aaqr
- const: sony,imx678
description:
The IMX678 sensor exists in a colour variant (IMX678-AAQR) and a mono
variant (IMX678-AAMR). The variant-specific compatible describes the
exact sensor variant, with the generic IMX678 compatible as a fallback.
reg:
maxItems: 1
clocks:
description: |
Input clock (INCK). Only the rates listed in the datasheet are supported:
13.5, 18, 24, 27, 36, 37.125, 72, and 74.25 MHz.
maxItems: 1
avdd-supply:
description: Analog power supply (3.3V)
ovdd-supply:
description: Interface power supply (1.8V)
dvdd-supply:
description: Digital power supply (1.1V)
reset-gpios:
description: Sensor RESETN (XCLR) GPIO
maxItems: 1
port:
$ref: /schemas/graph.yaml#/$defs/port-base
additionalProperties: false
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
oneOf:
- items:
- const: 1
- const: 2
- items:
- const: 1
- const: 2
- const: 3
- const: 4
- items:
- const: 1
- const: 2
- const: 3
- const: 4
- const: 5
- const: 6
- const: 7
- const: 8
required:
- data-lanes
- link-frequencies
required:
- endpoint
required:
- compatible
- reg
- clocks
- avdd-supply
- ovdd-supply
- dvdd-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
camera@1a {
compatible = "sony,imx678-aaqr", "sony,imx678";
reg = <0x1a>;
clocks = <&imx678_clk>;
avdd-supply = <&camera_avdd_3v3>;
ovdd-supply = <&camera_ovdd_1v8>;
dvdd-supply = <&camera_dvdd_1v1>;
reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
port {
imx678: endpoint {
remote-endpoint = <&cam>;
data-lanes = <1 2 3 4>;
link-frequencies = /bits/ 64 <720000000>;
};
};
};
};
...

View File

@ -27,9 +27,10 @@ properties:
compatible:
enum:
- st,vd55g1
- st,vd55g4
- st,vd65g4
description:
VD55G1 is the monochrome variant, while VD65G4 is the color one.
VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one.
reg:
maxItems: 1

View File

@ -117,7 +117,9 @@ allOf:
compatible:
not:
contains:
const: fsl,imx91-isi
enum:
- fsl,imx8ulp-isi
- fsl,imx91-isi
then:
required:
- fsl,blk-ctrl

View File

@ -0,0 +1,166 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/qcom,milos-iris.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Milos SoC Iris video encoder and decoder
maintainers:
- Alexander Koskovich <akoskovich@pm.me>
description:
The Iris video processing unit on Qualcomm Milos SoC is a video encode and
decode accelerator.
properties:
compatible:
enum:
- qcom,milos-iris
clocks:
maxItems: 3
clock-names:
items:
- const: iface
- const: core
- const: vcodec0_core
dma-coherent: true
interconnects:
maxItems: 2
interconnect-names:
items:
- const: cpu-cfg
- const: video-mem
iommus:
maxItems: 2
operating-points-v2: true
opp-table:
type: object
power-domains:
maxItems: 4
power-domain-names:
items:
- const: venus
- const: vcodec0
- const: cx
- const: mx
resets:
maxItems: 2
reset-names:
items:
- const: bus
- const: core
required:
- compatible
- dma-coherent
- interconnects
- interconnect-names
- iommus
- power-domain-names
- resets
- reset-names
allOf:
- $ref: qcom,venus-common.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/clock/qcom,milos-gcc.h>
#include <dt-bindings/clock/qcom,milos-videocc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interconnect/qcom,icc.h>
#include <dt-bindings/interconnect/qcom,milos-rpmh.h>
#include <dt-bindings/power/qcom-rpmpd.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
video-codec@aa00000 {
compatible = "qcom,milos-iris";
reg = <0x0aa00000 0xf0000>;
clocks = <&gcc GCC_VIDEO_AXI0_CLK>,
<&videocc VIDEO_CC_MVS0C_CLK>,
<&videocc VIDEO_CC_MVS0_CLK>;
clock-names = "iface",
"core",
"vcodec0_core";
dma-coherent;
iommus = <&apps_smmu 0x1960 0>,
<&apps_smmu 0x1967 0>;
interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
&cnoc_cfg SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
<&mmss_noc MASTER_VIDEO QCOM_ICC_TAG_ALWAYS
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
interconnect-names = "cpu-cfg",
"video-mem";
interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
operating-points-v2 = <&iris_opp_table>;
memory-region = <&video_mem>;
power-domains = <&videocc VIDEO_CC_MVS0C_GDSC>,
<&videocc VIDEO_CC_MVS0_GDSC>,
<&rpmhpd RPMHPD_CX>,
<&rpmhpd RPMHPD_MX>;
power-domain-names = "venus",
"vcodec0",
"cx",
"mx";
resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>,
<&videocc VIDEO_CC_MVS0C_CLK_ARES>;
reset-names = "bus",
"core";
iris_opp_table: opp-table {
compatible = "operating-points-v2";
opp-240000000 {
opp-hz = /bits/ 64 <240000000>;
required-opps = <&rpmhpd_opp_low_svs>,
<&rpmhpd_opp_svs>;
};
opp-338000000 {
opp-hz = /bits/ 64 <338000000>;
required-opps = <&rpmhpd_opp_svs>,
<&rpmhpd_opp_svs>;
};
opp-366000000 {
opp-hz = /bits/ 64 <366000000>;
required-opps = <&rpmhpd_opp_svs_l1>,
<&rpmhpd_opp_svs>;
};
opp-444000000 {
opp-hz = /bits/ 64 <444000000>;
required-opps = <&rpmhpd_opp_nom>,
<&rpmhpd_opp_svs_l1>;
};
opp-552000000 {
opp-hz = /bits/ 64 <552000000>;
required-opps = <&rpmhpd_opp_turbo>,
<&rpmhpd_opp_nom>;
};
};
};

View File

@ -14,7 +14,11 @@ description:
properties:
compatible:
const: qcom,qcm2290-camss
oneOf:
- items:
- const: qcom,shikra-camss
- const: qcom,qcm2290-camss
- const: qcom,qcm2290-camss
reg:
maxItems: 9
@ -76,7 +80,14 @@ properties:
- const: sf_mnoc
iommus:
maxItems: 4
oneOf:
- items:
- description: S1 HLOS VFE non-protected (VFE only)
- items:
- description: S1 HLOS VFE non-protected
- description: S1 HLOS CDM non-protected
- description: S1 HLOS OPE read non-protected
- description: S1 HLOS OPE write non-protected
power-domains:
items:

View File

@ -122,10 +122,10 @@ properties:
- rc-proteus-2309
- rc-purpletv
- rc-pv951
- rc-rc5-tv
- rc-rc6-mce
- rc-real-audio-220-32-keys
- rc-reddo
- rc-siemens-gigaset-rc20
- rc-snapstream-firefly
- rc-streamzap
- rc-su3000
@ -147,11 +147,11 @@ properties:
- rc-tt-1500
- rc-twinhan-dtv-cab-ci
- rc-twinhan1027
- rc-vega-s9x
- rc-videomate-k100
- rc-videomate-s350
- rc-videomate-tv-pvr
- rc-videostrong-kii-pro
- rc-vega-s9x
- rc-wetek-hub
- rc-wetek-play2
- rc-winfast

View File

@ -34,6 +34,8 @@ properties:
- renesas,r9a09g047-fcpvd # RZ/G3E
- renesas,r9a09g056-fcpvd # RZ/V2N
- renesas,r9a09g057-fcpvd # RZ/V2H(P)
- renesas,r9a09g077-fcpvd # RZ/T2H
- renesas,r9a09g087-fcpvd # RZ/N2H
- const: renesas,fcpv # Generic FCP for VSP fallback
reg:
@ -66,7 +68,6 @@ required:
- reg
- clocks
- power-domains
- resets
additionalProperties: false
@ -83,6 +84,8 @@ allOf:
- renesas,r9a09g047-fcpvd
- renesas,r9a09g056-fcpvd
- renesas,r9a09g057-fcpvd
- renesas,r9a09g077-fcpvd
- renesas,r9a09g087-fcpvd
then:
properties:
clocks:
@ -94,6 +97,19 @@ allOf:
clocks:
maxItems: 1
clock-names: false
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g077-fcpvd
- renesas,r9a09g087-fcpvd
then:
properties:
resets: false
else:
required:
- resets
examples:
# R8A7795 (R-Car H3) FCP for VSP-D1

View File

@ -29,6 +29,8 @@ properties:
- renesas,r9a09g047-vsp2 # RZ/G3E
- renesas,r9a09g056-vsp2 # RZ/V2N
- renesas,r9a09g057-vsp2 # RZ/V2H(P)
- renesas,r9a09g077-vsp2 # RZ/T2H
- renesas,r9a09g087-vsp2 # RZ/N2H
- const: renesas,r9a07g044-vsp2 # RZ/G2L fallback
reg:
@ -67,7 +69,6 @@ required:
- interrupts
- clocks
- power-domains
- resets
additionalProperties: false
@ -101,6 +102,20 @@ allOf:
maxItems: 1
clock-names: false
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g077-vsp2
- renesas,r9a09g087-vsp2
then:
properties:
resets: false
else:
required:
- resets
examples:
# R8A7790 (R-Car H2) VSP1-S
- |

View File

@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/ti,am437x-vpfe.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments AM437x CAMERA Video Processing Front End (VPFE)
maintainers:
- Benoit Parrot <bparrot@ti.com>
- Bhargav Joshi <j.bhargav.u@gmail.com>
description:
The Video Processing Front End (VPFE) is a key component for image capture
applications. The capture module provides the system interface and the
processing capability to connect RAW image-sensor modules and video decoders
to the AM437x device.
properties:
compatible:
const: ti,am437x-vpfe
reg:
maxItems: 1
interrupts:
maxItems: 1
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description:
A single parallel input port connecting to an external image sensor or
video decoder. The VPFE supports only one input port per instance.
properties:
endpoint:
$ref: video-interfaces.yaml#
unevaluatedProperties: false
properties:
ti,am437x-vpfe-interface:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4]
description:
Selects the VPFE input interface type can be one of following
0 - Raw Bayer (VPFE_RAW_BAYER)
1 - BT.656 8-bit (VPFE_BT656)
2 - BT.656 10-bit (VPFE_BT656_10BIT)
3 - YCbCr 8-bit sync (VPFE_YCBCR_SYNC_8)
4 - YCbCr 16-bit sync (VPFE_YCBCR_SYNC_16)
bus-width:
minimum: 8
maximum: 16
required:
- remote-endpoint
- ti,am437x-vpfe-interface
- bus-width
required:
- compatible
- reg
- interrupts
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
vpfe@48328000{
compatible = "ti,am437x-vpfe";
reg = <0x48328000 0x2000>;
interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&vpfe_pins_default>;
pinctrl-1 = <&vpfe_pins_sleep>;
port {
vpfe0_ep: endpoint {
remote-endpoint = <&ov2659_1>;
ti,am437x-vpfe-interface = <0>;
bus-width = <8>;
hsync-active = <0>;
vsync-active = <0>;
};
};
};

View File

@ -1,61 +0,0 @@
Texas Instruments AM437x CAMERA (VPFE)
--------------------------------------
The Video Processing Front End (VPFE) is a key component for image capture
applications. The capture module provides the system interface and the
processing capability to connect RAW image-sensor modules and video decoders
to the AM437x device.
Required properties:
- compatible: must be "ti,am437x-vpfe"
- reg: physical base address and length of the registers set for the device;
- interrupts: should contain IRQ line for the VPFE;
- ti,am437x-vpfe-interface: can be one of the following,
0 - Raw Bayer Interface.
1 - 8 Bit BT656 Interface.
2 - 10 Bit BT656 Interface.
3 - YCbCr 8 Bit Interface.
4 - YCbCr 16 Bit Interface.
VPFE supports a single port node with parallel bus. It should contain one
'port' child node with child 'endpoint' node. Please refer to the bindings
defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
Example:
vpfe: vpfe@f0034000 {
compatible = "ti,am437x-vpfe";
reg = <0x48328000 0x2000>;
interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&vpfe_pins_default>;
pinctrl-1 = <&vpfe_pins_sleep>;
port {
#address-cells = <1>;
#size-cells = <0>;
vpfe0_ep: endpoint {
remote-endpoint = <&ov2659_1>;
ti,am437x-vpfe-interface = <0>;
bus-width = <8>;
hsync-active = <0>;
vsync-active = <0>;
};
};
};
i2c1: i2c@4802a000 {
ov2659@30 {
compatible = "ti,ov2659";
reg = <0x30>;
port {
ov2659_1: endpoint {
remote-endpoint = <&vpfe0_ep>;
bus-width = <8>;
mclk-frequency = <12000000>;
};
};
};

View File

@ -432,6 +432,10 @@ The Media Driver Maintainers responsible for specific areas are:
- Qualcomm drivers
- Ricardo Ribalda <ribalda@chromium.org>
- Media-CI
Submit Checklist Addendum
-------------------------

View File

@ -185,6 +185,13 @@ logical address types are already defined will return with error ``EBUSY``.
are CEC devices that can only handle CDC messages.
All other messages are ignored.
* .. _`CEC-LOG-ADDRS-FL-CONFIG-FAILED`:
- ``CEC_LOG_ADDRS_FL_CONFIG_FAILED``
- 8
- If this flag is set, then the CEC device failed to claim a free logical
address and is in the unconfigured state. This can never happen if
``CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK`` was set as well.
.. tabularcolumns:: |p{7.8cm}|p{1.0cm}|p{8.5cm}|

View File

@ -344,7 +344,7 @@ Variables
- ..
- :rspan:`5` ``stuct play``
- :rspan:`5` ``struct play``
- :rspan:`4` ``__s32 speed``

View File

@ -80,15 +80,25 @@ Colorimetry Control IDs
- ``white_point_x``
- Specifies the normalized x chromaticity coordinate of the white
point of the mastering display in increments of 0.00002.
When both ``white_point_x`` and ``white_point_y`` are zero,
the white point chromaticity is unknown. If either coordinate is
non-zero, both coordinates shall be within their valid ranges.
* - __u16
- ``white_point_y``
- Specifies the normalized y chromaticity coordinate of the white
point of the mastering display in increments of 0.00002.
When both ``white_point_x`` and ``white_point_y`` are zero,
the white point chromaticity is unknown. If either coordinate is
non-zero, both coordinates shall be within their valid ranges.
* - __u32
- ``max_luminance``
- Specifies the nominal maximum display luminance of the mastering
display in units of 0.0001 cd/m\ :sup:`2`.
A value of zero indicates that the nominal maximum display
luminance is unknown.
* - __u32
- ``min_luminance``
- specifies the nominal minimum display luminance of the mastering
- Specifies the nominal minimum display luminance of the mastering
display in units of 0.0001 cd/m\ :sup:`2`.
A value of zero indicates that the nominal minimum display
luminance is unknown.

View File

@ -21,6 +21,7 @@ These formats are used for the :ref:`metadata` interface only.
metafmt-pisp-be
metafmt-pisp-fe
metafmt-rkisp1
metafmt-rppx1
metafmt-uvc
metafmt-uvc-msxu-1-5
metafmt-vivid

View File

@ -0,0 +1,99 @@
.. SPDX-License-Identifier: GPL-2.0
.. _v4l2-meta-fmt-rppx1-params:
.. _v4l2-meta-fmt-rppx1-stats:
*************************************************************************
V4L2_META_FMT_RPPX1_PARAMS ('DR1P'), V4L2_META_FMT_RPPX1_STATS ('DR1S')
*************************************************************************
Configuration Parameters
========================
The configuration parameters are passed to the metadata output video node, using
the :c:type:`v4l2_meta_format` interface. Rather than a single struct containing
sub-structs for each configurable area of the ISP, parameters for the Dreamchip
RPPX1 use the v4l2-isp parameters system, through which groups of parameters are
defined as distinct structs or "blocks" which may be added to the data member of
:c:type:`v4l2_isp_buffer`. Userspace is responsible for populating the data
member with the blocks that need to be configured by the driver. Each
block-specific struct embeds :c:type:`v4l2_isp_block_header` as its first member
and userspace must populate the type member with a value from
:c:type:`rppx1_params_block_type`.
.. code-block:: c
struct v4l2_isp_params_buffer *params =
(struct v4l2_isp_params_buffer *)buffer;
params->version = V4L2_ISP_PARAMS_VERSION_V1;
params->data_size = 0;
void *data = (void *)params->data;
struct rppx1_ccor_params *ccor =
(struct rppx1_ccor_params *)data;
ccor->header.type = RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST;
ccor->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
ccor->header.size = sizeof(struct rppx1_ccor_params);
ccor->coeff[0][0] = 0x1000;
ccor->coeff[0][1] = 0x0000;
ccor->coeff[0][2] = 0x0000;
ccor->coeff[1][0] = 0x0000;
ccor->coeff[1][1] = 0x1000;
ccor->coeff[1][2] = 0x0000;
ccor->coeff[2][0] = 0x0000;
ccor->coeff[2][1] = 0x0000;
ccor->coeff[2][2] = 0x1000;
ccor->offset[0] = 0x200000;
ccor->offset[1] = 0x200000;
ccor->offset[2] = 0x200000;
data += sizeof(struct rppx1_ccor_params);
params->data_size += sizeof(struct rppx1_ccor_params);
3A Statistics
=============
The ISP device collects different statistics over an input bayer frame. Those
statistics can be obtained by userspace from the metadata capture video node,
using the :c:type:`v4l2_meta_format` interface. Rather than a single struct
containing sub-structs for each statistics area of the ISP, statistics for the
Dreamchip RPPX1 use the v4l2-isp statistics system, through which groups of
statistics are defined as distinct structs or "blocks" which may be added to the
data member of :c:type:`v4l2_isp_buffer`. Userspace is responsible for parsing
the buffer and extracting the blocks of statistics. Each block-specific struct
embeds :c:type:`v4l2_isp_block_header` as its first member and userspace must
interpret the type member with a value from :c:type:`rppx1_stats_block_type`.
.. code-block:: C
const struct v4l2_isp_buffer *stats =
(struct v4l2_isp_buffer *)buf;
size_t block_offset = 0;
while (block_offset < stats->data_size) {
const struct v4l2_isp_stats_block_header *block =
(void*)(stats->data + block_offset);
block_offset += block->size;
switch (block->type) {
case RPPX1_STATS_BLOCK_TYPE_HIST_POST:
for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++)
printf("hist.hist_bins[%u] = 0x%08x\n",
i, hist.hist_bins[%i]);
break;
default:
printf("Unknown block type 0x%04x", block->type);
break;
}
}
Dreamchip RPPX1 uAPI data types
===============================
.. kernel-doc:: include/uapi/linux/media/dreamchip/rppx1-config.h

View File

@ -18,22 +18,22 @@ single C structure that contains a header, followed by a binary buffer where
userspace programs a variable number of ISP configuration data block, one for
each supported ISP feature.
The :c:type:`v4l2_isp_params_buffer` structure defines the buffer header which
is followed by a binary buffer of ISP configuration data. Userspace shall
correctly populate the buffer header with the generic parameters format version
and with the size (in bytes) of the binary data buffer where it will store the
ISP blocks configuration.
The :c:type:`v4l2_isp_buffer` structure defines the buffer header which is
followed by a binary buffer of ISP configuration data. Userspace shall correctly
populate the buffer header with the serialization format version and with the
size (in bytes) of the binary data buffer where it will store the ISP blocks
configuration.
Each *ISP configuration block* is preceded by an header implemented by the
:c:type:`v4l2_isp_params_block_header` structure, followed by the configuration
Each *ISP configuration block* is preceded by a header implemented by the
:c:type:`v4l2_isp_block_header` structure, followed by the configuration
parameters for that specific block, defined by the ISP driver specific data
types.
Userspace applications are responsible for correctly populating each block's
header fields (type, flags and size) and the block-specific parameters.
ISP block enabling, disabling and configuration
-----------------------------------------------
ISP parameters block enabling, disabling and configuration
----------------------------------------------------------
When userspace wants to configure and enable an ISP block it shall fully
populate the block configuration and set the V4L2_ISP_PARAMS_FL_BLOCK_ENABLE
@ -59,7 +59,30 @@ definition without invalidating the existing ones.
ISP statistics
==============
Support for generic statistics format is not yet implemented in Video4Linux2.
The generic ISP statistics format is identical to the generic ISP configuration
parameters format. It is realized by defining a C structure that contains a
header, followed by binary buffer where the ISP driver copies a variable number
of ISP statistics blocks.
Extensible statistics buffers have :c:type:`v4l2_isp_buffer` header followed by
a binary buffer of ISP statistics data. ISP drivers populate the buffer header
with the serialization format version and with the size (in bytes) of the binary
data buffer where ISP statistics data are serialized. Applications shall
validate that the serialization format version matches the expected one and that
the buffer size doesn't exceed the maximum size for a statistics buffer as
declared by the driver's uAPI header.
Each *ISP statistics block* is preceded by a header implemented by the
:c:type:`v4l2_isp_block_header` structure, followed by the statistics data for
that specific block. The driver might optionally report platform-specific flags
associated with each statistics block.
Applications inspect the statistics block type as reported in the header and
validates the reported size matches the block's expected size before accessing
the ISP statistics data.
Extension to the statistics format can be implemented by adding new blocks
definition without invalidating the existing ones.
V4L2 ISP uAPI data types
========================

View File

@ -72,12 +72,6 @@ partial array, all elements have to be set or retrieved. The total size
is calculated as ``elems`` * ``elem_size``. These values can be obtained
by calling :ref:`VIDIOC_QUERY_EXT_CTRL <VIDIOC_QUERYCTRL>`.
To change the value of a set of controls applications initialize the
``id``, ``size``, ``reserved2`` and ``value/value64/string/ptr`` fields
of each struct :c:type:`v4l2_ext_control` and call
the :ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctl. The controls will only be set if *all*
control values are valid.
To check if a set of controls have correct values applications
initialize the ``id``, ``size``, ``reserved2`` and
``value/value64/string/ptr`` fields of each struct
@ -86,6 +80,15 @@ initialize the ``id``, ``size``, ``reserved2`` and
values are automatically adjusted to a valid value or if an error is
returned.
To change the value of a set of controls applications initialize the
``id``, ``size``, ``reserved2`` and ``value/value64/string/ptr`` fields
of each struct :c:type:`v4l2_ext_control` and call
the :ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctl.
:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` will implicitly call
:ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` first and return an
error if that fails. So the controls will only be set if *all*
control values are valid.
When the ``id`` or ``which`` is invalid drivers return an ``EINVAL`` error
code. When the value is out of bounds drivers can choose to take the
closest valid value or return an ``ERANGE`` error code, whatever seems more

View File

@ -67,7 +67,7 @@ subdevice routing table. This may be smaller or larger than the value of
drivers may adjust the requested routing table.
The kernel can return a ``num_routes`` value larger than ``len_routes`` from
both ioctls. This indicates thare are more routes in the routing table than fits
both ioctls. This indicates there are more routes in the routing table than fits
the ``routes`` array. In this case, the ``routes`` array is filled by the kernel
with the first ``len_routes`` entries of the subdevice routing table. This is
not considered to be an error, and the ioctl call succeeds. If the applications

View File

@ -3936,7 +3936,7 @@ F: Documentation/devicetree/bindings/leds/ams,as3668.yaml
F: drivers/leds/leds-as3668.c
ASAHI KASEI AK7375 LENS VOICE COIL DRIVER
M: Tianshu Qiu <tian.shu.qiu@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -7868,6 +7868,16 @@ F: drivers/block/drbd/
F: include/linux/drbd*
F: lib/lru_cache.c
DREAMCHIP RPPX1 ISP
M: Jacopo Mondi <jacopo.mondi+renesas@ideasonboard.com>
M: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/userspace-api/media/v4l/metafmt-rppx1.rst
F: drivers/media/platform/dreamchip/rppx1/
F: include/uapi/linux/media/dreamchip/rppx1-config.h
DRIVER COMPONENT FRAMEWORK
L: dri-devel@lists.freedesktop.org
F: drivers/base/component.c
@ -13133,9 +13143,7 @@ F: drivers/iommu/intel/
INTEL IPU3 CSI-2 CIO2 DRIVER
M: Yong Zhi <yong.zhi@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Bingbu Cao <bingbu.cao@intel.com>
M: Dan Scally <dan.scally@ideasonboard.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -13144,8 +13152,6 @@ F: drivers/media/pci/intel/ipu3/
INTEL IPU3 CSI-2 IMGU DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@intel.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/media/ipu3.rst
@ -13155,8 +13161,6 @@ F: drivers/staging/media/ipu3/
INTEL IPU6 INPUT SYSTEM DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Bingbu Cao <bingbu.cao@intel.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -13165,7 +13169,6 @@ F: drivers/media/pci/intel/ipu6/
INTEL IPU7 INPUT SYSTEM DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -13377,6 +13380,7 @@ INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
M: Daniel Scally <dan.scally@ideasonboard.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
S: Maintained
T: git git://linuxtv.org/media.git
F: drivers/platform/x86/intel/int3472/
F: include/linux/platform_data/x86/int3472.h
@ -13467,7 +13471,6 @@ F: drivers/net/wireless/intel/iwlwifi/
INTEL VISION SENSING CONTROLLER DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@intel.com>
R: Lixu Zhang <lixu.zhang@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
@ -19501,7 +19504,9 @@ F: drivers/iio/adc/vf610_adc.c
NXP i.MX 8M ISI DRIVER
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
R: Frank Li <Frank.Li@nxp.com>
L: linux-media@vger.kernel.org
L: imx@lists.linux.dev
S: Maintained
F: Documentation/devicetree/bindings/media/fsl,imx8*-isi.yaml
F: Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
@ -19934,7 +19939,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml
F: drivers/media/i2c/os05b10.c
OMNIVISION OV01A10 SENSOR DRIVER
M: Bingbu Cao <bingbu.cao@intel.com>
M: Bingbu Cao <bingbu.cao@amd.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -20027,9 +20033,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov2735.yaml
F: drivers/media/i2c/ov2735.c
OMNIVISION OV2740 SENSOR DRIVER
M: Tianshu Qiu <tian.shu.qiu@intel.com>
R: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@amd.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -20171,8 +20176,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt
F: drivers/media/i2c/ov9650.c
OMNIVISION OV9734 SENSOR DRIVER
M: Tianshu Qiu <tian.shu.qiu@intel.com>
R: Bingbu Cao <bingbu.cao@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bingbu Cao <bingbu.cao@amd.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -25253,7 +25258,8 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx296.yaml
F: drivers/media/i2c/imx296.c
SONY IMX319 SENSOR DRIVER
M: Bingbu Cao <bingbu.cao@intel.com>
M: Bingbu Cao <bingbu.cao@amd.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -25275,7 +25281,9 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx335.yaml
F: drivers/media/i2c/imx335.c
SONY IMX355 SENSOR DRIVER
M: Tianshu Qiu <tian.shu.qiu@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Dave Stevenson <dave.stevenson@raspberrypi.com>
R: David Heidelberg <david@ixit.cz>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@ -25296,6 +25304,19 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
F: drivers/media/i2c/imx415.c
SONY IMX471 SENSOR DRIVER
M: Kate Hsuan <hpa@redhat.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/imx471.c
SONY IMX678 SENSOR DRIVER
M: Jai Luthra <jai.luthra@ideasonboard.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml
F: drivers/media/i2c/imx678.c
SONY MEMORYSTICK SUBSYSTEM
M: Maxim Levitsky <maximlevitsky@gmail.com>
M: Alex Dubov <oakad@yahoo.com>

View File

@ -859,6 +859,7 @@ static const char * const acpi_honor_dep_ids[] = {
"INTC10DE", /* CVS (LNL) driver must be loaded to allow camera streaming */
"INTC10E0", /* CVS (ARL) driver must be loaded to allow camera streaming */
"INTC10E1", /* CVS (PTL) driver must be loaded to allow camera streaming */
"INTC10FA", /* CVS (NVL) driver must be loaded to allow camera streaming */
"RSCV0001", /* RISC-V PLIC */
"RSCV0002", /* RISC-V APLIC */
"RSCV0005", /* RISC-V SBI MPXY MBOX */

View File

@ -80,9 +80,9 @@ void cec_queue_event_fh(struct cec_fh *fh,
const struct cec_event *new_ev, u64 ts)
{
static const u16 max_events[CEC_NUM_EVENTS] = {
1, 1, 800, 800, 8, 8, 8, 8
3, 1, 800, 800, 8, 8, 8, 8
};
struct cec_event_entry *entry;
struct cec_event_entry *new_entry, *entry;
unsigned int ev_idx = new_ev->event - 1;
if (WARN_ON(ev_idx >= ARRAY_SIZE(fh->events)))
@ -92,36 +92,57 @@ void cec_queue_event_fh(struct cec_fh *fh,
ts = ktime_get_ns();
mutex_lock(&fh->lock);
if (ev_idx < CEC_NUM_CORE_EVENTS)
entry = &fh->core_events[ev_idx];
else
entry = kmalloc_obj(*entry);
if (entry) {
new_entry = kmalloc_obj(*new_entry);
if (new_entry) {
if (new_ev->event == CEC_EVENT_LOST_MSGS &&
fh->queued_events[ev_idx]) {
entry = list_first_entry(&fh->events[ev_idx],
struct cec_event_entry, list);
entry->ev.lost_msgs.lost_msgs +=
new_ev->lost_msgs.lost_msgs;
kfree(new_entry);
goto unlock;
}
entry->ev = *new_ev;
entry->ev.ts = ts;
new_entry->ev = *new_ev;
new_entry->ev.ts = ts;
/*
* If the physical address becomes invalid (HPD went low),
* then just flush all pending STATE_CHANGE events since
* those are all obsoleted.
*
* This ensures you will not see stale STATE_CHANGE events.
*/
if (new_ev->event == CEC_EVENT_STATE_CHANGE &&
new_ev->state_change.phys_addr == CEC_PHYS_ADDR_INVALID &&
fh->queued_events[ev_idx]) {
/* drop all events */
while (!list_empty(&fh->events[ev_idx])) {
entry = list_first_entry(&fh->events[ev_idx],
struct cec_event_entry, list);
list_del(&entry->list);
kfree(entry);
fh->total_queued_events--;
fh->queued_events[ev_idx]--;
}
new_entry->ev.flags |= CEC_EVENT_FL_DROPPED_EVENTS;
}
if (fh->queued_events[ev_idx] < max_events[ev_idx]) {
/* Add new msg at the end of the queue */
list_add_tail(&entry->list, &fh->events[ev_idx]);
list_add_tail(&new_entry->list, &fh->events[ev_idx]);
fh->queued_events[ev_idx]++;
fh->total_queued_events++;
goto unlock;
}
if (ev_idx >= CEC_NUM_CORE_EVENTS) {
list_add_tail(&entry->list, &fh->events[ev_idx]);
/* drop the oldest event */
entry = list_first_entry(&fh->events[ev_idx],
struct cec_event_entry, list);
list_del(&entry->list);
kfree(entry);
}
list_add_tail(&new_entry->list, &fh->events[ev_idx]);
/* drop the oldest event */
entry = list_first_entry(&fh->events[ev_idx],
struct cec_event_entry, list);
list_del(&entry->list);
kfree(entry);
}
/* Mark that events were lost */
entry = list_first_entry_or_null(&fh->events[ev_idx],
@ -608,6 +629,13 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
attempts_made = 1;
mutex_lock(&adap->lock);
if (adap->error_inj_tx_timeouts) {
dprintk(2, "%s: error_inj_tx_timeouts %u\n",
__func__, adap->error_inj_tx_timeouts);
adap->error_inj_tx_timeouts--;
mutex_unlock(&adap->lock);
return;
}
data = adap->transmitting;
if (!data) {
/*
@ -965,7 +993,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
*/
mutex_unlock(&adap->lock);
err = wait_for_completion_killable(&data->c);
cancel_delayed_work_sync(&data->work);
disable_delayed_work_sync(&data->work);
mutex_lock(&adap->lock);
if (err)
@ -1317,7 +1345,7 @@ static int cec_config_log_addr(struct cec_adapter *adap,
{
struct cec_log_addrs *las = &adap->log_addrs;
struct cec_msg msg = { };
const unsigned int max_retries = 2;
const unsigned int max_attempts = 3;
unsigned int i;
int err;
@ -1328,7 +1356,7 @@ static int cec_config_log_addr(struct cec_adapter *adap,
msg.len = 1;
msg.msg[0] = (log_addr << 4) | log_addr;
for (i = 0; i < max_retries; i++) {
for (i = 0; i < max_attempts; i++) {
err = cec_transmit_msg_fh(adap, &msg, NULL, true);
/*
@ -1345,31 +1373,24 @@ static int cec_config_log_addr(struct cec_adapter *adap,
if (err)
return err;
/*
* The message was aborted or timed out due to a disconnect or
* unconfigure, just bail out.
*/
if (msg.tx_status &
(CEC_TX_STATUS_ABORTED | CEC_TX_STATUS_TIMEOUT))
return -EINTR;
if (msg.tx_status & CEC_TX_STATUS_OK)
return 0;
if (msg.tx_status & CEC_TX_STATUS_NACK)
break;
/*
* Retry up to max_retries times if the message was neither
* Do up to max_attempts if the message was neither
* OKed or NACKed. This can happen due to e.g. a Lost
* Arbitration condition.
*/
}
/*
* If we are unable to get an OK or a NACK after max_retries attempts
* If we are unable to get an OK or a NACK after max_attempts
* (and note that each attempt already consists of four polls), then
* we assume that something is really weird and that it is not a
* good idea to try and claim this logical address.
*/
if (i == max_retries) {
if (i == max_attempts) {
dprintk(0, "polling for LA %u failed with tx_status=0x%04x\n",
log_addr, msg.tx_status);
return 0;
@ -1468,6 +1489,7 @@ static int cec_config_thread_func(void *arg)
dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs);
las->log_addr_mask = 0;
las->flags &= ~CEC_LOG_ADDRS_FL_CONFIG_FAILED;
if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
goto configured;
@ -1600,6 +1622,8 @@ static int cec_config_thread_func(void *arg)
unconfigure:
for (i = 0; i < las->num_log_addrs; i++)
las->log_addr[i] = CEC_LOG_ADDR_INVALID;
if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
las->flags |= CEC_LOG_ADDRS_FL_CONFIG_FAILED;
cec_adap_unconfigure(adap);
adap->is_configuring = false;
adap->must_reconfigure = false;
@ -1717,7 +1741,6 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
cec_phys_addr_exp(phys_addr));
if (becomes_invalid || !is_invalid) {
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
cec_post_state_event(adap);
cec_adap_unconfigure(adap);
if (becomes_invalid) {
cec_adap_enable(adap);
@ -2219,9 +2242,13 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
* Unprocessed messages are aborted if userspace isn't doing
* any processing either.
*/
mutex_lock(&adap->lock);
if (!is_broadcast && !is_reply && !adap->follower_cnt &&
!adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
!adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) {
mutex_unlock(&adap->lock);
return cec_feature_abort(adap, msg);
}
mutex_unlock(&adap->lock);
break;
}
@ -2234,10 +2261,12 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
* Send to the exclusive follower if there is one, otherwise send
* to all followers.
*/
mutex_lock(&adap->lock);
if (adap->cec_follower)
cec_queue_msg_fh(adap->cec_follower, msg);
else
cec_queue_msg_followers(adap, msg);
mutex_unlock(&adap->lock);
return 0;
}

View File

@ -345,8 +345,7 @@ static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
if (copy_to_user(parg, &ev->ev, sizeof(ev->ev)))
err = -EFAULT;
if (ev_idx >= CEC_NUM_CORE_EVENTS)
kfree(ev);
kfree(ev);
fh->queued_events[ev_idx]--;
fh->total_queued_events--;
@ -673,7 +672,7 @@ static int cec_release(struct inode *inode, struct file *filp)
list_del(&entry->list);
kfree(entry);
}
for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) {
for (i = 0; i < CEC_NUM_EVENTS; i++) {
while (!list_empty(&fh->events[i])) {
struct cec_event_entry *entry =
list_first_entry(&fh->events[i],

View File

@ -5,16 +5,33 @@
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/cdev.h>
#include <linux/container_of.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/mm.h>
#include <linux/kobject.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/printk.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/sprintf.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <asm/page.h>
#include "cec-priv.h"
@ -93,7 +110,7 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
minor = find_first_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES);
if (minor == CEC_NUM_DEVICES) {
mutex_unlock(&cec_devnode_lock);
pr_err("could not get a free minor\n");
pr_err("Could not get a free minor\n");
return -ENFILE;
}
@ -104,19 +121,19 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
devnode->dev.bus = &cec_bus_type;
devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
devnode->dev.release = cec_devnode_release;
dev_set_name(&devnode->dev, "cec%d", devnode->minor);
dev_set_name(&devnode->dev, "%s%d", CEC_NAME, devnode->minor);
device_initialize(&devnode->dev);
/* Part 2: Initialize and register the character device */
cdev_init(&devnode->cdev, &cec_devnode_fops);
devnode->cdev.owner = owner;
kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
kobject_set_name(&devnode->cdev.kobj, "%s%d", CEC_NAME, devnode->minor);
devnode->registered = true;
ret = cdev_device_add(&devnode->cdev, &devnode->dev);
if (ret) {
devnode->registered = false;
pr_err("%s: cdev_device_add failed\n", __func__);
pr_err("cdev_device_add() failed\n");
goto clr_bit;
}
@ -205,19 +222,35 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused)
return call_op(adap, error_inj_show, sf);
}
DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj);
static int cec_error_inj_open(struct inode *inode, struct file *file)
static ssize_t cec_error_inj_tx_timeouts_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
return single_open(file, cec_error_inj_show, inode->i_private);
struct seq_file *sf = file->private_data;
struct cec_adapter *adap = sf->private;
int ret;
if (count > 5)
return -EINVAL;
mutex_lock(&adap->lock);
ret = kstrtou32_from_user(ubuf, count, 0, &adap->error_inj_tx_timeouts);
if (ret)
adap->error_inj_tx_timeouts = 0;
mutex_unlock(&adap->lock);
return ret ? : count;
}
static const struct file_operations cec_error_inj_fops = {
.open = cec_error_inj_open,
.write = cec_error_inj_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int cec_error_inj_tx_timeouts_show(struct seq_file *sf, void *unused)
{
struct cec_adapter *adap = sf->private;
seq_printf(sf, "%u", adap->error_inj_tx_timeouts);
return 0;
}
DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj_tx_timeouts);
#endif
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
@ -263,12 +296,11 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
mutex_init(&adap->devnode.lock_fhs);
mutex_init(&adap->devnode.lock);
adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
adap->kthread = kthread_run(cec_thread_func, adap, "%s-%s", CEC_NAME, name);
if (IS_ERR(adap->kthread)) {
pr_err("cec-%s: kernel_thread() failed\n", name);
pr_err("%s: kthread_run() failed\n", name);
res = PTR_ERR(adap->kthread);
kfree(adap);
return ERR_PTR(res);
goto err_free_adap;
}
#ifdef CONFIG_MEDIA_CEC_RC
@ -278,11 +310,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
/* Prepare the RC input device */
adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE);
if (!adap->rc) {
pr_err("cec-%s: failed to allocate memory for rc_dev\n",
name);
pr_err("%s: failed to allocate memory for rc_dev\n", name);
kthread_stop(adap->kthread);
kfree(adap);
return ERR_PTR(-ENOMEM);
res = -ENOMEM;
goto err_free_adap;
}
snprintf(adap->input_phys, sizeof(adap->input_phys),
@ -298,9 +329,18 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
adap->rc->allowed_protocols = RC_PROTO_BIT_CEC;
adap->rc->priv = adap;
adap->rc->map_name = RC_MAP_CEC;
adap->rc->timeout = MS_TO_US(550);
adap->rc->timeout = 550 * USEC_PER_MSEC;
#endif
return adap;
err_free_adap:
mutex_destroy(&adap->devnode.lock);
mutex_destroy(&adap->devnode.lock_fhs);
mutex_destroy(&adap->lock);
kfree(adap);
return ERR_PTR(res);
}
EXPORT_SYMBOL_GPL(cec_allocate_adapter);
@ -326,8 +366,7 @@ int cec_register_adapter(struct cec_adapter *adap,
res = rc_register_device(adap->rc);
if (res) {
pr_err("cec-%s: failed to prepare input device\n",
adap->name);
pr_err("%s: failed to prepare input device\n", adap->name);
rc_free_device(adap->rc);
adap->rc = NULL;
return res;
@ -356,6 +395,9 @@ int cec_register_adapter(struct cec_adapter *adap,
debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir,
cec_adap_status);
debugfs_create_file("error-inj-tx-timeouts", 0644, adap->cec_dir, adap,
&cec_error_inj_tx_timeouts_fops);
if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)
return 0;
debugfs_create_file("error-inj", 0644, adap->cec_dir, adap,
@ -371,9 +413,7 @@ void cec_unregister_adapter(struct cec_adapter *adap)
return;
#ifdef CONFIG_MEDIA_CEC_RC
/* Note: rc_unregister also calls rc_free */
rc_unregister_device(adap->rc);
adap->rc = NULL;
#endif
debugfs_remove_recursive(adap->cec_dir);
#ifdef CONFIG_CEC_NOTIFIER
@ -387,14 +427,23 @@ void cec_delete_adapter(struct cec_adapter *adap)
{
if (IS_ERR_OR_NULL(adap))
return;
if (adap->kthread_config)
kthread_stop(adap->kthread_config);
kthread_stop(adap->kthread);
if (adap->ops->adap_free)
adap->ops->adap_free(adap);
#ifdef CONFIG_MEDIA_CEC_RC
rc_free_device(adap->rc);
#endif
mutex_destroy(&adap->devnode.lock);
mutex_destroy(&adap->devnode.lock_fhs);
mutex_destroy(&adap->lock);
kfree(adap);
}
EXPORT_SYMBOL_GPL(cec_delete_adapter);
@ -407,14 +456,14 @@ static int __init cec_devnode_init(void)
int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME);
if (ret < 0) {
pr_warn("cec: unable to allocate major\n");
pr_warn("Unable to allocate major\n");
return ret;
}
#ifdef CONFIG_DEBUG_FS
top_cec_dir = debugfs_create_dir("cec", NULL);
top_cec_dir = debugfs_create_dir(CEC_NAME, NULL);
if (IS_ERR_OR_NULL(top_cec_dir)) {
pr_warn("cec: Failed to create debugfs cec dir\n");
pr_warn("Failed to create debugfs " CEC_NAME " dir\n");
top_cec_dir = NULL;
}
#endif
@ -423,7 +472,7 @@ static int __init cec_devnode_init(void)
if (ret < 0) {
debugfs_remove_recursive(top_cec_dir);
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
pr_warn("cec: bus_register failed\n");
pr_warn("bus_register() failed\n");
return -EIO;
}

View File

@ -115,7 +115,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
return;
pin->adap->cec_pin_is_high = v;
if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
if (atomic_read_acquire(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
u8 ev = v;
if (pin->work_pin_events_dropped) {
@ -126,7 +126,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
pin->work_pin_events_wr =
(pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS;
atomic_inc(&pin->work_pin_num_events);
atomic_inc_return_release(&pin->work_pin_num_events);
} else {
pin->work_pin_events_dropped = true;
pin->work_pin_events_dropped_cnt++;
@ -692,7 +692,6 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
v = cec_pin_read(pin);
if (!v)
break;
pin->state = CEC_ST_RX_START_BIT_HIGH;
delta = ktime_us_delta(ts, pin->ts);
/* Start bit low is too short, go back to idle */
if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) {
@ -703,7 +702,16 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
cec_pin_to_idle(pin);
break;
}
pin->state = CEC_ST_RX_START_BIT_HIGH;
if (rx_arb_lost(pin, &poll)) {
/*
* Normally rx_toggle is toggled in cec_pin_to_idle()
* when we're in an RX state, but here we switch to TX
* mode, so cec_pin_to_idle() sees a TX mode and never
* toggles rx_toggle. So toggle it here as a special
* corner case.
*/
pin->rx_toggle ^= 1;
cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf);
pin->tx_generated_poll = true;
pin->tx_extra_bytes = 0;
@ -1101,7 +1109,7 @@ static int cec_pin_thread_func(void *_adap)
pin->work_tx_ts);
}
while (atomic_read(&pin->work_pin_num_events)) {
while (atomic_read_acquire(&pin->work_pin_num_events)) {
unsigned int idx = pin->work_pin_events_rd;
u8 v = pin->work_pin_events[idx];
@ -1110,7 +1118,7 @@ static int cec_pin_thread_func(void *_adap)
v & CEC_PIN_EVENT_FL_DROPPED,
pin->work_pin_ts[idx]);
pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
atomic_dec(&pin->work_pin_num_events);
atomic_dec_return_release(&pin->work_pin_num_events);
}
switch (atomic_xchg(&pin->work_irq_change,

View File

@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr,
}
static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
.name = "core",
.reg_bits = 8,
.val_bits = 8,
.reg_read = meson_ao_cec_g12a_read,
@ -688,10 +689,8 @@ static int meson_ao_cec_g12a_probe(struct platform_device *pdev)
meson_ao_cec_g12a_irq,
meson_ao_cec_g12a_irq_thread,
0, NULL, ao_cec);
if (ret) {
dev_err(&pdev->dev, "irq request failed\n");
if (ret)
goto out_probe_adapter;
}
ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin");
if (IS_ERR(ao_cec->oscin)) {

View File

@ -636,10 +636,8 @@ static int meson_ao_cec_probe(struct platform_device *pdev)
meson_ao_cec_irq,
meson_ao_cec_irq_thread,
0, NULL, ao_cec);
if (ret) {
dev_err(&pdev->dev, "irq request failed\n");
if (ret)
goto out_probe_adapter;
}
ao_cec->core = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(ao_cec->core)) {

View File

@ -7,14 +7,15 @@
* Copyright (C) 2018, Aidilab Srl.
*/
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/time.h>
/* CEC Framework */
#include <media/cec-notifier.h>
@ -356,7 +357,7 @@ static int secocec_ir_probe(void *priv)
cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
cec->ir->priv = cec;
cec->ir->map_name = RC_MAP_HAUPPAUGE;
cec->ir->timeout = MS_TO_US(100);
cec->ir->timeout = 100 * USEC_PER_MSEC;
/* Clear the status register */
status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
@ -618,7 +619,6 @@ static int secocec_probe(struct platform_device *pdev)
dev_name(&pdev->dev), secocec);
if (ret) {
dev_err(dev, "Cannot request IRQ %d\n", secocec->irq);
ret = -EIO;
goto err;
}

View File

@ -132,7 +132,8 @@ static void stm32_rx_done(struct stm32_cec *cec, u32 status)
u32 val;
regmap_read(cec->regmap, CEC_RXDR, &val);
cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
if (cec->rx_msg.len < CEC_MAX_MSG_SIZE)
cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
}
if (cec->irq_status & RXEND) {
@ -193,18 +194,24 @@ static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable)
static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
{
struct stm32_cec *cec = adap->priv;
u32 oar = (1 << logical_addr) << 16;
u32 val;
int ret;
/* Poll every 100µs the register CEC_CR to wait end of transmission */
regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM),
100, CEC_XFER_TIMEOUT_MS * 1000);
ret = regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM),
100, CEC_XFER_TIMEOUT_MS * 1000);
if (ret)
return ret;
regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
if (logical_addr == CEC_LOG_ADDR_INVALID)
if (logical_addr == CEC_LOG_ADDR_INVALID) {
regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0);
else
} else {
u32 oar = BIT(logical_addr) << 16;
regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar);
}
regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);

View File

@ -24,6 +24,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/clk/tegra.h>
#include <media/cec-notifier.h>
@ -47,6 +48,7 @@ struct tegra_cec {
u32 tx_buf[CEC_MAX_MSG_SIZE];
u8 tx_buf_cur;
u8 tx_buf_cnt;
u32 rx_total_low_drives;
};
static inline u32 cec_read(struct tegra_cec *cec, u32 reg)
@ -116,6 +118,13 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
return IRQ_WAKE_THREAD;
}
if (status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED) {
dev_warn_ratelimited(dev, "RX bus error detected, generated low drive\n");
cec->rx_total_low_drives++;
cec_write(cec, TEGRA_CEC_INT_STAT,
TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED);
}
if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
(status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
tegra_cec_error_recovery(cec);
@ -241,9 +250,21 @@ static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)
TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |
TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED |
TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED);
cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);
/*
* TX_NAK_MODE ensures that the whole message is transmitted even
* if each byte is NACKed. Without this flag the retransmit of the
* messages after a NACK can be corrupt. This is a bug in the hardware.
*
* While less efficient, in practice you rarely transmit messages
* that can be NACKed, with the exception of POLL messages which
* are just one byte anyway.
*/
cec_write(cec, TEGRA_CEC_HW_CONTROL,
TEGRA_CEC_HWCTRL_TX_RX_MODE |
TEGRA_CEC_HWCTRL_TX_NAK_MODE);
return 0;
}
@ -307,11 +328,20 @@ static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
return 0;
}
static void tegra_cec_adap_status(struct cec_adapter *adap, struct seq_file *file)
{
struct tegra_cec *cec = adap->priv;
seq_printf(file, "receive low drive count: %u\n",
cec->rx_total_low_drives);
}
static const struct cec_adap_ops tegra_cec_ops = {
.adap_enable = tegra_cec_adap_enable,
.adap_log_addr = tegra_cec_adap_log_addr,
.adap_transmit = tegra_cec_adap_transmit,
.adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable,
.adap_status = tegra_cec_adap_status,
};
static int tegra_cec_probe(struct platform_device *pdev)
@ -381,11 +411,8 @@ static int tegra_cec_probe(struct platform_device *pdev)
tegra_cec_irq_handler, tegra_cec_irq_thread_handler,
0, "cec_irq", &pdev->dev);
if (ret) {
dev_err(&pdev->dev,
"Unable to request interrupt for device\n");
if (ret)
goto err_clk;
}
cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,
CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL |
@ -458,6 +485,7 @@ static const struct of_device_id tegra_cec_of_match[] = {
{ .compatible = "nvidia,tegra210-cec", },
{},
};
MODULE_DEVICE_TABLE(of, tegra_cec_of_match);
static struct platform_driver tegra_cec_driver = {
.driver = {

View File

@ -657,7 +657,8 @@ static void extron_process_received(struct extron_port *port, const char *data)
if (!port || port->disconnected)
return;
if (len < 5 || (len - 2) % 3 || data[len - 2] != '*')
if (len < 5 || ((len - 2) / 3 > sizeof(msg.msg)) ||
(len - 2) % 3 || data[len - 2] != '*')
goto malformed;
while (*data != '*') {

View File

@ -2346,9 +2346,11 @@ static void tpg_fill_params_extras(const struct tpg_data *tpg,
(params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
}
static void tpg_fill_plane_extras(const struct tpg_data *tpg,
const struct tpg_draw_params *params,
unsigned p, unsigned h, u8 *vbuf)
/* noinline to work around clang KASAN issues */
static noinline_for_stack void
tpg_fill_plane_extras(const struct tpg_data *tpg,
const struct tpg_draw_params *params,
unsigned p, unsigned h, u8 *vbuf)
{
unsigned twopixsize = params->twopixsize;
unsigned img_width = params->img_width;
@ -2483,9 +2485,9 @@ static void tpg_fill_plane_extras(const struct tpg_data *tpg,
}
}
static void tpg_fill_plane_pattern(const struct tpg_data *tpg,
const struct tpg_draw_params *params,
unsigned p, unsigned h, u8 *vbuf)
static noinline_for_stack void
tpg_fill_plane_pattern(const struct tpg_data *tpg, const struct tpg_draw_params *params,
unsigned p, unsigned h, u8 *vbuf)
{
unsigned twopixsize = params->twopixsize;
unsigned img_width = params->img_width;

View File

@ -635,19 +635,6 @@ struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp)
}
EXPORT_SYMBOL_GPL(vb2_find_buffer);
/*
* vb2_querybuf() - query video buffer information
* @q: vb2 queue
* @b: buffer struct passed from userspace to vidioc_querybuf handler
* in driver
*
* Should be called from vidioc_querybuf ioctl handler in driver.
* This function will verify the passed v4l2_buffer structure and fill the
* relevant information for the userspace.
*
* The return values from this function are intended to be directly returned
* from vidioc_querybuf handler in driver.
*/
int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;

View File

@ -884,6 +884,7 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
struct dmxdev_filter *filter, u16 pid)
{
struct dmxdev_feed *feed;
int ret;
if ((filter->type != DMXDEV_TYPE_PES) ||
(filter->state < DMXDEV_STATE_SET))
@ -901,8 +902,14 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
feed->pid = pid;
list_add(&feed->next, &filter->feed.ts);
if (filter->state >= DMXDEV_STATE_GO)
return dvb_dmxdev_start_feed(dmxdev, filter, feed);
if (filter->state >= DMXDEV_STATE_GO) {
ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
if (ret < 0) {
list_del(&feed->next);
kfree(feed);
return ret;
}
}
return 0;
}

View File

@ -30,6 +30,7 @@
#include <linux/kthread.h>
#include <linux/ktime.h>
#include <linux/compat.h>
#include <linux/lockdep.h>
#include <asm/processor.h>
#include <media/dvb_frontend.h>
@ -2760,77 +2761,25 @@ static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *w
return 0;
}
static int dvb_frontend_open(struct inode *inode, struct file *file)
static int __dvb_frontend_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend *fe = dvbdev->priv;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
struct dvb_adapter *adapter = fe->dvb;
int ret;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (fe->exit == DVB_FE_DEVICE_REMOVED)
return -ENODEV;
if (adapter->mfe_shared == 2) {
mutex_lock(&adapter->mfe_lock);
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (adapter->mfe_dvbdev &&
!adapter->mfe_dvbdev->writers) {
mutex_unlock(&adapter->mfe_lock);
return -EBUSY;
}
adapter->mfe_dvbdev = dvbdev;
}
} else if (adapter->mfe_shared) {
mutex_lock(&adapter->mfe_lock);
if (!adapter->mfe_dvbdev)
adapter->mfe_dvbdev = dvbdev;
else if (adapter->mfe_dvbdev != dvbdev) {
struct dvb_device
*mfedev = adapter->mfe_dvbdev;
struct dvb_frontend
*mfe = mfedev->priv;
struct dvb_frontend_private
*mfepriv = mfe->frontend_priv;
int mferetry = (dvb_mfe_wait_time << 1);
mutex_unlock(&adapter->mfe_lock);
while (mferetry-- && (mfedev->users != -1 ||
mfepriv->thread)) {
if (msleep_interruptible(500)) {
if (signal_pending(current))
return -EINTR;
}
}
mutex_lock(&adapter->mfe_lock);
if (adapter->mfe_dvbdev != dvbdev) {
mfedev = adapter->mfe_dvbdev;
mfe = mfedev->priv;
mfepriv = mfe->frontend_priv;
if (mfedev->users != -1 ||
mfepriv->thread) {
mutex_unlock(&adapter->mfe_lock);
return -EBUSY;
}
adapter->mfe_dvbdev = dvbdev;
}
}
}
if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
goto err0;
return ret;
/* If we took control of the bus, we need to force
reinitialization. This is because many ts_bus_ctrl()
functions strobe the RESET pin on the demod, and if the
frontend thread already exists then the dvb_init() routine
won't get called (which is what usually does initial
register configuration). */
/*
* If we took control of the bus, we need to force
* reinitialization. This is because many ts_bus_ctrl()
* functions strobe the RESET pin on the demod, and if the
* frontend thread already exists then the dvb_init() routine
* won't get called (which is what usually does initial
* register configuration).
*/
fepriv->reinitialise = 1;
}
@ -2871,8 +2820,6 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
dvb_frontend_get(fe);
if (adapter->mfe_shared)
mutex_unlock(&adapter->mfe_lock);
return ret;
err3:
@ -2891,12 +2838,88 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
err1:
if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
fe->ops.ts_bus_ctrl(fe, 0);
err0:
if (adapter->mfe_shared)
mutex_unlock(&adapter->mfe_lock);
return ret;
}
static int wait_dvb_frontend(struct dvb_adapter *adapter,
struct dvb_device *mfedev)
{
struct dvb_frontend *mfe = mfedev->priv;
struct dvb_frontend_private *mfepriv = mfe->frontend_priv;
int mferetry = (dvb_mfe_wait_time << 1);
int ret = 0;
lockdep_assert_held(&adapter->mfe_lock);
if (mfedev->users == -1 && !mfepriv->thread)
return 0;
mutex_unlock(&adapter->mfe_lock);
while (mferetry-- && (mfedev->users != -1 || mfepriv->thread)) {
if (msleep_interruptible(500))
if (signal_pending(current)) {
ret = -EINTR;
break;
}
}
mutex_lock(&adapter->mfe_lock);
return ret;
}
static int dvb_frontend_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend *fe = dvbdev->priv;
struct dvb_adapter *adapter = fe->dvb;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (fe->exit == DVB_FE_DEVICE_REMOVED)
return -ENODEV;
if (!adapter->mfe_shared)
return __dvb_frontend_open(inode, file);
guard(mutex)(&adapter->mfe_lock);
if (adapter->mfe_shared == 2) {
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (adapter->mfe_dvbdev &&
!adapter->mfe_dvbdev->writers)
return -EBUSY;
adapter->mfe_dvbdev = dvbdev;
}
return __dvb_frontend_open(inode, file);
}
if (!adapter->mfe_dvbdev) {
adapter->mfe_dvbdev = dvbdev;
} else if (adapter->mfe_dvbdev != dvbdev) {
struct dvb_device *mfedev = adapter->mfe_dvbdev;
struct dvb_frontend *mfe = mfedev->priv;
struct dvb_frontend_private *mfepriv = mfe->frontend_priv;
int ret;
ret = wait_dvb_frontend(adapter, mfedev);
if (ret)
return ret;
if (adapter->mfe_dvbdev != dvbdev) {
mfedev = adapter->mfe_dvbdev;
mfe = mfedev->priv;
mfepriv = mfe->frontend_priv;
if (mfedev->users != -1 || mfepriv->thread)
return -EBUSY;
adapter->mfe_dvbdev = dvbdev;
}
}
return __dvb_frontend_open(inode, file);
}
static int dvb_frontend_release(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;

View File

@ -995,22 +995,22 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
const struct helene_config *config,
struct i2c_adapter *i2c)
{
struct helene_priv *priv = NULL;
struct helene_priv *pr = NULL;
priv = kzalloc_obj(struct helene_priv);
if (priv == NULL)
pr = kzalloc_obj(struct helene_priv);
if (!pr)
return NULL;
priv->i2c_address = (config->i2c_address >> 1);
priv->i2c = i2c;
priv->set_tuner_data = config->set_tuner_priv;
priv->set_tuner = config->set_tuner_callback;
priv->xtal = config->xtal;
pr->i2c_address = (config->i2c_address >> 1);
pr->i2c = i2c;
pr->set_tuner_data = config->set_tuner_priv;
pr->set_tuner = config->set_tuner_callback;
pr->xtal = config->xtal;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (helene_x_pon(priv) != 0) {
kfree(priv);
if (helene_x_pon(pr) != 0) {
kfree(pr);
return NULL;
}
@ -1019,10 +1019,10 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = priv;
dev_info(&priv->i2c->dev,
"Sony HELENE Sat attached on addr=%x at I2C adapter %p\n",
priv->i2c_address, priv->i2c);
fe->tuner_priv = pr;
dev_info(&pr->i2c->dev,
"Sony HELENE Sat attached on addr=%x at I2C adapter %p\n",
pr->i2c_address, pr->i2c);
return fe;
}
EXPORT_SYMBOL_GPL(helene_attach_s);
@ -1031,22 +1031,22 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
const struct helene_config *config,
struct i2c_adapter *i2c)
{
struct helene_priv *priv = NULL;
struct helene_priv *pr = NULL;
priv = kzalloc_obj(struct helene_priv);
if (priv == NULL)
pr = kzalloc_obj(struct helene_priv);
if (!pr)
return NULL;
priv->i2c_address = (config->i2c_address >> 1);
priv->i2c = i2c;
priv->set_tuner_data = config->set_tuner_priv;
priv->set_tuner = config->set_tuner_callback;
priv->xtal = config->xtal;
pr->i2c_address = (config->i2c_address >> 1);
pr->i2c = i2c;
pr->set_tuner_data = config->set_tuner_priv;
pr->set_tuner = config->set_tuner_callback;
pr->xtal = config->xtal;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (helene_x_pon(priv) != 0) {
kfree(priv);
if (helene_x_pon(pr) != 0) {
kfree(pr);
return NULL;
}
@ -1055,10 +1055,10 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = priv;
dev_info(&priv->i2c->dev,
"Sony HELENE Ter attached on addr=%x at I2C adapter %p\n",
priv->i2c_address, priv->i2c);
fe->tuner_priv = pr;
dev_info(&pr->i2c->dev,
"Sony HELENE Ter attached on addr=%x at I2C adapter %p\n",
pr->i2c_address, pr->i2c);
return fe;
}
EXPORT_SYMBOL_GPL(helene_attach);

View File

@ -906,9 +906,12 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
goto err;
mutex_unlock(&dev->v4l2_lock);
return 0;
err:
rtl2832_sdr_free_urbs(dev);
rtl2832_sdr_free_stream_bufs(dev);
rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
mutex_unlock(&dev->v4l2_lock);
@ -1477,14 +1480,22 @@ static void rtl2832_sdr_remove(struct platform_device *pdev)
dev_dbg(&pdev->dev, "\n");
mutex_lock(&dev->vb_queue_lock);
mutex_lock(&dev->v4l2_lock);
/* No need to keep the urbs around after disconnection */
dev->udev = NULL;
/*
* vb2_video_unregister_device() releases the vb2 queue, which
* triggers rtl2832_sdr_stop_streaming() if streaming is active.
* stop_streaming() uses dev->udev to free URBs and coherent DMA
* stream buffers via usb_free_coherent(), so it must run before
* dev->udev is cleared. vb2_video_unregister_device() locks
* vb_queue_lock internally and stop_streaming() locks v4l2_lock,
* so neither may be held by the caller.
*/
v4l2_device_disconnect(&dev->v4l2_dev);
video_unregister_device(&dev->vdev);
vb2_video_unregister_device(&dev->vdev);
mutex_lock(&dev->v4l2_lock);
dev->udev = NULL;
mutex_unlock(&dev->v4l2_lock);
mutex_unlock(&dev->vb_queue_lock);
v4l2_device_put(&dev->v4l2_dev);
module_put(pdev->dev.parent->driver->owner);
}

View File

@ -259,6 +259,7 @@ config VIDEO_IMX335
config VIDEO_IMX355
tristate "Sony IMX355 sensor support"
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
IMX355 camera.
@ -269,6 +270,7 @@ config VIDEO_IMX355
config VIDEO_IMX412
tristate "Sony IMX412 sensor support"
depends on OF
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
IMX412 camera.
@ -287,6 +289,27 @@ config VIDEO_IMX415
To compile this driver as a module, choose M here: the
module will be called imx415.
config VIDEO_IMX471
tristate "Sony IMX471 sensor support"
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
IMX471 camera.
To compile this driver as a module, choose M here: the
module will be called imx471.
config VIDEO_IMX678
tristate "Sony IMX678 sensor support"
depends on GPIOLIB
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
IMX678 camera.
To compile this driver as a module, choose M here: the
module will be called imx678.
config VIDEO_MAX9271_LIB
tristate

View File

@ -61,6 +61,8 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_IMX412) += imx412.o
obj-$(CONFIG_VIDEO_IMX415) += imx415.o
obj-$(CONFIG_VIDEO_IMX678) += imx678.o
obj-$(CONFIG_VIDEO_IMX471) += imx471.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o

View File

@ -1290,7 +1290,7 @@ static int alvium_set_ctrl_auto_exposure(struct alvium_dev *alvium, bool on)
struct device *dev = &alvium->i2c_client->dev;
int ret;
ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW,
ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_AUTO_RW,
on ? 0x02 : 0x00);
if (ret) {
dev_err(dev, "Fail to set autoexposure reg\n");

View File

@ -31,6 +31,7 @@
#define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */
#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */
#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */
/*
* IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h.
@ -39,6 +40,7 @@
static const struct pci_device_id icvs_ipu7_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) },
{ }
};
@ -962,6 +964,7 @@ static const struct acpi_device_id intel_cvs_acpi_match[] = {
{ "INTC10DE" }, /* LNL */
{ "INTC10E0" }, /* ARL */
{ "INTC10E1" }, /* PTL */
{ "INTC10FA" }, /* NVL */
{ }
};
MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match);

View File

@ -432,7 +432,6 @@ enum icvs_state {
* @freq_ctrl: (future) frequency control pointer
* @pads: Local media pads (sink/source)
* @nr_of_lanes: Active CSI-2 lane count
* @link_freq: Current link frequency (Hz)
* @ipu_link: PM runtime device link (IPU consumer, CVS supplier)
* @res: Resource capability (light/full)
* @caps: Reported device protocol capabilities
@ -458,7 +457,6 @@ struct icvs {
struct v4l2_ctrl *freq_ctrl;
struct media_pad pads[ICVS_CSI_NUM_PADS];
u32 nr_of_lanes;
u64 link_freq;
struct device_link *ipu_link;
enum icvs_resources res;
struct icvs_dev_capabilities caps;

View File

@ -46,6 +46,7 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = {
/**
* csi_set_link_cfg - Program default CSI-2 link parameters
* @ctx: CVS device context
* @link_freq: Link frequency (Hz)
*
* Populates a HOST_SET_MIPI_CONFIG command using current lane count and
* link frequency, then submits it to the device.
@ -53,12 +54,12 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = {
*
* Return: 0 on success or negative errno.
*/
static int csi_set_link_cfg(struct icvs *ctx)
static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq)
{
struct icvs_cmd cmd = {
.cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG),
.param.conf.nr_of_lanes = ctx->nr_of_lanes,
.param.conf.link_freq = ctx->link_freq,
.param.conf.link_freq = link_freq,
};
size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
@ -91,7 +92,7 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev *remote_sd =
media_entity_to_v4l2_subdev(ctx->remote->entity);
struct device *dev = cvs_dev(ctx);
s64 freq;
s64 link_freq;
int ret;
/* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */
@ -99,15 +100,14 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd,
if (ret < 0)
return ret;
freq = v4l2_get_link_freq(ctx->remote, 0, 0);
if (freq < 0) {
ret = freq;
link_freq = v4l2_get_link_freq(ctx->remote, 0, 0);
if (link_freq < 0) {
ret = link_freq;
goto err_rpm_put;
}
ctx->link_freq = freq;
if (ctx->i2c_client) {
ret = csi_set_link_cfg(ctx);
ret = csi_set_link_cfg(ctx, link_freq);
if (ret < 0)
goto err_rpm_put_sync;
}
@ -345,7 +345,6 @@ static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
if (freq < 0)
return -EINVAL;
ctx->link_freq = freq;
cfg->link_freq = freq;
return 0;

View File

@ -3962,6 +3962,7 @@ static int cx25840_probe(struct i2c_client *client)
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
media_entity_cleanup(&sd->entity);
return err;
}
if (!is_cx2583x(state))
@ -3986,6 +3987,7 @@ static void cx25840_remove(struct i2c_client *client)
cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
media_entity_cleanup(&sd->entity);
}
static const struct i2c_device_id cx25840_id[] = {

View File

@ -6,6 +6,7 @@
* Copyright (c) 2023-2025 Hans de Goede <hansg@kernel.org>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
@ -84,6 +85,8 @@
#define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd)
struct gc0310_device {
struct clk *clk;
struct v4l2_subdev sd;
struct media_pad pad;
@ -635,7 +638,6 @@ static int gc0310_check_hwcfg(struct device *dev)
};
struct fwnode_handle *ep_fwnode;
unsigned long link_freq_bitmap;
u32 mclk;
int ret;
/*
@ -647,21 +649,6 @@ static int gc0310_check_hwcfg(struct device *dev)
return dev_err_probe(dev, -EPROBE_DEFER,
"waiting for fwnode graph endpoint\n");
ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
&mclk);
if (ret) {
fwnode_handle_put(ep_fwnode);
return dev_err_probe(dev, ret,
"reading clock-frequency property\n");
}
if (mclk != GC0310_MCLK_FREQ) {
fwnode_handle_put(ep_fwnode);
return dev_err_probe(dev, -EINVAL,
"external clock %u is not supported\n",
mclk);
}
ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg);
fwnode_handle_put(ep_fwnode);
if (ret)
@ -685,6 +672,7 @@ static int gc0310_check_hwcfg(struct device *dev)
static int gc0310_probe(struct i2c_client *client)
{
struct gc0310_device *sensor;
unsigned long freq;
int ret;
ret = gc0310_check_hwcfg(&client->dev);
@ -695,6 +683,16 @@ static int gc0310_probe(struct i2c_client *client)
if (!sensor)
return -ENOMEM;
sensor->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
if (IS_ERR(sensor->clk))
return dev_err_probe(&client->dev, PTR_ERR(sensor->clk),
"failed to get clock\n");
freq = clk_get_rate(sensor->clk);
if (freq != GC0310_MCLK_FREQ)
return dev_err_probe(&client->dev, -EINVAL,
"external clock %lu is not supported\n", freq);
sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset)) {
return dev_err_probe(&client->dev, PTR_ERR(sensor->reset),

View File

@ -110,11 +110,16 @@
/* Test Pattern Control */
#define IMX219_REG_TEST_PATTERN CCI_REG16(0x0600)
#define IMX219_TEST_PATTERN_DISABLE 0
#define IMX219_TEST_PATTERN_SOLID_COLOR 1
#define IMX219_TEST_PATTERN_COLOR_BARS 2
#define IMX219_TEST_PATTERN_GREY_COLOR 3
#define IMX219_TEST_PATTERN_PN9 4
#define IMX219_TEST_PATTERN_DISABLE 0
#define IMX219_TEST_PATTERN_SOLID_COLOR 1
#define IMX219_TEST_PATTERN_COLOR_BARS 2
#define IMX219_TEST_PATTERN_GREY_COLOR 3
#define IMX219_TEST_PATTERN_PN9 4
#define IMX219_TEST_PATTERN_16SPLIT_COLOR_BARS 5
#define IMX219_TEST_PATTERN_16SPLIT_INV_COLOR_BARS 6
#define IMX219_TEST_PATTERN_COLUMN_COUNTER 7
#define IMX219_TEST_PATTERN_INV_COLUMN_COUNTER 8
#define IMX219_TEST_PATTERN_PN31 9
/* Test pattern colour components */
#define IMX219_REG_TESTP_RED CCI_REG16(0x0602)
@ -238,7 +243,12 @@ static const char * const imx219_test_pattern_menu[] = {
"Color Bars",
"Solid Color",
"Grey Color Bars",
"PN9"
"PN9",
"16 Split Color Bars",
"16 Split Inverted Color Bars",
"Column Counter",
"Inverted Column Counter",
"PN31"
};
static const int imx219_test_pattern_val[] = {
@ -247,6 +257,11 @@ static const int imx219_test_pattern_val[] = {
IMX219_TEST_PATTERN_SOLID_COLOR,
IMX219_TEST_PATTERN_GREY_COLOR,
IMX219_TEST_PATTERN_PN9,
IMX219_TEST_PATTERN_16SPLIT_COLOR_BARS,
IMX219_TEST_PATTERN_16SPLIT_INV_COLOR_BARS,
IMX219_TEST_PATTERN_COLUMN_COUNTER,
IMX219_TEST_PATTERN_INV_COLUMN_COUNTER,
IMX219_TEST_PATTERN_PN31
};
/* regulator supplies */

File diff suppressed because it is too large Load Diff

View File

@ -13,38 +13,39 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
/* Streaming Mode */
#define IMX412_REG_MODE_SELECT 0x0100
#define IMX412_REG_MODE_SELECT CCI_REG8(0x0100)
#define IMX412_MODE_STANDBY 0x00
#define IMX412_MODE_STREAMING 0x01
/* Lines per frame */
#define IMX412_REG_LPFR 0x0340
#define IMX412_REG_LPFR CCI_REG16(0x0340)
/* Chip ID */
#define IMX412_REG_ID 0x0016
#define IMX412_REG_ID CCI_REG16(0x0016)
#define IMX412_ID 0x577
/* Exposure control */
#define IMX412_REG_EXPOSURE_CIT 0x0202
#define IMX412_REG_EXPOSURE_CIT CCI_REG16(0x0202)
#define IMX412_EXPOSURE_MIN 8
#define IMX412_EXPOSURE_OFFSET 22
#define IMX412_EXPOSURE_STEP 1
#define IMX412_EXPOSURE_DEFAULT 0x0648
/* Analog gain control */
#define IMX412_REG_AGAIN 0x0204
#define IMX412_REG_AGAIN CCI_REG16(0x0204)
#define IMX412_AGAIN_MIN 0
#define IMX412_AGAIN_MAX 978
#define IMX412_AGAIN_STEP 1
#define IMX412_AGAIN_DEFAULT 0
/* Group hold register */
#define IMX412_REG_HOLD 0x0104
#define IMX412_REG_HOLD CCI_REG8(0x0104)
/* Input clock rate */
#define IMX412_INCLK_RATE 24000000
@ -56,16 +57,6 @@
#define IMX412_REG_MIN 0x00
#define IMX412_REG_MAX 0xffff
/**
* struct imx412_reg - imx412 sensor register
* @address: Register address
* @val: Register value
*/
struct imx412_reg {
u16 address;
u8 val;
};
/**
* struct imx412_reg_list - imx412 sensor register list
* @num_of_regs: Number of registers in the list
@ -73,7 +64,7 @@ struct imx412_reg {
*/
struct imx412_reg_list {
u32 num_of_regs;
const struct imx412_reg *regs;
const struct cci_reg_sequence *regs;
};
/**
@ -111,6 +102,7 @@ static const char * const imx412_supply_names[] = {
/**
* struct imx412 - imx412 sensor device structure
* @dev: Pointer to generic device
* @cci: CCI register map
* @client: Pointer to i2c client
* @sd: V4L2 sub-device
* @pad: Media pad. Only one pad supported
@ -126,10 +118,10 @@ static const char * const imx412_supply_names[] = {
* @again_ctrl: Pointer to analog gain control
* @vblank: Vertical blanking in lines
* @cur_mode: Pointer to current selected sensor mode
* @mutex: Mutex for serializing sensor controls
*/
struct imx412 {
struct device *dev;
struct regmap *cci;
struct i2c_client *client;
struct v4l2_subdev sd;
struct media_pad pad;
@ -147,7 +139,6 @@ struct imx412 {
};
u32 vblank;
const struct imx412_mode *cur_mode;
struct mutex mutex;
};
static const s64 link_freq[] = {
@ -155,238 +146,238 @@ static const s64 link_freq[] = {
};
/* Sensor mode registers */
static const struct imx412_reg mode_4056x3040_regs[] = {
{0x0136, 0x18},
{0x0137, 0x00},
{0x3c7e, 0x08},
{0x3c7f, 0x02},
{0x38a8, 0x1f},
{0x38a9, 0xff},
{0x38aa, 0x1f},
{0x38ab, 0xff},
{0x55d4, 0x00},
{0x55d5, 0x00},
{0x55d6, 0x07},
{0x55d7, 0xff},
{0x55e8, 0x07},
{0x55e9, 0xff},
{0x55ea, 0x00},
{0x55eb, 0x00},
{0x575c, 0x07},
{0x575d, 0xff},
{0x575e, 0x00},
{0x575f, 0x00},
{0x5764, 0x00},
{0x5765, 0x00},
{0x5766, 0x07},
{0x5767, 0xff},
{0x5974, 0x04},
{0x5975, 0x01},
{0x5f10, 0x09},
{0x5f11, 0x92},
{0x5f12, 0x32},
{0x5f13, 0x72},
{0x5f14, 0x16},
{0x5f15, 0xba},
{0x5f17, 0x13},
{0x5f18, 0x24},
{0x5f19, 0x60},
{0x5f1a, 0xe3},
{0x5f1b, 0xad},
{0x5f1c, 0x74},
{0x5f2d, 0x25},
{0x5f5c, 0xd0},
{0x6a22, 0x00},
{0x6a23, 0x1d},
{0x7ba8, 0x00},
{0x7ba9, 0x00},
{0x886b, 0x00},
{0x9002, 0x0a},
{0x9004, 0x1a},
{0x9214, 0x93},
{0x9215, 0x69},
{0x9216, 0x93},
{0x9217, 0x6b},
{0x9218, 0x93},
{0x9219, 0x6d},
{0x921a, 0x57},
{0x921b, 0x58},
{0x921c, 0x57},
{0x921d, 0x59},
{0x921e, 0x57},
{0x921f, 0x5a},
{0x9220, 0x57},
{0x9221, 0x5b},
{0x9222, 0x93},
{0x9223, 0x02},
{0x9224, 0x93},
{0x9225, 0x03},
{0x9226, 0x93},
{0x9227, 0x04},
{0x9228, 0x93},
{0x9229, 0x05},
{0x922a, 0x98},
{0x922b, 0x21},
{0x922c, 0xb2},
{0x922d, 0xdb},
{0x922e, 0xb2},
{0x922f, 0xdc},
{0x9230, 0xb2},
{0x9231, 0xdd},
{0x9232, 0xe2},
{0x9233, 0xe1},
{0x9234, 0xb2},
{0x9235, 0xe2},
{0x9236, 0xb2},
{0x9237, 0xe3},
{0x9238, 0xb7},
{0x9239, 0xb9},
{0x923a, 0xb7},
{0x923b, 0xbb},
{0x923c, 0xb7},
{0x923d, 0xbc},
{0x923e, 0xb7},
{0x923f, 0xc5},
{0x9240, 0xb7},
{0x9241, 0xc7},
{0x9242, 0xb7},
{0x9243, 0xc9},
{0x9244, 0x98},
{0x9245, 0x56},
{0x9246, 0x98},
{0x9247, 0x55},
{0x9380, 0x00},
{0x9381, 0x62},
{0x9382, 0x00},
{0x9383, 0x56},
{0x9384, 0x00},
{0x9385, 0x52},
{0x9388, 0x00},
{0x9389, 0x55},
{0x938a, 0x00},
{0x938b, 0x55},
{0x938c, 0x00},
{0x938d, 0x41},
{0x5078, 0x01},
{0x0112, 0x0a},
{0x0113, 0x0a},
{0x0114, 0x03},
{0x0342, 0x11},
{0x0343, 0xa0},
{0x0340, 0x0d},
{0x0341, 0xda},
{0x3210, 0x00},
{0x0344, 0x00},
{0x0345, 0x00},
{0x0346, 0x00},
{0x0347, 0x00},
{0x0348, 0x0f},
{0x0349, 0xd7},
{0x034a, 0x0b},
{0x034b, 0xdf},
{0x00e3, 0x00},
{0x00e4, 0x00},
{0x00e5, 0x01},
{0x00fc, 0x0a},
{0x00fd, 0x0a},
{0x00fe, 0x0a},
{0x00ff, 0x0a},
{0xe013, 0x00},
{0x0220, 0x00},
{0x0221, 0x11},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x0900, 0x00},
{0x0901, 0x11},
{0x0902, 0x00},
{0x3140, 0x02},
{0x3241, 0x11},
{0x3250, 0x03},
{0x3e10, 0x00},
{0x3e11, 0x00},
{0x3f0d, 0x00},
{0x3f42, 0x00},
{0x3f43, 0x00},
{0x0401, 0x00},
{0x0404, 0x00},
{0x0405, 0x10},
{0x0408, 0x00},
{0x0409, 0x00},
{0x040a, 0x00},
{0x040b, 0x00},
{0x040c, 0x0f},
{0x040d, 0xd8},
{0x040e, 0x0b},
{0x040f, 0xe0},
{0x034c, 0x0f},
{0x034d, 0xd8},
{0x034e, 0x0b},
{0x034f, 0xe0},
{0x0301, 0x05},
{0x0303, 0x02},
{0x0305, 0x04},
{0x0306, 0x00},
{0x0307, 0xc8},
{0x0309, 0x0a},
{0x030b, 0x01},
{0x030d, 0x02},
{0x030e, 0x01},
{0x030f, 0x5e},
{0x0310, 0x00},
{0x0820, 0x12},
{0x0821, 0xc0},
{0x0822, 0x00},
{0x0823, 0x00},
{0x3e20, 0x01},
{0x3e37, 0x00},
{0x3f50, 0x00},
{0x3f56, 0x00},
{0x3f57, 0xe2},
{0x3c0a, 0x5a},
{0x3c0b, 0x55},
{0x3c0c, 0x28},
{0x3c0d, 0x07},
{0x3c0e, 0xff},
{0x3c0f, 0x00},
{0x3c10, 0x00},
{0x3c11, 0x02},
{0x3c12, 0x00},
{0x3c13, 0x03},
{0x3c14, 0x00},
{0x3c15, 0x00},
{0x3c16, 0x0c},
{0x3c17, 0x0c},
{0x3c18, 0x0c},
{0x3c19, 0x0a},
{0x3c1a, 0x0a},
{0x3c1b, 0x0a},
{0x3c1c, 0x00},
{0x3c1d, 0x00},
{0x3c1e, 0x00},
{0x3c1f, 0x00},
{0x3c20, 0x00},
{0x3c21, 0x00},
{0x3c22, 0x3f},
{0x3c23, 0x0a},
{0x3e35, 0x01},
{0x3f4a, 0x03},
{0x3f4b, 0xbf},
{0x3f26, 0x00},
{0x0202, 0x0d},
{0x0203, 0xc4},
{0x0204, 0x00},
{0x0205, 0x00},
{0x020e, 0x01},
{0x020f, 0x00},
{0x0210, 0x01},
{0x0211, 0x00},
{0x0212, 0x01},
{0x0213, 0x00},
{0x0214, 0x01},
{0x0215, 0x00},
{0xbcf1, 0x00},
static const struct cci_reg_sequence mode_4056x3040_regs[] = {
{ CCI_REG8(0x0136), 0x18 },
{ CCI_REG8(0x0137), 0x00 },
{ CCI_REG8(0x3c7e), 0x08 },
{ CCI_REG8(0x3c7f), 0x02 },
{ CCI_REG8(0x38a8), 0x1f },
{ CCI_REG8(0x38a9), 0xff },
{ CCI_REG8(0x38aa), 0x1f },
{ CCI_REG8(0x38ab), 0xff },
{ CCI_REG8(0x55d4), 0x00 },
{ CCI_REG8(0x55d5), 0x00 },
{ CCI_REG8(0x55d6), 0x07 },
{ CCI_REG8(0x55d7), 0xff },
{ CCI_REG8(0x55e8), 0x07 },
{ CCI_REG8(0x55e9), 0xff },
{ CCI_REG8(0x55ea), 0x00 },
{ CCI_REG8(0x55eb), 0x00 },
{ CCI_REG8(0x575c), 0x07 },
{ CCI_REG8(0x575d), 0xff },
{ CCI_REG8(0x575e), 0x00 },
{ CCI_REG8(0x575f), 0x00 },
{ CCI_REG8(0x5764), 0x00 },
{ CCI_REG8(0x5765), 0x00 },
{ CCI_REG8(0x5766), 0x07 },
{ CCI_REG8(0x5767), 0xff },
{ CCI_REG8(0x5974), 0x04 },
{ CCI_REG8(0x5975), 0x01 },
{ CCI_REG8(0x5f10), 0x09 },
{ CCI_REG8(0x5f11), 0x92 },
{ CCI_REG8(0x5f12), 0x32 },
{ CCI_REG8(0x5f13), 0x72 },
{ CCI_REG8(0x5f14), 0x16 },
{ CCI_REG8(0x5f15), 0xba },
{ CCI_REG8(0x5f17), 0x13 },
{ CCI_REG8(0x5f18), 0x24 },
{ CCI_REG8(0x5f19), 0x60 },
{ CCI_REG8(0x5f1a), 0xe3 },
{ CCI_REG8(0x5f1b), 0xad },
{ CCI_REG8(0x5f1c), 0x74 },
{ CCI_REG8(0x5f2d), 0x25 },
{ CCI_REG8(0x5f5c), 0xd0 },
{ CCI_REG8(0x6a22), 0x00 },
{ CCI_REG8(0x6a23), 0x1d },
{ CCI_REG8(0x7ba8), 0x00 },
{ CCI_REG8(0x7ba9), 0x00 },
{ CCI_REG8(0x886b), 0x00 },
{ CCI_REG8(0x9002), 0x0a },
{ CCI_REG8(0x9004), 0x1a },
{ CCI_REG8(0x9214), 0x93 },
{ CCI_REG8(0x9215), 0x69 },
{ CCI_REG8(0x9216), 0x93 },
{ CCI_REG8(0x9217), 0x6b },
{ CCI_REG8(0x9218), 0x93 },
{ CCI_REG8(0x9219), 0x6d },
{ CCI_REG8(0x921a), 0x57 },
{ CCI_REG8(0x921b), 0x58 },
{ CCI_REG8(0x921c), 0x57 },
{ CCI_REG8(0x921d), 0x59 },
{ CCI_REG8(0x921e), 0x57 },
{ CCI_REG8(0x921f), 0x5a },
{ CCI_REG8(0x9220), 0x57 },
{ CCI_REG8(0x9221), 0x5b },
{ CCI_REG8(0x9222), 0x93 },
{ CCI_REG8(0x9223), 0x02 },
{ CCI_REG8(0x9224), 0x93 },
{ CCI_REG8(0x9225), 0x03 },
{ CCI_REG8(0x9226), 0x93 },
{ CCI_REG8(0x9227), 0x04 },
{ CCI_REG8(0x9228), 0x93 },
{ CCI_REG8(0x9229), 0x05 },
{ CCI_REG8(0x922a), 0x98 },
{ CCI_REG8(0x922b), 0x21 },
{ CCI_REG8(0x922c), 0xb2 },
{ CCI_REG8(0x922d), 0xdb },
{ CCI_REG8(0x922e), 0xb2 },
{ CCI_REG8(0x922f), 0xdc },
{ CCI_REG8(0x9230), 0xb2 },
{ CCI_REG8(0x9231), 0xdd },
{ CCI_REG8(0x9232), 0xe2 },
{ CCI_REG8(0x9233), 0xe1 },
{ CCI_REG8(0x9234), 0xb2 },
{ CCI_REG8(0x9235), 0xe2 },
{ CCI_REG8(0x9236), 0xb2 },
{ CCI_REG8(0x9237), 0xe3 },
{ CCI_REG8(0x9238), 0xb7 },
{ CCI_REG8(0x9239), 0xb9 },
{ CCI_REG8(0x923a), 0xb7 },
{ CCI_REG8(0x923b), 0xbb },
{ CCI_REG8(0x923c), 0xb7 },
{ CCI_REG8(0x923d), 0xbc },
{ CCI_REG8(0x923e), 0xb7 },
{ CCI_REG8(0x923f), 0xc5 },
{ CCI_REG8(0x9240), 0xb7 },
{ CCI_REG8(0x9241), 0xc7 },
{ CCI_REG8(0x9242), 0xb7 },
{ CCI_REG8(0x9243), 0xc9 },
{ CCI_REG8(0x9244), 0x98 },
{ CCI_REG8(0x9245), 0x56 },
{ CCI_REG8(0x9246), 0x98 },
{ CCI_REG8(0x9247), 0x55 },
{ CCI_REG8(0x9380), 0x00 },
{ CCI_REG8(0x9381), 0x62 },
{ CCI_REG8(0x9382), 0x00 },
{ CCI_REG8(0x9383), 0x56 },
{ CCI_REG8(0x9384), 0x00 },
{ CCI_REG8(0x9385), 0x52 },
{ CCI_REG8(0x9388), 0x00 },
{ CCI_REG8(0x9389), 0x55 },
{ CCI_REG8(0x938a), 0x00 },
{ CCI_REG8(0x938b), 0x55 },
{ CCI_REG8(0x938c), 0x00 },
{ CCI_REG8(0x938d), 0x41 },
{ CCI_REG8(0x5078), 0x01 },
{ CCI_REG8(0x0112), 0x0a },
{ CCI_REG8(0x0113), 0x0a },
{ CCI_REG8(0x0114), 0x03 },
{ CCI_REG8(0x0342), 0x11 },
{ CCI_REG8(0x0343), 0xa0 },
{ CCI_REG8(0x0340), 0x0d },
{ CCI_REG8(0x0341), 0xda },
{ CCI_REG8(0x3210), 0x00 },
{ CCI_REG8(0x0344), 0x00 },
{ CCI_REG8(0x0345), 0x00 },
{ CCI_REG8(0x0346), 0x00 },
{ CCI_REG8(0x0347), 0x00 },
{ CCI_REG8(0x0348), 0x0f },
{ CCI_REG8(0x0349), 0xd7 },
{ CCI_REG8(0x034a), 0x0b },
{ CCI_REG8(0x034b), 0xdf },
{ CCI_REG8(0x00e3), 0x00 },
{ CCI_REG8(0x00e4), 0x00 },
{ CCI_REG8(0x00e5), 0x01 },
{ CCI_REG8(0x00fc), 0x0a },
{ CCI_REG8(0x00fd), 0x0a },
{ CCI_REG8(0x00fe), 0x0a },
{ CCI_REG8(0x00ff), 0x0a },
{ CCI_REG8(0xe013), 0x00 },
{ CCI_REG8(0x0220), 0x00 },
{ CCI_REG8(0x0221), 0x11 },
{ CCI_REG8(0x0381), 0x01 },
{ CCI_REG8(0x0383), 0x01 },
{ CCI_REG8(0x0385), 0x01 },
{ CCI_REG8(0x0387), 0x01 },
{ CCI_REG8(0x0900), 0x00 },
{ CCI_REG8(0x0901), 0x11 },
{ CCI_REG8(0x0902), 0x00 },
{ CCI_REG8(0x3140), 0x02 },
{ CCI_REG8(0x3241), 0x11 },
{ CCI_REG8(0x3250), 0x03 },
{ CCI_REG8(0x3e10), 0x00 },
{ CCI_REG8(0x3e11), 0x00 },
{ CCI_REG8(0x3f0d), 0x00 },
{ CCI_REG8(0x3f42), 0x00 },
{ CCI_REG8(0x3f43), 0x00 },
{ CCI_REG8(0x0401), 0x00 },
{ CCI_REG8(0x0404), 0x00 },
{ CCI_REG8(0x0405), 0x10 },
{ CCI_REG8(0x0408), 0x00 },
{ CCI_REG8(0x0409), 0x00 },
{ CCI_REG8(0x040a), 0x00 },
{ CCI_REG8(0x040b), 0x00 },
{ CCI_REG8(0x040c), 0x0f },
{ CCI_REG8(0x040d), 0xd8 },
{ CCI_REG8(0x040e), 0x0b },
{ CCI_REG8(0x040f), 0xe0 },
{ CCI_REG8(0x034c), 0x0f },
{ CCI_REG8(0x034d), 0xd8 },
{ CCI_REG8(0x034e), 0x0b },
{ CCI_REG8(0x034f), 0xe0 },
{ CCI_REG8(0x0301), 0x05 },
{ CCI_REG8(0x0303), 0x02 },
{ CCI_REG8(0x0305), 0x04 },
{ CCI_REG8(0x0306), 0x00 },
{ CCI_REG8(0x0307), 0xc8 },
{ CCI_REG8(0x0309), 0x0a },
{ CCI_REG8(0x030b), 0x01 },
{ CCI_REG8(0x030d), 0x02 },
{ CCI_REG8(0x030e), 0x01 },
{ CCI_REG8(0x030f), 0x5e },
{ CCI_REG8(0x0310), 0x00 },
{ CCI_REG8(0x0820), 0x12 },
{ CCI_REG8(0x0821), 0xc0 },
{ CCI_REG8(0x0822), 0x00 },
{ CCI_REG8(0x0823), 0x00 },
{ CCI_REG8(0x3e20), 0x01 },
{ CCI_REG8(0x3e37), 0x00 },
{ CCI_REG8(0x3f50), 0x00 },
{ CCI_REG8(0x3f56), 0x00 },
{ CCI_REG8(0x3f57), 0xe2 },
{ CCI_REG8(0x3c0a), 0x5a },
{ CCI_REG8(0x3c0b), 0x55 },
{ CCI_REG8(0x3c0c), 0x28 },
{ CCI_REG8(0x3c0d), 0x07 },
{ CCI_REG8(0x3c0e), 0xff },
{ CCI_REG8(0x3c0f), 0x00 },
{ CCI_REG8(0x3c10), 0x00 },
{ CCI_REG8(0x3c11), 0x02 },
{ CCI_REG8(0x3c12), 0x00 },
{ CCI_REG8(0x3c13), 0x03 },
{ CCI_REG8(0x3c14), 0x00 },
{ CCI_REG8(0x3c15), 0x00 },
{ CCI_REG8(0x3c16), 0x0c },
{ CCI_REG8(0x3c17), 0x0c },
{ CCI_REG8(0x3c18), 0x0c },
{ CCI_REG8(0x3c19), 0x0a },
{ CCI_REG8(0x3c1a), 0x0a },
{ CCI_REG8(0x3c1b), 0x0a },
{ CCI_REG8(0x3c1c), 0x00 },
{ CCI_REG8(0x3c1d), 0x00 },
{ CCI_REG8(0x3c1e), 0x00 },
{ CCI_REG8(0x3c1f), 0x00 },
{ CCI_REG8(0x3c20), 0x00 },
{ CCI_REG8(0x3c21), 0x00 },
{ CCI_REG8(0x3c22), 0x3f },
{ CCI_REG8(0x3c23), 0x0a },
{ CCI_REG8(0x3e35), 0x01 },
{ CCI_REG8(0x3f4a), 0x03 },
{ CCI_REG8(0x3f4b), 0xbf },
{ CCI_REG8(0x3f26), 0x00 },
{ CCI_REG8(0x0202), 0x0d },
{ CCI_REG8(0x0203), 0xc4 },
{ CCI_REG8(0x0204), 0x00 },
{ CCI_REG8(0x0205), 0x00 },
{ CCI_REG8(0x020e), 0x01 },
{ CCI_REG8(0x020f), 0x00 },
{ CCI_REG8(0x0210), 0x01 },
{ CCI_REG8(0x0211), 0x00 },
{ CCI_REG8(0x0212), 0x01 },
{ CCI_REG8(0x0213), 0x00 },
{ CCI_REG8(0x0214), 0x01 },
{ CCI_REG8(0x0215), 0x00 },
{ CCI_REG8(0xbcf1), 0x00 },
};
/* Supported sensor mode configurations */
@ -417,97 +408,6 @@ static inline struct imx412 *to_imx412(struct v4l2_subdev *subdev)
return container_of(subdev, struct imx412, sd);
}
/**
* imx412_read_reg() - Read registers.
* @imx412: pointer to imx412 device
* @reg: register address
* @len: length of bytes to read. Max supported bytes is 4
* @val: pointer to register value to be filled.
*
* Return: 0 if successful, error code otherwise.
*/
static int imx412_read_reg(struct imx412 *imx412, u16 reg, u32 len, u32 *val)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
struct i2c_msg msgs[2] = {0};
u8 addr_buf[2] = {0};
u8 data_buf[4] = {0};
int ret;
if (WARN_ON(len > 4))
return -EINVAL;
put_unaligned_be16(reg, addr_buf);
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = ARRAY_SIZE(addr_buf);
msgs[0].buf = addr_buf;
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = &data_buf[4 - len];
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return -EIO;
*val = get_unaligned_be32(data_buf);
return 0;
}
/**
* imx412_write_reg() - Write register
* @imx412: pointer to imx412 device
* @reg: register address
* @len: length of bytes. Max supported bytes is 4
* @val: register value
*
* Return: 0 if successful, error code otherwise.
*/
static int imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd);
u8 buf[6] = {0};
if (WARN_ON(len > 4))
return -EINVAL;
put_unaligned_be16(reg, buf);
put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
if (i2c_master_send(client, buf, len + 2) != len + 2)
return -EIO;
return 0;
}
/**
* imx412_write_regs() - Write a list of registers
* @imx412: pointer to imx412 device
* @regs: list of registers to be written
* @len: length of registers array
*
* Return: 0 if successful, error code otherwise.
*/
static int imx412_write_regs(struct imx412 *imx412,
const struct imx412_reg *regs, u32 len)
{
unsigned int i;
int ret;
for (i = 0; i < len; i++) {
ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val);
if (ret)
return ret;
}
return 0;
}
/**
* imx412_update_controls() - Update control ranges based on streaming mode
* @imx412: pointer to imx412 device
@ -543,29 +443,25 @@ static int imx412_update_controls(struct imx412 *imx412,
static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain)
{
u32 lpfr;
int ret;
int ret = 0;
int ret_hold;
lpfr = imx412->vblank + imx412->cur_mode->height;
dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u\n",
exposure, gain, lpfr);
ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1);
if (ret)
return ret;
cci_write(imx412->cci, IMX412_REG_HOLD, 1, &ret);
ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr);
if (ret)
goto error_release_group_hold;
cci_write(imx412->cci, IMX412_REG_LPFR, lpfr, &ret);
ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, exposure);
if (ret)
goto error_release_group_hold;
cci_write(imx412->cci, IMX412_REG_EXPOSURE_CIT, exposure, &ret);
ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain);
cci_write(imx412->cci, IMX412_REG_AGAIN, gain, &ret);
error_release_group_hold:
imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 0);
ret_hold = cci_write(imx412->cci, IMX412_REG_HOLD, 0, NULL);
if (ret_hold)
return ret_hold;
return ret;
}
@ -679,8 +575,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd,
{
struct imx412 *imx412 = to_imx412(sd);
mutex_lock(&imx412->mutex);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
struct v4l2_mbus_framefmt *framefmt;
@ -690,8 +584,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd,
imx412_fill_pad_format(imx412, imx412->cur_mode, fmt);
}
mutex_unlock(&imx412->mutex);
return 0;
}
@ -703,8 +595,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd,
const struct imx412_mode *mode;
int ret = 0;
mutex_lock(&imx412->mutex);
mode = &supported_mode;
imx412_fill_pad_format(imx412, mode, fmt);
@ -719,8 +609,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd,
imx412->cur_mode = mode;
}
mutex_unlock(&imx412->mutex);
return ret;
}
@ -737,88 +625,86 @@ static int imx412_init_state(struct v4l2_subdev *sd,
}
/**
* imx412_start_streaming() - Start sensor stream
* @imx412: pointer to imx412 device
* imx412_enable_streams() - Enable specified streams for the sensor
* @sd: pointer to the V4L2 subdevice
* @state: pointer to the subdevice state
* @pad: pad number for which streams are enabled
* @streams_mask: bitmask specifying the streams to enable
*
* Return: 0 if successful, error code otherwise.
*/
static int imx412_start_streaming(struct imx412 *imx412)
static int imx412_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
const struct imx412_reg_list *reg_list;
struct imx412 *imx412 = to_imx412(sd);
int ret;
ret = pm_runtime_resume_and_get(imx412->dev);
if (ret < 0)
return ret;
/* Write sensor mode registers */
reg_list = &imx412->cur_mode->reg_list;
ret = imx412_write_regs(imx412, reg_list->regs,
reg_list->num_of_regs);
ret = cci_multi_reg_write(imx412->cci, reg_list->regs,
reg_list->num_of_regs, NULL);
if (ret) {
dev_err(imx412->dev, "fail to write initial registers\n");
return ret;
goto err_rpm_put;
}
/* Setup handler will write actual exposure and gain */
ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler);
if (ret) {
dev_err(imx412->dev, "fail to setup handler\n");
return ret;
goto err_rpm_put;
}
/* Delay is required before streaming*/
usleep_range(7400, 8000);
/* Start streaming */
ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
1, IMX412_MODE_STREAMING);
ret = cci_write(imx412->cci, IMX412_REG_MODE_SELECT,
IMX412_MODE_STREAMING, NULL);
if (ret) {
dev_err(imx412->dev, "fail to start streaming\n");
return ret;
goto err_rpm_put;
}
return 0;
err_rpm_put:
pm_runtime_put(imx412->dev);
return ret;
}
/**
* imx412_stop_streaming() - Stop sensor stream
* @imx412: pointer to imx412 device
* imx412_disable_streams() - Enable specified streams for the sensor
* @sd: pointer to the V4L2 subdevice
* @state: pointer to the subdevice state
* @pad: pad number for which streams are disabled
* @streams_mask: bitmask specifying the streams to disable
*
* Return: 0 if successful, error code otherwise.
*/
static int imx412_stop_streaming(struct imx412 *imx412)
{
return imx412_write_reg(imx412, IMX412_REG_MODE_SELECT,
1, IMX412_MODE_STANDBY);
}
static int imx412_set_stream(struct v4l2_subdev *sd, int enable)
static int imx412_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct imx412 *imx412 = to_imx412(sd);
int ret;
mutex_lock(&imx412->mutex);
ret = cci_write(imx412->cci, IMX412_REG_MODE_SELECT,
IMX412_MODE_STANDBY, NULL);
if (enable) {
ret = pm_runtime_resume_and_get(imx412->dev);
if (ret)
goto error_unlock;
if (ret)
dev_err(imx412->dev, "failed to set stream off\n");
ret = imx412_start_streaming(imx412);
if (ret)
goto error_power_off;
} else {
imx412_stop_streaming(imx412);
pm_runtime_put(imx412->dev);
}
mutex_unlock(&imx412->mutex);
pm_runtime_put(imx412->dev);
return 0;
error_power_off:
pm_runtime_put(imx412->dev);
error_unlock:
mutex_unlock(&imx412->mutex);
return ret;
}
/**
@ -830,16 +716,18 @@ static int imx412_set_stream(struct v4l2_subdev *sd, int enable)
static int imx412_detect(struct imx412 *imx412)
{
int ret;
u32 val;
u64 val;
ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val);
ret = cci_read(imx412->cci, IMX412_REG_ID, &val, NULL);
if (ret)
return ret;
return dev_err_probe(imx412->dev, ret,
"failed to read chip id %x\n",
IMX412_ID);
if (val != IMX412_ID) {
dev_err(imx412->dev, "chip id mismatch: %x!=%x\n",
IMX412_ID, val);
return -ENXIO;
return dev_err_probe(imx412->dev, -ENODEV,
"chip id mismatch: %x!=%llx",
IMX412_ID, val);
}
return 0;
@ -933,7 +821,7 @@ static int imx412_parse_hw_config(struct imx412 *imx412)
/* V4l2 subdevice ops */
static const struct v4l2_subdev_video_ops imx412_video_ops = {
.s_stream = imx412_set_stream,
.s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops imx412_pad_ops = {
@ -941,6 +829,8 @@ static const struct v4l2_subdev_pad_ops imx412_pad_ops = {
.enum_frame_size = imx412_enum_frame_size,
.get_fmt = imx412_get_pad_format,
.set_fmt = imx412_set_pad_format,
.enable_streams = imx412_enable_streams,
.disable_streams = imx412_disable_streams,
};
static const struct v4l2_subdev_ops imx412_subdev_ops = {
@ -1021,9 +911,6 @@ static int imx412_init_controls(struct imx412 *imx412)
if (ret)
return ret;
/* Serialize controls with sensor device */
ctrl_hdlr->lock = &imx412->mutex;
/* Initialize exposure and gain */
lpfr = mode->vblank + mode->height;
imx412->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
@ -1104,6 +991,11 @@ static int imx412_probe(struct i2c_client *client)
if (!name)
return -ENODEV;
imx412->cci = devm_cci_regmap_init_i2c(client, 16);
if (IS_ERR(imx412->cci))
return dev_err_probe(imx412->dev, PTR_ERR(imx412->cci),
"Failed to init CCI\n");
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops);
imx412->sd.internal_ops = &imx412_internal_ops;
@ -1114,13 +1006,10 @@ static int imx412_probe(struct i2c_client *client)
return ret;
}
mutex_init(&imx412->mutex);
ret = imx412_power_on(imx412->dev);
if (ret) {
dev_err(imx412->dev, "failed to power-on the sensor\n");
goto error_mutex_destroy;
}
if (ret)
return dev_err_probe(imx412->dev, ret,
"failed to power-on the sensor\n");
/* Check module identity */
ret = imx412_detect(imx412);
@ -1153,27 +1042,37 @@ static int imx412_probe(struct i2c_client *client)
goto error_handler_free;
}
ret = v4l2_async_register_subdev_sensor(&imx412->sd);
imx412->sd.state_lock = imx412->ctrl_handler.lock;
ret = v4l2_subdev_init_finalize(&imx412->sd);
if (ret < 0) {
dev_err(imx412->dev,
"failed to register async subdev: %d\n", ret);
dev_err_probe(imx412->dev, ret, "subdev init error\n");
goto error_media_entity;
}
pm_runtime_set_active(imx412->dev);
pm_runtime_enable(imx412->dev);
ret = v4l2_async_register_subdev_sensor(&imx412->sd);
if (ret < 0) {
dev_err_probe(imx412->dev, ret,
"failed to register sub-device\n");
goto error_subdev_cleanup;
}
pm_runtime_idle(imx412->dev);
return 0;
error_subdev_cleanup:
v4l2_subdev_cleanup(&imx412->sd);
pm_runtime_disable(imx412->dev);
pm_runtime_set_suspended(imx412->dev);
error_media_entity:
media_entity_cleanup(&imx412->sd.entity);
error_handler_free:
v4l2_ctrl_handler_free(imx412->sd.ctrl_handler);
error_power_off:
imx412_power_off(imx412->dev);
error_mutex_destroy:
mutex_destroy(&imx412->mutex);
return ret;
}
@ -1181,9 +1080,9 @@ static int imx412_probe(struct i2c_client *client)
static void imx412_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct imx412 *imx412 = to_imx412(sd);
v4l2_async_unregister_subdev(sd);
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
@ -1191,8 +1090,6 @@ static void imx412_remove(struct i2c_client *client)
if (!pm_runtime_status_suspended(&client->dev))
imx412_power_off(&client->dev);
pm_runtime_set_suspended(&client->dev);
mutex_destroy(&imx412->mutex);
}
static const struct dev_pm_ops imx412_pm_ops = {

View File

@ -686,7 +686,7 @@ static int imx415_set_testpattern(struct imx415 *sensor, int val)
cci_write(sensor->regmap, IMX415_DIG_CLP_MODE, 0x01, &ret);
cci_write(sensor->regmap, IMX415_WRJ_OPEN, 0x01, &ret);
}
return 0;
return ret;
}
static int imx415_s_ctrl(struct v4l2_ctrl *ctrl)
@ -720,7 +720,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl)
ret = cci_write(sensor->regmap, IMX415_VMAX,
format->height + ctrl->val, NULL);
if (ret)
return ret;
break;
/*
* Exposure is set based on VMAX which has just changed, so
* program exposure register as well

957
drivers/media/i2c/imx471.c Normal file
View File

@ -0,0 +1,957 @@
// SPDX-License-Identifier: GPL-2.0
/*
* imx471.c - imx471 sensor driver
*
* Copyright (C) 2025 Intel Corporation
* Copyright (C) 2026 Kate Hsuan <hpa@redhat.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-fwnode.h>
#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100)
#define IMX471_MODE_STANDBY 0x00
#define IMX471_MODE_STREAMING 0x01
/* Chip ID */
#define IMX471_REG_CHIP_ID CCI_REG16(0x0016)
#define IMX471_CHIP_ID 0x0471
/* V_TIMING internal */
#define IMX471_REG_FLL CCI_REG16(0x0340)
#define IMX471_FLL_MAX 0xffff
/* Exposure control */
#define IMX471_REG_EXPOSURE CCI_REG16(0x0202)
#define IMX471_EXPOSURE_MIN 1
#define IMX471_EXPOSURE_STEP 1
#define IMX471_EXPOSURE_DEFAULT 1270
/* Default exposure margin */
#define IMX471_EXPOSURE_MARGIN 18
/* Analog gain control */
#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204)
#define IMX471_ANA_GAIN_MIN 0
#define IMX471_ANA_GAIN_MAX 800
#define IMX471_ANA_GAIN_STEP 1
#define IMX471_ANA_GAIN_DEFAULT 0
/* Digital gain control */
#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9)
#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e)
#define IMX471_DGTL_GAIN_MIN 256
#define IMX471_DGTL_GAIN_MAX 4095
#define IMX471_DGTL_GAIN_STEP 1
#define IMX471_DGTL_GAIN_DEFAULT 256
/* HFLIP and VFLIP control */
#define IMX471_REG_ORIENTATION CCI_REG8(0x0101)
/* Test Pattern Control */
#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600)
/* default link frequency and external clock */
#define IMX471_LINK_FREQ_DEFAULT 200000000LL
#define IMX471_EXT_CLK 19200000
/* PLL */
#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301)
#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303)
#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305)
#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306)
#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309)
#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b)
#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310)
#define IMX471_PLL_SINGLE 0
#define IMX471_PLL_DUAL 1
/* IMX471 native and active pixel array size */
#define IMX471_NATIVE_WIDTH 4672
#define IMX471_NATIVE_HEIGHT 3512
#define IMX471_PIXEL_ARRAY_LEFT 8
#define IMX471_PIXEL_ARRAY_TOP 8
#define IMX471_PIXEL_ARRAY_WIDTH 4656
#define IMX471_PIXEL_ARRAY_HEIGHT 3496
#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136)
#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112)
#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a
#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114)
#define IMX471_CSI_2_LANE_MODE 1
#define IMX471_CSI_4_LANE_MODE 3
#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344)
#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346)
#define IMX471_REG_X_ADD_END CCI_REG16(0x0348)
#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a)
#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c)
#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e)
#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381)
#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383)
#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385)
#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387)
#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408)
#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a)
#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c)
#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e)
/* Binning mode */
#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900)
#define IMX471_BINNING_NONE 0
#define IMX471_BINNING_ENABLE 1
#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901)
#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902)
#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd)
static const char * const imx471_supply_name[] = {
"vana",
};
struct imx471_mode {
u32 width;
u32 height;
/* V-timing */
u32 fll_def;
u32 fll_min;
/* H-timing */
u32 llp;
const struct cci_reg_sequence *default_mode_regs;
unsigned int default_mode_regs_length;
};
struct imx471 {
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vflip;
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *exposure;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)];
struct clk *img_clk;
struct device *dev;
struct regmap *regmap;
};
static const struct cci_reg_sequence imx471_global_regs[] = {
{ IMX471_REG_EXCK_FREQ, 0x1333 },
{ CCI_REG8(0x3c7e), 0x08 },
{ CCI_REG8(0x3c7f), 0x05 },
{ CCI_REG8(0x3e35), 0x00 },
{ CCI_REG8(0x3e36), 0x00 },
{ CCI_REG8(0x3e37), 0x00 },
{ CCI_REG8(0x3f7f), 0x01 },
{ CCI_REG8(0x4431), 0x04 },
{ CCI_REG8(0x531c), 0x01 },
{ CCI_REG8(0x531d), 0x02 },
{ CCI_REG8(0x531e), 0x04 },
{ CCI_REG8(0x5928), 0x00 },
{ CCI_REG8(0x5929), 0x2f },
{ CCI_REG8(0x592a), 0x00 },
{ CCI_REG8(0x592b), 0x85 },
{ CCI_REG8(0x592c), 0x00 },
{ CCI_REG8(0x592d), 0x32 },
{ CCI_REG8(0x592e), 0x00 },
{ CCI_REG8(0x592f), 0x88 },
{ CCI_REG8(0x5930), 0x00 },
{ CCI_REG8(0x5931), 0x3d },
{ CCI_REG8(0x5932), 0x00 },
{ CCI_REG8(0x5933), 0x93 },
{ CCI_REG8(0x5938), 0x00 },
{ CCI_REG8(0x5939), 0x24 },
{ CCI_REG8(0x593a), 0x00 },
{ CCI_REG8(0x593b), 0x7a },
{ CCI_REG8(0x593c), 0x00 },
{ CCI_REG8(0x593d), 0x24 },
{ CCI_REG8(0x593e), 0x00 },
{ CCI_REG8(0x593f), 0x7a },
{ CCI_REG8(0x5940), 0x00 },
{ CCI_REG8(0x5941), 0x2f },
{ CCI_REG8(0x5942), 0x00 },
{ CCI_REG8(0x5943), 0x85 },
{ CCI_REG8(0x5f0e), 0x6e },
{ CCI_REG8(0x5f11), 0xc6 },
{ CCI_REG8(0x5f17), 0x5e },
{ CCI_REG8(0x7990), 0x01 },
{ CCI_REG8(0x7993), 0x5d },
{ CCI_REG8(0x7994), 0x5d },
{ CCI_REG8(0x7995), 0xa1 },
{ CCI_REG8(0x799a), 0x01 },
{ CCI_REG8(0x799d), 0x00 },
{ CCI_REG8(0x8169), 0x01 },
{ CCI_REG8(0x8359), 0x01 },
{ CCI_REG8(0x9302), 0x1e },
{ CCI_REG8(0x9306), 0x1f },
{ CCI_REG8(0x930a), 0x26 },
{ CCI_REG8(0x930e), 0x23 },
{ CCI_REG8(0x9312), 0x23 },
{ CCI_REG8(0x9316), 0x2c },
{ CCI_REG8(0x9317), 0x19 },
{ CCI_REG8(0xb046), 0x01 },
{ CCI_REG8(0xb048), 0x01 },
};
static const struct cci_reg_sequence mode_1928x1088_regs[] = {
{ IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 },
{ IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE },
{ IMX471_REG_X_ADD_STA, 8 },
{ IMX471_REG_Y_ADD_STA, 408 },
{ IMX471_REG_X_ADD_END, 4647 },
{ IMX471_REG_Y_ADD_END, 3051 },
{ IMX471_REG_X_EVEN_INC, 1 },
{ IMX471_REG_X_ODD_INC, 1 },
{ IMX471_REG_Y_EVEN_INC, 1 },
{ IMX471_REG_Y_ODD_INC, 1 },
{ IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE },
{ IMX471_REG_BINNING_TYPE, 0x22 },
{ IMX471_REG_BINNING_WEIGHTING, 0x08 },
{ IMX471_REG_DIG_CROP_X_OFFSET, 208 },
{ IMX471_REG_DIG_CROP_Y_OFFSET, 108 },
{ IMX471_REG_DIG_CROP_WIDTH, 1928 },
{ IMX471_REG_DIG_CROP_HEIGHT, 1088 },
{ IMX471_REG_X_OUTPUT_SIZE, 1928 },
{ IMX471_REG_Y_OUTPUT_SIZE, 1088 },
{ IMX471_REG_VTPXCK_DIV, 0x06 },
{ IMX471_REG_VTSYCK_DIV, 0x02 },
{ IMX471_REG_PREPLLCK_VT_DIV, 0x02 },
{ IMX471_REG_PLL_VT_MPY, 0x0079 },
{ IMX471_REG_OPSYCK_DIV, 0x01 },
{ CCI_REG8(0x030d), 0x02 },
{ CCI_REG8(0x030e), 0x00 },
{ CCI_REG8(0x030f), 0x53 },
{ IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL },
{ CCI_REG8(0x3f4c), 0x81 },
{ CCI_REG8(0x3f4d), 0x81 },
{ CCI_REG8(0x3f78), 0x01 },
{ CCI_REG8(0x3f79), 0x31 },
{ CCI_REG8(0x3ffe), 0x00 },
{ CCI_REG8(0x3fff), 0x8a },
{ CCI_REG8(0x5f0a), 0xb6 },
};
static const char * const imx471_test_pattern_menu[] = {
"Disabled",
"Solid Colour",
"Eight Vertical Colour Bars",
"Colour Bars With Fade to Grey",
"Pseudorandom Sequence (PN9)",
};
static const s64 link_freq_menu_items[] = {
IMX471_LINK_FREQ_DEFAULT,
};
/*
* The Bayer formats for the flipping.
* - no flip
* - h flip
* - v flip
* - h and v flips
*/
static const u32 imx471_hv_flips_bayer_order[] = {
MEDIA_BUS_FMT_SRGGB10_1X10,
MEDIA_BUS_FMT_SGRBG10_1X10,
MEDIA_BUS_FMT_SGBRG10_1X10,
MEDIA_BUS_FMT_SBGGR10_1X10,
};
static const struct imx471_mode imx471_modes[] = {
{
.width = 1928,
.height = 1088,
.fll_def = 1308,
.fll_min = 1308,
.llp = 2328,
.default_mode_regs = mode_1928x1088_regs,
.default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs),
},
};
static int imx471_get_regulators(struct device *dev, struct imx471 *sensor)
{
for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++)
sensor->supplies[i].supply = imx471_supply_name[i];
return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name),
sensor->supplies);
}
static int imx471_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx471 *sensor = container_of_const(ctrl->handler,
struct imx471,
ctrl_handler);
struct v4l2_subdev_state *state =
v4l2_subdev_get_locked_active_state(&sensor->sd);
const struct v4l2_mbus_framefmt *format =
v4l2_subdev_state_get_format(state, 0);
int ret;
if (ctrl->id == V4L2_CID_VBLANK) {
s64 exposure_max = format->height + ctrl->val -
IMX471_EXPOSURE_MARGIN;
ret = __v4l2_ctrl_modify_range(sensor->exposure,
sensor->exposure->minimum,
exposure_max,
sensor->exposure->step,
exposure_max);
if (ret)
return ret;
}
if (!pm_runtime_get_if_in_use(sensor->dev))
return 0;
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN,
ctrl->val, NULL);
break;
case V4L2_CID_DIGITAL_GAIN:
ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL,
ctrl->val, NULL);
break;
case V4L2_CID_EXPOSURE:
ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE,
ctrl->val, NULL);
break;
case V4L2_CID_VBLANK:
/* Update FLL that meets expected vertical blanking */
ret = cci_write(sensor->regmap, IMX471_REG_FLL,
format->height + ctrl->val, NULL);
break;
case V4L2_CID_TEST_PATTERN:
ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN,
ctrl->val, NULL);
break;
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION,
sensor->hflip->val | sensor->vflip->val << 1,
NULL);
break;
default:
ret = -EINVAL;
dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n",
ctrl->id, ctrl->val);
break;
}
pm_runtime_put(sensor->dev);
return ret;
}
static const struct v4l2_ctrl_ops imx471_ctrl_ops = {
.s_ctrl = imx471_set_ctrl,
};
static u32 imx471_get_format_code(struct imx471 *sensor)
{
unsigned int i;
i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0);
return imx471_hv_flips_bayer_order[i];
}
static int imx471_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct imx471 *sensor = to_imx471(sd);
if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4))
return -EINVAL;
code->code = imx471_get_format_code(sensor);
return 0;
}
static int imx471_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index >= ARRAY_SIZE(imx471_modes))
return -EINVAL;
fse->min_width = imx471_modes[fse->index].width;
fse->max_width = fse->min_width;
fse->min_height = imx471_modes[fse->index].height;
fse->max_height = fse->min_height;
return 0;
}
static void imx471_update_pad_format(struct imx471 *sensor,
const struct imx471_mode *mode,
struct v4l2_subdev_format *fmt)
{
fmt->format.code = imx471_get_format_code(sensor);
fmt->format.width = mode->width;
fmt->format.height = mode->height;
fmt->format.field = V4L2_FIELD_NONE;
}
static int imx471_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct imx471 *sensor = to_imx471(sd);
const struct imx471_mode *mode;
int h_blank, ret;
mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes),
width, height, fmt->format.width,
fmt->format.height);
imx471_update_pad_format(sensor, mode, fmt);
*v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
if (media_entity_is_streaming(&sensor->sd.entity))
return -EBUSY;
ret = __v4l2_ctrl_modify_range(sensor->vblank,
mode->fll_min - mode->height,
IMX471_FLL_MAX - mode->height,
1,
mode->fll_def - mode->height);
if (ret)
return ret;
h_blank = mode->llp - mode->width;
/*
* Currently hblank is not changeable.
* So FPS control is done only by vblank.
*/
return __v4l2_ctrl_modify_range(sensor->hblank, h_blank,
h_blank, 1, h_blank);
}
static int imx471_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:
sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad);
break;
case V4L2_SEL_TGT_NATIVE_SIZE:
sel->r.top = 0;
sel->r.left = 0;
sel->r.width = IMX471_NATIVE_WIDTH;
sel->r.height = IMX471_NATIVE_HEIGHT;
return 0;
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.top = IMX471_PIXEL_ARRAY_TOP;
sel->r.left = IMX471_PIXEL_ARRAY_LEFT;
sel->r.width = IMX471_PIXEL_ARRAY_WIDTH;
sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT;
return 0;
}
return -EINVAL;
}
static int imx471_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_TRY,
.format = {
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
.width = imx471_modes[0].width,
.height = imx471_modes[0].height,
},
};
return imx471_set_pad_format(sd, sd_state, &fmt);
}
static int imx471_identify_module(struct imx471 *sensor)
{
int ret;
u64 val;
ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL);
if (ret)
return dev_err_probe(sensor->dev, ret,
"failed to read chip id\n");
if (val != IMX471_CHIP_ID)
return dev_err_probe(sensor->dev, -EIO,
"chip id mismatch: %x!=%llx\n",
IMX471_CHIP_ID, val);
return 0;
}
static int imx471_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct imx471 *sensor = to_imx471(sd);
clk_disable_unprepare(sensor->img_clk);
gpiod_set_value_cansleep(sensor->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name),
sensor->supplies);
return 0;
}
static int imx471_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct imx471 *sensor = to_imx471(sd);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name),
sensor->supplies);
if (ret < 0) {
dev_err(dev, "failed to enable regulators: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(sensor->img_clk);
if (ret < 0) {
regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name),
sensor->supplies);
dev_err(dev, "failed to enable imaging clock: %d\n", ret);
return ret;
}
gpiod_set_value_cansleep(sensor->reset_gpio, 0);
usleep_range(10000, 15000);
return 0;
}
static int imx471_enable_stream(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct imx471 *sensor = to_imx471(sd);
const struct imx471_mode *mode;
struct v4l2_mbus_framefmt *fmt;
int ret;
ret = pm_runtime_resume_and_get(sensor->dev);
if (ret)
return ret;
ret = imx471_identify_module(sensor);
if (ret)
goto error_powerdown;
ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs,
ARRAY_SIZE(imx471_global_regs), NULL);
if (ret) {
dev_err(sensor->dev, "failed to set global settings: %d\n",
ret);
goto error_powerdown;
}
fmt = v4l2_subdev_state_get_format(state, 0);
mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes),
width, height, fmt->width, fmt->height);
ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs,
mode->default_mode_regs_length, NULL);
if (ret) {
dev_err(sensor->dev, "failed to set mode: %d\n", ret);
goto error_powerdown;
}
ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1,
NULL);
if (ret)
goto error_powerdown;
ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
if (ret)
goto error_powerdown;
ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT,
IMX471_MODE_STREAMING, NULL);
if (ret)
goto error_powerdown;
__v4l2_ctrl_grab(sensor->vflip, true);
__v4l2_ctrl_grab(sensor->hflip, true);
return ret;
error_powerdown:
pm_runtime_put(sensor->dev);
return ret;
}
static int imx471_disable_stream(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct imx471 *sensor = to_imx471(sd);
int ret;
ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT,
IMX471_MODE_STANDBY, NULL);
pm_runtime_put(sensor->dev);
if (ret)
dev_err(sensor->dev,
"failed to disable stream with return value: %d\n",
ret);
__v4l2_ctrl_grab(sensor->vflip, false);
__v4l2_ctrl_grab(sensor->hflip, false);
return 0;
}
static const struct v4l2_subdev_video_ops imx471_video_ops = {
.s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops imx471_pad_ops = {
.enum_mbus_code = imx471_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = imx471_set_pad_format,
.get_selection = imx471_get_selection,
.enum_frame_size = imx471_enum_frame_size,
.enable_streams = imx471_enable_stream,
.disable_streams = imx471_disable_stream,
};
static const struct v4l2_subdev_ops imx471_subdev_ops = {
.video = &imx471_video_ops,
.pad = &imx471_pad_ops,
};
static const struct v4l2_subdev_internal_ops imx471_internal_ops = {
.init_state = imx471_init_state,
};
static int imx471_init_controls(struct imx471 *sensor)
{
const struct imx471_mode *mode = &imx471_modes[0];
struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl_handler *ctrl_hdlr;
struct v4l2_ctrl *link_freq;
s64 exposure_max, hblank;
u64 pixel_rate;
int ret;
ret = v4l2_fwnode_device_parse(sensor->dev, &props);
if (ret) {
dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret);
return ret;
}
ctrl_hdlr = &sensor->ctrl_handler;
v4l2_ctrl_handler_init(ctrl_hdlr, 12);
v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props);
link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
&imx471_ctrl_ops,
V4L2_CID_LINK_FREQ,
ARRAY_SIZE(link_freq_menu_items) - 1,
0,
link_freq_menu_items);
/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10);
v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_PIXEL_RATE, pixel_rate,
pixel_rate, 1, pixel_rate);
sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr,
&imx471_ctrl_ops,
V4L2_CID_VBLANK,
mode->fll_min - mode->height,
IMX471_FLL_MAX - mode->height,
1,
mode->fll_def - mode->height);
hblank = mode->llp - mode->width;
sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_HBLANK, hblank, hblank,
1, hblank);
/* fll >= exposure time + adjust parameter (default value is 18) */
exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN;
sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_EXPOSURE,
IMX471_EXPOSURE_MIN, exposure_max,
IMX471_EXPOSURE_STEP,
IMX471_EXPOSURE_DEFAULT);
v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX,
IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT);
v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX,
IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT);
v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(imx471_test_pattern_menu) - 1,
0, 0, imx471_test_pattern_menu);
sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
if (ctrl_hdlr->error) {
dev_err(sensor->dev, "%s control init failed: %d\n",
__func__, ctrl_hdlr->error);
goto error;
}
link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
sensor->sd.ctrl_handler = ctrl_hdlr;
return 0;
error:
v4l2_ctrl_handler_free(ctrl_hdlr);
return ctrl_hdlr->error;
}
static int imx471_check_hwcfg(struct imx471 *sensor)
{
struct v4l2_fwnode_endpoint bus_cfg = {
.bus_type = V4L2_MBUS_CSI2_DPHY,
};
struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev);
unsigned long link_freq_bitmap;
struct clk *clk;
int ret;
clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(sensor->dev, PTR_ERR(clk),
"can't get clock frequency\n");
if (clk_get_rate(clk) != IMX471_EXT_CLK)
return dev_err_probe(sensor->dev, -EINVAL,
"external clock %lu is not supported\n",
clk_get_rate(clk));
ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
return dev_err_probe(sensor->dev, ret,
"parsing endpoint failed\n");
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
ret = dev_err_probe(sensor->dev, -EINVAL,
"number of CSI2 data lanes %u is not supported\n",
bus_cfg.bus.mipi_csi2.num_data_lanes);
goto done_endpoint_free;
}
ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_freq_menu_items,
ARRAY_SIZE(link_freq_menu_items),
&link_freq_bitmap);
done_endpoint_free:
v4l2_fwnode_endpoint_free(&bus_cfg);
return ret;
}
static int imx471_probe(struct i2c_client *client)
{
struct imx471 *sensor;
int ret;
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return dev_err_probe(&client->dev, -ENOMEM,
"failed to allocate memory\n");
sensor->dev = &client->dev;
ret = imx471_check_hwcfg(sensor);
if (ret)
return dev_err_probe(sensor->dev, ret,
"failed to check hwcfg: %d\n", ret);
ret = imx471_get_regulators(sensor->dev, sensor);
if (ret)
return dev_err_probe(sensor->dev, ret,
"failed to get regulators\n");
sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset_gpio))
return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio),
"failed to get reset gpio\n");
sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL);
if (IS_ERR(sensor->img_clk))
return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk),
"failed to get imaging clock\n");
v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops);
sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
if (IS_ERR(sensor->regmap))
return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap),
"failed to initialize CCI\n");
ret = imx471_power_on(sensor->dev);
if (ret)
return dev_err_probe(sensor->dev, ret,
"failed to power on\n");
ret = imx471_identify_module(sensor);
if (ret) {
dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n",
ret);
goto error_power_off;
}
ret = imx471_init_controls(sensor);
if (ret) {
dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n",
ret);
goto error_power_off;
}
sensor->sd.internal_ops = &imx471_internal_ops;
sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
if (ret) {
dev_err_probe(sensor->dev, ret,
"failed to init entity pads: %d\n", ret);
goto error_v4l2_ctrl_handler_free;
}
sensor->sd.state_lock = sensor->ctrl_handler.lock;
ret = v4l2_subdev_init_finalize(&sensor->sd);
if (ret < 0) {
dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n",
ret);
goto error_media_entity_pm;
}
pm_runtime_set_active(sensor->dev);
pm_runtime_enable(sensor->dev);
ret = v4l2_async_register_subdev_sensor(&sensor->sd);
if (ret < 0)
goto error_v4l2_subdev_cleanup;
pm_runtime_idle(sensor->dev);
return 0;
error_v4l2_subdev_cleanup:
pm_runtime_disable(sensor->dev);
pm_runtime_set_suspended(sensor->dev);
v4l2_subdev_cleanup(&sensor->sd);
error_media_entity_pm:
media_entity_cleanup(&sensor->sd.entity);
error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
error_power_off:
imx471_power_off(sensor->dev);
return ret;
}
static void imx471_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_async_unregister_subdev(sd);
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev)) {
imx471_power_off(&client->dev);
pm_runtime_set_suspended(&client->dev);
}
}
static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off,
imx471_power_on, NULL);
static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = {
{ "SONY471A" },
{ "TBE20A0" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids);
static struct i2c_driver imx471_i2c_driver = {
.driver = {
.name = "imx471",
.acpi_match_table = ACPI_PTR(imx471_acpi_ids),
.pm = pm_sleep_ptr(&imx471_pm_ops),
},
.probe = imx471_probe,
.remove = imx471_remove,
};
module_i2c_driver(imx471_i2c_driver);
MODULE_AUTHOR("Jimmy Su <jimmy.su@intel.com>");
MODULE_AUTHOR("Serin Yeh <serin.yeh@intel.com>");
MODULE_AUTHOR("Kate Hsuan <hpa@redhat.com>");
MODULE_DESCRIPTION("Sony imx471 sensor driver");
MODULE_LICENSE("GPL");

1447
drivers/media/i2c/imx678.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1062,7 +1062,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
priv->sd.state_lock = priv->ctrls.lock;
ret = v4l2_subdev_init_finalize(&priv->sd);
if (ret)
goto err_async;
goto err_entity;
ret = v4l2_async_register_subdev(&priv->sd);
if (ret < 0) {
@ -1074,6 +1074,8 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
err_subdev:
v4l2_subdev_cleanup(&priv->sd);
err_entity:
media_entity_cleanup(&priv->sd.entity);
err_async:
v4l2_ctrl_handler_free(&priv->ctrls);
max9286_v4l2_notifier_unregister(priv);
@ -1084,6 +1086,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
static void max9286_v4l2_unregister(struct max9286_priv *priv)
{
v4l2_subdev_cleanup(&priv->sd);
media_entity_cleanup(&priv->sd.entity);
v4l2_ctrl_handler_free(&priv->ctrls);
v4l2_async_unregister_subdev(&priv->sd);
max9286_v4l2_notifier_unregister(priv);

View File

@ -717,6 +717,7 @@ static int msp_probe(struct i2c_client *client)
if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
dev_dbg_lvl(&client->dev, 1, msp_debug,
"not an msp3400 (cannot read chip version)\n");
media_entity_cleanup(&sd->entity);
return -ENODEV;
}
@ -812,6 +813,7 @@ static int msp_probe(struct i2c_client *client)
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
media_entity_cleanup(&sd->entity);
return err;
}
@ -865,6 +867,7 @@ static void msp_remove(struct i2c_client *client)
msp_reset(client);
v4l2_ctrl_handler_free(&state->hdl);
media_entity_cleanup(&state->sd.entity);
}
/* ----------------------------------------------------------------------- */

View File

@ -452,11 +452,7 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
if (ret < 0)
return ret;
ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
if (ret < 0)
return ret;
return ret;
return mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
}
static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)

View File

@ -1079,11 +1079,12 @@ static int mt9t112_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
priv->clk = devm_v4l2_sensor_clk_get(&client->dev, "extclk");
if (PTR_ERR(priv->clk) == -ENOENT)
if (IS_ERR(priv->clk)) {
if (PTR_ERR(priv->clk) != -ENOENT)
return dev_err_probe(&client->dev, PTR_ERR(priv->clk),
"Unable to get clock \"extclk\"\n");
priv->clk = NULL;
else if (IS_ERR(priv->clk))
return dev_err_probe(&client->dev, PTR_ERR(priv->clk),
"Unable to get clock \"extclk\"\n");
}
priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby",
GPIOD_OUT_HIGH);

View File

@ -520,6 +520,7 @@ static int mt9v011_probe(struct i2c_client *c)
(version != MT9V011_REV_B_VERSION)) {
v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
version);
media_entity_cleanup(&sd->entity);
return -EINVAL;
}
@ -542,6 +543,7 @@ static int mt9v011_probe(struct i2c_client *c)
v4l2_err(sd, "control initialization error %d\n", ret);
v4l2_ctrl_handler_free(&core->ctrls);
media_entity_cleanup(&sd->entity);
return ret;
}
core->sd.ctrl_handler = &core->ctrls;
@ -577,6 +579,7 @@ static void mt9v011_remove(struct i2c_client *c)
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&core->ctrls);
media_entity_cleanup(&sd->entity);
}
/* ----------------------------------------------------------------------- */

View File

@ -820,18 +820,16 @@ static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10)
if (!ep)
return -ENXIO;
/* Optional indication of MIPI clock voltage unit */
if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
&clk_volt))
ov02a10->mipi_clock_voltage = clk_volt;
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
return ret;
/* Optional indication of MIPI clock voltage unit */
ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
&clk_volt);
if (!ret)
ov02a10->mipi_clock_voltage = clk_volt;
for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
if (link_freq_menu_items[i] ==

View File

@ -1241,41 +1241,42 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val,
return ret;
}
static const struct regmap_config regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.disable_locking = true,
};
static int ov2740_register_nvmem(struct i2c_client *client,
struct ov2740 *ov2740)
{
struct nvm_data *nvm;
struct regmap_config regmap_config = { };
struct nvmem_config nvmem_config = { };
struct regmap *regmap;
struct device *dev = ov2740->dev;
struct nvmem_config nvmem_config = {
.name = dev_name(dev),
.dev = dev,
.read_only = true,
.root_only = true,
.owner = THIS_MODULE,
.compat = true,
.base_dev = dev,
.reg_read = ov2740_nvmem_read,
.stride = 1,
.word_size = 1,
.size = CUSTOMER_USE_OTP_SIZE,
};
nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL);
if (!nvm)
return -ENOMEM;
regmap_config.val_bits = 8;
regmap_config.reg_bits = 16;
regmap_config.disable_locking = true;
regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
nvm->regmap = regmap;
nvmem_config.name = dev_name(dev);
nvmem_config.dev = dev;
nvmem_config.read_only = true;
nvmem_config.root_only = true;
nvmem_config.owner = THIS_MODULE;
nvmem_config.compat = true;
nvmem_config.base_dev = dev;
nvmem_config.reg_read = ov2740_nvmem_read;
nvmem_config.reg_write = NULL;
nvmem_config.priv = nvm;
nvmem_config.stride = 1;
nvmem_config.word_size = 1;
nvmem_config.size = CUSTOMER_USE_OTP_SIZE;
nvm->nvmem = devm_nvmem_register(dev, &nvmem_config);
if (IS_ERR(nvm->nvmem))

View File

@ -1116,18 +1116,14 @@ static void ov7740_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
mutex_destroy(&ov7740->mutex);
v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
media_entity_cleanup(&ov7740->subdev.entity);
v4l2_async_unregister_subdev(sd);
media_entity_cleanup(&ov7740->subdev.entity);
ov7740_free_controls(ov7740);
pm_runtime_get_sync(&client->dev);
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev))
ov7740_set_power(ov7740, 0);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
ov7740_set_power(ov7740, 0);
}
static int __maybe_unused ov7740_runtime_suspend(struct device *dev)

View File

@ -2608,7 +2608,7 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
struct ov8865_state *state = &sensor->state;
int ret;
int ret = 0;
if (enable) {
ret = pm_runtime_resume_and_get(sensor->dev);
@ -2617,18 +2617,32 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable)
}
mutex_lock(&sensor->mutex);
ret = ov8865_sw_standby(sensor, !enable);
/*
* The sensor may have been kept powered by something else (e.g. the
* VCM's runtime PM device link on IPU3 platforms), in which case
* runtime resume did not run and the hardware may still be
* configured for a previous mode. Always program the negotiated
* configuration on stream start.
*/
if (enable) {
ret = ov8865_sensor_init(sensor);
if (!ret)
ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
}
if (!ret)
ret = ov8865_sw_standby(sensor, !enable);
mutex_unlock(&sensor->mutex);
if (ret)
return ret;
state->streaming = !!enable;
if (!enable)
if (ret || !enable)
pm_runtime_put(sensor->dev);
return 0;
if (!ret)
state->streaming = enable;
return ret;
}
static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = {
@ -2915,15 +2929,15 @@ static int ov8865_resume(struct device *dev)
if (ret)
goto complete;
ret = ov8865_sensor_init(sensor);
if (ret)
goto error_power;
ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
if (ret)
goto error_power;
if (state->streaming) {
ret = ov8865_sensor_init(sensor);
if (ret)
goto error_power;
ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
if (ret)
goto error_power;
ret = ov8865_sw_standby(sensor, false);
if (ret)
goto error_power;

View File

@ -197,7 +197,6 @@ struct ov9282_mode {
* @noncontinuous_clock: Selection of CSI2 noncontinuous clock mode
* @cur_mode: Pointer to current selected sensor mode
* @code: Mbus code currently selected
* @mutex: Mutex for serializing sensor controls
*/
struct ov9282 {
struct device *dev;
@ -648,7 +647,8 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
ctrl->val ? OV9282_OUTPUT_ENABLE6_STROBE : 0, NULL);
break;
case V4L2_CID_FLASH_DURATION:
ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, ctrl->val, NULL);
ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN,
ov9282_us_to_flash_duration(ov9282, ctrl->val), NULL);
break;
default:
dev_err(ov9282->dev, "Invalid control %d", ctrl->id);

View File

@ -588,10 +588,12 @@ static int rdacm21_probe(struct i2c_client *client)
ret = v4l2_async_register_subdev(&dev->sd);
if (ret)
goto error_free_ctrls;
goto error_entity_cleanup;
return 0;
error_entity_cleanup:
media_entity_cleanup(&dev->sd.entity);
error_free_ctrls:
v4l2_ctrl_handler_free(&dev->ctrls);
error:
@ -606,6 +608,7 @@ static void rdacm21_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(&dev->sd);
v4l2_ctrl_handler_free(&dev->ctrls);
media_entity_cleanup(&dev->sd.entity);
i2c_unregister_device(dev->isp);
}

View File

@ -1867,6 +1867,7 @@ static int saa711x_probe(struct i2c_client *client)
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
media_entity_cleanup(&sd->entity);
return err;
}
v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
@ -1925,6 +1926,7 @@ static void saa711x_remove(struct i2c_client *client)
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
media_entity_cleanup(&sd->entity);
}
static const struct i2c_device_id saa711x_id[] = {

View File

@ -29,11 +29,7 @@
/* Register Map */
#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000)
#define VD55G1_MODEL_ID_VD55G1 0x53354731 /* Mono */
#define VD55G1_MODEL_ID_VD65G4 0x53354733 /* RGB */
#define VD55G1_REG_REVISION CCI_REG16_LE(0x0004)
#define VD55G1_REVISION_CCB 0x2020
#define VD55G1_REVISION_BAYER 0x3030
#define VD55G1_REG_COLOR_VERSION CCI_REG32_LE(0x0670)
#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012)
#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c)
@ -60,7 +56,10 @@
#define VD55G1_PATGEN_ENABLE BIT(0)
#define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501)
#define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502)
#define VD55G1_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0504)
#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0504)
#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0506)
#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0508)
#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x050a)
#define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8)
#define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea)
#define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec)
@ -114,9 +113,8 @@
#define VD55G1_WIDTH 804
#define VD55G1_HEIGHT 704
#define VD55G1_MODE_DEF 0
#define VD55G1_MODE_IDX_DEF 0
#define VD55G1_NB_GPIOS 4
#define VD55G1_MBUS_CODE_DEF 0
#define VD55G1_DGAIN_DEF 256
#define VD55G1_AGAIN_DEF 19
#define VD55G1_EXPO_MAX_TERM 64
@ -135,8 +133,39 @@
#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
#define VD55G1_MODEL_ID_NAME(id) \
((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4")
enum vd55g1_model_id {
VD55G1_MODEL_ID_2 = 0x53354731,
VD55G1_MODEL_ID_3 = 0x53354733,
};
enum vd55g1_color_version {
VD55G1_COLOR_VERSION_MONO,
VD55G1_COLOR_VERSION_BAYER,
};
struct vd55g1_version {
char *name;
enum vd55g1_model_id id;
enum vd55g1_color_version color;
};
static const struct vd55g1_version vd55g1_versions[] = {
{
.name = "vd55g1",
.id = VD55G1_MODEL_ID_2,
.color = VD55G1_COLOR_VERSION_MONO,
},
{
.name = "vd55g4",
.id = VD55G1_MODEL_ID_3,
.color = VD55G1_COLOR_VERSION_MONO,
},
{
.name = "vd65g4",
.id = VD55G1_MODEL_ID_3,
.color = VD55G1_COLOR_VERSION_BAYER,
},
};
static const u8 vd55g1_patch_array[] = {
0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
@ -532,7 +561,7 @@ struct vd55g1_vblank_limits {
struct vd55g1 {
struct device *dev;
unsigned int id;
const struct vd55g1_version *version;
struct v4l2_subdev sd;
struct media_pad pad;
struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)];
@ -623,20 +652,28 @@ static unsigned int vd55g1_get_fmt_data_type(u32 code)
static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code)
{
u32 fallback_code;
unsigned int i, j;
if (sensor->id == VD55G1_MODEL_ID_VD55G1)
return code;
for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) {
for (j = 0; j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]); j++) {
if (vd55g1_mbus_formats_bayer[i][j] == code)
goto adapt_bayer_pattern;
if (sensor->version->color == VD55G1_COLOR_VERSION_MONO) {
fallback_code = vd55g1_mbus_formats_mono[0];
for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_mono); i++)
if (vd55g1_mbus_formats_mono[i] == code)
return code;
} else {
fallback_code = vd55g1_mbus_formats_bayer[0][0];
for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) {
for (j = 0;
j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]);
j++) {
if (vd55g1_mbus_formats_bayer[i][j] == code)
goto adapt_bayer_pattern;
}
}
}
dev_warn(sensor->dev, "Unsupported mbus format\n");
dev_warn(sensor->dev, "Unsupported mbus format: 0x%x\n", code);
return code;
return fallback_code;
adapt_bayer_pattern:
j = 0;
@ -649,12 +686,6 @@ static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code)
return vd55g1_mbus_formats_bayer[i][j];
}
static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor,
struct v4l2_mbus_framefmt *format)
{
return sensor->mipi_rate / vd55g1_get_fmt_bpp(format->code);
}
static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor,
struct v4l2_mbus_framefmt *format,
struct v4l2_rect *crop)
@ -850,9 +881,16 @@ static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto)
vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN,
sensor->again_ctrl->val, &ret);
if (!is_auto && sensor->dgain_ctrl->is_new)
vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN,
if (!is_auto && sensor->dgain_ctrl->is_new) {
vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0,
sensor->dgain_ctrl->val, &ret);
vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1,
sensor->dgain_ctrl->val, &ret);
vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2,
sensor->dgain_ctrl->val, &ret);
vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3,
sensor->dgain_ctrl->val, &ret);
}
return ret;
}
@ -1173,8 +1211,8 @@ static int vd55g1_patch(struct vd55g1 *sensor)
u64 patch;
int ret = 0;
/* vd55g1 needs a patch while vd65g4 does not */
if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
/* Version 2 needs a patch while version 3 does not */
if (sensor->version->id == VD55G1_MODEL_ID_2) {
vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
sizeof(vd55g1_patch_array),
vd55g1_patch_array, &ret);
@ -1246,7 +1284,7 @@ static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd,
struct vd55g1 *sensor = to_vd55g1(sd);
u32 base_code;
if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER) {
if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono))
return -EINVAL;
base_code = vd55g1_mbus_formats_mono[code->index];
@ -1285,12 +1323,6 @@ static int vd55g1_new_format_change_controls(struct vd55g1 *sensor,
if (ret)
return ret;
/* Update pixel rate to reflect new bpp */
ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl,
vd55g1_get_pixel_rate(sensor, format));
if (ret)
return ret;
/* Update hblank according to new width */
hblank = vd55g1_get_hblank_min(sensor, format, crop);
ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
@ -1347,6 +1379,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
{
struct vd55g1 *sensor = to_vd55g1(sd);
struct v4l2_subdev_format fmt = { 0 };
int code;
struct v4l2_subdev_route routes[] = {
{ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE }
};
@ -1361,9 +1394,13 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
if (ret)
return ret;
vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF),
&fmt.format);
if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER)
code = vd55g1_mbus_formats_mono[0];
else
code = vd55g1_mbus_formats_bayer[0][0];
fmt.format.code = vd55g1_get_fmt_code(sensor, code);
fmt.format.width = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].width;
fmt.format.height = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].height;
return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
}
@ -1549,7 +1586,6 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor)
v4l2_subdev_state_get_crop(state, 0);
struct v4l2_mbus_framefmt *format =
v4l2_subdev_state_get_format(state, 0);
s32 pixel_rate = vd55g1_get_pixel_rate(sensor, format);
int ret;
v4l2_ctrl_handler_init(hdl, 16);
@ -1591,7 +1627,7 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor)
sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_PIXEL_RATE, 1,
INT_MAX, 1,
pixel_rate);
sensor->pixel_clock);
if (sensor->pixel_rate_ctrl)
sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK,
@ -1644,38 +1680,46 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor)
return ret;
}
static const struct vd55g1_version *
vd55g1_get_version(enum vd55g1_model_id id,
enum vd55g1_color_version color)
{
for (unsigned int i = 0; i < ARRAY_SIZE(vd55g1_versions); i++) {
if (vd55g1_versions[i].id == id &&
vd55g1_versions[i].color == color)
return &vd55g1_versions[i];
}
return NULL;
}
static int vd55g1_detect(struct vd55g1 *sensor)
{
unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev);
u64 rev, id;
int ret;
const struct vd55g1_version *dt_version =
device_get_match_data(sensor->dev);
const struct vd55g1_version *version;
u64 color, id;
int ret = 0;
ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL);
vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, &ret);
vd55g1_read(sensor, VD55G1_REG_COLOR_VERSION, &color, &ret);
if (ret)
return ret;
if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) {
dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n",
(u32)id);
version = vd55g1_get_version(id, color);
if (!version) {
dev_warn(sensor->dev, "Unsupported sensor version, expected %s\n",
dt_version->name);
return -ENODEV;
}
if (id != dt_id) {
dev_err(sensor->dev, "Probed sensor %s and device tree definition (%s) mismatch",
VD55G1_MODEL_ID_NAME(id), VD55G1_MODEL_ID_NAME(dt_id));
if (version->id != dt_version->id ||
version->color != dt_version->color) {
dev_err(sensor->dev, "Probed sensor version %s and device tree definition %s mismatch",
version->name, dt_version->name);
return -ENODEV;
}
sensor->id = id;
ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL);
if (ret)
return ret;
if ((id == VD55G1_MODEL_ID_VD55G1 && rev != VD55G1_REVISION_CCB) &&
(id == VD55G1_MODEL_ID_VD65G4 && rev != VD55G1_REVISION_BAYER)) {
dev_err(sensor->dev, "Unsupported sensor revision 0x%x for sensor %s\n",
(u16)rev, VD55G1_MODEL_ID_NAME(id));
return -ENODEV;
}
sensor->version = version;
return 0;
}
@ -2033,8 +2077,9 @@ static void vd55g1_remove(struct i2c_client *client)
}
static const struct of_device_id vd55g1_dt_ids[] = {
{ .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 },
{ .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 },
{ .compatible = "st,vd55g1", .data = (void *)&vd55g1_versions[0] },
{ .compatible = "st,vd55g4", .data = (void *)&vd55g1_versions[1] },
{ .compatible = "st,vd65g4", .data = (void *)&vd55g1_versions[2] },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vd55g1_dt_ids);

View File

@ -522,8 +522,12 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
if (!ret)
return 0;
if (ret) {
data->kthread_vid_cap = NULL;
goto error_rpm_put;
}
return 0;
error_rpm_put:
pm_runtime_put_autosuspend(dev);

View File

@ -3563,12 +3563,12 @@ static int __maybe_unused bttv_resume(struct device *dev)
}
static const struct pci_device_id bttv_pci_tbl[] = {
{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
{0,}
{ PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848) },
{ PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849) },
{ PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878) },
{ PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879) },
{ PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879) },
{ }
};
MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);

View File

@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_stream *s)
err_exit_free:
if (sc != NULL)
snd_card_free(sc);
kfree(cobsc);
err_exit:
return ret;
}

View File

@ -26,9 +26,8 @@
/* add your revision and whatnot here */
static const struct pci_device_id cobalt_pci_tbl[] = {
{PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_COBALT,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_COBALT) },
{ }
};
MODULE_DEVICE_TABLE(pci, cobalt_pci_tbl);

View File

@ -40,9 +40,8 @@ EXPORT_SYMBOL(cx18_ext_init);
/* add your revision and whatnot here */
static const struct pci_device_id cx18_pci_tbl[] = {
{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
{ PCI_VDEVICE(CX, PCI_DEVICE_ID_CX23418) },
{ }
};
MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);

View File

@ -135,7 +135,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
: VBI_HBLANK_SAMPLES_50HZ;
/* find the first valid line */
for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
for (i = hdr_size, buf += hdr_size; i + 3 < size; i++, buf++) {
if (buf[0] == 0xff && !buf[1] && !buf[2] &&
(buf[3] == sliced_vbi_eav_rp[0] ||
buf[3] == sliced_vbi_eav_rp[1]))

View File

@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport *port)
if (NULL == state->ca.data)
return;
cancel_work_sync(&state->work);
dvb_ca_en50221_release(&state->ca);
kfree(state);
}

View File

@ -2279,16 +2279,10 @@ static int __maybe_unused cx23885_resume(struct device *dev_d)
static const struct pci_device_id cx23885_pci_tbl[] = {
{
/* CX23885 */
.vendor = 0x14f1,
.device = 0x8852,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_DEVICE(0x14f1, 0x8852),
}, {
/* CX23887 Rev 2 */
.vendor = 0x14f1,
.device = 0x8880,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_DEVICE(0x14f1, 0x8880),
}, {
/* --- end of list --- */
}

View File

@ -681,8 +681,8 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device,
*/
static const struct pci_device_id __maybe_unused cx25821_audio_pci_tbl[] = {
{0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
{ PCI_DEVICE(0x14f1, 0x0920) },
{ }
};
MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl);

View File

@ -1347,16 +1347,10 @@ static void cx25821_finidev(struct pci_dev *pci_dev)
static const struct pci_device_id cx25821_pci_tbl[] = {
{
/* CX25821 Athena */
.vendor = 0x14f1,
.device = 0x8210,
.subvendor = 0x14f1,
.subdevice = 0x0920,
PCI_DEVICE_SUB(0x14f1, 0x8210, 0x14f1, 0x0920),
}, {
/* CX25821 No Brand */
.vendor = 0x14f1,
.device = 0x8210,
.subvendor = 0x0000,
.subdevice = 0x0000,
PCI_DEVICE_SUB(0x14f1, 0x8210, 0x0000, 0x0000),
}, {
/* --- end of list --- */
}

View File

@ -809,9 +809,9 @@ static const struct snd_kcontrol_new snd_cx88_alc_switch = {
*/
static const struct pci_device_id cx88_audio_pci_tbl[] = {
{0x14f1, 0x8801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0x14f1, 0x8811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0, }
{ PCI_DEVICE(0x14f1, 0x8801) },
{ PCI_DEVICE(0x14f1, 0x8811) },
{ }
};
MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);

View File

@ -640,6 +640,7 @@ int cx8802_register_driver(struct cx8802_driver *drv)
list_add_tail(&driver->drvlist, &dev->drvlist);
} else {
pr_err("cx8802 probe failed, err = %d\n", err);
kfree(driver);
}
mutex_unlock(&drv->core->lock);
}
@ -787,10 +788,7 @@ static void cx8802_remove(struct pci_dev *pci_dev)
static const struct pci_device_id cx8802_pci_tbl[] = {
{
.vendor = 0x14f1,
.device = 0x8802,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_DEVICE(0x14f1, 0x8802),
}, {
/* --- end of list --- */
}

View File

@ -1610,10 +1610,7 @@ static int __maybe_unused cx8800_resume(struct device *dev_d)
static const struct pci_device_id cx8800_pci_tbl[] = {
{
.vendor = 0x14f1,
.device = 0x8800,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_DEVICE(0x14f1, 0x8800),
}, {
/* --- end of list --- */
}

View File

@ -269,7 +269,7 @@ static const struct pci_device_id ddb_id_table[] = {
DDB_DEVICE_ANY(0x0323),
DDB_DEVICE_ANY(0x0328),
DDB_DEVICE_ANY(0x0329),
{0}
{ }
};
MODULE_DEVICE_TABLE(pci, ddb_id_table);

View File

@ -14,7 +14,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dvb/ca.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>

View File

@ -1209,15 +1209,9 @@ static void dm1105_remove(struct pci_dev *pdev)
static const struct pci_device_id dm1105_id_table[] = {
{
.vendor = PCI_VENDOR_ID_TRIGEM,
.device = PCI_DEVICE_ID_DM1105,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_VDEVICE(TRIGEM, PCI_DEVICE_ID_DM1105),
}, {
.vendor = PCI_VENDOR_ID_AXESS,
.device = PCI_DEVICE_ID_DM05,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_VDEVICE(AXESS, PCI_DEVICE_ID_DM05),
}, {
/* empty */
},

View File

@ -577,8 +577,8 @@ static void dt3155_remove(struct pci_dev *pdev)
}
static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) },
{ 0, /* zero marks the end */ },
{ PCI_VDEVICE(INTEL, DT3155_DEVICE_ID) },
{ /* zero marks the end */ },
};
MODULE_DEVICE_TABLE(pci, pci_ids);

View File

@ -17,14 +17,10 @@ int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfp
int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i);
int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i);
int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i);
int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a);
int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a);
int hws_vidioc_dv_timings_cap(struct file *file, void *fh,
struct v4l2_dv_timings_cap *cap);
int hws_vidioc_s_dv_timings(struct file *file, void *fh,
struct v4l2_dv_timings *timings);
int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
int hws_vidioc_g_dv_timings(struct file *file, void *fh,
struct v4l2_dv_timings *timings);
int hws_vidioc_enum_dv_timings(struct file *file, void *fh,

View File

@ -49,6 +49,8 @@
* Please keep the list sorted by ACPI HID.
*/
static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Himax HM1092 */
IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000),
/* Himax HM11B1 */
IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000),
/* Himax HM2170 */
@ -97,6 +99,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
/* Sony IMX471 */
IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
/* Sony IMX471 (found on Lenovo X1 Carbon G14) */
IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000),
/* Toshiba T4KA3 */
IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000),
};
@ -134,6 +138,60 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
},
.driver_data = "OVTI02C1",
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 (Dell 16 Premium) DA16250"),
},
.driver_data = "OVTI02C1",
},
/*
* The first four characters of DMI_BOARD_NAME identify the Lenovo
* machine type/model. For example, a DMI_BOARD_NAME starting with
* "21Q6" indicates a ThinkPad X9-15.
*
* Reference: https://psref.lenovo.com/
*/
{
/* Lenovo X9-14 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_BOARD_NAME, "21QA"),
},
.driver_data = "SONY471A",
},
{
/* Lenovo X9-14 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_BOARD_NAME, "21QB"),
},
.driver_data = "SONY471A",
},
{
/* Lenovo X9-15 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_BOARD_NAME, "21Q6"),
},
.driver_data = "SONY471A",
},
{
/* Lenovo X9-15 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_BOARD_NAME, "21Q7"),
},
.driver_data = "SONY471A",
},
{
/* Samsung Galaxy Book5 Pro 360 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960QHA"),
},
.driver_data = "OVTI02E1",
},
{} /* Terminating entry */
};
@ -171,6 +229,7 @@ static const struct acpi_device_id ivsc_acpi_ids[] = {
{ "INTC10DE" }, /* LNL */
{ "INTC10E0" }, /* ARL */
{ "INTC10E1" }, /* PTL */
{ "INTC10FA" }, /* NVL */
};
static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
@ -180,8 +239,8 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev
for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) {
const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i];
struct acpi_device *consumer, *ivsc_adev;
acpi_handle handle = acpi_device_handle(ACPI_PTR(adev));
for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1)
/* camera sensor depends on IVSC in DSDT if exist */
for_each_acpi_consumer_dev(ivsc_adev, consumer)
@ -298,9 +357,11 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev,
{
const struct dmi_system_id *dmi_id;
dmi_id = dmi_first_match(upside_down_sensor_dmi_ids);
if (dmi_id && acpi_dev_hid_match(adev, dmi_id->driver_data))
return 180;
/* A machine may have one entry per sensor, so check all matches. */
for (dmi_id = dmi_first_match(upside_down_sensor_dmi_ids); dmi_id;
dmi_id = dmi_first_match(dmi_id + 1))
if (acpi_dev_hid_match(adev, dmi_id->driver_data))
return 180;
switch (ssdb->degree) {
case IPU_SENSOR_ROTATION_NORMAL:

View File

@ -1961,7 +1961,7 @@ static const struct dev_pm_ops cio2_pm_ops = {
};
static const struct pci_device_id cio2_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) },
{ PCI_VDEVICE(INTEL, CIO2_PCI_ID) },
{ }
};

View File

@ -761,6 +761,7 @@ static int isys_notifier_init(struct ipu6_isys *isys)
err_parse:
fwnode_handle_put(ep);
v4l2_async_nf_cleanup(&isys->notifier);
return ret;
}

View File

@ -400,7 +400,6 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
&ipdata->hw_variant);
if (IS_ERR(isys_adev->mmu)) {
put_device(&isys_adev->auxdev.dev);
kfree(pdata);
return dev_err_cast_probe(dev, isys_adev->mmu,
"ipu6_mmu_init(isys_adev->mmu) failed\n");
}
@ -408,10 +407,8 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
isys_adev->mmu->dev = &isys_adev->auxdev.dev;
ret = ipu6_bus_add_device(isys_adev);
if (ret) {
kfree(pdata);
if (ret)
return ERR_PTR(ret);
}
return isys_adev;
}
@ -444,7 +441,6 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
&ipdata->hw_variant);
if (IS_ERR(psys_adev->mmu)) {
put_device(&psys_adev->auxdev.dev);
kfree(pdata);
return dev_err_cast_probe(&pdev->dev, psys_adev->mmu,
"ipu6_mmu_init(psys_adev->mmu) failed\n");
}
@ -452,10 +448,8 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
psys_adev->mmu->dev = &psys_adev->auxdev.dev;
ret = ipu6_bus_add_device(psys_adev);
if (ret) {
kfree(pdata);
if (ret)
return ERR_PTR(ret);
}
return psys_adev;
}

View File

@ -60,11 +60,9 @@ EXPORT_SYMBOL(ivtv_ext_init);
/* add your revision and whatnot here */
static const struct pci_device_id ivtv_pci_tbl[] = {
{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
{ PCI_VDEVICE(ICOMP, PCI_DEVICE_ID_IVTV15) },
{ PCI_VDEVICE(ICOMP, PCI_DEVICE_ID_IVTV16) },
{ }
};
MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);

View File

@ -330,7 +330,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8
unsigned lines = 0;
/* find the first valid line */
for (i = 0; i < size; i++, buf++) {
for (i = 0; i + 3 < size; i++, buf++) {
if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
break;
}

View File

@ -58,11 +58,9 @@
#define TERRATEC 0x153b
#define MAKE_ENTRY(__subven, __subdev, __configptr, __rc) { \
.vendor = TWINHAN_TECHNOLOGIES, \
.device = MANTIS, \
.subvendor = (__subven), \
.subdevice = (__subdev), \
.driver_data = (unsigned long) \
PCI_DEVICE_SUB(TWINHAN_TECHNOLOGIES, MANTIS, \
(__subven), (__subdev)), \
.driver_data = (unsigned long) \
&(struct mantis_pci_drvdata){__configptr, __rc} \
}

View File

@ -694,9 +694,9 @@ static void mgb4_remove(struct pci_dev *pdev)
}
static const struct pci_device_id mgb4_pci_ids[] = {
{ PCI_DEVICE(DIGITEQ_VID, T100_DID), },
{ PCI_DEVICE(DIGITEQ_VID, T200_DID), },
{ 0, }
{ PCI_DEVICE(DIGITEQ_VID, T100_DID) },
{ PCI_DEVICE(DIGITEQ_VID, T200_DID) },
{ }
};
MODULE_DEVICE_TABLE(pci, mgb4_pci_ids);

View File

@ -1014,7 +1014,7 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev)
static const struct pci_device_id netup_unidvb_pci_tbl[] = {
{ PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */
{ PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */
{ 0, }
{ }
};
MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl);

View File

@ -1155,8 +1155,7 @@ static const struct ngene_info ngene_info_terratec = {
/****************************************************************************/
#define NGENE_ID(_subvend, _subdev, _driverdata) { \
.vendor = NGENE_VID, .device = NGENE_PID, \
.subvendor = _subvend, .subdevice = _subdev, \
PCI_DEVICE_SUB(NGENE_VID, NGENE_PID, (_subvend), (_subdev)), \
.driver_data = (unsigned long) &_driverdata }
/****************************************************************************/
@ -1173,7 +1172,7 @@ static const struct pci_device_id ngene_id_tbl[] = {
NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
NGENE_ID(0x1461, 0x062e, ngene_info_m780),
NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
{0}
{ }
};
MODULE_DEVICE_TABLE(pci, ngene_id_tbl);

View File

@ -762,10 +762,7 @@ static void pluto2_remove(struct pci_dev *pdev)
static const struct pci_device_id pluto2_id_table[] = {
{
.vendor = PCI_VENDOR_ID_SCM,
.device = PCI_DEVICE_ID_PLUTO2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
PCI_VDEVICE(SCM, PCI_DEVICE_ID_PLUTO2),
}, {
/* empty */
},

File diff suppressed because it is too large Load Diff

View File

@ -361,22 +361,14 @@ static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
static const struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2401,
.driver_data = (unsigned long) &hexium_gemini_4bnc,
},
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2401),
.driver_data = (unsigned long)&hexium_gemini_4bnc,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2402,
.driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
},
{
.vendor = 0,
}
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2402),
.driver_data = (unsigned long)&hexium_gemini_dual_4bnc,
},
{ }
};
MODULE_DEVICE_TABLE(pci, pci_tbl);

View File

@ -425,29 +425,16 @@ static struct saa7146_pci_extension_data hexium_orion_4bnc = {
static const struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x0000,
.subdevice = 0x0000,
.driver_data = (unsigned long) &hexium_hv_pci6,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x0101,
.driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
},
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x17c8,
.subdevice = 0x2101,
.driver_data = (unsigned long) &hexium_orion_4bnc,
},
{
.vendor = 0,
}
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x0000, 0x0000),
.driver_data = (unsigned long)&hexium_hv_pci6,
}, {
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x0101),
.driver_data = (unsigned long)&hexium_orion_1svhs_3bnc,
}, {
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2101),
.driver_data = (unsigned long)&hexium_orion_4bnc,
},
{ }
};
MODULE_DEVICE_TABLE(pci, pci_tbl);

View File

@ -821,14 +821,10 @@ static struct saa7146_pci_extension_data mxb = {
static const struct pci_device_id pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7146,
.subvendor = 0x0000,
.subdevice = 0x0000,
PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x0000, 0x0000),
.driver_data = (unsigned long)&mxb,
}, {
.vendor = 0,
}
},
{ }
};
MODULE_DEVICE_TABLE(pci, pci_tbl);

Some files were not shown because too many files have changed in this diff Show More