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

Pull media updates from Mauro Carvalho Chehab:

 - new CSI tegra support, covering Tegra20 and Tegra30

 - new camera sensor drivers: T4ka3 and ov2732

 - m88ds3103: add 3103c chip support

 - uvcvideo: add support for Intel RealSense D436/D555 and P010 pixel format

 - synopsys csi2rx: add i.MX93 support

 - imx8-isi: add i.MX95 support

 - imx8mq-mipi-csi2: add i.MX8ULP support

 - dw100: add V4L2 requests support

 - support for DTV devices from Hauppauge got some improvements

 - media staging: dropped starfive-camss driver

 - media docs: document multi-committers model and improve maint profile

 - media core:
    - add v4l2_subdev_get_frame_desc_passthrough() helper
    - improve error handling in fwnode parsing

 - lots of driver fixes, cleanups and improvements

* tag 'media/v7.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (251 commits)
  Revert "media: cx231xx: add USB ID 2040:8360 for Hauppauge WinTV-HVR-935"
  media: synopsys: csi2rx: add i.MX93 support
  media: dt-bindings: add NXP i.MX93 compatible string
  media: synopsys: csi2rx: Use enum and u32 array for register offsets
  media: synopsys: csi2rx: implement .get_frame_desc() callback
  media: synopsys: csi2rx: only check errors from devm_clk_bulk_get_all()
  media: synopsys: csi2rx: use devm_reset_control_get_optional_exclusive()
  media: i2c: imx283: add support for non-continuous MIPI clock mode
  media: i2c: ov08d10: add support for 24 MHz input clock
  media: i2c: ov08d10: add support for reset and power management
  media: i2c: ov08d10: add support for binding via device tree
  dt-bindings: media: i2c: document Omnivision OV08D10 CMOS image sensor
  media: i2c: ov08d10: add missing newline to prints
  media: i2c: ov08d10: fix some typos in comments
  media: i2c: ov08d10: remove duplicate register write
  media: i2c: ov08d10: fix image vertical start setting
  media: i2c: ov08d10: fix runtime PM handling in probe
  staging: media: ipu7: Update TODO
  media: Add t4ka3 camera sensor driver
  media: i2c: Add ov2732 image sensor driver
  ...
This commit is contained in:
Linus Torvalds
2026-04-15 08:32:10 -07:00
266 changed files with 10060 additions and 7144 deletions

View File

@@ -317,6 +317,7 @@ Hans de Goede <hansg@kernel.org> <hdegoede@redhat.com>
Hans Verkuil <hverkuil@kernel.org> <hverkuil@xs4all.nl>
Hans Verkuil <hverkuil@kernel.org> <hverkuil-cisco@xs4all.nl>
Hans Verkuil <hverkuil@kernel.org> <hansverk@cisco.com>
Hans Verkuil <hverkuil@kernel.org> <hans.verkuil@cisco.com>
Hao Ge <hao.ge@linux.dev> <gehao@kylinos.cn>
Harry Yoo <harry.yoo@oracle.com> <42.hyeyoo@gmail.com>
Harry Yoo <harry@kernel.org> <harry.yoo@oracle.com>

View File

@@ -74,6 +74,7 @@ Common FPDL3/GMSL input parameters
| 0 - OLDI/JEIDA
| 1 - SPWG/VESA (default)
| 2 - ZDML
**link_status** (R):
Video link status. If the link is locked, chips are properly connected and
@@ -240,6 +241,13 @@ Common FPDL3/GMSL output parameters
*Note: This parameter can not be changed while the output v4l2 device is
open.*
**color_mapping** (RW):
Mapping of the outgoing bits in the signal to the colour bits of the pixels.
| 0 - OLDI/JEIDA
| 1 - SPWG/VESA (default)
| 2 - ZDML
**frame_rate** (RW):
Output video signal frame rate limit in frames per second. Due to
the limited output pixel clock steps, the card can not always generate

View File

@@ -1,72 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
================================
Starfive Camera Subsystem driver
================================
Introduction
------------
This file documents the driver for the Starfive Camera Subsystem found on
Starfive JH7110 SoC. The driver is located under drivers/staging/media/starfive/
camss.
The driver implements V4L2, Media controller and v4l2_subdev interfaces. Camera
sensor using V4L2 subdev interface in the kernel is supported.
The driver has been successfully used on the Gstreamer 1.18.5 with v4l2src
plugin.
Starfive Camera Subsystem hardware
----------------------------------
The Starfive Camera Subsystem hardware consists of::
|\ +---------------+ +-----------+
+----------+ | \ | | | |
| | | | | | | |
| MIPI |----->| |----->| ISP |----->| |
| | | | | | | |
+----------+ | | | | | Memory |
|MUX| +---------------+ | Interface |
+----------+ | | | |
| | | |---------------------------->| |
| Parallel |----->| | | |
| | | | | |
+----------+ | / | |
|/ +-----------+
- MIPI: The MIPI interface, receiving data from a MIPI CSI-2 camera sensor.
- Parallel: The parallel interface, receiving data from a parallel sensor.
- ISP: The ISP, processing raw Bayer data from an image sensor and producing
YUV frames.
Topology
--------
The media controller pipeline graph is as follows:
.. _starfive_camss_graph:
.. kernel-figure:: starfive_camss_graph.dot
:alt: starfive_camss_graph.dot
:align: center
The driver has 2 video devices:
- capture_raw: The capture device, capturing image data directly from a sensor.
- capture_yuv: The capture device, capturing YUV frame data processed by the
ISP module
The driver has 3 subdevices:
- stf_isp: is responsible for all the isp operations, outputs YUV frames.
- cdns_csi2rx: a CSI-2 bridge supporting up to 4 CSI lanes in input, and 4
different pixel streams in output.
- imx219: an image sensor, image data is sent through MIPI CSI-2.

View File

@@ -1,12 +0,0 @@
digraph board {
rankdir=TB
n00000001 [label="{{<port0> 0} | stf_isp\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
n00000001:port1 -> n00000008 [style=dashed]
n00000004 [label="capture_raw\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
n00000008 [label="capture_yuv\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
n0000000e [label="{{<port0> 0} | cdns_csi2rx.19800000.csi-bridge\n | {<port1> 1 | <port2> 2 | <port3> 3 | <port4> 4}}", shape=Mrecord, style=filled, fillcolor=green]
n0000000e:port1 -> n00000001:port0 [style=dashed]
n0000000e:port1 -> n00000004 [style=dashed]
n00000018 [label="{{} | imx219 6-0010\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
n00000018:port0 -> n0000000e:port0 [style=bold]
}

View File

@@ -33,7 +33,6 @@ Video4Linux (V4L) driver-specific documentation
si470x
si4713
si476x
starfive_camss
vimc
visl
vivid

View File

@@ -8,7 +8,7 @@ title: Allied Vision Alvium Camera
maintainers:
- Tommaso Merciai <tomm.merciai@gmail.com>
- Martin Hecht <martin.hecht@avnet.eu>
- Martin Hecht <mhecht73@gmail.com>
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#

View File

@@ -17,7 +17,9 @@ description: |-
properties:
compatible:
const: onnn,mt9m114
enum:
- onnn,mt9m114
- aptina,mi1040
reg:
description: I2C device address

View File

@@ -0,0 +1,101 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/i2c/ovti,ov08d10.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Omnivision OV08D10 1/4-Inch 8MP CMOS color image sensor
maintainers:
- Matthias Fend <matthias.fend@emfend.at>
description:
The Omnivision OV08D10 is a 1/4-Inch 8MP CMOS color image sensor with an
active array size of 3280 x 2464. It is programmable through I2C
interface. Image data is transmitted via MIPI CSI-2 using 2 lanes.
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
const: ovti,ov08d10
reg:
maxItems: 1
clocks:
description: MCLK input clock (6 - 27 MHz)
maxItems: 1
reset-gpios:
description: Active low XSHUTDN pin
maxItems: 1
dovdd-supply:
description: IO power supply (1.8V)
avdd-supply:
description: Analog power supply (2.8V)
dvdd-supply:
description: Core power supply (1.2V)
port:
$ref: /schemas/graph.yaml#/$defs/port-base
additionalProperties: false
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
required:
- data-lanes
- link-frequencies
required:
- endpoint
required:
- compatible
- reg
- clocks
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
sensor@36 {
compatible = "ovti,ov08d10";
reg = <0x36>;
clocks = <&ov08d10_clk>;
dovdd-supply = <&ov08d10_vdddo_1v8>;
avdd-supply = <&ov08d10_vdda_2v8>;
dvdd-supply = <&ov08d10_vddd_1v2>;
orientation = <2>;
rotation = <0>;
reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
port {
ov08d10_output: endpoint {
data-lanes = <1 2>;
link-frequencies = /bits/ 64 <360000000 720000000>;
remote-endpoint = <&csi_input>;
};
};
};
};
...

View File

@@ -0,0 +1,103 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/i2c/ovti,ov2732.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: OmniVision OV2732 Image Sensor
maintainers:
- Walter Werner Schneider <contact@schnwalter.eu>
description:
The OmniVision OV2732 is a 2MP (1920x1080) color CMOS image sensor controlled
through an I2C-compatible SCCB bus.
properties:
compatible:
const: ovti,ov2732
reg:
maxItems: 1
clocks:
items:
- description: XVCLK clock
avdd-supply:
description: Analog Domain Power Supply
dovdd-supply:
description: I/O Domain Power Supply
dvdd-supply:
description: Digital Domain Power Supply
powerdown-gpios:
maxItems: 1
description: Reference to the GPIO connected to the pwdn pin. Active low.
reset-gpios:
maxItems: 1
description: Reference to the GPIO connected to the reset pin. Active low.
port:
description: MIPI CSI-2 transmitter port
$ref: /schemas/graph.yaml#/$defs/port-base
additionalProperties: false
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
items:
- const: 1
- const: 2
required:
- data-lanes
- link-frequencies
required:
- compatible
- reg
- clocks
- avdd-supply
- dovdd-supply
- dvdd-supply
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
ov2732: camera@36 {
compatible = "ovti,ov2732";
reg = <0x36>;
clocks = <&ov2732_clk>;
avdd-supply = <&ov2732_avdd>;
dovdd-supply = <&ov2732_dovdd>;
dvdd-supply = <&ov2732_dvdd>;
powerdown-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
port {
camera_out: endpoint {
data-lanes = <1 2>;
link-frequencies = /bits/ 64 <360000000>;
remote-endpoint = <&mipi_in_camera>;
};
};
};
};

View File

@@ -18,6 +18,9 @@ description: |-
through I2C and two-wire SCCB. The sensor output is available via CSI-2
serial data output (up to 4-lane).
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
const: ovti,ov8856
@@ -57,6 +60,9 @@ properties:
This corresponds to the hardware pin XSHUTDOWN which is physically
active low.
orientation: true
rotation: true
port:
$ref: /schemas/graph.yaml#/$defs/port-base
additionalProperties: false

View File

@@ -0,0 +1,111 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/i2c/sony,imx355.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sony IMX355 Sensor
maintainers:
- Richard Acayan <mailingradian@gmail.com>
description:
The IMX355 sensor is a 3280x2464 image sensor, commonly found as the front
camera in smartphones.
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
properties:
compatible:
const: sony,imx355
reg:
maxItems: 1
clocks:
maxItems: 1
avdd-supply:
description: Analog power supply.
dvdd-supply:
description: Digital power supply.
dovdd-supply:
description: Interface power supply.
reset-gpios:
description: Reset GPIO (active low).
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:
minItems: 4
maxItems: 4
required:
- link-frequencies
required:
- endpoint
required:
- compatible
- reg
- clocks
- avdd-supply
- dvdd-supply
- dovdd-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,camcc-sdm845.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
camera@1a {
compatible = "sony,imx355";
reg = <0x1a>;
clocks = <&camcc CAM_CC_MCLK2_CLK>;
assigned-clocks = <&camcc CAM_CC_MCLK2_CLK>;
assigned-clock-rates = <24000000>;
reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>;
avdd-supply = <&cam_front_ldo>;
dvdd-supply = <&cam_front_ldo>;
dovdd-supply = <&cam_vio_ldo>;
pinctrl-names = "default";
pinctrl-0 = <&cam_front_default>;
rotation = <270>;
orientation = <0>;
port {
cam_front_endpoint: endpoint {
data-lanes = <1 2 3 4>;
link-frequencies = /bits/ 64 <360000000>;
remote-endpoint = <&camss_endpoint1>;
};
};
};
};

View File

@@ -13,12 +13,10 @@ description:
The TI DS90UB9XX devices are FPD-Link video deserializers with I2C and GPIO
forwarding.
allOf:
- $ref: /schemas/i2c/i2c-atr.yaml#
properties:
compatible:
enum:
- ti,ds90ub954-q1
- ti,ds90ub960-q1
- ti,ds90ub9702-q1
@@ -125,109 +123,9 @@ properties:
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: FPD-Link input 0
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
description:
Endpoint for FPD-Link port. If the RX mode for this port is RAW,
hsync-active and vsync-active must be defined.
port@1:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: FPD-Link input 1
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
description:
Endpoint for FPD-Link port. If the RX mode for this port is RAW,
hsync-active and vsync-active must be defined.
port@2:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: FPD-Link input 2
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
description:
Endpoint for FPD-Link port. If the RX mode for this port is RAW,
hsync-active and vsync-active must be defined.
port@3:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: FPD-Link input 3
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
description:
Endpoint for FPD-Link port. If the RX mode for this port is RAW,
hsync-active and vsync-active must be defined.
port@4:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: CSI-2 Output 0
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
minItems: 1
maxItems: 4
link-frequencies:
maxItems: 1
required:
- data-lanes
- link-frequencies
port@5:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: CSI-2 Output 1
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
minItems: 1
maxItems: 4
link-frequencies:
maxItems: 1
required:
- data-lanes
- link-frequencies
required:
- port@0
- port@1
- port@2
- port@3
- port@4
- port@5
description:
Ports represent FPD-Link inputs to the deserializer and CSI TX outputs
from the deserializer. The number of ports is model-dependent.
required:
- compatible
@@ -236,6 +134,117 @@ required:
- clock-names
- ports
$defs:
FPDLink-input-port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: FPD-Link input
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
description:
Endpoint for FPD-Link port. If the RX mode for this port is RAW,
hsync-active and vsync-active must be defined.
CSI2-output-port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: CSI-2 Output
properties:
endpoint:
$ref: /schemas/media/video-interfaces.yaml#
unevaluatedProperties: false
properties:
data-lanes:
minItems: 1
maxItems: 4
link-frequencies:
maxItems: 1
required:
- data-lanes
- link-frequencies
allOf:
- $ref: /schemas/i2c/i2c-atr.yaml#
- if:
properties:
compatible:
contains:
enum:
- ti,ds90ub960-q1
- ti,ds90ub9702-q1
then:
properties:
ports:
properties:
port@0:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 0
port@1:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 1
port@2:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 2
port@3:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 3
port@4:
$ref: '#/$defs/CSI2-output-port'
description: CSI-2 Output 0
port@5:
$ref: '#/$defs/CSI2-output-port'
description: CSI-2 Output 1
required:
- port@0
- port@1
- port@2
- port@3
- port@4
- port@5
- if:
properties:
compatible:
contains:
const: ti,ds90ub954-q1
then:
properties:
ports:
properties:
port@0:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 0
port@1:
$ref: '#/$defs/FPDLink-input-port'
description: FPD-Link input 1
port@2:
$ref: '#/$defs/CSI2-output-port'
description: CSI-2 Output 0
required:
- port@0
- port@1
- port@2
links:
properties:
link@2: false
link@3: false
unevaluatedProperties: false
examples:

View File

@@ -24,6 +24,7 @@ properties:
- fsl,imx8ulp-isi
- fsl,imx91-isi
- fsl,imx93-isi
- fsl,imx95-isi
reg:
maxItems: 1
@@ -50,7 +51,7 @@ properties:
interrupts:
description: Processing pipeline interrupts, one per pipeline
minItems: 1
maxItems: 2
maxItems: 8
power-domains:
maxItems: 1
@@ -99,6 +100,7 @@ allOf:
then:
properties:
interrupts:
minItems: 2
maxItems: 2
ports:
properties:
@@ -120,6 +122,29 @@ allOf:
required:
- fsl,blk-ctrl
- if:
properties:
compatible:
contains:
const: fsl,imx95-isi
then:
properties:
interrupts:
minItems: 8
ports:
properties:
port@0:
description: Pixel Link Slave 0
port@1:
description: Pixel Link Slave 1
port@2:
description: MIPI CSI-2 RX 0
port@3:
description: MIPI CSI-2 RX 1
required:
- port@2
- port@3
additionalProperties: false
examples:

View File

@@ -20,6 +20,7 @@ properties:
- enum:
- fsl,imx8mq-mipi-csi2
- fsl,imx8qxp-mipi-csi2
- fsl,imx8ulp-mipi-csi2
- items:
- const: fsl,imx8qm-mipi-csi2
- const: fsl,imx8qxp-mipi-csi2
@@ -39,12 +40,16 @@ properties:
clock that the RX DPHY receives.
- description: ui is the pixel clock (phy_ref up to 333Mhz).
See the reference manual for details.
- description: pclk is clock for csr APB interface.
minItems: 3
clock-names:
items:
- const: core
- const: esc
- const: ui
- const: pclk
minItems: 3
power-domains:
maxItems: 1
@@ -130,21 +135,53 @@ allOf:
compatible:
contains:
enum:
- fsl,imx8qxp-mipi-csi2
- fsl,imx8mq-mipi-csi2
then:
properties:
reg:
maxItems: 1
resets:
minItems: 3
clocks:
maxItems: 3
clock-names:
maxItems: 3
required:
- fsl,mipi-phy-gpr
- if:
properties:
compatible:
contains:
const: fsl,imx8qxp-mipi-csi2
then:
properties:
reg:
minItems: 2
resets:
maxItems: 1
else:
clocks:
maxItems: 3
clock-names:
maxItems: 3
- if:
properties:
compatible:
contains:
enum:
- fsl,imx8ulp-mipi-csi2
then:
properties:
reg:
maxItems: 1
minItems: 2
resets:
minItems: 3
required:
- fsl,mipi-phy-gpr
minItems: 2
maxItems: 2
clocks:
minItems: 4
clock-names:
minItems: 4
additionalProperties: false

View File

@@ -124,7 +124,6 @@ properties:
maxItems: 4
required:
- clock-lanes
- data-lanes
port@1:
@@ -147,7 +146,6 @@ properties:
maxItems: 4
required:
- clock-lanes
- data-lanes
port@2:
@@ -170,7 +168,6 @@ properties:
maxItems: 4
required:
- clock-lanes
- data-lanes
required:

View File

@@ -17,6 +17,7 @@ description:
properties:
compatible:
enum:
- fsl,imx93-mipi-csi2
- rockchip,rk3568-mipi-csi2
reg:
@@ -26,14 +27,23 @@ properties:
items:
- description: Interrupt that signals changes in CSI2HOST_ERR1.
- description: Interrupt that signals changes in CSI2HOST_ERR2.
minItems: 1
interrupt-names:
items:
- const: err1
- const: err2
minItems: 1
clocks:
maxItems: 1
minItems: 1
maxItems: 2
clock-names:
items:
- const: per
- const: pixel
minItems: 1
phys:
maxItems: 1
@@ -88,10 +98,43 @@ required:
- phys
- ports
- power-domains
- resets
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: rockchip,rk3568-mipi-csi2
then:
properties:
interrupts:
minItems: 2
interrupt-names:
minItems: 2
clocks:
maxItems: 1
clock-names:
maxItems: 1
required:
- resets
- if:
properties:
compatible:
contains:
const: fsl,imx93-mipi-csi2
then:
properties:
interrupts:
maxItems: 1
interrupt-names: false
clocks:
minItems: 2
clock-names:
minItems: 2
examples:
- |
#include <dt-bindings/clock/rk3568-cru.h>

View File

@@ -28,16 +28,20 @@ properties:
reg:
minItems: 1
items:
- description: The function configuration registers base
- description: The link table configuration registers base
- description: The cache configuration registers base
maxItems: 3
reg-names:
items:
- const: function
- const: link
- const: cache
oneOf:
- items:
- const: link
- const: function
- const: cache
- items:
- const: function
- const: link
- const: cache
deprecated: true
description: Use link,function,cache block order instead.
interrupts:
maxItems: 1
@@ -123,6 +127,8 @@ allOf:
minItems: 5
reset-names:
minItems: 5
required:
- reg-names
else:
properties:
reg:

View File

@@ -27,11 +27,14 @@ properties:
- const: mclk
dmas:
maxItems: 1
minItems: 1
maxItems: 2
dma-names:
items:
- const: tx
- const: mdma_tx
minItems: 1
resets:
maxItems: 1
@@ -40,6 +43,15 @@ properties:
minItems: 1
maxItems: 2
power-domains:
maxItems: 1
sram:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to a reserved SRAM region which is used as temporary
storage memory between DMA and MDMA engines.
port:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false

View File

@@ -1,180 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Starfive SoC CAMSS ISP
maintainers:
- Jack Zhu <jack.zhu@starfivetech.com>
- Changhuang Liang <changhuang.liang@starfivetech.com>
description:
The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It
consists of a VIN controller (Video In Controller, a top-level control unit)
and an ISP.
properties:
compatible:
const: starfive,jh7110-camss
reg:
maxItems: 2
reg-names:
items:
- const: syscon
- const: isp
clocks:
maxItems: 7
clock-names:
items:
- const: apb_func
- const: wrapper_clk_c
- const: dvp_inv
- const: axiwr
- const: mipi_rx0_pxl
- const: ispcore_2x
- const: isp_axi
resets:
maxItems: 6
reset-names:
items:
- const: wrapper_p
- const: wrapper_c
- const: axird
- const: axiwr
- const: isp_top_n
- const: isp_top_axi
power-domains:
items:
- description: JH7110 ISP Power Domain Switch Controller.
interrupts:
maxItems: 4
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
description: Input port for receiving DVP data.
properties:
endpoint:
$ref: video-interfaces.yaml#
unevaluatedProperties: false
properties:
bus-type:
enum: [5, 6]
bus-width:
enum: [8, 10, 12]
data-shift:
enum: [0, 2]
default: 0
hsync-active:
enum: [0, 1]
default: 1
vsync-active:
enum: [0, 1]
default: 1
required:
- bus-type
- bus-width
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Input port for receiving CSI data.
required:
- port@0
- port@1
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- resets
- reset-names
- power-domains
- interrupts
- ports
additionalProperties: false
examples:
- |
isp@19840000 {
compatible = "starfive,jh7110-camss";
reg = <0x19840000 0x10000>,
<0x19870000 0x30000>;
reg-names = "syscon", "isp";
clocks = <&ispcrg 0>,
<&ispcrg 13>,
<&ispcrg 2>,
<&ispcrg 12>,
<&ispcrg 1>,
<&syscrg 51>,
<&syscrg 52>;
clock-names = "apb_func",
"wrapper_clk_c",
"dvp_inv",
"axiwr",
"mipi_rx0_pxl",
"ispcore_2x",
"isp_axi";
resets = <&ispcrg 0>,
<&ispcrg 1>,
<&ispcrg 10>,
<&ispcrg 11>,
<&syscrg 41>,
<&syscrg 42>;
reset-names = "wrapper_p",
"wrapper_c",
"axird",
"axiwr",
"isp_top_n",
"isp_top_axi";
power-domains = <&pwrc 5>;
interrupts = <92>, <87>, <88>, <90>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
vin_from_sc2235: endpoint {
remote-endpoint = <&sc2235_to_vin>;
bus-type = <5>;
bus-width = <8>;
data-shift = <2>;
hsync-active = <1>;
vsync-active = <0>;
pclk-sample = <1>;
};
};
port@1 {
reg = <1>;
vin_from_csi2rx: endpoint {
remote-endpoint = <&csi2rx_to_vin>;
};
};
};
};

View File

@@ -26,6 +26,7 @@ Documentation/userspace-api/media/index.rst
:numbered:
maintainer-entry-profile
media-committers
v4l2-core
dtv-core

View File

