mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
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:
1
.mailmap
1
.mailmap
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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]
|
||||
}
|
||||
@@ -33,7 +33,6 @@ Video4Linux (V4L) driver-specific documentation
|
||||
si470x
|
||||
si4713
|
||||
si476x
|
||||
starfive_camss
|
||||
vimc
|
||||
visl
|
||||
vivid
|
||||
|
||||
@@ -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#
|
||||
|
||||
@@ -17,7 +17,9 @@ description: |-
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: onnn,mt9m114
|
||||
enum:
|
||||
- onnn,mt9m114
|
||||
- aptina,mi1040
|
||||
|
||||
reg:
|
||||
description: I2C device address
|
||||
|
||||
101
Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
Normal file
101
Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
103
Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
Normal file
103
Documentation/devicetree/bindings/media/i2c/ovti,ov2732.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
|
||||
111
Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
Normal file
111
Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -26,6 +26,7 @@ Documentation/userspace-api/media/index.rst
|
||||
:numbered:
|
||||
|
||||
maintainer-entry-profile
|
||||
media-committers
|
||||
|
||||
v4l2-core
|
||||
dtv-core
|
||||
|
||||
@@ -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 e‑mail, 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.
|
||||
|
||||
203
Documentation/driver-api/media/media-committers.rst
Normal file
203
Documentation/driver-api/media/media-committers.rst
Normal 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/>`_.
|
||||
@@ -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
|
||||
--------------------------------------------
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
@@ -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
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
30
MAINTAINERS
30
MAINTAINERS
@@ -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/
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ host1x-y = \
|
||||
job.o \
|
||||
debug.o \
|
||||
mipi.o \
|
||||
tegra114-mipi.o \
|
||||
fence.o \
|
||||
hw/host1x01.o \
|
||||
hw/host1x02.o \
|
||||
|
||||
@@ -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);
|
||||
|
||||
483
drivers/gpu/host1x/tegra114-mipi.c
Normal file
483
drivers/gpu/host1x/tegra114-mipi.c
Normal 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,
|
||||
};
|
||||
@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)
|
||||
|
||||
data->rc_dev = NULL;
|
||||
rc_unregister_device(rdev);
|
||||
rc_free_device(rdev);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 },
|
||||
{}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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
790
drivers/media/i2c/ov2732.c
Normal 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>");
|
||||
@@ -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:
|
||||
/*
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
1064
drivers/media/i2c/t4ka3.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user