mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
media: i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS)
Add driver for Intel Computer Vision Sensing (CVS) devices found on Intel Luna Lake (LNL), Panther Lake (PTL), and Arrow Lake (ARL) platforms. The CVS device acts as a V4L2 sub-device bridge that manages CSI-2 link ownership between the host (Linux) and firmware for camera sensors. It provides: - Query the device status via sysfs interface - CSI-2 link ownership arbitration between host and CVS firmware - MIPI CSI-2 configuration management - Privacy LED control coordination - Power management integration with runtime PM The driver consists of two main components: core.c: Core driver with probe, command transport, and power management v4l2.c: V4L2 sub-device and media framework integration Hardware Interface: - I2C for command/control communication with device firmware - GPIO signals for ownership handshaking (request/response) - Optional reset and wake interrupt for full-capability variants - Integration with Intel IPU via ipu_bridge The driver supports two hardware capability levels: - Light capability: Basic GPIO-based ownership (2 GPIOs) - Full capability: Enhanced with reset control and wake IRQ (4 GPIOs) Device-specific quirks are handled via a quirk table to accommodate variations across different CVS implementations (Lattice, Synaptics). In addition to I2C-based operation, the driver supports platform device instantiation for systems where CVS is exposed without I2C transport, falling back to GPIO-only ownership control. The CVS driver integrates with the IPU bridge for automatic device discovery via ACPI on supported platforms. PCI device IDs for Intel IPU7 (0x645d, shared by MTL and LNL) and IPU7.5 (0xb05d, shared by ARL and PTL) are included in the driver-local icvs_pci_tbl lookup table, enabling CVS to locate these IPU variants without modifying the shared ipu6-pci-table header. A PM runtime device link is established between IPU (consumer) and CVS (supplier) so that the PM framework automatically resumes CVS before IPU begins streaming, triggering cvs_runtime_resume() to claim CSI-2 link ownership. Ownership is released via cvs_runtime_suspend() after the autosuspend delay. Signed-off-by: Miguel Vadillo <miguel.vadillo@intel.com> Tested-by: Mehdi Djait <mehdi.djait@linux.intel.com> # Dell XPS 13 9350 + IPU7 Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
This commit is contained in:
parent
a96fcde9fa
commit
8e2b43d2c1
|
|
@ -12848,6 +12848,12 @@ S: Orphan
|
|||
T: git git://git.code.sf.net/p/intel-sas/isci
|
||||
F: drivers/scsi/isci/
|
||||
|
||||
INTEL COMPUTER VISION SENSING (CVS) DRIVER
|
||||
M: Miguel Vadillo <miguel.vadillo@intel.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/media/i2c/cvs/
|
||||
|
||||
INTEL CPU family model numbers
|
||||
M: Tony Luck <tony.luck@intel.com>
|
||||
M: x86@kernel.org
|
||||
|
|
|
|||
|
|
@ -1657,6 +1657,8 @@ endmenu
|
|||
menu "Miscellaneous helper chips"
|
||||
visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
|
||||
|
||||
source "drivers/media/i2c/cvs/Kconfig"
|
||||
|
||||
config VIDEO_I2C
|
||||
tristate "I2C transport video support"
|
||||
depends on VIDEO_DEV && I2C
|
||||
|
|
|
|||
|
|
@ -171,3 +171,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
|
|||
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
|
||||
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
|
||||
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
|
||||
obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/
|
||||
|
|
|
|||
21
drivers/media/i2c/cvs/Kconfig
Normal file
21
drivers/media/i2c/cvs/Kconfig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config VIDEO_INTEL_CVS
|
||||
tristate "Intel CVS CSI-2 bridge support"
|
||||
depends on I2C && ACPI && VIDEO_DEV
|
||||
depends on IPU_BRIDGE || !IPU_BRIDGE
|
||||
select MEDIA_CONTROLLER
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select V4L2_FWNODE
|
||||
help
|
||||
This adds support for the Intel Computer Vision Sensing (CVS).
|
||||
|
||||
The driver registers a V4L2 sub-device to arbitrate CSI-2 link
|
||||
ownership between the host and CVS firmware, and configures the
|
||||
device for camera sensor streaming.
|
||||
|
||||
The driver can operate with full I2C transport or in a reduced
|
||||
platform (GPIO-only) mode when I2C is unavailable.
|
||||
|
||||
Say Y to build into the kernel, or M to build as a module.
|
||||
The module will be named intel_cvs. If unsure, say N.
|
||||
4
drivers/media/i2c/cvs/Makefile
Normal file
4
drivers/media/i2c/cvs/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
intel_cvs-y := core.o v4l2.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_INTEL_CVS) += intel_cvs.o
|
||||
1043
drivers/media/i2c/cvs/core.c
Normal file
1043
drivers/media/i2c/cvs/core.c
Normal file
File diff suppressed because it is too large
Load Diff
495
drivers/media/i2c/cvs/icvs.h
Normal file
495
drivers/media/i2c/cvs/icvs.h
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _ICVS_H
|
||||
#define _ICVS_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
/*
|
||||
* PCI device IDs for all IPU generations that co-exist with the CVS bridge.
|
||||
* IPU7 (0x645d) is shared by MTL and LNL; IPU7P5 (0xb05d) is shared by
|
||||
* ARL and PTL. These are not yet in the shared ipu6-pci-table so they are
|
||||
* listed here alongside the IPU6 family for probe-time IPU discovery.
|
||||
*/
|
||||
struct gpio_desc;
|
||||
struct i2c_client;
|
||||
|
||||
/*
|
||||
* GPIO resource counts (ACPI enumerated).
|
||||
*
|
||||
* 4-GPIO configuration has a wake IRQ and supports asynchronous messaging.
|
||||
* 2-GPIO configuration has no IRQ, so communication is synchronous only.
|
||||
*/
|
||||
#define ICVS_GPIO_ASYNC 4
|
||||
#define ICVS_GPIO_SYNC 2
|
||||
|
||||
/* Firmware response prefix (optional, for protocol revision 2.x or newer) */
|
||||
#define ICVS_PREFIX_VAL 0xCAFEB0BA
|
||||
|
||||
/*
|
||||
* CSI bridge sub-device definitions
|
||||
*/
|
||||
/**
|
||||
* enum icvs_csi_cmd_id - Low-level CSI bridge command identifiers
|
||||
*
|
||||
* These numeric IDs are part of the legacy CSI-side protocol not mapped
|
||||
* directly to the higher level firmware opcodes in enum icvs_command. Only
|
||||
* a minimal subset is presently issued/handled by the driver. Others are
|
||||
* reserved for future expansion of the CSI bridge feature set.
|
||||
*
|
||||
* @ICVS_CSI_SET_OWNER: Set CSI sensor ownership between host and CVS
|
||||
* @ICVS_CSI_SET_CONF: Apply CSI link configuration parameters
|
||||
* @ICVS_CSI_PRIVACY_NOTIF: Notify host of a privacy state transition
|
||||
*/
|
||||
enum icvs_csi_cmd_id {
|
||||
ICVS_CSI_SET_OWNER = 0,
|
||||
ICVS_CSI_SET_CONF = 2,
|
||||
ICVS_CSI_PRIVACY_NOTIF = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_csi_link_owner - CSI-2 link / sensor ownership
|
||||
*
|
||||
* Ownership reflects which endpoint currently controls the attached image
|
||||
* sensor over the CSI-2 link. Transitions may gate streaming or reconfigure
|
||||
* link parameters. The host requests ownership changes via protocol opcodes
|
||||
* and may need to assert GPIO signals on platforms without full capability.
|
||||
*
|
||||
* @ICVS_CSI_LINK_HOST: Host (Linux) owns the sensor and may start streaming
|
||||
* @ICVS_CSI_LINK_CVS: CVS firmware owns the sensor (host must not stream)
|
||||
*/
|
||||
enum icvs_csi_link_owner {
|
||||
ICVS_CSI_LINK_HOST,
|
||||
ICVS_CSI_LINK_CVS,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_csi_privacy_status - Reported privacy state
|
||||
*
|
||||
* Reflects user privacy (e.g. camera LED assertion and stream gating). The
|
||||
* MAX value is a sentinel used for bounds checking and is not a real state.
|
||||
*
|
||||
* @ICVS_CSI_PRIVACY_OFF: Privacy not asserted (LED off, streaming permitted)
|
||||
* @ICVS_CSI_PRIVACY_ON: Privacy asserted (LED on and/or stream gated)
|
||||
* @ICVS_CSI_PRIVACY_MAX: Sentinel; not a valid operational value
|
||||
*/
|
||||
enum icvs_csi_privacy_status {
|
||||
ICVS_CSI_PRIVACY_OFF,
|
||||
ICVS_CSI_PRIVACY_ON,
|
||||
ICVS_CSI_PRIVACY_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_csi_pads - Media pads exposed by the CVS sub-device
|
||||
*
|
||||
* The bridge presents a single sink (from the remote sensor) and a single
|
||||
* source (toward the rest of the media graph / consumers). NUM_PADS is used
|
||||
* for sizing arrays and iteration; it is not a real pad index.
|
||||
*
|
||||
* @ICVS_CSI_PAD_SINK: Sink pad receiving frames from remote sensor
|
||||
* @ICVS_CSI_PAD_SOURCE: Source pad emitting frames to downstream entities
|
||||
* @ICVS_CSI_NUM_PADS: Count sentinel (array size / iteration bound)
|
||||
*/
|
||||
enum icvs_csi_pads {
|
||||
ICVS_CSI_PAD_SINK,
|
||||
ICVS_CSI_PAD_SOURCE,
|
||||
ICVS_CSI_NUM_PADS
|
||||
};
|
||||
|
||||
/*
|
||||
* Core driver structures and functions used by both I2C and platform modes
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: CVS device quirk flags
|
||||
*
|
||||
* These bit flag macros describe per-device behavioral adjustments applied
|
||||
* after VID/PID matching (see cvs_i2c_check() and cvs_apply_quirks()). They
|
||||
* allow the driver to selectively alter logic paths for firmware / hardware
|
||||
* variants without introducing hard-coded conditionals at each call site.
|
||||
*
|
||||
* @ICVS_NO_MIPI_CONFIG: Device firmware performs its own MIPI link setup;
|
||||
* skip sending HOST_SET_MIPI_CONFIG.
|
||||
* @ICVS_SKIP_FW_RESET: Skip issuing a post firmware-update reset sequence.
|
||||
* @ICVS_NO_CAPS: Device firmware does not support GET_DEV_CAPABILITY;
|
||||
* skip capability query and treat caps as unsupported.
|
||||
* @ICVS_FW_BUF_SIZE_256: Firmware expects chunk transfer buffer size of 256
|
||||
* bytes (override defaults if they differ).
|
||||
* @ICVS_FW_HEADER_SIZE_256: Firmware header size fixed at 256 bytes offset
|
||||
* for start of payload data.
|
||||
* @ICVS_HOST_SENSOR_PWR_CTRL: Host must control sensor power sequencing.
|
||||
* @ICVS_HOST_PRIV_CTRL: Host owns privacy LED gating.
|
||||
* @ICVS_HOST_VISION_SENSING: Host enables vision sensing capability bit
|
||||
* @ICVS_NO_FW_UPDATE: Device does not support firmware update.
|
||||
*/
|
||||
#define ICVS_NO_MIPI_CONFIG BIT(0)
|
||||
#define ICVS_SKIP_FW_RESET BIT(1)
|
||||
#define ICVS_NO_CAPS BIT(2)
|
||||
#define ICVS_FW_BUF_SIZE_256 BIT(3)
|
||||
#define ICVS_FW_HEADER_SIZE_256 BIT(4)
|
||||
#define ICVS_HOST_SENSOR_PWR_CTRL BIT(5)
|
||||
#define ICVS_HOST_PRIV_CTRL BIT(6)
|
||||
#define ICVS_HOST_VISION_SENSING BIT(7)
|
||||
#define ICVS_NO_FW_UPDATE BIT(8)
|
||||
|
||||
/**
|
||||
* struct icvs_device_quirk - Device-specific quirk entry
|
||||
* @vid: Vendor ID
|
||||
* @pid: Product ID
|
||||
* @quirks: Quirk flags for this device
|
||||
*/
|
||||
struct icvs_device_quirk {
|
||||
u16 vid;
|
||||
u16 pid;
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_dt_config - Data type configuration for a virtual channel
|
||||
* @pixel_width: Pixel width in pixels
|
||||
* @pixel_height: Pixel height in pixels
|
||||
* @data_type: MIPI CSI-2 data type (RAW10, RAW12, etc.)
|
||||
* @reserved: Reserved for future use
|
||||
*/
|
||||
struct icvs_dt_config {
|
||||
u16 pixel_width;
|
||||
u16 pixel_height;
|
||||
u8 data_type;
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_vc_config - Virtual channel configuration
|
||||
* @vc: Virtual channel index (0-31)
|
||||
* @dt_count: Number of data types configured
|
||||
* @dt_configs: Array of data type configurations (up to 4)
|
||||
* @reserved: Reserved for future use
|
||||
*/
|
||||
struct icvs_vc_config {
|
||||
u8 vc;
|
||||
u8 dt_count;
|
||||
struct icvs_dt_config dt_configs[4];
|
||||
u8 reserved[6];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_link_cfg - Host to CVS CSI link configuration
|
||||
* @fps: Frames per second
|
||||
* @nr_of_lanes: Number of CSI-2 data lanes used
|
||||
* @phy_mode: 0 = DPHY, 1 = CPHY
|
||||
* @vc_count: Number of virtual channels enabled
|
||||
* @vc_configs: Per-VC configuration
|
||||
* @link_freq: Link frequency in Hz
|
||||
* @reserved: Reserved for future use
|
||||
*/
|
||||
struct icvs_link_cfg {
|
||||
u8 fps;
|
||||
u8 nr_of_lanes;
|
||||
u8 phy_mode;
|
||||
u8 vc_count;
|
||||
struct icvs_vc_config vc_configs[4];
|
||||
u32 link_freq;
|
||||
u8 reserved[8];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_fw_version - Firmware version tuple
|
||||
* @major: Major version
|
||||
* @minor: Minor version
|
||||
* @hotfix: Hotfix/patch level
|
||||
* @build: Build number
|
||||
*/
|
||||
struct icvs_fw_version {
|
||||
u32 major;
|
||||
u32 minor;
|
||||
u32 hotfix;
|
||||
u32 build;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_vid_pid - Device vendor/product IDs
|
||||
* @v_id: Vendor ID
|
||||
* @p_id: Product ID
|
||||
*/
|
||||
struct icvs_vid_pid {
|
||||
u16 v_id;
|
||||
u16 p_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_mipi_data_packet - Encapsulated MIPI link configuration packet
|
||||
* @cmd_id: Command identifier
|
||||
* @size: Payload size (bytes)
|
||||
* @crc: Checksum over payload
|
||||
* @conf: CSI link configuration
|
||||
* @reserved: Reserved for future use
|
||||
*/
|
||||
struct icvs_mipi_data_packet {
|
||||
__be16 cmd_id;
|
||||
u32 size;
|
||||
u32 crc;
|
||||
struct icvs_link_cfg conf;
|
||||
u8 reserved[70];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct icvs_mipi_read_packet - Read-back MIPI configuration
|
||||
* @size: Payload size
|
||||
* @crc: Payload checksum
|
||||
* @conf: CSI link configuration
|
||||
*/
|
||||
struct icvs_mipi_read_packet {
|
||||
u32 size;
|
||||
u32 crc;
|
||||
struct icvs_link_cfg conf;
|
||||
};
|
||||
|
||||
/* Host identifier bitfield masks */
|
||||
#define ICVS_HOST_ID_RGBCAMERA_PWRUP BIT(31)
|
||||
#define ICVS_HOST_ID_PRIVACY_LED BIT(30)
|
||||
#define ICVS_HOST_ID_DEVICE_POWER GENMASK(29, 28)
|
||||
#define ICVS_HOST_ID_VISION_SENSING BIT(27)
|
||||
|
||||
/* Device state bitfield masks (u8) */
|
||||
#define ICVS_DEV_STATE_PRIVACY BIT(0)
|
||||
#define ICVS_DEV_STATE_ON BIT(1)
|
||||
#define ICVS_DEV_STATE_SENSOR_OWNER BIT(2)
|
||||
#define ICVS_DEV_STATE_DOWNLOAD BIT(4)
|
||||
#define ICVS_DEV_STATE_ERROR BIT(6)
|
||||
#define ICVS_DEV_STATE_BUSY BIT(7)
|
||||
|
||||
/* Device capability bitfield masks (u16) */
|
||||
#define ICVS_CAP_HOST_MIPI_REQUIRED BIT(15)
|
||||
#define ICVS_CAP_FW_ANTIROLLBACK BIT(14)
|
||||
#define ICVS_CAP_PRIVACY2VISIONDRIVER BIT(13)
|
||||
#define ICVS_CAP_FWUPDATE_RESET_HOST BIT(12)
|
||||
#define ICVS_CAP_NO_CAMERA_FWUPDATE BIT(11)
|
||||
#define ICVS_CAP_POWER_DOMAIN_SUPPORT BIT(10)
|
||||
#define ICVS_CAP_FW_FLASHED_IN_PLACE BIT(9)
|
||||
#define ICVS_CAP_IO_CONTEXT_HOT BIT(8)
|
||||
|
||||
/**
|
||||
* struct icvs_dev_capabilities - Protocol capabilities reported by device
|
||||
* @protocol_version_major: Major protocol version
|
||||
* @protocol_version_minor: Minor protocol version
|
||||
* @capability: Capability bitfield - use ICVS_CAP_* masks
|
||||
* @max_packet_time: Max packet processing time (ms)
|
||||
* @max_post_dl_time: Max post-download time (s)
|
||||
*/
|
||||
struct icvs_dev_capabilities {
|
||||
u8 protocol_version_major;
|
||||
u8 protocol_version_minor;
|
||||
u16 capability;
|
||||
u16 max_packet_time;
|
||||
u16 max_post_dl_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs_cmd - Generic command container
|
||||
* @cmd_id: Command identifier
|
||||
* @param: Parameter union providing multiple variants
|
||||
* @param.param: Raw parameter
|
||||
* @param.host_id: Host identifier (ICVS_SET_DEV_HOST_ID) - use
|
||||
* ICVS_HOST_ID_* masks
|
||||
* @param.conf: CSI link configuration (ICVS_HOST_SET_MIPI_CONFIG)
|
||||
*/
|
||||
struct icvs_cmd {
|
||||
__be16 cmd_id;
|
||||
union {
|
||||
u32 param;
|
||||
u32 host_id;
|
||||
struct icvs_link_cfg conf;
|
||||
} param;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct icvs_resp - Firmware response container
|
||||
* @status: Internal status code
|
||||
* @cmd_id: Original command identifier
|
||||
* @resp: Response union containing variant payload
|
||||
* @resp.state: Device state response (u8, use ICVS_DEV_STATE_* masks)
|
||||
* @resp.cap: Capability response
|
||||
* @resp.conf: Link configuration response
|
||||
* @resp.mipi_read: Raw link config read-back
|
||||
* @resp.vid_pid: Vendor/product identifiers
|
||||
* @resp.fw_version: Firmware version tuple
|
||||
*/
|
||||
struct icvs_resp {
|
||||
u32 status;
|
||||
__be16 cmd_id;
|
||||
union {
|
||||
u8 state;
|
||||
struct icvs_dev_capabilities cap;
|
||||
struct icvs_link_cfg conf;
|
||||
struct icvs_mipi_read_packet mipi_read;
|
||||
struct icvs_vid_pid vid_pid;
|
||||
struct icvs_fw_version fw_version;
|
||||
} resp;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_resources - Device capability / resource category
|
||||
*
|
||||
* Categorizes hardware resource availability which influences protocol
|
||||
* features (e.g. reset control, wake IRQ, GPIO mediated ownership). Light
|
||||
* capability devices expose a reduced set of control GPIOs; full capability
|
||||
* devices provide all optional signals and features. NOTSUP represents an
|
||||
* unsupported or uninitialized state.
|
||||
*
|
||||
* @ICVS_NOTSUP: Capability not supported / not yet determined
|
||||
* @ICVS_LIGHTCAP: Light capability (limited GPIO / no dedicated wake IRQ)
|
||||
* @ICVS_FULLCAP: Full capability (reset GPIO, wake IRQ, extended protocol)
|
||||
*/
|
||||
enum icvs_resources {
|
||||
ICVS_NOTSUP,
|
||||
ICVS_LIGHTCAP,
|
||||
ICVS_FULLCAP,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_command - Protocol command opcodes (firmware space 0x0800+)
|
||||
* @ICVS_GET_DEV_STATE: Query current device state bitfield
|
||||
* @ICVS_GET_DEV_FW_VERSION: Retrieve firmware version tuple
|
||||
* @ICVS_GET_DEV_VID_PID: Read vendor / product identifiers
|
||||
* @ICVS_GET_DEV_ERR_CODE: Fetch last error code (if any)
|
||||
* @ICVS_GET_DEV_CAPABILITY: Read protocol capability structure
|
||||
* @ICVS_SET_DEV_HOST_ID: Set host identity / ownership bits
|
||||
* @ICVS_GET_DEV_HOST_ID: Read back host identity
|
||||
* @ICVS_FW_LOADER_START: Begin firmware download sequence
|
||||
* @ICVS_FW_LOADER_DATA: Stream a chunk of firmware payload
|
||||
* @ICVS_FW_LOADER_END: End firmware download / trigger flash
|
||||
* @ICVS_HOST_GET_MIPI_CONFIG: Request current MIPI CSI link configuration
|
||||
* @ICVS_HOST_SET_MIPI_CONFIG: Apply new MIPI CSI link configuration
|
||||
* @ICVS_HOST_SENSOR_OWNER: Toggle CSI sensor ownership (GPIO assist)
|
||||
*/
|
||||
enum icvs_command {
|
||||
ICVS_GET_DEV_STATE = 0x0800,
|
||||
ICVS_GET_DEV_FW_VERSION = 0x0801,
|
||||
ICVS_GET_DEV_VID_PID = 0x0802,
|
||||
ICVS_GET_DEV_ERR_CODE = 0x0803,
|
||||
ICVS_GET_DEV_CAPABILITY = 0x0804,
|
||||
ICVS_SET_DEV_HOST_ID = 0x0805,
|
||||
ICVS_GET_DEV_HOST_ID = 0x0806,
|
||||
ICVS_FW_LOADER_START = 0x0820,
|
||||
ICVS_FW_LOADER_DATA = 0x0821,
|
||||
ICVS_FW_LOADER_END = 0x0822,
|
||||
ICVS_HOST_GET_MIPI_CONFIG = 0x082F,
|
||||
ICVS_HOST_SET_MIPI_CONFIG = 0x0830,
|
||||
ICVS_HOST_SENSOR_OWNER = 0x0831,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum icvs_state - Device state bitfield flags
|
||||
*
|
||||
* These flags correspond to bits in the device state byte returned via
|
||||
* GET_DEV_STATE and decoded in union icvs_dev_state. Multiple bits may be
|
||||
* asserted simultaneously. Reserved bits are omitted.
|
||||
*
|
||||
* @ICVS_DEVICE_OFF_STATE: Raw zero value; device is powered off or
|
||||
* not yet ready
|
||||
* @ICVS_DEVICE_PRIVACY_ON: Privacy mode active (LED asserted and/or
|
||||
* stream gated)
|
||||
* @ICVS_DEVICE_ON_STATE: Device powered and responsive to protocol commands
|
||||
* @ICVS_DEVICE_SENSOR_OWNER: CVS currently owns the attached CSI sensor
|
||||
* @ICVS_DEVICE_DWNLD_STATE: Firmware download / flash operation in progress
|
||||
* @ICVS_DEVICE_ERROR_STATE: Device has reported an error (query
|
||||
* ICVS_GET_DEV_ERR_CODE)
|
||||
* @ICVS_DEVICE_BUSY_STATE: Device is busy processing a prior command
|
||||
*/
|
||||
enum icvs_state {
|
||||
ICVS_DEVICE_OFF_STATE = 0x00,
|
||||
ICVS_DEVICE_PRIVACY_ON = BIT(0),
|
||||
ICVS_DEVICE_ON_STATE = BIT(1),
|
||||
ICVS_DEVICE_SENSOR_OWNER = BIT(2),
|
||||
ICVS_DEVICE_DWNLD_STATE = BIT(4),
|
||||
ICVS_DEVICE_ERROR_STATE = BIT(6),
|
||||
ICVS_DEVICE_BUSY_STATE = BIT(7),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct icvs - Core CVS device context
|
||||
* @i2c_client: I2C client (NULL in platform-only mode)
|
||||
* @work: Delayed work for polling device state / completion
|
||||
* @wq_resp: Last response container populated by workqueue
|
||||
* @cmd_completion: Completion for command waiters
|
||||
* @lock: Mutex protecting command submission & shared state
|
||||
* @subdev: V4L2 sub-device representing the CSI bridge entity
|
||||
* @remote: Remote media pad connected to upstream camera sensor
|
||||
* @notifier: Async notifier for remote sensor discovery
|
||||
* @ctrl_handler: V4L2 control handler
|
||||
* @freq_ctrl: (future) frequency control pointer
|
||||
* @pads: Local media pads (sink/source)
|
||||
* @nr_of_lanes: Active CSI-2 lane count
|
||||
* @link_freq: Current link frequency (Hz)
|
||||
* @ipu_link: PM runtime device link (IPU consumer, CVS supplier)
|
||||
* @res: Resource capability (light/full)
|
||||
* @caps: Reported device protocol capabilities
|
||||
* @prefix: Firmware response prefix present
|
||||
* @quirks: Device-specific quirk flags
|
||||
* @rst: Reset GPIO (full capability only)
|
||||
* @req: Request GPIO (ownership signaling)
|
||||
* @resp: Response GPIO (ownership signaling)
|
||||
* @irq: Wake IRQ (full capability)
|
||||
* @hostwake_event: Waitqueue for wake events
|
||||
* @hostwake_event_arg: Wake event flag
|
||||
*/
|
||||
struct icvs {
|
||||
struct i2c_client *i2c_client;
|
||||
struct delayed_work work;
|
||||
struct icvs_resp wq_resp;
|
||||
struct completion cmd_completion;
|
||||
struct mutex lock; /* Protects command execution and device state */
|
||||
struct v4l2_subdev subdev;
|
||||
struct media_pad *remote;
|
||||
struct v4l2_async_notifier notifier;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
struct v4l2_ctrl *freq_ctrl;
|
||||
struct media_pad pads[ICVS_CSI_NUM_PADS];
|
||||
u32 nr_of_lanes;
|
||||
u64 link_freq;
|
||||
struct device_link *ipu_link;
|
||||
enum icvs_resources res;
|
||||
struct icvs_dev_capabilities caps;
|
||||
bool prefix;
|
||||
unsigned long quirks;
|
||||
struct gpio_desc *rst;
|
||||
struct gpio_desc *req;
|
||||
struct gpio_desc *resp;
|
||||
int irq;
|
||||
wait_queue_head_t hostwake_event;
|
||||
bool hostwake_event_arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* cvs_dev - Helper returning the struct device for a CVS context
|
||||
* @ctx: CVS context
|
||||
*
|
||||
* Avoids repeating transport conditional logic at each call site when
|
||||
* acquiring the device pointer for logging or PM operations.
|
||||
*
|
||||
* Return: Device pointer (never NULL if @ctx is valid).
|
||||
*/
|
||||
static inline struct device *cvs_dev(struct icvs *ctx)
|
||||
{
|
||||
return ctx->i2c_client ? &ctx->i2c_client->dev : ctx->subdev.dev;
|
||||
}
|
||||
|
||||
/* Cross-unit interfaces */
|
||||
int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len);
|
||||
int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner);
|
||||
int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c);
|
||||
void cvs_csi_remove(struct icvs *ctx);
|
||||
|
||||
#endif /* _ICVS_H */
|
||||
618
drivers/media/i2c/cvs/v4l2.c
Normal file
618
drivers/media/i2c/cvs/v4l2.c
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2026 Intel Corporation
|
||||
* CVS driver - CSI/V4L2 subdev support
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-mc.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include "icvs.h"
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
static inline struct icvs *notifier_to_csi(struct v4l2_async_notifier *n)
|
||||
{
|
||||
return container_of(n, struct icvs, notifier);
|
||||
}
|
||||
|
||||
static inline struct icvs *sd_to_csi(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct icvs, subdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Default formats
|
||||
*/
|
||||
static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = {
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.code = MEDIA_BUS_FMT_Y8_1X8,
|
||||
.field = V4L2_FIELD_NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* csi_set_link_cfg - Program default CSI-2 link parameters
|
||||
* @ctx: CVS device context
|
||||
*
|
||||
* Populates a HOST_SET_MIPI_CONFIG command using current lane count and
|
||||
* link frequency, then submits it to the device.
|
||||
* Rest of the link parameters are left at firmware defaults.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int csi_set_link_cfg(struct icvs *ctx)
|
||||
{
|
||||
struct icvs_cmd cmd = {
|
||||
.cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG),
|
||||
.param.conf.nr_of_lanes = ctx->nr_of_lanes,
|
||||
.param.conf.link_freq = ctx->link_freq,
|
||||
};
|
||||
size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
|
||||
|
||||
guard(mutex)(&ctx->lock);
|
||||
return cvs_send(ctx, &cmd, cmd_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Streaming
|
||||
*/
|
||||
|
||||
/**
|
||||
* cvs_csi_enable_streams - Start streaming through the bridge
|
||||
* @sd: Sub-device pointer
|
||||
* @state: Active state
|
||||
* @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE)
|
||||
* @streams_mask: Streams to enable (bit 0 supported)
|
||||
*
|
||||
* Runtime-resumes the bridge (triggering cvs_runtime_resume() to claim CSI-2
|
||||
* link ownership), fetches the link frequency, programs the MIPI configuration,
|
||||
* and forwards the enable request downstream.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_enable_streams(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
u32 pad, u64 streams_mask)
|
||||
{
|
||||
struct icvs *ctx = sd_to_csi(sd);
|
||||
struct v4l2_subdev *remote_sd =
|
||||
media_entity_to_v4l2_subdev(ctx->remote->entity);
|
||||
struct device *dev = cvs_dev(ctx);
|
||||
s64 freq;
|
||||
int ret;
|
||||
|
||||
/* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
freq = v4l2_get_link_freq(ctx->remote, 0, 0);
|
||||
if (freq < 0) {
|
||||
ret = freq;
|
||||
goto err_rpm_put;
|
||||
}
|
||||
ctx->link_freq = freq;
|
||||
|
||||
if (ctx->i2c_client) {
|
||||
ret = csi_set_link_cfg(ctx);
|
||||
if (ret < 0)
|
||||
goto err_rpm_put_sync;
|
||||
}
|
||||
|
||||
ret = v4l2_subdev_enable_streams(remote_sd,
|
||||
ctx->remote->index,
|
||||
streams_mask);
|
||||
if (ret)
|
||||
goto err_rpm_put_sync;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rpm_put_sync:
|
||||
/* Bypass autosuspend to immediately release ownership on error */
|
||||
pm_runtime_put_sync(dev);
|
||||
return ret;
|
||||
err_rpm_put:
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cvs_csi_disable_streams - Stop streaming through the bridge
|
||||
* @sd: Sub-device pointer
|
||||
* @state: Active state
|
||||
* @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE)
|
||||
* @streams_mask: Streams to disable (bit 0 supported)
|
||||
*
|
||||
* Disables the remote sensor stream then drops the PM reference acquired
|
||||
* during enable. After the autosuspend delay, cvs_runtime_suspend() will
|
||||
* return CSI-2 link ownership to CVS firmware.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_disable_streams(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
u32 pad, u64 streams_mask)
|
||||
{
|
||||
struct icvs *ctx = sd_to_csi(sd);
|
||||
struct v4l2_subdev *remote_sd =
|
||||
media_entity_to_v4l2_subdev(ctx->remote->entity);
|
||||
struct device *dev = cvs_dev(ctx);
|
||||
int ret;
|
||||
|
||||
ret = v4l2_subdev_disable_streams(remote_sd,
|
||||
ctx->remote->index,
|
||||
streams_mask);
|
||||
if (ret)
|
||||
dev_err(dev, "disable streams failed: %d\n", ret);
|
||||
|
||||
/* cvs_set_link_owner(ICVS_CSI_LINK_CVS) */
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pad operations / formats
|
||||
*/
|
||||
/**
|
||||
* cvs_csi_init_state - Initialize pad formats in subdev state
|
||||
* @sd: Sub-device
|
||||
* @state: State container
|
||||
*
|
||||
* Sets all pad formats to a minimal 1x1 default.
|
||||
*
|
||||
* Return: 0.
|
||||
*/
|
||||
static int cvs_csi_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
for (unsigned int i = 0; i < sd->entity.num_pads; i++)
|
||||
*v4l2_subdev_state_get_format(state, i) =
|
||||
cvs_csi_format_mbus_default;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cvs_csi_set_fmt - Negotiate pad format
|
||||
* @sd: Sub-device
|
||||
* @state: State
|
||||
* @format: Desired / returned format
|
||||
*
|
||||
* Mirrors sink format onto source pad. Accepts many media bus codes, falling
|
||||
* back to Y8 if unsupported. Normalizes field setting.
|
||||
*
|
||||
* Return: 0.
|
||||
*/
|
||||
static int cvs_csi_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *src =
|
||||
v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SOURCE);
|
||||
struct v4l2_mbus_framefmt *sink =
|
||||
v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SINK);
|
||||
|
||||
if (format->pad == ICVS_CSI_PAD_SOURCE) { /* source pad mirrors sink */
|
||||
*src = *sink;
|
||||
return 0;
|
||||
}
|
||||
|
||||
v4l_bound_align_image(&format->format.width, 1, 65536, 0,
|
||||
&format->format.height, 1, 65536, 0, 0);
|
||||
|
||||
switch (format->format.code) {
|
||||
/* Accept a large list; default fallback to Y8 */
|
||||
case MEDIA_BUS_FMT_RGB444_1X12:
|
||||
case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
|
||||
case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
|
||||
case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
|
||||
case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
case MEDIA_BUS_FMT_BGR565_2X8_BE:
|
||||
case MEDIA_BUS_FMT_BGR565_2X8_LE:
|
||||
case MEDIA_BUS_FMT_RGB565_2X8_BE:
|
||||
case MEDIA_BUS_FMT_RGB565_2X8_LE:
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
case MEDIA_BUS_FMT_RBG888_1X24:
|
||||
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
|
||||
case MEDIA_BUS_FMT_BGR888_1X24:
|
||||
case MEDIA_BUS_FMT_GBR888_1X24:
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
case MEDIA_BUS_FMT_RGB888_2X12_BE:
|
||||
case MEDIA_BUS_FMT_RGB888_2X12_LE:
|
||||
case MEDIA_BUS_FMT_ARGB8888_1X32:
|
||||
case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
|
||||
case MEDIA_BUS_FMT_RGB101010_1X30:
|
||||
case MEDIA_BUS_FMT_RGB121212_1X36:
|
||||
case MEDIA_BUS_FMT_RGB161616_1X48:
|
||||
case MEDIA_BUS_FMT_Y8_1X8:
|
||||
case MEDIA_BUS_FMT_UV8_1X8:
|
||||
case MEDIA_BUS_FMT_UYVY8_1_5X8:
|
||||
case MEDIA_BUS_FMT_VYUY8_1_5X8:
|
||||
case MEDIA_BUS_FMT_YUYV8_1_5X8:
|
||||
case MEDIA_BUS_FMT_YVYU8_1_5X8:
|
||||
case MEDIA_BUS_FMT_UYVY8_2X8:
|
||||
case MEDIA_BUS_FMT_VYUY8_2X8:
|
||||
case MEDIA_BUS_FMT_YUYV8_2X8:
|
||||
case MEDIA_BUS_FMT_YVYU8_2X8:
|
||||
case MEDIA_BUS_FMT_Y10_1X10:
|
||||
case MEDIA_BUS_FMT_UYVY10_2X10:
|
||||
case MEDIA_BUS_FMT_VYUY10_2X10:
|
||||
case MEDIA_BUS_FMT_YUYV10_2X10:
|
||||
case MEDIA_BUS_FMT_YVYU10_2X10:
|
||||
case MEDIA_BUS_FMT_Y12_1X12:
|
||||
case MEDIA_BUS_FMT_UYVY12_2X12:
|
||||
case MEDIA_BUS_FMT_VYUY12_2X12:
|
||||
case MEDIA_BUS_FMT_YUYV12_2X12:
|
||||
case MEDIA_BUS_FMT_YVYU12_2X12:
|
||||
case MEDIA_BUS_FMT_UYVY8_1X16:
|
||||
case MEDIA_BUS_FMT_VYUY8_1X16:
|
||||
case MEDIA_BUS_FMT_YUYV8_1X16:
|
||||
case MEDIA_BUS_FMT_YVYU8_1X16:
|
||||
case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
|
||||
case MEDIA_BUS_FMT_UYVY10_1X20:
|
||||
case MEDIA_BUS_FMT_VYUY10_1X20:
|
||||
case MEDIA_BUS_FMT_YUYV10_1X20:
|
||||
case MEDIA_BUS_FMT_YVYU10_1X20:
|
||||
case MEDIA_BUS_FMT_VUY8_1X24:
|
||||
case MEDIA_BUS_FMT_YUV8_1X24:
|
||||
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
||||
case MEDIA_BUS_FMT_UYVY12_1X24:
|
||||
case MEDIA_BUS_FMT_VYUY12_1X24:
|
||||
case MEDIA_BUS_FMT_YUYV12_1X24:
|
||||
case MEDIA_BUS_FMT_YVYU12_1X24:
|
||||
case MEDIA_BUS_FMT_YUV10_1X30:
|
||||
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
||||
case MEDIA_BUS_FMT_AYUV8_1X32:
|
||||
case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
||||
case MEDIA_BUS_FMT_YUV12_1X36:
|
||||
case MEDIA_BUS_FMT_YUV16_1X48:
|
||||
case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
||||
case MEDIA_BUS_FMT_JPEG_1X8:
|
||||
case MEDIA_BUS_FMT_AHSV8888_1X32:
|
||||
case MEDIA_BUS_FMT_SBGGR8_1X8:
|
||||
case MEDIA_BUS_FMT_SGBRG8_1X8:
|
||||
case MEDIA_BUS_FMT_SGRBG8_1X8:
|
||||
case MEDIA_BUS_FMT_SRGGB8_1X8:
|
||||
case MEDIA_BUS_FMT_SBGGR10_1X10:
|
||||
case MEDIA_BUS_FMT_SGBRG10_1X10:
|
||||
case MEDIA_BUS_FMT_SGRBG10_1X10:
|
||||
case MEDIA_BUS_FMT_SRGGB10_1X10:
|
||||
case MEDIA_BUS_FMT_SBGGR12_1X12:
|
||||
case MEDIA_BUS_FMT_SGBRG12_1X12:
|
||||
case MEDIA_BUS_FMT_SGRBG12_1X12:
|
||||
case MEDIA_BUS_FMT_SRGGB12_1X12:
|
||||
case MEDIA_BUS_FMT_SBGGR14_1X14:
|
||||
case MEDIA_BUS_FMT_SGBRG14_1X14:
|
||||
case MEDIA_BUS_FMT_SGRBG14_1X14:
|
||||
case MEDIA_BUS_FMT_SRGGB14_1X14:
|
||||
case MEDIA_BUS_FMT_SBGGR16_1X16:
|
||||
case MEDIA_BUS_FMT_SGBRG16_1X16:
|
||||
case MEDIA_BUS_FMT_SGRBG16_1X16:
|
||||
case MEDIA_BUS_FMT_SRGGB16_1X16:
|
||||
break;
|
||||
default:
|
||||
format->format.code = MEDIA_BUS_FMT_Y8_1X8;
|
||||
break;
|
||||
}
|
||||
|
||||
if (format->format.field == V4L2_FIELD_ANY)
|
||||
format->format.field = V4L2_FIELD_NONE;
|
||||
|
||||
*sink = format->format;
|
||||
*src = *sink;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cvs_csi_get_mbus_config - Provide current CSI-2 bus configuration
|
||||
* @sd: Sub-device
|
||||
* @pad: Pad index
|
||||
* @cfg: Returned bus config
|
||||
*
|
||||
* Fills lane ordering and number of lanes; retrieves link frequency from
|
||||
* remote entity.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct icvs *ctx = sd_to_csi(sd);
|
||||
s64 freq;
|
||||
|
||||
cfg->type = V4L2_MBUS_CSI2_DPHY;
|
||||
for (unsigned int i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++)
|
||||
cfg->bus.mipi_csi2.data_lanes[i] = i + 1;
|
||||
cfg->bus.mipi_csi2.num_data_lanes = ctx->nr_of_lanes;
|
||||
|
||||
freq = v4l2_get_link_freq(ctx->remote, 0, 0);
|
||||
if (freq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ctx->link_freq = freq;
|
||||
cfg->link_freq = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_core_ops cvs_csi_subdev_core_ops = {
|
||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops cvs_csi_video_ops = {
|
||||
.s_stream = v4l2_subdev_s_stream_helper,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops cvs_csi_pad_ops = {
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = cvs_csi_set_fmt,
|
||||
.get_mbus_config = cvs_csi_get_mbus_config,
|
||||
.enable_streams = cvs_csi_enable_streams,
|
||||
.disable_streams = cvs_csi_disable_streams,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops cvs_csi_subdev_ops = {
|
||||
.core = &cvs_csi_subdev_core_ops,
|
||||
.video = &cvs_csi_video_ops,
|
||||
.pad = &cvs_csi_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops cvs_csi_internal_ops = {
|
||||
.init_state = cvs_csi_init_state,
|
||||
};
|
||||
|
||||
static const struct media_entity_operations cvs_csi_entity_ops = {
|
||||
.link_validate = v4l2_subdev_link_validate,
|
||||
};
|
||||
|
||||
/*
|
||||
* Async notifier
|
||||
*/
|
||||
/**
|
||||
* cvs_csi_notify_bound - Remote sensor bound callback
|
||||
* @notifier: Async notifier
|
||||
* @sd: Remote subdev
|
||||
* @asc: Async match connection
|
||||
*
|
||||
* Locates the source pad of the remote sensor and creates a media link to
|
||||
* the CVS bridge sink pad enabling it by default.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *sd,
|
||||
struct v4l2_async_connection *asc)
|
||||
{
|
||||
struct icvs *ctx = notifier_to_csi(notifier);
|
||||
int pad;
|
||||
|
||||
pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode,
|
||||
MEDIA_PAD_FL_SOURCE);
|
||||
if (pad < 0)
|
||||
return pad;
|
||||
|
||||
ctx->remote = &sd->entity.pads[pad];
|
||||
|
||||
return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity,
|
||||
ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED |
|
||||
MEDIA_LNK_FL_IMMUTABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* cvs_csi_notify_unbind - Remote sensor unbind callback
|
||||
* @notifier: Notifier
|
||||
* @sd: Remote subdev
|
||||
* @asc: Connection
|
||||
*/
|
||||
static void cvs_csi_notify_unbind(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *sd,
|
||||
struct v4l2_async_connection *asc)
|
||||
{
|
||||
struct icvs *ctx = notifier_to_csi(notifier);
|
||||
|
||||
ctx->remote = NULL;
|
||||
}
|
||||
|
||||
static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = {
|
||||
.bound = cvs_csi_notify_bound,
|
||||
.unbind = cvs_csi_notify_unbind,
|
||||
};
|
||||
|
||||
/*
|
||||
* Controls
|
||||
*/
|
||||
/**
|
||||
* cvs_csi_init_controls - Initialize V4L2 controls
|
||||
* @ctx: CVS context
|
||||
*
|
||||
* Currently sets up a read-only privacy control placeholder.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_init_controls(struct icvs *ctx)
|
||||
{
|
||||
struct v4l2_ctrl *privacy_ctrl;
|
||||
|
||||
v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
|
||||
|
||||
privacy_ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, NULL,
|
||||
V4L2_CID_PRIVACY, 0, 1, 1, 0);
|
||||
if (privacy_ctrl)
|
||||
privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
if (ctx->ctrl_handler.error) {
|
||||
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
||||
return ctx->ctrl_handler.error;
|
||||
}
|
||||
|
||||
ctx->subdev.ctrl_handler = &ctx->ctrl_handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Firmware (graph) parsing
|
||||
*/
|
||||
/**
|
||||
* cvs_csi_parse_firmware - Parse firmware (ACPI graph) endpoints
|
||||
* @ctx: CVS context
|
||||
*
|
||||
* Discovers sink and remote source endpoints, validates lane counts and
|
||||
* registers an async notifier for the remote sensor.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
static int cvs_csi_parse_firmware(struct icvs *ctx)
|
||||
{
|
||||
struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
|
||||
struct device *dev = cvs_dev(ctx);
|
||||
struct fwnode_handle *sink_ep, *source_ep;
|
||||
struct v4l2_async_connection *asc;
|
||||
int ret;
|
||||
|
||||
sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
|
||||
v4l2_async_subdev_nf_init(&ctx->notifier, &ctx->subdev);
|
||||
ctx->notifier.ops = &cvs_csi_notify_ops;
|
||||
|
||||
ret = v4l2_fwnode_endpoint_parse(sink_ep, &ep);
|
||||
if (ret)
|
||||
goto err_nf_cleanup;
|
||||
|
||||
ctx->nr_of_lanes = ep.bus.mipi_csi2.num_data_lanes;
|
||||
source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0);
|
||||
ret = v4l2_fwnode_endpoint_parse(source_ep, &ep);
|
||||
fwnode_handle_put(source_ep);
|
||||
if (ret)
|
||||
goto err_nf_cleanup;
|
||||
|
||||
if (ctx->nr_of_lanes != ep.bus.mipi_csi2.num_data_lanes) {
|
||||
ret = -EINVAL;
|
||||
goto err_nf_cleanup;
|
||||
}
|
||||
|
||||
asc = v4l2_async_nf_add_fwnode_remote(&ctx->notifier, sink_ep,
|
||||
struct v4l2_async_connection);
|
||||
if (IS_ERR(asc)) {
|
||||
ret = PTR_ERR(asc);
|
||||
goto err_nf_cleanup;
|
||||
}
|
||||
|
||||
ret = v4l2_async_nf_register(&ctx->notifier);
|
||||
if (ret)
|
||||
goto err_nf_cleanup;
|
||||
|
||||
fwnode_handle_put(sink_ep);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nf_cleanup:
|
||||
v4l2_async_nf_cleanup(&ctx->notifier);
|
||||
fwnode_handle_put(sink_ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public CSI init/cleanup used by core probe/remove
|
||||
*/
|
||||
/**
|
||||
* cvs_csi_init - Initialize CSI/V4L2 sub-device side of bridge
|
||||
* @ctx: CVS context
|
||||
* @dev: Parent device
|
||||
* @i2c: I2C client (may be NULL for platform mode)
|
||||
*
|
||||
* Sets up sub-device, media entity pads, async notifier, controls and
|
||||
* registers with the V4L2 framework.
|
||||
*
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (i2c) {
|
||||
v4l2_i2c_subdev_init(&ctx->subdev, i2c, &cvs_csi_subdev_ops);
|
||||
} else {
|
||||
v4l2_subdev_init(&ctx->subdev, &cvs_csi_subdev_ops);
|
||||
ctx->subdev.dev = dev;
|
||||
}
|
||||
|
||||
ctx->subdev.internal_ops = &cvs_csi_internal_ops;
|
||||
v4l2_set_subdevdata(&ctx->subdev, ctx);
|
||||
ctx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
ctx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
|
||||
ctx->subdev.entity.ops = &cvs_csi_entity_ops;
|
||||
snprintf(ctx->subdev.name, sizeof(ctx->subdev.name), "Intel CVS");
|
||||
|
||||
ret = cvs_csi_parse_firmware(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cvs_csi_init_controls(ctx);
|
||||
if (ret)
|
||||
goto err_nf_unreg;
|
||||
|
||||
ctx->pads[ICVS_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
|
||||
ctx->pads[ICVS_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
ret = media_entity_pads_init(&ctx->subdev.entity, ICVS_CSI_NUM_PADS,
|
||||
ctx->pads);
|
||||
if (ret)
|
||||
goto err_ctrl_cleanup;
|
||||
|
||||
ctx->subdev.state_lock = ctx->ctrl_handler.lock;
|
||||
ret = v4l2_subdev_init_finalize(&ctx->subdev);
|
||||
if (ret)
|
||||
goto err_entity_cleanup;
|
||||
|
||||
ret = v4l2_async_register_subdev(&ctx->subdev);
|
||||
if (ret)
|
||||
goto err_entity_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
err_entity_cleanup:
|
||||
media_entity_cleanup(&ctx->subdev.entity);
|
||||
|
||||
err_ctrl_cleanup:
|
||||
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
||||
|
||||
err_nf_unreg:
|
||||
v4l2_async_nf_unregister(&ctx->notifier);
|
||||
v4l2_async_nf_cleanup(&ctx->notifier);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cvs_csi_remove - Cleanup CSI/V4L2 sub-device
|
||||
* @ctx: CVS context
|
||||
*/
|
||||
void cvs_csi_remove(struct icvs *ctx)
|
||||
{
|
||||
v4l2_async_nf_unregister(&ctx->notifier);
|
||||
v4l2_async_nf_cleanup(&ctx->notifier);
|
||||
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
||||
v4l2_async_unregister_subdev(&ctx->subdev);
|
||||
v4l2_subdev_cleanup(&ctx->subdev);
|
||||
media_entity_cleanup(&ctx->subdev.entity);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Miguel Vadillo <miguel.vadillo@intel.com>");
|
||||
MODULE_DESCRIPTION("CSI/V4L2 support for Intel Vision Sensing Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in New Issue
Block a user