@@ -1,45 +1,328 @@
.. SPDX-License-Identifier: GPL-2.0
Media Subsystem Profile
=======================
Overview
--------
The media subsystem covers support for a variety of devices: stream
capture, analog and digital TV streams, cameras, remote controllers, HDMI CEC
and media pipeline control.
The Linux Media Community (aka: the LinuxTV Community) is formed by
developers working on Linux Kernel Media Subsystem, together with users
who also play an important role in testing the code.
It covers, mainly, the contents of those directories:
The Media Subsystem has code to support a wide variety of media-related
devices: stream capture, analog and digital TV streams, cameras,
video codecs, video processing (resizers, etc.), radio, remote controllers,
HDMI CEC and media pipeline control.
The Media Subsystem consists of the following directories in the kernel
tree:
- drivers/media
- drivers/staging/media
- include/media
- Documentation/devicetree/bindings/media/\ [1]_
- Documentation/admin-guide/media
- Documentation/driver-api/media
- Documentation/userspace-api/media
- Documentation/devicetree/bindings/media/\ [1]_
- include/media
.. [1] Device tree bindings are maintained by the
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS maintainers
(see the MAINTAINERS file). So, changes there must be reviewed
by them before being merged via the media subsystem's development
by them before being merged into the media subsystem's development
tree.
Both media userspace and Kernel APIs are documented and the documentation
must be kept in sync with the API changes. It means that all patches that
add new features to the subsystem must also bring changes to the
corresponding API files.
corresponding API documentation.
Due to the size and wide scope of the media subsystem, media's
maintainership model is to have sub-maintainers that have a broad
knowledge of a specific aspect of the subsystem. It is the sub-maintainers'
task to review the patches, providing feedback to users if the patches are
following the subsystem rules and are properly using the media kernel and
userspace APIs.
Media Maintainers
-----------------
Patches for the media subsystem must be sent to the media mailing list
at linux-media@vger.kernel.org as plain text only e-mail. Emails with
HTML will be automatically rejected by the mail server. It could be wise
to also copy the sub-maintainer(s).
Media Maintainers are not just people capable of writing code, but they
are developers who have demonstrated their ability to collaborate with
the team, get the most knowledgeable people to review code, contribute
high-quality code, and follow through to fix issues (in code or tests).
Due to the size and wide scope of the media subsystem, multiple layers of
maintainers are required, each with their own areas of expertise:
- **Media Driver Maintainer**:
Responsible for one or more drivers within the Media Subsystem. They
are listed in the MAINTAINERS file as maintainer for those drivers. Media
Driver Maintainers review patches for those drivers, provide feedback if
patches do not follow the subsystem rules, or are not using the
media kernel or userspace APIs correctly, or if they have poor code
quality.
If you are the patch author, you work with other Media
Maintainers to ensure your patches are reviewed.
Some Media Driver Maintainers have additional responsibilities. They have
been granted Patchwork access and keep
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
up to date, decide when patches are ready for merging, and create Pull
Requests for the Media Subsystem Maintainers to merge.
- **Media Core Maintainer**:
Media Driver Maintainers with Patchwork access who are also responsible for
one or more media core frameworks.
Core framework changes are done via consensus between the relevant Media
Core Maintainers. Media Maintainers may include core framework changes in
their Pull Requests if they are signed off by the relevant Media Core
Maintainers.
- **Media Subsystem Maintainers**:
Media Core Maintainers who are also responsible for the subsystem as a
whole, with access to the entire subsystem. Responsible for merging Pull
Requests from other Media Maintainers.
Userspace API/ABI changes are made via consensus among Media Subsystem
Maintainers\ [2]_. Media Maintainers may include API/ABI changes in
their Pull Requests if they are signed off by all Media Subsystem
Maintainers.
All Media Maintainers shall agree with the Kernel development process as
described in Documentation/process/index.rst and with the Kernel development
rules in the Kernel documentation, including its code of conduct.
Media Maintainers are often reachable via the #linux-media IRC channel at OFTC.
.. [2] Everything that would break backward compatibility with existing
non-kernel code are API/ABI changes. This includes ioctl and sysfs
interfaces, v4l2 controls, and their behaviors.
Patchwork Access
----------------
All Media Maintainers who have been granted Patchwork access shall ensure that
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
will reflect the current status, e.g. patches shall be delegated to the Media
Maintainer who is handling them and the patch status shall be updated according
to these rules:
- ``Under Review``: Used if the patch requires a second opinion
or when it is part of a Pull Request;
- ``Superseded``: There is a newer version of the patch posted to the
mailing list.
- ``Duplicated``: There was another patch doing the same thing from someone
else that was accepted.
- ``Not Applicable``: Use for patch series that are not merged at media.git
tree (e.g. drm, dmabuf, upstream merge, etc.) but were cross-posted to the
linux-media mailing list.
- ``Accepted``: Once a patch is merged in the multi-committer tree. Only Media
Maintainers with commit rights are allowed to set this state.
If Media Maintainers decide not to accept a patch, they should reply to the
patch authors by email, explaining why it is not accepted, and
update `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
accordingly with one of the following statuses:
- ``Changes Requested``: if a new revision was requested;
- ``Rejected``: if the proposed change is not acceptable at all.
.. Note::
Patchwork supports a couple of clients to help semi-automate
status updates via its REST interface:
https://patchwork.readthedocs.io/en/latest/usage/clients/
For patches that fall within their area of responsibility a Media Maintainer
also decides when those patches are ready for merging, and create Pull Requests
for the Media Subsystem Maintainers to merge.
The most important aspect of becoming a Media Maintainer with Patchwork access
is that you have demonstrated an ability to give good code reviews. We value
your ability to deliver thorough, constructive code reviews.
As such, potential maintainers must earn enough credibility and trust from the
Linux Media Community. To do that, developers shall be familiar with the open
source model and have been active in the Linux Kernel community for some time,
and, in particular, in the media subsystem.
In addition to actually making the code changes, you are basically
demonstrating your:
- commitment to the project;
- ability to collaborate with the team and communicate well;
- understanding of how upstream and the Linux Media Community work
(policies, processes for testing, code review, ...)
- reasonable knowledge about:
- the Kernel development process:
Documentation/process/index.rst
- the Media development profile:
Documentation/driver-api/media/maintainer-entry-profile.rst
- understanding of the projects' code base and coding style;
- ability to provide feedback to the patch authors;
- ability to judge when a patch might be ready for review and to submit;
- ability to write good code (last but certainly not least).
Media Driver Maintainers that desire to get Patchwork access are encouraged
to participate at the yearly Linux Media Summit, typically co-located with
a Linux-related conference. These summits are announced on the linux-media
mailing list.
If you are doing such tasks and have become a valued developer, an
existing Media Maintainer can nominate you to the Media Subsystem Maintainers.
The ultimate responsibility for accepting a nominated maintainer is up to
the subsystem's maintainers. The nominated maintainer must have earned a trust
relationship with all Media Subsystem Maintainers, as, by being granted
Patchwork access, you will take over part of their maintenance tasks.
Media Committers
----------------
Experienced and trusted Media Maintainers may be granted commit rights
which allow them to directly push patches to the media development tree instead
of posting a Pull Request for the Media Subsystem Maintainers. This helps
offloading some of the work of the Media Subsystem Maintainers.
More details about Media Committers' roles and responsibilities can be
found here: :ref:`Media Committers`.
Media development sites
-----------------------
The `LinuxTV <https://linuxtv.org/>`_ web site hosts news about the subsystem,
together with:
- `Wiki pages <https://www.linuxtv.org/wiki/index.php/Main_Page>`_;
- `Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_;
- `Linux Media documentation <https://linuxtv.org/docs.php>`_;
- and more.
The main development trees used by the media subsystem are at:
- Stable tree:
- https://git.linuxtv.org/media.git/
- Media committers tree:
- https://gitlab.freedesktop.org/linux-media/media-committers.git
Please note that it can be rebased, although only as a last resort.
- Media development trees, including apps and CI:
- https://git.linuxtv.org/
- https://gitlab.freedesktop.org/linux-media/
.. _Media development workflow:
Media development workflow
++++++++++++++++++++++++++
All changes for the media subsystem shall be sent first as e-mails to the
media mailing list, following the process documented at
Documentation/process/index.rst.
It means that patches shall be submitted as plain text only via e-mail to
linux-media@vger.kernel.org (aka: LMML). While subscription is not mandatory,
you can find details about how to subscribe to it and to see its archives at:
https://subspace.kernel.org/vger.kernel.org.html
Emails with HTML will be automatically rejected by the mail server.
It could be wise to also copy the relevant Media Maintainer(s). You should use
``scripts/get_maintainers.pl`` to identify whom else needs to be copied.
Please always copy driver's authors and maintainers.
To minimize the chance of merge conflicts for your patch series, and make it
easier to backport patches to stable Kernels, we recommend that you use the
following baseline for your patch series:
1. Features for the next mainline release:
- baseline shall be the ``media-committers.git next`` branch;
2. Bug fixes for the next mainline release:
- baseline shall be the ``media-committers.git next`` branch. If the
changes depend on a fix from the ``media-committers.git fixes``
branch, then you can use that as baseline.
3. Bug fixes for the current mainline release (-rcX):
- baseline shall be the latest mainline -rcX release or the
``media-committers.git fixes`` branch if changes depend on a mainline
fix that is not yet merged;
.. Note::
See https://www.kernel.org/category/releases.html for an overview
about Kernel release types.
Patches with fixes shall have:
- a ``Fixes:`` tag pointing to the first commit that introduced the bug;
- when applicable, a ``Cc: stable@vger.kernel.org``.
Patches that were fixing bugs publicly reported by someone at the
linux-media@vger.kernel.org mailing list shall have:
- a ``Reported-by:`` tag immediately followed by a ``Closes:`` tag.
Patches that change API shall update documentation accordingly at the
same patch series.
See Documentation/process/index.rst for more details about e-mail submission.
Once a patch is submitted, it may follow either one of the following
workflows:
a. Media Maintainers' workflow: Media Maintainers post the Pull Requests,
which are handled by the Media Subsystem Maintainers::
+-------+ +------------+ +------+ +-------+ +---------------------+
|e-mail |-->|picked up by|-->|code |-->|pull |-->|Subsystem Maintainers|
|to LMML| |Patchwork | |review| |request| |merge in |
| | | | | | | | |media-committers.git |
+-------+ +------------+ +------+ +-------+ +---------------------+
For this workflow, Pull Requests are generated by Media Maintainers with
Patchwork access. If you do not have Patchwork access, then please don't
submit Pull Requests, as they will not be processed.
b. Media Committers' workflow: patches are handled by Media Maintainers with
commit rights::
+-------+ +------------+ +------+ +--------------------------+
|e-mail |-->|picked up by|-->|code |-->|Media Committers merge in |
|to LMML| |Patchwork | |review| |media-committers.git |
+-------+ +------------+ +------+ +--------------------------+
When patches are picked up by
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_
and when merged at media-committers, Media CI bots will check for errors and
may provide e-mail feedback about patch problems. When this happens, the patch
submitter must fix them or explain why the errors are false positives.
Patches will only be moved to the next stage in these two workflows if they
pass on Media CI or if there are false-positives in the Media CI reports.
For both workflows, all patches shall be properly reviewed at
linux-media@vger.kernel.org (LMML) before being merged in
``media-committers.git``. Media patches will be reviewed in a timely manner
by the maintainers and reviewers as listed in the MAINTAINERS file.
Media Maintainers shall request reviews from other Media Maintainers and
developers where applicable, i.e. because those developers have more
knowledge about some areas that are changed by a patch.
There shall be no open issues or unresolved or conflicting feedback
from anyone. Clear them up first. Defer to the Media Subsystem
Maintainers if needed.
Failures during e-mail submission
+++++++++++++++++++++++++++++++++
Media's workflow is heavily based on Patchwork, meaning that, once a patch
is submitted, the e-mail will first be accepted by the mailing list
@@ -47,51 +330,107 @@ server, and, after a while, it should appear at:
- https://patchwork.linuxtv.org/project/linux-media/list/
If it doesn't automatically appear there after a few minutes, then
If it doesn't automatically appear there after some time [3]_, then
probably something went wrong on your submission. Please check if the
email is in plain text\ [2]_ only and if your emailer is not mangling
email is in plain text\ [4]_ only and if your emailer is not mangling
whitespaces before complaining or submitting them again.
You can check if the mailing list server accepted your patch, by looking at:
To troubleshoot problems, you should first check if the mailing list
server has accepted your patch, by looking at:
- https://lore.kernel.org/linux-media/
.. [2] If your email contains HTML, the mailing list server will simply
If the patch is there and not at
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
it is likely that your e-mailer mangled the patch. Patchwork internally
has logic that checks if the received e-mail contains a valid patch.
Any whitespace and new line breakages mangling the patch won't be recognized by
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
and such a patch will be rejected.
.. [3] It usually takes a few minutes for the patch to arrive, but
the e-mail server may be busy, so it may take a longer time
for a patch to be picked by
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_.
.. [4] If your email contains HTML, the mailing list server will simply
drop it, without any further notice.
.. _media-developers-gpg:
Media maintainers
+++++++++++++++++
Authentication for pull and merge requests
++++++++++++++++++++++++++++++++++++++++++
At the media subsystem, we have a group of senior developers that
are responsible for doing the code reviews at the drivers (also known as
sub-maintainers), and another senior developer responsible for the
subsystem as a whole. For core changes, whenever possible, multiple
media maintainers do the review.
The authenticity of developers submitting Pull Requests and merge requests
shall be validated by using the Linux Kernel Web of Trust, with PGP signing
at some moment. See: :ref:`kernel_org_trust_repository`.
The media maintainers that work on specific areas of the subsystem are:
With the Pull Request workflow, Pull Requests shall use PGP-signed tags.
- Remote Controllers (infrared):
Sean Young <sean@mess.org>
With the committers' workflow, this is ensured at the time merge request
rights will be granted to the gitlab instance used by the media-committers.git
tree, after receiving the e-mail documented in
:ref:`media-committer-agreement`.
- HDMI CEC:
Hans Verkuil <hverkuil@kernel.org>
For more details about PGP signing, please read
Documentation/process/maintainer-pgp-guide.rst.
- Media controller drivers:
Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Maintaining media maintainer status
-----------------------------------
- ISP, v4l2-async, v4l2-fwnode, v4l2-flash-led-class and Sensor drivers:
Sakari Ailus <sakari.ailus@linux.intel.com>
See :ref:`Maintain Media Status`.
- V4L2 drivers and core V4L2 frameworks:
Hans Verkuil <hverkuil@kernel.org>
List of Media Maintainers
-------------------------
The subsystem maintainer is:
Mauro Carvalho Chehab <mchehab@kernel.org>
The Media Maintainers listed here all have patchwork access and can
make Pull Requests or have commit rights.
Media maintainers may delegate a patch to other media maintainers as needed.
On such case, checkpatch's ``delegate`` field indicates who's currently
responsible for reviewing a patch.
The Media Subsystem Maintainers are:
- Mauro Carvalho Chehab <mchehab@kernel.org>
- Hans Verkuil <hverkuil@kernel.org>
The Media Core Maintainers are:
- Sakari Ailus <sakari.ailus@linux.intel.com>
- Media controller drivers
- Core media controller framework
- ISP
- sensor drivers
- v4l2-async and v4l2-fwnode core frameworks
- v4l2-flash-led-class core framework
- Mauro Carvalho Chehab <mchehab@kernel.org>
- DVB
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- Media controller drivers
- Core media controller framework
- ISP
- Hans Verkuil <hverkuil@kernel.org>
- V4L2 drivers
- V4L2 and videobuf2 core frameworks
- HDMI CEC drivers
- HDMI CEC core framework
- Sean Young <sean@mess.org>
- Remote Controller (infrared) drivers
- Remote Controller (infrared) core framework
The Media Driver Maintainers responsible for specific areas are:
- Nicolas Dufresne <nicolas.dufresne@collabora.com>
- Codec drivers
- M2M driver not otherwise delegated
- Bryan O'Donoghue <bryan.odonoghue@linaro.org>
- Qualcomm drivers
Submit Checklist Addendum
-------------------------
@@ -106,18 +445,15 @@ that should be used in order to check if the drivers are properly
implementing the media APIs:
==================== =======================================================
Type Tool
Type Utility
==================== =======================================================
V4L2 drivers\ [3]_ ``v4l2-compliance``
V4L2 drivers\ [5]_ ``v4l2-compliance``
V4L2 virtual drivers ``contrib/test/test-media``
CEC drivers ``cec-compliance``
==================== =======================================================
.. [3] The ``v4l2-compliance`` also covers the media controller usage inside
V4L2 drivers.
Other compliance tools are under development to check other parts of the
subsystem.
.. [5] The ``v4l2-compliance`` utility also covers the media controller usage
inside V4L2 drivers.
Those tests need to pass before the patches go upstream.
@@ -134,6 +470,8 @@ Where the check script is::
Be sure to not introduce new warnings on your patches without a
very good reason.
Please see `Media development workflow`_ for e-mail submission rules.
Style Cleanup Patches
+++++++++++++++++++++
@@ -173,34 +511,35 @@ least, simply wrapping the lines.
In particular, we accept lines with more than 80 columns:
- on strings, as they shouldn't be broken due to line length limits;
- when a function or variable name need to have a big identifier name,
which keeps hard to honor the 80 columns limit;
- when a function or variable name needs to have a long identifier name,
which makes hard to honor the 80 columns limit;
- on arithmetic expressions, when breaking lines makes them harder to
read;
- when they avoid a line to end with an open parenthesis or an open
- when they avoid a line ending with an open parenthesis or an open
bracket.
Key Cycle Dates
---------------
New submissions can be sent at any time, but if they intend to hit the
New submissions can be sent at any time, but if they are intended to hit the
next merge window they should be sent before -rc5, and ideally stabilized
in the linux-media branch by -rc6.
Review Cadence
--------------
Provided that your patch is at https://patchwork.linuxtv.org, it should
be sooner or later handled, so you don't need to re-submit a patch.
Provided that your patch has landed in
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_, it
should be sooner or later handled, so you don't need to re-submit a patch.
Except for bug fixes, we don't usually add new patches to the development
tree between -rc6 and the next -rc1.
Except for important bug fixes, we don't usually add new patches to the
development tree between -rc6 and the next -rc1.
Please notice that the media subsystem is a high traffic one, so it
could take a while for us to be able to review your patches. Feel free
to ping if you don't get a feedback in a couple of weeks or to ask
other developers to publicly add Reviewed-by and, more importantly,
other developers to publicly add ``Reviewed-by:`` and, more importantly,
``Tested-by:`` tags.
Please note that we expect a detailed description for ``Tested-by:``,
identifying what boards were used at the test and what it was tested.
identifying what boards were used during the test and what it was tested.

View File

@@ -0,0 +1,203 @@
.. SPDX-License-Identifier: GPL-2.0
.. _Media Committers:
Media Committers
================
Who is a Media Committer?
-------------------------
A Media Committer is a Media Maintainer with patchwork access who has been
granted commit access to push patches from other developers and their own
patches to the
`media-committers <https://gitlab.freedesktop.org/linux-media/media-committers>`_
tree.
These commit rights are granted with expectation of responsibility:
committers are people who care about the Linux Kernel as a whole and
about the Linux media subsystem and want to advance its development. It
is also based on a trust relationship among other committers, maintainers
and the Linux Media community.
As Media Committer you have the following additional responsibilities:
1. Patches you authored must have a ``Signed-off-by``, ``Reviewed-by``
or ``Acked-by`` from another Media Maintainer;
2. If a patch introduces a regression, then that must be corrected as soon
as possible. Typically the patch is either reverted, or an additional
patch is committed to fix the regression;
3. If patches are fixing bugs against already released Kernels, including
the reverts mentioned above, the Media Committer shall add the needed
tags. Please see :ref:`Media development workflow` for more details.
4. All Media Committers are responsible for maintaining
`Patchwork <https://patchwork.linuxtv.org/project/linux-media/list/>`_,
updating the state of the patches they review or merge.
Becoming a Media Committer
--------------------------
Existing Media Committers can nominate a Media Maintainer to be granted
commit rights. The Media Maintainer must have patchwork access,
have been reviewing patches from third parties for some time, and has
demonstrated a good understanding of the maintainer's duties and processes.
The ultimate responsibility for accepting a nominated committer is up to
the Media Subsystem Maintainers. The nominated committer must have earned a
trust relationship with all Media Subsystem Maintainers, as, by granting you
commit rights, part of their responsibilities are handed over to you.
Due to that, to become a Media Committer, a consensus between all Media
Subsystem Maintainers is required.
.. Note::
In order to preserve/protect the developers that could have their commit
rights granted, denied or removed as well as the subsystem maintainers who
have the task to accept or deny commit rights, all communication related to
changing commit rights should happen in private as much as possible.
.. _media-committer-agreement:
Media Committer's agreement
---------------------------
Once a nominated committer is accepted by all Media Subsystem Maintainers,
they will ask if the developer is interested in the nomination and discuss
what area(s) of the media subsystem the committer will be responsible for.
Those areas will typically be the same as the areas that the nominated
committer is already maintaining.
When the developer accepts being a committer, the new committer shall
explicitly accept the Kernel development policies described under its
Documentation/, and in particular to the rules in this document, by writing
an e-mail to media-committers@linuxtv.org, with a declaration of intent
following the model below::
I, John Doe, would like to change my status to: Committer
As Media Maintainer I accept commit rights for the following areas of
the media subsystem:
...
For the purpose of committing patches to the media-committers tree,
I'll be using my user https://gitlab.freedesktop.org/users/<username>.
Followed by a formal declaration of agreement with the Kernel development
rules::
I agree to follow the Kernel development rules described at:
https://www.kernel.org/doc/html/latest/driver-api/media/media-committers.rst
and to the Linux Kernel development process rules.
I agree to abide by the Code of Conduct as documented in:
https://www.kernel.org/doc/html/latest/process/code-of-conduct.rst
I am aware that I can, at any point of time, retire. In that case, I will
send an e-mail to notify the Media Subsystem Maintainers for them to revoke
my commit rights.
I am aware that the Kernel development rules change over time.
By doing a new push to media-committers tree, I understand that I agree
to follow the rules in effect at the time of the commit.
That e-mail shall be signed via the Kernel Web of trust with a PGP key cross
signed by other Kernel and media developers. As described at
:ref:`media-developers-gpg`, the PGP signature, together with the gitlab user
security are fundamental components that ensure the authenticity of the merge
requests that will happen at the media-committers.git tree.
In case the kernel development process changes, by merging new commits to the
`media-committers tree <https://gitlab.freedesktop.org/linux-media/media-committers>`_,
the Media Committer implicitly declares their agreement with the latest
version of the documented process including the contents of this file.
If a Media Committer decides to retire, it is the committer's duty to
notify the Media Subsystem Maintainers about that decision.
.. note::
1. Changes to the kernel media development process shall be announced in
the media-committers mailing list with a reasonable review period. All
committers are automatically subscribed to that mailing list;
2. Due to the distributed nature of the Kernel development, it is
possible that kernel development process changes may end being
reviewed/merged at the Linux Docs and/or at the Linux Kernel mailing
lists, especially for the contents under Documentation/process and for
trivial typo fixes.
Media Core Committers
---------------------
A Media Core Committer is a Media Core Maintainer with commit rights.
As described in Documentation/driver-api/media/maintainer-entry-profile.rst,
a Media Core Maintainer maintains media core frameworks as well, besides
just drivers, and so is allowed to change core files and the media subsystem's
Kernel API. The extent of the core committer's grants will be detailed by the
Media Subsystem Maintainers when they nominate a Media Core Committer.
Existing Media Committers may become Media Core Committers and vice versa.
Such decisions will be taken in consensus among the Media Subsystem
Maintainers.
Media committers rules
----------------------
Media committers shall do their best efforts to avoid merging patches that
would break any existing drivers. If it breaks, fixup or revert patches
shall be merged as soon as possible, aiming to be merged at the same Kernel
cycle the bug is reported.
Media committers shall behave accordingly to the rights granted by
the Media Subsystem Maintainers, especially with regards of the scope of changes
they may apply directly at the media-committers tree. That scope can
change over time on a mutual agreement between Media Committers and
Media Subsystem Maintainers.
The Media Committer workflow is described at :ref:`Media development workflow`.
.. _Maintain Media Status:
Maintaining Media Maintainer or Committer status
------------------------------------------------
A community of maintainers working together to move the Linux Kernel
forward is essential to creating successful projects that are rewarding
to work on. If there are problems or disagreements within the community,
they can usually be solved through healthy discussion and debate.
In the unhappy event that a Media Maintainer or Committer continues to
disregard good citizenship (or actively disrupts the project), we may need
to revoke that person's status. In such cases, if someone suggests the
revocation with a good reason, then after discussing this among the Media
Maintainers, the final decision is taken by the Media Subsystem Maintainers.
As the decision to become a Media Maintainer or Committer comes from a
consensus between Media Subsystem Maintainers, a single Media Subsystem
Maintainer not trusting the Media Maintainer or Committer anymore is enough
to revoke their maintenance, Patchwork grants and/or commit rights.
Having commit rights revoked doesn't prevent Media Maintainers to keep
contributing to the subsystem either via the pull request or via email workflow
as documented at the :ref:`Media development workflow`.
If a maintainer is inactive for more than a couple of Kernel cycles,
maintainers will try to reach you via e-mail. If not possible, they may
revoke their maintainer/patchwork and committer rights and update MAINTAINERS
file entries accordingly. If you wish to resume contributing as maintainer
later on, then contact the Media Subsystem Maintainers to ask if your
maintenance, Patchwork grants and commit rights can be restored.
References
----------
Much of this was inspired by/copied from the committer policies of:
- `Chromium <https://chromium.googlesource.com/chromium/src/+/main/docs/contributing.md>`_;
- `WebKit <https://webkit.org/commit-and-review-policy/>`_;
- `Mozilla <https://www.mozilla.org/hacking/committer/>`_.

View File

@@ -897,6 +897,8 @@ the new default in GnuPG v2). To set it, add (or modify) the
trust-model tofu+pgp
.. _kernel_org_trust_repository:
Using the kernel.org web of trust repository
--------------------------------------------

View File

@@ -444,7 +444,7 @@ Description
~~~~~~~~~~~
A call to `AUDIO_GET_CAPABILITIES`_ returns an unsigned integer with the
following bits set according to the hardwares capabilities.
following bits set according to the hardware's capabilities.
-----

View File

@@ -159,14 +159,18 @@ formats in memory (a raw Bayer image won't be magically converted to
JPEG just by storing it to memory), there is no one-to-one
correspondence between them.
The media bus pixel codes document parallel formats. Should the pixel data be
transported over a serial bus, the media bus pixel code that describes a
parallel format that transfers a sample on a single clock cycle is used. For
instance, both MEDIA_BUS_FMT_BGR888_1X24 and MEDIA_BUS_FMT_BGR888_3X8 are used
on parallel busses for transferring an 8 bits per sample BGR data, whereas on
serial busses the data in this format is only referred to using
MEDIA_BUS_FMT_BGR888_1X24. This is because there is effectively only a single
way to transport that format on the serial busses.
While the media bus pixel codes are named based on how pixels are
transmitted on parallel buses, serial buses do not define separate
codes. By convention, they use the codes that transfer a sample on a
single clock cycle, and whose bit orders from LSB to MSB correspond to
the order in which colour components are transmitted on the serial bus.
For instance, the MIPI CSI-2 24-bit RGB (RGB888) format uses the
MEDIA_BUS_FMT_RGB888_1X24 media bus code because CSI-2 transmits the
blue colour component first, followed by green and red, and
MEDIA_BUS_FMT_RGB888_1X24 defines the first bit of blue at bit 0.
While used for 24-bit RGB data on parallel buses, the
MEDIA_BUS_FMT_RGB888_3X8 or MEDIA_BUS_FMT_BGR888_1X24 codes must not be
used for CSI-2.
Packed RGB Formats
^^^^^^^^^^^^^^^^^^

View File

@@ -16136,8 +16136,9 @@ MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
S: Maintained
P: Documentation/driver-api/media/maintainer-entry-profile.rst
W: https://linuxtv.org
Q: http://patchwork.kernel.org/project/linux-media/list/
Q: https://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/media.git
F: Documentation/admin-guide/media/
F: Documentation/devicetree/bindings/media/
@@ -19542,9 +19543,11 @@ F: drivers/media/i2c/ov02e10.c
OMNIVISION OV08D10 SENSOR DRIVER
M: Jimmy Su <jimmy.su@intel.com>
R: Matthias Fend <matthias.fend@emfend.at>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
F: drivers/media/i2c/ov08d10.c
OMNIVISION OV08X40 SENSOR DRIVER
@@ -19585,6 +19588,13 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/ovti,ov2685.yaml
F: drivers/media/i2c/ov2685.c
OMNIVISION OV2732 SENSOR DRIVER
M: Walter Werner Schneider <contact@schnwalter.eu>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
F: drivers/media/i2c/ov2732.c
OMNIVISION OV2735 SENSOR DRIVER
M: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
@@ -24586,7 +24596,6 @@ F: include/uapi/rdma/rdma_user_rxe.h
SOFTLOGIC 6x10 MPEG CODEC
M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
M: Andrey Utkin <andrey_utkin@fastmail.com>
M: Ismael Luceno <ismael@iodev.co.uk>
L: linux-media@vger.kernel.org
S: Supported
@@ -25237,15 +25246,6 @@ M: Ion Badulescu <ionut@badula.org>
S: Odd Fixes
F: drivers/net/ethernet/adaptec/starfire*
STARFIVE CAMERA SUBSYSTEM DRIVER
M: Jack Zhu <jack.zhu@starfivetech.com>
M: Changhuang Liang <changhuang.liang@starfivetech.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/media/starfive_camss.rst
F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml
F: drivers/staging/media/starfive/camss
STARFIVE CRYPTO DRIVER
M: Jia Jie Ho <jiajie.ho@starfivetech.com>
M: William Qiu <william.qiu@starfivetech.com>
@@ -26816,6 +26816,12 @@ F: drivers/char/toshiba.c
F: include/linux/toshiba.h
F: include/uapi/linux/toshiba.h
TOSHIBA T4KA3 CAMERA SENSOR DRIVER
M: Kate Hsuan <hpa@redhat.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/t4ka3.c
TOSHIBA TC358743 DRIVER
M: Hans Verkuil <hverkuil@kernel.org>
L: linux-media@vger.kernel.org
@@ -27036,8 +27042,6 @@ F: drivers/platform/x86/tuxedo/
TW5864 VIDEO4LINUX DRIVER
M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
M: Andrey Utkin <andrey_utkin@fastmail.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/pci/tw5864/

View File

@@ -120,6 +120,17 @@ static int tps68470_gpio_input(struct gpio_chip *gc, unsigned int offset)
TPS68470_GPIO_MODE_MASK, 0x00);
}
static int tps68470_enable_i2c_daisy_chain(struct gpio_chip *gc)
{
int ret;
ret = tps68470_gpio_input(gc, 1);
if (ret)
return ret;
return tps68470_gpio_input(gc, 2);
}
static const char *tps68470_names[TPS68470_N_GPIO] = {
"gpio.0", "gpio.1", "gpio.2", "gpio.3",
"gpio.4", "gpio.5", "gpio.6",
@@ -129,6 +140,7 @@ static const char *tps68470_names[TPS68470_N_GPIO] = {
static int tps68470_gpio_probe(struct platform_device *pdev)
{
struct tps68470_gpio_data *tps68470_gpio;
int ret;
tps68470_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps68470_gpio),
GFP_KERNEL);
@@ -149,7 +161,14 @@ static int tps68470_gpio_probe(struct platform_device *pdev)
tps68470_gpio->gc.base = -1;
tps68470_gpio->gc.parent = &pdev->dev;
return devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, tps68470_gpio);
ret = devm_gpiochip_add_data(&pdev->dev, &tps68470_gpio->gc, tps68470_gpio);
if (ret)
return ret;
if (device_property_present(&pdev->dev, "daisy-chain-enable"))
ret = tps68470_enable_i2c_daisy_chain(&tps68470_gpio->gc);
return ret;
}
static struct platform_driver tps68470_gpio_driver = {

View File

@@ -2221,6 +2221,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
return;
rc_unregister_device(ctx->rc_dev);
rc_free_device(ctx->rc_dev);
}
static int sii8620_is_packing_required(struct sii8620 *ctx,

View File

@@ -14,6 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/tegra-mipi-cal.h>
#include <video/mipi_display.h>

View File

@@ -9,6 +9,7 @@ host1x-y = \
job.o \
debug.o \
mipi.o \
tegra114-mipi.o \
fence.o \
hw/host1x01.o \
hw/host1x02.o \

View File

@@ -1,215 +1,110 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
* Copyright (C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <linux/clk.h>
#include <linux/host1x.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/tegra-mipi-cal.h>
#include "dev.h"
/* only need to support one provider */
static struct {
struct device_node *np;
const struct tegra_mipi_ops *ops;
} provider;
#define MIPI_CAL_CTRL 0x00
#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
#define MIPI_CAL_CTRL_START (1 << 0)
#define MIPI_CAL_AUTOCAL_CTRL 0x01
#define MIPI_CAL_STATUS 0x02
#define MIPI_CAL_STATUS_DONE (1 << 16)
#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
#define MIPI_CAL_CONFIG_CSIA 0x05
#define MIPI_CAL_CONFIG_CSIB 0x06
#define MIPI_CAL_CONFIG_CSIC 0x07
#define MIPI_CAL_CONFIG_CSID 0x08
#define MIPI_CAL_CONFIG_CSIE 0x09
#define MIPI_CAL_CONFIG_CSIF 0x0a
#define MIPI_CAL_CONFIG_DSIA 0x0e
#define MIPI_CAL_CONFIG_DSIB 0x0f
#define MIPI_CAL_CONFIG_DSIC 0x10
#define MIPI_CAL_CONFIG_DSID 0x11
#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
/* for data and clock lanes */
#define MIPI_CAL_CONFIG_SELECT (1 << 21)
/* for data lanes */
#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
/* for clock lanes */
#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
#define MIPI_CAL_BIAS_PAD_CFG0 0x16
#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
#define MIPI_CAL_BIAS_PAD_CFG1 0x17
#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
#define MIPI_CAL_BIAS_PAD_CFG2 0x18
#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
struct tegra_mipi_pad {
unsigned long data;
unsigned long clk;
};
struct tegra_mipi_soc {
bool has_clk_lane;
const struct tegra_mipi_pad *pads;
unsigned int num_pads;
bool clock_enable_override;
bool needs_vclamp_ref;
/* bias pad configuration settings */
u8 pad_drive_down_ref;
u8 pad_drive_up_ref;
u8 pad_vclamp_level;
u8 pad_vauxp_level;
/* calibration settings for data lanes */
u8 hspdos;
u8 hspuos;
u8 termos;
/* calibration settings for clock lanes */
u8 hsclkpdos;
u8 hsclkpuos;
};
struct tegra_mipi {
const struct tegra_mipi_soc *soc;
struct device *dev;
void __iomem *regs;
struct mutex lock;
struct clk *clk;
unsigned long usage_count;
};
struct tegra_mipi_device {
struct platform_device *pdev;
struct tegra_mipi *mipi;
struct device *device;
unsigned long pads;
};
static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
unsigned long offset)
/**
* tegra_mipi_enable - Enable the Tegra MIPI calibration device.
* @device: Handle to the Tegra MIPI calibration device.
*
* This calls the enable sequence for the Tegra MIPI calibration device.
*
* Returns 0 on success or a negative error code on failure.
*/
int tegra_mipi_enable(struct tegra_mipi_device *device)
{
return readl(mipi->regs + (offset << 2));
}
static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
unsigned long offset)
{
writel(value, mipi->regs + (offset << 2));
}
static int tegra_mipi_power_up(struct tegra_mipi *mipi)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
if (mipi->soc->needs_vclamp_ref)
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
clk_disable(mipi->clk);
if (device->ops->enable)
return device->ops->enable(device);
return 0;
}
EXPORT_SYMBOL(tegra_mipi_enable);
static int tegra_mipi_power_down(struct tegra_mipi *mipi)
/**
* tegra_mipi_disable - Disable the Tegra MIPI calibration device.
* @device: Handle to the Tegra MIPI calibration device.
*
* This calls the disable sequence for the Tegra MIPI calibration device.
*
* Returns 0 on success or a negative error code on failure.
*/
int tegra_mipi_disable(struct tegra_mipi_device *device)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
/*
* The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
* supplies the DSI pads. This must be kept enabled until none of the
* DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value |= MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
/*
* MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
* control a regulator that supplies current to the pre-driver logic.
* Powering down this regulator causes DSI to fail, so it must remain
* powered on until none of the DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
if (mipi->soc->needs_vclamp_ref)
value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
if (device->ops->disable)
return device->ops->disable(device);
return 0;
}
EXPORT_SYMBOL(tegra_mipi_disable);
/**
* tegra_mipi_start_calibration - Start the Tegra MIPI calibration sequence.
* @device: Handle to the Tegra MIPI calibration device.
*
* This initiates the calibration of CSI/DSI interfaces via the Tegra MIPI
* calibration device.
*
* Returns 0 on success or a negative error code on failure.
*/
int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
{
if (device->ops->start_calibration)
return device->ops->start_calibration(device);
return 0;
}
EXPORT_SYMBOL(tegra_mipi_start_calibration);
/**
* tegra_mipi_finish_calibration - Finish the Tegra MIPI calibration sequence.
* @device: Handle to the Tegra MIPI calibration device.
*
* This completes the calibration of CSI/DSI interfaces via the Tegra MIPI
* calibration device.
*
* Returns 0 on success or a negative error code on failure.
*/
int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
{
if (device->ops->finish_calibration)
return device->ops->finish_calibration(device);
return 0;
}
EXPORT_SYMBOL(tegra_mipi_finish_calibration);
/**
* tegra_mipi_request - Request a Tegra MIPI calibration device.
* @device: Handle of the device requesting the MIPI calibration function.
* @np: Device node pointer of the device requesting the MIPI calibration
* function.
*
* This function requests a reference to a Tegra MIPI calibration device.
*
* Returns a pointer to the Tegra MIPI calibration device on success,
* or an ERR_PTR-encoded error code on failure.
*/
struct tegra_mipi_device *tegra_mipi_request(struct device *device,
struct device_node *np)
{
struct tegra_mipi_device *dev;
struct tegra_mipi_device *mipidev;
struct of_phandle_args args;
int err;
@@ -219,321 +114,80 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
if (err < 0)
return ERR_PTR(err);
dev = kzalloc_obj(*dev);
if (!dev) {
if (provider.np != args.np)
return ERR_PTR(-ENODEV);
mipidev = kzalloc_obj(*mipidev);
if (!mipidev) {
err = -ENOMEM;
goto out;
}
dev->pdev = of_find_device_by_node(args.np);
if (!dev->pdev) {
mipidev->pdev = of_find_device_by_node(args.np);
if (!mipidev->pdev) {
err = -ENODEV;
goto free;
}
dev->mipi = platform_get_drvdata(dev->pdev);
if (!dev->mipi) {
err = -EPROBE_DEFER;
goto put;
}
of_node_put(args.np);
dev->pads = args.args[0];
dev->device = device;
mipidev->ops = provider.ops;
mipidev->pads = args.args[0];
return dev;
return mipidev;
put:
platform_device_put(dev->pdev);
free:
kfree(dev);
kfree(mipidev);
out:
of_node_put(args.np);
return ERR_PTR(err);
}
EXPORT_SYMBOL(tegra_mipi_request);
void tegra_mipi_free(struct tegra_mipi_device *device)
/**
* tegra_mipi_free - Free a Tegra MIPI calibration device.
* @mipidev: Handle to the Tegra MIPI calibration device.
*
* This function releases a reference to a Tegra MIPI calibration device
* previously requested by tegra_mipi_request().
*/
void tegra_mipi_free(struct tegra_mipi_device *mipidev)
{
platform_device_put(device->pdev);
kfree(device);
platform_device_put(mipidev->pdev);
kfree(mipidev);
}
EXPORT_SYMBOL(tegra_mipi_free);
int tegra_mipi_enable(struct tegra_mipi_device *dev)
static void tegra_mipi_remove_provider(void *data)
{
int err = 0;
mutex_lock(&dev->mipi->lock);
if (dev->mipi->usage_count++ == 0)
err = tegra_mipi_power_up(dev->mipi);
mutex_unlock(&dev->mipi->lock);
return err;
}
EXPORT_SYMBOL(tegra_mipi_enable);
int tegra_mipi_disable(struct tegra_mipi_device *dev)
{
int err = 0;
mutex_lock(&dev->mipi->lock);
if (--dev->mipi->usage_count == 0)
err = tegra_mipi_power_down(dev->mipi);
mutex_unlock(&dev->mipi->lock);
return err;
}
EXPORT_SYMBOL(tegra_mipi_disable);
int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
{
struct tegra_mipi *mipi = device->mipi;
void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
u32 value;
int err;
err = readl_relaxed_poll_timeout(status_reg, value,
!(value & MIPI_CAL_STATUS_ACTIVE) &&
(value & MIPI_CAL_STATUS_DONE), 50,
250000);
mutex_unlock(&device->mipi->lock);
clk_disable(device->mipi->clk);
return err;
}
EXPORT_SYMBOL(tegra_mipi_finish_calibration);
int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
{
const struct tegra_mipi_soc *soc = device->mipi->soc;
unsigned int i;
u32 value;
int err;
err = clk_enable(device->mipi->clk);
if (err < 0)
return err;
mutex_lock(&device->mipi->lock);
value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
for (i = 0; i < soc->num_pads; i++) {
u32 clk = 0, data = 0;
if (device->pads & BIT(i)) {
data = MIPI_CAL_CONFIG_SELECT |
MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
MIPI_CAL_CONFIG_TERMOS(soc->termos);
clk = MIPI_CAL_CONFIG_SELECT |
MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
}
tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
if (soc->has_clk_lane && soc->pads[i].clk != 0)
tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
}
value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
value |= MIPI_CAL_CTRL_PRESCALE(0x2);
if (!soc->clock_enable_override)
value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
else
value |= MIPI_CAL_CTRL_CLKEN_OVR;
tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
/* clear any pending status bits */
value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
value |= MIPI_CAL_CTRL_START;
tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
/*
* Wait for min 72uS to let calibration logic finish calibration
* sequence codes before waiting for pads idle state to apply the
* results.
*/
usleep_range(75, 80);
return 0;
}
EXPORT_SYMBOL(tegra_mipi_start_calibration);
static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA },
{ .data = MIPI_CAL_CONFIG_CSIB },
{ .data = MIPI_CAL_CONFIG_CSIC },
{ .data = MIPI_CAL_CONFIG_CSID },
{ .data = MIPI_CAL_CONFIG_CSIE },
{ .data = MIPI_CAL_CONFIG_DSIA },
{ .data = MIPI_CAL_CONFIG_DSIB },
{ .data = MIPI_CAL_CONFIG_DSIC },
{ .data = MIPI_CAL_CONFIG_DSID },
};
static const struct tegra_mipi_soc tegra114_mipi_soc = {
.has_clk_lane = false,
.pads = tegra114_mipi_pads,
.num_pads = ARRAY_SIZE(tegra114_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = true,
.pad_drive_down_ref = 0x2,
.pad_drive_up_ref = 0x0,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x4,
.termos = 0x5,
.hsclkpdos = 0x0,
.hsclkpuos = 0x4,
};
static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
};
static const struct tegra_mipi_soc tegra124_mipi_soc = {
.has_clk_lane = true,
.pads = tegra124_mipi_pads,
.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = true,
.pad_drive_down_ref = 0x2,
.pad_drive_up_ref = 0x0,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x0,
.termos = 0x0,
.hsclkpdos = 0x1,
.hsclkpuos = 0x2,
};
static const struct tegra_mipi_soc tegra132_mipi_soc = {
.has_clk_lane = true,
.pads = tegra124_mipi_pads,
.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
.clock_enable_override = false,
.needs_vclamp_ref = false,
.pad_drive_down_ref = 0x0,
.pad_drive_up_ref = 0x3,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x0,
.termos = 0x0,
.hsclkpdos = 0x3,
.hsclkpuos = 0x2,
};
static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
};
static const struct tegra_mipi_soc tegra210_mipi_soc = {
.has_clk_lane = true,
.pads = tegra210_mipi_pads,
.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = false,
.pad_drive_down_ref = 0x0,
.pad_drive_up_ref = 0x3,
.pad_vclamp_level = 0x1,
.pad_vauxp_level = 0x1,
.hspdos = 0x0,
.hspuos = 0x2,
.termos = 0x0,
.hsclkpdos = 0x0,
.hsclkpuos = 0x2,
};
static const struct of_device_id tegra_mipi_of_match[] = {
{ .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
{ .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
{ .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
{ .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
{ },
};
static int tegra_mipi_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct tegra_mipi *mipi;
match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
if (!mipi)
return -ENOMEM;
mipi->soc = match->data;
mipi->dev = &pdev->dev;
mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(mipi->regs))
return PTR_ERR(mipi->regs);
mutex_init(&mipi->lock);
mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
if (IS_ERR(mipi->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
return PTR_ERR(mipi->clk);
}
platform_set_drvdata(pdev, mipi);
return 0;
provider.np = NULL;
provider.ops = NULL;
}
struct platform_driver tegra_mipi_driver = {
.driver = {
.name = "tegra-mipi",
.of_match_table = tegra_mipi_of_match,
},
.probe = tegra_mipi_probe,
};
/**
* devm_tegra_mipi_add_provider - Managed registration of a Tegra MIPI
* calibration function provider.
* @device: Handle to the device providing the MIPI calibration function.
* @np: Device node pointer of the device providing the MIPI calibration
* function.
* @ops: Operations supported by the MIPI calibration device.
*
* This registers a device that provides MIPI calibration functions.
* For Tegra20 and Tegra30, this is the CSI block, while Tegra114 and
* newer SoC generations have a dedicated hardware block for these
* functions.
*
* Returns 0 on success or a negative error code on failure.
*/
int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
const struct tegra_mipi_ops *ops)
{
if (provider.np)
return -EBUSY;
provider.np = np;
provider.ops = ops;
return devm_add_action_or_reset(device, tegra_mipi_remove_provider, NULL);
}
EXPORT_SYMBOL(devm_tegra_mipi_add_provider);

View File

@@ -0,0 +1,483 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <linux/clk.h>
#include <linux/host1x.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/tegra-mipi-cal.h>
#include "dev.h"
#define MIPI_CAL_CTRL 0x00
#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
#define MIPI_CAL_CTRL_START BIT(0)
#define MIPI_CAL_AUTOCAL_CTRL 0x01
#define MIPI_CAL_STATUS 0x02
#define MIPI_CAL_STATUS_DONE BIT(16)
#define MIPI_CAL_STATUS_ACTIVE BIT(0)
#define MIPI_CAL_CONFIG_CSIA 0x05
#define MIPI_CAL_CONFIG_CSIB 0x06
#define MIPI_CAL_CONFIG_CSIC 0x07
#define MIPI_CAL_CONFIG_CSID 0x08
#define MIPI_CAL_CONFIG_CSIE 0x09
#define MIPI_CAL_CONFIG_CSIF 0x0a
#define MIPI_CAL_CONFIG_DSIA 0x0e
#define MIPI_CAL_CONFIG_DSIB 0x0f
#define MIPI_CAL_CONFIG_DSIC 0x10
#define MIPI_CAL_CONFIG_DSID 0x11
#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
/* for data and clock lanes */
#define MIPI_CAL_CONFIG_SELECT BIT(21)
/* for data lanes */
#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
/* for clock lanes */
#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
#define MIPI_CAL_BIAS_PAD_CFG0 0x16
#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1)
#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0)
#define MIPI_CAL_BIAS_PAD_CFG1 0x17
#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
#define MIPI_CAL_BIAS_PAD_CFG2 0x18
#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
struct tegra_mipi_pad {
unsigned long data;
unsigned long clk;
};
struct tegra_mipi_soc {
bool has_clk_lane;
const struct tegra_mipi_pad *pads;
unsigned int num_pads;
bool clock_enable_override;
bool needs_vclamp_ref;
/* bias pad configuration settings */
u8 pad_drive_down_ref;
u8 pad_drive_up_ref;
u8 pad_vclamp_level;
u8 pad_vauxp_level;
/* calibration settings for data lanes */
u8 hspdos;
u8 hspuos;
u8 termos;
/* calibration settings for clock lanes */
u8 hsclkpdos;
u8 hsclkpuos;
};
struct tegra_mipi {
const struct tegra_mipi_soc *soc;
struct device *dev;
void __iomem *regs;
struct mutex lock; /* for register access */
struct clk *clk;
unsigned long usage_count;
};
static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
unsigned long offset)
{
return readl(mipi->regs + (offset << 2));
}
static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
unsigned long offset)
{
writel(value, mipi->regs + (offset << 2));
}
static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
if (mipi->soc->needs_vclamp_ref)
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
clk_disable(mipi->clk);
return 0;
}
static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
/*
* The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
* supplies the DSI pads. This must be kept enabled until none of the
* DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value |= MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
/*
* MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
* control a regulator that supplies current to the pre-driver logic.
* Powering down this regulator causes DSI to fail, so it must remain
* powered on until none of the DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
if (mipi->soc->needs_vclamp_ref)
value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
return 0;
}
static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
{
struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
int err = 0;
mutex_lock(&mipi->lock);
if (mipi->usage_count++ == 0)
err = tegra114_mipi_power_up(mipi);
mutex_unlock(&mipi->lock);
return err;
}
static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
{
struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
int err = 0;
mutex_lock(&mipi->lock);
if (--mipi->usage_count == 0)
err = tegra114_mipi_power_down(mipi);
mutex_unlock(&mipi->lock);
return err;
}
static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
{
struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
u32 value;
int err;
err = readl_relaxed_poll_timeout(status_reg, value,
!(value & MIPI_CAL_STATUS_ACTIVE) &&
(value & MIPI_CAL_STATUS_DONE), 50,
250000);
mutex_unlock(&mipi->lock);
clk_disable(mipi->clk);
return err;
}
static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
{
struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
const struct tegra_mipi_soc *soc = mipi->soc;
unsigned int i;
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
mutex_lock(&mipi->lock);
value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
for (i = 0; i < soc->num_pads; i++) {
u32 clk = 0, data = 0;
if (mipidev->pads & BIT(i)) {
data = MIPI_CAL_CONFIG_SELECT |
MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
MIPI_CAL_CONFIG_TERMOS(soc->termos);
clk = MIPI_CAL_CONFIG_SELECT |
MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
}
tegra_mipi_writel(mipi, data, soc->pads[i].data);
if (soc->has_clk_lane && soc->pads[i].clk != 0)
tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
}
value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
value |= MIPI_CAL_CTRL_PRESCALE(0x2);
if (!soc->clock_enable_override)
value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
else
value |= MIPI_CAL_CTRL_CLKEN_OVR;
tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
/* clear any pending status bits */
value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
value |= MIPI_CAL_CTRL_START;
tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
/*
* Wait for min 72uS to let calibration logic finish calibration
* sequence codes before waiting for pads idle state to apply the
* results.
*/
usleep_range(75, 80);
return 0;
}
static const struct tegra_mipi_ops tegra114_mipi_ops = {
.enable = tegra114_mipi_enable,
.disable = tegra114_mipi_disable,
.start_calibration = tegra114_mipi_start_calibration,
.finish_calibration = tegra114_mipi_finish_calibration,
};
static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA },
{ .data = MIPI_CAL_CONFIG_CSIB },
{ .data = MIPI_CAL_CONFIG_CSIC },
{ .data = MIPI_CAL_CONFIG_CSID },
{ .data = MIPI_CAL_CONFIG_CSIE },
{ .data = MIPI_CAL_CONFIG_DSIA },
{ .data = MIPI_CAL_CONFIG_DSIB },
{ .data = MIPI_CAL_CONFIG_DSIC },
{ .data = MIPI_CAL_CONFIG_DSID },
};
static const struct tegra_mipi_soc tegra114_mipi_soc = {
.has_clk_lane = false,
.pads = tegra114_mipi_pads,
.num_pads = ARRAY_SIZE(tegra114_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = true,
.pad_drive_down_ref = 0x2,
.pad_drive_up_ref = 0x0,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x4,
.termos = 0x5,
.hsclkpdos = 0x0,
.hsclkpuos = 0x4,
};
static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
};
static const struct tegra_mipi_soc tegra124_mipi_soc = {
.has_clk_lane = true,
.pads = tegra124_mipi_pads,
.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = true,
.pad_drive_down_ref = 0x2,
.pad_drive_up_ref = 0x0,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x0,
.termos = 0x0,
.hsclkpdos = 0x1,
.hsclkpuos = 0x2,
};
static const struct tegra_mipi_soc tegra132_mipi_soc = {
.has_clk_lane = true,
.pads = tegra124_mipi_pads,
.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
.clock_enable_override = false,
.needs_vclamp_ref = false,
.pad_drive_down_ref = 0x0,
.pad_drive_up_ref = 0x3,
.pad_vclamp_level = 0x0,
.pad_vauxp_level = 0x0,
.hspdos = 0x0,
.hspuos = 0x0,
.termos = 0x0,
.hsclkpdos = 0x3,
.hsclkpuos = 0x2,
};
static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
};
static const struct tegra_mipi_soc tegra210_mipi_soc = {
.has_clk_lane = true,
.pads = tegra210_mipi_pads,
.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
.clock_enable_override = true,
.needs_vclamp_ref = false,
.pad_drive_down_ref = 0x0,
.pad_drive_up_ref = 0x3,
.pad_vclamp_level = 0x1,
.pad_vauxp_level = 0x1,
.hspdos = 0x0,
.hspuos = 0x2,
.termos = 0x0,
.hsclkpdos = 0x0,
.hsclkpuos = 0x2,
};
static const struct of_device_id tegra_mipi_of_match[] = {
{ .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
{ .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
{ .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
{ .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
{ },
};
static int tegra_mipi_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct tegra_mipi *mipi;
match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
if (!mipi)
return -ENOMEM;
mipi->soc = match->data;
mipi->dev = &pdev->dev;
mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(mipi->regs))
return PTR_ERR(mipi->regs);
mutex_init(&mipi->lock);
mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
if (IS_ERR(mipi->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
return PTR_ERR(mipi->clk);
}
platform_set_drvdata(pdev, mipi);
return devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node,
&tegra114_mipi_ops);
}
struct platform_driver tegra_mipi_driver = {
.driver = {
.name = "tegra-mipi",
.of_match_table = tegra_mipi_of_match,
},
.probe = tegra_mipi_probe,
};

View File

@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
data->rc_dev = NULL;
rc_unregister_device(rdev);
rc_free_device(rdev);
}

View File

@@ -338,8 +338,8 @@ int cec_register_adapter(struct cec_adapter *adap,
res = cec_devnode_register(&adap->devnode, adap->owner);
if (res) {
#ifdef CONFIG_MEDIA_CEC_RC
/* Note: rc_unregister also calls rc_free */
rc_unregister_device(adap->rc);
rc_free_device(adap->rc);
adap->rc = NULL;
#endif
return res;

View File

@@ -235,6 +235,9 @@ static int pulse8_send_and_wait_once(struct pulse8 *pulse8,
{
int err;
if (!pulse8->serio)
return -ENODEV;
if (debug > 1)
dev_info(pulse8->dev, "transmit %s: %*ph\n",
pulse8_msgname(cmd[0]), cmd_len, cmd);
@@ -655,6 +658,10 @@ static void pulse8_disconnect(struct serio *serio)
{
struct pulse8 *pulse8 = serio_get_drvdata(serio);
cancel_delayed_work_sync(&pulse8->ping_eeprom_work);
mutex_lock(&pulse8->lock);
pulse8->serio = NULL;
mutex_unlock(&pulse8->lock);
cec_unregister_adapter(pulse8->adap);
serio_set_drvdata(serio, NULL);
serio_close(serio);

View File

@@ -291,20 +291,6 @@ void flexcop_device_exit(struct flexcop_device *fc)
}
EXPORT_SYMBOL(flexcop_device_exit);
static int flexcop_module_init(void)
{
info(DRIVER_NAME " loaded successfully");
return 0;
}
static void flexcop_module_cleanup(void)
{
info(DRIVER_NAME " unloaded successfully");
}
module_init(flexcop_module_init);
module_exit(flexcop_module_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@@ -417,19 +417,6 @@ int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
}
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
static int __init saa7146_vv_init_module(void)
{
return 0;
}
static void __exit saa7146_vv_cleanup_module(void)
{
}
module_init(saa7146_vv_init_module);
module_exit(saa7146_vv_cleanup_module);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");

View File

@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
void sms_ir_exit(struct smscore_device_t *coredev)
{
rc_unregister_device(coredev->ir.dev);
rc_free_device(coredev->ir.dev);
pr_debug("\n");
}

View File

@@ -40,6 +40,10 @@ static const struct uvc_format_desc uvc_fmts[] = {
.guid = UVC_GUID_FORMAT_M420,
.fcc = V4L2_PIX_FMT_M420,
},
{
.guid = UVC_GUID_FORMAT_P010,
.fcc = V4L2_PIX_FMT_P010,
},
{
.guid = UVC_GUID_FORMAT_UYVY,
.fcc = V4L2_PIX_FMT_UYVY,

View File

@@ -345,6 +345,7 @@ static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
return err;
}
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
/*
* Use common vm_area operations to track buffer refcount.
*/

View File

@@ -567,6 +567,7 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd,
case AU8522_COMPOSITE_CH1:
case AU8522_SVIDEO_CH13:
case AU8522_COMPOSITE_CH4_SIF:
case AU8522_COMPOSITE_CH4:
state->vid_input = input;
break;
default:

View File

@@ -2695,7 +2695,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff)
static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
{
s16 unit_khz_dds_val;
s32 unit_khz_dds_val;
u32 abs_offset_khz = abs(offset_khz);
u32 dds = state->cfg.pll->ifreq & 0x1ffffff;
u8 invert = !!(state->cfg.pll->ifreq & (1 << 25));
@@ -2716,7 +2716,7 @@ static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
dds = (1<<26) - dds;
} else {
ratio = 2;
unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal);
unit_khz_dds_val = 67108864 / state->cfg.pll->internal;
if (offset_khz < 0)
unit_khz_dds_val *= -1;

View File

@@ -78,7 +78,10 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
.addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2
};
m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
val = 0x11;
ret = regmap_write(dev->regmap, 0x03, val);
@@ -90,10 +93,18 @@ static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n",
dev->dt_addr, ret, reg, data);
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
return -EREMOTEIO;
}
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
dev->dt_addr, reg, data);
@@ -127,9 +138,14 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
}
};
m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
if (dev->chip_id == M88DS3103C_CHIP_ID) {
m88ds3103_update_bits(dev, 0x04, 0x10, 0x00);
val = 0x11;
} else {
m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
val = 0x12;
}
val = 0x12;
ret = regmap_write(dev->regmap, 0x03, val);
if (ret)
dev_dbg(&client->dev, "fail=%d\n", ret);
@@ -139,10 +155,18 @@ static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n",
dev->dt_addr, ret, reg);
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
return -EREMOTEIO;
}
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
dev->dt_addr, reg, b1[0]);
@@ -185,14 +209,25 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
switch (c->delivery_system) {
case SYS_DVBS:
ret = regmap_read(dev->regmap, 0xd1, &utmp);
if (ret)
goto err;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = regmap_read(dev->regmap, 0x0d, &utmp);
if (ret)
goto err;
if ((utmp & 0x07) == 0x07)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
if ((utmp & 0xf7) == 0xf7)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
} else {
ret = regmap_read(dev->regmap, 0xd1, &utmp);
if (ret)
goto err;
if ((utmp & 0x07) == 0x07)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
}
break;
case SYS_DVBS2:
ret = regmap_read(dev->regmap, 0x0d, &utmp);
@@ -201,8 +236,8 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
if ((utmp & 0x8f) == 0x8f)
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
break;
default:
dev_dbg(&client->dev, "invalid delivery_system\n");
@@ -371,6 +406,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -428,8 +464,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
reg15 = m88ds3103b_dt_read(dev, 0x15);
m88ds3103b_dt_write(dev, 0x05, 0x40);
m88ds3103b_dt_write(dev, 0x11, 0x08);
if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
m88ds3103b_dt_write(dev, 0x05, 0x40);
m88ds3103b_dt_write(dev, 0x11, 0x08);
}
if (big_symbol)
reg15 |= 0x02;
@@ -441,8 +479,10 @@ static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
usleep_range(5000, 5500);
m88ds3103b_dt_write(dev, 0x05, 0x00);
m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
m88ds3103b_dt_write(dev, 0x05, 0x00);
m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
}
usleep_range(5000, 5500);
@@ -583,12 +623,6 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
sm = N - 1;
/* Write to registers */
//reg15 &= 0x01;
//reg15 |= (pll_div_fb >> 8) & 0x01;
//reg16 = pll_div_fb & 0xFF;
reg1D &= ~0x03;
reg1D |= sm;
reg1D |= 0x80;
@@ -596,8 +630,11 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
reg1E = ((f3 << 4) + f2) & 0xFF;
reg1F = ((f1 << 4) + f0) & 0xFF;
m88ds3103b_dt_write(dev, 0x05, 0x40);
m88ds3103b_dt_write(dev, 0x11, 0x08);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
m88ds3103b_dt_write(dev, 0x05, 0x40);
m88ds3103b_dt_write(dev, 0x11, 0x08);
}
m88ds3103b_dt_write(dev, 0x1D, reg1D);
m88ds3103b_dt_write(dev, 0x1E, reg1E);
m88ds3103b_dt_write(dev, 0x1F, reg1F);
@@ -607,14 +644,88 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
usleep_range(5000, 5500);
m88ds3103b_dt_write(dev, 0x05, 0x00);
m88ds3103b_dt_write(dev, 0x11, 0x0A);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
m88ds3103b_dt_write(dev, 0x05, 0x00);
m88ds3103b_dt_write(dev, 0x11, 0x0A);
}
usleep_range(5000, 5500);
return 0;
}
static int mt_fe_dmd_ds3103c_set_ts_out_mode(struct dvb_frontend *fe, enum m88ds3103_ts_mode mode)
{
struct m88ds3103_dev *dev = fe->demodulator_priv;
unsigned int tmp, val = 0;
regmap_read(dev->regmap, 0x0b, &val);
val &= ~0x01;
regmap_write(dev->regmap, 0x0b, val);
regmap_read(dev->regmap, 0xfd, &tmp);
if (mode == M88DS3103_TS_PARALLEL) {
tmp &= ~0x01;
tmp &= ~0x04;
regmap_write(dev->regmap, 0xfa, 0x01);
regmap_write(dev->regmap, 0xf1, 0x60);
regmap_write(dev->regmap, 0xfa, 0x00);
} else if (mode == M88DS3103_TS_SERIAL) {
tmp &= ~0x01;
tmp |= 0x04;
} else {
tmp |= 0x01;
tmp &= ~0x04;
regmap_write(dev->regmap, 0xfa, 0x01);
regmap_write(dev->regmap, 0xf1, 0x60);
regmap_write(dev->regmap, 0xfa, 0x00);
}
if (dev->cfg->ts_clk_pol) {
tmp &= ~0xf8;
tmp |= 0x02;
} else {
tmp &= ~0xb8;
tmp |= 0x42;
}
tmp |= 0x80;
regmap_write(dev->regmap, 0xfd, tmp);
val = 0;
if (mode != M88DS3103_TS_SERIAL) {
tmp = M88DS3103_TS_CI;
val |= tmp & 0x03;
val |= (tmp << 2) & 0x0C;
val |= (tmp << 4) & 0x30;
val |= (tmp << 6) & 0xC0;
} else {
val = 0x00;
}
regmap_write(dev->regmap, 0x0a, val);
regmap_read(dev->regmap, 0x0b, &tmp);
tmp &= ~0x20;
tmp |= 0x01;
regmap_write(dev->regmap, 0x0b, tmp);
regmap_read(dev->regmap, 0x0c, &tmp);
regmap_write(dev->regmap, 0xf4, 0x01);
tmp &= ~0x80;
regmap_write(dev->regmap, 0x0c, tmp);
return 0;
}
static int m88ds3103_set_frontend(struct dvb_frontend *fe)
{
struct m88ds3103_dev *dev = fe->demodulator_priv;
@@ -627,6 +738,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
u16 u16tmp;
u32 tuner_frequency_khz, target_mclk, u32tmp;
s32 s32tmp;
unsigned int utmp;
static const struct reg_sequence reset_buf[] = {
{0x07, 0x80}, {0x07, 0x00}
};
@@ -646,9 +758,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
/* Clear TS */
ret = regmap_write(dev->regmap, 0xf5, 0x00);
/* Disable demod clock path */
if (dev->chip_id == M88RS6000_CHIP_ID) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
if (dev->chip_id == M88RS6000_CHIP_ID ||
dev->chip_id == M88DS3103C_CHIP_ID) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = regmap_read(dev->regmap, 0xb2, &u32tmp);
if (ret)
goto err;
@@ -688,7 +805,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
}
/* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */
if (dev->chip_id == M88RS6000_CHIP_ID) {
if (dev->chip_id == M88RS6000_CHIP_ID ||
dev->chip_id == M88DS3103C_CHIP_ID) {
if (c->symbol_rate > 45010000)
dev->mclk = 110250000;
else
@@ -699,7 +817,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
else
target_mclk = 144000000;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
m88ds3103b_select_mclk(dev);
m88ds3103b_set_mclk(dev, target_mclk / 1000);
}
@@ -772,6 +891,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->chip_id == M88RS6000_CHIP_ID) {
len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
init = m88rs6000_dvbs_init_reg_vals;
} else if (dev->chip_id == M88DS3103C_CHIP_ID) {
len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
init = m88ds3103c_dvbs_init_reg_vals;
} else {
len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
init = m88ds3103_dvbs_init_reg_vals;
@@ -781,6 +903,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->chip_id == M88RS6000_CHIP_ID) {
len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
init = m88rs6000_dvbs2_init_reg_vals;
} else if (dev->chip_id == M88DS3103C_CHIP_ID) {
len = ARRAY_SIZE(m88ds3103c_dvbs_init_reg_vals);
init = m88ds3103c_dvbs_init_reg_vals;
} else {
len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
init = m88ds3103_dvbs2_init_reg_vals;
@@ -799,7 +924,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
goto err;
}
if (dev->chip_id == M88RS6000_CHIP_ID) {
if (dev->chip_id == M88RS6000_CHIP_ID ||
dev->chip_id == M88DS3103C_CHIP_ID) {
if (c->delivery_system == SYS_DVBS2 &&
c->symbol_rate <= 5000000) {
ret = regmap_write(dev->regmap, 0xc0, 0x04);
@@ -812,11 +938,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
}
ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
if (ret)
goto err;
if (dev->chip_id != M88DS3103C_CHIP_ID) {
ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
if (ret)
goto err;
}
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
buf[0] = m88ds3103b_dt_read(dev, 0x15);
buf[1] = m88ds3103b_dt_read(dev, 0x16);
@@ -838,15 +967,22 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
m88ds3103b_dt_write(dev, 0x16, buf[1]);
regmap_read(dev->regmap, 0x30, &u32tmp);
u32tmp &= ~0x80;
regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
if (dev->chip_id == M88DS3103C_CHIP_ID) {
regmap_write(dev->regmap, 0x30, dev->config.agc_inv ? 0x18 : 0x08);
} else {
u32tmp &= ~0x80;
regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
}
}
ret = regmap_write(dev->regmap, 0xf1, 0x01);
if (ret)
goto err;
if (dev->chip_id != M88DS3103C_CHIP_ID) {
ret = regmap_write(dev->regmap, 0xf1, 0x01);
if (ret)
goto err;
}
if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) {
if (dev->chiptype != M88DS3103_CHIPTYPE_3103B &&
dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80);
if (ret)
goto err;
@@ -864,7 +1000,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
break;
case M88DS3103_TS_PARALLEL:
u8tmp = 0x02;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
u8tmp = 0x01;
u8tmp1 = 0x01;
}
@@ -881,10 +1018,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (dev->cfg->ts_clk_pol)
u8tmp |= 0x40;
/* TS mode */
ret = regmap_write(dev->regmap, 0xfd, u8tmp);
if (ret)
goto err;
if (dev->chiptype != M88DS3103_CHIPTYPE_3103C) {
/* TS mode */
ret = regmap_write(dev->regmap, 0xfd, u8tmp);
if (ret)
goto err;
}
switch (dev->cfg->ts_mode) {
case M88DS3103_TS_SERIAL:
@@ -918,7 +1057,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
if (ret)
goto err;
u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
u8tmp = 0xcb;
else
u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
ret = regmap_write(dev->regmap, 0xea, u8tmp);
if (ret)
goto err;
@@ -930,7 +1074,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
else
u8tmp = 0x06;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C)
m88ds3103b_set_mclk(dev, target_mclk / 1000);
ret = regmap_write(dev->regmap, 0xc3, 0x08);
@@ -949,9 +1094,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
u16tmp = DIV_ROUND_CLOSEST_ULL((((u64)c->symbol_rate << 15) + dev->mclk / 4),
(dev->mclk / 2));
else
u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
buf[0] = (u16tmp >> 0) & 0xff;
buf[1] = (u16tmp >> 8) & 0xff;
ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
if (ret)
goto err;
@@ -960,7 +1111,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
ret = m88ds3103_update_bits(dev, 0x30, 0x08, dev->cfg->agc_inv << 3);
} else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = m88ds3103_update_bits(dev, 0x08, 0x43, 0x43);
ret = m88ds3103_update_bits(dev, 0x30, 0x18, dev->cfg->agc_inv ? 0x18 : 0x08);
} else {
ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
}
if (ret)
goto err;
@@ -974,10 +1133,37 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
(c->delivery_system == SYS_DVBS) ? 0x10 : 0x0);
if (ret)
goto err;
}
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = m88ds3103_update_bits(dev, 0x76, 0x80, 0x00);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x22, 0x01, 0x01);
ret = m88ds3103_update_bits(dev, 0x23, 0x01, 0x00);
ret = m88ds3103_update_bits(dev, 0x24, 0x01, 0x00);
ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08);
if (ret)
goto err;
ret = regmap_read(dev->regmap, 0x08, &utmp);
if (ret)
goto err;
if (c->delivery_system == SYS_DVBS) {
utmp = (utmp & 0xfb) | 0x40;
regmap_write(dev->regmap, 0x08, utmp);
regmap_write(dev->regmap, 0xe0, 0xf8);
} else if (c->delivery_system == SYS_DVBS2) {
utmp = utmp | 0x44;
regmap_write(dev->regmap, 0x08, utmp);
} else {
utmp = utmp & 0xbb;
regmap_write(dev->regmap, 0x08, utmp);
regmap_write(dev->regmap, 0xe0, 0xf8);
}
}
dev_dbg(&client->dev, "carrier offset=%d\n",
@@ -986,8 +1172,12 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
/* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
usleep_range(1000, 1200);
buf[0] = (s32tmp >> 0) & 0xff;
buf[1] = (s32tmp >> 8) & 0xff;
ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
if (ret)
goto err;
@@ -1012,6 +1202,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1031,13 +1222,65 @@ static int m88ds3103_init(struct dvb_frontend *fe)
dev->warm = false;
/* wake up device from sleep */
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
ret = m88ds3103_update_bits(dev, 0x0b, 0x90, 0x80); /* set dt_addr */
m88ds3103b_dt_write(dev, 0x04, 0x01); /* reset */
m88ds3103b_dt_write(dev, 0x04, 0x00);
usleep_range(800, 1200);
ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
if (ret)
goto err;
m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */
m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x0b, 0x01, 0x01);
if (ret)
goto err;
/* global reset, global diseqc reset, global fec reset */
ret = regmap_write(dev->regmap, 0x07, 0x80);
if (ret)
goto err;
ret = regmap_write(dev->regmap, 0x07, 0x00);
if (ret)
goto err;
usleep_range(800, 1200);
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
if (ret)
goto err;
} else {
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
if (ret)
goto err;
}
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
m88ds3103b_dt_write(dev, 0x10, 0x01); /* wakeup */
m88ds3103b_dt_write(dev, 0x11, 0x01); /* wakeup */
m88ds3103b_dt_write(dev, 0x24, 0x04);
m88ds3103b_dt_write(dev, 0x84, 0x04);
m88ds3103b_dt_write(dev, 0x15, 0x6c);
usleep_range(800, 1200);
}
/* global reset, global diseqc reset, global fec reset */
ret = regmap_write(dev->regmap, 0x07, 0xe0);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
ret = regmap_write(dev->regmap, 0x07, 0x00);
if (ret)
goto err;
@@ -1051,20 +1294,14 @@ static int m88ds3103_init(struct dvb_frontend *fe)
if (utmp)
goto warm;
/* global reset, global diseqc reset, global fec reset */
ret = regmap_write(dev->regmap, 0x07, 0xe0);
if (ret)
goto err;
ret = regmap_write(dev->regmap, 0x07, 0x00);
if (ret)
goto err;
/* cold state - try to download firmware */
dev_info(&client->dev, "found a '%s' in cold state\n",
dev->fe.ops.info.name);
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
name = M88DS3103B_FIRMWARE;
else if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
name = M88DS3103C_FIRMWARE;
else if (dev->chip_id == M88RS6000_CHIP_ID)
name = M88RS6000_FIRMWARE;
else
@@ -1116,6 +1353,18 @@ static int m88ds3103_init(struct dvb_frontend *fe)
dev_info(&client->dev, "firmware version: %X.%X\n",
(utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
mt_fe_dmd_ds3103c_set_ts_out_mode(fe, dev->cfg->ts_mode);
ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1);
if (ret)
goto err;
ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv ? 0x10 : 0x00);
if (ret)
goto err;
}
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
m88ds3103b_dt_write(dev, 0x21, 0x92);
m88ds3103b_dt_write(dev, 0x15, 0x6C);
@@ -1139,6 +1388,7 @@ err_release_firmware:
release_firmware(firmware);
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1157,6 +1407,8 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
/* TS Hi-Z */
if (dev->chip_id == M88RS6000_CHIP_ID)
utmp = 0x29;
else if (dev->chip_id == M88DS3103C_CHIP_ID)
utmp = 0x0b;
else
utmp = 0x27;
ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00);
@@ -1167,6 +1419,17 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
if (ret)
goto err;
/* Internal tuner sleep */
if (dev->chip_id == M88DS3103C_CHIP_ID) {
ret = m88ds3103b_dt_write(dev, 0x10, 0x00);
if (ret)
goto err;
ret = m88ds3103b_dt_write(dev, 0x11, 0x00);
if (ret)
goto err;
}
ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
if (ret)
goto err;
@@ -1177,6 +1440,7 @@ static int m88ds3103_sleep(struct dvb_frontend *fe)
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1341,11 +1605,13 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe,
if (ret)
goto err;
// dev_dbg(&client->dev, "%s() 0x%X | 0x%X\n", __func__, buf[0], buf[1]);
c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1378,7 +1644,8 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
int ret;
unsigned int utmp, tone, reg_a1_mask;
dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode);
dev_dbg(&client->dev, "fe_sec_tone_mode=%s\n",
fe_sec_tone_mode == SEC_TONE_ON ? "ON" : "OFF");
if (!dev->warm) {
ret = -EAGAIN;
@@ -1413,6 +1680,7 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1463,6 +1731,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1542,6 +1811,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1621,6 +1891,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -1818,6 +2089,7 @@ static int m88ds3103_probe(struct i2c_client *client)
switch (dev->chip_id) {
case M88RS6000_CHIP_ID:
case M88DS3103_CHIP_ID:
case M88DS3103C_CHIP_ID:
break;
default:
ret = -ENODEV;
@@ -1847,7 +2119,8 @@ static int m88ds3103_probe(struct i2c_client *client)
/* 0x29 register is defined differently for m88rs6000. */
/* set internal tuner address to 0x21 */
if (dev->chip_id == M88RS6000_CHIP_ID)
if (dev->chip_id == M88RS6000_CHIP_ID ||
dev->chip_id == M88DS3103C_CHIP_ID)
utmp = 0x00;
ret = regmap_write(dev->regmap, 0x29, utmp);
@@ -1882,6 +2155,9 @@ static int m88ds3103_probe(struct i2c_client *client)
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B",
sizeof(dev->fe.ops.info.name));
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103C",
sizeof(dev->fe.ops.info.name));
else if (dev->chip_id == M88RS6000_CHIP_ID)
strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000",
sizeof(dev->fe.ops.info.name));
@@ -1894,15 +2170,22 @@ static int m88ds3103_probe(struct i2c_client *client)
pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
if (dev->chiptype == M88DS3103_CHIPTYPE_3103B ||
dev->chiptype == M88DS3103_CHIPTYPE_3103C) {
/* enable i2c repeater for tuner */
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
if (dev->chip_id == M88DS3103C_CHIP_ID)
m88ds3103_update_bits(dev, 0x04, 0x10, 0x10);
else
m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
/* get frontend address */
ret = regmap_read(dev->regmap, 0x29, &utmp);
if (ret)
goto err_del_adapters;
dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1;
if (dev->chiptype == M88DS3103_CHIPTYPE_3103C)
dev->dt_addr = 0x5c >> 1;
dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr);
dev->dt_client = i2c_new_dummy_device(client->adapter,
@@ -1941,6 +2224,7 @@ static const struct i2c_device_id m88ds3103_id_table[] = {
{"m88ds3103", M88DS3103_CHIPTYPE_3103},
{"m88rs6000", M88DS3103_CHIPTYPE_RS6000},
{"m88ds3103b", M88DS3103_CHIPTYPE_3103B},
{"m88ds3103c", M88DS3103_CHIPTYPE_3103C},
{}
};
MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);

View File

@@ -17,15 +17,18 @@
#include <linux/math64.h>
#define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw"
#define M88DS3103C_FIRMWARE "dvb-demod-m88ds3103c.fw"
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
#define M88RS6000_CHIP_ID 0x74
#define M88DS3103_CHIP_ID 0x70
#define M88DS3103_CHIP_ID 0x70
#define M88RS6000_CHIP_ID 0x74
#define M88DS3103C_CHIP_ID 0x71
#define M88DS3103_CHIPTYPE_3103 0
#define M88DS3103_CHIPTYPE_RS6000 1
#define M88DS3103_CHIPTYPE_3103B 2
#define M88DS3103_CHIPTYPE_3103C 3
struct m88ds3103_dev {
struct i2c_client *client;
@@ -399,4 +402,43 @@ static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
{0xb8, 0x00},
{0x29, 0x01},
};
static const struct m88ds3103_reg_val m88ds3103c_dvbs_init_reg_vals[] = {
{0x04, 0x10},
{0x8a, 0x01},
{0x16, 0xa7},
{0x30, 0x08},
{0x32, 0x32},
{0x33, 0x35},
{0x35, 0xff},
{0x4a, 0x80},
{0x4d, 0x93},
{0xae, 0x09},
{0x22, 0x01},
{0x23, 0x00},
{0x24, 0x00},
{0x27, 0x07},
{0x9c, 0x31},
{0x9d, 0xc1},
{0xcb, 0xf4},
{0xca, 0x00},
{0x7f, 0x04},
{0x78, 0x0c},
{0x85, 0x08},
{0x08, 0x47},
{0xf0, 0x03},
{0xfa, 0x01},
{0xf2, 0x00},
{0xfa, 0x00},
{0xe6, 0x00},
{0xe7, 0xf3},
{0x08, 0x43},
{0xe0, 0xf8},
{0x00, 0x00},
{0xbd, 0x82},
{0x80, 0xa8},
{0x81, 0xea},
{0xbe, 0xa1}
};
#endif

View File

@@ -40,7 +40,7 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
if (cmd->rlen) {
/* wait cmd execution terminate */
#define TIMEOUT 70
#define TIMEOUT 140
timeout = jiffies + msecs_to_jiffies(TIMEOUT);
while (!time_after(jiffies, timeout)) {
ret = i2c_master_recv(client, cmd->args, cmd->rlen);
@@ -54,6 +54,8 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
/* firmware ready? */
if ((cmd->args[0] >> 7) & 0x01)
break;
usleep_range(2500, 3500);
}
dev_dbg(&client->dev, "cmd execution took %d ms\n",
@@ -572,8 +574,8 @@ static int si2168_sleep(struct dvb_frontend *fe)
if (ret)
goto err;
/* Firmware later than B 4.0-11 loses warm state during sleep */
if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
/* Firmware B 4.0-11 and later lose warm state during sleep */
if (dev->version >= ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
dev->warm = false;
cmd_init(&cmd, "\x13", 1, 0);

View File

@@ -355,6 +355,7 @@ config VIDEO_MT9V111
config VIDEO_OG01A1B
tristate "OmniVision OG01A1B sensor support"
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OG01A1B camera.
@@ -489,6 +490,19 @@ config VIDEO_OV2685
To compile this driver as a module, choose M here: the
module will be called ov2685.
config VIDEO_OV2732
tristate "OmniVision OV2732 sensor support"
depends on OF
select MEDIA_CONTROLLER
select V4L2_CCI_I2C
select VIDEO_V4L2_SUBDEV_API
help
This is a Video4Linux2 sensor driver for the OmniVision
OV2732 camera.
To compile this driver as a module, choose M here: the
module will be called ov2732.
config VIDEO_OV2735
tristate "OmniVision OV2735 sensor support"
select V4L2_CCI_I2C
@@ -690,6 +704,7 @@ config VIDEO_OV8865
config VIDEO_OV9282
tristate "OmniVision OV9282 sensor support"
depends on OF_GPIO
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
OV9282 camera sensor.
@@ -788,6 +803,18 @@ config VIDEO_S5KJN1
To compile this driver as a module, choose M here: the
module will be called s5kjn1.
config VIDEO_T4KA3
tristate "Toshiba T4KA3 sensor support"
depends on ACPI || COMPILE_TEST
depends on GPIOLIB
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Toshiba T4KA3 8 MP
camera sensor.
To compile this driver as a module, choose M here: the
module will be called t4ka3.
config VIDEO_VD55G1
tristate "ST VD55G1 sensor support"
select V4L2_CCI_I2C
@@ -1724,8 +1751,8 @@ config VIDEO_DS90UB960
select V4L2_FWNODE
select VIDEO_V4L2_SUBDEV_API
help
Device driver for the Texas Instruments DS90UB960
FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer.
Device driver for the Texas Instruments DS90UB954, DS90UB960
FPD-Link III Deserializers and DS90UB9702 FPD-Link IV Deserializer.
config VIDEO_MAX96714
tristate "Maxim MAX96714 GMSL2 deserializer"

View File

@@ -97,6 +97,7 @@ obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
obj-$(CONFIG_VIDEO_OV2732) += ov2732.o
obj-$(CONFIG_VIDEO_OV2735) += ov2735.o
obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
@@ -139,6 +140,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
obj-$(CONFIG_VIDEO_T4KA3) += t4ka3.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
obj-$(CONFIG_VIDEO_TC358746) += tc358746.o
obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o

View File

@@ -2541,6 +2541,6 @@ module_i2c_driver(alvium_i2c_driver);
MODULE_DESCRIPTION("Allied Vision's Alvium Camera Driver");
MODULE_AUTHOR("Tommaso Merciai <tomm.merciai@gmail.com>");
MODULE_AUTHOR("Martin Hecht <martin.hecht@avnet.eu>");
MODULE_AUTHOR("Martin Hecht <mhecht73@gmail.com>");
MODULE_AUTHOR("Avnet Silica Software & Services EMEA");
MODULE_LICENSE("GPL");

View File

@@ -1094,6 +1094,9 @@ static int ar0521_probe(struct i2c_client *client)
/* Request optional reset pin (usually active low) and assert it */
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset_gpio))
return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
"failed to get reset gpio\n");
v4l2_i2c_subdev_init(&sensor->sd, client, &ar0521_subdev_ops);

View File

@@ -824,9 +824,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
op_lim_fr->min_pll_ip_clk_freq_hz));
min_op_pre_pll_clk_div =
max_t(u16, op_lim_fr->min_pre_pll_clk_div,
clk_div_even_up(
DIV_ROUND_UP(pll->ext_clk_freq_hz,
op_lim_fr->max_pll_ip_clk_freq_hz)));
DIV_ROUND_UP(pll->ext_clk_freq_hz,
op_lim_fr->max_pll_ip_clk_freq_hz));
dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);

View File

@@ -1652,10 +1652,14 @@ static int set_v4lstd(struct i2c_client *client)
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
u8 pal_n = 0;
u8 ntsc_j = 0;
u8 tmp_reg = 0;
/* First tests should be against specific std */
if (state->std == V4L2_STD_NTSC_M_JP) {
fmt = 0x2;
ntsc_j = 0x80;
} else if (state->std == V4L2_STD_NTSC_443) {
fmt = 0x3;
} else if (state->std == V4L2_STD_PAL_M) {
@@ -1663,6 +1667,7 @@ static int set_v4lstd(struct i2c_client *client)
fmt = 0x5;
} else if (state->std == V4L2_STD_PAL_N) {
fmt = 0x6;
pal_n = 0x40;
} else if (state->std == V4L2_STD_PAL_Nc) {
fmt = 0x7;
} else if (state->std == V4L2_STD_PAL_60) {
@@ -1689,10 +1694,30 @@ static int set_v4lstd(struct i2c_client *client)
/* Set format to NTSC-M */
cx25840_and_or(client, 0x400, ~0xf, 1);
/* Turn off LCOMB */
cx25840_and_or(client, 0x47b, ~6, 0);
cx25840_and_or(client, 0x47b, ~0x6, 0);
} else if (fmt == 0xc) { /* SECAM - Step 9c - toggle CKILLEN */
tmp_reg = cx25840_read(client, 0x401);
cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20);
cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00);
}
cx25840_and_or(client, 0x400, ~0xf, fmt);
cx25840_and_or(client, 0x403, ~0x3, pal_m);
if (fmt >= 4 && fmt < 8) {
tmp_reg = cx25840_read(client, 0x401);
cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x00 : 0x40); /* CAGCEN */
cx25840_and_or(client, 0x401, ~0x40, tmp_reg & 0x40 ? 0x40 : 0x00);
cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x00 : 0x20); /* CKILLEN */
cx25840_and_or(client, 0x401, ~0x20, tmp_reg & 0x20 ? 0x20 : 0x00);
}
if (pal_m)
cx25840_and_or(client, 0x403, ~0x3, pal_m);
else if (pal_n) /* cx25840 datasheet table 3-19 */
cx25840_and_or(client, 0x403, ~0x40, pal_n);
else if (ntsc_j) /* cx25840 datasheet table 3-19 */
cx25840_and_or(client, 0x403, ~0x80, ntsc_j);
if (is_cx23888(state))
cx23888_std_setup(client);
else

View File

@@ -372,63 +372,6 @@ static int ub913_set_routing(struct v4l2_subdev *sd,
return _ub913_set_routing(sd, state, routing);
}
static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
struct ub913_data *priv = sd_to_ub913(sd);
const struct v4l2_subdev_krouting *routing;
struct v4l2_mbus_frame_desc source_fd;
struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
int ret;
if (pad != UB913_PAD_SOURCE)
return -EINVAL;
ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
priv->source_sd_pad, &source_fd);
if (ret)
return ret;
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL;
state = v4l2_subdev_lock_and_get_active_state(sd);
routing = &state->routing;
for_each_active_route(routing, route) {
unsigned int i;
if (route->source_pad != pad)
continue;
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == route->sink_stream)
break;
}
if (i == source_fd.num_entries) {
dev_err(&priv->client->dev,
"Failed to find stream from source frame desc\n");
ret = -EPIPE;
goto out_unlock;
}
fd->entry[fd->num_entries].stream = route->source_stream;
fd->entry[fd->num_entries].flags = source_fd.entry[i].flags;
fd->entry[fd->num_entries].length = source_fd.entry[i].length;
fd->entry[fd->num_entries].pixelcode =
source_fd.entry[i].pixelcode;
fd->num_entries++;
}
out_unlock:
v4l2_subdev_unlock_state(state);
return ret;
}
static int ub913_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
@@ -544,7 +487,7 @@ static const struct v4l2_subdev_pad_ops ub913_pad_ops = {
.enable_streams = ub913_enable_streams,
.disable_streams = ub913_disable_streams,
.set_routing = ub913_set_routing,
.get_frame_desc = ub913_get_frame_desc,
.get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ub913_set_fmt,
};

View File

@@ -424,65 +424,6 @@ static int ub953_set_routing(struct v4l2_subdev *sd,
return _ub953_set_routing(sd, state, routing);
}
static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
struct ub953_data *priv = sd_to_ub953(sd);
struct v4l2_mbus_frame_desc source_fd;
struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
int ret;
if (pad != UB953_PAD_SOURCE)
return -EINVAL;
ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
priv->source_sd_pad, &source_fd);
if (ret)
return ret;
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
state = v4l2_subdev_lock_and_get_active_state(sd);
for_each_active_route(&state->routing, route) {
struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
unsigned int i;
if (route->source_pad != pad)
continue;
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == route->sink_stream) {
source_entry = &source_fd.entry[i];
break;
}
}
if (!source_entry) {
dev_err(&priv->client->dev,
"Failed to find stream from source frame desc\n");
ret = -EPIPE;
goto out_unlock;
}
fd->entry[fd->num_entries].stream = route->source_stream;
fd->entry[fd->num_entries].flags = source_entry->flags;
fd->entry[fd->num_entries].length = source_entry->length;
fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
fd->entry[fd->num_entries].bus.csi2.vc =
source_entry->bus.csi2.vc;
fd->entry[fd->num_entries].bus.csi2.dt =
source_entry->bus.csi2.dt;
fd->num_entries++;
}
out_unlock:
v4l2_subdev_unlock_state(state);
return ret;
}
static int ub953_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
@@ -694,7 +635,7 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = {
.enable_streams = ub953_enable_streams,
.disable_streams = ub953_disable_streams,
.set_routing = ub953_set_routing,
.get_frame_desc = ub953_get_frame_desc,
.get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ub953_set_fmt,
};

View File

@@ -396,6 +396,13 @@
#define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3)
#define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0)
#define UB954_IR_RX_ANA_STROBE_SET_CLK_DATA 0x08
#define UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY BIT(3)
#define UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(7)
#define UB954_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK GENMASK(2, 0)
#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(4, 6)
#define UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT 4
/* UB9702 Registers */
#define UB9702_SR_CSI_EXCLUSIVE_FWD2 0x3c
@@ -454,12 +461,23 @@
#define UB960_MAX_EQ_LEVEL 14
#define UB960_NUM_EQ_LEVELS (UB960_MAX_EQ_LEVEL - UB960_MIN_EQ_LEVEL + 1)
enum chip_type {
UB954,
UB960,
UB9702,
};
enum chip_family {
FAMILY_FPD3,
FAMILY_FPD4,
};
struct ub960_hw_data {
const char *model;
enum chip_type chip_type;
enum chip_family chip_family;
u8 num_rxports;
u8 num_txports;
bool is_ub9702;
bool is_fpdlink4;
};
enum ub960_rxport_mode {
@@ -982,6 +1000,10 @@ static int ub960_txport_select(struct ub960_data *priv, u8 nport)
lockdep_assert_held(&priv->reg_lock);
/* UB954 has only 1 CSI TX. Hence, no need to select */
if (priv->hw_data->chip_type == UB954)
return 0;
if (priv->reg_current.txport == nport)
return 0;
@@ -1406,10 +1428,11 @@ static int ub960_parse_dt_txport(struct ub960_data *priv,
priv->tx_link_freq[0] = vep.link_frequencies[0];
priv->tx_data_rate = priv->tx_link_freq[0] * 2;
if (priv->tx_data_rate != MHZ(1600) &&
priv->tx_data_rate != MHZ(1200) &&
priv->tx_data_rate != MHZ(800) &&
priv->tx_data_rate != MHZ(400)) {
if ((priv->tx_data_rate != MHZ(1600) &&
priv->tx_data_rate != MHZ(1200) &&
priv->tx_data_rate != MHZ(800) &&
priv->tx_data_rate != MHZ(400)) ||
(priv->hw_data->chip_type == UB954 && priv->tx_data_rate == MHZ(1200))) {
dev_err(dev, "tx%u: invalid 'link-frequencies' value\n", nport);
ret = -EINVAL;
goto err_free_vep;
@@ -1533,22 +1556,35 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv,
u8 clk_delay, data_delay;
int ret;
ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL);
if (ret)
return ret;
if (priv->hw_data->chip_type == UB954) {
ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, &v, NULL);
if (ret)
return ret;
clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL);
if (ret)
return ret;
data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
clk_delay = (v & UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
data_delay = (v & UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
} else {
ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL);
if (ret)
return ret;
clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL);
if (ret)
return ret;
data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ?
0 : UB960_MANUAL_STROBE_EXTRA_DELAY;
}
ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v, NULL);
if (ret)
return ret;
@@ -1569,26 +1605,49 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv,
static int ub960_rxport_set_strobe_pos(struct ub960_data *priv,
unsigned int nport, s8 strobe_pos)
{
u8 clk_delay, data_delay;
int ret = 0;
clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
if (priv->hw_data->chip_type == UB954) {
u8 clk_data_delay;
if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY;
else if (strobe_pos < 0)
clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
else if (strobe_pos > 0)
data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
clk_data_delay = UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY |
UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret);
if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
clk_data_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
clk_data_delay = (strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY) <<
UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT;
else if (strobe_pos < 0)
clk_data_delay = abs(strobe_pos) |
UB954_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
else if (strobe_pos > 0)
clk_data_delay = (strobe_pos |
UB954_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) <<
UB954_IR_RX_ANA_STROBE_SET_DATA_DELAY_SHIFT;
ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret);
ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB954_IR_RX_ANA_STROBE_SET_CLK_DATA, clk_data_delay, &ret);
} else {
u8 clk_delay, data_delay;
clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
if (strobe_pos < UB960_MIN_AEQ_STROBE_POS)
clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY;
else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS)
data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY;
else if (strobe_pos < 0)
clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
else if (strobe_pos > 0)
data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret);
ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret);
}
return ret;
}
@@ -1924,7 +1983,7 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv,
if (ret)
return ret;
if (priv->hw_data->is_ub9702) {
if (priv->hw_data->chip_type == UB9702) {
dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n",
nport, ((u64)v * HZ_PER_MHZ) >> 8);
} else {
@@ -2186,7 +2245,7 @@ static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport)
ser_pdata->port = nport;
ser_pdata->atr = priv->atr;
if (priv->hw_data->is_ub9702)
if (priv->hw_data->chip_type == UB9702)
ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub9702(priv, rxport);
else
ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub960(priv, rxport);
@@ -2352,7 +2411,7 @@ static int ub960_init_tx_ports(struct ub960_data *priv)
{
int ret;
if (priv->hw_data->is_ub9702)
if (priv->hw_data->chip_type == UB9702)
ret = ub960_init_tx_ports_ub9702(priv);
else
ret = ub960_init_tx_ports_ub960(priv);
@@ -3624,7 +3683,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv,
case RXPORT_MODE_CSI2_SYNC:
case RXPORT_MODE_CSI2_NONSYNC:
if (!priv->hw_data->is_ub9702) {
if (priv->hw_data->chip_type == UB960 ||
priv->hw_data->chip_type == UB954) {
/* Map all VCs from this port to the same VC */
ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP,
(vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) |
@@ -4158,33 +4218,40 @@ static int ub960_log_status(struct v4l2_subdev *sd)
dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1),
v & (u8)BIT(0));
ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
/*
* Frame counter, frame error counter, line counter and line error counter
* registers are marked as reserved in the UB954 datasheet. Hence restrict
* the following register reads only for UB960 and UB9702.
*/
if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB9702) {
ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
dev_info(dev, "\tframe counter %u\n", v16);
dev_info(dev, "\tframe counter %u\n", v16);
ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
dev_info(dev, "\tframe error counter %u\n", v16);
dev_info(dev, "\tframe error counter %u\n", v16);
ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
dev_info(dev, "\tline counter %u\n", v16);
dev_info(dev, "\tline counter %u\n", v16);
ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport),
&v16, NULL);
if (ret)
return ret;
dev_info(dev, "\tline error counter %u\n", v16);
dev_info(dev, "\tline error counter %u\n", v16);
}
}
for_each_rxport(priv, it) {
@@ -4250,7 +4317,7 @@ static int ub960_log_status(struct v4l2_subdev *sd)
dev_info(dev, "\tcsi_err_counter %u\n", v);
if (!priv->hw_data->is_ub9702) {
if (priv->hw_data->chip_type == UB960 || priv->hw_data->chip_type == UB954) {
ret = ub960_log_status_ub960_sp_eq(priv, nport);
if (ret)
return ret;
@@ -4408,7 +4475,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
return -EINVAL;
}
if (!priv->hw_data->is_fpdlink4 && cdr_mode == RXPORT_CDR_FPD4) {
if (priv->hw_data->chip_family != FAMILY_FPD4 && cdr_mode == RXPORT_CDR_FPD4) {
dev_err(dev, "rx%u: FPD-Link 4 CDR not supported\n", nport);
return -EINVAL;
}
@@ -5010,7 +5077,12 @@ static int ub960_enable_core_hw(struct ub960_data *priv)
if (ret)
goto err_pd_gpio;
if (priv->hw_data->is_ub9702)
/*
* UB954 REFCLK_FREQ is not synchronized, so multiple reads are recommended
* by the datasheet. However, a single read is practically seen to be
* sufficient and moreover it is only used for a debug print.
*/
if (priv->hw_data->chip_type == UB9702)
ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq,
NULL);
else
@@ -5029,7 +5101,7 @@ static int ub960_enable_core_hw(struct ub960_data *priv)
goto err_pd_gpio;
/* release GPIO lock */
if (priv->hw_data->is_ub9702) {
if (priv->hw_data->chip_type == UB9702) {
ret = ub960_update_bits(priv, UB960_SR_RESET,
UB960_SR_RESET_GPIO_LOCK_RELEASE,
UB960_SR_RESET_GPIO_LOCK_RELEASE,
@@ -5102,7 +5174,7 @@ static int ub960_probe(struct i2c_client *client)
if (ret)
goto err_free_ports;
if (priv->hw_data->is_ub9702)
if (priv->hw_data->chip_type == UB9702)
ret = ub960_init_rx_ports_ub9702(priv);
else
ret = ub960_init_rx_ports_ub960(priv);
@@ -5169,21 +5241,32 @@ static void ub960_remove(struct i2c_client *client)
mutex_destroy(&priv->reg_lock);
}
static const struct ub960_hw_data ds90ub954_hw = {
.model = "ub954",
.chip_type = UB954,
.chip_family = FAMILY_FPD3,
.num_rxports = 2,
.num_txports = 1,
};
static const struct ub960_hw_data ds90ub960_hw = {
.model = "ub960",
.chip_type = UB960,
.chip_family = FAMILY_FPD3,
.num_rxports = 4,
.num_txports = 2,
};
static const struct ub960_hw_data ds90ub9702_hw = {
.model = "ub9702",
.chip_type = UB9702,
.chip_family = FAMILY_FPD4,
.num_rxports = 4,
.num_txports = 2,
.is_ub9702 = true,
.is_fpdlink4 = true,
};
static const struct i2c_device_id ub960_id[] = {
{ "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw },
{ "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw },
{ "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw },
{}
@@ -5191,6 +5274,7 @@ static const struct i2c_device_id ub960_id[] = {
MODULE_DEVICE_TABLE(i2c, ub960_id);
static const struct of_device_id ub960_dt_ids[] = {
{ .compatible = "ti,ds90ub954-q1", .data = &ds90ub954_hw },
{ .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw },
{ .compatible = "ti,ds90ub9702-q1", .data = &ds90ub9702_hw },
{}

View File

@@ -551,6 +551,6 @@ static struct i2c_driver dw9768_i2c_driver = {
};
module_i2c_driver(dw9768_i2c_driver);
MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>");
MODULE_AUTHOR("Dongchun Zhu");
MODULE_DESCRIPTION("DW9768 VCM driver");
MODULE_LICENSE("GPL v2");

View File

@@ -1218,6 +1218,9 @@ static int imx219_probe(struct i2c_client *client)
/* Request optional enable pin */
imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(imx219->reset_gpio))
return dev_err_probe(dev, PTR_ERR(imx219->reset_gpio),
"failed to get reset gpio\n");
/*
* The sensor must be powered for imx219_identify_module()

View File

@@ -709,12 +709,16 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
v4l2_subdev_state_get_format(fh->state, 0);
struct v4l2_rect *try_crop;
mutex_lock(&imx258->mutex);
/* Initialize try_fmt */
try_fmt->width = supported_modes[0].width;
try_fmt->height = supported_modes[0].height;
try_fmt->code = imx258_get_format_code(imx258);
try_fmt->field = V4L2_FIELD_NONE;
mutex_unlock(&imx258->mutex);
/* Initialize try_crop */
try_crop = v4l2_subdev_state_get_crop(fh->state, 0);
try_crop->left = IMX258_PIXEL_ARRAY_LEFT;
@@ -839,7 +843,9 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
mutex_lock(&imx258->mutex);
code->code = imx258_get_format_code(imx258);
mutex_unlock(&imx258->mutex);
return 0;
}
@@ -849,10 +855,16 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_size_enum *fse)
{
struct imx258 *imx258 = to_imx258(sd);
u32 code;
if (fse->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
if (fse->code != imx258_get_format_code(imx258))
mutex_lock(&imx258->mutex);
code = imx258_get_format_code(imx258);
mutex_unlock(&imx258->mutex);
if (fse->code != code)
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;

View File

@@ -129,7 +129,8 @@
/* Master Mode Operation Control */
#define IMX283_REG_XMSTA CCI_REG8(0x3105)
#define IMX283_XMSTA BIT(0)
#define IMX283_XMSTA_START 0
#define IMX283_XMSTA_STOP BIT(0)
#define IMX283_REG_SYNCDRV CCI_REG8(0x3107)
#define IMX283_SYNCDRV_XHS_XVS (0xa0 | 0x02)
@@ -149,6 +150,9 @@
#define IMX283_REG_PLSTMG02 CCI_REG8(0x36aa)
#define IMX283_PLSTMG02_VAL 0x00
#define IMX283_REG_MIPI_CLK CCI_REG8(0x3a43)
#define IMX283_MIPI_CLK_NONCONTINUOUS BIT(0)
#define IMX283_REG_EBD_X_OUT_SIZE CCI_REG16_LE(0x3a54)
/* Test pattern generator */
@@ -565,6 +569,7 @@ struct imx283 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vflip;
bool mipi_clk_noncontinuous;
unsigned long link_freq_bitmap;
u16 hmax;
@@ -988,6 +993,7 @@ static int imx283_set_pad_format(struct v4l2_subdev *sd,
static int imx283_standby_cancel(struct imx283 *imx283)
{
unsigned int link_freq_idx;
u8 mipi_clk;
int ret = 0;
cci_write(imx283->cci, IMX283_REG_STANDBY,
@@ -1007,6 +1013,10 @@ static int imx283_standby_cancel(struct imx283 *imx283)
/* Enable PLL */
cci_write(imx283->cci, IMX283_REG_STBPL, IMX283_STBPL_NORMAL, &ret);
/* Configure MIPI clock mode */
mipi_clk = imx283->mipi_clk_noncontinuous ? IMX283_MIPI_CLK_NONCONTINUOUS : 0;
cci_write(imx283->cci, IMX283_REG_MIPI_CLK, mipi_clk, &ret);
/* Configure the MIPI link speed */
link_freq_idx = __ffs(imx283->link_freq_bitmap);
cci_multi_reg_write(imx283->cci, link_freq_reglist[link_freq_idx].regs,
@@ -1023,8 +1033,6 @@ static int imx283_standby_cancel(struct imx283 *imx283)
usleep_range(19000, 20000);
cci_write(imx283->cci, IMX283_REG_CLAMP, IMX283_CLPSQRST, &ret);
cci_write(imx283->cci, IMX283_REG_XMSTA, 0, &ret);
cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret);
return ret;
}
@@ -1117,6 +1125,10 @@ static int imx283_start_streaming(struct imx283 *imx283,
/* Apply customized values from controls (HMAX/VMAX/SHR) */
ret = __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler);
/* Start master mode */
cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_START, &ret);
cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret);
return ret;
}
@@ -1153,12 +1165,14 @@ static int imx283_disable_streams(struct v4l2_subdev *sd,
u64 streams_mask)
{
struct imx283 *imx283 = to_imx283(sd);
int ret;
int ret = 0;
if (pad != IMAGE_PAD)
return -EINVAL;
ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL);
cci_write(imx283->cci, IMX283_REG_XMSTA, IMX283_XMSTA_STOP, &ret);
cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STANDBY, &ret);
if (ret)
dev_err(imx283->dev, "Failed to stop stream\n");
@@ -1426,6 +1440,9 @@ static int imx283_parse_endpoint(struct imx283 *imx283)
goto done_endpoint_free;
}
imx283->mipi_clk_noncontinuous =
bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_frequencies, ARRAY_SIZE(link_frequencies),

View File

@@ -3,9 +3,13 @@
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
#include <media/v4l2-ctrls.h>
@@ -62,6 +66,9 @@
#define IMX355_EXT_CLK 19200000
#define IMX355_LINK_FREQ_INDEX 0
/* number of data lanes */
#define IMX355_DATA_LANES 4
struct imx355_reg {
u16 address;
u8 val;
@@ -125,6 +132,15 @@ struct imx355 {
* Protect access to sensor v4l2 controls.
*/
struct mutex mutex;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *supplies;
};
static const struct regulator_bulk_data imx355_supplies[] = {
{ .supply = "avdd" },
{ .supply = "dvdd" },
{ .supply = "dovdd" },
};
static const struct imx355_reg imx355_global_regs[] = {
@@ -1515,6 +1531,52 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = {
.open = imx355_open,
};
static int imx355_power_off(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct imx355 *imx355 = to_imx355(sd);
gpiod_set_value_cansleep(imx355->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(imx355_supplies), imx355->supplies);
clk_disable_unprepare(imx355->clk);
return 0;
}
static int imx355_power_on(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct imx355 *imx355 = to_imx355(sd);
int ret;
ret = clk_prepare_enable(imx355->clk);
if (ret)
return dev_err_probe(dev, ret, "failed to enable clocks");
ret = regulator_bulk_enable(ARRAY_SIZE(imx355_supplies),
imx355->supplies);
if (ret) {
dev_err_probe(dev, ret, "failed to enable regulators");
goto error_disable_clocks;
}
usleep_range(1000, 2000);
gpiod_set_value_cansleep(imx355->reset_gpio, 0);
usleep_range(10000, 11000);
return 0;
error_disable_clocks:
clk_disable_unprepare(imx355->clk);
return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(imx355_pm_ops, imx355_power_off,
imx355_power_on, NULL);
/* Initialize control handlers */
static int imx355_init_controls(struct imx355 *imx355)
{
@@ -1646,6 +1708,9 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev)
if (!cfg)
goto out_err;
if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES)
goto out_err;
ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
bus_cfg.nr_of_link_frequencies,
link_freq_menu_items,
@@ -1689,16 +1754,26 @@ static int imx355_probe(struct i2c_client *client)
"external clock %lu is not supported\n",
freq);
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops);
/* Check module identity */
ret = imx355_identify_module(imx355);
ret = devm_regulator_bulk_get_const(imx355->dev,
ARRAY_SIZE(imx355_supplies),
imx355_supplies,
&imx355->supplies);
if (ret) {
dev_err(imx355->dev, "failed to find sensor: %d", ret);
dev_err_probe(imx355->dev, ret, "could not get regulators");
goto error_probe;
}
imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(imx355->reset_gpio)) {
ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio),
"failed to get gpios");
goto error_probe;
}
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops);
imx355->hwcfg = imx355_get_hwcfg(imx355->dev);
if (!imx355->hwcfg) {
dev_err(imx355->dev, "failed to get hwcfg");
@@ -1706,13 +1781,24 @@ static int imx355_probe(struct i2c_client *client)
goto error_probe;
}
ret = imx355_power_on(imx355->dev);
if (ret)
goto error_probe;
/* Check module identity */
ret = imx355_identify_module(imx355);
if (ret) {
dev_err(imx355->dev, "failed to find sensor: %d", ret);
goto error_power_off;
}
/* Set default mode to max resolution */
imx355->cur_mode = &supported_modes[0];
ret = imx355_init_controls(imx355);
if (ret) {
dev_err(imx355->dev, "failed to init controls: %d", ret);
goto error_probe;
goto error_power_off;
}
/* Initialize subdev */
@@ -1752,6 +1838,9 @@ error_media_entity_runtime_pm:
error_handler_free:
v4l2_ctrl_handler_free(imx355->sd.ctrl_handler);
error_power_off:
imx355_power_off(imx355->dev);
error_probe:
mutex_destroy(&imx355->mutex);
@@ -1768,7 +1857,11 @@ static void imx355_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(imx355->dev);
pm_runtime_set_suspended(imx355->dev);
if (!pm_runtime_status_suspended(imx355->dev)) {
imx355_power_off(imx355->dev);
pm_runtime_set_suspended(imx355->dev);
}
mutex_destroy(&imx355->mutex);
}
@@ -1779,10 +1872,18 @@ static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = {
};
MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids);
static const struct of_device_id imx355_match_table[] = {
{ .compatible = "sony,imx355", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx355_match_table);
static struct i2c_driver imx355_i2c_driver = {
.driver = {
.name = "imx355",
.acpi_match_table = ACPI_PTR(imx355_acpi_ids),
.of_match_table = imx355_match_table,
.pm = &imx355_pm_ops,
},
.probe = imx355_probe,
.remove = imx355_remove,

View File

@@ -925,7 +925,7 @@ static int imx412_parse_hw_config(struct imx412 *imx412)
/* Request optional reset pin */
imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
GPIOD_OUT_LOW);
GPIOD_OUT_HIGH);
if (IS_ERR(imx412->reset_gpio)) {
dev_err(imx412->dev, "failed to get reset gpio %pe\n",
imx412->reset_gpio);
@@ -1037,7 +1037,11 @@ static int imx412_power_on(struct device *dev)
goto error_reset;
}
usleep_range(1000, 1200);
/*
* Certain Arducam IMX577 module variants require a longer reset settle
* time. Increasing the delay from 1ms to 10ms ensures reliable startup.
*/
usleep_range(10000, 12000);
return 0;

View File

@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
mutex_unlock(&ir->lock);
if (rc == -ENODEV) {
rc_unregister_device(ir->rc);
rc_free_device(ir->rc);
ir->rc = NULL;
return;
}
@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
i2c_unregister_device(ir->tx_c);
rc_unregister_device(ir->rc);
rc_free_device(ir->rc);
}
static const struct i2c_device_id ir_kbd_id[] = {

View File

@@ -1205,7 +1205,7 @@ static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
{
struct max9286_priv *priv = gpiochip_get_data(chip);
return priv->gpio_state & BIT(offset);
return !!(priv->gpio_state & BIT(offset));
}
static int max9286_register_gpio(struct max9286_priv *priv)

View File

@@ -368,6 +368,10 @@
* Data Structures
*/
struct mt9m114_model_info {
bool state_standby_polling;
};
enum mt9m114_format_flag {
MT9M114_FMT_FLAG_PARALLEL = BIT(0),
MT9M114_FMT_FLAG_CSI2 = BIT(1),
@@ -417,6 +421,8 @@ struct mt9m114 {
struct v4l2_ctrl *tpg[4];
} ifp;
const struct mt9m114_model_info *info;
};
/* -----------------------------------------------------------------------------
@@ -2284,9 +2290,11 @@ static int mt9m114_power_on(struct mt9m114 *sensor)
* reaches the standby mode (either initiated manually above in
* parallel mode, or automatically after reset in MIPI mode).
*/
ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY);
if (ret < 0)
goto error_clock;
if (sensor->info->state_standby_polling) {
ret = mt9m114_poll_state(sensor, MT9M114_SYS_STATE_STANDBY);
if (ret < 0)
goto error_clock;
}
return 0;
@@ -2532,6 +2540,10 @@ static int mt9m114_probe(struct i2c_client *client)
if (ret < 0)
return ret;
sensor->info = device_get_match_data(dev);
if (!sensor->info)
return -ENODEV;
/* Acquire clocks, GPIOs and regulators. */
sensor->clk = devm_v4l2_sensor_clk_get(dev, NULL);
if (IS_ERR(sensor->clk)) {
@@ -2646,15 +2658,24 @@ static void mt9m114_remove(struct i2c_client *client)
pm_runtime_set_suspended(dev);
}
static const struct mt9m114_model_info mt9m114_models_default = {
.state_standby_polling = true,
};
static const struct mt9m114_model_info mt9m114_models_aptina = {
.state_standby_polling = false,
};
static const struct of_device_id mt9m114_of_ids[] = {
{ .compatible = "onnn,mt9m114" },
{ /* sentinel */ },
{ .compatible = "onnn,mt9m114", .data = &mt9m114_models_default },
{ .compatible = "aptina,mi1040", .data = &mt9m114_models_aptina },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mt9m114_of_ids);
static const struct acpi_device_id mt9m114_acpi_ids[] = {
{ "INT33F0" },
{ /* sentinel */ },
{ "INT33F0", (kernel_ulong_t)&mt9m114_models_default },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_ids);

View File

@@ -1183,6 +1183,10 @@ static int mt9p031_probe(struct i2c_client *client)
mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(mt9p031->reset)) {
ret = PTR_ERR(mt9p031->reset);
goto done;
}
ret = mt9p031_clk_setup(mt9p031);
if (ret)

File diff suppressed because it is too large Load Diff

View File

@@ -998,6 +998,6 @@ static struct i2c_driver ov02a10_i2c_driver = {
};
module_i2c_driver(ov02a10_i2c_driver);
MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>");
MODULE_AUTHOR("Dongchun Zhu");
MODULE_DESCRIPTION("OmniVision OV02A10 sensor driver");
MODULE_LICENSE("GPL v2");

View File

@@ -8,12 +8,12 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#define OV08D10_SCLK 144000000ULL
#define OV08D10_XVCLK_19_2 19200000
#define OV08D10_ROWCLK 36000
#define OV08D10_DATA_LANES 2
#define OV08D10_RGB_DEPTH 10
@@ -77,8 +77,13 @@ struct ov08d10_reg_list {
const struct ov08d10_reg *regs;
};
static const u32 ov08d10_xvclk_freqs[] = {
19200000,
24000000
};
struct ov08d10_link_freq_config {
const struct ov08d10_reg_list reg_list;
const struct ov08d10_reg_list reg_list[ARRAY_SIZE(ov08d10_xvclk_freqs)];
};
struct ov08d10_mode {
@@ -88,13 +93,13 @@ struct ov08d10_mode {
/* Frame height in pixels */
u32 height;
/* Horizontal timining size */
/* Horizontal timing size */
u32 hts;
/* Default vertical timining size */
/* Default vertical timing size */
u32 vts_def;
/* Min vertical timining size */
/* Min vertical timing size */
u32 vts_min;
/* Link frequency needed for this resolution */
@@ -107,8 +112,8 @@ struct ov08d10_mode {
u8 data_lanes;
};
/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes */
static const struct ov08d10_reg mipi_data_rate_720mbps[] = {
/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 19.2 MHz */
static const struct ov08d10_reg mipi_data_rate_720mbps_19_2[] = {
{0xfd, 0x00},
{0x11, 0x2a},
{0x14, 0x43},
@@ -118,8 +123,8 @@ static const struct ov08d10_reg mipi_data_rate_720mbps[] = {
{0xb7, 0x02}
};
/* 1632x1224 needs 360Mbps/lane, 2 lanes */
static const struct ov08d10_reg mipi_data_rate_360mbps[] = {
/* 1632x1224 needs 360Mbps/lane, 2 lanes - 19.2 MHz */
static const struct ov08d10_reg mipi_data_rate_360mbps_19_2[] = {
{0xfd, 0x00},
{0x1a, 0x04},
{0x1b, 0xe1},
@@ -131,6 +136,30 @@ static const struct ov08d10_reg mipi_data_rate_360mbps[] = {
{0xb7, 0x02}
};
/* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes - 24 MHz */
static const struct ov08d10_reg mipi_data_rate_720mbps_24_0[] = {
{0xfd, 0x00},
{0x11, 0x2a},
{0x14, 0x43},
{0x1a, 0x04},
{0x1b, 0xb4},
{0x1e, 0x13},
{0xb7, 0x02}
};
/* 1632x1224 needs 360Mbps/lane, 2 lanes - 24 MHz */
static const struct ov08d10_reg mipi_data_rate_360mbps_24_0[] = {
{0xfd, 0x00},
{0x1a, 0x04},
{0x1b, 0xb4},
{0x1d, 0x00},
{0x1c, 0x19},
{0x11, 0x2a},
{0x14, 0x54},
{0x1e, 0x13},
{0xb7, 0x02}
};
static const struct ov08d10_reg lane_2_mode_3280x2460[] = {
/* 3280x2460 resolution */
{0xfd, 0x01},
@@ -217,7 +246,7 @@ static const struct ov08d10_reg lane_2_mode_3280x2460[] = {
{0x9a, 0x30},
{0xa8, 0x02},
{0xfd, 0x02},
{0xa1, 0x01},
{0xa1, 0x00},
{0xa2, 0x09},
{0xa3, 0x9c},
{0xa5, 0x00},
@@ -335,7 +364,7 @@ static const struct ov08d10_reg lane_2_mode_3264x2448[] = {
{0x9a, 0x30},
{0xa8, 0x02},
{0xfd, 0x02},
{0xa1, 0x09},
{0xa1, 0x08},
{0xa2, 0x09},
{0xa3, 0x90},
{0xa5, 0x08},
@@ -381,7 +410,6 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = {
{0x07, 0x05},
{0x21, 0x02},
{0x24, 0x30},
{0x33, 0x03},
{0x31, 0x06},
{0x33, 0x03},
{0x01, 0x03},
@@ -467,7 +495,7 @@ static const struct ov08d10_reg lane_2_mode_1632x1224[] = {
{0xaa, 0xd0},
{0xab, 0x06},
{0xac, 0x68},
{0xa1, 0x09},
{0xa1, 0x04},
{0xa2, 0x04},
{0xa3, 0xc8},
{0xa5, 0x04},
@@ -514,9 +542,18 @@ static const char * const ov08d10_test_pattern_menu[] = {
"Standard Color Bar",
};
static const char *const ov08d10_supply_names[] = {
"dovdd", /* Digital I/O power */
"avdd", /* Analog power */
"dvdd", /* Digital core power */
};
struct ov08d10 {
struct device *dev;
struct clk *clk;
struct reset_control *reset;
struct regulator_bulk_data supplies[ARRAY_SIZE(ov08d10_supply_names)];
u8 xvclk_index;
struct v4l2_subdev sd;
struct media_pad pad;
@@ -534,7 +571,7 @@ struct ov08d10 {
/* Current mode */
const struct ov08d10_mode *cur_mode;
/* To serialize asynchronus callbacks */
/* To serialize asynchronous callbacks */
struct mutex mutex;
/* lanes index */
@@ -557,17 +594,29 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = {
},
{{
.reg_list = {
{
.num_of_regs =
ARRAY_SIZE(mipi_data_rate_720mbps),
.regs = mipi_data_rate_720mbps,
}
ARRAY_SIZE(mipi_data_rate_720mbps_19_2),
.regs = mipi_data_rate_720mbps_19_2,
},
{
.num_of_regs =
ARRAY_SIZE(mipi_data_rate_720mbps_24_0),
.regs = mipi_data_rate_720mbps_24_0,
}}
},
{
.reg_list = {
{
.num_of_regs =
ARRAY_SIZE(mipi_data_rate_360mbps),
.regs = mipi_data_rate_360mbps,
}
ARRAY_SIZE(mipi_data_rate_360mbps_19_2),
.regs = mipi_data_rate_360mbps_19_2,
},
{
.num_of_regs =
ARRAY_SIZE(mipi_data_rate_360mbps_24_0),
.regs = mipi_data_rate_360mbps_24_0,
}}
}},
{{
.width = 3280,
@@ -613,8 +662,8 @@ static const struct ov08d10_lane_cfg lane_cfg_2 = {
static u32 ov08d10_get_format_code(struct ov08d10 *ov08d10)
{
static const u32 codes[2][2] = {
{ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10},
{ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10},
{ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10 },
{ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10 },
};
return codes[ov08d10->vflip->val][ov08d10->hflip->val];
@@ -665,7 +714,7 @@ static int ov08d10_write_reg_list(struct ov08d10 *ov08d10,
r_list->regs[i].val);
if (ret) {
dev_err_ratelimited(ov08d10->dev,
"failed to write reg 0x%2.2x. error = %d",
"failed to write reg 0x%2.2x. error = %d\n",
r_list->regs[i].address, ret);
return ret;
}
@@ -864,7 +913,7 @@ static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl)
exposure_max);
}
/* V4L2 controls values will be applied only when power is already up */
/* V4L2 control values will be applied only when power is already up */
if (!pm_runtime_get_if_in_use(ov08d10->dev))
return 0;
@@ -1020,37 +1069,38 @@ static int ov08d10_start_streaming(struct ov08d10 *ov08d10)
link_freq_index = ov08d10->cur_mode->link_freq_index;
reg_list =
&ov08d10->priv_lane->link_freq_configs[link_freq_index].reg_list;
&ov08d10->priv_lane->link_freq_configs[link_freq_index]
.reg_list[ov08d10->xvclk_index];
/* soft reset */
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to reset sensor");
dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to reset sensor");
dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
usleep_range(3000, 4000);
ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to reset sensor");
dev_err(ov08d10->dev, "failed to reset sensor\n");
return ret;
}
/* update sensor setting */
ret = ov08d10_write_reg_list(ov08d10, reg_list);
if (ret) {
dev_err(ov08d10->dev, "failed to set plls");
dev_err(ov08d10->dev, "failed to set plls\n");
return ret;
}
reg_list = &ov08d10->cur_mode->reg_list;
ret = ov08d10_write_reg_list(ov08d10, reg_list);
if (ret) {
dev_err(ov08d10->dev, "failed to set mode");
dev_err(ov08d10->dev, "failed to set mode\n");
return ret;
}
@@ -1077,19 +1127,19 @@ static void ov08d10_stop_streaming(struct ov08d10 *ov08d10)
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to stop streaming");
dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT,
OV08D10_MODE_STANDBY);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to stop streaming");
dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to stop streaming");
dev_err(ov08d10->dev, "failed to stop streaming\n");
return;
}
}
@@ -1266,6 +1316,56 @@ static const struct v4l2_subdev_internal_ops ov08d10_internal_ops = {
.open = ov08d10_open,
};
static int ov08d10_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov08d10 *ov08d10 = to_ov08d10(sd);
reset_control_assert(ov08d10->reset);
regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies),
ov08d10->supplies);
clk_disable_unprepare(ov08d10->clk);
return 0;
}
static int ov08d10_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov08d10 *ov08d10 = to_ov08d10(sd);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ov08d10->supplies),
ov08d10->supplies);
if (ret < 0) {
dev_err(dev, "failed to enable regulators: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(ov08d10->clk);
if (ret < 0) {
regulator_bulk_disable(ARRAY_SIZE(ov08d10->supplies),
ov08d10->supplies);
dev_err(dev, "failed to enable imaging clock: %d\n", ret);
return ret;
}
if (ov08d10->reset) {
/* Delay from DVDD stable to sensor XSHUTDN pull up: 5ms */
fsleep(5 * USEC_PER_MSEC);
reset_control_deassert(ov08d10->reset);
/* Delay from XSHUTDN pull up to SCCB start: 8ms */
fsleep(8 * USEC_PER_MSEC);
}
return 0;
}
static int ov08d10_identify_module(struct ov08d10 *ov08d10)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
@@ -1325,7 +1425,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
/* Get number of data lanes */
if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
dev_err(dev, "number of CSI2 data lanes %d is not supported",
dev_err(dev, "number of CSI2 data lanes %d is not supported\n",
bus_cfg.bus.mipi_csi2.num_data_lanes);
ret = -EINVAL;
goto check_hwcfg_error;
@@ -1337,7 +1437,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
ov08d10->modes_size = ov08d10_modes_num(ov08d10);
if (!bus_cfg.nr_of_link_frequencies) {
dev_err(dev, "no link frequencies defined");
dev_err(dev, "no link frequencies defined\n");
ret = -EINVAL;
goto check_hwcfg_error;
}
@@ -1350,7 +1450,7 @@ static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10)
}
if (j == bus_cfg.nr_of_link_frequencies) {
dev_err(dev, "no link frequency %lld supported",
dev_err(dev, "no link frequency %lld supported\n",
ov08d10->priv_lane->link_freq_menu[i]);
ret = -EINVAL;
goto check_hwcfg_error;
@@ -1372,6 +1472,10 @@ static void ov08d10_remove(struct i2c_client *client)
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(ov08d10->dev);
if (!pm_runtime_status_suspended(ov08d10->dev)) {
ov08d10_power_off(ov08d10->dev);
pm_runtime_set_suspended(ov08d10->dev);
}
mutex_destroy(&ov08d10->mutex);
}
@@ -1379,6 +1483,7 @@ static int ov08d10_probe(struct i2c_client *client)
{
struct ov08d10 *ov08d10;
unsigned long freq;
unsigned int i;
int ret;
ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL);
@@ -1393,30 +1498,56 @@ static int ov08d10_probe(struct i2c_client *client)
"failed to get clock\n");
freq = clk_get_rate(ov08d10->clk);
if (freq != OV08D10_XVCLK_19_2)
dev_warn(ov08d10->dev,
"external clock rate %lu is not supported\n", freq);
for (i = 0; i < ARRAY_SIZE(ov08d10_xvclk_freqs); i++) {
if (freq == ov08d10_xvclk_freqs[i])
break;
}
if (i >= ARRAY_SIZE(ov08d10_xvclk_freqs))
return dev_err_probe(ov08d10->dev, -EINVAL,
"external clock rate %lu is not supported\n",
freq);
ov08d10->xvclk_index = i;
ret = ov08d10_get_hwcfg(ov08d10);
if (ret) {
dev_err(ov08d10->dev, "failed to get HW configuration: %d",
dev_err(ov08d10->dev, "failed to get HW configuration: %d\n",
ret);
return ret;
}
ov08d10->reset = devm_reset_control_get_optional_exclusive(ov08d10->dev, NULL);
if (IS_ERR(ov08d10->reset))
return dev_err_probe(ov08d10->dev, PTR_ERR(ov08d10->reset),
"failed to get reset\n");
reset_control_assert(ov08d10->reset);
for (i = 0; i < ARRAY_SIZE(ov08d10_supply_names); i++)
ov08d10->supplies[i].supply = ov08d10_supply_names[i];
ret = devm_regulator_bulk_get(ov08d10->dev,
ARRAY_SIZE(ov08d10->supplies),
ov08d10->supplies);
if (ret)
return dev_err_probe(ov08d10->dev, ret,
"failed to get regulators\n");
v4l2_i2c_subdev_init(&ov08d10->sd, client, &ov08d10_subdev_ops);
ret = ov08d10_power_on(ov08d10->dev);
if (ret)
return dev_err_probe(ov08d10->dev, ret, "failed to power on\n");
ret = ov08d10_identify_module(ov08d10);
if (ret) {
dev_err(ov08d10->dev, "failed to find sensor: %d", ret);
return ret;
dev_err(ov08d10->dev, "failed to find sensor: %d\n", ret);
goto probe_error_power_off;
}
mutex_init(&ov08d10->mutex);
ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0];
ret = ov08d10_init_controls(ov08d10);
if (ret) {
dev_err(ov08d10->dev, "failed to init controls: %d", ret);
dev_err(ov08d10->dev, "failed to init controls: %d\n", ret);
goto probe_error_v4l2_ctrl_handler_free;
}
@@ -1426,37 +1557,42 @@ static int ov08d10_probe(struct i2c_client *client)
ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad);
if (ret) {
dev_err(ov08d10->dev, "failed to init entity pads: %d", ret);
dev_err(ov08d10->dev, "failed to init entity pads: %d\n", ret);
goto probe_error_v4l2_ctrl_handler_free;
}
pm_runtime_set_active(ov08d10->dev);
pm_runtime_enable(ov08d10->dev);
ret = v4l2_async_register_subdev_sensor(&ov08d10->sd);
if (ret < 0) {
dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d",
dev_err(ov08d10->dev, "failed to register V4L2 subdev: %d\n",
ret);
goto probe_error_media_entity_cleanup;
}
/*
* Device is already turned on by i2c-core with ACPI domain PM.
* Enable runtime PM and turn off the device.
*/
pm_runtime_set_active(ov08d10->dev);
pm_runtime_enable(ov08d10->dev);
pm_runtime_idle(ov08d10->dev);
return 0;
probe_error_media_entity_cleanup:
pm_runtime_disable(ov08d10->dev);
pm_runtime_set_suspended(ov08d10->dev);
media_entity_cleanup(&ov08d10->sd.entity);
probe_error_v4l2_ctrl_handler_free:
v4l2_ctrl_handler_free(ov08d10->sd.ctrl_handler);
mutex_destroy(&ov08d10->mutex);
probe_error_power_off:
ov08d10_power_off(ov08d10->dev);
return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(ov08d10_pm_ops,
ov08d10_power_off, ov08d10_power_on, NULL);
#ifdef CONFIG_ACPI
static const struct acpi_device_id ov08d10_acpi_ids[] = {
{ "OVTI08D1" },
@@ -1466,10 +1602,18 @@ static const struct acpi_device_id ov08d10_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ov08d10_acpi_ids);
#endif
static const struct of_device_id ov08d10_of_match[] = {
{ .compatible = "ovti,ov08d10" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ov08d10_of_match);
static struct i2c_driver ov08d10_i2c_driver = {
.driver = {
.name = "ov08d10",
.pm = pm_ptr(&ov08d10_pm_ops),
.acpi_match_table = ACPI_PTR(ov08d10_acpi_ids),
.of_match_table = ov08d10_of_match,
},
.probe = ov08d10_probe,
.remove = ov08d10_remove,

790
drivers/media/i2c/ov2732.c Normal file
View File

@@ -0,0 +1,790 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ov2732 driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
* Copyright (C) 2025-2026 Walter Werner Schneider <contact@schnwalter.eu>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#define OV2732_LANES 2
#define OV2732_BITS_PER_SAMPLE 10
#define OV2732_LINK_FREQ_DEFAULT 360000000
#define OV2732_XVCLK_FREQ 24000000
#define OV2732_PIXEL_RATE \
(OV2732_LINK_FREQ_DEFAULT * 2 * OV2732_LANES / OV2732_BITS_PER_SAMPLE)
#define OV2732_NATIVE_WIDTH 1920U
#define OV2732_NATIVE_HEIGHT 1080U
/* Delay from power up to the first SCCB transaction. */
#define OV2732_POWER_UP_DELAY_CYCLES 8192
/* Delay from the last SCCB transaction to power down. */
#define OV2732_POWER_DOWN_DELAY_CYCLES 512
#define OV2732_DELAY_US(cycles) \
(DIV_ROUND_UP((cycles), OV2732_XVCLK_FREQ / USEC_PER_SEC))
#define OV2732_REG_CHIP_ID CCI_REG24(0x300a)
#define OV2732_CHIP_ID 0x002732
#define OV2732_REG_MODE_SELECT CCI_REG8(0x0100)
#define OV2732_MODE_STANDBY 0x00
#define OV2732_MODE_STREAMING 0x01
#define OV2732_REG_ANALOGUE_GAIN CCI_REG16(0x3508)
#define OV2732_REG_DIGITAL_GAIN CCI_REG16(0x350a)
#define OV2732_REG_EXPOSURE CCI_REG24(0x3500)
#define OV2732_REG_HTS CCI_REG16(0x380c)
#define OV2732_REG_VTS CCI_REG16(0x380e)
#define OV2732_ANALOGUE_GAIN_MIN 0x80
/* Max analogue gain is documented as 0x3fff, but it overflows after 0x3ff. */
#define OV2732_ANALOGUE_GAIN_MAX 0x3ff
#define OV2732_ANALOGUE_GAIN_DEFAULT 0x80
#define OV2732_DIGITAL_GAIN_MIN 0x00
#define OV2732_DIGITAL_GAIN_MAX 0x3fff
#define OV2732_DIGITAL_GAIN_DEFAULT 0x0400
#define OV2732_EXPOSURE_DEFAULT 0x40
#define OV2732_EXPOSURE_MIN 0x10
#define OV2732_EXPOSURE_OFFSET 4
#define OV2732_HBLANK_DEFAULT 0x0068
#define OV2732_VTS_MAX 0x7fff
#define OV2732_REG_TEST_PATTERN CCI_REG8(0x5080)
#define OV2732_TEST_PATTERN_DISABLE 0x00
#define OV2732_TEST_PATTERN_BAR1 0x80
#define OV2732_TEST_PATTERN_BAR2 0x84
#define OV2732_TEST_PATTERN_BAR3 0x88
#define OV2732_TEST_PATTERN_BAR4 0x8c
#define OV2732_TEST_PATTERN_BAR5 0xC0
#define OV2732_TEST_PATTERN_RANDOM 0x81
#define OV2732_TEST_PATTERN_SQUARES_C 0x82
#define OV2732_TEST_PATTERN_SQUARES_BW 0x92
static const char * const ov2732_supply_names[] = {
"avdd", /* Analog power */
"dovdd", /* Digital I/O power */
"dvdd", /* Digital core power */
};
static const struct cci_reg_sequence ov2732_common_regs[] = {
/* PLL control, reset all registers. */
{ CCI_REG8(0x0103), 0x01 },
/* Analog control. */
{ CCI_REG8(0x3600), 0x55 },
{ CCI_REG8(0x3601), 0x52 },
{ CCI_REG8(0x3612), 0xb5 },
{ CCI_REG8(0x3613), 0xb3 },
{ CCI_REG8(0x3616), 0x83 },
{ CCI_REG8(0x3621), 0x00 },
{ CCI_REG8(0x3624), 0x06 },
{ CCI_REG8(0x3642), 0x88 },
{ CCI_REG8(0x3660), 0x00 },
{ CCI_REG8(0x3661), 0x00 },
{ CCI_REG8(0x366a), 0x64 },
{ CCI_REG8(0x366c), 0x00 },
{ CCI_REG8(0x366e), 0xff },
{ CCI_REG8(0x366f), 0xff },
{ CCI_REG8(0x3677), 0x11 },
{ CCI_REG8(0x3678), 0x11 },
{ CCI_REG8(0x3679), 0x0c },
{ CCI_REG8(0x3680), 0xff },
{ CCI_REG8(0x3681), 0x16 },
{ CCI_REG8(0x3682), 0x16 },
{ CCI_REG8(0x3683), 0x90 },
{ CCI_REG8(0x3684), 0x90 },
/* ADC sync control. */
{ CCI_REG8(0x4503), 0x00 },
{ CCI_REG8(0x4508), 0x14 },
{ CCI_REG8(0x450a), 0x00 },
{ CCI_REG8(0x450b), 0x40 },
/* ISP control, enable: WIN, DPC & ISP. */
{ CCI_REG8(0x5000), 0xa1 },
};
struct ov2732_mode {
u32 width;
u32 height;
u32 vts;
};
static const struct ov2732_mode supported_modes[] = {
{
.width = 1920,
.height = 1080,
.vts = 1184,
},
};
struct ov2732 {
struct device *dev;
struct regmap *regmap;
struct media_pad pad;
struct v4l2_subdev sd;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *exposure;
struct clk *xvclk;
u32 xvclk_freq;
struct gpio_desc *powerdown_gpio;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(ov2732_supply_names)];
};
#define to_ov2732(_sd) container_of(_sd, struct ov2732, sd)
static const s64 link_freq_menu_items[] = {
OV2732_LINK_FREQ_DEFAULT,
};
static const char * const ov2732_test_pattern_menu[] = {
"Disabled",
"Vertical Color Bar Type 1",
"Vertical Color Bar Type 2",
"Vertical Color Bar Type 3",
"Vertical Color Bar Type 4",
"Vertical Color Bar Type 5",
"Random",
"Color Squares",
"Black and White Squares",
};
static const int ov2732_test_pattern_val[] = {
OV2732_TEST_PATTERN_DISABLE,
OV2732_TEST_PATTERN_BAR1,
OV2732_TEST_PATTERN_BAR2,
OV2732_TEST_PATTERN_BAR3,
OV2732_TEST_PATTERN_BAR4,
OV2732_TEST_PATTERN_BAR5,
OV2732_TEST_PATTERN_RANDOM,
OV2732_TEST_PATTERN_SQUARES_C,
OV2732_TEST_PATTERN_SQUARES_BW,
};
static int ov2732_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov2732 *ov2732 = to_ov2732(sd);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ov2732_supply_names),
ov2732->supplies);
if (ret) {
dev_err(dev, "failed to enable regulators\n");
return ret;
}
ret = clk_prepare_enable(ov2732->xvclk);
if (ret) {
dev_err(dev, "failed to enable clock\n");
goto reg_off;
}
/* Wait 10ms before power up, as per datasheet. */
fsleep(10 * USEC_PER_MSEC);
gpiod_set_value_cansleep(ov2732->reset_gpio, 0);
gpiod_set_value_cansleep(ov2732->powerdown_gpio, 0);
/* Datasheet requires an 8192 cycles wait, but that isn't enough. */
fsleep(OV2732_DELAY_US(OV2732_POWER_UP_DELAY_CYCLES * 2));
return 0;
reg_off:
regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names),
ov2732->supplies);
return ret;
}
static int ov2732_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov2732 *ov2732 = to_ov2732(sd);
clk_disable_unprepare(ov2732->xvclk);
/* Wait for 512 cycles as per datasheet. */
fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES));
gpiod_set_value_cansleep(ov2732->powerdown_gpio, 1);
gpiod_set_value_cansleep(ov2732->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ov2732_supply_names),
ov2732->supplies);
return 0;
}
static int ov2732_identify_chip(struct ov2732 *ov2732)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
int ret;
u64 val;
ret = cci_read(ov2732->regmap, OV2732_REG_CHIP_ID, &val, NULL);
if (ret)
return dev_err_probe(&client->dev, ret,
"failed to read chip id\n");
if (val != OV2732_CHIP_ID)
return dev_err_probe(&client->dev, -ENODEV,
"chip id mismatch: %x!=%llx\n",
OV2732_CHIP_ID, val);
return 0;
}
static int ov2732_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
if (code->index != 0)
return -EINVAL;
code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
return 0;
}
static int ov2732_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
fse->max_width = fse->min_width;
fse->min_height = supported_modes[fse->index].height;
fse->max_height = fse->min_height;
return 0;
}
static int ov2732_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct ov2732 *ov2732 = to_ov2732(sd);
const struct ov2732_mode *mode;
s64 vblank_def;
int ret;
mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes),
width, height,
fmt->format.width, fmt->format.height);
fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
fmt->format.width = mode->width;
fmt->format.height = mode->height;
fmt->format.field = V4L2_FIELD_NONE;
fmt->format.colorspace = V4L2_COLORSPACE_RAW;
fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601;
fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
*v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
vblank_def = mode->vts - mode->height;
ret = __v4l2_ctrl_modify_range(ov2732->vblank, vblank_def,
OV2732_VTS_MAX - mode->height, 1,
vblank_def);
return ret;
}
static int ov2732_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
sel->r = *v4l2_subdev_state_get_crop(state, 0);
return 0;
case V4L2_SEL_TGT_NATIVE_SIZE:
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.top = 0;
sel->r.left = 0;
sel->r.width = OV2732_NATIVE_WIDTH;
sel->r.height = OV2732_NATIVE_HEIGHT;
return 0;
}
return -EINVAL;
}
static int ov2732_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
{
struct ov2732 *ov2732 = to_ov2732(sd);
struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
int ret;
ret = pm_runtime_resume_and_get(&client->dev);
if (ret < 0)
return ret;
/* Set stream off register for PLL changes. */
ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
OV2732_MODE_STANDBY, NULL);
if (ret)
goto err_put_autosuspend;
/* Send all registers that are common to all modes */
ret = cci_multi_reg_write(ov2732->regmap, ov2732_common_regs,
ARRAY_SIZE(ov2732_common_regs), NULL);
if (ret) {
dev_err(&client->dev, "failed to init registers\n");
goto err_put_autosuspend;
}
/* Apply customized values from user */
ret = __v4l2_ctrl_handler_setup(ov2732->sd.ctrl_handler);
if (ret)
goto err_put_autosuspend;
/* Set stream on register */
ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
OV2732_MODE_STREAMING, NULL);
if (ret)
goto err_put_autosuspend;
return 0;
err_put_autosuspend:
pm_runtime_put_autosuspend(&client->dev);
return ret;
}
static int ov2732_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
{
struct ov2732 *ov2732 = to_ov2732(sd);
struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
int ret;
/* set stream off register */
ret = cci_write(ov2732->regmap, OV2732_REG_MODE_SELECT,
OV2732_MODE_STANDBY, NULL);
if (ret)
dev_err(&client->dev, "%s failed to set stream\n", __func__);
/* Wait for 512 cycles as per datasheet. */
fsleep(OV2732_DELAY_US(OV2732_POWER_DOWN_DELAY_CYCLES));
pm_runtime_put_autosuspend(&client->dev);
return ret;
}
static const struct v4l2_subdev_video_ops ov2732_video_ops = {
.s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops ov2732_pad_ops = {
.enum_mbus_code = ov2732_enum_mbus_code,
.enum_frame_size = ov2732_enum_frame_size,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ov2732_set_fmt,
.get_selection = ov2732_get_selection,
.enable_streams = ov2732_enable_streams,
.disable_streams = ov2732_disable_streams,
};
static const struct v4l2_subdev_ops ov2732_subdev_ops = {
.video = &ov2732_video_ops,
.pad = &ov2732_pad_ops,
};
static int ov2732_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_TRY,
.format = {
.width = 1920,
.height = 1080,
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.colorspace = V4L2_COLORSPACE_RAW,
.field = V4L2_FIELD_NONE,
.quantization = V4L2_QUANTIZATION_FULL_RANGE,
.xfer_func = V4L2_XFER_FUNC_NONE,
.ycbcr_enc = V4L2_YCBCR_ENC_601,
}
};
return ov2732_set_fmt(sd, sd_state, &fmt);
}
static const struct v4l2_subdev_internal_ops ov2732_internal_ops = {
.init_state = ov2732_init_state,
};
static int ov2732_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov2732 *ov2732 =
container_of(ctrl->handler, struct ov2732, ctrl_handler);
struct i2c_client *client = v4l2_get_subdevdata(&ov2732->sd);
const struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
int ret = 0;
state = v4l2_subdev_get_locked_active_state(&ov2732->sd);
format = v4l2_subdev_state_get_format(state, 0);
if (ctrl->id == V4L2_CID_VBLANK) {
int exposure_max, exposure_def;
exposure_max = format->height + ctrl->val -
OV2732_EXPOSURE_OFFSET;
exposure_def = exposure_max;
ret = __v4l2_ctrl_modify_range(ov2732->exposure,
OV2732_EXPOSURE_MIN,
exposure_max,
ov2732->exposure->step,
exposure_def);
if (ret)
return ret;
}
if (pm_runtime_get_if_in_use(&client->dev) == 0)
return 0;
switch (ctrl->id) {
case V4L2_CID_ANALOGUE_GAIN:
cci_write(ov2732->regmap, OV2732_REG_ANALOGUE_GAIN,
ctrl->val, &ret);
break;
case V4L2_CID_DIGITAL_GAIN:
cci_write(ov2732->regmap, OV2732_REG_DIGITAL_GAIN,
ctrl->val, &ret);
break;
case V4L2_CID_EXPOSURE:
/* Lowest 4 bits are fraction bits. */
cci_write(ov2732->regmap, OV2732_REG_EXPOSURE,
(u32)ctrl->val << 4, &ret);
break;
case V4L2_CID_VBLANK:
cci_write(ov2732->regmap, OV2732_REG_VTS,
format->height + ctrl->val, &ret);
break;
case V4L2_CID_TEST_PATTERN:
cci_write(ov2732->regmap, OV2732_REG_TEST_PATTERN,
ov2732_test_pattern_val[ctrl->val], &ret);
break;
default:
dev_info(&client->dev,
"ctrl(id:0x%x,val:0x%x) is not handled\n",
ctrl->id, ctrl->val);
ret = -EINVAL;
break;
}
pm_runtime_put(&client->dev);
return ret;
};
static const struct v4l2_ctrl_ops ov2732_ctrl_ops = {
.s_ctrl = ov2732_set_ctrl,
};
static int ov2732_init_controls(struct ov2732 *ov2732)
{
const struct ov2732_mode *mode = &supported_modes[0];
struct v4l2_ctrl_handler *handler;
struct v4l2_ctrl *ctrl;
struct v4l2_fwnode_device_properties props;
s64 exposure_max, vblank_def, vblank_max;
int ret;
handler = &ov2732->ctrl_handler;
ret = v4l2_ctrl_handler_init(handler, 10);
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
OV2732_PIXEL_RATE, OV2732_PIXEL_RATE,
1, OV2732_PIXEL_RATE);
ctrl = v4l2_ctrl_new_int_menu(handler, &ov2732_ctrl_ops,
V4L2_CID_LINK_FREQ, 0, 0,
link_freq_menu_items);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
ov2732->hblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
V4L2_CID_HBLANK,
OV2732_HBLANK_DEFAULT,
OV2732_HBLANK_DEFAULT,
1, OV2732_HBLANK_DEFAULT);
if (ov2732->hblank)
ov2732->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
vblank_def = mode->vts - mode->height;
vblank_max = OV2732_VTS_MAX - mode->height;
ov2732->vblank = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
V4L2_CID_VBLANK,
vblank_def, vblank_max,
1, vblank_def);
exposure_max = mode->vts - OV2732_EXPOSURE_OFFSET;
ov2732->exposure = v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops,
V4L2_CID_EXPOSURE,
OV2732_EXPOSURE_MIN, exposure_max,
1, OV2732_EXPOSURE_DEFAULT);
v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
OV2732_ANALOGUE_GAIN_MIN, OV2732_ANALOGUE_GAIN_MAX,
1, OV2732_ANALOGUE_GAIN_DEFAULT);
v4l2_ctrl_new_std(handler, &ov2732_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
OV2732_DIGITAL_GAIN_MIN, OV2732_DIGITAL_GAIN_MAX,
1, OV2732_DIGITAL_GAIN_DEFAULT);
v4l2_ctrl_new_std_menu_items(handler, &ov2732_ctrl_ops,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov2732_test_pattern_menu) - 1,
0, 0, ov2732_test_pattern_menu);
if (handler->error) {
ret = handler->error;
dev_err_probe(ov2732->dev, ret, "Control init failed\n");
goto err_handler_free;
}
ret = v4l2_fwnode_device_parse(ov2732->dev, &props);
if (ret)
goto err_handler_free;
ret = v4l2_ctrl_new_fwnode_properties(handler, &ov2732_ctrl_ops, &props);
if (ret)
goto err_handler_free;
ov2732->sd.ctrl_handler = handler;
return 0;
err_handler_free:
v4l2_ctrl_handler_free(handler);
return ret;
}
static int ov2632_probe_dt(struct ov2732 *ov2732)
{
struct fwnode_handle *ep;
struct fwnode_handle *fwnode = dev_fwnode(ov2732->dev);
struct v4l2_fwnode_endpoint bus_cfg = {
.bus_type = V4L2_MBUS_CSI2_DPHY,
};
int ret;
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) {
dev_err_probe(ov2732->dev, -EINVAL, "could not parse endpoint\n");
goto err_probe_dt;
}
if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV2732_LANES) {
dev_err(ov2732->dev, "only a 2-lane CSI2 config is supported\n");
ret = -EINVAL;
}
err_probe_dt:
v4l2_fwnode_endpoint_free(&bus_cfg);
return ret;
}
static int ov2732_get_regulators(struct ov2732 *ov2732)
{
for (unsigned int i = 0; i < ARRAY_SIZE(ov2732_supply_names); i++)
ov2732->supplies[i].supply = ov2732_supply_names[i];
return devm_regulator_bulk_get(ov2732->dev,
ARRAY_SIZE(ov2732_supply_names),
ov2732->supplies);
}
static int ov2732_probe(struct i2c_client *client)
{
struct ov2732 *ov2732;
int ret;
ov2732 = devm_kzalloc(&client->dev, sizeof(*ov2732), GFP_KERNEL);
if (!ov2732)
return -ENOMEM;
ov2732->dev = &client->dev;
ret = ov2632_probe_dt(ov2732);
if (ret)
return ret;
ov2732->xvclk = devm_v4l2_sensor_clk_get(ov2732->dev, NULL);
if (IS_ERR(ov2732->xvclk))
return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->xvclk),
"failed to get xvclk\n");
ov2732->xvclk_freq = clk_get_rate(ov2732->xvclk);
if (ov2732->xvclk_freq != OV2732_XVCLK_FREQ)
return dev_err_probe(ov2732->dev, -EINVAL,
"xvclk frequency not supported: %dHz\n",
ov2732->xvclk_freq);
ov2732->powerdown_gpio = devm_gpiod_get_optional(ov2732->dev,
"powerdown",
GPIOD_OUT_HIGH);
ov2732->reset_gpio = devm_gpiod_get_optional(ov2732->dev, "reset",
GPIOD_OUT_HIGH);
ov2732->regmap = devm_cci_regmap_init_i2c(client, 16);
if (IS_ERR(ov2732->regmap))
return dev_err_probe(ov2732->dev, PTR_ERR(ov2732->regmap),
"failed to init CCI\n");
ret = ov2732_get_regulators(ov2732);
if (ret)
return dev_err_probe(ov2732->dev, ret,
"failed to get regulators\n");
v4l2_i2c_subdev_init(&ov2732->sd, client, &ov2732_subdev_ops);
/* Device must be powered on for ov2732_identify_chip(). */
ret = ov2732_power_on(ov2732->dev);
if (ret)
return ret;
pm_runtime_set_active(ov2732->dev);
pm_runtime_enable(ov2732->dev);
ret = ov2732_identify_chip(ov2732);
if (ret)
goto err_power_off;
ret = ov2732_init_controls(ov2732);
if (ret)
goto err_power_off;
/* Initialize subdev */
ov2732->sd.internal_ops = &ov2732_internal_ops;
ov2732->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ov2732->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
/* Initialize source pad */
ov2732->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&ov2732->sd.entity, 1, &ov2732->pad);
if (ret) {
dev_err_probe(ov2732->dev, ret, "failed to init entity pads\n");
goto error_handler_free;
}
ov2732->sd.state_lock = ov2732->ctrl_handler.lock;
ret = v4l2_subdev_init_finalize(&ov2732->sd);
if (ret < 0) {
dev_err_probe(ov2732->dev, ret, "subdev init error\n");
goto err_media_entity;
}
ret = v4l2_async_register_subdev_sensor(&ov2732->sd);
if (ret < 0) {
dev_err_probe(ov2732->dev, ret,
"failed to register sensor sub-device\n");
goto err_subdev_cleanup;
}
pm_runtime_set_autosuspend_delay(ov2732->dev, 1000);
pm_runtime_use_autosuspend(ov2732->dev);
pm_runtime_idle(ov2732->dev);
return 0;
err_subdev_cleanup:
v4l2_subdev_cleanup(&ov2732->sd);
err_media_entity:
media_entity_cleanup(&ov2732->sd.entity);
error_handler_free:
v4l2_ctrl_handler_free(&ov2732->ctrl_handler);
err_power_off:
pm_runtime_disable(ov2732->dev);
pm_runtime_set_suspended(ov2732->dev);
ov2732_power_off(ov2732->dev);
return ret;
}
static void ov2732_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov2732 *ov2732 = to_ov2732(sd);
v4l2_async_unregister_subdev(sd);
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(&ov2732->ctrl_handler);
pm_runtime_disable(ov2732->dev);
if (!pm_runtime_status_suspended(ov2732->dev)) {
ov2732_power_off(ov2732->dev);
pm_runtime_set_suspended(ov2732->dev);
}
}
static const struct of_device_id ov2732_of_match[] = {
{ .compatible = "ovti,ov2732", },
{ },
};
MODULE_DEVICE_TABLE(of, ov2732_of_match);
static DEFINE_RUNTIME_DEV_PM_OPS(ov2732_pm_ops, ov2732_power_off,
ov2732_power_on, NULL);
static struct i2c_driver ov2732_i2c_driver = {
.driver = {
.name = "ov2732",
.of_match_table = ov2732_of_match,
.pm = pm_sleep_ptr(&ov2732_pm_ops),
},
.probe = ov2732_probe,
.remove = ov2732_remove,
};
module_i2c_driver(ov2732_i2c_driver);
MODULE_DESCRIPTION("OmniVision ov2732 sensor driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Walter Werner Schneider <contact@schnwalter.eu>");

View File

@@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_AUTOGAIN:
/* Non-zero turns on AGC by clearing bit 1.*/
return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1),
ctrl->val ? 0 : BIT(1), NULL);
ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1),
ctrl->val ? 0 : BIT(1), NULL);
break;
case V4L2_CID_EXPOSURE_AUTO:
/*
* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
* clearing bit 0.
*/
return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0),
ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL);
ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0),
ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL);
break;
case V4L2_CID_ANALOGUE_GAIN:
/* 10 bits of gain, 2 in the high register. */
return cci_write(sensor->regmap, OV5647_REG_GAIN,
ctrl->val & 0x3ff, NULL);
ret = cci_write(sensor->regmap, OV5647_REG_GAIN,
ctrl->val & 0x3ff, NULL);
break;
case V4L2_CID_EXPOSURE:
/*

View File

@@ -1181,17 +1181,26 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
if (!fwnode)
return -ENXIO;
ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0,
FWNODE_GRAPH_ENDPOINT_NEXT);
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
return ret;
ov5675->xvclk = devm_v4l2_sensor_clk_get(dev, NULL);
if (IS_ERR(ov5675->xvclk))
return dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
"failed to get xvclk: %pe\n",
ov5675->xvclk);
if (IS_ERR(ov5675->xvclk)) {
ret = dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
"failed to get xvclk\n");
goto check_hwcfg_error;
}
xvclk_rate = clk_get_rate(ov5675->xvclk);
if (xvclk_rate != OV5675_XVCLK_19_2) {
dev_err(dev, "external clock rate %u is unsupported",
xvclk_rate);
return -EINVAL;
ret = -EINVAL;
goto check_hwcfg_error;
}
ov5675->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -1199,7 +1208,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
if (IS_ERR(ov5675->reset_gpio)) {
ret = PTR_ERR(ov5675->reset_gpio);
dev_err(dev, "failed to get reset-gpios: %d\n", ret);
return ret;
goto check_hwcfg_error;
}
for (i = 0; i < OV5675_NUM_SUPPLIES; i++)
@@ -1208,16 +1217,7 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
ret = devm_regulator_bulk_get(dev, OV5675_NUM_SUPPLIES,
ov5675->supplies);
if (ret)
return ret;
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
return -ENXIO;
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
return ret;
goto check_hwcfg_error;
if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV5675_DATA_LANES) {
dev_err(dev, "number of CSI2 data lanes %d is not supported",

View File

@@ -1887,12 +1887,14 @@ static const struct v4l2_ctrl_ops ov8856_ctrl_ops = {
static int ov8856_init_controls(struct ov8856 *ov8856)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
struct v4l2_fwnode_device_properties props;
struct v4l2_ctrl_handler *ctrl_hdlr;
s64 exposure_max, h_blank;
int ret;
ctrl_hdlr = &ov8856->ctrl_handler;
ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
@@ -1951,12 +1953,27 @@ static int ov8856_init_controls(struct ov8856 *ov8856)
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
if (ctrl_hdlr->error)
return ctrl_hdlr->error;
if (ctrl_hdlr->error) {
ret = ctrl_hdlr->error;
goto err_ctrl_handler_free;
}
ret = v4l2_fwnode_device_parse(&client->dev, &props);
if (ret)
goto err_ctrl_handler_free;
ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov8856_ctrl_ops,
&props);
if (ret)
goto err_ctrl_handler_free;
ov8856->sd.ctrl_handler = ctrl_hdlr;
return 0;
err_ctrl_handler_free:
v4l2_ctrl_handler_free(ctrl_hdlr);
return ret;
}
static void ov8856_update_pad_format(struct ov8856 *ov8856,

File diff suppressed because it is too large Load Diff

1064
drivers/media/i2c/t4ka3.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1802,6 +1802,9 @@ static int vgxy61_probe(struct i2c_client *client)
sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(sensor->reset_gpio))
return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
"failed to get reset gpio\n");
ret = vgxy61_get_regulators(sensor);
if (ret) {

View File

@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv)
if (btv->remote == NULL)
return;
bttv_ir_stop(btv);
rc_unregister_device(btv->remote->dev);
bttv_ir_stop(btv);
rc_free_device(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
}

View File

@@ -2443,6 +2443,9 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_VIEWCAST_460E:
case CX23885_BOARD_AVERMEDIA_CE310B:
case CX23885_BOARD_AVERMEDIA_H789C:
if (dev->disable_analog)
break;
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);

View File

@@ -48,6 +48,11 @@ static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
static unsigned int disable_analog_video[8] = { 0, 0, 0, 0, 0, 0, 0, 0};
static int disable_analog_argc;
module_param_array(disable_analog_video, int, &disable_analog_argc, 0644);
MODULE_PARM_DESC(disable_analog_video, "disable analog video for card type");
static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");
@@ -924,6 +929,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885;
}
for (i = 0; i < disable_analog_argc; i++) {
if (disable_analog_video[i] == dev->board) {
pr_warn("Disabling analog for board %d\n", dev->board);
dev->disable_analog = 1;
}
}
/* If the user specific a clk freq override, apply it */
if (cx23885_boards[dev->board].clk_freq > 0)
dev->clk_freq = cx23885_boards[dev->board].clk_freq;
@@ -1043,7 +1055,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_gpio_enable(dev, 0x300, 0);
}
if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO &&
!dev->disable_analog) {
if (cx23885_video_register(dev) < 0) {
pr_err("%s() Failed to register analog video adapters on VID_A\n",
__func__);

View File

@@ -2373,7 +2373,8 @@ static int dvb_register(struct cx23885_tsport *port)
port->i2c_client_tuner = client_tuner;
/* we only attach tuner for analog on the 888 version */
if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) {
if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB &&
!dev->disable_analog) {
pr_info("%s(): QUADHD_DVB analog setup\n",
__func__);
dev->ts1.analog_fe.tuner_priv = client_tuner;
@@ -2466,7 +2467,8 @@ static int dvb_register(struct cx23885_tsport *port)
port->i2c_client_tuner = client_tuner;
/* we only attach tuner for analog on the 888 version */
if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) {
if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC &&
!dev->disable_analog) {
pr_info("%s(): QUADHD_ATSC analog setup\n",
__func__);
dev->ts1.analog_fe.tuner_priv = client_tuner;

View File

@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
if (dev->kernel_ir == NULL)
return;
rc_unregister_device(dev->kernel_ir->rc);
rc_free_device(dev->kernel_ir->rc);
kfree(dev->kernel_ir->phys);
kfree(dev->kernel_ir->name);
kfree(dev->kernel_ir);

View File

@@ -404,6 +404,7 @@ struct cx23885_dev {
unsigned char radio_addr;
struct v4l2_subdev *sd_cx25840;
struct work_struct cx25840_work;
unsigned int disable_analog;
/* Infrared */
struct v4l2_subdev *sd_ir;

View File

@@ -397,7 +397,7 @@ static int dsp_buffer_free(struct cx25821_audio_dev *chip)
{
struct cx25821_riscmem *risc = &chip->buf->risc;
BUG_ON(!chip->dma_size);
WARN_ON(!chip->dma_size);
dprintk(2, "Freeing buffer\n");
cx25821_alsa_dma_unmap(chip);
@@ -509,8 +509,8 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream,
chip->num_periods = params_periods(hw_params);
chip->dma_size = chip->period_size * params_periods(hw_params);
BUG_ON(!chip->dma_size);
BUG_ON(chip->num_periods & (chip->num_periods - 1));
WARN_ON(!chip->dma_size);
WARN_ON(chip->num_periods & (chip->num_periods - 1));
buf = kzalloc_obj(*buf);
if (NULL == buf)

View File

@@ -509,8 +509,9 @@ int cx88_ir_fini(struct cx88_core *core)
if (!ir)
return 0;
cx88_ir_stop(core);
rc_unregister_device(ir->dev);
cx88_ir_stop(core);
rc_free_device(ir->dev);
kfree(ir);
/* done */

View File

@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
static void dm1105_ir_exit(struct dm1105_dev *dm1105)
{
rc_unregister_device(dm1105->ir.dev);
rc_free_device(dm1105->ir.dev);
}
static int dm1105_hw_init(struct dm1105_dev *dev)

View File

@@ -91,6 +91,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
/* Omnivision OV2680 */
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
/* Omnivision OV5675 */
IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
/* Sony IMX471 */
@@ -104,6 +106,13 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
* without reporting a rotation of 180° in neither the SSDB nor the _PLD.
*/
static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9340"),
},
.driver_data = "OVTI02C1",
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -111,6 +120,13 @@ 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 14 9440"),
},
.driver_data = "OVTI02C1",
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),

View File

@@ -686,7 +686,7 @@ out_free_irq:
out_ipu6_rpm_put:
pm_runtime_put_sync(&isp->psys->auxdev.dev);
out_ipu6_bus_del_devices:
if (isp->psys) {
if (!IS_ERR_OR_NULL(isp->psys)) {
ipu6_cpd_free_pkg_dir(isp->psys);
ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
}

View File

@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init);
void mantis_input_exit(struct mantis_pci *mantis)
{
rc_unregister_device(mantis->rc);
rc_free_device(mantis->rc);
}
EXPORT_SYMBOL_GPL(mantis_input_exit);

View File

@@ -84,7 +84,8 @@ static int temp_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
u32 val10, raw;
u32 raw;
int val10;
if (type != hwmon_temp || attr != hwmon_temp_input)
return -EOPNOTSUPP;

View File

@@ -128,7 +128,16 @@ static ssize_t color_mapping_show(struct device *dev,
u32 config = mgb4_read_reg(&vindev->mgbdev->video,
vindev->config->regs.config);
return sprintf(buf, "%s\n", config & (1U << 8) ? "0" : "1");
switch ((config >> 7) & 3) {
case 0: /* SPWG/VESA */
return sprintf(buf, "1\n");
case 1: /* ZDML */
return sprintf(buf, "2\n");
case 2: /* OLDI/JEIDA */
return sprintf(buf, "0\n");
default:
return -EIO;
}
}
/*
@@ -151,17 +160,20 @@ static ssize_t color_mapping_store(struct device *dev,
switch (val) {
case 0: /* OLDI/JEIDA */
fpga_data = (1U << 8);
fpga_data = 2;
break;
case 1: /* SPWG/VESA */
fpga_data = 0;
break;
case 2: /* ZDML */
fpga_data = 1;
break;
default:
return -EINVAL;
}
mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.config,
1U << 8, fpga_data);
3U << 7, fpga_data << 7);
return count;
}

View File

@@ -143,6 +143,64 @@ end:
return ret;
}
static ssize_t color_mapping_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct video_device *vdev = to_video_device(dev);
struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
u32 config = mgb4_read_reg(&voutdev->mgbdev->video,
voutdev->config->regs.config);
switch ((config >> 6) & 3) {
case 0: /* SPWG/VESA */
return sprintf(buf, "1\n");
case 1: /* ZDML */
return sprintf(buf, "2\n");
case 2: /* OLDI/JEIDA */
return sprintf(buf, "0\n");
default:
return -EIO;
}
}
/*
* Color mapping change is expected to be called on live streams. Video device
* locking/queue check is not needed.
*/
static ssize_t color_mapping_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct video_device *vdev = to_video_device(dev);
struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
u32 fpga_data;
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
switch (val) {
case 0: /* OLDI/JEIDA */
fpga_data = 2;
break;
case 1: /* SPWG/VESA */
fpga_data = 0;
break;
case 2: /* ZDML */
fpga_data = 1;
break;
default:
return -EINVAL;
}
mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config,
3U << 6, fpga_data << 6);
return count;
}
static ssize_t display_width_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -711,6 +769,7 @@ static DEVICE_ATTR_RW(hback_porch);
static DEVICE_ATTR_RW(hfront_porch);
static DEVICE_ATTR_RW(vback_porch);
static DEVICE_ATTR_RW(vfront_porch);
static DEVICE_ATTR_RW(color_mapping);
static DEVICE_ATTR_RW(fpdl3_output_width);
@@ -731,6 +790,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = {
&dev_attr_vback_porch.attr,
&dev_attr_vfront_porch.attr,
&dev_attr_fpdl3_output_width.attr,
&dev_attr_color_mapping.attr,
NULL
};
@@ -740,6 +800,7 @@ struct attribute *mgb4_gmsl3_out_attrs[] = {
&dev_attr_display_width.attr,
&dev_attr_display_height.attr,
&dev_attr_frame_rate.attr,
&dev_attr_color_mapping.attr,
NULL
};
@@ -759,5 +820,6 @@ struct attribute *mgb4_gmsl1_out_attrs[] = {
&dev_attr_hfront_porch.attr,
&dev_attr_vback_porch.attr,
&dev_attr_vfront_porch.attr,
&dev_attr_color_mapping.attr,
NULL
};

View File

@@ -28,7 +28,7 @@ static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
#define i2c_dbg(level, fmt, arg...) do { \
#define saa7134_i2c_dbg(level, fmt, arg...) do { \
if (i2c_debug == level) \
printk(KERN_DEBUG pr_fmt("i2c: " fmt), ## arg); \
} while (0)
@@ -84,20 +84,20 @@ static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev)
enum i2c_status status;
status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f;
i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]);
saa7134_i2c_dbg(2, "i2c stat <= %s\n", str_i2c_status[status]);
return status;
}
static inline void i2c_set_status(struct saa7134_dev *dev,
enum i2c_status status)
{
i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]);
saa7134_i2c_dbg(2, "i2c stat => %s\n", str_i2c_status[status]);
saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status);
}
static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr)
{
i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]);
saa7134_i2c_dbg(2, "i2c attr => %s\n", str_i2c_attr[attr]);
saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6);
}
@@ -160,7 +160,7 @@ static int i2c_reset(struct saa7134_dev *dev)
enum i2c_status status;
int count;
i2c_dbg(2, "i2c reset\n");
saa7134_i2c_dbg(2, "i2c reset\n");
status = i2c_get_status(dev);
if (!i2c_is_error(status))
return true;
@@ -198,7 +198,7 @@ static inline int i2c_send_byte(struct saa7134_dev *dev,
// dword |= 0x40 << 16; /* 400 kHz */
dword |= 0xf0 << 24;
saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword);
i2c_dbg(2, "i2c data => 0x%x\n", data);
saa7134_i2c_dbg(2, "i2c data => 0x%x\n", data);
if (!i2c_is_busy_wait(dev))
return -EIO;
@@ -220,7 +220,7 @@ static inline int i2c_recv_byte(struct saa7134_dev *dev)
if (i2c_is_error(status))
return -EIO;
data = saa_readb(SAA7134_I2C_DATA);
i2c_dbg(2, "i2c data <= 0x%x\n", data);
saa7134_i2c_dbg(2, "i2c data <= 0x%x\n", data);
return data;
}
@@ -237,12 +237,12 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
if (!i2c_reset(dev))
return -EIO;
i2c_dbg(2, "start xfer\n");
i2c_dbg(1, "i2c xfer:");
saa7134_i2c_dbg(2, "start xfer\n");
saa7134_i2c_dbg(1, "i2c xfer:");
for (i = 0; i < num; i++) {
if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) {
/* send address */
i2c_dbg(2, "send address\n");
saa7134_i2c_dbg(2, "send address\n");
addr = msgs[i].addr << 1;
if (msgs[i].flags & I2C_M_RD)
addr |= 1;
@@ -265,7 +265,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
i2c_dbg(2, "read bytes\n");
saa7134_i2c_dbg(2, "read bytes\n");
for (byte = 0; byte < msgs[i].len; byte++) {
i2c_cont(1, " =");
rc = i2c_recv_byte(dev);
@@ -286,7 +286,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
} else {
/* write bytes */
i2c_dbg(2, "write bytes\n");
saa7134_i2c_dbg(2, "write bytes\n");
for (byte = 0; byte < msgs[i].len; byte++) {
data = msgs[i].buf[byte];
i2c_cont(1, " %02x", data);
@@ -296,7 +296,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
}
}
}
i2c_dbg(2, "xfer done\n");
saa7134_i2c_dbg(2, "xfer done\n");
i2c_cont(1, " >");
i2c_set_attr(dev,STOP);
rc = -EIO;

View File

@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;
rc_unregister_device(dev->remote->dev);
rc_free_device(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
}

View File

@@ -888,6 +888,15 @@ static int get_resources(struct saa7164_dev *dev)
return -EBUSY;
}
static void release_resources(struct saa7164_dev *dev)
{
release_mem_region(pci_resource_start(dev->pci, 0),
pci_resource_len(dev->pci, 0));
release_mem_region(pci_resource_start(dev->pci, 2),
pci_resource_len(dev->pci, 2));
}
static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
{
struct saa7164_port *port = NULL;
@@ -947,9 +956,9 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
mutex_lock(&devlist);
list_add_tail(&dev->devlist, &saa7164_devlist);
mutex_unlock(&devlist);
scoped_guard(mutex, &devlist) {
list_add_tail(&dev->devlist, &saa7164_devlist);
}
/* board config */
dev->board = UNSET;
@@ -996,11 +1005,17 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
}
/* PCI/e allocations */
dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
pci_resource_len(dev->pci, 0));
dev->lmmio = pci_ioremap_bar(dev->pci, 0);
if (!dev->lmmio) {
dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 0\n");
goto err_ioremap_bar0;
}
dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
pci_resource_len(dev->pci, 2));
dev->lmmio2 = pci_ioremap_bar(dev->pci, 2);
if (!dev->lmmio2) {
dev_err(&dev->pci->dev, "Failed to remap MMIO BAR 2\n");
goto err_ioremap_bar2;
}
dev->bmmio = (u8 __iomem *)dev->lmmio;
dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
@@ -1019,17 +1034,25 @@ static int saa7164_dev_setup(struct saa7164_dev *dev)
saa7164_pci_quirks(dev);
return 0;
err_ioremap_bar2:
iounmap(dev->lmmio);
err_ioremap_bar0:
release_resources(dev);
scoped_guard(mutex, &devlist) {
list_del(&dev->devlist);
}
saa7164_devcount--;
return -ENODEV;
}
static void saa7164_dev_unregister(struct saa7164_dev *dev)
{
dprintk(1, "%s()\n", __func__);
release_mem_region(pci_resource_start(dev->pci, 0),
pci_resource_len(dev->pci, 0));
release_mem_region(pci_resource_start(dev->pci, 2),
pci_resource_len(dev->pci, 2));
release_resources(dev);
if (!atomic_dec_and_test(&dev->refcount))
return;

View File

@@ -10,8 +10,8 @@
#include "saa7164.h"
#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw"
#define SAA7164_REV2_FIRMWARE_SIZE 4019072
#define SAA7164_REV2_FIRMWARE "v4l-saa7164-1.0.2-3.fw"
#define SAA7164_REV2_FIRMWARE_SIZE 4038864
#define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw"
#define SAA7164_REV3_FIRMWARE_SIZE 4019072

View File

@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev)
rc_unregister_device(rc_dev);
smi_ir_stop(ir);
rc_free_device(rc_dev);
ir->rc_dev = NULL;
}

View File

@@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work);
rc_unregister_device(budget_ci->ir.dev);
rc_free_device(budget_ci->ir.dev);
}
static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)

View File

@@ -1373,7 +1373,7 @@ static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (zr->codec->type != zr->card.video_codec) {
pci_err(pdev, "%s - wrong codec\n", __func__);
goto zr_unreg_videocodec;
goto zr_detach_codec;
}
}
if (zr->card.video_vfe != 0) {

View File

@@ -447,17 +447,14 @@ static void vpu_m2m_device_run(void *priv)
{
}
static void vpu_m2m_job_abort(void *priv)
static int vpu_m2m_job_ready(void *priv)
{
struct vpu_inst *inst = priv;
struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx;
v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx);
return 0;
}
static const struct v4l2_m2m_ops vpu_m2m_ops = {
.device_run = vpu_m2m_device_run,
.job_abort = vpu_m2m_job_abort
.job_ready = vpu_m2m_job_ready,
};
static int vpu_vb2_queue_setup(struct vb2_queue *vq,

View File

@@ -306,5 +306,7 @@ bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55);
void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55,
enum mali_c55_config_spaces cfg_space);
void mali_c55_params_write_config(struct mali_c55 *mali_c55);
void mali_c55_params_init_isp_config(struct mali_c55 *mali_c55,
const struct v4l2_subdev_state *state);
#endif /* _MALI_C55_COMMON_H */

View File

@@ -663,41 +663,6 @@ static int mali_c55_init_context(struct mali_c55 *mali_c55,
mali_c55->base + config_space_addrs[MALI_C55_CONFIG_PING],
MALI_C55_CONFIG_SPACE_SIZE);
/*
* Some features of the ISP need to be disabled by default and only
* enabled at the same time as they're configured by a parameters buffer
*/
/* Bypass the sqrt and square compression and expansion modules */
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1,
MALI_C55_REG_BYPASS_1_FE_SQRT,
MALI_C55_REG_BYPASS_1_FE_SQRT);
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
MALI_C55_REG_BYPASS_3_SQUARE_BE,
MALI_C55_REG_BYPASS_3_SQUARE_BE);
/* Bypass the temper module */
mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2,
MALI_C55_REG_BYPASS_2_TEMPER);
/* Disable the temper module's DMA read/write */
mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0);
/* Bypass the colour noise reduction */
mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4,
MALI_C55_REG_BYPASS_4_CNR);
/* Disable the sinter module */
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG,
MALI_C55_SINTER_ENABLE_MASK, 0);
/* Disable the RGB Gamma module for each output */
mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0);
mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0);
/* Disable the colour correction matrix */
mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0);
return 0;
}

View File

@@ -112,9 +112,6 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55,
const struct v4l2_subdev_state *state)
{
struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
const struct mali_c55_isp_format_info *cfg;
const struct v4l2_mbus_framefmt *format;
const struct v4l2_rect *crop;
u32 val;
int ret;
@@ -122,35 +119,11 @@ static int mali_c55_isp_start(struct mali_c55 *mali_c55,
MALI_C55_REG_MCU_CONFIG_WRITE_MASK,
MALI_C55_REG_MCU_CONFIG_WRITE_PING);
/* Apply input windowing */
crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
format = v4l2_subdev_state_get_format(state,
MALI_C55_ISP_PAD_SINK_VIDEO);
cfg = mali_c55_isp_get_mbus_config_by_code(format->code);
mali_c55_write(mali_c55, MALI_C55_REG_HC_START,
MALI_C55_HC_START(crop->left));
mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE,
MALI_C55_HC_SIZE(crop->width));
mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE,
MALI_C55_VC_START(crop->top) |
MALI_C55_VC_SIZE(crop->height));
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width);
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
MALI_C55_REG_ACTIVE_HEIGHT_MASK,
format->height << 16);
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER,
MALI_C55_BAYER_ORDER_MASK, cfg->order);
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH,
MALI_C55_INPUT_WIDTH_MASK,
MALI_C55_INPUT_WIDTH_20BIT);
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK,
cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK :
0x00);
/*
* Apply default ISP configuration and the apply configurations from
* the first available parameters buffer.
*/
mali_c55_params_init_isp_config(mali_c55, state);
mali_c55_params_write_config(mali_c55);
ret = mali_c55_config_write(ctx, MALI_C55_CONFIG_PING, true);
if (ret) {

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