From 3b7818e4a763b912f50f475c67802f5f7276ac03 Mon Sep 17 00:00:00 2001 From: Emrah Billur Date: Mon, 8 Apr 2024 14:31:29 +0300 Subject: [PATCH] Camera Support trial Signed-off-by: Emrah Billur Toshiba camera drivers integrated Toshiba and Alvium Drivers Kernel config fixed Signed-off-by: Emrah Billur --- .../nvidia-jetson-orin/agx-camera.nix | 53 + .../nvidia-jetson-orin/alvium_toshiba.patch | 18708 ++++++++++++++++ .../nvidia-jetson-orin/camera-common.nix | 18 + .../hardware/nvidia-jetson-orin/nx-camera.nix | 53 + .../toshiba_t358743_dtb.patch | 209 + .../toshiba_t358743_kernel.patch | 4669 ++++ .../jetpack/nvidia-jetson-orin/default.nix | 4 + targets/nvidia-jetson-orin/flake-module.nix | 2 + 8 files changed, 23716 insertions(+) create mode 100644 modules/hardware/nvidia-jetson-orin/agx-camera.nix create mode 100644 modules/hardware/nvidia-jetson-orin/alvium_toshiba.patch create mode 100644 modules/hardware/nvidia-jetson-orin/camera-common.nix create mode 100644 modules/hardware/nvidia-jetson-orin/nx-camera.nix create mode 100644 modules/hardware/nvidia-jetson-orin/toshiba_t358743_dtb.patch create mode 100644 modules/hardware/nvidia-jetson-orin/toshiba_t358743_kernel.patch diff --git a/modules/hardware/nvidia-jetson-orin/agx-camera.nix b/modules/hardware/nvidia-jetson-orin/agx-camera.nix new file mode 100644 index 000000000..8e868bc35 --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/agx-camera.nix @@ -0,0 +1,53 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.orin.agx; +in { + options.ghaf.hardware.nvidia.orin.agx.camera = + lib.mkEnableOption + "Enabling E-con camera driver for Orin AGX "; + config = lib.mkIf cfg.camera { + # Orin NX camera driver + ghaf.hardware.nvidia.orin.camera = true; + + boot.kernelPatches = [ + # { + # name = "nx-camera-dtb-patch"; + # # This patch is for Toshiba HDMI camera dtb + # patch = ./toshiba_t358743_dtb.patch; + # } + # { + # name = "nx-camera-kernel-patch"; + # # This patch is for Toshiba HDMI camera kernel + # patch = ./toshiba_t358743_kernel.patch; + # } + { + name = "nx-camera-patch"; + # This patch is for Toshiba HDMI camera and Alvium CSI2 camera patch + patch = ./alvium_toshiba.patch; + # For Alvium Kernel configuration changes + extraStructuredConfig = with lib.kernel; { + CONFIG_LOCALVERSION_AUTO = yes; + CONFIG_FB_EFI=lib.mkForce unset; + CONFIG_PCI_SERIAL_CH384=lib.mkForce unset; + CONFIG_SENSORS_F75308=lib.mkForce unset; + CONFIG_USB_NET_CDC_MBIM=lib.mkForce unset; + CONFIG_TEGRA23X_OC_EVENT=lib.mkForce unset; + CONFIG_TEGRA19X_OC_EVENT=lib.mkForce unset; + CONFIG_VIDEO_ECAM =lib.mkForce unset; + CONFIG_VIDEO_AVT_CSI2=lib.mkForce unset; + CONFIG_NV_VIDEO_HAWK_OWL=lib.mkForce unset; + CONFIG_HID_SHIELD_REMOTE=lib.mkForce unset; + CONFIG_USB_WDM = module; + CONFIG_TYPEC_FUSB301=lib.mkForce unset; + CONFIG_ISO9660_FS=lib.mkForce unset; + CONFIG_SECURITY_DMESG_RESTRICT=lib.mkForce unset; + }; + } + ]; + }; +} \ No newline at end of file diff --git a/modules/hardware/nvidia-jetson-orin/alvium_toshiba.patch b/modules/hardware/nvidia-jetson-orin/alvium_toshiba.patch new file mode 100644 index 000000000..c1dc428c4 --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/alvium_toshiba.patch @@ -0,0 +1,18708 @@ +diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c +index 72350343a56a..abb680c558bc 100644 +--- a/drivers/media/common/videobuf2/videobuf2-core.c ++++ b/drivers/media/common/videobuf2/videobuf2-core.c +@@ -390,7 +390,7 @@ static void __setup_offsets(struct vb2_buffer *vb) + */ + static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, + unsigned int num_buffers, unsigned int num_planes, +- const unsigned plane_sizes[VB2_MAX_PLANES]) ++ const unsigned plane_sizes[VB2_MAX_PLANES], bool req_index, int index) + { + unsigned int buffer, plane; + struct vb2_buffer *vb; +@@ -411,7 +411,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, + vb->state = VB2_BUF_STATE_DEQUEUED; + vb->vb2_queue = q; + vb->num_planes = num_planes; +- vb->index = q->num_buffers + buffer; ++ vb->index = req_index ? index + buffer : q->num_buffers + buffer; + vb->type = q->type; + vb->memory = memory; + /* +@@ -491,6 +491,22 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) + } + } + ++static void __vb2_free_mem_single(struct vb2_queue *q, unsigned int index) ++{ ++ struct vb2_buffer *vb; ++ ++ vb = q->bufs[index]; ++ if (!vb) ++ return; ++ ++ if (q->memory == VB2_MEMORY_MMAP) ++ __vb2_buf_mem_free(vb); ++ else if (q->memory == VB2_MEMORY_DMABUF) ++ __vb2_buf_dmabuf_put(vb); ++ else ++ __vb2_buf_userptr_put(vb); ++} ++ + /* + * __vb2_queue_free() - free buffers at the end of the queue - video memory and + * related information, if no buffers are left return the queue to an +@@ -608,6 +624,44 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) + return 0; + } + ++int vb2_buffer_free(struct vb2_queue *q, unsigned int index) ++{ ++ struct vb2_buffer *vb = q->bufs[index]; ++ ++ /* ++ * Sanity check: when preparing a buffer the queue lock is released for ++ * a short while (see __buf_prepare for the details), which would allow ++ * a race with a reqbufs which can call this function. Removing the ++ * buffers from underneath __buf_prepare is obviously a bad idea, so we ++ * check if any of the buffers is in the state PREPARING, and if so we ++ * just return -EAGAIN. ++ */ ++ if (q->bufs[index] == NULL) ++ return -EINVAL; ++ ++ if (q->bufs[index]->state == VB2_BUF_STATE_PREPARING) { ++ dprintk(q, 1, "preparing buffers, cannot free\n"); ++ return -EAGAIN; ++ } ++ ++ if (vb && vb->planes[0].mem_priv) ++ call_void_vb_qop(vb, buf_cleanup, vb); ++ ++ /* Release video buffer memory */ ++ __vb2_free_mem_single(q, index); ++ ++ kfree(q->bufs[index]); ++ q->bufs[index] = NULL; ++ ++ q->num_buffers--; ++ if (!q->num_buffers) { ++ q->memory = 0; ++ INIT_LIST_HEAD(&q->queued_list); ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(vb2_buffer_free); ++ + bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) + { + unsigned int plane; +@@ -808,7 +862,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, + + /* Finally, allocate buffers and video memory */ + allocated_buffers = +- __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); ++ __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, false, 0); + if (allocated_buffers == 0) { + dprintk(q, 1, "memory allocation failed\n"); + return -ENOMEM; +@@ -871,15 +925,80 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, + } + EXPORT_SYMBOL_GPL(vb2_core_reqbufs); + ++int vb2_core_create_single_buf(struct vb2_queue *q, enum vb2_memory memory, ++ unsigned int *count, unsigned requested_planes, ++ const unsigned requested_sizes[], bool req_index, int index) ++{ ++ unsigned int num_planes = 0, num_buffers, allocated_buffers; ++ unsigned plane_sizes[VB2_MAX_PLANES] = { }; ++ int ret; ++ ++ if (req_index && q->num_buffers > index) ++ return 0; ++ ++ if (q->num_buffers == VB2_MAX_FRAME) { ++ dprintk(q, 1, "maximum number of buffers already allocated\n"); ++ return -ENOBUFS; ++ } ++ ++ if (!q->num_buffers) { ++ memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); ++ q->memory = memory; ++ q->waiting_for_buffers = !q->is_output; ++ } ++ ++ num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); ++ ++ if (requested_planes && requested_sizes) { ++ num_planes = requested_planes; ++ memcpy(plane_sizes, requested_sizes, sizeof(plane_sizes)); ++ } ++ ++ /* ++ * Ask the driver, whether the requested number of buffers, planes per ++ * buffer and their sizes are acceptable ++ */ ++ ret = call_qop(q, queue_setup, q, &num_buffers, ++ &num_planes, plane_sizes, q->alloc_devs); ++ if (ret) ++ return ret; ++ ++ /* Finally, allocate buffers and video memory */ ++ allocated_buffers = __vb2_queue_alloc(q, memory, *count, ++ num_planes, plane_sizes, req_index, index); ++ if (allocated_buffers == 0) { ++ dprintk(q, 1, "memory allocation failed\n"); ++ return -ENOMEM; ++ } ++ mutex_lock(&q->mmap_lock); ++ q->num_buffers += allocated_buffers; ++ ++ if (ret < 0) { ++ /* ++ * Note: __vb2_queue_free() will subtract 'allocated_buffers' ++ * from q->num_buffers. ++ */ ++ __vb2_queue_free(q, allocated_buffers); ++ mutex_unlock(&q->mmap_lock); ++ return -ENOMEM; ++ } ++ mutex_unlock(&q->mmap_lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(vb2_core_create_single_buf); ++ + int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, +- unsigned int *count, +- unsigned int requested_planes, +- const unsigned int requested_sizes[]) ++ unsigned int *count, unsigned requested_planes, ++ const unsigned requested_sizes[], bool req_index, int index) + { + unsigned int num_planes = 0, num_buffers, allocated_buffers; + unsigned plane_sizes[VB2_MAX_PLANES] = { }; + int ret; + ++ if (req_index && q->num_buffers > index) ++ return 0; ++ + if (q->num_buffers == VB2_MAX_FRAME) { + dprintk(q, 1, "maximum number of buffers already allocated\n"); + return -ENOBUFS; +@@ -918,7 +1037,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, + + /* Finally, allocate buffers and video memory */ + allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, +- num_planes, plane_sizes); ++ num_planes, plane_sizes, req_index, index); + if (allocated_buffers == 0) { + dprintk(q, 1, "memory allocation failed\n"); + return -ENOMEM; +@@ -1930,7 +2049,14 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, + } + EXPORT_SYMBOL_GPL(vb2_core_dqbuf); + +-/* ++ ++void vb2_core_queue_cancel(struct vb2_queue *q) ++{ ++ __vb2_queue_cancel(q); ++} ++EXPORT_SYMBOL_GPL(vb2_core_queue_cancel); ++ ++/** + * __vb2_queue_cancel() - cancel and stop (pause) streaming + * + * Removes all queued buffers from driver's queue and all buffers queued by +@@ -1955,10 +2081,14 @@ static void __vb2_queue_cancel(struct vb2_queue *q) + */ + if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { + for (i = 0; i < q->num_buffers; ++i) +- if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { +- pr_warn("driver bug: stop_streaming operation is leaving buf %p in active state\n", +- q->bufs[i]); +- vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR); ++ if (q->bufs[i] != NULL) ++ { ++ if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) ++ { ++ pr_warn("driver bug: stop_streaming operation is leaving buf %p in active state\n", ++ q->bufs[i]); ++ vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR); ++ } + } + /* Must be zero now */ + WARN_ON(atomic_read(&q->owned_by_drv_count)); +@@ -1994,7 +2124,15 @@ static void __vb2_queue_cancel(struct vb2_queue *q) + */ + for (i = 0; i < q->num_buffers; ++i) { + struct vb2_buffer *vb = q->bufs[i]; +- struct media_request *req = vb->req_obj.req; ++ struct media_request *req; ++ ++ if (vb == NULL) ++ { ++ pr_warn("Buffer at %d is NULL ptr!",i); ++ continue; ++ } ++ ++ req = vb->req_obj.req; + + /* + * If a request is associated with this buffer, then +diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c +index 3f61f5863bf7..ac17b345c461 100644 +--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c ++++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c +@@ -804,7 +804,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) + return ret ? ret : vb2_core_create_bufs(q, create->memory, + &create->count, + requested_planes, +- requested_sizes); ++ requested_sizes, false, 0); + } + EXPORT_SYMBOL_GPL(vb2_create_bufs); + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index a95c73b9bc2c..a80ea8fb4d70 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -1,367 +1,13 @@ +-# SPDX-License-Identifier: GPL-2.0-only +-# +-# Multimedia Video device configuration +-# +- + if VIDEO_V4L2 + +-comment "IR I2C driver auto-selected by 'Autoselect ancillary drivers'" +- depends on MEDIA_SUBDRV_AUTOSELECT && I2C && RC_CORE +- +-config VIDEO_IR_I2C +- tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT || EXPERT +- depends on I2C && RC_CORE +- default y +- help +- Most boards have an IR chip directly connected via GPIO. However, +- some video boards have the IR connected via I2C bus. +- +- If your board doesn't have an I2C IR chip, you may disable this +- option. +- +- In doubt, say Y. +- +-# +-# V4L2 I2C drivers that aren't related with Camera support +-# +- +-comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'" +- depends on MEDIA_HIDE_ANCILLARY_SUBDRV +-# +-# Encoder / Decoder module configuration +-# +- +-menu "Audio decoders, processors and mixers" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_TVAUDIO +- tristate "Simple audio decoder chips" +- depends on VIDEO_V4L2 && I2C +- help +- Support for several audio decoder chips found on some bt8xx boards: +- Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, +- tea6320, tea6420, tda8425, ta8874z. +- Microchip: pic16c54 based design on ProVideo PV951 board. +- +- To compile this driver as a module, choose M here: the +- module will be called tvaudio. +- +-config VIDEO_TDA7432 +- tristate "Philips TDA7432 audio processor" +- depends on VIDEO_V4L2 && I2C +- help +- Support for tda7432 audio decoder chip found on some bt8xx boards. +- +- To compile this driver as a module, choose M here: the +- module will be called tda7432. +- +-config VIDEO_TDA9840 +- tristate "Philips TDA9840 audio processor" +- depends on I2C +- help +- Support for tda9840 audio decoder chip found on some Zoran boards. +- +- To compile this driver as a module, choose M here: the +- module will be called tda9840. +- +-config VIDEO_TDA1997X +- tristate "NXP TDA1997x HDMI receiver" +- depends on VIDEO_V4L2 && I2C +- depends on SND_SOC +- select HDMI +- select SND_PCM +- select V4L2_FWNODE +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. +- +- To compile this driver as a module, choose M here: the +- module will be called tda1997x. +- +-config VIDEO_TEA6415C +- tristate "Philips TEA6415C audio processor" +- depends on I2C +- help +- Support for tea6415c audio decoder chip found on some bt8xx boards. +- +- To compile this driver as a module, choose M here: the +- module will be called tea6415c. +- +-config VIDEO_TEA6420 +- tristate "Philips TEA6420 audio processor" +- depends on I2C +- help +- Support for tea6420 audio decoder chip found on some bt8xx boards. +- +- To compile this driver as a module, choose M here: the +- module will be called tea6420. +- +-config VIDEO_MSP3400 +- tristate "Micronas MSP34xx audio decoders" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Micronas MSP34xx series of audio decoders. +- +- To compile this driver as a module, choose M here: the +- module will be called msp3400. +- +-config VIDEO_CS3308 +- tristate "Cirrus Logic CS3308 audio ADC" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Cirrus Logic CS3308 High Performance 8-Channel +- Analog Volume Control +- +- To compile this driver as a module, choose M here: the +- module will be called cs3308. +- +-config VIDEO_CS5345 +- tristate "Cirrus Logic CS5345 audio ADC" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Cirrus Logic CS5345 24-bit, 192 kHz +- stereo A/D converter. +- +- To compile this driver as a module, choose M here: the +- module will be called cs5345. +- +-config VIDEO_CS53L32A +- tristate "Cirrus Logic CS53L32A audio ADC" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Cirrus Logic CS53L32A low voltage +- stereo A/D converter. +- +- To compile this driver as a module, choose M here: the +- module will be called cs53l32a. +- +-config VIDEO_TLV320AIC23B +- tristate "Texas Instruments TLV320AIC23B audio codec" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Texas Instruments TLV320AIC23B audio codec. +- +- To compile this driver as a module, choose M here: the +- module will be called tlv320aic23b. +- +-config VIDEO_UDA1342 +- tristate "Philips UDA1342 audio codec" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Philips UDA1342 audio codec. +- +- To compile this driver as a module, choose M here: the +- module will be called uda1342. +- +-config VIDEO_WM8775 +- tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Wolfson Microelectronics WM8775 high +- performance stereo A/D Converter with a 4 channel input mixer. +- +- To compile this driver as a module, choose M here: the +- module will be called wm8775. +- +-config VIDEO_WM8739 +- tristate "Wolfson Microelectronics WM8739 stereo audio ADC" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Wolfson Microelectronics WM8739 +- stereo A/D Converter. +- +- To compile this driver as a module, choose M here: the +- module will be called wm8739. +- +-config VIDEO_VP27SMPX +- tristate "Panasonic VP27's internal MPX" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the internal MPX of the Panasonic VP27s tuner. +- +- To compile this driver as a module, choose M here: the +- module will be called vp27smpx. +- +-config VIDEO_SONY_BTF_MPX +- tristate "Sony BTF's internal MPX" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the internal MPX of the Sony BTF-PG472Z tuner. +- +- To compile this driver as a module, choose M here: the +- module will be called sony-btf-mpx. +-endmenu +- +-menu "RDS decoders" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_SAA6588 +- tristate "SAA6588 Radio Chip RDS decoder support" +- depends on VIDEO_V4L2 && I2C +- +- help +- Support for this Radio Data System (RDS) decoder. This allows +- seeing radio station identification transmitted using this +- standard. +- +- To compile this driver as a module, choose M here: the +- module will be called saa6588. +-endmenu +- +-menu "Video decoders" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_ADV7180 +- tristate "Analog Devices ADV7180 decoder" +- depends on GPIOLIB && VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- Support for the Analog Devices ADV7180 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7180. +- +-config VIDEO_ADV7183 +- tristate "Analog Devices ADV7183 decoder" +- depends on VIDEO_V4L2 && I2C +- help +- V4l2 subdevice driver for the Analog Devices +- ADV7183 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7183. +- +-config VIDEO_ADV748X +- tristate "Analog Devices ADV748x decoder" +- depends on VIDEO_V4L2 && I2C +- depends on OF +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_I2C +- select V4L2_FWNODE +- help +- V4L2 subdevice driver for the Analog Devices +- ADV7481 and ADV7482 HDMI/Analog video decoders. +- +- To compile this driver as a module, choose M here: the +- module will be called adv748x. +- +-config VIDEO_ADV7604 +- tristate "Analog Devices ADV7604 decoder" +- depends on VIDEO_V4L2 && I2C +- depends on GPIOLIB || COMPILE_TEST +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_I2C +- select HDMI +- select V4L2_FWNODE +- help +- Support for the Analog Devices ADV7604 video decoder. +- +- This is a Analog Devices Component/Graphics Digitizer +- with 4:1 Multiplexed HDMI Receiver. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7604. +- +-config VIDEO_ADV7604_CEC +- bool "Enable Analog Devices ADV7604 CEC support" +- depends on VIDEO_ADV7604 +- select CEC_CORE +- help +- When selected the adv7604 will support the optional +- HDMI CEC feature. +- +-config VIDEO_ADV7842 +- tristate "Analog Devices ADV7842 decoder" +- depends on VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select HDMI +- help +- Support for the Analog Devices ADV7842 video decoder. +- +- This is a Analog Devices Component/Graphics/SD Digitizer +- with 2:1 Multiplexed HDMI Receiver. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7842. +- +-config VIDEO_ADV7842_CEC +- bool "Enable Analog Devices ADV7842 CEC support" +- depends on VIDEO_ADV7842 +- select CEC_CORE +- help +- When selected the adv7842 will support the optional +- HDMI CEC feature. +- +-config VIDEO_BT819 +- tristate "BT819A VideoStream decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for BT819A video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called bt819. +- +-config VIDEO_BT856 +- tristate "BT856 VideoStream decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for BT856 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called bt856. +- +-config VIDEO_BT866 +- tristate "BT866 VideoStream decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for BT866 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called bt866. +- +-config VIDEO_KS0127 +- tristate "KS0127 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for KS0127 video decoder. +- +- This chip is used on AverMedia AVS6EYES Zoran-based MJPEG +- cards. +- +- To compile this driver as a module, choose M here: the +- module will be called ks0127. +- +-config VIDEO_ML86V7667 +- tristate "OKI ML86V7667 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the OKI Semiconductor ML86V7667 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called ml86v7667. +- +-config VIDEO_SAA7110 +- tristate "Philips SAA7110 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Philips SAA7110 video decoders. +- +- To compile this driver as a module, choose M here: the +- module will be called saa7110. +- +-config VIDEO_SAA711X +- tristate "Philips SAA7111/3/4/5 video decoders" +- depends on VIDEO_V4L2 && I2C ++config VIDEO_AVT_CSI2 ++ tristate "Allied Vision CSI2 camera support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help +- Support for the Philips SAA7111/3/4/5 video decoders. ++ This is a Video4Linux2 sensor-level driver for Allied Vision camera. + + To compile this driver as a module, choose M here: the +- module will be called saa7115. ++ module will be called avt_csi2. + + config VIDEO_TC358743 + tristate "Toshiba TC358743 decoder" +@@ -384,951 +30,4 @@ config VIDEO_TC358743_CEC + When selected the tc358743 will support the optional + HDMI CEC feature. + +-config VIDEO_TVP514X +- tristate "Texas Instruments TVP514x video decoder" +- depends on VIDEO_V4L2 && I2C +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the TI TVP5146/47 +- decoder. It is currently working with the TI OMAP3 camera +- controller. +- +- To compile this driver as a module, choose M here: the +- module will be called tvp514x. +- +-config VIDEO_TVP5150 +- tristate "Texas Instruments TVP5150 video decoder" +- depends on VIDEO_V4L2 && I2C +- select V4L2_FWNODE +- select REGMAP_I2C +- help +- Support for the Texas Instruments TVP5150 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called tvp5150. +- +-config VIDEO_TVP7002 +- tristate "Texas Instruments TVP7002 video decoder" +- depends on VIDEO_V4L2 && I2C +- select V4L2_FWNODE +- help +- Support for the Texas Instruments TVP7002 video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called tvp7002. +- +-config VIDEO_TW2804 +- tristate "Techwell TW2804 multiple video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Techwell tw2804 multiple video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called tw2804. +- +-config VIDEO_TW9903 +- tristate "Techwell TW9903 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Techwell tw9903 multi-standard video decoder +- with high quality down scaler. +- +- To compile this driver as a module, choose M here: the +- module will be called tw9903. +- +-config VIDEO_TW9906 +- tristate "Techwell TW9906 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Techwell tw9906 enhanced multi-standard comb filter +- video decoder with YCbCr input support. +- +- To compile this driver as a module, choose M here: the +- module will be called tw9906. +- +-config VIDEO_TW9910 +- tristate "Techwell TW9910 video decoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for Techwell TW9910 NTSC/PAL/SECAM video decoder. +- +- To compile this driver as a module, choose M here: the +- module will be called tw9910. +- +-config VIDEO_VPX3220 +- tristate "vpx3220a, vpx3216b & vpx3214c video decoders" +- depends on VIDEO_V4L2 && I2C +- help +- Support for VPX322x video decoders. +- +- To compile this driver as a module, choose M here: the +- module will be called vpx3220. +- +-config VIDEO_MAX9286 +- tristate "Maxim MAX9286 GMSL deserializer support" +- depends on I2C && I2C_MUX +- depends on OF_GPIO +- select V4L2_FWNODE +- select VIDEO_V4L2_SUBDEV_API +- select MEDIA_CONTROLLER +- help +- This driver supports the Maxim MAX9286 GMSL deserializer. +- +- To compile this driver as a module, choose M here: the +- module will be called max9286. +- +-comment "Video and audio decoders" +- +-config VIDEO_SAA717X +- tristate "Philips SAA7171/3/4 audio/video decoders" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Philips SAA7171/3/4 audio/video decoders. +- +- To compile this driver as a module, choose M here: the +- module will be called saa717x. +- +-source "drivers/media/i2c/cx25840/Kconfig" +- +-endmenu +- +-menu "Video encoders" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_SAA7127 +- tristate "Philips SAA7127/9 digital video encoders" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Philips SAA7127/9 digital video encoders. +- +- To compile this driver as a module, choose M here: the +- module will be called saa7127. +- +-config VIDEO_SAA7185 +- tristate "Philips SAA7185 video encoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Philips SAA7185 video encoder. +- +- To compile this driver as a module, choose M here: the +- module will be called saa7185. +- +-config VIDEO_ADV7170 +- tristate "Analog Devices ADV7170 video encoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Analog Devices ADV7170 video encoder driver +- +- To compile this driver as a module, choose M here: the +- module will be called adv7170. +- +-config VIDEO_ADV7175 +- tristate "Analog Devices ADV7175 video encoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Analog Devices ADV7175 video encoder driver +- +- To compile this driver as a module, choose M here: the +- module will be called adv7175. +- +-config VIDEO_ADV7343 +- tristate "ADV7343 video encoder" +- depends on I2C +- help +- Support for Analog Devices I2C bus based ADV7343 encoder. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7343. +- +-config VIDEO_ADV7393 +- tristate "ADV7393 video encoder" +- depends on I2C +- help +- Support for Analog Devices I2C bus based ADV7393 encoder. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7393. +- +-config VIDEO_ADV7511 +- tristate "Analog Devices ADV7511 encoder" +- depends on VIDEO_V4L2 && I2C +- depends on DRM_I2C_ADV7511=n || COMPILE_TEST +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select HDMI +- help +- Support for the Analog Devices ADV7511 video encoder. +- +- This is a Analog Devices HDMI transmitter. +- +- To compile this driver as a module, choose M here: the +- module will be called adv7511. +- +-config VIDEO_ADV7511_CEC +- bool "Enable Analog Devices ADV7511 CEC support" +- depends on VIDEO_ADV7511 +- select CEC_CORE +- help +- When selected the adv7511 will support the optional +- HDMI CEC feature. +- +-config VIDEO_AD9389B +- tristate "Analog Devices AD9389B encoder" +- depends on VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- +- help +- Support for the Analog Devices AD9389B video encoder. +- +- This is a Analog Devices HDMI transmitter. +- +- To compile this driver as a module, choose M here: the +- module will be called ad9389b. +- +-config VIDEO_AK881X +- tristate "AK8813/AK8814 video encoders" +- depends on I2C +- help +- Video output driver for AKM AK8813 and AK8814 TV encoders +- +-config VIDEO_THS8200 +- tristate "Texas Instruments THS8200 video encoder" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Texas Instruments THS8200 video encoder. +- +- To compile this driver as a module, choose M here: the +- module will be called ths8200. +-endmenu +- +-menu "Video improvement chips" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_UPD64031A +- tristate "NEC Electronics uPD64031A Ghost Reduction" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the NEC Electronics uPD64031A Ghost Reduction +- video chip. It is most often found in NTSC TV cards made for +- Japan and is used to reduce the 'ghosting' effect that can +- be present in analog TV broadcasts. +- +- To compile this driver as a module, choose M here: the +- module will be called upd64031a. +- +-config VIDEO_UPD64083 +- tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the NEC Electronics uPD64083 3-Dimensional Y/C +- separation video chip. It is used to improve the quality of +- the colors of a composite signal. +- +- To compile this driver as a module, choose M here: the +- module will be called upd64083. +-endmenu +- +-menu "Audio/Video compression chips" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_SAA6752HS +- tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" +- depends on VIDEO_V4L2 && I2C +- select CRC32 +- help +- Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 +- audio encoder with multiplexer. +- +- To compile this driver as a module, choose M here: the +- module will be called saa6752hs. +- +-endmenu +- +-menu "SDR tuner chips" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config SDR_MAX2175 +- tristate "Maxim 2175 RF to Bits tuner" +- depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C +- select REGMAP_I2C +- help +- Support for Maxim 2175 tuner. It is an advanced analog/digital +- radio receiver with RF-to-Bits front-end designed for SDR solutions. +- +- To compile this driver as a module, choose M here; the +- module will be called max2175. +- +- +-endmenu +- +-menu "Miscellaneous helper chips" +- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +- +-config VIDEO_THS7303 +- tristate "THS7303/53 Video Amplifier" +- depends on VIDEO_V4L2 && I2C +- help +- Support for TI THS7303/53 video amplifier +- +- To compile this driver as a module, choose M here: the +- module will be called ths7303. +- +-config VIDEO_M52790 +- tristate "Mitsubishi M52790 A/V switch" +- depends on VIDEO_V4L2 && I2C +- help +- Support for the Mitsubishi M52790 A/V switch. +- +- To compile this driver as a module, choose M here: the +- module will be called m52790. +- +-config VIDEO_I2C +- tristate "I2C transport video support" +- depends on VIDEO_V4L2 && I2C +- select VIDEOBUF2_VMALLOC +- imply HWMON +- help +- Enable the I2C transport video support which supports the +- following: +- * Panasonic AMG88xx Grid-Eye Sensors +- * Melexis MLX90640 Thermal Cameras +- +- To compile this driver as a module, choose M here: the +- module will be called video-i2c +- +-config VIDEO_ST_MIPID02 +- tristate "STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. +- It is used to allow usage of CSI-2 sensor with PARALLEL port +- controller. +- +- To compile this driver as a module, choose M here: the +- module will be called st-mipid02. +-endmenu +- +-# +-# V4L2 I2C drivers that are related with Camera support +-# +- +-menu "Camera sensor devices" +- visible if MEDIA_CAMERA_SUPPORT +- +-config VIDEO_APTINA_PLL +- tristate +- +-config VIDEO_SMIAPP_PLL +- tristate +- +-config VIDEO_HI556 +- tristate "Hynix Hi-556 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the Hynix +- Hi-556 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called hi556. +- +-config VIDEO_IMX214 +- tristate "Sony IMX214 sensor support" +- depends on GPIOLIB && I2C && VIDEO_V4L2 +- select V4L2_FWNODE +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_I2C +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX214 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called imx214. +- +-config VIDEO_IMX219 +- tristate "Sony IMX219 sensor support" +- depends on I2C && VIDEO_V4L2 +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX219 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called imx219. +- +-config VIDEO_IMX258 +- tristate "Sony IMX258 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX258 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called imx258. +- +-config VIDEO_IMX274 +- tristate "Sony IMX274 sensor support" +- depends on I2C && VIDEO_V4L2 +- select REGMAP_I2C +- help +- This is a V4L2 sensor driver for the Sony IMX274 +- CMOS image sensor. +- +-config VIDEO_IMX290 +- tristate "Sony IMX290 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_I2C +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX290 camera sensor. +- +- To compile this driver as a module, choose M here: the +- module will be called imx290. +- +-config VIDEO_IMX319 +- tristate "Sony IMX319 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX319 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called imx319. +- +-config VIDEO_IMX355 +- tristate "Sony IMX355 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a Video4Linux2 sensor driver for the Sony +- IMX355 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called imx355. +- +-config VIDEO_OV2640 +- tristate "OmniVision OV2640 sensor support" +- depends on VIDEO_V4L2 && I2C +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV2640 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov2640. +- +-config VIDEO_OV2659 +- tristate "OmniVision OV2659 sensor support" +- depends on VIDEO_V4L2 && I2C && GPIOLIB +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV2659 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov2659. +- +-config VIDEO_OV2680 +- tristate "OmniVision OV2680 sensor support" +- depends on VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV2680 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov2680. +- +-config VIDEO_OV2685 +- tristate "OmniVision OV2685 sensor support" +- depends on VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV2685 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov2685. +- +-config VIDEO_OV2740 +- tristate "OmniVision OV2740 sensor support" +- depends on VIDEO_V4L2 && I2C +- depends on ACPI || COMPILE_TEST +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV2740 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov2740. +- +-config VIDEO_OV5640 +- tristate "OmniVision OV5640 sensor support" +- depends on OF +- depends on GPIOLIB && VIDEO_V4L2 && I2C +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the Omnivision +- OV5640 camera sensor with a MIPI CSI-2 interface. +- +-config VIDEO_OV5645 +- tristate "OmniVision OV5645 sensor support" +- depends on OF +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV5645 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov5645. +- +-config VIDEO_OV5647 +- tristate "OmniVision OV5647 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV5647 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov5647. +- +-config VIDEO_OV6650 +- tristate "OmniVision OV6650 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV6650 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov6650. +- +-config VIDEO_OV5670 +- tristate "OmniVision OV5670 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV5670 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov5670. +- +-config VIDEO_OV5675 +- tristate "OmniVision OV5675 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV5675 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov5675. +- +-config VIDEO_OV5695 +- tristate "OmniVision OV5695 sensor support" +- depends on I2C && VIDEO_V4L2 +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV5695 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov5695. +- +-config VIDEO_OV7251 +- tristate "OmniVision OV7251 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV7251 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov7251. +- +-config VIDEO_OV772X +- tristate "OmniVision OV772x sensor support" +- depends on I2C && VIDEO_V4L2 +- select REGMAP_SCCB +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV772x camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov772x. +- +-config VIDEO_OV7640 +- tristate "OmniVision OV7640 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV7640 camera. +- +- To compile this driver as a module, choose M here: the +- module will be called ov7640. +- +-config VIDEO_OV7670 +- tristate "OmniVision OV7670 sensor support" +- depends on I2C && VIDEO_V4L2 +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV7670 VGA camera. It currently only works with the M88ALP01 +- controller. +- +-config VIDEO_OV7740 +- tristate "OmniVision OV7740 sensor support" +- depends on I2C && VIDEO_V4L2 +- select REGMAP_SCCB +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV7740 VGA camera sensor. +- +-config VIDEO_OV8856 +- tristate "OmniVision OV8856 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV8856 camera sensor. +- +- To compile this driver as a module, choose M here: the +- module will be called ov8856. +- +-config VIDEO_OV9640 +- tristate "OmniVision OV9640 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV9640 camera sensor. +- +-config VIDEO_OV9650 +- tristate "OmniVision OV9650/OV9652 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_SCCB +- help +- This is a V4L2 sensor driver for the Omnivision +- OV9650 and OV9652 camera sensors. +- +-config VIDEO_OV13858 +- tristate "OmniVision OV13858 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the OmniVision +- OV13858 camera. +- +-config VIDEO_VS6624 +- tristate "ST VS6624 sensor support" +- depends on VIDEO_V4L2 && I2C +- help +- This is a Video4Linux2 sensor driver for the ST VS6624 +- camera. +- +- To compile this driver as a module, choose M here: the +- module will be called vs6624. +- +-config VIDEO_MT9M001 +- tristate "mt9m001 support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This driver supports MT9M001 cameras from Micron, monochrome +- and colour models. +- +-config VIDEO_MT9M032 +- tristate "MT9M032 camera sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select VIDEO_APTINA_PLL +- help +- This driver supports MT9M032 camera sensors from Aptina, monochrome +- models only. +- +-config VIDEO_MT9M111 +- tristate "mt9m111, mt9m112 and mt9m131 support" +- depends on I2C && VIDEO_V4L2 +- select V4L2_FWNODE +- help +- This driver supports MT9M111, MT9M112 and MT9M131 cameras from +- Micron/Aptina +- +-config VIDEO_MT9P031 +- tristate "Aptina MT9P031 support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select VIDEO_APTINA_PLL +- help +- This is a Video4Linux2 sensor driver for the Aptina +- (Micron) mt9p031 5 Mpixel camera. +- +-config VIDEO_MT9T001 +- tristate "Aptina MT9T001 support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a Video4Linux2 sensor driver for the Aptina +- (Micron) mt0t001 3 Mpixel camera. +- +-config VIDEO_MT9T112 +- tristate "Aptina MT9T111/MT9T112 support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the Aptina +- (Micron) MT9T111 and MT9T112 3 Mpixel camera. +- +- To compile this driver as a module, choose M here: the +- module will be called mt9t112. +- +-config VIDEO_MT9V011 +- tristate "Micron mt9v011 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the Micron +- mt0v011 1.3 Mpixel camera. It currently only works with the +- em28xx driver. +- +-config VIDEO_MT9V032 +- tristate "Micron MT9V032 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select REGMAP_I2C +- select V4L2_FWNODE +- help +- This is a Video4Linux2 sensor driver for the Micron +- MT9V032 752x480 CMOS sensor. +- +-config VIDEO_MT9V111 +- tristate "Aptina MT9V111 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a Video4Linux2 sensor driver for the Aptina/Micron +- MT9V111 sensor. +- +- To compile this driver as a module, choose M here: the +- module will be called mt9v111. +- +-config VIDEO_SR030PC30 +- tristate "Siliconfile SR030PC30 sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This driver supports SR030PC30 VGA camera from Siliconfile +- +-config VIDEO_NOON010PC30 +- tristate "Siliconfile NOON010PC30 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This driver supports NOON010PC30 CIF camera from Siliconfile +- +-source "drivers/media/i2c/m5mols/Kconfig" +- +-config VIDEO_RDACM20 +- tristate "IMI RDACM20 camera support" +- depends on I2C +- select V4L2_FWNODE +- select VIDEO_V4L2_SUBDEV_API +- select MEDIA_CONTROLLER +- help +- This driver supports the IMI RDACM20 GMSL camera, used in +- ADAS systems. +- +- This camera should be used in conjunction with a GMSL +- deserialiser such as the MAX9286. +- +-config VIDEO_RJ54N1 +- tristate "Sharp RJ54N1CB0C sensor support" +- depends on I2C && VIDEO_V4L2 +- help +- This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image +- sensor. +- +- To compile this driver as a module, choose M here: the +- module will be called rj54n1. +- +-config VIDEO_S5K6AA +- tristate "Samsung S5K6AAFX sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M +- camera sensor with an embedded SoC image signal processor. +- +-config VIDEO_S5K6A3 +- tristate "Samsung S5K6A3 sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a V4L2 sensor driver for Samsung S5K6A3 raw +- camera sensor. +- +-config VIDEO_S5K4ECGX +- tristate "Samsung S5K4ECGX sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select CRC32 +- help +- This is a V4L2 sensor driver for Samsung S5K4ECGX 5M +- camera sensor with an embedded SoC image signal processor. +- +-config VIDEO_S5K5BAF +- tristate "Samsung S5K5BAF sensor support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a V4L2 sensor driver for Samsung S5K5BAF 2M +- camera sensor with an embedded SoC image signal processor. +- +-source "drivers/media/i2c/smiapp/Kconfig" +-source "drivers/media/i2c/et8ek8/Kconfig" +- +-config VIDEO_S5C73M3 +- tristate "Samsung S5C73M3 sensor support" +- depends on I2C && SPI && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a V4L2 sensor driver for Samsung S5C73M3 +- 8 Mpixel camera. +- +-endmenu +- +-menu "Lens drivers" +- visible if MEDIA_CAMERA_SUPPORT +- +-config VIDEO_AD5820 +- tristate "AD5820 lens voice coil support" +- depends on GPIOLIB && I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- help +- This is a driver for the AD5820 camera lens voice coil. +- It is used for example in Nokia N900 (RX-51). +- +-config VIDEO_AK7375 +- tristate "AK7375 lens voice coil support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a driver for the AK7375 camera lens voice coil. +- AK7375 is a 12 bit DAC with 120mA output current sink +- capability. This is designed for linear control of +- voice coil motors, controlled via I2C serial interface. +- +-config VIDEO_DW9714 +- tristate "DW9714 lens voice coil support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a driver for the DW9714 camera lens voice coil. +- DW9714 is a 10 bit DAC with 120mA output current sink +- capability. This is designed for linear control of +- voice coil motors, controlled via I2C serial interface. +- +-config VIDEO_DW9768 +- tristate "DW9768 lens voice coil support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- select V4L2_FWNODE +- help +- This is a driver for the DW9768 camera lens voice coil. +- DW9768 is a 10 bit DAC with 100mA output current sink +- capability. This is designed for linear control of +- voice coil motors, controlled via I2C serial interface. +- +-config VIDEO_DW9807_VCM +- tristate "DW9807 lens voice coil support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select VIDEO_V4L2_SUBDEV_API +- help +- This is a driver for the DW9807 camera lens voice coil. +- DW9807 is a 10 bit DAC with 100mA output current sink +- capability. This is designed for linear control of +- voice coil motors, controlled via I2C serial interface. +- +-endmenu +- +-menu "Flash devices" +- visible if MEDIA_CAMERA_SUPPORT +- +-config VIDEO_ADP1653 +- tristate "ADP1653 flash support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- help +- This is a driver for the ADP1653 flash controller. It is used for +- example in Nokia N900. +- +-config VIDEO_LM3560 +- tristate "LM3560 dual flash driver support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select REGMAP_I2C +- help +- This is a driver for the lm3560 dual flash controllers. It controls +- flash, torch LEDs. +- +-config VIDEO_LM3646 +- tristate "LM3646 dual flash driver support" +- depends on I2C && VIDEO_V4L2 +- select MEDIA_CONTROLLER +- select REGMAP_I2C +- help +- This is a driver for the lm3646 dual flash controllers. It controls +- flash, torch LEDs. +-endmenu +- + endif # VIDEO_V4L2 +diff --git a/drivers/media/i2c/Kconfig_ori b/drivers/media/i2c/Kconfig_ori +new file mode 100644 +index 000000000000..a95c73b9bc2c +--- /dev/null ++++ b/drivers/media/i2c/Kconfig_ori +@@ -0,0 +1,1334 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# Multimedia Video device configuration ++# ++ ++if VIDEO_V4L2 ++ ++comment "IR I2C driver auto-selected by 'Autoselect ancillary drivers'" ++ depends on MEDIA_SUBDRV_AUTOSELECT && I2C && RC_CORE ++ ++config VIDEO_IR_I2C ++ tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT || EXPERT ++ depends on I2C && RC_CORE ++ default y ++ help ++ Most boards have an IR chip directly connected via GPIO. However, ++ some video boards have the IR connected via I2C bus. ++ ++ If your board doesn't have an I2C IR chip, you may disable this ++ option. ++ ++ In doubt, say Y. ++ ++# ++# V4L2 I2C drivers that aren't related with Camera support ++# ++ ++comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'" ++ depends on MEDIA_HIDE_ANCILLARY_SUBDRV ++# ++# Encoder / Decoder module configuration ++# ++ ++menu "Audio decoders, processors and mixers" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_TVAUDIO ++ tristate "Simple audio decoder chips" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for several audio decoder chips found on some bt8xx boards: ++ Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, ++ tea6320, tea6420, tda8425, ta8874z. ++ Microchip: pic16c54 based design on ProVideo PV951 board. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvaudio. ++ ++config VIDEO_TDA7432 ++ tristate "Philips TDA7432 audio processor" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for tda7432 audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tda7432. ++ ++config VIDEO_TDA9840 ++ tristate "Philips TDA9840 audio processor" ++ depends on I2C ++ help ++ Support for tda9840 audio decoder chip found on some Zoran boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tda9840. ++ ++config VIDEO_TDA1997X ++ tristate "NXP TDA1997x HDMI receiver" ++ depends on VIDEO_V4L2 && I2C ++ depends on SND_SOC ++ select HDMI ++ select SND_PCM ++ select V4L2_FWNODE ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tda1997x. ++ ++config VIDEO_TEA6415C ++ tristate "Philips TEA6415C audio processor" ++ depends on I2C ++ help ++ Support for tea6415c audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tea6415c. ++ ++config VIDEO_TEA6420 ++ tristate "Philips TEA6420 audio processor" ++ depends on I2C ++ help ++ Support for tea6420 audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tea6420. ++ ++config VIDEO_MSP3400 ++ tristate "Micronas MSP34xx audio decoders" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Micronas MSP34xx series of audio decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called msp3400. ++ ++config VIDEO_CS3308 ++ tristate "Cirrus Logic CS3308 audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Cirrus Logic CS3308 High Performance 8-Channel ++ Analog Volume Control ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cs3308. ++ ++config VIDEO_CS5345 ++ tristate "Cirrus Logic CS5345 audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Cirrus Logic CS5345 24-bit, 192 kHz ++ stereo A/D converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cs5345. ++ ++config VIDEO_CS53L32A ++ tristate "Cirrus Logic CS53L32A audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Cirrus Logic CS53L32A low voltage ++ stereo A/D converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cs53l32a. ++ ++config VIDEO_TLV320AIC23B ++ tristate "Texas Instruments TLV320AIC23B audio codec" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Texas Instruments TLV320AIC23B audio codec. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tlv320aic23b. ++ ++config VIDEO_UDA1342 ++ tristate "Philips UDA1342 audio codec" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips UDA1342 audio codec. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called uda1342. ++ ++config VIDEO_WM8775 ++ tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Wolfson Microelectronics WM8775 high ++ performance stereo A/D Converter with a 4 channel input mixer. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm8775. ++ ++config VIDEO_WM8739 ++ tristate "Wolfson Microelectronics WM8739 stereo audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Wolfson Microelectronics WM8739 ++ stereo A/D Converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm8739. ++ ++config VIDEO_VP27SMPX ++ tristate "Panasonic VP27's internal MPX" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the internal MPX of the Panasonic VP27s tuner. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vp27smpx. ++ ++config VIDEO_SONY_BTF_MPX ++ tristate "Sony BTF's internal MPX" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the internal MPX of the Sony BTF-PG472Z tuner. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sony-btf-mpx. ++endmenu ++ ++menu "RDS decoders" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_SAA6588 ++ tristate "SAA6588 Radio Chip RDS decoder support" ++ depends on VIDEO_V4L2 && I2C ++ ++ help ++ Support for this Radio Data System (RDS) decoder. This allows ++ seeing radio station identification transmitted using this ++ standard. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa6588. ++endmenu ++ ++menu "Video decoders" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_ADV7180 ++ tristate "Analog Devices ADV7180 decoder" ++ depends on GPIOLIB && VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ Support for the Analog Devices ADV7180 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7180. ++ ++config VIDEO_ADV7183 ++ tristate "Analog Devices ADV7183 decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ V4l2 subdevice driver for the Analog Devices ++ ADV7183 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7183. ++ ++config VIDEO_ADV748X ++ tristate "Analog Devices ADV748x decoder" ++ depends on VIDEO_V4L2 && I2C ++ depends on OF ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_I2C ++ select V4L2_FWNODE ++ help ++ V4L2 subdevice driver for the Analog Devices ++ ADV7481 and ADV7482 HDMI/Analog video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv748x. ++ ++config VIDEO_ADV7604 ++ tristate "Analog Devices ADV7604 decoder" ++ depends on VIDEO_V4L2 && I2C ++ depends on GPIOLIB || COMPILE_TEST ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_I2C ++ select HDMI ++ select V4L2_FWNODE ++ help ++ Support for the Analog Devices ADV7604 video decoder. ++ ++ This is a Analog Devices Component/Graphics Digitizer ++ with 4:1 Multiplexed HDMI Receiver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7604. ++ ++config VIDEO_ADV7604_CEC ++ bool "Enable Analog Devices ADV7604 CEC support" ++ depends on VIDEO_ADV7604 ++ select CEC_CORE ++ help ++ When selected the adv7604 will support the optional ++ HDMI CEC feature. ++ ++config VIDEO_ADV7842 ++ tristate "Analog Devices ADV7842 decoder" ++ depends on VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select HDMI ++ help ++ Support for the Analog Devices ADV7842 video decoder. ++ ++ This is a Analog Devices Component/Graphics/SD Digitizer ++ with 2:1 Multiplexed HDMI Receiver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7842. ++ ++config VIDEO_ADV7842_CEC ++ bool "Enable Analog Devices ADV7842 CEC support" ++ depends on VIDEO_ADV7842 ++ select CEC_CORE ++ help ++ When selected the adv7842 will support the optional ++ HDMI CEC feature. ++ ++config VIDEO_BT819 ++ tristate "BT819A VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for BT819A video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt819. ++ ++config VIDEO_BT856 ++ tristate "BT856 VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for BT856 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt856. ++ ++config VIDEO_BT866 ++ tristate "BT866 VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for BT866 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt866. ++ ++config VIDEO_KS0127 ++ tristate "KS0127 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for KS0127 video decoder. ++ ++ This chip is used on AverMedia AVS6EYES Zoran-based MJPEG ++ cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ks0127. ++ ++config VIDEO_ML86V7667 ++ tristate "OKI ML86V7667 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the OKI Semiconductor ML86V7667 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ml86v7667. ++ ++config VIDEO_SAA7110 ++ tristate "Philips SAA7110 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips SAA7110 video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7110. ++ ++config VIDEO_SAA711X ++ tristate "Philips SAA7111/3/4/5 video decoders" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips SAA7111/3/4/5 video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7115. ++ ++config VIDEO_TC358743 ++ tristate "Toshiba TC358743 decoder" ++ depends on VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select HDMI ++ select V4L2_FWNODE ++ help ++ Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tc358743. ++ ++config VIDEO_TC358743_CEC ++ bool "Enable Toshiba TC358743 CEC support" ++ depends on VIDEO_TC358743 ++ select CEC_CORE ++ help ++ When selected the tc358743 will support the optional ++ HDMI CEC feature. ++ ++config VIDEO_TVP514X ++ tristate "Texas Instruments TVP514x video decoder" ++ depends on VIDEO_V4L2 && I2C ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the TI TVP5146/47 ++ decoder. It is currently working with the TI OMAP3 camera ++ controller. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp514x. ++ ++config VIDEO_TVP5150 ++ tristate "Texas Instruments TVP5150 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ select V4L2_FWNODE ++ select REGMAP_I2C ++ help ++ Support for the Texas Instruments TVP5150 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp5150. ++ ++config VIDEO_TVP7002 ++ tristate "Texas Instruments TVP7002 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ select V4L2_FWNODE ++ help ++ Support for the Texas Instruments TVP7002 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp7002. ++ ++config VIDEO_TW2804 ++ tristate "Techwell TW2804 multiple video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Techwell tw2804 multiple video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tw2804. ++ ++config VIDEO_TW9903 ++ tristate "Techwell TW9903 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Techwell tw9903 multi-standard video decoder ++ with high quality down scaler. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tw9903. ++ ++config VIDEO_TW9906 ++ tristate "Techwell TW9906 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Techwell tw9906 enhanced multi-standard comb filter ++ video decoder with YCbCr input support. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tw9906. ++ ++config VIDEO_TW9910 ++ tristate "Techwell TW9910 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for Techwell TW9910 NTSC/PAL/SECAM video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tw9910. ++ ++config VIDEO_VPX3220 ++ tristate "vpx3220a, vpx3216b & vpx3214c video decoders" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for VPX322x video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpx3220. ++ ++config VIDEO_MAX9286 ++ tristate "Maxim MAX9286 GMSL deserializer support" ++ depends on I2C && I2C_MUX ++ depends on OF_GPIO ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ select MEDIA_CONTROLLER ++ help ++ This driver supports the Maxim MAX9286 GMSL deserializer. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called max9286. ++ ++comment "Video and audio decoders" ++ ++config VIDEO_SAA717X ++ tristate "Philips SAA7171/3/4 audio/video decoders" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips SAA7171/3/4 audio/video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa717x. ++ ++source "drivers/media/i2c/cx25840/Kconfig" ++ ++endmenu ++ ++menu "Video encoders" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_SAA7127 ++ tristate "Philips SAA7127/9 digital video encoders" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips SAA7127/9 digital video encoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7127. ++ ++config VIDEO_SAA7185 ++ tristate "Philips SAA7185 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Philips SAA7185 video encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7185. ++ ++config VIDEO_ADV7170 ++ tristate "Analog Devices ADV7170 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Analog Devices ADV7170 video encoder driver ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7170. ++ ++config VIDEO_ADV7175 ++ tristate "Analog Devices ADV7175 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Analog Devices ADV7175 video encoder driver ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7175. ++ ++config VIDEO_ADV7343 ++ tristate "ADV7343 video encoder" ++ depends on I2C ++ help ++ Support for Analog Devices I2C bus based ADV7343 encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7343. ++ ++config VIDEO_ADV7393 ++ tristate "ADV7393 video encoder" ++ depends on I2C ++ help ++ Support for Analog Devices I2C bus based ADV7393 encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7393. ++ ++config VIDEO_ADV7511 ++ tristate "Analog Devices ADV7511 encoder" ++ depends on VIDEO_V4L2 && I2C ++ depends on DRM_I2C_ADV7511=n || COMPILE_TEST ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select HDMI ++ help ++ Support for the Analog Devices ADV7511 video encoder. ++ ++ This is a Analog Devices HDMI transmitter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7511. ++ ++config VIDEO_ADV7511_CEC ++ bool "Enable Analog Devices ADV7511 CEC support" ++ depends on VIDEO_ADV7511 ++ select CEC_CORE ++ help ++ When selected the adv7511 will support the optional ++ HDMI CEC feature. ++ ++config VIDEO_AD9389B ++ tristate "Analog Devices AD9389B encoder" ++ depends on VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ ++ help ++ Support for the Analog Devices AD9389B video encoder. ++ ++ This is a Analog Devices HDMI transmitter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ad9389b. ++ ++config VIDEO_AK881X ++ tristate "AK8813/AK8814 video encoders" ++ depends on I2C ++ help ++ Video output driver for AKM AK8813 and AK8814 TV encoders ++ ++config VIDEO_THS8200 ++ tristate "Texas Instruments THS8200 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Texas Instruments THS8200 video encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ths8200. ++endmenu ++ ++menu "Video improvement chips" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_UPD64031A ++ tristate "NEC Electronics uPD64031A Ghost Reduction" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the NEC Electronics uPD64031A Ghost Reduction ++ video chip. It is most often found in NTSC TV cards made for ++ Japan and is used to reduce the 'ghosting' effect that can ++ be present in analog TV broadcasts. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called upd64031a. ++ ++config VIDEO_UPD64083 ++ tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the NEC Electronics uPD64083 3-Dimensional Y/C ++ separation video chip. It is used to improve the quality of ++ the colors of a composite signal. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called upd64083. ++endmenu ++ ++menu "Audio/Video compression chips" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_SAA6752HS ++ tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" ++ depends on VIDEO_V4L2 && I2C ++ select CRC32 ++ help ++ Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 ++ audio encoder with multiplexer. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa6752hs. ++ ++endmenu ++ ++menu "SDR tuner chips" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config SDR_MAX2175 ++ tristate "Maxim 2175 RF to Bits tuner" ++ depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C ++ select REGMAP_I2C ++ help ++ Support for Maxim 2175 tuner. It is an advanced analog/digital ++ radio receiver with RF-to-Bits front-end designed for SDR solutions. ++ ++ To compile this driver as a module, choose M here; the ++ module will be called max2175. ++ ++ ++endmenu ++ ++menu "Miscellaneous helper chips" ++ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV ++ ++config VIDEO_THS7303 ++ tristate "THS7303/53 Video Amplifier" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for TI THS7303/53 video amplifier ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ths7303. ++ ++config VIDEO_M52790 ++ tristate "Mitsubishi M52790 A/V switch" ++ depends on VIDEO_V4L2 && I2C ++ help ++ Support for the Mitsubishi M52790 A/V switch. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called m52790. ++ ++config VIDEO_I2C ++ tristate "I2C transport video support" ++ depends on VIDEO_V4L2 && I2C ++ select VIDEOBUF2_VMALLOC ++ imply HWMON ++ help ++ Enable the I2C transport video support which supports the ++ following: ++ * Panasonic AMG88xx Grid-Eye Sensors ++ * Melexis MLX90640 Thermal Cameras ++ ++ To compile this driver as a module, choose M here: the ++ module will be called video-i2c ++ ++config VIDEO_ST_MIPID02 ++ tristate "STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ Support for STMicroelectronics MIPID02 CSI-2 to PARALLEL bridge. ++ It is used to allow usage of CSI-2 sensor with PARALLEL port ++ controller. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called st-mipid02. ++endmenu ++ ++# ++# V4L2 I2C drivers that are related with Camera support ++# ++ ++menu "Camera sensor devices" ++ visible if MEDIA_CAMERA_SUPPORT ++ ++config VIDEO_APTINA_PLL ++ tristate ++ ++config VIDEO_SMIAPP_PLL ++ tristate ++ ++config VIDEO_HI556 ++ tristate "Hynix Hi-556 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the Hynix ++ Hi-556 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hi556. ++ ++config VIDEO_IMX214 ++ tristate "Sony IMX214 sensor support" ++ depends on GPIOLIB && I2C && VIDEO_V4L2 ++ select V4L2_FWNODE ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_I2C ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX214 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx214. ++ ++config VIDEO_IMX219 ++ tristate "Sony IMX219 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX219 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx219. ++ ++config VIDEO_IMX258 ++ tristate "Sony IMX258 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX258 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx258. ++ ++config VIDEO_IMX274 ++ tristate "Sony IMX274 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select REGMAP_I2C ++ help ++ This is a V4L2 sensor driver for the Sony IMX274 ++ CMOS image sensor. ++ ++config VIDEO_IMX290 ++ tristate "Sony IMX290 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_I2C ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX290 camera sensor. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx290. ++ ++config VIDEO_IMX319 ++ tristate "Sony IMX319 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX319 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx319. ++ ++config VIDEO_IMX355 ++ tristate "Sony IMX355 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX355 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx355. ++ ++config VIDEO_OV2640 ++ tristate "OmniVision OV2640 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV2640 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov2640. ++ ++config VIDEO_OV2659 ++ tristate "OmniVision OV2659 sensor support" ++ depends on VIDEO_V4L2 && I2C && GPIOLIB ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV2659 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov2659. ++ ++config VIDEO_OV2680 ++ tristate "OmniVision OV2680 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV2680 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov2680. ++ ++config VIDEO_OV2685 ++ tristate "OmniVision OV2685 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV2685 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov2685. ++ ++config VIDEO_OV2740 ++ tristate "OmniVision OV2740 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ depends on ACPI || COMPILE_TEST ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV2740 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov2740. ++ ++config VIDEO_OV5640 ++ tristate "OmniVision OV5640 sensor support" ++ depends on OF ++ depends on GPIOLIB && VIDEO_V4L2 && I2C ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the Omnivision ++ OV5640 camera sensor with a MIPI CSI-2 interface. ++ ++config VIDEO_OV5645 ++ tristate "OmniVision OV5645 sensor support" ++ depends on OF ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV5645 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov5645. ++ ++config VIDEO_OV5647 ++ tristate "OmniVision OV5647 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV5647 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov5647. ++ ++config VIDEO_OV6650 ++ tristate "OmniVision OV6650 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV6650 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov6650. ++ ++config VIDEO_OV5670 ++ tristate "OmniVision OV5670 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV5670 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov5670. ++ ++config VIDEO_OV5675 ++ tristate "OmniVision OV5675 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV5675 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov5675. ++ ++config VIDEO_OV5695 ++ tristate "OmniVision OV5695 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV5695 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov5695. ++ ++config VIDEO_OV7251 ++ tristate "OmniVision OV7251 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV7251 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov7251. ++ ++config VIDEO_OV772X ++ tristate "OmniVision OV772x sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select REGMAP_SCCB ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV772x camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov772x. ++ ++config VIDEO_OV7640 ++ tristate "OmniVision OV7640 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV7640 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov7640. ++ ++config VIDEO_OV7670 ++ tristate "OmniVision OV7670 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV7670 VGA camera. It currently only works with the M88ALP01 ++ controller. ++ ++config VIDEO_OV7740 ++ tristate "OmniVision OV7740 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select REGMAP_SCCB ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV7740 VGA camera sensor. ++ ++config VIDEO_OV8856 ++ tristate "OmniVision OV8856 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV8856 camera sensor. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov8856. ++ ++config VIDEO_OV9640 ++ tristate "OmniVision OV9640 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV9640 camera sensor. ++ ++config VIDEO_OV9650 ++ tristate "OmniVision OV9650/OV9652 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_SCCB ++ help ++ This is a V4L2 sensor driver for the Omnivision ++ OV9650 and OV9652 camera sensors. ++ ++config VIDEO_OV13858 ++ tristate "OmniVision OV13858 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV13858 camera. ++ ++config VIDEO_VS6624 ++ tristate "ST VS6624 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ help ++ This is a Video4Linux2 sensor driver for the ST VS6624 ++ camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vs6624. ++ ++config VIDEO_MT9M001 ++ tristate "mt9m001 support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This driver supports MT9M001 cameras from Micron, monochrome ++ and colour models. ++ ++config VIDEO_MT9M032 ++ tristate "MT9M032 camera sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select VIDEO_APTINA_PLL ++ help ++ This driver supports MT9M032 camera sensors from Aptina, monochrome ++ models only. ++ ++config VIDEO_MT9M111 ++ tristate "mt9m111, mt9m112 and mt9m131 support" ++ depends on I2C && VIDEO_V4L2 ++ select V4L2_FWNODE ++ help ++ This driver supports MT9M111, MT9M112 and MT9M131 cameras from ++ Micron/Aptina ++ ++config VIDEO_MT9P031 ++ tristate "Aptina MT9P031 support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select VIDEO_APTINA_PLL ++ help ++ This is a Video4Linux2 sensor driver for the Aptina ++ (Micron) mt9p031 5 Mpixel camera. ++ ++config VIDEO_MT9T001 ++ tristate "Aptina MT9T001 support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a Video4Linux2 sensor driver for the Aptina ++ (Micron) mt0t001 3 Mpixel camera. ++ ++config VIDEO_MT9T112 ++ tristate "Aptina MT9T111/MT9T112 support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the Aptina ++ (Micron) MT9T111 and MT9T112 3 Mpixel camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mt9t112. ++ ++config VIDEO_MT9V011 ++ tristate "Micron mt9v011 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the Micron ++ mt0v011 1.3 Mpixel camera. It currently only works with the ++ em28xx driver. ++ ++config VIDEO_MT9V032 ++ tristate "Micron MT9V032 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select REGMAP_I2C ++ select V4L2_FWNODE ++ help ++ This is a Video4Linux2 sensor driver for the Micron ++ MT9V032 752x480 CMOS sensor. ++ ++config VIDEO_MT9V111 ++ tristate "Aptina MT9V111 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a Video4Linux2 sensor driver for the Aptina/Micron ++ MT9V111 sensor. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mt9v111. ++ ++config VIDEO_SR030PC30 ++ tristate "Siliconfile SR030PC30 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This driver supports SR030PC30 VGA camera from Siliconfile ++ ++config VIDEO_NOON010PC30 ++ tristate "Siliconfile NOON010PC30 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This driver supports NOON010PC30 CIF camera from Siliconfile ++ ++source "drivers/media/i2c/m5mols/Kconfig" ++ ++config VIDEO_RDACM20 ++ tristate "IMI RDACM20 camera support" ++ depends on I2C ++ select V4L2_FWNODE ++ select VIDEO_V4L2_SUBDEV_API ++ select MEDIA_CONTROLLER ++ help ++ This driver supports the IMI RDACM20 GMSL camera, used in ++ ADAS systems. ++ ++ This camera should be used in conjunction with a GMSL ++ deserialiser such as the MAX9286. ++ ++config VIDEO_RJ54N1 ++ tristate "Sharp RJ54N1CB0C sensor support" ++ depends on I2C && VIDEO_V4L2 ++ help ++ This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image ++ sensor. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called rj54n1. ++ ++config VIDEO_S5K6AA ++ tristate "Samsung S5K6AAFX sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M ++ camera sensor with an embedded SoC image signal processor. ++ ++config VIDEO_S5K6A3 ++ tristate "Samsung S5K6A3 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a V4L2 sensor driver for Samsung S5K6A3 raw ++ camera sensor. ++ ++config VIDEO_S5K4ECGX ++ tristate "Samsung S5K4ECGX sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select CRC32 ++ help ++ This is a V4L2 sensor driver for Samsung S5K4ECGX 5M ++ camera sensor with an embedded SoC image signal processor. ++ ++config VIDEO_S5K5BAF ++ tristate "Samsung S5K5BAF sensor support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a V4L2 sensor driver for Samsung S5K5BAF 2M ++ camera sensor with an embedded SoC image signal processor. ++ ++source "drivers/media/i2c/smiapp/Kconfig" ++source "drivers/media/i2c/et8ek8/Kconfig" ++ ++config VIDEO_S5C73M3 ++ tristate "Samsung S5C73M3 sensor support" ++ depends on I2C && SPI && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a V4L2 sensor driver for Samsung S5C73M3 ++ 8 Mpixel camera. ++ ++endmenu ++ ++menu "Lens drivers" ++ visible if MEDIA_CAMERA_SUPPORT ++ ++config VIDEO_AD5820 ++ tristate "AD5820 lens voice coil support" ++ depends on GPIOLIB && I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ help ++ This is a driver for the AD5820 camera lens voice coil. ++ It is used for example in Nokia N900 (RX-51). ++ ++config VIDEO_AK7375 ++ tristate "AK7375 lens voice coil support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a driver for the AK7375 camera lens voice coil. ++ AK7375 is a 12 bit DAC with 120mA output current sink ++ capability. This is designed for linear control of ++ voice coil motors, controlled via I2C serial interface. ++ ++config VIDEO_DW9714 ++ tristate "DW9714 lens voice coil support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a driver for the DW9714 camera lens voice coil. ++ DW9714 is a 10 bit DAC with 120mA output current sink ++ capability. This is designed for linear control of ++ voice coil motors, controlled via I2C serial interface. ++ ++config VIDEO_DW9768 ++ tristate "DW9768 lens voice coil support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ This is a driver for the DW9768 camera lens voice coil. ++ DW9768 is a 10 bit DAC with 100mA output current sink ++ capability. This is designed for linear control of ++ voice coil motors, controlled via I2C serial interface. ++ ++config VIDEO_DW9807_VCM ++ tristate "DW9807 lens voice coil support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select VIDEO_V4L2_SUBDEV_API ++ help ++ This is a driver for the DW9807 camera lens voice coil. ++ DW9807 is a 10 bit DAC with 100mA output current sink ++ capability. This is designed for linear control of ++ voice coil motors, controlled via I2C serial interface. ++ ++endmenu ++ ++menu "Flash devices" ++ visible if MEDIA_CAMERA_SUPPORT ++ ++config VIDEO_ADP1653 ++ tristate "ADP1653 flash support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ help ++ This is a driver for the ADP1653 flash controller. It is used for ++ example in Nokia N900. ++ ++config VIDEO_LM3560 ++ tristate "LM3560 dual flash driver support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select REGMAP_I2C ++ help ++ This is a driver for the lm3560 dual flash controllers. It controls ++ flash, torch LEDs. ++ ++config VIDEO_LM3646 ++ tristate "LM3646 dual flash driver support" ++ depends on I2C && VIDEO_V4L2 ++ select MEDIA_CONTROLLER ++ select REGMAP_I2C ++ help ++ This is a driver for the lm3646 dual flash controllers. It controls ++ flash, torch LEDs. ++endmenu ++ ++endif # VIDEO_V4L2 +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index f0a77473979d..23c04ff4eefd 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -1,127 +1,2 @@ +-# SPDX-License-Identifier: GPL-2.0 +-msp3400-objs := msp3400-driver.o msp3400-kthreads.o +-obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o +- +-obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ +-obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ +-obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +-obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ +- +-obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o +-obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +-obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o +-obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o +-obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o +-obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o +-obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o +-obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o +-obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o +-obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +-obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o +-obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +-obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o +-obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o +-obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +-obj-$(CONFIG_VIDEO_AK7375) += ak7375.o +-obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +-obj-$(CONFIG_VIDEO_DW9768) += dw9768.o +-obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o +-obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o +-obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +-obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +-obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o +-obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +-obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o +-obj-$(CONFIG_VIDEO_ADV748X) += adv748x/ +-obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o +-obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o +-obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o +-obj-$(CONFIG_VIDEO_ADV7511) += adv7511-v4l2.o +-obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o +-obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +-obj-$(CONFIG_VIDEO_BT819) += bt819.o +-obj-$(CONFIG_VIDEO_BT856) += bt856.o +-obj-$(CONFIG_VIDEO_BT866) += bt866.o +-obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +-obj-$(CONFIG_VIDEO_THS7303) += ths7303.o +-obj-$(CONFIG_VIDEO_THS8200) += ths8200.o +-obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +-obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +-obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +-obj-$(CONFIG_VIDEO_TW2804) += tw2804.o +-obj-$(CONFIG_VIDEO_TW9903) += tw9903.o +-obj-$(CONFIG_VIDEO_TW9906) += tw9906.o +-obj-$(CONFIG_VIDEO_TW9910) += tw9910.o +-obj-$(CONFIG_VIDEO_CS3308) += cs3308.o +-obj-$(CONFIG_VIDEO_CS5345) += cs5345.o +-obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o +-obj-$(CONFIG_VIDEO_M52790) += m52790.o +-obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +-obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o +-obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +-obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +-obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +-obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o +-obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +-obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +-obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +-obj-$(CONFIG_VIDEO_OV2680) += ov2680.o +-obj-$(CONFIG_VIDEO_OV2685) += ov2685.o +-obj-$(CONFIG_VIDEO_OV2740) += ov2740.o +-obj-$(CONFIG_VIDEO_OV5640) += ov5640.o +-obj-$(CONFIG_VIDEO_OV5645) += ov5645.o +-obj-$(CONFIG_VIDEO_OV5647) += ov5647.o +-obj-$(CONFIG_VIDEO_OV5670) += ov5670.o +-obj-$(CONFIG_VIDEO_OV5675) += ov5675.o +-obj-$(CONFIG_VIDEO_OV5695) += ov5695.o +-obj-$(CONFIG_VIDEO_OV6650) += ov6650.o +-obj-$(CONFIG_VIDEO_OV7251) += ov7251.o +-obj-$(CONFIG_VIDEO_OV7640) += ov7640.o +-obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +-obj-$(CONFIG_VIDEO_OV772X) += ov772x.o +-obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +-obj-$(CONFIG_VIDEO_OV8856) += ov8856.o +-obj-$(CONFIG_VIDEO_OV9640) += ov9640.o +-obj-$(CONFIG_VIDEO_OV9650) += ov9650.o +-obj-$(CONFIG_VIDEO_OV13858) += ov13858.o +-obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o +-obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o +-obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o +-obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o +-obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o +-obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o +-obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o +-obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +-obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o +-obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o +-obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +-obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o +-obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +-obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o +-obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o +-obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o +-obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ +-obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o +-obj-$(CONFIG_VIDEO_LM3560) += lm3560.o +-obj-$(CONFIG_VIDEO_LM3646) += lm3646.o +-obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o +-obj-$(CONFIG_VIDEO_AK881X) += ak881x.o +-obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +-obj-$(CONFIG_VIDEO_I2C) += video-i2c.o +-obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o +-obj-$(CONFIG_VIDEO_OV2659) += ov2659.o +-obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +-obj-$(CONFIG_VIDEO_HI556) += hi556.o +-obj-$(CONFIG_VIDEO_IMX214) += imx214.o +-obj-$(CONFIG_VIDEO_IMX219) += imx219.o +-obj-$(CONFIG_VIDEO_IMX258) += imx258.o +-obj-$(CONFIG_VIDEO_IMX274) += imx274.o +-obj-$(CONFIG_VIDEO_IMX290) += imx290.o +-obj-$(CONFIG_VIDEO_IMX319) += imx319.o +-obj-$(CONFIG_VIDEO_IMX355) += imx355.o +-obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +-rdacm20-camera_module-objs := rdacm20.o max9271.o +-obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o +-obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o +- +-obj-$(CONFIG_SDR_MAX2175) += max2175.o ++obj-$(CONFIG_VIDEO_AVT_CSI2) += avt_csi2.o ++obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +diff --git a/drivers/media/i2c/Makefile_ori b/drivers/media/i2c/Makefile_ori +new file mode 100644 +index 000000000000..f0a77473979d +--- /dev/null ++++ b/drivers/media/i2c/Makefile_ori +@@ -0,0 +1,127 @@ ++# SPDX-License-Identifier: GPL-2.0 ++msp3400-objs := msp3400-driver.o msp3400-kthreads.o ++obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o ++ ++obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ ++obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ ++obj-$(CONFIG_VIDEO_CX25840) += cx25840/ ++obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ ++ ++obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o ++obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o ++obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o ++obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o ++obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o ++obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o ++obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o ++obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o ++obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o ++obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o ++obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o ++obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o ++obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o ++obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o ++obj-$(CONFIG_VIDEO_AD5820) += ad5820.o ++obj-$(CONFIG_VIDEO_AK7375) += ak7375.o ++obj-$(CONFIG_VIDEO_DW9714) += dw9714.o ++obj-$(CONFIG_VIDEO_DW9768) += dw9768.o ++obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o ++obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o ++obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o ++obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o ++obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o ++obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o ++obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o ++obj-$(CONFIG_VIDEO_ADV748X) += adv748x/ ++obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o ++obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o ++obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o ++obj-$(CONFIG_VIDEO_ADV7511) += adv7511-v4l2.o ++obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o ++obj-$(CONFIG_VIDEO_VS6624) += vs6624.o ++obj-$(CONFIG_VIDEO_BT819) += bt819.o ++obj-$(CONFIG_VIDEO_BT856) += bt856.o ++obj-$(CONFIG_VIDEO_BT866) += bt866.o ++obj-$(CONFIG_VIDEO_KS0127) += ks0127.o ++obj-$(CONFIG_VIDEO_THS7303) += ths7303.o ++obj-$(CONFIG_VIDEO_THS8200) += ths8200.o ++obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o ++obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o ++obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o ++obj-$(CONFIG_VIDEO_TW2804) += tw2804.o ++obj-$(CONFIG_VIDEO_TW9903) += tw9903.o ++obj-$(CONFIG_VIDEO_TW9906) += tw9906.o ++obj-$(CONFIG_VIDEO_TW9910) += tw9910.o ++obj-$(CONFIG_VIDEO_CS3308) += cs3308.o ++obj-$(CONFIG_VIDEO_CS5345) += cs5345.o ++obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o ++obj-$(CONFIG_VIDEO_M52790) += m52790.o ++obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o ++obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o ++obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++obj-$(CONFIG_VIDEO_WM8739) += wm8739.o ++obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o ++obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o ++obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o ++obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o ++obj-$(CONFIG_VIDEO_OV2640) += ov2640.o ++obj-$(CONFIG_VIDEO_OV2680) += ov2680.o ++obj-$(CONFIG_VIDEO_OV2685) += ov2685.o ++obj-$(CONFIG_VIDEO_OV2740) += ov2740.o ++obj-$(CONFIG_VIDEO_OV5640) += ov5640.o ++obj-$(CONFIG_VIDEO_OV5645) += ov5645.o ++obj-$(CONFIG_VIDEO_OV5647) += ov5647.o ++obj-$(CONFIG_VIDEO_OV5670) += ov5670.o ++obj-$(CONFIG_VIDEO_OV5675) += ov5675.o ++obj-$(CONFIG_VIDEO_OV5695) += ov5695.o ++obj-$(CONFIG_VIDEO_OV6650) += ov6650.o ++obj-$(CONFIG_VIDEO_OV7251) += ov7251.o ++obj-$(CONFIG_VIDEO_OV7640) += ov7640.o ++obj-$(CONFIG_VIDEO_OV7670) += ov7670.o ++obj-$(CONFIG_VIDEO_OV772X) += ov772x.o ++obj-$(CONFIG_VIDEO_OV7740) += ov7740.o ++obj-$(CONFIG_VIDEO_OV8856) += ov8856.o ++obj-$(CONFIG_VIDEO_OV9640) += ov9640.o ++obj-$(CONFIG_VIDEO_OV9650) += ov9650.o ++obj-$(CONFIG_VIDEO_OV13858) += ov13858.o ++obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o ++obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o ++obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o ++obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o ++obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o ++obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o ++obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o ++obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o ++obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o ++obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o ++obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o ++obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o ++obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o ++obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o ++obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o ++obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o ++obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ ++obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o ++obj-$(CONFIG_VIDEO_LM3560) += lm3560.o ++obj-$(CONFIG_VIDEO_LM3646) += lm3646.o ++obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o ++obj-$(CONFIG_VIDEO_AK881X) += ak881x.o ++obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o ++obj-$(CONFIG_VIDEO_I2C) += video-i2c.o ++obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o ++obj-$(CONFIG_VIDEO_OV2659) += ov2659.o ++obj-$(CONFIG_VIDEO_TC358743) += tc358743.o ++obj-$(CONFIG_VIDEO_HI556) += hi556.o ++obj-$(CONFIG_VIDEO_IMX214) += imx214.o ++obj-$(CONFIG_VIDEO_IMX219) += imx219.o ++obj-$(CONFIG_VIDEO_IMX258) += imx258.o ++obj-$(CONFIG_VIDEO_IMX274) += imx274.o ++obj-$(CONFIG_VIDEO_IMX290) += imx290.o ++obj-$(CONFIG_VIDEO_IMX319) += imx319.o ++obj-$(CONFIG_VIDEO_IMX355) += imx355.o ++obj-$(CONFIG_VIDEO_MAX9286) += max9286.o ++rdacm20-camera_module-objs := rdacm20.o max9271.o ++obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o ++obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o ++ ++obj-$(CONFIG_SDR_MAX2175) += max2175.o +diff --git a/drivers/media/i2c/alvium_helper.h b/drivers/media/i2c/alvium_helper.h +new file mode 100644 +index 000000000000..7e43eb52bbf8 +--- /dev/null ++++ b/drivers/media/i2c/alvium_helper.h +@@ -0,0 +1,128 @@ ++/*============================================================================= ++ Copyright (C) 2020 Allied Vision Technologies. All Rights Reserved. ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ ----------------------------------------------------------------------------- ++ ++File: alvium_helper.h ++ ++version: 1.0.0 ++=============================================================================*/ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// INCLUDES ++//////////////////////////////////////////////////////////////////////////////// ++#include "alvium_regs.h" ++ ++//////////////////////////////////////////////////////////////////////////////// ++// DEFINES ++//////////////////////////////////////////////////////////////////////////////// ++#ifndef ALVIUM_HELPER_H ++#define ALVIUM_HELPER_H ++ ++#define AV_CAM_SOFTWARE_TRIGGER 4 ++ ++//////////////////////////////////////////////////////////////////////////////// ++// ENUMS ++//////////////////////////////////////////////////////////////////////////////// ++enum CCI_REG_INFO { ++ CCI_REGISTER_LAYOUT_VERSION = 0, ++ RESERVED4BIT, ++ DEVICE_CAPABILITIES, ++ GCPRM_ADDRESS, ++ RESERVED2BIT, ++ BCRM_ADDRESS, ++ RESERVED2BIT_2, ++ DEVICE_GUID, ++ MANUFACTURER_NAME, ++ MODEL_NAME, ++ FAMILY_NAME, ++ DEVICE_VERSION, ++ MANUFACTURER_INFO, ++ SERIAL_NUMBER, ++ USER_DEFINED_NAME, ++ CHECKSUM, ++ CHANGE_MODE, ++ CURRENT_MODE, ++ SOFT_RESET, ++ HEARTBEAT, ++ MAX_CMD = HEARTBEAT ++}; ++ ++//////////////////////////////////////////////////////////////////////////////// ++// STRUCTS ++//////////////////////////////////////////////////////////////////////////////// ++struct cci_cmd { ++ __u8 command_index; /* diagnostc test name */ ++ const __u32 address; /* NULL for no alias name */ ++ __u32 byte_count; ++}; ++ ++static struct cci_cmd cci_cmd_tbl[MAX_CMD] = { ++ /* command index address */ ++ { CCI_REGISTER_LAYOUT_VERSION, CCI_REG_LAYOUT_VER_32R, 4 }, ++ { DEVICE_CAPABILITIES, CCI_DEVICE_CAP_64R, 8 }, ++ { GCPRM_ADDRESS, CCI_GCPRM_16R, 2 }, ++ { BCRM_ADDRESS, CCI_BCRM_16R, 2 }, ++ { DEVICE_GUID, CCI_DEVICE_GUID_512R, 64 }, ++ { MANUFACTURER_NAME, CCI_MANUF_NAME_512R, 64 }, ++ { MODEL_NAME, CCI_MODEL_NAME_512R, 64 }, ++ { FAMILY_NAME, CCI_FAMILY_NAME_512R, 64 }, ++ { DEVICE_VERSION, CCI_DEVICE_VERSION_512R, 64 }, ++ { MANUFACTURER_INFO, CCI_MANUF_INFO_512R, 64 }, ++ { SERIAL_NUMBER, CCI_SERIAL_NUM_512R, 64 }, ++ { USER_DEFINED_NAME, CCI_USER_DEF_NAME_512R, 64 }, ++ { CHECKSUM, CCI_CHECKSUM_32R, 4 }, ++ { CHANGE_MODE, CCI_CHANGE_MODE_8W, 1 }, ++ { CURRENT_MODE, CCI_CURRENT_MODE_8R, 1 }, ++ { SOFT_RESET, CCI_SOFT_RESET_8W, 1 }, ++ { HEARTBEAT, CCI_HEARTBEAT_8RW, 1 }, ++}; ++ ++struct __attribute__((__packed__)) cci_reg { ++ __u32 layout_version; ++ __u32 reserved_4bit; ++ __u64 device_capabilities; ++ __u16 gcprm_address; ++ __u16 reserved_2bit; ++ __u16 bcrm_addr; ++ __u16 reserved_2bit_2; ++ char device_guid[64]; ++ char manufacturer_name[64]; ++ char model_name[64]; ++ char family_name[64]; ++ char device_version[64]; ++ char manufacturer_info[64]; ++ char serial_number[64]; ++ char user_defined_name[64]; ++ __u32 checksum; ++ __u8 change_mode; ++ __u8 current_mode; ++ __u8 soft_reset; ++ __u8 heartbeat; ++}; ++ ++struct __attribute__((__packed__)) gencp_reg { ++ __u32 gcprm_layout_version; ++ __u16 gencp_out_buffer_address; ++ __u16 reserved_2byte; ++ __u16 gencp_out_buffer_size; ++ __u16 reserved_2byte_1; ++ __u16 gencp_in_buffer_address; ++ __u16 reserved_2byte_2; ++ __u16 gencp_in_buffer_size; ++ __u16 reserved_2byte_3; ++ __u32 checksum; ++}; ++ ++ ++#endif /* ALVIUM_HELPER_H */ +diff --git a/drivers/media/i2c/alvium_regs.h b/drivers/media/i2c/alvium_regs.h +new file mode 100755 +index 000000000000..5aecf2b49bdc +--- /dev/null ++++ b/drivers/media/i2c/alvium_regs.h +@@ -0,0 +1,515 @@ ++/*============================================================================= ++ Copyright (C) 2022 Allied Vision Technologies. All Rights Reserved. ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ ----------------------------------------------------------------------------- ++ ++File: alvium_regs.h ++ ++version: 1.8.0 ++=============================================================================*/ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// DEFINES ++//////////////////////////////////////////////////////////////////////////////// ++#ifndef ALVIUM_REGS_H ++#define ALVIUM_REGS_H ++ ++// Version of the GenCP over CSI spec ++#define GENCP_OVER_CCI_SPEC_VERSION_MAJOR 1 ++#define GENCP_OVER_CCI_SPEC_VERSION_MINOR 1 ++#define GENCP_OVER_CCI_SPEC_VERSION_PATCH 31 ++ ++// Version of the BCRM spec ++#define BCRM_SPEC_VERSION_MAJOR 1 ++#define BCRM_SPEC_VERSION_MINOR 1 ++#define BCRM_SPEC_VERSION_PATCH 16 ++ ++// CCI registers ++#define CCI_REG_LAYOUT_VER_32R 0x0000 ++#define CCI_DEVICE_CAP_64R 0x0008 ++#define CCI_GCPRM_16R 0x0010 ++#define CCI_BCRM_16R 0x0014 ++#define CCI_DEVICE_GUID_512R 0x0018 ++#define CCI_MANUF_NAME_512R 0x0058 ++#define CCI_MODEL_NAME_512R 0x0098 ++#define CCI_FAMILY_NAME_512R 0x00D8 ++#define CCI_DEVICE_VERSION_512R 0x0118 ++#define CCI_MANUF_INFO_512R 0x0158 ++#define CCI_SERIAL_NUM_512R 0x0198 ++#define CCI_USER_DEF_NAME_512R 0x01D8 ++#define CCI_CHECKSUM_32R 0x0218 ++#define CCI_CHANGE_MODE_8W 0x021C ++#define CCI_CURRENT_MODE_8R 0x021D ++#define CCI_SOFT_RESET_8W 0x021E ++#define CCI_HEARTBEAT_8RW 0x021F ++#define CCI_CAMERA_I2C_ADDRESS_8RW 0x0220 ++ ++// GCPRM register offsets ++#define GCPRM_LAYOUT_VERSION_32R 0x0000 ++#define GCPRM_GENCP_OUTBUF_ADDR_16R 0x0004 ++#define GCPRM_GENCP_OUTBUF_SIZE_16R 0x0008 ++#define GCPRM_GENCP_INBUF_ADDR_16R 0x000C ++#define GCPRM_GENCP_INBUF_SIZE_16R 0x0010 ++#define GCPRM_GENCP_CHECKSUM_32R 0x0014 ++#define GCPRM_GENCP_OUTHANDSHAKE_8RW 0x0018 ++#define GCPRM_GENCP_INHANDSHAKE_8RW 0x001C ++#define GCPRM_GENCP_OUT_SIZE_W16 0x0020 ++#define GCPRM_GENCP_IN_SIZE_R16 0x0024 ++ ++// SBRM register offsets ++#define SBRM_VERSION_32R 0x0000 ++#define SBRM_GENCP_CCI_DEV_CAP_64R 0x0004 ++#define SBRM_NUM_OF_STREAM_CHAN_32R 0x000C ++#define SBRM_SUPP_CSI2_LANE_COUNTS_8R 0x0010 ++#define SBRM_CSI2_LANE_COUNT_8RW 0x0014 ++#define SBRM_MIN_SUPP_CSI2_CLK_FREQ_32R 0x0018 ++#define SBRM_MAX_SUPP_CSI2_CLK_FREQ_32R 0x001C ++#define SBRM_CSI2_CLK_FREQ_32RW 0x0020 ++#define SBRM_SIRM_ADDR_64R 0x0024 ++#define SBRM_SIRM_LENGTH_32R 0x002C ++ ++// SIRM register offsets ++#define SIRM_STREAM_ENABLE_8RW 0x0000 ++#define SIRM_LEADER_SIZE_32R 0x0004 ++#define SIRM_PAYLOAD_SIZE_64R 0x0008 ++#define SIRM_TRAILER_SIZE_32R 0x0010 ++#define SIRM_CSI2_DATA_ID_INQ1_64R 0x0014 ++#define SIRM_CSI2_DATA_ID_INQ2_64R 0x001C ++#define SIRM_CSI2_DATA_ID_INQ3_64R 0x0024 ++#define SIRM_CSI2_DATA_ID_INQ4_64R 0x002C ++#define SIRM_CSI2_DATA_ID_8RW 0x0034 ++#define SIRM_IPU_X_MIN_32W 0x0038 ++#define SIRM_IPU_X_MAX_32W 0x003C ++#define SIRM_IPU_X_INC_32W 0x0040 ++#define SIRM_IPU_Y_MIN_32W 0x0044 ++#define SIRM_IPU_Y_MAX_32W 0x0048 ++#define SIRM_IPU_Y_INC_32W 0x004C ++#define SIRM_IPU_X_32R 0x0050 ++#define SIRM_IPU_Y_32R 0x0054 ++#define SIRM_GENCP_IMAGE_SIZE_X_32R 0x0058 ++#define SIRM_GENCP_IMAGE_SIZE_Y_32R 0x005C ++#define SIRM_GENCP_IMAGE_OFFSET_X_32R 0x0060 ++#define SIRM_GENCP_IMAGE_OFFSET_Y_32R 0x0064 ++#define SIRM_GENCP_IMAGE_PADDING_X_16R 0x0068 ++#define SIRM_GENCP_IMAGE_PIXEL_FORMAT_32R 0x006C ++#define SIRM_GENCP_IMAGE_PAYLOAD_TYPE_16R 0x0070 ++#define SIRM_GENCP_VALID_PAYLOAD_SIZE_64R 0x0074 ++#define SIRM_GENCP_CHUNK_LAYOUT_ID_32R 0x007C ++ ++// BCRM register offsets ++#define BCRM_VERSION_32R 0x0000 ++#define BCRM_FEATURE_INQUIRY_64R 0x0008 ++#define BCRM_DEVICE_FIRMWARE_VERSION_64R 0x0010 ++#define BCRM_WRITE_HANDSHAKE_8RW 0x0018 ++#define BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R 0x0040 ++#define BCRM_CSI2_LANE_COUNT_8RW 0x0044 ++#define BCRM_CSI2_CLOCK_MIN_32R 0x0048 ++#define BCRM_CSI2_CLOCK_MAX_32R 0x004C ++#define BCRM_CSI2_CLOCK_32RW 0x0050 ++#define BCRM_BUFFER_SIZE_32R 0x0054 ++#define BCRM_IPU_X_MIN_32W 0x0058 ++#define BCRM_IPU_X_MAX_32W 0x005C ++#define BCRM_IPU_X_INC_32W 0x0060 ++#define BCRM_IPU_Y_MIN_32W 0x0064 ++#define BCRM_IPU_Y_MAX_32W 0x0068 ++#define BCRM_IPU_Y_INC_32W 0x006C ++#define BCRM_IPU_X_32R 0x0070 ++#define BCRM_IPU_Y_32R 0x0074 ++#define BCRM_PHY_RESET_8RW 0x0078 ++#define BCRM_ACQUISITION_START_8RW 0x0080 ++#define BCRM_ACQUISITION_STOP_8RW 0x0084 ++#define BCRM_ACQUISITION_ABORT_8RW 0x0088 ++#define BCRM_ACQUISITION_STATUS_8R 0x008C ++#define BCRM_ACQUISITION_FRAME_RATE_64RW 0x0090 ++#define BCRM_ACQUISITION_FRAME_RATE_MIN_64R 0x0098 ++#define BCRM_ACQUISITION_FRAME_RATE_MAX_64R 0x00A0 ++#define BCRM_ACQUISITION_FRAME_RATE_INC_64R 0x00A8 ++#define BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW 0x00B0 ++#define BCRM_FRAME_START_TRIGGER_MODE_8RW 0x00B4 ++#define BCRM_FRAME_START_TRIGGER_SOURCE_8RW 0x00B8 ++#define BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW 0x00BC ++#define BCRM_FRAME_START_TRIGGER_SOFTWARE_8W 0x00C0 ++#define BCRM_FRAME_START_TRIGGER_DELAY_32RW 0x00C4 ++#define BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW 0x00C8 ++#define BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW 0x00CC ++#define BCRM_LINE_CONFIGURATION_32RW 0x00D0 ++#define BCRM_LINE_STATUS_8R 0x00D4 ++#define BCRM_IMG_WIDTH_32RW 0x0100 ++#define BCRM_IMG_WIDTH_MIN_32R 0x0104 ++#define BCRM_IMG_WIDTH_MAX_32R 0x0108 ++#define BCRM_IMG_WIDTH_INC_32R 0x010C ++#define BCRM_IMG_HEIGHT_32RW 0x0110 ++#define BCRM_IMG_HEIGHT_MIN_32R 0x0114 ++#define BCRM_IMG_HEIGHT_MAX_32R 0x0118 ++#define BCRM_IMG_HEIGHT_INC_32R 0x011C ++#define BCRM_IMG_OFFSET_X_32RW 0x0120 ++#define BCRM_IMG_OFFSET_X_MIN_32R 0x0124 ++#define BCRM_IMG_OFFSET_X_MAX_32R 0x0128 ++#define BCRM_IMG_OFFSET_X_INC_32R 0x012C ++#define BCRM_IMG_OFFSET_Y_32RW 0x0130 ++#define BCRM_IMG_OFFSET_Y_MIN_32R 0x0134 ++#define BCRM_IMG_OFFSET_Y_MAX_32R 0x0138 ++#define BCRM_IMG_OFFSET_Y_INC_32R 0x013C ++#define BCRM_IMG_MIPI_DATA_FORMAT_32RW 0x0140 ++#define BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R 0x0148 ++#define BCRM_IMG_BAYER_PATTERN_INQUIRY_8R 0x0150 ++#define BCRM_IMG_BAYER_PATTERN_8RW 0x0154 ++#define BCRM_IMG_REVERSE_X_8RW 0x0158 ++#define BCRM_IMG_REVERSE_Y_8RW 0x015C ++#define BCRM_SENSOR_WIDTH_32R 0x0160 ++#define BCRM_SENSOR_HEIGHT_32R 0x0164 ++#define BCRM_WIDTH_MAX_32R 0x0168 ++#define BCRM_HEIGHT_MAX_32R 0x016C ++#define BCRM_DIGITAL_BINNIG_INQ_16R 0x0170 ++#define BCRM_DIGITAL_BINNIG_SETTING_8RW 0x0174 ++#define BCRM_DIGITAL_BINNIG_MODE_8RW 0x0178 ++#define BCRM_EXPOSURE_TIME_64RW 0x0180 ++#define BCRM_EXPOSURE_TIME_MIN_64R 0x0188 ++#define BCRM_EXPOSURE_TIME_MAX_64R 0x0190 ++#define BCRM_EXPOSURE_TIME_INC_64R 0x0198 ++#define BCRM_EXPOSURE_AUTO_8RW 0x01A0 ++#define BCRM_INTENSITY_AUTO_PRECEDENCE_8RW 0x01A4 ++#define BCRM_INTENSITY_AUTO_PRECEDENCE_VALUE_32RW 0x01A8 ++#define BCRM_INTENSITY_AUTO_PRECEDENCE_MIN_32R 0x01AC ++#define BCRM_INTENSITY_AUTO_PRECEDENCE_MAX_32R 0x01B0 ++#define BCRM_INTENSITY_AUTO_PRECEDENCE_INC_32R 0x01B4 ++#define BCRM_BLACK_LEVEL_32RW 0x01B8 ++#define BCRM_BLACK_LEVEL_MIN_32R 0x01BC ++#define BCRM_BLACK_LEVEL_MAX_32R 0x01C0 ++#define BCRM_BLACK_LEVEL_INC_32R 0x01C4 ++#define BCRM_GAIN_64RW 0x01C8 ++#define BCRM_GAIN_MIN_64R 0x01D0 ++#define BCRM_GAIN_MAX_64R 0x01D8 ++#define BCRM_GAIN_INC_64R 0x01E0 ++#define BCRM_GAIN_AUTO_8RW 0x01E8 ++#define BCRM_GAMMA_64RW 0x01F0 ++#define BCRM_GAMMA_MIN_64R 0x01F8 ++#define BCRM_GAMMA_MAX_64R 0x0200 ++#define BCRM_GAMMA_INC_64R 0x0208 ++#define BCRM_CONTRAST_VALUE_32RW 0x0214 ++#define BCRM_CONTRAST_VALUE_MIN_32R 0x0218 ++#define BCRM_CONTRAST_VALUE_MAX_32R 0x021C ++#define BCRM_CONTRAST_VALUE_INC_32R 0x0220 ++#define BCRM_SATURATION_32RW 0x0240 ++#define BCRM_SATURATION_MIN_32R 0x0244 ++#define BCRM_SATURATION_MAX_32R 0x0248 ++#define BCRM_SATURATION_INC_32R 0x024C ++#define BCRM_HUE_32RW 0x0250 ++#define BCRM_HUE_MIN_32R 0x0254 ++#define BCRM_HUE_MAX_32R 0x0258 ++#define BCRM_HUE_INC_32R 0x025C ++#define BCRM_RED_BALANCE_RATIO_64RW 0x0280 ++#define BCRM_RED_BALANCE_RATIO_MIN_64R 0x0288 ++#define BCRM_RED_BALANCE_RATIO_MAX_64R 0x0290 ++#define BCRM_RED_BALANCE_RATIO_INC_64R 0x0298 ++#define BCRM_GREEN_BALANCE_RATIO_64RW 0x02A0 ++#define BCRM_GREEN_BALANCE_RATIO_MIN_64R 0x02A8 ++#define BCRM_GREEN_BALANCE_RATIO_MAX_64R 0x02B0 ++#define BCRM_GREEN_BALANCE_RATIO_INC_64R 0x02B8 ++#define BCRM_BLUE_BALANCE_RATIO_64RW 0x02C0 ++#define BCRM_BLUE_BALANCE_RATIO_MIN_64R 0x02C8 ++#define BCRM_BLUE_BALANCE_RATIO_MAX_64R 0x02D0 ++#define BCRM_BLUE_BALANCE_RATIO_INC_64R 0x02D8 ++#define BCRM_WHITE_BALANCE_AUTO_8RW 0x02E0 ++#define BCRM_SHARPNESS_32RW 0x0300 ++#define BCRM_SHARPNESS_MIN_32R 0x0304 ++#define BCRM_SHARPNESS_MAX_32R 0x0308 ++#define BCRM_SHARPNESS_INC_32R 0x030C ++#define BCRM_DEVICE_TEMPERATURE_32R 0x0310 ++#define BCRM_EXPOSURE_AUTO_MIN_64RW 0x0330 ++#define BCRM_EXPOSURE_AUTO_MAX_64RW 0x0338 ++#define BCRM_GAIN_AUTO_MIN_64RW 0x0340 ++#define BCRM_GAIN_AUTO_MAX_64RW 0x0348 ++#define BCRM_AUTO_REGION_WIDTH_32RW 0x0350 ++#define BCRM_AUTO_REGION_WIDTH_MIN_32R 0x0354 ++#define BCRM_AUTO_REGION_WIDTH_MAX_32R 0x0358 ++#define BCRM_AUTO_REGION_WIDTH_INC_32R 0x035C ++#define BCRM_AUTO_REGION_HEIGHT_32RW 0x0360 ++#define BCRM_AUTO_REGION_HEIGHT_MIN_32R 0x0364 ++#define BCRM_AUTO_REGION_HEIGHT_MAX_32R 0x0368 ++#define BCRM_AUTO_REGION_HEIGHT_INC_32R 0x036C ++#define BCRM_AUTO_REGION_OFFSET_X_32RW 0x0370 ++#define BCRM_AUTO_REGION_OFFSET_X_MIN_32R 0x0374 ++#define BCRM_AUTO_REGION_OFFSET_X_MAX_32R 0x0378 ++#define BCRM_AUTO_REGION_OFFSET_X_INC_32R 0x037C ++#define BCRM_AUTO_REGION_OFFSET_Y_32RW 0x0380 ++#define BCRM_AUTO_REGION_OFFSET_Y_MIN_32R 0x0384 ++#define BCRM_AUTO_REGION_OFFSET_Y_MAX_32R 0x0388 ++#define BCRM_AUTO_REGION_OFFSET_Y_INC_32R 0x038C ++#define _BCRM_LAST_ADDR BCRM_AUTO_REGION_OFFSET_Y_INC_32R ++ ++#define AV_CAM_REG_SIZE 2 ++#define AV_CAM_DATA_SIZE_8 1 ++#define AV_CAM_DATA_SIZE_16 2 ++#define AV_CAM_DATA_SIZE_32 4 ++#define AV_CAM_DATA_SIZE_64 8 ++ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// ENUMS ++//////////////////////////////////////////////////////////////////////////////// ++ ++/* BCRM_IMG_MIPI_DATA_FORMAT_32RW register values */ ++enum MIPI_DATA_FORMAT { ++ MIPI_DATA_FORMAT_YUV_420_8_LEG = 0x1A, ++ MIPI_DATA_FORMAT_YUV_420_8 = 0x18, ++ MIPI_DATA_FORMAT_YUV_420_10 = 0x19, ++ MIPI_DATA_FORMAT_YUV_420_8_CSPS = 0x1C, ++ MIPI_DATA_FORMAT_YUV_420_10_CSPS = 0x1D, ++ MIPI_DATA_FORMAT_YUV_422_8 = 0x1E, ++ MIPI_DATA_FORMAT_YUV_422_10 = 0x1F, ++ MIPI_DATA_FORMAT_RGB888 = 0x24, ++ MIPI_DATA_FORMAT_RGB666 = 0x23, ++ MIPI_DATA_FORMAT_RGB565 = 0x22, ++ MIPI_DATA_FORMAT_RGB555 = 0x21, ++ MIPI_DATA_FORMAT_RGB444 = 0x20, ++ MIPI_DATA_FORMAT_RAW6 = 0x28, ++ MIPI_DATA_FORMAT_RAW7 = 0x29, ++ MIPI_DATA_FORMAT_RAW8 = 0x2A, ++ MIPI_DATA_FORMAT_RAW10 = 0x2B, ++ MIPI_DATA_FORMAT_RAW12 = 0x2C, ++ MIPI_DATA_FORMAT_RAW14 = 0x2D, ++ MIPI_DATA_FORMAT_JPEG = 0x30 ++}; ++ ++/* BCRM_IMG_BAYER_PATTERN_8RW register values */ ++enum BAYER_PATTERN { ++ BAYER_PATTERN_MONO = 0, ++ BAYER_PATTERN_GR = 1, ++ BAYER_PATTERN_RG = 2, ++ BAYER_PATTERN_GB = 3, ++ BAYER_PATTERN_BG = 4 ++}; ++ ++/* BCRM_IMG_REVERSE_X_8RW */ ++enum REVERSE_X { ++ REVERSE_X_OFF = 0, ++ REVERSE_X_FLIP_H = 1, ++}; ++ ++/* BCRM_IMG_REVERSE_X_8RW */ ++enum REVERSE_Y { ++ REVERSE_Y_OFF = 0, ++ REVERSE_Y_FLIP_V = 1, ++}; ++ ++/* BCRM_IMG_BAYER_PATTERN_8RW register values */ ++enum EXPOSURE_AUTO { ++ EXPOSURE_AUTO_OFF = 0, ++ EXPOSURE_AUTO_ONCE = 1, ++ EXPOSURE_AUTO_CONTINUOUS = 2 ++}; ++ ++/* BCRM_GAIN_AUTO_8RW register values */ ++enum GAIN_AUTO { ++ GAIN_AUTO_OFF = 0, ++ GAIN_AUTO_ONCE = 1, ++ GAIN_AUTO_CONTINUOUS = 2 ++}; ++ ++/* BCRM_WHITE_BALANCE_AUTO_8RW register values */ ++enum WHITEBALANCE_AUTO { ++ WHITEBALANCE_AUTO_OFF = 0, ++ WHITEBALANCE_AUTO_ONCE = 1, ++ WHITEBALANCE_AUTO_CONTINUOUS = 2 ++}; ++ ++/* CCI_CHANGE_MODE_8W & CCI_CURRENT_MODE_8R */ ++enum OPERATION_MODE { ++ OPERATION_MODE_BCRM = 0, ++ OPERATION_MODE_GENCP = 1 ++}; ++ ++/* BCRM_ACQUISITION_STATUS_8R register values */ ++enum ACQUISITION_STATUS { ++ ACQUISITION_STATUS_STOPPED = 0, ++ ACQUISITION_STATUS_RUNNING = 1 ++}; ++ ++/* CCI Device capability String encoding */ ++enum CCI_STRING_ENC { ++ CCI_STRING_ENC_ASCII = 0, ++ CCI_STRING_ENC_UTF8 = 1, ++ CCI_STRING_ENC_UTF16 = 2 ++}; ++ ++/* BCRM digital binning setting */ ++enum BCRM_DIGITAL_BINNING_SETTING { ++ DIGITAL_BINNING_OFF = 0, ++ DIGITAL_BINNING_2X2 = 1, ++ DIGITAL_BINNING_3X3 = 2, ++ DIGITAL_BINNING_4X4 = 3, ++ DIGITAL_BINNING_5X5 = 4, ++ DIGITAL_BINNING_6X6 = 5, ++ DIGITAL_BINNING_7X7 = 6, ++ DIGITAL_BINNING_8X8 = 7 ++}; ++ ++/* BCRM digital binning mode */ ++enum BCRM_DIGITAL_BINNING_MODE { ++ DIGITAL_BINNING_MODE_AVG = 0, ++ DIGITAL_BINNING_MODE_SUM = 1 ++}; ++ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// UNIONS ++//////////////////////////////////////////////////////////////////////////////// ++/* CCI_DEVICE_CAP_64R regsiter values */ ++union cci_device_caps_reg { ++ struct { ++ unsigned long long user_name:1; ++ unsigned long long bcrm:1; ++ unsigned long long gencp:1; ++ unsigned long long reserved:1; ++ unsigned long long string_encoding:4; ++ unsigned long long family_name:1; ++ unsigned long long reserved2:55; ++ } caps; ++ ++ unsigned long long value; ++}; ++ ++/* BCRM_VERSION_32R register values */ ++union bcrm_version_reg { ++ struct { ++ unsigned long minor:16; ++ unsigned long major:16; ++ } handshake; ++ ++ unsigned long value; ++}; ++ ++/* BCRM_DEVICE_FIRMWARE_VERSION_64R register values */ ++union bcrm_device_firmware_version_reg { ++ struct { ++ unsigned long long special:8; ++ unsigned long long major:8; ++ unsigned long long minor:16; ++ unsigned long long patch:32; ++ } handshake; ++ ++ unsigned long long value; ++}; ++ ++/* BCRM_WRITE_HANDSHAKE_8RW register values */ ++union bcrm_write_done_handshake_reg { ++ struct { ++ unsigned char finished:1; ++ unsigned char reserved:6; ++ unsigned char handshake_supported:1; ++ } handshake; ++ ++ unsigned char value; ++}; ++ ++/* BCRM_FEATURE_INQUIRY_64R register values */ ++union bcrm_feature_reg { ++ struct { ++ unsigned long long reverse_x_avail:1; ++ unsigned long long reverse_y_avail:1; ++ unsigned long long intensity_auto_prcedence_avail:1; ++ unsigned long long black_level_avail:1; ++ unsigned long long gain_avail:1; ++ unsigned long long gamma_avail:1; ++ unsigned long long contrast_avail:1; ++ unsigned long long saturation_avail:1; ++ unsigned long long hue_avail:1; ++ unsigned long long white_balance_avail:1; ++ unsigned long long sharpness_avail:1; ++ unsigned long long exposure_auto:1; ++ unsigned long long gain_auto:1; ++ unsigned long long white_balance_auto_avail:1; ++ unsigned long long device_temperature_avail:1; ++ unsigned long long acquisition_abort:1; ++ unsigned long long acquisition_frame_rate:1; ++ unsigned long long frame_trigger:1; ++ unsigned long long exposure_active_line_avail:1; ++ unsigned long long reserved:45; ++ } feature_inq; ++ ++ unsigned long long value; ++}; ++ ++/* BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R register values */ ++union bcrm_supported_lanecount_reg { ++ struct { ++ unsigned char one_lane_avail:1; ++ unsigned char two_lane_avail:1; ++ unsigned char three_lane_avail:1; ++ unsigned char four_lane_avail:1; ++ unsigned char reserved:4; ++ } lane_count; ++ ++ unsigned char value; ++}; ++ ++/* BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R register values */ ++union bcrm_avail_mipi_reg { ++ struct { ++ unsigned long long yuv420_8_leg_avail:1; ++ unsigned long long yuv420_8_avail:1; ++ unsigned long long yuv420_10_avail:1; ++ unsigned long long yuv420_8_csps_avail:1; ++ unsigned long long yuv420_10_csps_avail:1; ++ unsigned long long yuv422_8_avail:1; ++ unsigned long long yuv422_10_avail:1; ++ unsigned long long rgb888_avail:1; ++ unsigned long long rgb666_avail:1; ++ unsigned long long rgb565_avail:1; ++ unsigned long long rgb555_avail:1; ++ unsigned long long rgb444_avail:1; ++ unsigned long long raw6_avail:1; ++ unsigned long long raw7_avail:1; ++ unsigned long long raw8_avail:1; ++ unsigned long long raw10_avail:1; ++ unsigned long long raw12_avail:1; ++ unsigned long long raw14_avail:1; ++ unsigned long long jpeg_avail:1; ++ unsigned long long reserved:45; ++ } avail_mipi; ++ ++ unsigned long long value; ++}; ++ ++/* BCRM_IMG_BAYER_PATTERN_INQUIRY_8R register values */ ++union bcrm_bayer_inquiry_reg { ++ struct { ++ unsigned char monochrome_avail:1; ++ unsigned char bayer_GR_avail:1; ++ unsigned char bayer_RG_avail:1; ++ unsigned char bayer_GB_avail:1; ++ unsigned char bayer_BG_avail:1; ++ unsigned char reserved:3; ++ } bayer_pattern; ++ ++ unsigned char value; ++}; ++ ++ ++union bcrm_digital_binning_inquiry_reg { ++ struct { ++ unsigned short int digital_binning_2x2:1; ++ unsigned short int digital_binning_3x3:1; ++ unsigned short int digital_binning_4x4:1; ++ unsigned short int digital_binning_5x5:1; ++ unsigned short int digital_binning_6x6:1; ++ unsigned short int digital_binning_7x7:1; ++ unsigned short int digital_binning_8x8:1; ++ unsigned short int reserved:9; ++ } digital_binning_inquiry; ++ ++ unsigned short int value; ++}; ++ ++#endif /* ALVIUM_REGS_H */ +diff --git a/drivers/media/i2c/avt_csi2.c b/drivers/media/i2c/avt_csi2.c +new file mode 100644 +index 000000000000..b3c613af37c4 +--- /dev/null ++++ b/drivers/media/i2c/avt_csi2.c +@@ -0,0 +1,7407 @@ ++/* ++ * Allied Vision CSI2 Camera ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++#include ++#else ++#include ++#include ++#endif //#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++#include ++#include ++#include ++#include ++ ++#include "avt_csi2.h" ++#include ++#include ++ ++static int debug; ++MODULE_PARM_DESC(debug, "debug"); ++module_param(debug, int, 0600);/* S_IRUGO */ ++ ++/* For overriding alignment value. 0=Use internal value.*/ ++static int v4l2_width_align = 0; ++MODULE_PARM_DESC(v4l2_width_align, "v4l2_width_align"); ++module_param(v4l2_width_align, int, 0600);/* S_IRUGO */ ++ ++ ++static int add_wait_time_ms = 2000; ++module_param(add_wait_time_ms, int, 0600); ++ ++static const char * const v4l2_triggeractivation_menu[] = { ++ "Rising Edge", ++ "Falling Edge", ++ "Any Edge", ++ "Level High", ++ "Level Low" ++}; ++static const char * const v4l2_triggersource_menu[] = { ++ "Line 0", ++ "Line 1", ++ "Line 2", ++ "Line 3", ++ "Software" ++}; ++ ++static const char * const v4l2_binning_mode_menu[] = { ++ "Average", ++ "Sum", ++}; ++ ++#define AVT_DBG_LVL 3 ++ ++#define avt_dbg(dev, fmt, args...) \ ++ v4l2_dbg(AVT_DBG_LVL, debug, dev, "%s:%d: " fmt "", \ ++ __func__, __LINE__, ##args) \ ++ ++#define avt_err(dev, fmt, args...) \ ++ v4l2_err(dev, "%s:%d: " fmt "", __func__, __LINE__, ##args) \ ++ ++#define avt_warn(dev, fmt, args...) \ ++ v4l2_warn(dev, "%s:%d: " fmt "", __func__, __LINE__, ##args) \ ++ ++#define avt_info(dev, fmt, args...) \ ++ v4l2_info(dev, "%s:%d: " fmt "", __func__, __LINE__, ##args) \ ++ ++#define DEFAULT_FPS 30 ++ ++#define AV_CAM_DEFAULT_FMT MEDIA_BUS_FMT_VYUY8_2X8 ++ ++#define IO_LIMIT 1024 ++#define BCRM_WAIT_HANDSHAKE_TIMEOUT 3000 ++ ++static int avt_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel); ++ ++static int avt_reg_read(struct i2c_client *client, __u32 reg, ++ __u32 reg_size, __u32 count, char *buffer); ++ ++static int avt_init_mode(struct v4l2_subdev *sd); ++ ++static int avt_init_frame_param(struct v4l2_subdev *sd); ++ ++static int avt_align_width(struct v4l2_subdev *sd, int width, u32 max_width, u32 mbus_fmt_code); ++static int avt_get_align_width(struct v4l2_subdev *sd, u32 mbus_fmt_code); ++static bool common_range(uint32_t nMin1, uint32_t nMax1, uint32_t nInc1, ++ uint32_t nMin2, uint32_t nMax2, uint32_t nInc2, ++ uint32_t *rMin, uint32_t *rMax, uint32_t *rInc); ++ ++static void bcrm_dump(struct i2c_client *client); ++static void dump_bcrm_reg_8(struct i2c_client *client, u16 nOffset, const char *pRegName); ++static void dump_bcrm_reg_32(struct i2c_client *client, u16 nOffset, const char *pRegName); ++static void dump_bcrm_reg_64(struct i2c_client *client, u16 nOffset, const char *pRegName); ++static int soft_reset(struct i2c_client *client); ++static void dump_frame_param(struct v4l2_subdev *sd); ++static void dump_camera_firmware_version(struct i2c_client *client); ++static bool is_fallback_app_running(struct i2c_client *client); ++static int ioctl_queryctrl64(struct v4l2_subdev *sd, struct v4l2_query_ext_ctrl *qctrl); ++ ++static void swapbytes(void *_object, size_t _size) ++{ ++ switch (_size) { ++ case 2: ++ cpu_to_be16s((uint16_t *)_object); ++ break; ++ case 4: ++ cpu_to_be32s((uint32_t *)_object); ++ break; ++ case 8: ++ cpu_to_be64s((uint64_t *)_object); ++ break; ++ } ++} ++ ++static int i2c_read(struct i2c_client *client, uint32_t reg, uint32_t size, uint32_t count, char *buf) ++{ ++ struct i2c_msg msg[2]; ++ u8 msgbuf[AV_CAM_REG_SIZE]; ++ int ret = 0, i = 0, j = 0, reg_size_bkp; ++ ++ BUG_ON(size != AV_CAM_REG_SIZE); ++ ++ reg_size_bkp = size; ++ ++ ++ /* clearing i2c msg with 0's */ ++ memset(msg, 0, sizeof(msg)); ++ ++ if (count > IO_LIMIT) { ++ dev_err(&client->dev, "Limit excedded! i2c_reg->count > IO_LIMIT\n"); ++ count = IO_LIMIT; ++ } ++ ++ /* find start address of buffer */ ++ for (i = --size; i >= 0; i--, j++) ++ msgbuf[i] = ((reg >> (8*j)) & 0xFF); ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = reg_size_bkp; ++ msg[0].buf = msgbuf; ++ msg[1].addr = client->addr; /* slave address */ ++ msg[1].flags = I2C_M_RD; /* read flag setting */ ++ msg[1].len = count; ++ msg[1].buf = buf; /* dest buffer */ ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ ++ return ret; ++} ++ ++static int i2c_write(struct i2c_client *client, uint32_t reg, uint32_t reg_size, uint32_t buf_size, char *buf) ++{ ++ int j = 0, i = 0; ++ char *i2c_w_buf; ++ int ret = 0; ++ ++ /* count exceeds writing IO_LIMIT characters */ ++ if (buf_size > IO_LIMIT) { ++ dev_err(&client->dev, "limit excedded! i2c_reg->count > IO_LIMIT\n"); ++ buf_size = IO_LIMIT; ++ } ++ ++ i2c_w_buf = kzalloc(buf_size + reg_size, GFP_KERNEL); ++ if (!i2c_w_buf) ++ return -ENOMEM; ++ ++ /* Fill the address in buffer upto size of address want to write */ ++ for (i = reg_size - 1, j = 0; i >= 0; i--, j++) ++ i2c_w_buf[i] = ((reg >> (8 * j)) & 0xFF); ++ ++ /* Append the data value in the same buffer */ ++ memcpy(i2c_w_buf + reg_size, buf, buf_size); ++ ++ ret = i2c_master_send(client, i2c_w_buf, buf_size + reg_size); ++ ++ kfree(i2c_w_buf); ++ ++ return ret; ++} ++ ++static bool is_fallback_app_running(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret = 0; ++ bool fallback_app_running = false; ++ u64 avail_mipi = 0; ++ uint8_t supported_lane_counts = 0; ++ ++ /* If camera lists no available MIPI data formats or no available MIPI lanes the fallback app is running */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &avail_mipi); ++ ++ if (ret < 0) ++ { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return false; ++ } ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &supported_lane_counts); ++ ++ if (ret < 0) ++ { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return false; ++ } ++ ++ fallback_app_running = ((avail_mipi == 0) || supported_lane_counts == 0); ++ if (fallback_app_running) ++ { ++ dev_warn(&client->dev, "Camera fallback app running. Streaming disabled.\n"); ++ } ++ ++ return fallback_app_running; ++} ++ ++static bool bcrm_get_write_handshake_availibility(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ u8 value = 0; ++ int status; ++ ++ /* Reading the device firmware version from camera */ ++ status = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_WRITE_HANDSHAKE_8RW, ++ AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ ++ if ((status >= 0) && (value & 0x80)) ++ { ++ dev_dbg(&client->dev, "BCRM write handshake supported!"); ++ return true; ++ } ++ else ++ { ++ dev_warn(&client->dev, "BCRM write handshake NOT supported!"); ++ return false; ++ } ++} ++ ++/** ++ * @brief Since the camera needs a few ms to process written data, we need to poll ++ the handshake register to make sure to continue not too early with the next write access. ++ * ++ * @param timeout_ms : Timeout value in ms ++ * @param reg : Register previously written to (used just for debug msg) ++ * @return uint64_t : Duration in ms ++ */ ++static uint64_t wait_for_bcrm_write_handshake(struct i2c_client *client, uint64_t timeout_ms, u16 reg) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ static const int poll_interval_ms = 2; ++ static const int default_wait_time_ms = 20; ++ int status = 0; ++ u8 buffer[3] = {0}; ++ u8 handshake_val = 0; ++ bool handshake_valid = false; ++ uint64_t duration_ms = 0; ++ uint64_t start_jiffies = get_jiffies_64(); ++ unsigned long timeout_jiffies = jiffies + msecs_to_jiffies(timeout_ms); ++ ++ if (priv->write_handshake_available) ++ { ++ /* We need to poll the handshake register and wait until the camera has processed the data */ ++ dev_dbg(&client->dev, " Wait for 'write done' bit (0x81) ..."); ++ do ++ { ++ usleep_range(poll_interval_ms*1000, (poll_interval_ms*1000)+1); ++ /* Read handshake register */ ++ status = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_WRITE_HANDSHAKE_8RW, ++ AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_8, ++ (char *)&handshake_val); ++ ++ if (status >= 0) ++ { ++ /* Check, if handshake bit is set */ ++ if ((handshake_val & 0x01) == 1) ++ { ++ do ++ { ++ /* Handshake set by camera. We should to reset it */ ++ buffer[0] = (priv->cci_reg.bcrm_addr + BCRM_WRITE_HANDSHAKE_8RW) >> 8; ++ buffer[1] = (priv->cci_reg.bcrm_addr + BCRM_WRITE_HANDSHAKE_8RW) & 0xff; ++ buffer[2] = (handshake_val & 0xFE); /* Reset LSB (handshake bit)*/ ++ status = i2c_master_send(client, buffer, sizeof(buffer)); ++ if (status >= 0) ++ { ++ /* Since the camera needs a few ms for every write access to finish, we need to poll here too */ ++ dev_dbg(&client->dev, " Wait for reset of 'write done' bit (0x80) ..."); ++ do ++ { ++ usleep_range(poll_interval_ms*1000, (poll_interval_ms*1000)+1); ++ /* We need to wait again until the bit is reset */ ++ status = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_WRITE_HANDSHAKE_8RW, ++ AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_8, ++ (char *)&handshake_val); ++ ++ if (status >= 0) ++ { ++ if ((handshake_val & 0x1) == 0) /* Verify write */ ++ { ++ duration_ms = jiffies_to_msecs(get_jiffies_64() - start_jiffies); ++ handshake_valid = true; ++ break; ++ } ++ usleep_range(poll_interval_ms*1000, (poll_interval_ms*1000)+1); ++ } ++ else ++ { ++ dev_err(&client->dev, " Error while reading WRITE_HANDSHAKE_REG_8RW register."); ++ break; ++ } ++ } while (time_before(jiffies, timeout_jiffies)); ++ if (!handshake_valid) ++ { ++ dev_warn(&client->dev, " Verify handshake timeout :-)"); ++ } ++ break; ++ } ++ else ++ { ++ dev_err(&client->dev, " Error while writing WRITE_HANDSHAKE_REG_8RW register."); ++ break; ++ } ++ } while (!handshake_valid && time_before(jiffies, timeout_jiffies)); ++ } ++ } ++ else ++ { ++ dev_err(&client->dev, " Error while reading WRITE_HANDSHAKE_REG_8RW register."); ++ break; ++ } ++ } ++ while (!handshake_valid && time_before(jiffies, timeout_jiffies)); ++ ++ if (!handshake_valid) ++ { ++ dev_err(&client->dev, " Write handshake timeout! (Register 0x%02X)", reg); ++ } ++ } ++ else ++ { ++ /* Handshake not supported. Use static sleep at least once as fallback */ ++ ++ usleep_range(default_wait_time_ms*1000, (default_wait_time_ms*1000)+1); ++ //for (i=0; i> 8; ++ au8Buf[1] = reg & 0xff; ++ au8Buf[2] = val; ++ ++ ret = i2c_master_send(client, au8Buf, 3); ++ ++ if (ret < 0) ++ dev_err(&client->dev, "%s, i2c write failed reg=%x,val=%x error=%d\n", ++ __func__, reg, val, ret); ++ ++ duration = wait_for_bcrm_write_handshake(client, BCRM_WAIT_HANDSHAKE_TIMEOUT, reg); ++ ++ dev_dbg(&client->dev, "i2c write success reg=0x%x, duration=%lldms, ret=%d\n", reg, duration, ret); ++ ++ return ret; ++} ++ ++static struct avt_csi2_priv *avt_get_priv(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client; ++ struct camera_common_data *s_data; ++ ++ if (sd == NULL) ++ return NULL; ++ ++ client = v4l2_get_subdevdata(sd); ++ if (client == NULL) ++ return NULL; ++ ++ s_data = to_camera_common_data(&client->dev); ++ if (s_data == NULL) ++ return NULL; ++ ++ return (struct avt_csi2_priv *)s_data->priv; ++} ++ ++static struct v4l2_ctrl *avt_get_control(struct v4l2_subdev *sd, u32 id) ++{ ++ int i; ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ ++ for (i = 0; i < AVT_MAX_CTRLS; i++) { ++ if (priv->ctrls[i] == NULL) ++ continue; ++ if (priv->ctrls[i]->id == id) ++ return priv->ctrls[i]; ++ } ++ ++ return NULL; ++} ++ ++static int ioctl_gencam_i2cwrite_reg(struct i2c_client *client, uint32_t reg, ++ uint32_t size, uint32_t count, const char *buf) ++{ ++ int j = 0, i = 0; ++ char *i2c_w_buf; ++ int ret = 0; ++ uint64_t duration = 0; ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ /* count exceeds writting IO_LIMIT characters */ ++ if (count > IO_LIMIT) { ++ dev_err(&client->dev, "limit excedded! i2c_reg->count > IO_LIMIT\n"); ++ count = IO_LIMIT; ++ } ++ ++ i2c_w_buf = kzalloc(count + size, GFP_KERNEL); ++ if (!i2c_w_buf) ++ return -ENOMEM; ++ ++ /* Fill the address in buffer upto size of address want to write */ ++ for (i = size - 1, j = 0; i >= 0; i--, j++) ++ i2c_w_buf[i] = ((reg >> (8 * j)) & 0xFF); ++ ++ /* Append the data value in the same buffer */ ++ memcpy(i2c_w_buf + size, buf, count); ++ ++ ret = i2c_master_send(client, i2c_w_buf, count + size); ++ ++ if (ret < 0) ++ dev_err(&client->dev, "%s:%d: i2c write failed ret %d\n", ++ __func__, __LINE__, ret); ++ ++ ++ /* Wait for write handshake register only for BCM registers */ ++ if ((reg >= priv->cci_reg.bcrm_addr) && (reg <= priv->cci_reg.bcrm_addr + _BCRM_LAST_ADDR)) ++ { ++ duration = wait_for_bcrm_write_handshake(client, BCRM_WAIT_HANDSHAKE_TIMEOUT, reg); ++ dev_dbg(&client->dev, "i2c write success reg=0x%x, duration=%lldms, ret=%d\n", reg, duration, ret); ++ } ++ ++ kfree(i2c_w_buf); ++ ++ return ret; ++} ++ ++static int ioctl_bcrm_i2cwrite_reg(struct i2c_client *client, ++ struct v4l2_ext_control *vc, ++ unsigned int reg, ++ int length) ++{ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ char *i2c_reg_buf; ++ ++ ssize_t ret; ++ __u64 temp = 0; ++ ++ if (length > AV_CAM_DATA_SIZE_32) { ++ temp = vc->value64; ++ swapbytes(&temp, length); ++ } else ++ swapbytes(&vc->value, length); ++ ++ i2c_reg = reg; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = length; ++ ++ if (length > AV_CAM_DATA_SIZE_32) ++ i2c_reg_buf = (char *) &temp; ++ else ++ i2c_reg_buf = (char *) &vc->value; ++ ++ ret = ioctl_gencam_i2cwrite_reg(client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ if (ret < 0) ++ dev_err(&client->dev, "%s:%d i2c write failed\n", ++ __func__, __LINE__); ++ ++ return ret; ++} ++ ++static int set_bayer_format(struct i2c_client *client, __u8 value) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret = 0; ++ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ ++ char *i2c_reg_buf; ++ ++ CLEAR(i2c_reg); ++ i2c_reg = priv->cci_reg.bcrm_addr + BCRM_IMG_BAYER_PATTERN_8RW; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = AV_CAM_DATA_SIZE_8; ++ i2c_reg_buf = (char *) &value; ++ ++ ret = ioctl_gencam_i2cwrite_reg(client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "%s:%d i2c write failed\n", ++ __func__, __LINE__); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++static bool avt_check_fmt_available(struct i2c_client *client, u32 media_bus_fmt) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ u64 avail_mipi = 0; ++ unsigned char bayer_val = 0; ++ union bcrm_avail_mipi_reg feature_inquiry_reg; ++ union bcrm_bayer_inquiry_reg bayer_inquiry_reg; ++ int ret; ++ ++ dev_dbg(&client->dev,"%s: media_bus_fmt: 0x%x\n", __FUNCTION__, media_bus_fmt); ++ ++ if (media_bus_fmt == MEDIA_BUS_FMT_CUSTOM) ++ return true; ++ ++ /* read the MIPI format register to check whether the camera ++ * really support the requested pixelformat format ++ */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &avail_mipi); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return false; ++ } ++ ++ dev_dbg(&client->dev,"%s: Camera available MIPI data formats: 0x%llx\n", __FUNCTION__, avail_mipi); ++ ++ if (priv->fallback_app_running) ++ { ++ /* Fallback app running? -> Fake pixelformat */ ++ avail_mipi = 0x80; // RGB888 ++ } ++ ++ feature_inquiry_reg.value = avail_mipi; ++ ++ /* read the Bayer Inquiry register to check whether the camera ++ * really support the requested RAW format ++ */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_IMG_BAYER_PATTERN_INQUIRY_8R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &bayer_val); ++ ++ dev_dbg(&client->dev,"%s: Camera bayer pattern inq: 0x%x\n", __FUNCTION__, bayer_val); ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return false; ++ } ++ ++ bayer_inquiry_reg.value = bayer_val; ++ ++ switch (media_bus_fmt) { ++ case MEDIA_BUS_FMT_RGB444_1X12: ++ return feature_inquiry_reg.avail_mipi.rgb444_avail; ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ return feature_inquiry_reg.avail_mipi.rgb565_avail; ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_BGR888_1X24: ++ return feature_inquiry_reg.avail_mipi.rgb888_avail; ++ case MEDIA_BUS_FMT_VYUY8_2X8: ++ return feature_inquiry_reg.avail_mipi.yuv422_8_avail; ++ /* RAW 8 */ ++ case MEDIA_BUS_FMT_Y8_1X8: ++ return feature_inquiry_reg.avail_mipi.raw8_avail && ++ bayer_inquiry_reg.bayer_pattern.monochrome_avail; ++ case MEDIA_BUS_FMT_SBGGR8_1X8: ++ return feature_inquiry_reg.avail_mipi.raw8_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_BG_avail; ++ case MEDIA_BUS_FMT_SGBRG8_1X8: ++ return feature_inquiry_reg.avail_mipi.raw8_avail ++ && bayer_inquiry_reg.bayer_pattern.bayer_GB_avail; ++ case MEDIA_BUS_FMT_SGRBG8_1X8: ++ return feature_inquiry_reg.avail_mipi.raw8_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_GR_avail; ++ case MEDIA_BUS_FMT_SRGGB8_1X8: ++ return feature_inquiry_reg.avail_mipi.raw8_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_RG_avail; ++ /* RAW 10 */ ++ case MEDIA_BUS_FMT_Y10_1X10: ++ return feature_inquiry_reg.avail_mipi.raw10_avail && ++ bayer_inquiry_reg.bayer_pattern.monochrome_avail; ++ case MEDIA_BUS_FMT_SGBRG10_1X10: ++ return feature_inquiry_reg.avail_mipi.raw10_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_GB_avail; ++ case MEDIA_BUS_FMT_SGRBG10_1X10: ++ return feature_inquiry_reg.avail_mipi.raw10_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_GR_avail; ++ case MEDIA_BUS_FMT_SRGGB10_1X10: ++ return feature_inquiry_reg.avail_mipi.raw10_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_RG_avail; ++ /* RAW 12 */ ++ case MEDIA_BUS_FMT_Y12_1X12: ++ return feature_inquiry_reg.avail_mipi.raw12_avail && ++ bayer_inquiry_reg.bayer_pattern.monochrome_avail; ++ case MEDIA_BUS_FMT_SBGGR12_1X12: ++ return feature_inquiry_reg.avail_mipi.raw12_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_BG_avail; ++ case MEDIA_BUS_FMT_SGBRG12_1X12: ++ return feature_inquiry_reg.avail_mipi.raw12_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_GB_avail; ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ return feature_inquiry_reg.avail_mipi.raw12_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_GR_avail; ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ return feature_inquiry_reg.avail_mipi.raw12_avail && ++ bayer_inquiry_reg.bayer_pattern.bayer_RG_avail; ++ } ++ ++ return false; ++} ++ ++static int avt_ctrl_send(struct i2c_client *client, ++ struct avt_ctrl *vc) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret = 0; ++ unsigned int reg = 0; ++ int length = 0; ++ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ ++ char *i2c_reg_buf; ++ ++ //void *mipi_csi2_info; ++ int r_wn = 0;/* write -> r_wn = 0, read -> r_wn = 1 */ ++ u64 avail_mipi = 0; ++ union bcrm_avail_mipi_reg feature_inquiry_reg; ++ union bcrm_bayer_inquiry_reg bayer_inquiry_reg; ++ unsigned char bayer_val = 0; ++ u64 temp = 0; ++ int gencp_mode_local = 0;/* Default BCRM mode */ ++ __u8 bayer_temp = 0; ++ ++ bayer_inquiry_reg.value = 0; ++ feature_inquiry_reg.value = 0; ++ ++ if (vc->id == V4L2_AV_CSI2_PIXELFORMAT_W) ++ { ++ /* read the MIPI format register to check whether the camera ++ * really support the requested pixelformat format ++ */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &avail_mipi); ++ ++ if (ret < 0) ++ { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ } ++ ++ if (priv->fallback_app_running) ++ { ++ /* Fallback app running? */ ++ avail_mipi = 0x80; // fake RGB888 ++ } ++ feature_inquiry_reg.value = avail_mipi; ++ ++ /* read the Bayer Inquiry register to check whether the camera ++ * really support the requested RAW format ++ */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_IMG_BAYER_PATTERN_INQUIRY_8R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &bayer_val); ++ ++ if (ret < 0) ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ ++ dev_dbg(&client->dev, "Bayer Inquiry Reg value : 0x%x\n", ++ bayer_val); ++ ++ bayer_inquiry_reg.value = bayer_val; ++ } ++ ++ switch (vc->id) { ++ case V4L2_AV_CSI2_STREAMON_W: ++ reg = BCRM_ACQUISITION_START_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_STREAMOFF_W: ++ reg = BCRM_ACQUISITION_STOP_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_ABORT_W: ++ reg = BCRM_ACQUISITION_ABORT_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_WIDTH_W: ++ reg = BCRM_IMG_WIDTH_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_HEIGHT_W: ++ reg = BCRM_IMG_HEIGHT_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_OFFSET_X_W: ++ reg = BCRM_IMG_OFFSET_X_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_OFFSET_Y_W: ++ reg = BCRM_IMG_OFFSET_Y_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_HFLIP_W: ++ reg = BCRM_IMG_REVERSE_X_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_VFLIP_W: ++ reg = BCRM_IMG_REVERSE_Y_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 0; ++ break; ++ case V4L2_AV_CSI2_PIXELFORMAT_W: ++ reg = BCRM_IMG_MIPI_DATA_FORMAT_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 0; ++ ++ if(!avt_check_fmt_available(client, vc->value0)) { ++ dev_err(&client->dev, "format 0x%x not supported\n", vc->value0); ++ return -EINVAL; ++ } ++ ++ switch (vc->value0) { ++ case MEDIA_BUS_FMT_CUSTOM: ++ vc->value0 = MIPI_DT_CUSTOM; ++ break; ++ case MEDIA_BUS_FMT_RGB444_1X12: ++ vc->value0 = MIPI_DT_RGB444; ++ break; ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ vc->value0 = MIPI_DT_RGB565; ++ break; ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_BGR888_1X24: ++ vc->value0 = MIPI_DT_RGB888; ++ break; ++ case MEDIA_BUS_FMT_VYUY8_2X8: ++ vc->value0 = MIPI_DT_YUV422; ++ break; ++ /* RAW 8 */ ++ case MEDIA_BUS_FMT_Y8_1X8: ++ vc->value0 = MIPI_DT_RAW8; ++ bayer_temp = monochrome; ++ break; ++ case MEDIA_BUS_FMT_SBGGR8_1X8: ++ vc->value0 = MIPI_DT_RAW8; ++ bayer_temp = bayer_bg; ++ break; ++ case MEDIA_BUS_FMT_SGBRG8_1X8: ++ vc->value0 = MIPI_DT_RAW8; ++ bayer_temp = bayer_gb; ++ break; ++ case MEDIA_BUS_FMT_SGRBG8_1X8: ++ vc->value0 = MIPI_DT_RAW8; ++ bayer_temp = bayer_gr; ++ break; ++ case MEDIA_BUS_FMT_SRGGB8_1X8: ++ vc->value0 = MIPI_DT_RAW8; ++ bayer_temp = bayer_rg; ++ break; ++ /* RAW 10 */ ++ case MEDIA_BUS_FMT_Y10_1X10: ++ vc->value0 = MIPI_DT_RAW10; ++ bayer_temp = monochrome; ++ break; ++ case MEDIA_BUS_FMT_SGBRG10_1X10: ++ vc->value0 = MIPI_DT_RAW10; ++ bayer_temp = bayer_gb; ++ break; ++ case MEDIA_BUS_FMT_SGRBG10_1X10: ++ vc->value0 = MIPI_DT_RAW10; ++ bayer_temp = bayer_gr; ++ break; ++ case MEDIA_BUS_FMT_SRGGB10_1X10: ++ vc->value0 = MIPI_DT_RAW10; ++ bayer_temp = bayer_rg; ++ break; ++ /* RAW 12 */ ++ case MEDIA_BUS_FMT_Y12_1X12: ++ vc->value0 = MIPI_DT_RAW12; ++ bayer_temp = monochrome; ++ break; ++ case MEDIA_BUS_FMT_SBGGR12_1X12: ++ vc->value0 = MIPI_DT_RAW12; ++ bayer_temp = bayer_bg; ++ break; ++ case MEDIA_BUS_FMT_SGBRG12_1X12: ++ vc->value0 = MIPI_DT_RAW12; ++ bayer_temp = bayer_gb; ++ break; ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ vc->value0 = MIPI_DT_RAW12; ++ bayer_temp = bayer_gr; ++ break; ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ vc->value0 = MIPI_DT_RAW12; ++ bayer_temp = bayer_rg; ++ break; ++ ++ case 0: ++ /* Fallback app running */ ++ dev_warn(&client->dev, "Invalid pixelformat detected (0). Fallback app running?"); ++ vc->value0 = MIPI_DT_RGB888; ++ break; ++ ++ default: ++ dev_err(&client->dev, "%s: format 0x%x not supported by the host\n", ++ __func__, vc->value0); ++ return -EINVAL; ++ } ++ break; ++ ++ case V4L2_AV_CSI2_WIDTH_R: ++ reg = BCRM_IMG_WIDTH_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_WIDTH_MINVAL_R: ++ reg = BCRM_IMG_WIDTH_MIN_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_WIDTH_MAXVAL_R: ++ reg = BCRM_IMG_WIDTH_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_WIDTH_INCVAL_R: ++ reg = BCRM_IMG_WIDTH_INC_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_HEIGHT_R: ++ reg = BCRM_IMG_HEIGHT_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_HEIGHT_MINVAL_R: ++ reg = BCRM_IMG_HEIGHT_MIN_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_HEIGHT_MAXVAL_R: ++ reg = BCRM_IMG_HEIGHT_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_HEIGHT_INCVAL_R: ++ reg = BCRM_IMG_HEIGHT_INC_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_X_R: ++ reg = BCRM_IMG_OFFSET_X_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_X_MIN_R: ++ reg = BCRM_IMG_OFFSET_X_MIN_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_X_MAX_R: ++ reg = BCRM_IMG_OFFSET_X_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_X_INC_R: ++ reg = BCRM_IMG_OFFSET_X_INC_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_Y_R: ++ reg = BCRM_IMG_OFFSET_Y_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_Y_MIN_R: ++ reg = BCRM_IMG_OFFSET_Y_MIN_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_Y_MAX_R: ++ reg = BCRM_IMG_OFFSET_Y_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_OFFSET_Y_INC_R: ++ reg = BCRM_IMG_OFFSET_Y_INC_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_SENSOR_WIDTH_R: ++ reg = BCRM_SENSOR_WIDTH_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_SENSOR_HEIGHT_R: ++ reg = BCRM_SENSOR_HEIGHT_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_MAX_WIDTH_R: ++ reg = BCRM_WIDTH_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_MAX_HEIGHT_R: ++ reg = BCRM_HEIGHT_MAX_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_PIXELFORMAT_R: ++ reg = BCRM_IMG_MIPI_DATA_FORMAT_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_PALYLOADSIZE_R: ++ reg = BCRM_BUFFER_SIZE_32R; ++ length = AV_CAM_DATA_SIZE_32; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_ACQ_STATUS_R: ++ reg = BCRM_ACQUISITION_STATUS_8R; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_HFLIP_R: ++ reg = BCRM_IMG_REVERSE_X_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_VFLIP_R: ++ reg = BCRM_IMG_REVERSE_Y_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_CURRENTMODE_R: ++ reg = CCI_CURRENT_MODE_8R; ++ length = AV_CAM_DATA_SIZE_8; ++ gencp_mode_local = 1; ++ r_wn = 1; ++ break; ++ case V4L2_AV_CSI2_CHANGEMODE_W: ++ reg = CCI_CHANGE_MODE_8W; ++ length = AV_CAM_DATA_SIZE_8; ++ gencp_mode_local = 1; ++ if (vc->value0 == MIPI_DT_CUSTOM) ++ priv->mode = AVT_GENCP_MODE; ++ else ++ priv->mode = AVT_BCRM_MODE; ++ r_wn = 0; ++ break; ++ default: ++ dev_err(&client->dev, "%s: unknown ctrl 0x%x\n", ++ __func__, vc->id); ++ return -EINVAL; ++ } ++ ++ if (r_wn) {/* read (r_wn=1) */ ++ ++ if (gencp_mode_local) { ++ ++ ret = avt_reg_read(client, ++ reg, AV_CAM_REG_SIZE, length, ++ (char *) &vc->value0); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ return 0; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + reg, ++ AV_CAM_REG_SIZE, length, ++ (char *) &vc->value0); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ if (vc->id == V4L2_AV_CSI2_PIXELFORMAT_R) { ++ /* To avoid ambiguity, resulting from ++ * two MBUS formats linked with ++ * one camera image format, ++ * we return value stored in private data ++ */ ++ vc->value0 = priv->mbus_fmt_code; ++ } ++ ++ return 0; ++ ++ } else {/* write (r_wn=0) */ ++ dev_dbg(&client->dev, "reg %x, length %d, vc->value0 0x%x\n", ++ reg, length, vc->value0); ++ ++ if (gencp_mode_local) { ++ ++ i2c_reg = reg; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = length; ++ ++ if (length > AV_CAM_DATA_SIZE_32) ++ i2c_reg_buf = (char *) &temp; ++ else ++ i2c_reg_buf = (char *) &vc->value0; ++ ++ ret = ioctl_gencam_i2cwrite_reg(client, ++ i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "%s:%d i2c write failed\n", ++ __func__, __LINE__); ++ return ret; ++ } else { ++ return 0; ++ } ++ } ++ ++ if (vc->id == V4L2_AV_CSI2_PIXELFORMAT_W) { ++ /* Set pixelformat then set bayer format, refer OCT-2417 ++ * ++ * XXX implement these somehow, below imx6 code: ++ * mipi_csi2_info = mipi_csi2_get_info(); ++ * mipi_csi2_set_datatype(mipi_csi2_info, vc->value0); ++ */ ++ } ++ ++ temp = vc->value0; ++ ++ if (length > AV_CAM_DATA_SIZE_32) ++ swapbytes(&temp, length); ++ else ++ swapbytes(&vc->value0, length); ++ ++ i2c_reg = priv->cci_reg.bcrm_addr + reg; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = length; ++ ++ if (length > AV_CAM_DATA_SIZE_32) ++ i2c_reg_buf = (char *) &temp; ++ else ++ i2c_reg_buf = (char *) &vc->value0; ++ ++ ret = ioctl_gencam_i2cwrite_reg(client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "%s:%d i2c write failed\n", ++ __func__, __LINE__); ++ return ret; ++ } ++ ++ /* Set pixelformat then set bayer format, refer OCT-2417 */ ++ if (vc->id == V4L2_AV_CSI2_PIXELFORMAT_W) { ++ ret = set_bayer_format(client, bayer_temp); ++ if (ret < 0) { ++ dev_err(&client->dev, "%s:%d i2c write failed, ret %d\n", ++ __func__, __LINE__, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++ } ++} ++ ++static void set_channel_avt_cam_mode(struct v4l2_subdev *sd, bool cam_mode) ++{ ++ struct tegra_channel *tch; ++ struct media_pad *pad_csi, *pad_vi; ++ struct v4l2_subdev *sd_csi, *sd_vi; ++ struct video_device *vdev_vi; ++ ++ if (!sd->entity.pads) ++ return; ++ ++ pad_csi = media_entity_remote_pad(&sd->entity.pads[0]); ++ sd_csi = media_entity_to_v4l2_subdev(pad_csi->entity); ++ pad_vi = media_entity_remote_pad(&sd_csi->entity.pads[1]); ++ sd_vi = media_entity_to_v4l2_subdev(pad_vi->entity); ++ vdev_vi = media_entity_to_video_device(pad_vi->entity); ++ tch = video_get_drvdata(vdev_vi); ++ ++ tch->avt_cam_mode = cam_mode; ++} ++ ++static void set_channel_trigger_mode(struct v4l2_subdev *sd, bool trigger_mode) ++{ ++ struct tegra_channel *tch; ++ struct media_pad *pad_csi, *pad_vi; ++ struct v4l2_subdev *sd_csi, *sd_vi; ++ struct video_device *vdev_vi; ++ ++ if (!sd->entity.pads) ++ return; ++ ++ pad_csi = media_entity_remote_pad(&sd->entity.pads[0]); ++ sd_csi = media_entity_to_v4l2_subdev(pad_csi->entity); ++ pad_vi = media_entity_remote_pad(&sd_csi->entity.pads[1]); ++ sd_vi = media_entity_to_v4l2_subdev(pad_vi->entity); ++ vdev_vi = media_entity_to_video_device(pad_vi->entity); ++ tch = video_get_drvdata(vdev_vi); ++ ++ tch->trigger_mode = trigger_mode; ++} ++ ++static void set_channel_pending_trigger(struct v4l2_subdev *sd) ++{ ++ struct tegra_channel *tch; ++ struct media_pad *pad_csi, *pad_vi; ++ struct v4l2_subdev *sd_csi, *sd_vi; ++ struct video_device *vdev_vi; ++ ++ if (!sd->entity.pads) ++ return; ++ ++ pad_csi = media_entity_remote_pad(&sd->entity.pads[0]); ++ sd_csi = media_entity_to_v4l2_subdev(pad_csi->entity); ++ pad_vi = media_entity_remote_pad(&sd_csi->entity.pads[1]); ++ sd_vi = media_entity_to_v4l2_subdev(pad_vi->entity); ++ vdev_vi = media_entity_to_video_device(pad_vi->entity); ++ tch = video_get_drvdata(vdev_vi); ++ ++ tch->pending_trigger = true; ++} ++ ++static void set_channel_timeout(struct v4l2_subdev *sd, unsigned long timeout) ++{ ++ struct tegra_channel *tch; ++ struct media_pad *pad_csi, *pad_vi; ++ struct v4l2_subdev *sd_csi, *sd_vi; ++ struct video_device *vdev_vi; ++ ++ if (!sd->entity.pads) ++ return; ++ ++ pad_csi = media_entity_remote_pad(&sd->entity.pads[0]); ++ sd_csi = media_entity_to_v4l2_subdev(pad_csi->entity); ++ pad_vi = media_entity_remote_pad(&sd_csi->entity.pads[1]); ++ sd_vi = media_entity_to_v4l2_subdev(pad_vi->entity); ++ vdev_vi = media_entity_to_video_device(pad_vi->entity); ++ tch = video_get_drvdata(vdev_vi); ++ ++ if (timeout == AVT_TEGRA_TIMEOUT_DISABLED) { ++ tch->timeout = timeout; ++ } ++ else ++ tch->timeout = msecs_to_jiffies(timeout); ++} ++ ++static void set_channel_stride_align(struct v4l2_subdev *sd, uint8_t align) ++{ ++ struct tegra_channel *tch; ++ struct media_pad *pad_csi, *pad_vi; ++ struct v4l2_subdev *sd_csi, *sd_vi; ++ struct video_device *vdev_vi; ++ ++ if (!sd->entity.pads) ++ return; ++ ++ pad_csi = media_entity_remote_pad(&sd->entity.pads[0]); ++ sd_csi = media_entity_to_v4l2_subdev(pad_csi->entity); ++ pad_vi = media_entity_remote_pad(&sd_csi->entity.pads[1]); ++ sd_vi = media_entity_to_v4l2_subdev(pad_vi->entity); ++ vdev_vi = media_entity_to_video_device(pad_vi->entity); ++ tch = video_get_drvdata(vdev_vi); ++ ++ tch->stride_align = align; ++} ++ ++static void set_channel_stride_align_for_format(struct v4l2_subdev *sd, uint32_t mbus_code) ++{ ++ /* Set stride alignment required for the format */ ++ switch (mbus_code) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_BGR888_1X24: ++ set_channel_stride_align(sd, 16); ++ break; ++ case MEDIA_BUS_FMT_VYUY8_2X8: ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ set_channel_stride_align(sd, 32); ++ break; ++ case MEDIA_BUS_FMT_CUSTOM: ++ set_channel_stride_align(sd, 64); ++ break; ++ /* 8 bit bayer formats */ ++ 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_Y8_1X8: ++ set_channel_stride_align(sd, 64); ++ break; ++ /* the remaining formats */ ++ default: ++ set_channel_stride_align(sd, 1); ++ break; ++ } ++} ++ ++static int avt_tegra_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd; ++ struct avt_csi2_priv *priv; ++ unsigned long timeout; ++ int i; ++ ++ priv = container_of(ctrl->handler, struct avt_csi2_priv, hdl); ++ sd = priv->subdev; ++ ++ switch (ctrl->id) { ++ case AVT_TEGRA_TIMEOUT: ++ if (ctrl->val == 0) ++ set_channel_timeout(sd, AVT_TEGRA_TIMEOUT_DISABLED); ++ else { ++ for (i = 0; i < ARRAY_SIZE(priv->ctrls); ++i) { ++ if (priv->ctrls[i]->id == AVT_TEGRA_TIMEOUT_VALUE) { ++ timeout = priv->ctrls[i]->val; ++ set_channel_timeout(sd, timeout); ++ return 0; ++ } ++ } ++ } ++ break; ++ case AVT_TEGRA_TIMEOUT_VALUE: ++ for (i = 0; i < ARRAY_SIZE(priv->ctrls); ++i) { ++ /* First check if the timouet is not disabled */ ++ if (priv->ctrls[i]->id == AVT_TEGRA_TIMEOUT) { ++ if (priv->ctrls[i]->val == 0) ++ return 0; ++ else ++ break; ++ } ++ } ++ ++ /* If it is not disabled, set it in HW */ ++ set_channel_timeout(sd, ctrl->val); ++ break; ++ case AVT_TEGRA_STRIDE_ALIGN: ++ if (ctrl->val == 0) ++ priv->stride_align_enabled = false; ++ else ++ priv->stride_align_enabled = true; ++ break; ++ case AVT_TEGRA_CROP_ALIGN: ++ if (ctrl->val == 0) ++ priv->crop_align_enabled = false; ++ else ++ priv->crop_align_enabled = true; ++ break; ++ case AVT_TEGRA_VALUE_UPDATE_INTERVAL: ++ priv->value_update_interval = ctrl->val; ++ atomic_set(&priv->force_value_update, 1); ++ wake_up_all(&priv->value_update_wq); ++ break; ++ case AVT_TEGRA_FORCE_VALUE_UPDATE: ++ atomic_set(&priv->force_value_update, 1); ++ wake_up_all(&priv->value_update_wq); ++ break; ++} ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops avt_tegra_ctrl_ops = { ++ .s_ctrl = avt_tegra_s_ctrl, ++}; ++ ++static const struct v4l2_ctrl_config avt_tegra_ctrl[] = { ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_TIMEOUT, ++ .name = "Frame timeout enabled", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .def = 1, ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_TIMEOUT_VALUE, ++ .name = "Frame timeout", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 100, ++ .max = 12000, ++ .step = 1, ++ .def = AVT_TEGRA_TIMEOUT_DEFAULT, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_STRIDE_ALIGN, ++ .name = "Stride alignment enabled", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .def = 1, ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_CROP_ALIGN, ++ .name = "Crop alignment enabled", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .def = 1, ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_VALUE_UPDATE_INTERVAL, ++ .name = "Value update interval", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .def = 1000, ++ .min = 0, ++ .max = 60000, ++ .step = 1, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = AVT_TEGRA_FORCE_VALUE_UPDATE, ++ .name = "Force value update", ++ .type = V4L2_CTRL_TYPE_BUTTON, ++ .def = 0, ++ .min = 0, ++ .max = 0, ++ .step = 0, ++ }, ++ { ++ .ops = &avt_tegra_ctrl_ops, ++ .id = V4L2_CID_LINK_FREQ, ++ .name = "Link Frequency", ++ .type = V4L2_CTRL_TYPE_INTEGER_MENU, ++ .def = 0, ++ .min = 0, ++ .max = 0, ++ .menu_skip_mask = 0, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY, ++ .is_private = true, ++ ++ }, ++}; ++ ++/* --------------- INIT --------------- */ ++ ++static int avt_csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); ++ case V4L2_EVENT_CTRL: ++ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); ++ default: ++ return -EINVAL; ++ } ++} ++ ++ ++/* --------------- CUSTOM IOCTLS --------------- */ ++ ++long avt_csi2_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ int ret = -ENOTTY; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct v4l2_i2c *i2c_reg; ++ char *i2c_reg_buf; ++ int *i2c_clk_freq; ++ struct v4l2_gencp_buffer_sizes *gencp_buf_sz; ++ struct v4l2_csi_driver_info *info; ++ struct v4l2_csi_config *config; ++ uint32_t i2c_reg_address; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ uint32_t clk; ++ uint8_t avt_supported_lane_counts = 0; ++ uint32_t avt_min_clk = 0; ++ uint32_t avt_max_clk = 0; ++ uint32_t common_min_clk = 0; ++ uint32_t common_max_clk = 0; ++ uint32_t common_inc_clk = 0; ++ struct i2c_adapter *root_adapter; ++ ++ avt_dbg(sd, "%s(cmd=%u)\n", __func__, cmd); ++ ++ switch(cmd) { ++ case VIDIOC_R_I2C: ++ avt_dbg(sd, "VIDIOC_R_I2C\n"); ++ i2c_reg = (struct v4l2_i2c *)arg; ++ i2c_reg_buf = kzalloc(i2c_reg->num_bytes, GFP_KERNEL); ++ if (!i2c_reg_buf) ++ return -ENOMEM; ++ ++ ret = i2c_read(client, i2c_reg->register_address, i2c_reg->register_size, ++ i2c_reg->num_bytes, i2c_reg_buf); ++ ++ if (ret < 0) ++ avt_err(sd, " I2C read failed. addr=0x%04X, num_bytes=%d, ret=%d\n", i2c_reg->register_address, i2c_reg->num_bytes, ret); ++ else ++ { ++ ret = copy_to_user((char *)i2c_reg->ptr_buffer, i2c_reg_buf, i2c_reg->num_bytes); ++ ++ if (ret == 0) ++ avt_dbg(sd, " I2C read success. addr=0x%04X, num_bytes=%d, ret=%d\n", i2c_reg->register_address, i2c_reg->num_bytes, ret); ++ else ++ avt_err(sd, " I2C read failed. copy_to_user failed. addr=0x%04X, num_bytes=%d, ret=%d\n", i2c_reg->register_address, i2c_reg->num_bytes, ret); ++ } ++ ++ kfree(i2c_reg_buf); ++ break; ++ ++ case VIDIOC_W_I2C: ++ avt_dbg(sd, "VIDIOC_W_I2C\n"); ++ i2c_reg = (struct v4l2_i2c *)arg; ++ ++ i2c_reg_buf = kzalloc(i2c_reg->num_bytes, GFP_KERNEL); ++ if (!i2c_reg_buf) ++ return -ENOMEM; ++ ++ ret = copy_from_user(i2c_reg_buf, (char *) i2c_reg->ptr_buffer, i2c_reg->num_bytes); ++ ++ ret = ioctl_gencam_i2cwrite_reg(client, i2c_reg->register_address, i2c_reg->register_size, i2c_reg->num_bytes, i2c_reg_buf); ++ ++ if (ret < 0) ++ { ++ avt_err(sd, " I2C write failed. addr=0x%04X, num_bytes=%d, ret=%d\n", i2c_reg->register_address, i2c_reg->num_bytes, ret); ++ } ++ /* Check if mode (BCRM or GenCP) is changed */ ++ else ++ { ++ avt_dbg(sd, " I2C write success. addr=0x%04X, num_bytes=%d, ret=%d\n", i2c_reg->register_address, i2c_reg->num_bytes, ret); ++ ++ if(i2c_reg->register_address == CCI_CHANGE_MODE_8W) ++ { ++ priv->mode = i2c_reg_buf[0] == 0 ? AVT_BCRM_MODE : AVT_GENCP_MODE; ++ set_channel_avt_cam_mode(sd, priv->mode); ++ if (priv->mode) ++ set_channel_timeout(sd, AVT_TEGRA_TIMEOUT_DISABLED); ++ else ++ set_channel_timeout(sd, CAPTURE_TIMEOUT_MS); ++ } ++ } ++ ++ break; ++ ++ case VIDIOC_G_I2C_CLOCK_FREQ: ++ avt_dbg(sd, "VIDIOC_G_I2C_CLOCK_FREQ\n"); ++ ++ i2c_clk_freq = arg; ++ root_adapter = i2c_root_adapter(&client->dev); ++ ++ *i2c_clk_freq = i2c_get_adapter_bus_clk_rate(root_adapter); ++ ++ avt_dbg(sd,"i2c clock %d",*i2c_clk_freq); ++ ++ ret = 0; ++ ++ break; ++ ++ case VIDIOC_G_GENCP_BUFFER_SIZES: ++ avt_dbg(sd, "VIDIOC_G_GENCP_BUFFER_SIZE\n"); ++ gencp_buf_sz = arg; ++ gencp_buf_sz->gencp_in_buffer_size = priv->gencp_reg.gencp_in_buffer_size; ++ gencp_buf_sz->gencp_out_buffer_size = priv->gencp_reg.gencp_out_buffer_size; ++ ret = 0; ++ break; ++ ++ case VIDIOC_G_DRIVER_INFO: ++ avt_dbg(sd, "VIDIOC_G_DRIVER_INFO\n"); ++ info = (struct v4l2_csi_driver_info *)arg; ++ ++ info->id.manufacturer_id = MANUFACTURER_ID_NVIDIA; ++ info->id.soc_family_id = SOC_FAMILY_ID_TEGRA; ++ info->id.driver_id = TEGRA_DRIVER_ID_DEFAULT; ++ ++ info->driver_version = (DRV_VER_MAJOR << 16) + (DRV_VER_MINOR << 8) + DRV_VER_PATCH; ++ info->driver_interface_version = (LIBCSI_DRV_SPEC_VERSION_MAJOR << 16) + (LIBCSI_DRV_SPEC_VERSION_MINOR << 8) + LIBCSI_DRV_SPEC_VERSION_PATCH; ++ info->driver_caps = AVT_DRVCAP_MMAP | AVT_DRVCAP_USRPTR; ++ info->usrptr_alignment = dma_get_cache_alignment(); ++ ++ ret = 0; ++ break; ++ ++ case VIDIOC_G_CSI_CONFIG: ++ avt_dbg(sd, "VIDIOC_G_CSI_CONFIG\n"); ++ config = (struct v4l2_csi_config *)arg; ++ ++ config->lane_count = priv->numlanes; ++ config->csi_clock = priv->csi_clk_freq; ++ ++ ret = 0; ++ break; ++ ++ case VIDIOC_S_CSI_CONFIG: ++ avt_dbg(sd, "VIDIOC_S_CSI_CONFIG\n"); ++ config = (struct v4l2_csi_config *)arg; ++ ++ /* Set number of lanes */ ++ priv->s_data->numlanes = config->lane_count; ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &avt_supported_lane_counts); ++ if (ret < 0) { ++ avt_err(sd, " BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R: i2c read failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ if(!(test_bit(priv->s_data->numlanes - 1, (const long *)(&avt_supported_lane_counts)))) { ++ avt_err(sd, " requested number of lanes (%u) not supported by this camera!\n", ++ priv->s_data->numlanes); ++ ret = -1; ++ break; ++ } ++ ret = avt_reg_write(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_LANE_COUNT_8RW, ++ priv->s_data->numlanes); ++ if (ret < 0){ ++ avt_err(sd, " i2c write failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ priv->numlanes = priv->s_data->numlanes; ++ ++ /* Set CSI clock frequency */ ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_LANE_COUNT_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_min_clk); ++ ++ if (ret < 0) { ++ avt_err(sd, " BCRM_CSI2_LANE_COUNT_8RW: i2c read failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_max_clk); ++ ++ if (ret < 0) { ++ avt_err(sd, " BCRM_CSI2_CLOCK_MAX_32R: i2c read failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ ++ if (common_range(avt_min_clk, avt_max_clk, 1, ++ config->csi_clock, config->csi_clock, 1, ++ &common_min_clk, &common_max_clk, &common_inc_clk) ++ == false) { ++ avt_err(sd, " clock value does not fit the supported frequency range!\n"); ++ return -EINVAL; ++ } ++ ++ CLEAR(i2c_reg_address); ++ clk = config->csi_clock; ++ swapbytes(&clk, AV_CAM_DATA_SIZE_32); ++ i2c_reg_address = priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_32RW; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = AV_CAM_DATA_SIZE_32; ++ i2c_reg_buf = (char *) &clk; ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, i2c_reg_address, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ if (ret < 0) { ++ avt_err(sd, " BCRM_CSI2_CLOCK_32RW: i2c write failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ ++ /* Read back CSI clock frequency */ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &priv->csi_clk_freq); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_CSI2_CLOCK_32RW: i2c read failed (%d)\n", ret); ++ ret = -1; ++ break; ++ } ++ ret = 0; ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++ ++/* --------------- VIDEO OPS --------------- */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++int avt_csi2_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++#else ++int avt_csi2_get_mbus_config(struct v4l2_subdev *sd, ++ unsigned int pad, ++ struct v4l2_mbus_config *cfg) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) ++ cfg->type = V4L2_MBUS_CSI2; ++#else ++ cfg->type = V4L2_MBUS_CSI2_DPHY; ++#endif ++ ++ cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ cfg->flags |= V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0; /* XXX wierd */ ++ ++ return 0; ++} ++ ++static int avt_set_param(struct i2c_client *client, ++ uint32_t id, uint32_t value) ++{ ++ struct avt_ctrl ct; ++ ++ CLEAR(ct); ++ ct.id = id; ++ ct.value0 = value; ++ ++ return avt_ctrl_send(client, &ct); ++} ++ ++static int avt_get_param(struct i2c_client *client, ++ uint32_t id, uint32_t *value) ++{ ++ struct avt_ctrl ct; ++ int ret; ++ ++ CLEAR(ct); ++ ct.id = id; ++ ret = avt_ctrl_send(client, &ct); ++ ++ if (ret < 0) ++ return ret; ++ ++ *value = ct.value0; ++ ++ return 0; ++} ++ ++static int auto_value_update_thread(void *param) ++{ ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)param; ++ struct i2c_client *client = priv->client; ++ struct v4l2_subdev *sd = priv->subdev; ++ struct v4l2_ctrl_handler *handler = sd->ctrl_handler; ++ struct v4l2_ctrl *exposure_ctrl,*exposure_auto_ctrl,*gain_ctrl,*gain_auto_ctrl,*awb_ctrl,*red_balance_ctrl, ++ *blue_balance_ctrl; ++ int ret; ++ ++ exposure_ctrl = v4l2_ctrl_find(handler,V4L2_CID_EXPOSURE); ++ exposure_auto_ctrl = v4l2_ctrl_find(handler,V4L2_CID_EXPOSURE_AUTO); ++ gain_ctrl = v4l2_ctrl_find(handler,V4L2_CID_GAIN); ++ gain_auto_ctrl = v4l2_ctrl_find(handler,V4L2_CID_AUTOGAIN); ++ awb_ctrl = v4l2_ctrl_find(handler,V4L2_CID_AUTO_WHITE_BALANCE); ++ red_balance_ctrl = v4l2_ctrl_find(handler,V4L2_CID_RED_BALANCE); ++ blue_balance_ctrl = v4l2_ctrl_find(handler,V4L2_CID_BLUE_BALANCE); ++ ++ ++ while (!kthread_should_stop()) ++ { ++ atomic_set(&priv->force_value_update, 0); ++ ++ if (exposure_auto_ctrl != NULL && exposure_ctrl != NULL) ++ { ++ int exposure_auto = v4l2_ctrl_g_ctrl(exposure_auto_ctrl); ++ ++ if (V4L2_EXPOSURE_AUTO == exposure_auto) ++ { ++ uint64_t exposure_time; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &exposure_time); ++ ++ if (ret < 0) ++ { ++ avt_warn(sd,"Automatic exposure time update failed"); ++ } ++ ++ avt_dbg(sd,"Exposure auto update"); ++ ++ priv->ignore_control_write = true; ++ v4l2_ctrl_s_ctrl_int64(exposure_ctrl,exposure_time); ++ priv->ignore_control_write = false; ++ } ++ } ++ ++ if (gain_auto_ctrl != NULL && gain_ctrl != NULL) ++ { ++ int auto_gain_enabled = v4l2_ctrl_g_ctrl(gain_auto_ctrl); ++ ++ if (auto_gain_enabled) ++ { ++ uint64_t gain; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &gain); ++ ++ if (ret < 0) ++ { ++ avt_warn(sd,"Automatic gain update failed"); ++ } ++ ++ avt_dbg(sd,"Gain auto update"); ++ ++ priv->ignore_control_write = true; ++ v4l2_ctrl_s_ctrl_int64(gain_ctrl,gain); ++ priv->ignore_control_write = false; ++ } ++ } ++ ++ if (awb_ctrl != NULL) ++ { ++ int awb_enabled = v4l2_ctrl_g_ctrl(awb_ctrl); ++ ++ if (awb_enabled) ++ { ++ if (red_balance_ctrl != NULL) ++ { ++ uint64_t red_balance; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_RED_BALANCE_RATIO_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &red_balance); ++ ++ if (ret < 0) ++ { ++ avt_warn(sd,"Red balance update failed"); ++ } ++ ++ avt_dbg(sd,"Red balance update"); ++ ++ priv->ignore_control_write = true; ++ v4l2_ctrl_s_ctrl_int64(red_balance_ctrl,red_balance); ++ priv->ignore_control_write = false; ++ } ++ ++ if (blue_balance_ctrl != NULL) ++ { ++ uint64_t blue_balance; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLUE_BALANCE_RATIO_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &blue_balance); ++ ++ if (ret < 0) ++ { ++ avt_warn(sd,"Blue balance update failed"); ++ } ++ ++ avt_dbg(sd,"Blue balance update"); ++ ++ priv->ignore_control_write = true; ++ v4l2_ctrl_s_ctrl_int64(blue_balance_ctrl,blue_balance); ++ priv->ignore_control_write = false; ++ } ++ } ++ } ++ ++ if (0 == priv->value_update_interval) ++ { ++ wait_event_interruptible(priv->value_update_wq, kthread_should_stop() || atomic_read(&priv->force_value_update)); ++ } ++ else ++ { ++ wait_event_interruptible_timeout(priv->value_update_wq, kthread_should_stop() || atomic_read(&priv->force_value_update), msecs_to_jiffies(priv->value_update_interval)); ++ } ++ } ++ ++ return 0; ++} ++ ++static bool avt_mode_reinit_required(struct avt_csi2_priv *priv) ++{ ++ if (priv->csi_fixed_lanes > 0) { ++ return priv->numlanes != priv->csi_fixed_lanes; ++ } ++ return priv->numlanes != priv->s_data->numlanes; ++} ++ ++static void avt_disable_stream_ctrls(struct avt_csi2_priv *priv,bool disabled) ++{ ++ int i; ++ ++ for (i = 0; i < AVT_MAX_CTRLS;i++) ++ { ++ if (priv->ctrls[i] != NULL) ++ { ++ if (priv->ctrls[i]->priv != NULL) ++ { ++ struct avt_ctrl_mapping *mapping = priv->ctrls[i]->priv; ++ ++ if (mapping->disabled_while_streaming) ++ { ++ v4l2_ctrl_grab(priv->ctrls[i],disabled); ++ } ++ } ++ } ++ } ++} ++ ++/* Start/Stop streaming from the device */ ++static int avt_csi2_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct v4l2_ctrl *trigger_sw_ctrl; ++ int ret = 0; ++ ++ trigger_sw_ctrl = v4l2_ctrl_find(sd->ctrl_handler,V4L2_CID_TRIGGER_SOFTWARE); ++ ++ ++ if (enable) { ++ if (avt_mode_reinit_required(priv)) { ++ ret = avt_init_mode(sd); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (priv->mode == AVT_BCRM_MODE) { ++ if (MEDIA_BUS_FMT_CUSTOM == priv->mbus_fmt_code) ++ return -EINVAL; ++ ++ if (trigger_sw_ctrl) ++ v4l2_ctrl_activate(trigger_sw_ctrl,true); ++ ++ avt_disable_stream_ctrls(priv,true); ++ ++ ret = avt_set_param(client, V4L2_AV_CSI2_STREAMON_W, 1); ++ ++ priv->value_update_thread = kthread_run(auto_value_update_thread, priv, "avt_csi2"); ++ } ++ } else if (priv->mode == AVT_BCRM_MODE) { ++ ret = avt_set_param(client, V4L2_AV_CSI2_STREAMOFF_W, 1); ++ if (priv->trig_thread) ++ kthread_stop(priv->trig_thread); ++ ++ if (priv->value_update_thread) ++ { ++ wake_up_all(&priv->value_update_wq); ++ kthread_stop(priv->value_update_thread); ++ priv->value_update_thread = NULL; ++ } ++ ++ if (trigger_sw_ctrl) ++ v4l2_ctrl_activate(trigger_sw_ctrl,false); ++ ++ avt_disable_stream_ctrls(priv,false); ++ } ++ ++ if (ret < 0) ++ return ret; ++ ++ priv->stream_on = enable ? true : false; ++ ++ return 0; ++} ++ ++static int avt_csi2_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ int ret = 0; ++ uint32_t val = 0; ++ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ ret = avt_get_param(client, V4L2_AV_CSI2_WIDTH_R, &val); ++ if (ret < 0) ++ return ret; ++ format->format.width = val; ++ ++ ret = avt_get_param(client, V4L2_AV_CSI2_HEIGHT_R, &val); ++ if (ret < 0) ++ return ret; ++ format->format.height = val; ++ ++ ret = avt_get_param(client, V4L2_AV_CSI2_PIXELFORMAT_R, &val); ++ if (ret < 0) ++ return ret; ++ format->format.code = val; ++ ++ /* Hardcoded default format */ ++ format->format.field = V4L2_FIELD_NONE; ++ format->format.colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return 0; ++} ++ ++static int avt_csi2_find_binning_idx(struct avt_csi2_priv *priv,int width, int height, u32 mbus_fmt_code) ++{ ++ int i; ++ ++ for (i = 0; i < priv->available_binnings_cnt; i++) { ++ int aligned_width = avt_align_width(priv->subdev,priv->available_binnings[i].width, ++ priv->available_binnings[i].width,mbus_fmt_code); ++ if (aligned_width == width && priv->available_binnings[i].height == height) { ++ return i; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int avt_csi2_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) { ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ int ret = 0; ++ ++ ret = avt_csi2_find_binning_idx(priv, format->format.width, format->format.height,format->format.code); ++ ++ if (ret < 0) { ++ format->format.width = priv->frmp.r.width; ++ format->format.height = priv->frmp.r.height; ++ } ++ ++ return 0; ++} ++ ++static int avt_csi2_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct v4l2_subdev_selection sel; ++ int ret; ++ ++ format->format.colorspace = V4L2_COLORSPACE_SRGB; ++ format->format.field = V4L2_FIELD_NONE; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) { ++ if (priv->mode == AVT_BCRM_MODE) ++ return avt_csi2_try_fmt(sd, cfg, format); ++ else ++ return 0; ++ } ++ ++ ++ priv->mbus_fmt_code = format->format.code; ++ ++ if (priv->mode != AVT_BCRM_MODE) ++ return 0; ++ ++ // changing the resolution is not allowed with VIDIOC_S_FMT ++ if (format->format.width != priv->frmp.r.width || ++ format->format.height != priv->frmp.r.height) ++ { ++ ret = avt_csi2_find_binning_idx(priv, format->format.width, format->format.height,format->format.code); ++ if (ret < 0) { ++ format->format.width = priv->frmp.r.width; ++ format->format.height = priv->frmp.r.height; ++ } ++ else { ++ int idx = ret; ++ struct avt_binning_config *binning_config = &priv->available_binnings[idx]; ++ uint8_t setting = binning_config->setting; ++ ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_SETTING_8RW, ++ AV_CAM_REG_SIZE,AV_CAM_DATA_SIZE_8, &setting); ++ if (ret < 0) { ++ avt_err(sd, "i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ priv->cur_binning_config = idx; ++ priv->frmp.r.width = binning_config->width; ++ priv->frmp.r.height = binning_config->height; ++ } ++ ++ } ++ else ++ { ++ //Update width and height with values of current binning setting to ensure the width and height are correct ++ //after alignment. ++ struct avt_binning_config *binning_config = &priv->available_binnings[priv->cur_binning_config]; ++ ++ priv->frmp.r.width = binning_config->width; ++ priv->frmp.r.height = binning_config->height; ++ } ++ ++ sel.target = V4L2_SEL_TGT_CROP; ++ sel.r = priv->frmp.r; ++ ++ /* Save format to private data only if ++ * set_param succeded ++ */ ++ ret = avt_set_param(client, V4L2_AV_CSI2_PIXELFORMAT_W, ++ format->format.code); ++ if(ret < 0) ++ return ret; ++ ++ avt_set_selection(sd, NULL, &sel); ++ ++ if (priv->stride_align_enabled) ++ set_channel_stride_align_for_format(sd, format->format.code); ++ else ++ set_channel_stride_align(sd, 1); ++ ++ format->format.width = priv->frmp.r.width; ++ return 0; ++} ++ ++static uint16_t avt_mbus_formats[] = { ++ /* RAW 8 */ ++ MEDIA_BUS_FMT_Y8_1X8, ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ ++ /* RAW10 */ ++ MEDIA_BUS_FMT_Y10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ ++ /* RAW12 */ ++ MEDIA_BUS_FMT_Y12_1X12, ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ /* RGB565 */ ++ MEDIA_BUS_FMT_RGB565_1X16, ++ /* RGB888 */ ++ MEDIA_BUS_FMT_RGB888_1X24, ++ MEDIA_BUS_FMT_BGR888_1X24, ++ /* YUV422 */ ++ MEDIA_BUS_FMT_VYUY8_2X8, ++ /* CUSTOM TP31 */ ++ //MEDIA_BUS_FMT_CUSTOM, ++}; ++ ++/* These formats are hidden from VIDIOC_ENUM_FMT ioctl */ ++static uint16_t avt_hidden_mbus_formats[] = { ++/*KHO ++ MEDIA_BUS_FMT_Y10_1X10, ++ MEDIA_BUS_FMT_Y12_1X12, ++ ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ */ ++}; ++ ++//static int32_t avt_avail_formats[ARRAY_SIZE(avt_mbus_formats)]; ++ ++static bool avt_mbus_fmt_is_hidden(uint16_t mbus_fmt) ++{ ++ int i; ++ for(i = 0; i < ARRAY_SIZE(avt_hidden_mbus_formats); i++) ++ if(avt_hidden_mbus_formats[i] == mbus_fmt) ++ return true; ++ ++ return false; ++} ++ ++static void avt_init_avail_formats(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ ++ int fmt_iter = 0; ++ int i; ++ int32_t *avail_fmts; ++ ++ avail_fmts = kmalloc(sizeof(int32_t)*ARRAY_SIZE(avt_mbus_formats), GFP_KERNEL); ++ ++ for(i = 0; i < ARRAY_SIZE(avt_mbus_formats); i++){ ++ if(avt_check_fmt_available(client, avt_mbus_formats[i]) ++ && !avt_mbus_fmt_is_hidden(avt_mbus_formats[i])) { ++ avail_fmts[fmt_iter++] = avt_mbus_formats[i]; ++ } ++ } ++ ++ avail_fmts[fmt_iter] = -EINVAL; ++ ++ priv->available_fmts = avail_fmts; ++ priv->available_fmts_cnt = fmt_iter+1;; ++} ++ ++static int avt_csi2_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ ++ avt_dbg(sd, "()\n"); ++ if (code->index >= priv->available_fmts_cnt) ++ return -EINVAL; ++ ++ code->code = priv->available_fmts[code->index]; ++ if (code->code == -EINVAL) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int avt_csi2_enum_framesizes(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ bool format_present = false; ++ int i; ++ ++ avt_dbg(sd, "()\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(avt_mbus_formats); i++) ++ if (avt_mbus_formats[i] == fse->code) ++ format_present = true; ++ ++ if (fse->index >= priv->available_binnings_cnt || format_present == false) ++ return -EINVAL; ++ ++ fse->min_width = fse->max_width = avt_align_width(sd,priv->available_binnings[fse->index].width, ++ priv->available_binnings[fse->index].width,fse->code); ++ fse->min_height = fse->max_height = priv->available_binnings[fse->index].height; ++ ++ ++ ++ return 0; ++} ++ ++ ++static int read_framerate(struct v4l2_subdev *sd, struct v4l2_fract *tpf) { ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ u8 framerate_enable; ++ u64 framerate; ++ ++ int ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &framerate_enable); ++ if (ret < 0) { ++ dev_err(&client->dev, "read framerate_enable failed\n"); ++ return ret; ++ } ++ ++ if(framerate_enable == 1) { ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_ACQUISITION_FRAME_RATE_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &framerate); ++ if (ret < 0) { ++ dev_err(&client->dev, "read frameinterval failed\n"); ++ return ret; ++ } ++ ++ /* Translate frequency to timeperframe ++ * by inverting the fraction ++ */ ++ tpf->numerator = FRAQ_NUM; ++ tpf->denominator = (framerate * FRAQ_NUM) / UHZ_TO_HZ; ++ } else { ++ tpf->numerator = FRAQ_NUM; ++ tpf->denominator = 0; ++ } ++ ++ return 0; ++} ++ ++ ++static int avt_csi2_enum_frameintervals(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_interval_enum *fie) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret; ++ u64 min_framerate,max_framerate,framerate_step,tmp; ++ ++ if(fie->index > 0) ++ return -EINVAL; ++ ++ if (avt_csi2_find_binning_idx(priv,fie->width,fie->height,fie->code) < 0) ++ { ++ return -EINVAL; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_ACQUISITION_FRAME_RATE_INC_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &framerate_step); ++ if (ret < 0) { ++ dev_err(&client->dev, "read frameinterval inc failed\n"); ++ return ret; ++ } ++ ++ if (framerate_step) { ++ fie->type = V4L2_SUBDEV_FRMIVAL_TYPE_STEPWISE; ++ fie->step_interval.numerator = FRAQ_NUM; ++ fie->step_interval.denominator = (framerate_step * FRAQ_NUM) / UHZ_TO_HZ; ++ } ++ else { ++ fie->type = V4L2_SUBDEV_FRMIVAL_TYPE_CONTINUOUS; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_ACQUISITION_FRAME_RATE_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &min_framerate); ++ if (ret < 0) { ++ dev_err(&client->dev, "read min frameinterval failed\n"); ++ return ret; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_ACQUISITION_FRAME_RATE_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &max_framerate); ++ if (ret < 0) { ++ dev_err(&client->dev, "read max frameinterval failed\n"); ++ return ret; ++ } ++ ++ tmp = min_framerate * FRAQ_NUM; ++ ++ //Check if result of division will be between 0 and 1, if it is so increase numerator ++ if (tmp < UHZ_TO_HZ) { ++ fie->max_interval.numerator = UHZ_TO_HZ; ++ fie->max_interval.denominator = min_framerate; ++ } ++ else { ++ fie->max_interval.numerator = FRAQ_NUM; ++ fie->max_interval.denominator = tmp / UHZ_TO_HZ; ++ } ++ ++ tmp = max_framerate * FRAQ_NUM; ++ ++ //Check if result of division will be between 0 and 1, if it is so increase numerator ++ if (tmp < UHZ_TO_HZ) { ++ fie->max_interval.numerator = UHZ_TO_HZ; ++ fie->max_interval.denominator = max_framerate; ++ } ++ else { ++ fie->interval.numerator = FRAQ_NUM; ++ fie->interval.denominator = tmp / UHZ_TO_HZ; ++ } ++ ++ ++ return 0; ++} ++ ++ ++static __s32 convert_s_ctrl(__s32 val, __s32 min, __s32 max, __s32 step) ++{ ++ int32_t valuedown = 0, valueup = 0; ++ ++ if (val > max) ++ val = max; ++ ++ else if (val < min) ++ val = min; ++ ++ valuedown = val - ((val - min) % step); ++ valueup = valuedown + step; ++ ++ if (val >= 0) { ++ if (((valueup - val) <= (val - valuedown)) && (valueup <= max)) ++ val = valueup; ++ else ++ val = valuedown; ++ } else { ++ if (((valueup - val) < (val - valuedown)) && (valueup <= max)) ++ val = valueup; ++ else ++ val = valuedown; ++ } ++ ++ return val; ++} ++ ++static __s64 convert_s_ctrl64(struct v4l2_query_ext_ctrl* qctrl_ext, __s64 val) ++{ ++ __s64 valuedown = 0, valueup = 0; ++ __s64 step=(__s64)qctrl_ext->step; ++ if (val > qctrl_ext->maximum) ++ val = qctrl_ext->maximum; ++ ++ else if (val < qctrl_ext->minimum) ++ val = qctrl_ext->minimum; ++ ++ valuedown = val - ((val - qctrl_ext->minimum) % step); ++ valueup = valuedown + step; ++ if (val >= 0) { ++ if (((valueup - val) <= (val - valuedown)) && (valueup <= qctrl_ext->maximum)) ++ val = valueup; ++ else ++ val = valuedown; ++ } else { ++ if (((valueup - val) < (val - valuedown)) && (valueup <= qctrl_ext->maximum)) ++ val = valueup; ++ else ++ val = valuedown; ++ } ++ return val; ++} ++ ++ ++static int read_feature_register(struct v4l2_subdev *sd, union bcrm_feature_reg *features) { ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_FEATURE_INQUIRY_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) features); ++} ++ ++ ++static int ioctl_queryctrl(struct v4l2_subdev *sd, ++ struct v4l2_queryctrl *qctrl) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ u32 value = 0; ++ int ret; ++ union bcrm_feature_reg feature_inquiry_reg; ++ ++ avt_dbg(sd, "\n"); ++ ++ ret = read_feature_register(sd, &feature_inquiry_reg); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_FEATURE_INQUIRY_64R: i2c read failed (%d)\n", ret); ++ } ++ ++ switch (qctrl->id) { ++ ++ /* BLACK LEVEL is deprecated and thus we use Brightness */ ++ case V4L2_CID_BRIGHTNESS: ++ avt_dbg(sd, "case V4L2_CID_BRIGHTNESS\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.black_level_avail) { ++ avt_info(sd, "control 'Brightness' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the current Black Level value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLACK_LEVEL_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLACK_LEVEL_32RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ /* reading the Minimum Black Level */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLACK_LEVEL_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLACK_LEVEL_MIN_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = value; ++ ++ /* reading the Maximum Black Level */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLACK_LEVEL_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLACK_LEVEL_MAX_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = value; ++ ++ /* reading the Black Level step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLACK_LEVEL_INC_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLACK_LEVEL_INC_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Brightness: min > max! (%d > %d)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ if (qctrl->step <= 0) { ++ avt_err(sd, "Brightness: non-positive step value (%d)!\n", ++ qctrl->step); ++ return -EINVAL; ++ } ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(qctrl->name, "Brightness"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_AUTO\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_auto) { ++ avt_info(sd, "control 'Exposure Auto' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the current exposure auto value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_AUTO_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_AUTO_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ if (value == 2) ++ /* continous mode, Refer BCRM doc */ ++ qctrl->default_value = V4L2_EXPOSURE_AUTO; ++ else ++ /* false (OFF) */ ++ qctrl->default_value = V4L2_EXPOSURE_MANUAL; ++ ++ qctrl->minimum = 0; ++ qctrl->step = 0; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_MENU; ++ ++ strcpy(qctrl->name, "Exposure Auto"); ++ break; ++ ++ case V4L2_CID_AUTOGAIN: ++ avt_dbg(sd, "case V4L2_CID_AUTOGAIN\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.gain_auto) { ++ avt_info(sd, "control 'Auto Gain' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Auto Gain value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_AUTO_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_AUTO_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ if (value == 2) ++ /* true (ON) for continous mode, Refer BCRM doc */ ++ qctrl->default_value = true; ++ else ++ /* false (OFF) */ ++ qctrl->default_value = false; ++ ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "Auto Gain"); ++ break; ++ ++ case V4L2_CID_HFLIP: ++ avt_dbg(sd, "case V4L2_CID_HFLIP\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.reverse_x_avail) { ++ avt_info(sd, "control 'Reversing X (Horizantal Flip)' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Reverse X value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_IMG_REVERSE_X_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_IMG_REVERSE_X_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ qctrl->minimum = 0; ++ qctrl->step = qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ strcpy(qctrl->name, "Reverse X"); ++ ++ break; ++ ++ case V4L2_CID_VFLIP: ++ avt_dbg(sd, "case V4L2_CID_VFLIP\n"); ++ if (!feature_inquiry_reg.feature_inq.reverse_y_avail) { ++ avt_info(sd, "control 'Reversing Y (Vertical Flip)' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Reverse Y value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_IMG_REVERSE_Y_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_IMG_REVERSE_Y_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ qctrl->minimum = 0; ++ qctrl->step = qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ strcpy(qctrl->name, "Reverse Y"); ++ ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ avt_dbg(sd, "case V4L2_CID_CONTRAST\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.contrast_avail) { ++ avt_info(sd, "control 'Contrast' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Contrast value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_CONTRAST_VALUE_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_CONTRAST_VALUE_32RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ /* reading the Minimum Contrast */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_CONTRAST_VALUE_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_CONTRAST_VALUE_MIN_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = value; ++ ++ /* reading the Maximum Contrast */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_CONTRAST_VALUE_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_CONTRAST_VALUE_MAX_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = value; ++ ++ /* reading the Contrast step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_CONTRAST_VALUE_INC_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_CONTRAST_VALUE_INC_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value; ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Contrast: min > max! (%d > %d)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ if (qctrl->step <= 0) { ++ avt_err(sd, "Contrast: non-positive step value (%d)!\n", ++ qctrl->step); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Contrast"); ++ break; ++ ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ avt_dbg(sd, "case V4L2_CID_AUTO_WHITE_BALANCE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.white_balance_auto_avail) { ++ avt_info(sd, "control 'White balance Auto' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the White balance auto value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_WHITE_BALANCE_AUTO_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_WHITE_BALANCE_AUTO_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ if (value == 2) ++ /* true (ON) */ ++ qctrl->default_value = true; ++ else ++ /* false (OFF) */ ++ qctrl->default_value = false; ++ ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "White Balance Auto"); ++ break; ++ ++ case V4L2_CID_DO_WHITE_BALANCE: ++ avt_dbg(sd, "case V4L2_CID_DO_WHITE_BALANCE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.white_balance_avail) { ++ avt_info(sd, "control 'White balance' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the White balance auto reg */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_WHITE_BALANCE_AUTO_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_WHITE_BALANCE_AUTO_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = 0; ++ qctrl->minimum = 0; ++ qctrl->step = 0; ++ qctrl->maximum = 0; ++ qctrl->type = V4L2_CTRL_TYPE_BUTTON; ++ ++ strcpy(qctrl->name, "White Balance"); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ avt_dbg(sd, "case V4L2_CID_SATURATION\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.saturation_avail) { ++ avt_info(sd, "control 'Saturation' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Saturation value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SATURATION_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SATURATION_32RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ /* reading the Minimum Saturation */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SATURATION_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SATURATION_MIN_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = value; ++ ++ /* reading the Maximum Saturation */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SATURATION_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SATURATION_MAX_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = value; ++ ++ /* reading the Saturation step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SATURATION_INC_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SATURATION_INC_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Saturation: min > max! (%d > %d)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ if (qctrl->step <= 0) { ++ avt_err(sd, "Saturation: non-positive step value (%d)!\n", ++ qctrl->step); ++ return -EINVAL; ++ } ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(qctrl->name, "Saturation"); ++ break; ++ ++ case V4L2_CID_HUE: ++ avt_dbg(sd, "case V4L2_CID_HUE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.hue_avail) { ++ avt_info(sd, "control 'Hue' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Hue value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_HUE_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_HUE_32RW: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ /* reading the Minimum HUE */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_HUE_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_HUE_MIN_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = value; ++ ++ /* reading the Maximum HUE */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_HUE_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_HUE_MAX_32R: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ qctrl->maximum = value; ++ ++ /* reading the HUE step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_HUE_INC_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_HUE_INC_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Hue: min > max! (%d > %d)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ if (qctrl->step <= 0) { ++ avt_err(sd, "Hue: non-positive step value (%d)!\n", ++ qctrl->step); ++ return -EINVAL; ++ } ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(qctrl->name, "Hue"); ++ break; ++ ++ case V4L2_CID_SHARPNESS: ++ avt_dbg(sd, "case V4L2_CID_SHARPNESS\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.sharpness_avail) { ++ avt_info(sd, "control 'Sharpness' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Sharpness value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SHARPNESS_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SHARPNESS_32RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ ++ /* reading the Minimum sharpness */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SHARPNESS_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SHARPNESS_MIN_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = value; ++ ++ /* reading the Maximum sharpness */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SHARPNESS_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SHARPNESS_MAX_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = value; ++ ++ /* reading the sharpness step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_SHARPNESS_INC_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_SHARPNESS_INC_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Sharpness: min > max! (%d > %d)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ if (qctrl->step <= 0) { ++ avt_err(sd, "Sharpness: non-positive step value (%d)!\n", ++ qctrl->step); ++ return -EINVAL; ++ } ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(qctrl->name, "Sharpness"); ++ break; ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE: ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the exposure active line mode value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ if (value == 1) ++ /* true (ON) */ ++ qctrl->default_value = true; ++ else ++ /* false (OFF) */ ++ qctrl->default_value = false; ++ ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "Exposure Active Line Mode"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR: ++ { ++ char selector; ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the exposure active line mode value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &selector); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = selector; ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ ++ strcpy(qctrl->name, "Exposure Active Line Selector"); ++ break; ++ } ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_INVERT: ++ { ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_INVERT\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ qctrl->default_value = false; ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "Exposure Active Invert"); ++ break; ++ } ++ ++ case V4L2_CID_TRIGGER_MODE: ++ { ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_MODE\n"); ++ qctrl->default_value = priv->trigger_mode; ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "Trigger Mode"); ++ break; ++ } ++ ++ case V4L2_CID_TRIGGER_ACTIVATION: ++ { ++ u8 trigger_activation; ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_ACTIVATION\n"); ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ &trigger_activation); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ qctrl->default_value = trigger_activation; ++ qctrl->minimum = V4L2_TRIGGER_ACTIVATION_RISING_EDGE; ++ qctrl->step = 0; ++ qctrl->maximum = V4L2_TRIGGER_ACTIVATION_LEVEL_LOW; ++ ++ qctrl->type = V4L2_CTRL_TYPE_MENU; ++ ++ strcpy(qctrl->name, "Trigger Activation"); ++ break; ++ } ++ ++ case V4L2_CID_TRIGGER_SOURCE: ++ { ++ u8 trigger_source_reg; ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOURCE\n"); ++ ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_FRAME_START_TRIGGER_SOURCE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &trigger_source_reg); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ if(trigger_source_reg > V4L2_TRIGGER_SOURCE_SOFTWARE) { ++ avt_err(sd, " Unknown trigger mode (%d) returned from camera. Driver outdated?", trigger_source_reg); ++ return -1; ++ } ++ qctrl->default_value = trigger_source_reg; ++ qctrl->minimum = V4L2_TRIGGER_SOURCE_LINE0; ++ qctrl->step = 0; ++ qctrl->maximum = V4L2_TRIGGER_SOURCE_SOFTWARE; ++ ++ qctrl->type = V4L2_CTRL_TYPE_MENU; ++ strcpy(qctrl->name, "Trigger Source"); ++ break; ++ } ++ ++ case V4L2_CID_TRIGGER_SOFTWARE: ++ { ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOFTWARE\n"); ++ ++ qctrl->default_value = 0; ++ qctrl->minimum = 0; ++ qctrl->step = 0; ++ qctrl->maximum = 0; ++ qctrl->type = V4L2_CTRL_TYPE_BUTTON; ++ qctrl->flags = V4L2_CTRL_FLAG_INACTIVE; ++ ++ strcpy(qctrl->name, "Trigger software"); ++ break; ++ } ++ ++ case V4L2_CID_EXPOSURE_ABSOLUTE: ++ { ++ struct v4l2_query_ext_ctrl query_exposure = {.id = V4L2_CID_EXPOSURE}; ++ ioctl_queryctrl64(sd, &query_exposure); ++ qctrl->default_value = (int32_t)(query_exposure.default_value / EXP_ABS); ++ qctrl->minimum = (int32_t)(query_exposure.minimum / EXP_ABS); ++ qctrl->maximum = (int32_t)(query_exposure.maximum / EXP_ABS); ++ qctrl->step = max((int32_t)(query_exposure.step / EXP_ABS), 1); ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(qctrl->name, "Exposure Absolute"); ++ break; ++ } ++ ++ case V4L2_CID_DEVICE_TEMPERATURE: ++ avt_dbg(sd, "case V4L2_CID_DEVICE_TEMPERATURE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.device_temperature_avail) { ++ avt_info(sd, "control 'Device Temperature' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the current Device Temperature value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_DEVICE_TEMPERATURE_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_DEVICE_TEMPERATURE_32R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = value; ++ qctrl->minimum = -1000; ++ qctrl->maximum = 2000; ++ qctrl->step = 1; ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ qctrl->flags = V4L2_CTRL_FLAG_VOLATILE; ++ strcpy(qctrl->name, "Device Temperature"); ++ break; ++ case V4L2_CID_BINNING_MODE: ++ { ++ u8 binning_mode; ++ avt_dbg(sd, "case V4L2_CID_BINNING_MODE\n"); ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_MODE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ &binning_mode); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ qctrl->default_value = binning_mode; ++ qctrl->minimum = DIGITAL_BINNING_MODE_AVG; ++ qctrl->step = 0; ++ qctrl->maximum = DIGITAL_BINNING_MODE_SUM; ++ ++ qctrl->type = V4L2_CTRL_TYPE_MENU; ++ ++ strcpy(qctrl->name, "Binning Mode"); ++ break; ++ } ++ ++ ++ default: ++ avt_info(sd, "case default or not supported qctrl->id 0x%x\n", ++ qctrl->id); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_dbg(sd, "ret = %d\n", ret); ++ ++ return 0; ++} ++ ++static int ioctl_queryctrl64(struct v4l2_subdev *sd, ++ struct v4l2_query_ext_ctrl *qctrl) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ u64 value64 = 0; ++ int ret; ++ union bcrm_feature_reg feature_inquiry_reg; ++ ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER64; /* all types are V4L2_CTRL_TYPE_INTEGER64*/ ++ qctrl->elem_size=8; ++ qctrl->elems=1; ++ ++ avt_dbg(sd, "\n"); ++ ++ /* reading the Feature inquiry register */ ++ ret = read_feature_register(sd, &feature_inquiry_reg); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_FEATURE_INQUIRY_64R: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ switch (qctrl->id) { ++ ++ case V4L2_CID_EXPOSURE: ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE\n"); ++ ++ /* reading the Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = (s64)value64; ++ ++ /* reading the Minimum Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->minimum = (s64)value64; ++ ++ /* reading the Maximum Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = (s64)value64; ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Exposure: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Exposure"); ++ break; ++ ++ case V4L2_CID_GAIN: ++ ++ avt_dbg(sd, "case V4L2_CID_GAIN\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.gain_avail) { ++ avt_info(sd, "control 'Gain' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Gain value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_64RW: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Minimum Gain value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = (__s64) value64; ++ ++ /* reading the Maximum Gain value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = (__s64) value64; ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Gain: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Gain"); ++ break; ++ ++ case V4L2_CID_GAMMA: ++ avt_dbg(sd, "case V4L2_CID_GAMMA\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.gamma_avail) { ++ avt_info(sd, "control 'Gamma' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Gamma value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAMMA_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAMMA_64RW: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Minimum Gamma */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAMMA_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAMMA_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = (__s64) value64; ++ ++ /* reading the Maximum Gamma */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAMMA_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAMMA_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = (__s64) value64; ++ ++ /* reading the Gamma step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAMMA_INC_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAMMA_INC_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value64; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Gamma: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Gamma"); ++ break; ++ ++ case V4L2_CID_BLUE_BALANCE: ++ avt_dbg(sd, "case V4L2_CID_BLUE_BALANCE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.white_balance_avail) { ++ avt_info(sd, "control 'Blue balance' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Blue balance value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLUE_BALANCE_RATIO_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLUE_BALANCE_RATIO_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Minimum Blue balance */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLUE_BALANCE_RATIO_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLUE_BALANCE_RATIO_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = (__s64) value64; ++ ++ /* reading the Maximum Blue balance */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_BLUE_BALANCE_RATIO_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLUE_BALANCE_RATIO_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = (__s64) value64; ++ ++ /* reading the Blue balance step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_BLUE_BALANCE_RATIO_INC_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_BLUE_BALANCE_RATIO_INC_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value64; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Blue Balance: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Blue Balance"); ++ break; ++ ++ case V4L2_CID_RED_BALANCE: ++ avt_dbg(sd, "case V4L2_CID_RED_BALANCE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.white_balance_avail) { ++ avt_info(sd, "control 'Red balance' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the Red balance value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_RED_BALANCE_RATIO_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_RED_BALANCE_RATIO_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Minimum Red balance */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_RED_BALANCE_RATIO_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_RED_BALANCE_RATIO_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->minimum = (__s64) value64; ++ ++ /* reading the Maximum Red balance */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_RED_BALANCE_RATIO_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_RED_BALANCE_RATIO_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->maximum = (__s64) value64; ++ ++ /* reading the Red balance step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_RED_BALANCE_RATIO_INC_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_RED_BALANCE_RATIO_INC_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->step = value64; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Red Balance: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ ++ strcpy(qctrl->name, "Red Balance"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MIN: ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_AUTO_MIN\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_auto) { ++ avt_info(sd, "control 'Exposure Auto Min' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* get min and max times for Exposure they are also valid for ++ Auto Exposure min time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MIN_64R: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ qctrl->minimum = (__s64) value64; ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Maximum Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MAX_64R: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ qctrl->maximum = ((__s64) value64); ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Exposure auto: min > max! (%lld > %lld)\n", qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ strcpy(qctrl->name, "Exposure auto min"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MAX: ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_AUTO_MAX\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_auto) { ++ avt_info(sd, "control 'Exposure Auto Max' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* get min and max times for Exposure they are also valid for ++ Auto Exposure max time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->maximum = (__s64) value64; ++ qctrl->default_value = value64; ++ ++ /* reading the Minimum Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_TIME_MIN_64R: i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ qctrl->minimum = (__s64) value64; ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Exposure auto: min > max! (%lld > %lld)\n", qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ strcpy(qctrl->name, "Exposure auto max"); ++ break; ++ case V4L2_CID_GAIN_AUTO_MIN: ++ ++ avt_dbg(sd, "case V4L2_CID_GAIN_AUTO_MIN\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.gain_auto) { ++ avt_info(sd, "control 'Gain Auto Min' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* Auto gain max value should be non-zero */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_AUTO_MAX_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_AUTO_MAX_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->maximum = (__s64) value64; ++ ++ /* reading the Auto gain min val */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_AUTO_MIN_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_AUTO_MIN_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->default_value = (__s64) value64; ++ ++ /* get min and max vals for auto gain they are also valid for ++ Auto gain min val */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->minimum = (__s64) value64; ++ ++ /* reading the Maximum gain time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ /* max auto gain val should be ++ <= (min(Auto gian max value, max gain val)*/ ++ if(qctrl->maximum > ((__s64) value64)) ++ qctrl->maximum = ((__s64) value64); ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ strcpy(qctrl->name, "Gain auto min"); ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Gain auto: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ strcpy(qctrl->name, "Auto gain min"); ++ break; ++ ++ case V4L2_CID_GAIN_AUTO_MAX: ++ ++ avt_dbg(sd, "case V4L2_CID_GAIN_AUTO_MAX\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.gain_auto) { ++ avt_info(sd, "control 'Gain Auto Max' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* Auto gain max value should be non-zero */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_AUTO_MAX_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_AUTO_MAX_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->default_value = (__s64) value64; ++ ++ /* reading the Auto gain min val */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_AUTO_MIN_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_AUTO_MIN_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->minimum = (__s64) value64; ++ ++ /* get min and max vals for auto gain they are also valid for ++ Auto gain max val */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ qctrl->maximum = (__s64) value64; ++ ++ /* reading the Minimum gain time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_GAIN_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_GAIN_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ /* min auto gain val should be ++ >= (max(Auto gain min value, min gain val)*/ ++ if(qctrl->minimum < ((__s64) value64)) ++ qctrl->minimum = ((__s64) value64); ++ ++ /* setting step value fixed to 1 to avoid fighting between camera and driver side adjustments */ ++ qctrl->step = 1; ++ ++ strcpy(qctrl->name, "Gain auto max"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE: ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ if (qctrl->minimum > qctrl->maximum) { ++ avt_err(sd, "Red Balance: min > max! (%lld > %lld)\n", ++ qctrl->minimum, qctrl->maximum); ++ return -EINVAL; ++ } ++ strcpy(qctrl->name, "Auto gain max"); ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR: ++ { ++ char selector; ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ /* reading the exposure active line mode value */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &selector); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl->default_value = selector; ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_INTEGER; ++ ++ strcpy(qctrl->name, "Exposure Active Line Selector"); ++ break; ++ } ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_INVERT: ++ { ++ avt_dbg(sd, "case V4L2_CID_EXPOSURE_ACTIVE_INVERT\n"); ++ ++ if (!feature_inquiry_reg.feature_inq.exposure_active_line_avail) { ++ avt_info(sd, "control 'exposure active line' not supported by firmware\n"); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ qctrl->default_value = false; ++ qctrl->minimum = 0; ++ qctrl->step = 1; ++ qctrl->maximum = 1; ++ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; ++ ++ strcpy(qctrl->name, "Exposure Active Invert"); ++ break; ++ } ++ ++ ++ default: ++ avt_info(sd, "case default or not supported qctrl->id 0x%x\n", ++ qctrl->id); ++ qctrl->flags = V4L2_CTRL_FLAG_DISABLED; ++ return 0; ++ } ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_dbg(sd, "ret = %d\n", ret); ++ ++ return 0; ++} ++ ++static int32_t convert_bcrm_to_v4l2_gctrl(struct bcrm_to_v4l2 *bcrmv4l2, ++ int64_t val64) ++{ ++ int32_t value = 0; ++ int32_t min = 0; ++ int32_t max = 0; ++ int32_t step = 0; ++ int32_t result = 0; ++ int32_t valuedown = 0; ++ int32_t valueup = 0; ++ ++/* 1. convert to double */ ++ step = bcrmv4l2->step_v4l2; ++ max = bcrmv4l2->max_v4l2; ++ min = bcrmv4l2->min_v4l2; ++ value = (int32_t) val64; ++ ++ /* 2. convert the units */ ++/* value *= factor; */ ++ ++ /* 3. Round value to next integer */ ++ ++ if (value < S32_MIN) ++ result = S32_MIN; ++ else if (value > S32_MAX) ++ result = S32_MAX; ++ else ++ result = value; ++ ++ /* 4. Clamp to limits */ ++ if (result > max) ++ result = max; ++ else if (result < min) ++ result = min; ++ ++ /* 5. Use nearest increment */ ++ valuedown = result - ((result - min) % (step)); ++ valueup = valuedown + step; ++ ++ if (result >= 0) { ++ if (((valueup - result) <= (result - valuedown)) ++ && (valueup <= bcrmv4l2->max_bcrm)) ++ result = valueup; ++ else ++ result = valuedown; ++ } else { ++ if (((valueup - result) < (result - valuedown)) ++ && (valueup <= bcrmv4l2->max_bcrm)) ++ result = valueup; ++ else ++ result = valuedown; ++ } ++ ++ return result; ++} ++ ++static __s64 convert_bcrm_to_v4l2_gctrl64(struct v4l2_query_ext_ctrl* qctrl_ext, int64_t val64) ++{ ++ __s64 result = val64; ++ __s64 valuedown = 0; ++ __s64 valueup = 0; ++ __s64 step = (__s64)(qctrl_ext->step); ++ ++ if (result > qctrl_ext->maximum) ++ result = qctrl_ext->maximum; ++ else if (result < qctrl_ext->minimum) ++ result = qctrl_ext->minimum; ++ ++ /* 5. Use nearest increment */ ++ valuedown = result - ((result - qctrl_ext->minimum) % (step)); ++ valueup = valuedown + step; ++ ++ if (result >= 0) { ++ if (((valueup - result) <= (result - valuedown)) ++ && (valueup <= qctrl_ext->maximum)) ++ result = valueup; ++ else ++ result = valuedown; ++ } else { ++ if (((valueup - result) < (result - valuedown)) ++ && (valueup <= qctrl_ext->maximum)) ++ result = valueup; ++ else ++ result = valuedown; ++ } ++ ++ return result; ++} ++ ++static int avt_ioctl_g_ctrl(struct v4l2_subdev *sd, struct v4l2_ext_control *vc) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ unsigned int reg = 0; ++ int length = 0; ++ struct bcrm_to_v4l2 bcrm_v4l2; ++ struct v4l2_queryctrl qctrl; ++ struct v4l2_query_ext_ctrl qctrl_ext; ++ int ret = 0; ++ uint64_t val64 = 0; ++ ++ avt_dbg(sd, "\n"); ++ ++ vc->value = 0; ++ vc->value64=0; ++ ++ switch (vc->id) { ++ ++/* BLACK LEVEL is deprecated and thus we use Brightness */ ++ case V4L2_CID_BRIGHTNESS: ++ avt_dbg(sd, "V4L2_CID_BRIGHTNESS\n"); ++ reg = BCRM_BLACK_LEVEL_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ break; ++ case V4L2_CID_GAMMA: ++ avt_dbg(sd, "V4L2_CID_GAMMA\n"); ++ reg = BCRM_GAIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ case V4L2_CID_CONTRAST: ++ avt_dbg(sd, "V4L2_CID_CONTRAST\n"); ++ reg = BCRM_CONTRAST_VALUE_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ break; ++ case V4L2_CID_DO_WHITE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_DO_WHITE_BALANCE\n"); ++ reg = BCRM_WHITE_BALANCE_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_AUTO_WHITE_BALANCE\n"); ++ reg = BCRM_WHITE_BALANCE_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ case V4L2_CID_SATURATION: ++ avt_dbg(sd, "V4L2_CID_SATURATION\n"); ++ reg = BCRM_SATURATION_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ break; ++ case V4L2_CID_HUE: ++ avt_dbg(sd, "V4L2_CID_HUE\n"); ++ reg = BCRM_HUE_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ avt_dbg(sd, "V4L2_CID_RED_BALANCE\n"); ++ reg = BCRM_RED_BALANCE_RATIO_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_BLUE_BALANCE\n"); ++ reg = BCRM_BLUE_BALANCE_RATIO_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ case V4L2_CID_EXPOSURE: ++ reg = BCRM_EXPOSURE_TIME_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ case V4L2_CID_EXPOSURE_ABSOLUTE: ++ { ++ struct v4l2_ext_control query_exp = {.id = V4L2_CID_EXPOSURE}; ++ int res_exp = avt_ioctl_g_ctrl(sd, &query_exp); ++ if (res_exp == 0) { ++ vc->value = (int32_t)(query_exp.value / EXP_ABS); ++ } ++ return res_exp; ++ } ++ ++ case V4L2_CID_GAIN: ++ avt_dbg(sd, "V4L2_CID_GAIN\n"); ++ reg = BCRM_GAIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ ++ case V4L2_CID_AUTOGAIN: ++ avt_dbg(sd, "V4L2_CID_AUTOGAIN\n"); ++ reg = BCRM_GAIN_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ ++ case V4L2_CID_SHARPNESS: ++ avt_dbg(sd, "V4L2_CID_SHARPNESS\n"); ++ reg = BCRM_SHARPNESS_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MIN: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_AUTO_MIN\n"); ++ reg = BCRM_EXPOSURE_AUTO_MIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MAX: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_AUTO_MAX\n"); ++ reg = BCRM_EXPOSURE_AUTO_MAX_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ ++ case V4L2_CID_GAIN_AUTO_MIN: ++ avt_dbg(sd, "V4L2_CID_GAIN_AUTO_MIN\n"); ++ reg = BCRM_GAIN_AUTO_MIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ ++ case V4L2_CID_GAIN_AUTO_MAX: ++ avt_dbg(sd, "V4L2_CID_GAIN_AUTO_MAX\n"); ++ reg = BCRM_GAIN_AUTO_MAX_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE\n"); ++ reg = BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR\n"); ++ reg = BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_INVERT: ++ vc->value = priv->acquisition_active_invert; ++ return 0; ++ ++ case V4L2_CID_TRIGGER_MODE: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_MODE\n"); ++ vc->value=priv->trigger_mode; ++ length=AV_CAM_DATA_SIZE_8; ++ return 0; ++ ++ case V4L2_CID_TRIGGER_ACTIVATION: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_ACTIVATION\n"); ++ reg = BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ ++ case V4L2_CID_TRIGGER_SOURCE: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOURCE\n"); ++ reg = BCRM_FRAME_START_TRIGGER_SOURCE_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ ++ case V4L2_CID_TRIGGER_SOFTWARE: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOFTWARE\n"); ++ vc->value = 0; ++ length = AV_CAM_DATA_SIZE_8; ++ return 0; ++ case V4L2_CID_DEVICE_TEMPERATURE: ++ avt_dbg(sd, "case V4L2_CID_DEVICE_TEMPERATURE\n"); ++ length = AV_CAM_DATA_SIZE_32; ++ reg = BCRM_DEVICE_TEMPERATURE_32R; ++ vc->value = 0; ++ break; ++ case V4L2_CID_BINNING_MODE: ++ avt_dbg(sd, "case V4L2_CID_BINNING_MODE\n"); ++ reg = BCRM_DIGITAL_BINNIG_MODE_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ break; ++ default: ++ avt_err(sd, "case default or not supported\n"); ++ return -EINVAL; ++ } ++ ++ CLEAR(bcrm_v4l2); ++ CLEAR(qctrl); ++ CLEAR(qctrl_ext); ++ ++ qctrl.id = vc->id; ++ qctrl_ext.id = vc->id; ++ if(length == AV_CAM_DATA_SIZE_64){ ++ /* 64 bit ext control */ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ if (ret < 0) { ++ avt_err(sd, "queryctrl64 failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + reg, ++ AV_CAM_REG_SIZE, length, (char *) &val64); ++ vc->value = convert_bcrm_to_v4l2_gctrl64(&qctrl_ext, (__s64)val64); ++ ++ } ++ else { ++ ret = ioctl_queryctrl(sd, &qctrl); ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ bcrm_v4l2.min_v4l2 = qctrl.minimum; ++ bcrm_v4l2.max_v4l2 = qctrl.maximum; ++ bcrm_v4l2.step_v4l2 = qctrl.step; ++ ++ /* Overwrite the queryctrl maximum value for auto features since value ++ * 2 is 'true' (1) ++ */ ++ if (vc->id == V4L2_CID_AUTOGAIN || ++ vc->id == V4L2_CID_AUTO_WHITE_BALANCE) ++ bcrm_v4l2.max_v4l2 = 2; ++ ++ /* Check values from BCRM */ ++ if ((bcrm_v4l2.min_v4l2 > bcrm_v4l2.max_v4l2) || ++ (bcrm_v4l2.step_v4l2 <= 0)) { ++ avt_err(sd, "invalid BCRM values found. vc->id %d\n", vc->id); ++ return -EINVAL; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + reg, ++ AV_CAM_REG_SIZE, length, (char *) &val64); ++ ++ vc->value = convert_bcrm_to_v4l2_gctrl(&bcrm_v4l2, val64); ++ ++ /* BCRM Auto Exposure changes -> Refer to BCRM document */ ++ if (vc->id == V4L2_CID_EXPOSURE_AUTO) { ++ if (vc->value == 2) ++ /* continous mode, Refer BCRM doc */ ++ vc->value = V4L2_EXPOSURE_AUTO; ++ else ++ /* OFF for off & once mode, Refer BCRM doc */ ++ vc->value = V4L2_EXPOSURE_MANUAL; ++ } ++ ++ /* BCRM Auto Gain/WB changes -> Refer to BCRM document */ ++ if (vc->id == V4L2_CID_AUTOGAIN || ++ vc->id == V4L2_CID_AUTO_WHITE_BALANCE) { ++ ++ if (vc->value == 2) ++ /* continous mode, Refer BCRM doc */ ++ vc->value = true; ++ else ++ /* OFF for off & once mode, Refer BCRM doc */ ++ vc->value = false; ++ } ++ } ++ ++ return ret; ++} ++static int avt_get_acquitision_active_line(struct v4l2_subdev *sd, int *line) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, (char *) line); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ return 0; ++} ++ ++ ++static int avt_get_acquisition_active_mode(struct v4l2_subdev *sd, int *mode) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret; ++ char mode_tmp; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, &mode_tmp); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ *mode = mode_tmp; ++ ++ return 0; ++} ++ ++ ++static int avt_set_acquitision_active_line(struct v4l2_subdev *sd, int line) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret, active; ++ struct v4l2_ext_control ctrl = { .value = line }; ++ ++ ret = avt_get_acquisition_active_mode(sd, &active); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (active) { ++ avt_err(sd, "Cannot set acquisition active line while acquisition active mode is enabled\n"); ++ return -EBUSY; ++ } ++ ++ ret = ioctl_bcrm_i2cwrite_reg(client, &ctrl, BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_DATA_SIZE_8); ++ ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW: i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ return 0; ++} ++ ++ ++static int avt_set_acquisition_active_mode(struct v4l2_subdev *sd, int mode) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int line; ++ int ret; ++ struct v4l2_ext_control ctrl = { 0 }; ++ ++ ret = avt_get_acquitision_active_line(sd, &line); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ctrl.value = (mode ? (1 | (priv->acquisition_active_invert ? 2 : 0)) : 0) << (8*line); ++ ret = ioctl_bcrm_i2cwrite_reg(client, &ctrl, BCRM_LINE_CONFIGURATION_32RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_DATA_SIZE_32); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_LINE_CONFIGURATION_32RW: i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ ctrl.value = mode; ++ ret = ioctl_bcrm_i2cwrite_reg(client, &ctrl, BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_DATA_SIZE_8); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW: i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++static int avt_set_acquisition_active_invert(struct v4l2_subdev *sd, int invert) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int ret, active; ++ ++ ret = avt_get_acquisition_active_mode(sd, &active); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (active) { ++ avt_err(sd, "Cannot set acquisition active invert while acquisition active mode is enabled\n"); ++ return -EBUSY; ++ } ++ ++ priv->acquisition_active_invert = invert; ++ ++ return 0; ++} ++ ++ ++static bool register_readback_required(u32 control) ++{ ++ return (control == V4L2_CID_EXPOSURE) || ++ (control == V4L2_CID_GAIN) || ++ (control == V4L2_CID_GAIN_AUTO_MIN) || ++ (control == V4L2_CID_GAIN_AUTO_MAX) || ++ (control == V4L2_CID_EXPOSURE_AUTO_MIN) || ++ (control == V4L2_CID_EXPOSURE_AUTO_MAX); ++} ++ ++ ++static int register_readback64(struct v4l2_subdev *sd, u32 ctrl_id, unsigned int reg) ++{ ++ int64_t value64 = 0; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct v4l2_ctrl *ctrl; ++ int ret; ++ ++ ctrl = avt_get_control(sd, ctrl_id); ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + reg, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ priv->ignore_control_write = true; ++ ret = __v4l2_ctrl_s_ctrl_int64(ctrl, value64); ++ priv->ignore_control_write = false; ++ ++ return ret; ++} ++ ++ ++static int avt_ioctl_s_ctrl(struct v4l2_subdev *sd, struct v4l2_ext_control *vc) ++{ ++ int ret = 0; ++ unsigned int reg = 0; ++ int length = 0; ++ __s64 value_bkp = 0; ++ struct v4l2_queryctrl qctrl; ++ struct v4l2_query_ext_ctrl qctrl_ext; ++ //struct v4l2_ctrl *vctrl_handle; ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ CLEAR(qctrl); ++ CLEAR(qctrl_ext); ++ ++ qctrl.id = vc->id; ++ qctrl_ext.id = vc->id; ++ ++ switch (vc->id) { ++ ++ case V4L2_CID_DO_WHITE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_DO_WHITE_BALANCE vc->value %u\n", ++ vc->value); ++ reg = BCRM_WHITE_BALANCE_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ vc->value = 1; /* Set 'once' in White Balance Auto Register. */ ++ break; ++ ++/* BLACK LEVEL is deprecated and thus we use Brightness */ ++ case V4L2_CID_BRIGHTNESS: ++ avt_dbg(sd, "V4L2_CID_BRIGHTNESS vc->value %u\n", vc->value); ++ reg = BCRM_BLACK_LEVEL_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ avt_dbg(sd, "V4L2_CID_CONTRAST vc->value %u\n", vc->value); ++ reg = BCRM_CONTRAST_VALUE_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ case V4L2_CID_SATURATION: ++ avt_dbg(sd, "V4L2_CID_SATURATION vc->value %u\n", vc->value); ++ reg = BCRM_SATURATION_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ case V4L2_CID_HUE: ++ avt_dbg(sd, "V4L2_CID_HUE vc->value %u\n", vc->value); ++ reg = BCRM_HUE_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ ++ break; ++ case V4L2_CID_RED_BALANCE: ++ avt_dbg(sd, "V4L2_CID_RED_BALANCE vc->value64 %llu\n", vc->value64); ++ reg = BCRM_RED_BALANCE_RATIO_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_BLUE_BALANCE vc->value64 %llu\n", vc->value64); ++ reg = BCRM_BLUE_BALANCE_RATIO_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ avt_dbg(sd, "V4L2_CID_AUTO_WHITE_BALANCE vc->value %u\n", ++ vc->value); ++ reg = BCRM_WHITE_BALANCE_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ ++ /* BCRM Auto White balance changes */ ++ if (vc->value == true) ++ vc->value = 2;/* Continouous mode */ ++ else ++ vc->value = 0;/* 1; OFF/once mode */ ++ ++ break; ++ case V4L2_CID_GAMMA: ++ avt_dbg(sd, "V4L2_CID_GAMMA vc->value64 %llu\n", vc->value64); ++ reg = BCRM_GAMMA_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ case V4L2_CID_EXPOSURE: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE, cci_reg.bcrm_addr 0x%x, vc->value64 %llu\n", ++ priv->cci_reg.bcrm_addr, vc->value64); ++ ++ value_bkp = vc->value64;/* backup the actual value */ ++ ++ /* i) Setting 'Manual' in Exposure Auto reg. Refer to BCRM ++ * document ++ */ ++ vc->value = 0; ++ ++ avt_dbg(sd, "V4L2_CID_EXPOSURE, cci_reg.bcrm_addr 0x%x, vc->value %u\n", ++ priv->cci_reg.bcrm_addr, vc->value); ++ ++ ret = ioctl_bcrm_i2cwrite_reg(client, ++ vc, BCRM_EXPOSURE_AUTO_8RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_DATA_SIZE_8); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ /* ii) Setting value in Exposure reg. */ ++ vc->value64 = value_bkp;/* restore the actual value */ ++ reg = BCRM_EXPOSURE_TIME_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ ++ /* Implicitly update exposure absolute */ ++ priv->cross_update = true; ++ ret = __v4l2_ctrl_s_ctrl(avt_get_control(sd, V4L2_CID_EXPOSURE_ABSOLUTE), (int32_t)(vc->value64 / EXP_ABS)); ++ if (ret) { ++ avt_err(sd, "failed to update exposure absolute: %d\n", ret); ++ } ++ priv->cross_update = false; ++ ++ break; ++ ++ case V4L2_CID_EXPOSURE_ABSOLUTE: ++ { ++ struct v4l2_ctrl *exposure_ctrl; ++ int res; ++ ++ if (priv->cross_update) { ++ return 0; ++ } ++ ++ exposure_ctrl = avt_get_control(sd,V4L2_CID_EXPOSURE); ++ res = __v4l2_ctrl_s_ctrl_int64(exposure_ctrl,(uint64_t)vc->value * EXP_ABS); ++ ++ return res; ++ } ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_AUTO vc->value %u\n", vc->value); ++ reg = BCRM_EXPOSURE_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ ++ /* BCRM Auto Gain changes */ ++ if (vc->value == V4L2_EXPOSURE_AUTO) { ++ vc->value = 2;/* Continouous mode */ ++ } else { ++ vc->value = 0;/* 1; OFF/once mode */ ++ } ++ ++ break; ++ ++ case V4L2_CID_AUTOGAIN: ++ avt_dbg(sd, "V4L2_CID_AUTOGAIN vc->value %u\n", vc->value); ++ reg = BCRM_GAIN_AUTO_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ ++ /* BCRM Auto Gain changes */ ++ if (vc->value == true) ++ vc->value = 2;/* Continouous mode */ ++ else ++ vc->value = 0;/* 1; OFF/once mode */ ++ ++ break; ++ case V4L2_CID_GAIN: ++ avt_dbg(sd, "V4L2_CID_GAIN, vc->value64 %llu\n", vc->value64); ++ reg = BCRM_GAIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ ++ break; ++ ++ case V4L2_CID_HFLIP: ++ avt_dbg(sd, "V4L2_CID_HFLIP, vc->value %u\n", vc->value); ++ reg = BCRM_IMG_REVERSE_X_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ ++ case V4L2_CID_VFLIP: ++ avt_dbg(sd, "V4L2_CID_VFLIP, vc->value %u\n", vc->value); ++ reg = BCRM_IMG_REVERSE_Y_8RW; ++ length = AV_CAM_DATA_SIZE_8; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ ++ case V4L2_CID_SHARPNESS: ++ avt_dbg(sd, "V4L2_CID_SHARPNESS, vc->value %u\n", vc->value); ++ reg = BCRM_SHARPNESS_32RW; ++ length = AV_CAM_DATA_SIZE_32; ++ ++ ret = ioctl_queryctrl(sd, &qctrl); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value = convert_s_ctrl(vc->value, ++ qctrl.minimum, qctrl.maximum, qctrl.step); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MIN: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_AUTO_MIN, vc->value64 %llu\n", vc->value64); ++ reg = BCRM_EXPOSURE_AUTO_MIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO_MAX: ++ avt_dbg(sd, "V4L2_CID_EXPOSURE_AUTO_MAX, vc->value64 %llu\n", vc->value64); ++ reg = BCRM_EXPOSURE_AUTO_MAX_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ ++ case V4L2_CID_GAIN_AUTO_MIN: ++ avt_dbg(sd, "V4L2_CID_GAIN_AUTO_MIN, vc->value %llu\n", vc->value64); ++ reg = BCRM_GAIN_AUTO_MIN_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ qctrl.id = V4L2_CID_GAIN_AUTO_MIN; ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ ++ case V4L2_CID_GAIN_AUTO_MAX: ++ avt_dbg(sd, "V4L2_CID_GAIN_AUTO_MAX, vc->value %llu\n", vc->value64); ++ reg = BCRM_GAIN_AUTO_MAX_64RW; ++ length = AV_CAM_DATA_SIZE_64; ++ ++ qctrl.id = V4L2_CID_GAIN_AUTO_MAX; ++ ret = ioctl_queryctrl64(sd, &qctrl_ext); ++ ++ if (ret < 0) { ++ avt_err(sd, "queryctrl failed: ret %d\n", ret); ++ return ret; ++ } ++ ++ vc->value64 = convert_s_ctrl64(&qctrl_ext, vc->value64); ++ break; ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE: ++ return avt_set_acquisition_active_mode(sd, vc->value); ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR: ++ return avt_set_acquitision_active_line(sd, vc->value); ++ ++ case V4L2_CID_EXPOSURE_ACTIVE_INVERT: ++ return avt_set_acquisition_active_invert(sd, vc->value); ++ ++ case V4L2_CID_TRIGGER_MODE: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_MODE vc->value %u\n", vc->value); ++ if(vc->value==0) ++ {/*trigger mode off*/ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_MODE_8RW, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ set_channel_trigger_mode(sd, false); ++ set_channel_timeout(sd, CAPTURE_TIMEOUT_MS); ++ priv->trigger_mode=false; ++ } ++ else ++ {/*trigger mode off*/ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_MODE_8RW, 1); ++ if (ret < 0) { ++ return ret; ++ } ++ set_channel_trigger_mode(sd, true); ++ set_channel_timeout(sd, AVT_TEGRA_TIMEOUT_DISABLED); ++ priv->trigger_mode=true; ++ } ++ return 0; ++ ++ case V4L2_CID_TRIGGER_ACTIVATION: ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_ACTIVATION vc->value %u\n", vc->value); ++ /* Setting Frame Start Trigger Activation */ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW, vc->value); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ return 0; ++ ++ case V4L2_CID_TRIGGER_SOURCE: ++ { ++ u8 cur_trigger_source; ++ u8 trigger_source_reg=vc->value; ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOURCE vc->value %u\n", vc->value); ++ if(trigger_source_reg > V4L2_TRIGGER_SOURCE_SOFTWARE) ++ { ++ avt_err(sd, " invalid trigger source (%d)", trigger_source_reg); ++ return -1; ++ } ++ /* Check if we need to write to Trigger Source register. ++ * If we do this more than once in the power cycle, ++ * triggering non-empty frames is not possible. ++ */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + ++ BCRM_FRAME_START_TRIGGER_SOURCE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &cur_trigger_source); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (cur_trigger_source == trigger_source_reg) { ++ avt_err(sd, " Trigger source already set!\n"); ++ return 0; ++ } ++ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_SOURCE_8RW, trigger_source_reg); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ return 0; ++ } ++ case V4L2_CID_TRIGGER_SOFTWARE: ++ { ++ u8 trigger_source; ++ avt_dbg(sd, "case V4L2_CID_TRIGGER_SOFTWARE\n"); ++ ++ ret = avt_reg_read(client, priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_SOURCE_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, (char *) &trigger_source); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (trigger_source != AV_CAM_SOFTWARE_TRIGGER) { ++ return -EPERM; ++ } ++ ++ /* Check if stream is already on */ ++ if (!priv->stream_on) ++ return -EAGAIN; ++ ++ /* Generate Trigger */ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_FRAME_START_TRIGGER_SOFTWARE_8W, 1); ++ ++ if (ret < 0) { ++ avt_err(sd, "generating trigger failed (%d)\n", ret); ++ return ret; ++ } ++ set_channel_pending_trigger(sd); ++ return 0; ++ } ++ case V4L2_CID_BINNING_MODE: ++ avt_dbg(sd, "case V4L2_CID_BINNING_MODE vc->value %u\n", vc->value); ++ /* Setting Binning Mode */ ++ ret = avt_reg_write(client, ++ priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_MODE_8RW, vc->value); ++ ++ if (ret < 0) { ++ avt_err(sd, "setting binning mode failed (%d)\n", ret); ++ return ret; ++ } ++ return 0; ++ default: ++ avt_err(sd, "case default or not supported\n"); ++ ret = -EPERM; ++ return ret; ++ } ++ ++ ret = ioctl_bcrm_i2cwrite_reg(client, ++ vc, reg + priv->cci_reg.bcrm_addr, length); ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c write failed failed (%d)\n", ret); ++ return ret; ++ } ++ ++ if (vc->id == V4L2_CID_EXPOSURE) ++ { ++ uint64_t value64; ++ struct v4l2_fract *tpf = (&priv->streamcap.timeperframe); ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_ACQUISITION_FRAME_RATE_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ tpf->numerator = FRAQ_NUM; ++ tpf->denominator = value64 / FRAQ_NUM; ++ } ++ ++ if(register_readback_required(vc->id)) { ++ ret = register_readback64(sd, vc->id, reg); ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static int avt_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd; ++ struct v4l2_ext_control c; ++ ++ struct avt_csi2_priv *priv; ++ ++ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) ++ return 0; ++ ++ priv = container_of(ctrl->handler, struct avt_csi2_priv, hdl); ++ sd = priv->subdev; ++ ++ ++ if(priv->ignore_control_write) { ++ return 0; ++ } ++ ++ c.id = ctrl->id; ++ c.value = ctrl->val; ++ /* For 64-bit extended V4L2 controls, ++ * new value comes in this pointer ++ */ ++ c.value64 = *(ctrl->p_new.p_s64); ++ ++ return avt_ioctl_s_ctrl(sd, &c); ++} ++ ++static int avt_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd; ++ struct v4l2_ext_control c; ++ struct avt_csi2_priv *priv; ++ int ret; ++ ++ priv = container_of(ctrl->handler, struct avt_csi2_priv, hdl); ++ sd = priv->subdev; ++ ++ c.id = ctrl->id; ++ ret = avt_ioctl_g_ctrl(sd, &c); ++ ctrl->val = c.value; ++ *(ctrl->p_new.p_s64)=c.value64; ++ ++ if(ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops avt_ctrl_ops = { ++ .g_volatile_ctrl = avt_g_volatile_ctrl, ++ .s_ctrl = avt_s_ctrl, ++}; ++ ++static int read_max_resolution(struct v4l2_subdev *sd, uint32_t *max_width, uint32_t *max_height) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ ret = avt_get_param(client, V4L2_AV_CSI2_WIDTH_MAXVAL_R, max_width); ++ if (ret < 0) ++ return ret; ++ ++ ret = avt_get_param(client, V4L2_AV_CSI2_HEIGHT_MAXVAL_R, max_height); ++ if (ret < 0) ++ return ret; ++ ++ return ret; ++} ++ ++static int avt_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ uint32_t max_width = 0, max_height = 0; ++ ++ if(read_max_resolution(sd, &max_width, &max_height) < 0) ++ { ++ return -EINVAL; ++ } ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_COMPOSE_DEFAULT: ++ case V4L2_SEL_TGT_COMPOSE_BOUNDS: ++ case V4L2_SEL_TGT_COMPOSE: ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->frmp.r; ++ break; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.top = sel->r.left = 0; ++ sel->r.width = avt_align_width(sd,max_width,max_width,priv->mbus_fmt_code); ++ sel->r.height = max_height; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ sel->flags = V4L2_SEL_FLAG_LE; ++ ++ return 0; ++} ++ ++static int avt_get_align_width(struct v4l2_subdev *sd,u32 mbus_fmt_code) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ int width_align = 0; ++ ++ if (priv->crop_align_enabled) { ++ /* ++ * Align with VI capabilities ++ * ++ * For each format line length has to be aligned to specific ++ * value ++ */ ++ switch (mbus_fmt_code) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_BGR888_1X24: ++ width_align = 16; ++ break; ++ ++ case MEDIA_BUS_FMT_VYUY8_2X8: ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ width_align = 32; ++ break; ++ ++ case MEDIA_BUS_FMT_SRGGB8_1X8: ++ case MEDIA_BUS_FMT_SGBRG8_1X8: ++ case MEDIA_BUS_FMT_SGRBG8_1X8: ++ case MEDIA_BUS_FMT_SBGGR8_1X8: ++ width_align = 16; ++ break; ++ ++ default: ++ width_align = 64; ++ break; ++ } ++ } ++ ++ /* Use kernel param override? */ ++ if (v4l2_width_align != 0) ++ { ++ width_align = v4l2_width_align; ++ avt_warn(sd, "v4l2_width_align override: %d\n", width_align); ++ } ++ ++ return width_align; ++} ++ ++static int avt_align_width(struct v4l2_subdev *sd, int width, u32 max_width,u32 mbus_fmt_code) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ avt_dbg(sd, "input width: %d\n", width); ++ if (priv->crop_align_enabled) { ++ int const align_size = avt_get_align_width(sd,mbus_fmt_code); ++ avt_dbg(sd, "align_size: %d\n", align_size); ++ ++ width = roundup(width, align_size); ++ if (width > max_width) { ++ width -= align_size; ++ } ++ avt_dbg(sd, "output width: %d\n", width); ++ } ++ else ++ { ++ avt_dbg(sd, "crop_align_enabled DISABLED\n"); ++ } ++ ++ return width; ++} ++ ++static int avt_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ ++ if (priv->crop_align_enabled) { ++ sel->r.width = avt_align_width(sd, sel->r.width,priv->frmp.maxw, priv->mbus_fmt_code); ++ } ++ ++/* ++* As per the crop concept document, the following steps should be followed before setting crop to the sensor. ++* ++* i) If new width is less or equal than current width, write the width register first then write offset X (left) register, ++* both values should be within the range (min, max and inc). ++* ii) If new width is higher than current width, write the offset X (left) register first then write the width register, ++* both values should be within the range (min, max, and inc) ++* iii) If new height is less or equal than current height, write the height register first then write offset Y (top) register, ++* both values should be within the range (min, max and inc). ++* iv) If new height is higher than current height, write the offset Y (top) register first then write the height register, ++* both values should be within the range (min, max, and inc) ++*/ ++ ++ if (sel->r.width <= priv->frmp.r.width) { /* case i) New width is lesser or equal than current */ ++ ++ // write width ++ sel->r.width = clamp(roundup(sel->r.width, priv->frmp.sw), priv->frmp.minw, priv->frmp.maxw); ++ avt_set_param(client, V4L2_AV_CSI2_WIDTH_W, sel->r.width); ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ // write offset x ++ sel->r.left = clamp(roundup(sel->r.left, priv->frmp.swoff), priv->frmp.minwoff, priv->frmp.maxwoff); ++ avt_set_param(client, V4L2_AV_CSI2_OFFSET_X_W, sel->r.left); ++ } ++ else { /* case ii) New width is higher than current */ ++ ++ // write offset x ++ sel->r.left = clamp(roundup(sel->r.left, priv->frmp.swoff), priv->frmp.minwoff, priv->frmp.maxwoff); ++ avt_set_param(client, V4L2_AV_CSI2_OFFSET_X_W, sel->r.left); ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ // write width ++ sel->r.width = clamp(roundup(sel->r.width, priv->frmp.sw), priv->frmp.minw, priv->frmp.maxw); ++ avt_set_param(client, V4L2_AV_CSI2_WIDTH_W, sel->r.width); ++ } ++ ++ if (sel->r.height <= priv->frmp.r.height) { /* case iii) New height is lesser or equal than current */ ++ // write height ++ sel->r.height = clamp(roundup(sel->r.height, priv->frmp.sh), priv->frmp.minh, priv->frmp.maxh); ++ avt_set_param(client, V4L2_AV_CSI2_HEIGHT_W, sel->r.height); ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ // write offset y ++ sel->r.top = clamp(roundup(sel->r.top, priv->frmp.shoff), priv->frmp.minhoff, priv->frmp.maxhoff); ++ avt_set_param(client, V4L2_AV_CSI2_OFFSET_Y_W, sel->r.top); ++ } ++ else { /* case iv) New height is higher than current */ ++ ++ // write offset y ++ sel->r.top = clamp(roundup(sel->r.top, priv->frmp.shoff), priv->frmp.minhoff, priv->frmp.maxhoff); ++ avt_set_param(client, V4L2_AV_CSI2_OFFSET_Y_W, sel->r.top); ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ // write height ++ sel->r.height = clamp(roundup(sel->r.height, priv->frmp.sh), priv->frmp.minh, priv->frmp.maxh); ++ avt_set_param(client, V4L2_AV_CSI2_HEIGHT_W, sel->r.height); ++ } ++ ++ // update width, height, offset x/y restrictions from camera ++ avt_init_frame_param(sd); ++ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int avt_s_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ struct v4l2_fract *tpf = &(priv->streamcap.timeperframe); ++ struct v4l2_ext_control vc; ++ int ret; ++ struct v4l2_query_ext_ctrl qctrl; ++ u64 value64; ++ union bcrm_feature_reg feature_inquiry_reg; ++ ++ /* reading the Feature inquiry register */ ++ ret = read_feature_register(sd, &feature_inquiry_reg); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ /* Check if setting acquisition frame rate is available */ ++ if(!feature_inquiry_reg.feature_inq.acquisition_frame_rate) { ++ avt_info(sd, "Acquisition frame rate setting not supported by firmware\n"); ++ return 0; ++ } ++ ++ /* Copy new settings to internal structure */ ++ memcpy(&priv->streamcap.timeperframe, &interval->interval, sizeof(struct v4l2_fract)); ++ ++ ++ avt_dbg(sd, "[mjsob] %u/%u\n", tpf->denominator, tpf->numerator); ++ ++ if (tpf->numerator == 0 || tpf->denominator == 0) { ++ ++ /* Enable auto frame rate */ ++ vc.value = 0; ++ ret = ioctl_bcrm_i2cwrite_reg(client, &vc, priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW, AV_CAM_DATA_SIZE_8); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_64RW: i2c write failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_64RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "BCRM_ACQUISITION_FRAME_RATE_64RW: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ tpf->numerator = FRAQ_NUM; ++ tpf->denominator = 0; ++ ++ /* Copy modified settings back */ ++ memcpy(&interval->interval, &priv->streamcap.timeperframe, sizeof(struct v4l2_fract)); ++ ++ } else { ++ /* Enable and set manual frame rate */ ++ ++ /* reading the Minimum Frame Rate Level */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl.minimum = value64; ++ ++ /* reading the Maximum Frame Rate Level */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl.maximum = value64; ++ ++ /* reading the Frame Rate Level step increment */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_INC_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value64); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_INCREMENT_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ qctrl.step = value64; ++ ++ /* Set step to 1 uHz because zero value came from camera register */ ++ if(!qctrl.step) ++ qctrl.step = 1; ++ ++ if (qctrl.minimum > qctrl.maximum) { ++ avt_err(sd, "Frame rate: min > max! (%llu > %llu)\n", ++ qctrl.minimum, qctrl.maximum); ++ return -EINVAL; ++ } ++ if (qctrl.step <= 0) { ++ avt_err(sd, "Frame rate: non-positive step value (%llu)!\n", ++ qctrl.step); ++ return -EINVAL; ++ } ++ ++ /* Translate timeperframe to frequency ++ * by inverting the fraction ++ */ ++ value64 = (tpf->denominator * UHZ_TO_HZ) / tpf->numerator; ++ value64 = convert_s_ctrl64(&qctrl,value64); ++ ++ if (value64 < 0) { ++ avt_err(sd, "Frame rate: non-positive value (%llu)!\n", ++ value64); ++ return -EINVAL; ++ } ++ ++ /* Enable manual frame rate */ ++ vc.value = 1; ++ ret = ioctl_bcrm_i2cwrite_reg(client, &vc, priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW, AV_CAM_DATA_SIZE_8); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_64RW: i2c write failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ /* Save new frame rate to camera register */ ++ vc.value64 = value64; ++ ret = ioctl_bcrm_i2cwrite_reg(client, &vc, priv->cci_reg.bcrm_addr + BCRM_ACQUISITION_FRAME_RATE_64RW, AV_CAM_DATA_SIZE_64); ++ if (ret < 0) { ++ avt_err(sd, "ACQUISITION_FRAME_RATE_64RW: i2c write failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ //Check if result of division will be between 0 and 1, if it is so increase numerator ++ if (value64 < FRAQ_NUM) { ++ tpf->numerator = FRAQ_NUM * FRAQ_NUM; ++ tpf->denominator = value64; ++ } ++ else { ++ tpf->numerator = FRAQ_NUM; ++ tpf->denominator = value64 / FRAQ_NUM; ++ } ++ ++ /* Copy modified settings back */ ++ memcpy(&interval->interval, &priv->streamcap.timeperframe, sizeof(struct v4l2_fract)); ++ } ++ ++ return 0; ++} ++ ++static int avt_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ if (interval->pad != 0) ++ return -EINVAL; ++ ++ memcpy(&interval->interval, &priv->streamcap.timeperframe, sizeof(struct v4l2_fract)); ++ ++ return 0; ++} ++ ++static int avt_csi2_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ // called when userspace app calls 'open' ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ int ret; ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ const uint32_t poll_delay_ms = 2; ++ const uint32_t timeout_ms = 3000; ++ unsigned long timeout_jiffies = 0; ++ uint8_t bcm_mode = 0; ++ char *i2c_reg_buf; ++ const int open_count = atomic_read(&priv->open_count); ++ ++ // set stride align ++ if (priv->stride_align_enabled) ++ set_channel_stride_align_for_format(sd, priv->mbus_fmt_code); ++ else ++ set_channel_stride_align(sd, 1); ++ ++ avt_dbg(sd,"Current open count %d\n",open_count); ++ ++ atomic_inc(&priv->open_count); ++ ++ // set BCRM mode if required ++ ret = avt_reg_read(priv->client, CCI_CURRENT_MODE_8R, AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, &bcm_mode); ++ if (ret < 0) { ++ avt_err(sd, "Failed to get device mode: i2c read failed (%d)\n", ret); ++ goto error; ++ } ++ else ++ { ++ avt_dbg(sd, "Initial device mode=%u (%s)\n", bcm_mode, (bcm_mode==0) ? "BCRM" : "GenCP"); ++ } ++ ++ if (!open_count) { ++ if (bcm_mode != OPERATION_MODE_BCRM) { ++ avt_info(sd,"Switching to bcrm mode"); ++ ++ ++ // GenCP mode -> Switch back to BCRM ++ CLEAR(i2c_reg); ++ bcm_mode = OPERATION_MODE_BCRM; ++ i2c_reg = CCI_CHANGE_MODE_8W; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = AV_CAM_DATA_SIZE_8; ++ i2c_reg_buf = (char *) &bcm_mode; ++ timeout_jiffies = jiffies + msecs_to_jiffies(timeout_ms); ++ ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, ++ i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ if (ret < 0) { ++ avt_err(sd, "Failed to set BCM mode: i2c write failed (%d)\n", ret); ++ goto error; ++ } ++ ++ // Wait for mode change ++ do { ++ usleep_range(poll_delay_ms * 1000, (poll_delay_ms * 1000) + 1); ++ ret = avt_reg_read(priv->client, CCI_CURRENT_MODE_8R, AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, &bcm_mode); ++ } while ((ret >= 0) && (bcm_mode != OPERATION_MODE_BCRM) && time_before(jiffies, timeout_jiffies)); ++ ++ if (bcm_mode != OPERATION_MODE_BCRM) { ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ ++ ++ priv->mode = AVT_BCRM_MODE; ++ } ++ ++ return 0; ++ ++error: ++ atomic_dec(&priv->open_count); ++ return ret; ++} ++ ++int avt_csi2_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ ++ if (0 == val) ++ { ++ int ret = soft_reset(priv->client); ++ ++ if (ret < 0) ++ { ++ return ret; ++ } ++ ++ set_channel_avt_cam_mode(sd,0); ++ ++ return avt_init_mode(sd); ++ } ++ ++ return -EINVAL; ++} ++ ++int avt_csi2_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ const int open_count = atomic_read(&priv->open_count); ++ ++ avt_dbg(sd,"Current open count %d\n",open_count); ++ ++ atomic_dec(&priv->open_count); ++ ++ return 0; ++} ++ ++ ++static const struct v4l2_subdev_core_ops avt_csi2_core_ops = { ++ .subscribe_event = avt_csi2_subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++ .ioctl = avt_csi2_ioctl, ++ .reset = avt_csi2_reset, ++}; ++ ++static const struct v4l2_subdev_internal_ops avt_csi2_int_ops = { ++ .open = avt_csi2_open, ++ .close = avt_csi2_close, ++}; ++ ++static const struct v4l2_subdev_video_ops avt_csi2_video_ops = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ .g_mbus_config = avt_csi2_g_mbus_config, ++#endif //#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ .s_stream = avt_csi2_s_stream, ++ .g_frame_interval = avt_g_frame_interval, ++ .s_frame_interval = avt_s_frame_interval, ++}; ++ ++static const struct v4l2_subdev_pad_ops avt_csi2_pad_ops = { ++ .set_fmt = avt_csi2_set_fmt, ++ .get_fmt = avt_csi2_get_fmt, ++ .enum_mbus_code = avt_csi2_enum_mbus_code, ++ .enum_frame_size = avt_csi2_enum_framesizes, ++ .enum_frame_interval = avt_csi2_enum_frameintervals, ++ .get_selection = avt_get_selection, ++ .set_selection = avt_set_selection, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) ++ .get_mbus_config = avt_csi2_get_mbus_config, ++#endif //#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) ++}; ++ ++static const struct v4l2_subdev_ops avt_csi2_subdev_ops = { ++ .core = &avt_csi2_core_ops, ++ .video = &avt_csi2_video_ops, ++ .pad = &avt_csi2_pad_ops, ++}; ++ ++static const struct media_entity_operations avt_csi2_media_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++const struct of_device_id avt_csi2_of_match[] = { ++ { .compatible = "alliedvision,avt_csi2",}, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, avt_csi2_of_match); ++ ++static int read_cci_registers(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ int ret = 0; ++ uint32_t crc = 0; ++ uint32_t crc_byte_count = 0; ++ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ ++ char *i2c_reg_buf; ++ ++ i2c_reg = cci_cmd_tbl[CCI_REGISTER_LAYOUT_VERSION].address; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ /* ++ * Avoid last 4 bytes read as its WRITE only register except ++ * CURRENT MODE REG ++ */ ++ i2c_reg_count = sizeof(priv->cci_reg) - 4; ++ ++ i2c_reg_buf = (char *)&priv->cci_reg; ++ /* Calculate CRC from each reg up to the CRC reg */ ++ crc_byte_count = ++ (uint32_t)((char *)&priv->cci_reg.checksum - (char *)&priv->cci_reg); ++ ++ dev_info(&client->dev, "crc_byte_count = %d, i2c_reg.count = %d\n", ++ crc_byte_count, i2c_reg_count); ++ ++ /* read CCI registers */ ++ ret = i2c_read(client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "Camera not responding. Error=%d\n", ret); ++ return ret; ++ } ++ ++ /* CRC calculation */ ++ crc = crc32(U32_MAX, &priv->cci_reg, crc_byte_count); ++ ++ /* Swap bytes if neccessary */ ++ cpu_to_be32s(&priv->cci_reg.layout_version); ++ cpu_to_be64s(&priv->cci_reg.device_capabilities); ++ cpu_to_be16s(&priv->cci_reg.gcprm_address); ++ cpu_to_be16s(&priv->cci_reg.bcrm_addr); ++ cpu_to_be32s(&priv->cci_reg.checksum); ++ ++ /* Check the checksum of received with calculated. */ ++ if (crc != priv->cci_reg.checksum) { ++ dev_err(&client->dev, ++ "wrong CCI CRC value! calculated = 0x%08x, received = 0x%08x\n", ++ crc, priv->cci_reg.checksum); ++ return -EINVAL; ++ } ++ ++ dev_info(&client->dev, "cci layout version: 0x%08x\n", ++ priv->cci_reg.layout_version); ++ dev_info(&client->dev, "cci device capabilities: 0x%016llx\n", ++ priv->cci_reg.device_capabilities); ++ dev_info(&client->dev, "cci device guid: %s\n", ++ priv->cci_reg.device_guid); ++ dev_info(&client->dev, "cci gcprm_address: 0x%04x\n", ++ priv->cci_reg.gcprm_address); ++ dev_info(&client->dev, "cci bcrm_address: 0x%04x\n", ++ priv->cci_reg.bcrm_addr); ++ dev_info(&client->dev, "cci device guid: %s\n", ++ priv->cci_reg.device_guid); ++ dev_info(&client->dev, "cci manufacturer_name: %s\n", ++ priv->cci_reg.manufacturer_name); ++ dev_info(&client->dev, "cci model_name: %s\n", ++ priv->cci_reg.model_name); ++ dev_info(&client->dev, "cci family_name: %s\n", ++ priv->cci_reg.family_name); ++ dev_info(&client->dev, "cci device_version: %s\n", ++ priv->cci_reg.device_version); ++ dev_info(&client->dev, "cci manufacturer_info: %s\n", ++ priv->cci_reg.manufacturer_info); ++ dev_info(&client->dev, "cci serial_number: %s\n", ++ priv->cci_reg.serial_number); ++ dev_info(&client->dev, "cci user_defined_name: %s\n", ++ priv->cci_reg.user_defined_name); ++ ++ return 0; ++} ++ ++static int read_gencp_registers(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ int ret = 0; ++ uint32_t crc = 0; ++ uint32_t crc_byte_count = 0; ++ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ ++ char *i2c_reg_buf; ++ ++ i2c_reg = priv->cci_reg.gcprm_address + 0x0000; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = sizeof(priv->gencp_reg); ++ i2c_reg_buf = (char *)&priv->gencp_reg; ++ ++ /* Calculate CRC from each reg up to the CRC reg */ ++ crc_byte_count = ++ (uint32_t)((char *)&priv->gencp_reg.checksum - (char *)&priv->gencp_reg); ++ ++ ret = i2c_read(client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ crc = crc32(U32_MAX, &priv->gencp_reg, crc_byte_count); ++ ++ if (ret < 0) { ++ pr_err("%s : I2C read failed, ret %d\n", __func__, ret); ++ return ret; ++ } ++ ++ be32_to_cpus(&priv->gencp_reg.gcprm_layout_version); ++ be16_to_cpus(&priv->gencp_reg.gencp_out_buffer_address); ++ be16_to_cpus(&priv->gencp_reg.gencp_in_buffer_address); ++ be16_to_cpus(&priv->gencp_reg.gencp_out_buffer_size); ++ be16_to_cpus(&priv->gencp_reg.gencp_in_buffer_size); ++ be32_to_cpus(&priv->gencp_reg.checksum); ++ ++ if (crc != priv->gencp_reg.checksum) { ++ dev_warn(&client->dev, ++ "wrong GENCP CRC value! calculated = 0x%08x, received = 0x%08x\n", ++ crc, priv->gencp_reg.checksum); ++ } ++ ++ dev_info(&client->dev, "gcprm layout version: 0x%08x\n", ++ priv->gencp_reg.gcprm_layout_version); ++ dev_info(&client->dev, "gcprm out buf addr: 0x%04x\n", ++ priv->gencp_reg.gencp_out_buffer_address); ++ dev_info(&client->dev, "gcprm out buf size: 0x%04x\n", ++ priv->gencp_reg.gencp_out_buffer_size); ++ dev_info(&client->dev, "gcprm in buf addr: 0x%04x\n", ++ priv->gencp_reg.gencp_in_buffer_address); ++ dev_info(&client->dev, "gcprm in buf size: 0x%04x\n", ++ priv->gencp_reg.gencp_in_buffer_size); ++ ++ return 0; ++} ++ ++static int cci_version_check(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ uint32_t cci_minor_ver, cci_major_ver; ++ ++ cci_minor_ver = (priv->cci_reg.layout_version & CCI_REG_LAYOUT_MINVER_MASK) ++ >> CCI_REG_LAYOUT_MINVER_SHIFT; ++ ++ /* We need at least the minor version defined */ ++ if (cci_minor_ver >= CCI_REG_LAYOUT_MINVER) { ++ dev_dbg(&client->dev, "%s: valid cci register minor version: read: %d, expected minimum: %d\n", ++ __func__, cci_minor_ver, CCI_REG_LAYOUT_MINVER); ++ } else { ++ dev_err(&client->dev, "%s: cci reg minor version mismatch! read: %d (0x%x), expected: %d\n", ++ __func__, cci_minor_ver, priv->cci_reg.layout_version, ++ CCI_REG_LAYOUT_MINVER); ++ return -EINVAL; ++ } ++ ++ cci_major_ver = (priv->cci_reg.layout_version & CCI_REG_LAYOUT_MAJVER_MASK) ++ >> CCI_REG_LAYOUT_MAJVER_SHIFT; ++ ++ /* We need the exact major version */ ++ if (cci_major_ver == CCI_REG_LAYOUT_MAJVER) { ++ dev_dbg(&client->dev, "%s: valid cci register major version: read: %d, expected: %d)\n", ++ __func__, cci_major_ver, CCI_REG_LAYOUT_MAJVER); ++ } else { ++ dev_err(&client->dev, "%s: cci reg major version mismatch! read: %d (0x%x), expected: %d\n", ++ __func__, cci_major_ver, priv->cci_reg.layout_version, ++ CCI_REG_LAYOUT_MAJVER); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int bcrm_version_check(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ u32 value = 0; ++ int ret; ++ ++ /* reading the BCRM version */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_VERSION_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &value); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ dev_info(&client->dev, "bcrm version (driver): 0x%08x (%d.%d)\n", ++ BCRM_DEVICE_VERSION, ++ BCRM_MAJOR_VERSION, ++ BCRM_MINOR_VERSION); ++ ++ dev_info(&client->dev, "bcrm version (camera): 0x%08x (%d.%d)\n", ++ value, ++ (value & 0xffff0000) >> 16, ++ (value & 0x0000ffff)); ++ ++ return (value >> 16) == BCRM_MAJOR_VERSION ? 1 : 0; ++} ++ ++static int gcprm_version_check(struct i2c_client *client) ++{ ++ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ u32 value = priv->gencp_reg.gcprm_layout_version; ++ ++ dev_info(&client->dev, "gcprm layout version (driver): 0x%08x (%d.%d)\n", ++ GCPRM_DEVICE_VERSION, ++ GCPRM_MAJOR_VERSION, ++ GCPRM_MINOR_VERSION); ++ ++ dev_info(&client->dev, "gcprm layout version (camera): 0x%08x (%d.%d)\n", ++ value, ++ (value & 0xffff0000) >> 16, ++ (value & 0x0000ffff)); ++ ++ return (value & 0xffff0000) >> 16 == GCPRM_MAJOR_VERSION ? 1 : 0; ++} ++ ++static void bcrm_dump(struct i2c_client *client) ++{ ++ return; /* DISABLED. DEBUG ONLY */ ++ ++ /* Dump all BCRM registers (client, except write only ones) */ ++ dump_bcrm_reg_32(client, BCRM_VERSION_32R, "BCRM_VERSION_32R"); ++ dump_bcrm_reg_64(client, BCRM_FEATURE_INQUIRY_64R, "BCRM_FEATURE_INQUIRY_64R"); ++ dump_bcrm_reg_64(client, BCRM_DEVICE_FIRMWARE_VERSION_64R, "BCRM_DEVICE_FIRMWARE_VERSION_64R"); ++ dump_bcrm_reg_8(client, BCRM_WRITE_HANDSHAKE_8RW, "BCRM_WRITE_HANDSHAKE_8RW"); ++ ++ /* Streaming Control Registers */ ++ dump_bcrm_reg_8(client, BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R, "BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R"); ++ dump_bcrm_reg_8(client, BCRM_CSI2_LANE_COUNT_8RW, "BCRM_CSI2_LANE_COUNT_8RW"); ++ dump_bcrm_reg_32(client, BCRM_CSI2_CLOCK_MIN_32R, "BCRM_CSI2_CLOCK_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_CSI2_CLOCK_MAX_32R, "BCRM_CSI2_CLOCK_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_CSI2_CLOCK_32RW, "BCRM_CSI2_CLOCK_32RW"); ++ dump_bcrm_reg_32(client, BCRM_BUFFER_SIZE_32R, "BCRM_BUFFER_SIZE_32R"); ++ dump_bcrm_reg_32(client, BCRM_PHY_RESET_8RW, "BCRM_PHY_RESET_8RW"); ++ ++ /* Acquisition Control Registers */ ++ dump_bcrm_reg_8(client, BCRM_ACQUISITION_START_8RW, "BCRM_ACQUISITION_START_8RW"); ++ dump_bcrm_reg_8(client, BCRM_ACQUISITION_STOP_8RW, "BCRM_ACQUISITION_STOP_8RW"); ++ dump_bcrm_reg_8(client, BCRM_ACQUISITION_ABORT_8RW, "BCRM_ACQUISITION_ABORT_8RW"); ++ dump_bcrm_reg_8(client, BCRM_ACQUISITION_STATUS_8R, "BCRM_ACQUISITION_STATUS_8R"); ++ dump_bcrm_reg_64(client, BCRM_ACQUISITION_FRAME_RATE_64RW, "BCRM_ACQUISITION_FRAME_RATE_64RW"); ++ dump_bcrm_reg_64(client, BCRM_ACQUISITION_FRAME_RATE_MIN_64R, "BCRM_ACQUISITION_FRAME_RATE_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_ACQUISITION_FRAME_RATE_MAX_64R, "BCRM_ACQUISITION_FRAME_RATE_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_ACQUISITION_FRAME_RATE_INC_64R, "BCRM_ACQUISITION_FRAME_RATE_INC_64R"); ++ dump_bcrm_reg_8(client, BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW, "BCRM_ACQUISITION_FRAME_RATE_ENABLE_8RW"); ++ ++ dump_bcrm_reg_8(client, BCRM_FRAME_START_TRIGGER_MODE_8RW, "BCRM_FRAME_START_TRIGGER_MODE_8RW"); ++ dump_bcrm_reg_8(client, BCRM_FRAME_START_TRIGGER_SOURCE_8RW, "BCRM_FRAME_START_TRIGGER_SOURCE_8RW"); ++ dump_bcrm_reg_8(client, BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW, "BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW"); ++ ++ dump_bcrm_reg_8(client, BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW, "BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW"); ++ dump_bcrm_reg_8(client, BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW, "BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW"); ++ dump_bcrm_reg_32(client, BCRM_LINE_CONFIGURATION_32RW, "BCRM_LINE_CONFIGURATION_32RW"); ++ dump_bcrm_reg_8(client, BCRM_LINE_STATUS_8R, "BCRM_LINE_STATUS_8R"); ++ ++ /* Image Format Control Registers */ ++ dump_bcrm_reg_32(client, BCRM_IMG_WIDTH_32RW, "BCRM_IMG_WIDTH_32RW"); ++ dump_bcrm_reg_32(client, BCRM_IMG_WIDTH_MIN_32R, "BCRM_IMG_WIDTH_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_WIDTH_MAX_32R, "BCRM_IMG_WIDTH_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_WIDTH_INC_32R, "BCRM_IMG_WIDTH_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_IMG_HEIGHT_32RW, "BCRM_IMG_HEIGHT_32RW"); ++ dump_bcrm_reg_32(client, BCRM_IMG_HEIGHT_MIN_32R, "BCRM_IMG_HEIGHT_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_HEIGHT_MAX_32R, "BCRM_IMG_HEIGHT_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_HEIGHT_INC_32R, "BCRM_IMG_HEIGHT_INC_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_X_32RW, "BCRM_IMG_OFFSET_X_32RW"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_X_MIN_32R, "BCRM_IMG_OFFSET_X_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_X_MAX_32R, "BCRM_IMG_OFFSET_X_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_X_INC_32R, "BCRM_IMG_OFFSET_X_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_Y_32RW, "BCRM_IMG_OFFSET_Y_32RW"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_Y_MIN_32R, "BCRM_IMG_OFFSET_Y_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_Y_MAX_32R, "BCRM_IMG_OFFSET_Y_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_IMG_OFFSET_Y_INC_32R, "BCRM_IMG_OFFSET_Y_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_IMG_MIPI_DATA_FORMAT_32RW, "BCRM_IMG_MIPI_DATA_FORMAT_32RW"); ++ dump_bcrm_reg_64(client, BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R, "BCRM_IMG_AVAILABLE_MIPI_DATA_FORMATS_64R"); ++ ++ dump_bcrm_reg_8(client, BCRM_IMG_BAYER_PATTERN_INQUIRY_8R, "BCRM_IMG_BAYER_PATTERN_INQUIRY_8R"); ++ dump_bcrm_reg_8(client, BCRM_IMG_BAYER_PATTERN_8RW, "BCRM_IMG_BAYER_PATTERN_8RW"); ++ ++ dump_bcrm_reg_8(client, BCRM_IMG_REVERSE_X_8RW, "BCRM_IMG_REVERSE_X_8RW"); ++ dump_bcrm_reg_8(client, BCRM_IMG_REVERSE_Y_8RW, "BCRM_IMG_REVERSE_Y_8RW"); ++ ++ dump_bcrm_reg_32(client, BCRM_SENSOR_WIDTH_32R, "BCRM_SENSOR_WIDTH_32R"); ++ dump_bcrm_reg_32(client, BCRM_SENSOR_HEIGHT_32R, "BCRM_SENSOR_HEIGHT_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_WIDTH_MAX_32R, "BCRM_WIDTH_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_HEIGHT_MAX_32R, "BCRM_HEIGHT_MAX_32R"); ++ ++ /* Brightness Control Registers */ ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_TIME_64RW, "BCRM_EXPOSURE_TIME_64RW"); ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_TIME_MIN_64R, "BCRM_EXPOSURE_TIME_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_TIME_MAX_64R, "BCRM_EXPOSURE_TIME_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_TIME_INC_64R, "BCRM_EXPOSURE_TIME_INC_64R"); ++ dump_bcrm_reg_8(client, BCRM_EXPOSURE_AUTO_8RW, "BCRM_EXPOSURE_AUTO_8RW"); ++ ++ dump_bcrm_reg_8(client, BCRM_INTENSITY_AUTO_PRECEDENCE_8RW, "BCRM_INTENSITY_AUTO_PRECEDENCE_8RW"); ++ dump_bcrm_reg_32(client, BCRM_INTENSITY_AUTO_PRECEDENCE_VALUE_32RW, "BCRM_INTENSITY_AUTO_PRECEDENCE_VALUE_32RW"); ++ dump_bcrm_reg_32(client, BCRM_INTENSITY_AUTO_PRECEDENCE_MIN_32R, "BCRM_INTENSITY_AUTO_PRECEDENCE_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_INTENSITY_AUTO_PRECEDENCE_MAX_32R, "BCRM_INTENSITY_AUTO_PRECEDENCE_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_INTENSITY_AUTO_PRECEDENCE_INC_32R, "BCRM_INTENSITY_AUTO_PRECEDENCE_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_BLACK_LEVEL_32RW, "BCRM_BLACK_LEVEL_32RW"); ++ dump_bcrm_reg_32(client, BCRM_BLACK_LEVEL_MIN_32R, "BCRM_BLACK_LEVEL_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_BLACK_LEVEL_MAX_32R, "BCRM_BLACK_LEVEL_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_BLACK_LEVEL_INC_32R, "BCRM_BLACK_LEVEL_INC_32R"); ++ ++ dump_bcrm_reg_64(client, BCRM_GAIN_64RW, "BCRM_GAIN_64RW"); ++ dump_bcrm_reg_64(client, BCRM_GAIN_MIN_64R, "BCRM_GAIN_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_GAIN_MAX_64R, "BCRM_GAIN_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_GAIN_INC_64R, "BCRM_GAIN_INC_64R"); ++ dump_bcrm_reg_8(client, BCRM_GAIN_AUTO_8RW, "BCRM_GAIN_AUTO_8RW"); ++ ++ dump_bcrm_reg_64(client, BCRM_GAMMA_64RW, "BCRM_GAMMA_64RW"); ++ dump_bcrm_reg_64(client, BCRM_GAMMA_MIN_64R, "BCRM_GAMMA_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_GAMMA_MAX_64R, "BCRM_GAMMA_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_GAMMA_INC_64R, "BCRM_GAMMA_INC_64R"); ++ ++ dump_bcrm_reg_32(client, BCRM_CONTRAST_VALUE_32RW, "BCRM_CONTRAST_VALUE_32RW"); ++ dump_bcrm_reg_32(client, BCRM_CONTRAST_VALUE_MIN_32R, "BCRM_CONTRAST_VALUE_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_CONTRAST_VALUE_MAX_32R, "BCRM_CONTRAST_VALUE_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_CONTRAST_VALUE_INC_32R, "BCRM_CONTRAST_VALUE_INC_32R"); ++ ++ /* Color Management Registers */ ++ dump_bcrm_reg_32(client, BCRM_SATURATION_32RW, "BCRM_SATURATION_32RW"); ++ dump_bcrm_reg_32(client, BCRM_SATURATION_MIN_32R, "BCRM_SATURATION_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_SATURATION_MAX_32R, "BCRM_SATURATION_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_SATURATION_INC_32R, "BCRM_SATURATION_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_HUE_32RW, "BCRM_HUE_32RW"); ++ dump_bcrm_reg_32(client, BCRM_HUE_MIN_32R, "BCRM_HUE_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_HUE_MAX_32R, "BCRM_HUE_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_HUE_INC_32R, "BCRM_HUE_INC_32R"); ++ ++ dump_bcrm_reg_64(client, BCRM_RED_BALANCE_RATIO_64RW, "BCRM_RED_BALANCE_RATIO_64RW"); ++ dump_bcrm_reg_64(client, BCRM_RED_BALANCE_RATIO_MIN_64R, "BCRM_RED_BALANCE_RATIO_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_RED_BALANCE_RATIO_MAX_64R, "BCRM_RED_BALANCE_RATIO_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_RED_BALANCE_RATIO_INC_64R, "BCRM_RED_BALANCE_RATIO_INC_64R"); ++ ++ dump_bcrm_reg_64(client, BCRM_GREEN_BALANCE_RATIO_64RW, "BCRM_GREEN_BALANCE_RATIO_64RW"); ++ dump_bcrm_reg_64(client, BCRM_GREEN_BALANCE_RATIO_MIN_64R, "BCRM_GREEN_BALANCE_RATIO_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_GREEN_BALANCE_RATIO_MAX_64R, "BCRM_GREEN_BALANCE_RATIO_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_GREEN_BALANCE_RATIO_INC_64R, "BCRM_GREEN_BALANCE_RATIO_INC_64R"); ++ ++ dump_bcrm_reg_64(client, BCRM_BLUE_BALANCE_RATIO_64RW, "BCRM_BLUE_BALANCE_RATIO_64RW"); ++ dump_bcrm_reg_64(client, BCRM_BLUE_BALANCE_RATIO_MIN_64R, "BCRM_BLUE_BALANCE_RATIO_MIN_64R"); ++ dump_bcrm_reg_64(client, BCRM_BLUE_BALANCE_RATIO_MAX_64R, "BCRM_BLUE_BALANCE_RATIO_MAX_64R"); ++ dump_bcrm_reg_64(client, BCRM_BLUE_BALANCE_RATIO_INC_64R, "BCRM_BLUE_BALANCE_RATIO_INC_64R"); ++ ++ dump_bcrm_reg_8(client, BCRM_WHITE_BALANCE_AUTO_8RW, "BCRM_WHITE_BALANCE_AUTO_8RW"); ++ ++ /* Other Registers */ ++ dump_bcrm_reg_32(client, BCRM_SHARPNESS_32RW, "BCRM_SHARPNESS_32RW"); ++ dump_bcrm_reg_32(client, BCRM_SHARPNESS_MIN_32R, "BCRM_SHARPNESS_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_SHARPNESS_MAX_32R, "BCRM_SHARPNESS_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_SHARPNESS_INC_32R, "BCRM_SHARPNESS_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_DEVICE_TEMPERATURE_32R, "BCRM_DEVICE_TEMPERATURE_32R"); ++ ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_AUTO_MIN_64RW, "BCRM_EXPOSURE_AUTO_MIN_64RW"); ++ dump_bcrm_reg_64(client, BCRM_EXPOSURE_AUTO_MAX_64RW, "BCRM_EXPOSURE_AUTO_MAX_64RW"); ++ dump_bcrm_reg_64(client, BCRM_GAIN_AUTO_MIN_64RW, "BCRM_GAIN_AUTO_MIN_64RW"); ++ dump_bcrm_reg_64(client, BCRM_GAIN_AUTO_MAX_64RW, "BCRM_GAIN_AUTO_MAX_64RW"); ++ ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_WIDTH_32RW, "BCRM_AUTO_REGION_WIDTH_32RW"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_WIDTH_MIN_32R, "BCRM_AUTO_REGION_WIDTH_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_WIDTH_MAX_32R, "BCRM_AUTO_REGION_WIDTH_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_WIDTH_INC_32R, "BCRM_AUTO_REGION_WIDTH_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_HEIGHT_32RW, "BCRM_AUTO_REGION_HEIGHT_32RW"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_HEIGHT_MIN_32R, "BCRM_AUTO_REGION_HEIGHT_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_HEIGHT_MAX_32R, "BCRM_AUTO_REGION_HEIGHT_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_HEIGHT_INC_32R, "BCRM_AUTO_REGION_HEIGHT_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_X_32RW, "BCRM_AUTO_REGION_OFFSET_X_32RW"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_X_MIN_32R, "BCRM_AUTO_REGION_OFFSET_X_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_X_MAX_32R, "BCRM_AUTO_REGION_OFFSET_X_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_X_INC_32R, "BCRM_AUTO_REGION_OFFSET_X_INC_32R"); ++ ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_Y_32RW, "BCRM_AUTO_REGION_OFFSET_Y_32RW"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_Y_MIN_32R, "BCRM_AUTO_REGION_OFFSET_Y_MIN_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_Y_MAX_32R, "BCRM_AUTO_REGION_OFFSET_Y_MAX_32R"); ++ dump_bcrm_reg_32(client, BCRM_AUTO_REGION_OFFSET_Y_INC_32R, "BCRM_AUTO_REGION_OFFSET_Y_INC_32R"); ++} ++ ++static void dump_bcrm_reg_8(struct i2c_client *client, u16 nOffset, const char *pRegName) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int status = 0; ++ u8 data = 0; ++ uint32_t nReg = 0; ++ ++ if (!priv) ++ return; ++ ++ nReg = priv->cci_reg.bcrm_addr + nOffset; ++ status = i2c_read(client, nReg, AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_8, (char *)&data); ++ ++ if (status >= 0) ++ dev_info(&client->dev, "%s (0x%04x): %u (0x%x)", pRegName, nReg, data, data); ++ else ++ dev_err(&client->dev, "%s: ERROR", pRegName); ++} ++ ++static void dump_bcrm_reg_32(struct i2c_client *client, u16 nOffset, const char *pRegName) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int status = 0; ++ u32 data = 0; ++ uint32_t nReg = 0; ++ ++ if (!priv) ++ return; ++ ++ nReg = priv->cci_reg.bcrm_addr + nOffset; ++ status = i2c_read(client, priv->cci_reg.bcrm_addr + nOffset, AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_32, (char *)&data); ++ ++ swapbytes(&data, sizeof(data)); ++ if (status >= 0) ++ dev_info(&client->dev, "%s (0x%04x): %u (0x%08x)", pRegName, nReg, data, data); ++ else ++ dev_err(&client->dev, "%s: ERROR", pRegName); ++} ++ ++static void dump_bcrm_reg_64(struct i2c_client *client, u16 nOffset, const char *pRegName) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int status = 0; ++ u64 data = 0; ++ uint32_t nReg = 0; ++ ++ if (!priv) ++ return; ++ ++ nReg = priv->cci_reg.bcrm_addr + nOffset; ++ status = i2c_read(client, priv->cci_reg.bcrm_addr + nOffset, AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_64, (char *)&data); ++ ++ swapbytes(&data, sizeof(data)); ++ if (status >= 0) ++ dev_info(&client->dev, "%s (0x%04x): %llu (0x%016llx)", pRegName, nReg, data, data); ++ else ++ dev_err(&client->dev, "%s: ERROR", pRegName); ++} ++ ++static void dump_camera_firmware_version(struct i2c_client *client) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(&client->dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ int status = 0; ++ u64 data = 0; ++ ++ if (!priv) ++ return; ++ ++ status = i2c_read(client, priv->cci_reg.bcrm_addr + BCRM_DEVICE_FIRMWARE_VERSION_64R, AV_CAM_REG_SIZE, ++ AV_CAM_DATA_SIZE_64, (char *)&data); ++ ++ swapbytes(&data, sizeof(data)); ++ ++ if (status >= 0) ++ if ((u32)((data >> 32) & 0xFFFFFFFF) < 50000) ++ { ++ dev_info(&client->dev, "Camera firmware version: %u.%u.%hu.%u (0x%016llx)", (u8)(data & 0xFF), ++ (u8)((data >> 8) & 0xFF), ++ (u16)((data >> 16) & 0xFFFF), ++ (u32)((data >> 32) & 0xFFFFFFFF), data); ++ } ++ else ++ { ++ /* Show git commit as hex */ ++ dev_info(&client->dev, "Camera firmware version: %u.%u.%hu.%x (0x%016llx)", (u8)(data & 0xFF), ++ (u8)((data >> 8) & 0xFF), ++ (u16)((data >> 16) & 0xFFFF), ++ (u32)((data >> 32) & 0xFFFFFFFF), data); ++ } ++ else ++ { ++ dev_err(&client->dev, "Error while retrieving camera firmware version"); ++ } ++} ++ ++/* Check if the device is answering to an I2C read request */ ++static bool device_present(struct i2c_client *client) ++{ ++ int status = 0; ++ u64 data = 0; ++ ++ status = i2c_read(client, CCI_DEVICE_CAP_64R, AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, (char *)&data); ++ ++ return ((status < 0) || (data == 0)) ? false : true; ++} ++ ++static int soft_reset(struct i2c_client *client) ++{ ++ int status = 0; ++ uint8_t reset_val = 1; ++ static const uint8_t default_heartbeat_val = 0x80; ++ uint8_t heartbeat_val = default_heartbeat_val; ++ uint64_t duration_ms = 0; ++ static const uint8_t heartbeat_low_limit = 0; ++ static const uint32_t delay_ms = 400; ++ static const uint32_t max_time_ms = 10000; ++ uint64_t start_jiffies = get_jiffies_64(); ++ bool device_available = false; ++ bool heartbeat_available = false; ++ ++ /* Check, if heartbeat register is available (write default value and read it back)*/ ++ status = i2c_write(client, CCI_HEARTBEAT_8RW, AV_CAM_REG_SIZE, sizeof(heartbeat_val), (char*)&heartbeat_val); ++ heartbeat_available = (i2c_read(client, CCI_HEARTBEAT_8RW, AV_CAM_REG_SIZE, sizeof(heartbeat_val), (char*)&heartbeat_val) < 0) ? false : true; ++ /* If camera does not support heartbeat it delivers always 0 */ ++ heartbeat_available = ((heartbeat_val != 0) && (status != 0)) ? true : false; ++ dev_info(&client->dev, "Heartbeat %ssupported", (heartbeat_available) ? "" : "NOT "); ++ ++ /* Execute soft reset */ ++ status = i2c_write(client, CCI_SOFT_RESET_8W, AV_CAM_REG_SIZE, sizeof(reset_val), (char*)&reset_val); ++ ++ if (status >= 0) ++ { ++ dev_info(&client->dev, "Soft reset executed. Initializing camera..."); ++ } ++ else ++ { ++ dev_err(&client->dev, "Soft reset ERROR"); ++ return -EIO; ++ } ++ ++ /* Poll camera register to check if camera is back again */ ++ do ++ { ++ usleep_range(delay_ms*1000, (delay_ms*1000)+1); ++ device_available = device_present(client); ++ duration_ms = jiffies_to_msecs(get_jiffies_64() - start_jiffies); ++ } while((duration_ms < max_time_ms) && !device_available); ++ ++ if (!heartbeat_available) ++ { ++ /* Camera might need a few more seconds to be fully booted */ ++ usleep_range(add_wait_time_ms*1000, (add_wait_time_ms*1000)+1); ++ } ++ else ++ { ++ /* Heartbeat is supported. Poll heartbeat register until value is lower than the default value again */ ++ do ++ { ++ usleep_range(delay_ms*1000, (delay_ms*1000)+1); ++ status = i2c_read(client, CCI_HEARTBEAT_8RW, AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, (char*)&heartbeat_val); ++ //dev_info(&client->dev, "Heartbeat val=0x%02X", heartbeat_val); ++ duration_ms = jiffies_to_msecs(get_jiffies_64() - start_jiffies); ++ if ((heartbeat_val > heartbeat_low_limit) && (heartbeat_val < default_heartbeat_val) && (status >= 0)) ++ { ++ /* Heartbeat active -> Camera alive */ ++ dev_info(&client->dev, "Heartbeat active!"); ++ break; ++ } ++ } while (duration_ms < max_time_ms); ++ } ++ ++ dev_info(&client->dev, "Camera boot time: %llums", duration_ms); ++ if (!device_available) ++ dev_err(&client->dev, "Camera not reconnected"); ++ ++ return 0; ++} ++ ++static ssize_t cci_register_layout_version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%d\n", priv->cci_reg.layout_version); ++} ++ ++static ssize_t csi_clock_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%d\n", priv->csi_clk_freq); ++} ++ ++static ssize_t device_capabilities_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%llu\n", priv->cci_reg.device_capabilities); ++} ++ ++static ssize_t device_guid_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.device_guid); ++} ++ ++static ssize_t manufacturer_name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.manufacturer_name); ++} ++ ++static ssize_t model_name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.model_name); ++} ++ ++static ssize_t family_name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.family_name); ++} ++ ++static ssize_t lane_count_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%d\n", priv->s_data->numlanes); ++} ++ ++static ssize_t device_version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.device_version); ++} ++ ++static ssize_t manufacturer_info_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.manufacturer_info); ++} ++ ++static ssize_t serial_number_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.serial_number); ++} ++ ++static ssize_t user_defined_name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct camera_common_data *s_data = to_camera_common_data(dev); ++ struct avt_csi2_priv *priv = (struct avt_csi2_priv *)s_data->priv; ++ ++ return sprintf(buf, "%s\n", priv->cci_reg.user_defined_name); ++} ++ ++static ssize_t driver_version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d.%d.%d.%d\n", ++ DRV_VER_MAJOR, DRV_VER_MINOR, DRV_VER_PATCH, DRV_VER_BUILD); ++} ++ ++static ssize_t debug_en_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", debug); ++} ++ ++static ssize_t debug_en_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ int ret; ++ ++ ret = kstrtoint(buf, 10, &debug); ++ if (ret < 0) ++ return ret; ++ ++ return count; ++} ++ ++static DEVICE_ATTR_RO(cci_register_layout_version); ++static DEVICE_ATTR_RO(csi_clock); ++static DEVICE_ATTR_RO(device_capabilities); ++static DEVICE_ATTR_RO(device_guid); ++static DEVICE_ATTR_RO(device_version); ++static DEVICE_ATTR_RO(driver_version); ++static DEVICE_ATTR_RO(family_name); ++static DEVICE_ATTR_RO(lane_count); ++static DEVICE_ATTR_RO(manufacturer_info); ++static DEVICE_ATTR_RO(manufacturer_name); ++static DEVICE_ATTR_RO(model_name); ++static DEVICE_ATTR_RO(serial_number); ++static DEVICE_ATTR_RO(user_defined_name); ++static DEVICE_ATTR_RW(debug_en); ++ ++static struct attribute *avt_csi2_attrs[] = { ++ &dev_attr_cci_register_layout_version.attr, ++ &dev_attr_csi_clock.attr, ++ &dev_attr_device_capabilities.attr, ++ &dev_attr_device_guid.attr, ++ &dev_attr_device_version.attr, ++ &dev_attr_driver_version.attr, ++ &dev_attr_family_name.attr, ++ &dev_attr_lane_count.attr, ++ &dev_attr_manufacturer_info.attr, ++ &dev_attr_manufacturer_name.attr, ++ &dev_attr_model_name.attr, ++ &dev_attr_serial_number.attr, ++ &dev_attr_user_defined_name.attr, ++ &dev_attr_debug_en.attr, ++ NULL ++}; ++ ++static struct attribute_group avt_csi2_attr_grp = { ++ .attrs = avt_csi2_attrs, ++}; ++ ++static bool common_range(uint32_t nMin1, uint32_t nMax1, uint32_t nInc1, ++ uint32_t nMin2, uint32_t nMax2, uint32_t nInc2, ++ uint32_t *rMin, uint32_t *rMax, uint32_t *rInc) ++{ ++ bool bResult = false; ++ ++ uint32_t nMin = max(nMin1, nMin2); ++ uint32_t nMax = min(nMax1, nMax2); ++ ++ /* Check if it is overlapping at all */ ++ if (nMax >= nMin) { ++ /* if both minima are equal, ++ * then the computation is a bit simpler ++ */ ++ if (nMin1 == nMin2) { ++ uint32_t nLCM = lcm(nInc1, nInc2); ++ *rMin = nMin; ++ *rMax = nMax - ((nMax - nMin) % nLCM); ++ ++ if (*rMin == *rMax) ++ *rInc = 1; ++ else ++ *rInc = nLCM; ++ ++ bResult = true; ++ } else if (nMin1 > nMin2) { ++ /* Find the first value that is ok for Host and BCRM */ ++ uint32_t nMin1Shifted = nMin1 - nMin2; ++ uint32_t nMaxShifted = nMax - nMin2; ++ uint32_t nValue = nMin1Shifted; ++ ++ for (; nValue <= nMaxShifted; nValue += nInc1) { ++ if ((nValue % nInc2) == 0) ++ break; ++ } ++ ++ /* Compute common increment and maximum */ ++ if (nValue <= nMaxShifted) { ++ uint32_t nLCM = lcm(nInc1, nInc2); ++ *rMin = nValue + nMin2; ++ *rMax = nMax - ((nMax - *rMin) % nLCM); ++ ++ if (*rMin == *rMax) ++ *rInc = 1; ++ else ++ *rInc = nLCM; ++ ++ bResult = true; ++ } ++ } else { ++ /* Find the first value that is ok for Host and BCRM */ ++ uint32_t nMin2Shifted = nMin2 - nMin1; ++ uint32_t nMaxShifted = nMax - nMin1; ++ uint32_t nValue = nMin2Shifted; ++ ++ for (; nValue <= nMaxShifted; nValue += nInc2) { ++ if ((nValue % nInc1) == 0) ++ break; ++ } ++ ++ /* Compute common increment and maximum */ ++ if (nValue <= nMaxShifted) { ++ uint32_t nLCM = lcm(nInc2, nInc1); ++ *rMin = nValue + nMin1; ++ *rMax = nMax - ((nMax - *rMin) % nLCM); ++ if (*rMin == *rMax) ++ *rInc = 1; ++ else ++ *rInc = nLCM; ++ ++ bResult = true; ++ } ++ } ++ } ++ ++ return bResult; ++} ++ ++static void dump_frame_param(struct v4l2_subdev *sd) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ avt_dbg(sd, "\n"); ++ avt_dbg(sd, "priv->frmp.minh=%d\n", priv->frmp.minh); ++ avt_dbg(sd, "priv->frmp.maxh=%d\n", priv->frmp.maxh); ++ avt_dbg(sd, "priv->frmp.sh=%d\n", priv->frmp.sh); ++ avt_dbg(sd, "priv->frmp.minw=%d\n", priv->frmp.minw); ++ avt_dbg(sd, "priv->frmp.maxw=%d\n", priv->frmp.maxw); ++ avt_dbg(sd, "priv->frmp.sw=%d\n", priv->frmp.sw); ++ avt_dbg(sd, "priv->frmp.minhoff=%d\n", priv->frmp.minhoff); ++ avt_dbg(sd, "priv->frmp.maxhoff=%d\n", priv->frmp.maxhoff); ++ avt_dbg(sd, "priv->frmp.shoff=%d\n", priv->frmp.shoff); ++ avt_dbg(sd, "priv->frmp.minwoff=%d\n", priv->frmp.minwoff); ++ avt_dbg(sd, "priv->frmp.maxwoff=%d\n", priv->frmp.maxwoff); ++ avt_dbg(sd, "priv->frmp.swoff=%d\n", priv->frmp.swoff); ++ avt_dbg(sd, "priv->frmp.r.width=%d\n", priv->frmp.r.width); ++ avt_dbg(sd, "priv->frmp.r.height=%d\n", priv->frmp.r.height); ++ avt_dbg(sd, "priv->frmp.r.left=%d\n", priv->frmp.r.left); ++ avt_dbg(sd, "priv->frmp.r.top=%d\n", priv->frmp.r.top); ++} ++ ++static int avt_init_frame_param(struct v4l2_subdev *sd) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ dump_frame_param(sd); ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_HEIGHT_MINVAL_R, ++ &priv->frmp.minh)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_HEIGHT_MAXVAL_R, ++ &priv->frmp.maxh)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_HEIGHT_INCVAL_R, ++ &priv->frmp.sh)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_WIDTH_MINVAL_R, ++ &priv->frmp.minw)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_WIDTH_MAXVAL_R, ++ &priv->frmp.maxw)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_WIDTH_INCVAL_R, ++ &priv->frmp.sw)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_Y_MIN_R, ++ &priv->frmp.minhoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_Y_MAX_R, ++ &priv->frmp.maxhoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_Y_INC_R, ++ &priv->frmp.shoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_X_MIN_R, ++ &priv->frmp.minwoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_X_MAX_R, ++ &priv->frmp.maxwoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_X_INC_R, ++ &priv->frmp.swoff)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_WIDTH_R, ++ &priv->frmp.r.width)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_HEIGHT_R, ++ &priv->frmp.r.height)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_X_R, ++ &priv->frmp.r.left)) ++ return -EINVAL; ++ ++ if (avt_get_param(priv->client, V4L2_AV_CSI2_OFFSET_Y_R, ++ &priv->frmp.r.top)) ++ return -EINVAL; ++ ++ /* We might need to correct some values */ ++ /* Tegra doesn't seem to accept offsets that are not divisible by 8. */ ++ roundup(priv->frmp.swoff, OFFSET_INC_W); ++ roundup(priv->frmp.shoff, OFFSET_INC_H); ++ /* Tegra doesn't allow image resolutions smaller than 64x32 */ ++ priv->frmp.minw = max_t(uint32_t, priv->frmp.minw, FRAMESIZE_MIN_W); ++ priv->frmp.minh = max_t(uint32_t, priv->frmp.minh, FRAMESIZE_MIN_H); ++ ++ priv->frmp.maxw = min_t(uint32_t, priv->frmp.maxw, FRAMESIZE_MAX_W); ++ priv->frmp.maxh = min_t(uint32_t, priv->frmp.maxh, FRAMESIZE_MAX_H); ++ ++ //max_height = rounddown(max_height, FRAMESIZE_INC_H); ++ ++ /* Take care of image width alignment*/ ++ if (priv->crop_align_enabled) { ++ priv->frmp.maxwoff = avt_align_width(sd, priv->frmp.maxwoff, priv->frmp.maxw,priv->mbus_fmt_code); ++ ++ priv->frmp.maxw = avt_align_width(sd, priv->frmp.maxw, priv->frmp.maxw,priv->mbus_fmt_code); ++ } ++ ++ dump_frame_param(sd); ++ return 0; ++} ++ ++/* Read image format from camera, ++ * should be only called once, during initialization ++ * */ ++static int avt_read_fmt_from_device(struct v4l2_subdev *sd, uint32_t *fmt) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct i2c_client *client = priv->client; ++ uint32_t avt_img_fmt = 0; ++ uint8_t bayer_pattern; ++ int ret = 0; ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_IMG_BAYER_PATTERN_8RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &bayer_pattern); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ dev_dbg(&client->dev, "Camera bayer_pattern=0x%X", bayer_pattern); ++ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_IMG_MIPI_DATA_FORMAT_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_img_fmt); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ dev_dbg(&client->dev, "BCRM_IMG_MIPI_DATA_FORMAT_32RW=0x%08X\n", avt_img_fmt); ++ ++ switch (avt_img_fmt) { ++ case MIPI_DT_RGB888: ++ avt_img_fmt = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ case MIPI_DT_RGB565: ++ avt_img_fmt = MEDIA_BUS_FMT_RGB565_1X16; ++ break; ++ case MIPI_DT_YUV422: ++ avt_img_fmt = MEDIA_BUS_FMT_VYUY8_2X8; ++ break; ++ case MIPI_DT_CUSTOM: ++ avt_img_fmt = MEDIA_BUS_FMT_CUSTOM; ++ break; ++ case MIPI_DT_RAW8: ++ switch (bayer_pattern) { ++ case monochrome: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_Y8_1X8; ++ break; ++ case bayer_gr: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGRBG8_1X8; ++ break; ++ case bayer_rg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SRGGB8_1X8; ++ break; ++ case bayer_gb: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGBRG8_1X8; ++ break; ++ case bayer_bg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SBGGR8_1X8; ++ break; ++ default: ++ dev_err(&client->dev, "%s:Unknown RAW8 pixelformat read, bayer_pattern %d\n", ++ __func__, ++ bayer_pattern); ++ return -EINVAL; ++ } ++ break; ++ case MIPI_DT_RAW10: ++ switch (bayer_pattern) { ++ case monochrome: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_Y10_1X10; ++ break; ++ case bayer_gr: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGRBG10_1X10; ++ break; ++ case bayer_rg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SRGGB10_1X10; ++ break; ++ case bayer_gb: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGBRG10_1X10; ++ break; ++ case bayer_bg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SBGGR10_1X10; ++ break; ++ default: ++ dev_err(&client->dev, "%s:Unknown RAW10 pixelformat read, bayer_pattern %d\n", ++ __func__, ++ bayer_pattern); ++ return -EINVAL; ++ } ++ break; ++ case MIPI_DT_RAW12: ++ switch (bayer_pattern) { ++ case monochrome: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_Y12_1X12; ++ break; ++ case bayer_gr: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGRBG12_1X12; ++ break; ++ case bayer_rg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SRGGB12_1X12; ++ break; ++ case bayer_gb: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SGBRG12_1X12; ++ break; ++ case bayer_bg: ++ avt_img_fmt = ++ MEDIA_BUS_FMT_SBGGR12_1X12; ++ break; ++ default: ++ dev_err(&client->dev, "%s:Unknown RAW12 pixelformat read, bayer_pattern %d\n", ++ __func__, ++ bayer_pattern); ++ return -EINVAL; ++ } ++ break; ++ ++ case 0: ++ /* Pixelformat 0 -> Probably fallback app running -> Emulate RAW888 */ ++ avt_img_fmt = MEDIA_BUS_FMT_RGB888_1X24; ++ dev_warn(&client->dev, "Invalid pixelformat detected (0). Fallback app running?"); ++ break; ++ ++ default: ++ dev_err(&client->dev, "%s:Unknown pixelformat read, avt_img_fmt 0x%x\n", ++ __func__, avt_img_fmt); ++ return -EINVAL; ++ } ++ ++ *fmt = avt_img_fmt; ++ ++ return 0; ++} ++ ++static int avt_init_binning(struct v4l2_subdev *sd) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ struct device *dev = &priv->client->dev; ++ int ret,i,j, binning_count = 1; ++ uint32_t width, height; ++ uint16_t binning_inquiry; ++ uint8_t setting; ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_INQ_16R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &binning_inquiry); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_dbg(sd,"Binning inquiry: %d\n",binning_inquiry); ++ ++ for (i = 0;i < 7;i++) { ++ if (binning_inquiry & (1 << i)) { ++ avt_dbg(sd,"Active binning: %d\n",i); ++ binning_count++; ++ } ++ } ++ ++ priv->available_binnings = devm_kzalloc(dev,sizeof(struct avt_binning_config) * binning_count,GFP_KERNEL); ++ priv->available_binnings_cnt = binning_count; ++ ++ ++ for (i = 0,j = 1;i < 7;i++) { ++ if (binning_inquiry & (1 << i)) { ++ priv->available_binnings[j].setting = i + 1; ++ j++; ++ } ++ } ++ ++ ++ for (i = 0; i < binning_count; i++) { ++ setting = priv->available_binnings[i].setting; ++ ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_SETTING_8RW, ++ AV_CAM_REG_SIZE,AV_CAM_DATA_SIZE_8, &setting); ++ if (ret < 0) { ++ avt_err(sd, "i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_WIDTH_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &width); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_HEIGHT_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &height); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ priv->available_binnings[i].width = width; ++ priv->available_binnings[i].height = height; ++ } ++ ++ ++ //Initial turn binning off ++ priv->cur_binning_config = 0; ++ ++ setting = priv->available_binnings[priv->cur_binning_config].setting; ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, priv->cci_reg.bcrm_addr + BCRM_DIGITAL_BINNIG_SETTING_8RW, ++ AV_CAM_REG_SIZE,AV_CAM_DATA_SIZE_8, &setting); ++ if (ret < 0) { ++ avt_err(sd, "i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ //Set framesize ++ priv->frmp.r.width = priv->available_binnings[priv->cur_binning_config].width; ++ priv->frmp.r.height = priv->available_binnings[priv->cur_binning_config].height; ++ ++ ++ return 0; ++} ++ ++static int avt_init_mode(struct v4l2_subdev *sd) ++{ ++ struct avt_csi2_priv *priv = avt_get_priv(sd); ++ int ret = 0; ++ uint32_t common_min_clk = 0; ++ uint32_t common_max_clk = 0; ++ uint32_t common_inc_clk = 0; ++ uint32_t avt_min_clk = 0; ++ uint32_t avt_max_clk = 0; ++ uint8_t avt_supported_lane_counts = 0; ++ ++ uint32_t i2c_reg; ++ uint32_t i2c_reg_size; ++ uint32_t i2c_reg_count; ++ uint32_t clk; ++ uint8_t bcm_mode = 0; ++ ++ char *i2c_reg_buf; ++ struct v4l2_subdev_selection sel; ++ ++ /* Check if requested number of lanes is supported */ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_SUPPORTED_CSI2_LANE_COUNTS_8R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_8, ++ (char *) &avt_supported_lane_counts); ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_info(sd, "Camera supported lane counts value: 0x%x\n", avt_supported_lane_counts); ++ ++ if (!priv->fallback_app_running) ++ { ++ uint32_t requested_lanes = (priv->csi_fixed_lanes > 0) ? priv->csi_fixed_lanes : priv->s_data->numlanes; ++ if (priv->csi_fixed_lanes > 0) { ++ avt_info(sd, "Lane count overridden in device tree: %u\n", requested_lanes); ++ } ++ ++ if(!(test_bit(requested_lanes - 1, (const long *)(&avt_supported_lane_counts)))) { ++ avt_err(sd, "requested number of lanes (%u) not supported by this camera!\n", ++ requested_lanes); ++ return -EINVAL; ++ } ++ ++ /* Set number of lanes */ ++ ret = avt_reg_write(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_LANE_COUNT_8RW, ++ requested_lanes); ++ if (ret < 0) { ++ avt_err(sd, "i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ ++ priv->numlanes = requested_lanes; ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_MIN_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_min_clk); ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_MAX_32R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_max_clk); ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_dbg(sd, "csi clock camera range: %d:%d Hz, host range: %d:%d Hz\n", ++ avt_min_clk, avt_max_clk, ++ CSI_HOST_CLK_MIN_FREQ, CSI_HOST_CLK_MAX_FREQ); ++ ++ if (common_range(avt_min_clk, avt_max_clk, 1, ++ CSI_HOST_CLK_MIN_FREQ, CSI_HOST_CLK_MAX_FREQ, 1, ++ &common_min_clk, &common_max_clk, &common_inc_clk) ++ == false) { ++ avt_err(sd, "no common clock range for camera and host possible!\n"); ++ return -EINVAL; ++ } ++ ++ avt_dbg(sd, "camera/host common csi clock range: %d:%d Hz\n", ++ common_min_clk, common_max_clk); ++ ++ priv->host_csi_clk_freq = common_max_clk; ++ ++ if (priv->csi_clk_freq == 0) { ++ avt_dbg(sd, "no csi clock requested, using common max (%d Hz)\n", ++ common_max_clk); ++ priv->csi_clk_freq = common_max_clk; ++ } else { ++ avt_dbg(sd, "using csi clock from dts: %u Hz\n", ++ priv->csi_clk_freq); ++ } ++ ++ if ((priv->csi_clk_freq < common_min_clk) || ++ (priv->csi_clk_freq > common_max_clk)) { ++ avt_err(sd, "unsupported csi clock frequency (%d Hz, range: %d:%d Hz)!\n", ++ priv->csi_clk_freq, common_min_clk, ++ common_max_clk); ++ return -EINVAL; ++ } ++ ++ CLEAR(i2c_reg); ++ clk = priv->csi_clk_freq; ++ swapbytes(&clk, AV_CAM_DATA_SIZE_32); ++ i2c_reg = priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_32RW; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = AV_CAM_DATA_SIZE_32; ++ i2c_reg_buf = (char *) &clk; ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ ++ ret = avt_reg_read(priv->client, ++ priv->cci_reg.bcrm_addr + BCRM_CSI2_CLOCK_32RW, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_32, ++ (char *) &avt_max_clk); ++ ++ if (ret < 0) { ++ avt_err(sd, "i2c read failed (%d)\n", ret); ++ return ret; ++ } ++ ++ avt_dbg(sd, "csi clock read from camera: %d Hz\n", avt_max_clk); ++ } ++ ++ ret = avt_read_fmt_from_device(sd, &(priv->mbus_fmt_code)); ++ ++ if (ret < 0) ++ return ret; ++ ++ ret = avt_init_frame_param(sd); ++ ++ if (ret < 0) ++ return ret; ++ ++ ret = avt_init_binning(sd); ++ ++ if(ret < 0) ++ return ret; ++ ++ sel.target = V4L2_SEL_TGT_CROP; ++ sel.r = priv->frmp.r; ++ ret = avt_set_selection(sd, NULL, &sel); ++ if (ret < 0) ++ return ret; ++ ++ // set BCRM mode ++ CLEAR(i2c_reg); ++ i2c_reg = CCI_CHANGE_MODE_8W; ++ i2c_reg_size = AV_CAM_REG_SIZE; ++ i2c_reg_count = AV_CAM_DATA_SIZE_8; ++ i2c_reg_buf = (char *) &bcm_mode; ++ ++ ret = ioctl_gencam_i2cwrite_reg(priv->client, ++ i2c_reg, i2c_reg_size, ++ i2c_reg_count, i2c_reg_buf); ++ if (ret < 0) { ++ avt_err(sd, "Failed to set BCM mode: i2c write failed (%d)\n", ret); ++ return ret; ++ } ++ priv->mode = AVT_BCRM_MODE; ++ ++ ++ return 0; ++} ++ ++static int avt_initialize_controls(struct i2c_client *client, struct avt_csi2_priv *priv) { ++ struct v4l2_queryctrl qctrl; ++ struct v4l2_query_ext_ctrl qctrl_ext; ++ struct v4l2_ctrl *ctrl; ++ int j, i, ret; ++ ++ v4l2_ctrl_handler_init(&priv->hdl, ARRAY_SIZE(avt_ctrl_mappings)); ++ ++ for (i = 0, j = 0; j < ARRAY_SIZE(avt_ctrl_mappings); ++j) { ++ CLEAR(qctrl); ++ CLEAR(qctrl_ext); ++ qctrl.id = avt_ctrl_mappings[j].id; ++ qctrl_ext.id = avt_ctrl_mappings[j].id; ++ if(avt_ctrl_mappings[j].data_size == AV_CAM_DATA_SIZE_64) { ++ ret = ioctl_queryctrl64(priv->subdev, &qctrl_ext); ++ if (ret < 0) { ++ continue; ++ } ++ ++ dev_dbg(&client->dev, "Checking caps: %s - Range: %lld-%lld s: %llu d: %lld - %sabled\n", ++ avt_ctrl_mappings[j].attr.name, ++ qctrl_ext.minimum, ++ qctrl_ext.maximum, ++ qctrl_ext.step, ++ qctrl_ext.default_value, ++ (qctrl_ext.flags & V4L2_CTRL_FLAG_DISABLED) ? ++ "dis" : "en"); ++ if (qctrl_ext.flags & V4L2_CTRL_FLAG_DISABLED) { ++ continue; ++ } ++ ++ priv->ctrl_cfg[i].type = qctrl_ext.type; ++ ++ priv->ctrl_cfg[i].min = qctrl_ext.minimum; ++ priv->ctrl_cfg[i].max = qctrl_ext.maximum; ++ priv->ctrl_cfg[i].def = qctrl_ext.default_value; ++ priv->ctrl_cfg[i].step = qctrl_ext.step; ++ priv->ctrl_cfg[i].flags = qctrl_ext.flags; ++ ++ if (qctrl_ext.type == V4L2_CTRL_TYPE_INTEGER64) { ++ priv->ctrl_cfg[i].flags |= V4L2_CTRL_FLAG_SLIDER; ++ } ++ ++ } else { ++ ret = ioctl_queryctrl(priv->subdev, &qctrl); ++ if (ret < 0) { ++ continue; ++ } ++ ++ dev_dbg(&client->dev, "Checking caps: %s - Range: %d-%d s: %d d: %d - %sabled\n", ++ avt_ctrl_mappings[j].attr.name, ++ qctrl.minimum, ++ qctrl.maximum, ++ qctrl.step, ++ qctrl.default_value, ++ (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) ? ++ "dis" : "en"); ++ ++ if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) { ++ continue; ++ } ++ ++ priv->ctrl_cfg[i].type = qctrl.type; ++ priv->ctrl_cfg[i].min = qctrl.minimum; ++ priv->ctrl_cfg[i].max = qctrl.maximum; ++ priv->ctrl_cfg[i].def = qctrl.default_value; ++ priv->ctrl_cfg[i].step = qctrl.step; ++ priv->ctrl_cfg[i].flags = qctrl.flags; ++ ++ if ((qctrl.type == V4L2_CTRL_TYPE_INTEGER) ++ && !(qctrl.flags & V4L2_CTRL_FLAG_VOLATILE)) { ++ priv->ctrl_cfg[i].flags |= V4L2_CTRL_FLAG_SLIDER; ++ } ++ } ++ ++ priv->ctrl_cfg[i].ops = &avt_ctrl_ops; ++ priv->ctrl_cfg[i].name = avt_ctrl_mappings[j].attr.name; ++ priv->ctrl_cfg[i].id = avt_ctrl_mappings[j].id; ++ priv->hdl.error = 0; ++ ++ if (priv->ctrl_cfg[i].id == V4L2_CID_TRIGGER_ACTIVATION) ++ { ++ priv->ctrl_cfg[i].qmenu = v4l2_triggeractivation_menu; ++ priv->ctrl_cfg[i].menu_skip_mask = 0; ++ } ++ ++ if (priv->ctrl_cfg[i].id == V4L2_CID_TRIGGER_SOURCE) ++ { ++ priv->ctrl_cfg[i].qmenu = v4l2_triggersource_menu; ++ priv->ctrl_cfg[i].menu_skip_mask = 0; ++ } ++ ++ if (priv->ctrl_cfg[i].id == V4L2_CID_BINNING_MODE) ++ { ++ priv->ctrl_cfg[i].qmenu = v4l2_binning_mode_menu; ++ priv->ctrl_cfg[i].menu_skip_mask = 0; ++ } ++ ++ ctrl = v4l2_ctrl_new_custom(&priv->hdl, &priv->ctrl_cfg[i], (void *)&avt_ctrl_mappings[j]); ++ ++ if (ctrl == NULL) { ++ dev_err(&client->dev, "Failed to init %s ctrl (%d)\n", priv->ctrl_cfg[i].name, priv->hdl.error); ++ continue; ++ } ++ ++ priv->ctrls[i] = ctrl; ++ i++; ++ } ++ ++ for (j = 0; j < ARRAY_SIZE(avt_tegra_ctrl); ++j, ++i) { ++ struct v4l2_ctrl_config config = avt_tegra_ctrl[j]; ++ ++ if (config.id == V4L2_CID_LINK_FREQ) { ++ ++ config.max = ARRAY_SIZE(priv->link_freqs) - 1; ++ ++ priv->link_freqs[0] = priv->host_csi_clk_freq; ++ ++ config.qmenu_int = priv->link_freqs; ++ } ++ ++ ctrl = v4l2_ctrl_new_custom(&priv->hdl, &config, NULL); ++ ++ if (ctrl == NULL) { ++ dev_err(&client->dev, "Failed to init %s ctrl\n",config.name); ++ continue; ++ } ++ ++ priv->ctrls[i] = ctrl; ++ } ++ ++ return i; ++} ++ ++ ++static int avt_csi2_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct avt_csi2_priv *priv; ++ struct device *dev = &client->dev; ++ int ret; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ struct v4l2_of_endpoint *endpoint; ++ struct device_node *ep; ++#endif //#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ struct camera_common_data *common_data; ++ union cci_device_caps_reg device_caps; ++ union bcrm_feature_reg feature_inquiry_reg; ++ ++ v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ common_data = devm_kzalloc(&client->dev, sizeof(struct camera_common_data), GFP_KERNEL); ++ if (!common_data) { ++ return -ENOMEM; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct avt_csi2_priv), GFP_KERNEL); ++ if (!priv) { ++ return -ENOMEM; ++ } ++ ++ atomic_set(&priv->open_count,0); ++ priv->subdev = &common_data->subdev; ++ priv->subdev->ctrl_handler = &priv->hdl; ++ priv->client = client; ++ priv->s_data = common_data; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ ep = of_graph_get_next_endpoint(dev->of_node, NULL); ++ if (!ep) { ++ dev_err(dev, "missing endpoint node\n"); ++ return -EINVAL; ++ } ++ ++ endpoint = v4l2_of_alloc_parse_endpoint(ep); ++ if (IS_ERR(endpoint)) { ++ dev_err(dev, "failed to parse endpoint\n"); ++ return PTR_ERR(endpoint); ++ } ++#endif //#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) ++ ++ v4l2_i2c_subdev_init(priv->subdev, client, &avt_csi2_subdev_ops); ++ ++ priv->subdev->internal_ops = &avt_csi2_int_ops; ++ priv->subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; ++ priv->subdev->dev = &client->dev; ++ ++ /* Set owner to NULL so we can unload the driver module */ ++ priv->subdev->owner = NULL; ++ ++ common_data->priv = priv; ++ common_data->dev = &client->dev; ++ common_data->ctrl_handler = &priv->hdl; ++ common_data->ctrls = priv->ctrls; ++ ++ atomic_set(&priv->force_value_update, 0); ++ priv->value_update_interval = 1000; ++ init_waitqueue_head(&priv->value_update_wq); ++ ++ ++ priv->streamcap.capability = V4L2_CAP_TIMEPERFRAME; ++ priv->streamcap.capturemode = 0; ++ priv->streamcap.timeperframe.denominator = DEFAULT_FPS; ++ priv->streamcap.timeperframe.numerator = 1; ++ priv->streamcap.readbuffers = 1; ++ ++ if (!device_present(client)) { ++ dev_err(dev, "No camera detected (driver V%s)", DRIVER_VERSION); ++ return -ENXIO; ++ } else { ++ dev_info(dev, "Camera detected! (driver V%s)", DRIVER_VERSION); ++ } ++ ++ /* Execute softreset to ensure camera is not in GenCP mode anymore */ ++ ret = soft_reset(client); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = read_cci_registers(client); ++ dump_camera_firmware_version(client); ++ ++ /* Check if camera is running fallback app */ ++ priv->fallback_app_running = is_fallback_app_running(client); ++ ++ /* DEBUG: Dump all BCRM registers */ ++ bcrm_dump(client); ++ ++ /* Set subdev name */ ++ snprintf(priv->subdev->name, sizeof(priv->subdev->name), "%s %s %s%d-%x", ++ priv->cci_reg.family_name, priv->cci_reg.model_name, ++ priv->fallback_app_running ? "FB " : "", ++ i2c_adapter_id(client->adapter), client->addr); ++ ++ if (ret < 0) { ++ dev_err(dev, "%s: read_cci_registers failed: %d\n", __func__, ret); ++ return -EIO; ++ } ++ ++ ret = cci_version_check(client); ++ if (ret < 0) { ++ dev_err(&client->dev, "cci version mismatch!\n"); ++ return -EINVAL; ++ } ++ ++ ret = bcrm_version_check(client); ++ if (ret < 0) { ++ dev_err(&client->dev, "bcrm version mismatch!\n"); ++ return -EINVAL; ++ } ++ ++ dev_dbg(&client->dev, "correct bcrm version\n"); ++ ++ priv->write_handshake_available = bcrm_get_write_handshake_availibility(client); ++ ++ avt_init_avail_formats(priv->subdev); ++ ++ device_caps.value = priv->cci_reg.device_capabilities; ++ ++ if (device_caps.caps.gencp) { ++ ret = read_gencp_registers(client); ++ if (ret < 0) { ++ dev_err(dev, "%s: read_gencp_registers failed: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ ret = gcprm_version_check(client); ++ if (ret < 0) { ++ dev_err(&client->dev, "gcprm version mismatch!\n"); ++ return ret; ++ } ++ ++ dev_dbg(&client->dev, "correct gcprm version\n"); ++ } ++ ++ ret = sysfs_create_group(&dev->kobj, &avt_csi2_attr_grp); ++ if (ret) { ++ dev_err(dev, "Failed to create sysfs group (%d)\n", ret); ++ return ret; ++ } ++ ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ priv->subdev->entity.ops = &avt_csi2_media_ops; ++ ret = tegra_media_entity_init(&priv->subdev->entity, 1, &priv->pad, true, true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = camera_common_initialize(common_data, "avt_csi2"); ++ if (ret) { ++ dev_err(&client->dev, "Failed to initialize tegra common for avt.\n"); ++ return ret; ++ } ++ ++ if (of_property_read_u32(dev->of_node, "csi_clk_freq", &priv->csi_clk_freq)) { ++ priv->csi_clk_freq = 0; ++ } ++ ++ if (of_property_read_u32(dev->of_node, "csi_lanes", &priv->csi_fixed_lanes)) { ++ priv->csi_fixed_lanes = 0; ++ } ++ ++ priv->numlanes = priv->s_data->numlanes; ++ priv->stream_on = false; ++ priv->cross_update = false; ++ priv->stride_align_enabled = true; ++ priv->crop_align_enabled = true; ++ ret = avt_init_mode(priv->subdev); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = read_feature_register(priv->subdev, &feature_inquiry_reg); ++ if (ret < 0) { ++ dev_err(&client->dev, "failed to read feature reqister: %d\n", ret); ++ return ret; ++ } ++ ++ /* Workaround for firmware not initializing auto exposure limits when exposure limits change */ ++ if (feature_inquiry_reg.feature_inq.exposure_auto) { ++ u64 value; ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MIN_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(priv->subdev, "BCRM_EXPOSURE_TIME_MIN_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ swapbytes(&value, 8); ++ ret = ioctl_gencam_i2cwrite_reg(client, BCRM_EXPOSURE_AUTO_MIN_64RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, (char*) &value); ++ ++ if (ret < 0) { ++ avt_err(priv->subdev, "Failed to initialize exposure auto minimum: %d\n", ret); ++ } ++ ++ /* reading the Maximum Exposure time */ ++ ret = avt_reg_read(client, ++ priv->cci_reg.bcrm_addr + BCRM_EXPOSURE_TIME_MAX_64R, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, ++ (char *) &value); ++ if (ret < 0) { ++ avt_err(priv->subdev, "BCRM_EXPOSURE_TIME_MAX_64R: i2c read failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ swapbytes(&value, 8); ++ ret = ioctl_gencam_i2cwrite_reg(client, BCRM_EXPOSURE_AUTO_MAX_64RW + priv->cci_reg.bcrm_addr, ++ AV_CAM_REG_SIZE, AV_CAM_DATA_SIZE_64, (char*) &value); ++ ++ if (ret < 0) { ++ avt_err(priv->subdev, "Failed to initialize exposure auto maximum: %d\n", ret); ++ } ++ } ++ ++ common_data->numctrls = avt_initialize_controls(client, priv); ++ ++ ret = read_framerate(priv->subdev, &(priv->streamcap.timeperframe)); ++ ++ if(ret < 0) { ++ return ret; ++ } ++ ++ priv->ignore_control_write = false; ++ ++ ret = v4l2_async_register_subdev(priv->subdev); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ dev_info(&client->dev, "sensor %s registered\n", priv->subdev->name); ++ ++ return 0; ++} ++ ++static int avt_csi2_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ sysfs_remove_group(&client->dev.kobj, &avt_csi2_attr_grp); ++ ++ v4l2_async_unregister_subdev(sd); ++ v4l2_device_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ ++ return 0; ++} ++ ++static struct i2c_device_id avt_csi2_id[] = { ++ {"avt_csi2", 0}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, avt_csi2_id); ++ ++static struct i2c_driver avt_csi2_driver = { ++ .driver = { ++ .name = "avt_csi2", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(avt_csi2_of_match), ++ }, ++ .probe = avt_csi2_probe, ++ .remove = avt_csi2_remove, ++ .id_table = avt_csi2_id, ++}; ++ ++module_i2c_driver(avt_csi2_driver); ++ ++MODULE_AUTHOR("Allied Vision Technologies GmbH"); ++MODULE_DESCRIPTION("Allied Vision MIPI CSI-2 Camera Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRIVER_VERSION); +diff --git a/drivers/media/i2c/avt_csi2.h b/drivers/media/i2c/avt_csi2.h +new file mode 100644 +index 000000000000..9a751b969239 +--- /dev/null ++++ b/drivers/media/i2c/avt_csi2.h +@@ -0,0 +1,634 @@ ++/* ++ * Allied Vision CSI2 Camera ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ */ ++ ++#ifndef __AVT_CSI2_H__ ++#define __AVT_CSI2_H__ ++ ++ ++#include ++#include "alvium_regs.h" ++#include "alvium_helper.h" ++ ++#define AVT_MAX_CTRLS 50 ++struct avt_frame_param { ++ /* crop settings */ ++ struct v4l2_rect r; ++ ++ /* min/max/step values for frame size */ ++ uint32_t minh; ++ uint32_t maxh; ++ uint32_t sh; ++ uint32_t minw; ++ uint32_t maxw; ++ uint32_t sw; ++ uint32_t minhoff; ++ uint32_t maxhoff; ++ uint32_t shoff; ++ uint32_t minwoff; ++ uint32_t maxwoff; ++ uint32_t swoff; ++}; ++ ++struct avt_binning_config { ++ enum BCRM_DIGITAL_BINNING_SETTING setting; ++ uint32_t width; ++ uint32_t height; ++}; ++ ++enum avt_mode { ++ AVT_BCRM_MODE, ++ AVT_GENCP_MODE, ++}; ++struct avt_csi2_priv { ++ struct v4l2_subdev *subdev; ++ struct media_pad pad; ++ struct i2c_client *client; ++ u32 mbus_fmt_code; ++ ++ struct v4l2_captureparm streamcap; ++ struct v4l2_ctrl_handler hdl; ++ struct camera_common_data *s_data; ++ ++ struct v4l2_ctrl_config ctrl_cfg[AVT_MAX_CTRLS]; ++ struct v4l2_ctrl *ctrls[AVT_MAX_CTRLS]; ++ ++ bool stream_on; ++ bool cross_update; ++ bool write_handshake_available; ++ bool stride_align_enabled; ++ bool crop_align_enabled; ++ bool trigger_mode; ++ bool fallback_app_running; ++ ++ uint32_t csi_fixed_lanes; ++ uint32_t csi_clk_freq; ++ uint32_t host_csi_clk_freq; ++ int numlanes; ++ struct avt_frame_param frmp; ++ ++ struct cci_reg cci_reg; ++ struct gencp_reg gencp_reg; ++ ++ enum avt_mode mode; ++ ++ int32_t *available_fmts; ++ uint32_t available_fmts_cnt; ++ ++ ++ struct task_struct *trig_thread; ++ struct v4l2_trigger_rate *trigger_rate; ++ ++ int acquisition_active_invert; ++ ++ struct task_struct *value_update_thread; ++ wait_queue_head_t value_update_wq; ++ atomic_t force_value_update; ++ int value_update_interval; ++ ++ bool ignore_control_write; ++ ++ struct avt_binning_config *available_binnings; ++ uint32_t available_binnings_cnt; ++ int cur_binning_config; ++ ++ s64 link_freqs[1]; ++ ++ atomic_t open_count; ++}; ++struct avt_ctrl { ++ __u32 id; ++ __u32 value0; ++ __u32 value1; ++}; ++ ++#define V4L2_AV_CSI2_BASE 0x1000 ++#define V4L2_AV_CSI2_WIDTH_R (V4L2_AV_CSI2_BASE+0x0001) ++#define V4L2_AV_CSI2_WIDTH_W (V4L2_AV_CSI2_BASE+0x0002) ++#define V4L2_AV_CSI2_WIDTH_MINVAL_R (V4L2_AV_CSI2_BASE+0x0003) ++#define V4L2_AV_CSI2_WIDTH_MAXVAL_R (V4L2_AV_CSI2_BASE+0x0004) ++#define V4L2_AV_CSI2_WIDTH_INCVAL_R (V4L2_AV_CSI2_BASE+0x0005) ++#define V4L2_AV_CSI2_HEIGHT_R (V4L2_AV_CSI2_BASE+0x0006) ++#define V4L2_AV_CSI2_HEIGHT_W (V4L2_AV_CSI2_BASE+0x0007) ++#define V4L2_AV_CSI2_HEIGHT_MINVAL_R (V4L2_AV_CSI2_BASE+0x0008) ++#define V4L2_AV_CSI2_HEIGHT_MAXVAL_R (V4L2_AV_CSI2_BASE+0x0009) ++#define V4L2_AV_CSI2_HEIGHT_INCVAL_R (V4L2_AV_CSI2_BASE+0x000A) ++#define V4L2_AV_CSI2_PIXELFORMAT_R (V4L2_AV_CSI2_BASE+0x000B) ++#define V4L2_AV_CSI2_PIXELFORMAT_W (V4L2_AV_CSI2_BASE+0x000C) ++#define V4L2_AV_CSI2_PALYLOADSIZE_R (V4L2_AV_CSI2_BASE+0x000D) ++#define V4L2_AV_CSI2_STREAMON_W (V4L2_AV_CSI2_BASE+0x000E) ++#define V4L2_AV_CSI2_STREAMOFF_W (V4L2_AV_CSI2_BASE+0x000F) ++#define V4L2_AV_CSI2_ABORT_W (V4L2_AV_CSI2_BASE+0x0010) ++#define V4L2_AV_CSI2_ACQ_STATUS_R (V4L2_AV_CSI2_BASE+0x0011) ++#define V4L2_AV_CSI2_HFLIP_R (V4L2_AV_CSI2_BASE+0x0012) ++#define V4L2_AV_CSI2_HFLIP_W (V4L2_AV_CSI2_BASE+0x0013) ++#define V4L2_AV_CSI2_VFLIP_R (V4L2_AV_CSI2_BASE+0x0014) ++#define V4L2_AV_CSI2_VFLIP_W (V4L2_AV_CSI2_BASE+0x0015) ++#define V4L2_AV_CSI2_OFFSET_X_W (V4L2_AV_CSI2_BASE+0x0016) ++#define V4L2_AV_CSI2_OFFSET_X_R (V4L2_AV_CSI2_BASE+0x0017) ++#define V4L2_AV_CSI2_OFFSET_X_MIN_R (V4L2_AV_CSI2_BASE+0x0018) ++#define V4L2_AV_CSI2_OFFSET_X_MAX_R (V4L2_AV_CSI2_BASE+0x0019) ++#define V4L2_AV_CSI2_OFFSET_X_INC_R (V4L2_AV_CSI2_BASE+0x001A) ++#define V4L2_AV_CSI2_OFFSET_Y_W (V4L2_AV_CSI2_BASE+0x001B) ++#define V4L2_AV_CSI2_OFFSET_Y_R (V4L2_AV_CSI2_BASE+0x001C) ++#define V4L2_AV_CSI2_OFFSET_Y_MIN_R (V4L2_AV_CSI2_BASE+0x001D) ++#define V4L2_AV_CSI2_OFFSET_Y_MAX_R (V4L2_AV_CSI2_BASE+0x001E) ++#define V4L2_AV_CSI2_OFFSET_Y_INC_R (V4L2_AV_CSI2_BASE+0x001F) ++#define V4L2_AV_CSI2_SENSOR_WIDTH_R (V4L2_AV_CSI2_BASE+0x0020) ++#define V4L2_AV_CSI2_SENSOR_HEIGHT_R (V4L2_AV_CSI2_BASE+0x0021) ++#define V4L2_AV_CSI2_MAX_WIDTH_R (V4L2_AV_CSI2_BASE+0x0022) ++#define V4L2_AV_CSI2_MAX_HEIGHT_R (V4L2_AV_CSI2_BASE+0x0023) ++#define V4L2_AV_CSI2_CURRENTMODE_R (V4L2_AV_CSI2_BASE+0x0024) ++#define V4L2_AV_CSI2_CHANGEMODE_W (V4L2_AV_CSI2_BASE+0x0025) ++#define V4L2_AV_CSI2_BAYER_PATTERN_R (V4L2_AV_CSI2_BASE+0x0026) ++#define V4L2_AV_CSI2_BAYER_PATTERN_W (V4L2_AV_CSI2_BASE+0x0027) ++ ++/* Driver release version */ ++#define STR_HELPER(x) #x ++#define STR(x) STR_HELPER(x) ++ ++/* Driver release version */ ++#define DRV_VER_MAJOR 5 ++#define DRV_VER_MINOR 1 ++#define DRV_VER_PATCH 1 ++#define DRV_VER_BUILD 0 ++#define DRIVER_VERSION STR(DRV_VER_MAJOR) "." STR(DRV_VER_MINOR) "." STR(DRV_VER_PATCH) "." STR(DRV_VER_BUILD) ++ ++#define BCRM_DEVICE_VERSION 0x00010000 ++#define BCRM_MAJOR_VERSION 0x0001 ++#define BCRM_MINOR_VERSION 0x0000 ++ ++#define GCPRM_DEVICE_VERSION 0x00010000 ++#define GCPRM_MAJOR_VERSION 0x0001 ++#define GCPRM_MINOR_VERSION 0x0000 ++ ++/* MIPI CSI-2 data types */ ++#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ ++#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ ++#define MIPI_DT_YUV422 0x1e /* UYVY... */ ++#define MIPI_DT_RGB444 0x20 ++#define MIPI_DT_RGB555 0x21 ++#define MIPI_DT_RGB565 0x22 ++#define MIPI_DT_RGB666 0x23 ++#define MIPI_DT_RGB888 0x24 ++#define MIPI_DT_RAW6 0x28 ++#define MIPI_DT_RAW7 0x29 ++#define MIPI_DT_RAW8 0x2a ++#define MIPI_DT_RAW10 0x2b ++#define MIPI_DT_RAW12 0x2c ++#define MIPI_DT_RAW14 0x2d ++#define MIPI_DT_CUSTOM 0x31 ++ ++enum bayer_format { ++ monochrome,/* 0 */ ++ bayer_gr, ++ bayer_rg, ++ bayer_gb, ++ bayer_bg, ++}; ++struct bcrm_to_v4l2 { ++ int64_t min_bcrm; ++ int64_t max_bcrm; ++ int64_t step_bcrm; ++ int32_t min_v4l2; ++ int32_t max_v4l2; ++ int32_t step_v4l2; ++}; ++ ++enum convert_type { ++ min_enum,/* 0 */ ++ max_enum, ++ step_enum, ++}; ++ ++#define CLEAR(x) memset(&(x), 0, sizeof(x)) ++ ++#define EXP_ABS 100000UL ++#define UHZ_TO_HZ 1000000UL ++#define FRAQ_NUM 1000 ++ ++#define CCI_REG_LAYOUT_MINVER_MASK (0x0000ffff) ++#define CCI_REG_LAYOUT_MINVER_SHIFT (0) ++#define CCI_REG_LAYOUT_MAJVER_MASK (0xffff0000) ++#define CCI_REG_LAYOUT_MAJVER_SHIFT (16) ++ ++#define CCI_REG_LAYOUT_MINVER 0 ++#define CCI_REG_LAYOUT_MAJVER 1 ++ ++#define AV_ATTR_REVERSE_X {"Reverse X", 0} ++#define AV_ATTR_REVERSE_Y {"Reverse Y", 1} ++#define AV_ATTR_INTENSITY_AUTO {"Intensity Auto", 2} ++#define AV_ATTR_BRIGHTNESS {"Brightness", 3} ++/* Red & Blue balance features are enabled by default since it doesn't have ++ * option in BCRM FEATURE REGISTER ++ */ ++#define AV_ATTR_RED_BALANCE {"Red Balance", 3} ++#define AV_ATTR_BLUE_BALANCE {"Blue Balance", 3} ++#define AV_ATTR_GAIN {"Gain", 4} ++#define AV_ATTR_GAMMA {"Gamma", 5} ++#define AV_ATTR_CONTRAST {"Contrast", 6} ++#define AV_ATTR_SATURATION {"Saturation", 7} ++#define AV_ATTR_HUE {"Hue", 8} ++#define AV_ATTR_WHITEBALANCE {"White Balance", 9} ++#define AV_ATTR_SHARPNESS {"Sharpnesss", 10} ++#define AV_ATTR_EXPOSURE_AUTO {"Exposure Auto", 11} ++#define AV_ATTR_EXPOSURE_AUTO_MIN {"Exposure Auto Min", 11} ++#define AV_ATTR_EXPOSURE_AUTO_MAX {"Exposure Auto Max", 11} ++#define AV_ATTR_AUTOGAIN {"Gain Auto", 12} ++#define AV_ATTR_GAIN_AUTO_MIN {"Gain Auto Min", 12} ++#define AV_ATTR_GAIN_AUTO_MAX {"Gain Auto Max", 12} ++#define AV_ATTR_EXPOSURE {"Exposure", 0} ++#define AV_ATTR_EXPOSURE_ABSOLUTE {"Exposure Absolute", 0} ++#define AV_ATTR_WHITEBALANCE_AUTO {"Auto White Balance", 13} ++#define AV_ATTR_EXPOSURE_ACTIVE_LINE_MODE {"Exposure Active Line Mode", 18} ++#define AV_ATTR_EXPOSURE_ACTIVE_LINE_SELECTOR {"Exposure Active Line Selector", 18} ++#define AV_ATTR_EXPOSURE_ACTIVE_INVERT {"Exposure Active Invert", 18} ++ ++#define AV_ATTR_TRIGGER_MODE {"Trigger Mode", 17} ++#define AV_ATTR_TRIGGER_ACTIVATION {"Trigger Activation", 17} ++#define AV_ATTR_TRIGGER_SOURCE {"Trigger Source", 17} ++#define AV_ATTR_TRIGGER_SOFTWARE {"Trigger Software", 17} ++#define AV_ATTR_DEVICE_TEMPERATURE {"Device Temperature", 14} ++#define AV_ATTR_BINNING_MODE {"Binning Mode", 14} ++ ++struct avt_ctrl_mapping { ++ u8 reg_size; ++ u8 data_size; ++ u16 min_offset; ++ u16 max_offset; ++ u16 reg_offset; ++ u16 step_offset; ++ u32 id; ++ u32 type; ++ u32 flags; ++ struct { ++ s8 *name; ++ u8 feature_avail; ++ } attr; ++ bool disabled_while_streaming : 1; ++}; ++ ++#define V4L2_CID_EXPOSURE_AUTO_MIN (V4L2_CID_CAMERA_CLASS_BASE+40) ++#define V4L2_CID_EXPOSURE_AUTO_MAX (V4L2_CID_CAMERA_CLASS_BASE+41) ++#define V4L2_CID_GAIN_AUTO_MIN (V4L2_CID_CAMERA_CLASS_BASE+42) ++#define V4L2_CID_GAIN_AUTO_MAX (V4L2_CID_CAMERA_CLASS_BASE+43) ++#define V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE (V4L2_CID_CAMERA_CLASS_BASE+44) ++#define V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR (V4L2_CID_CAMERA_CLASS_BASE+45) ++#define V4L2_CID_EXPOSURE_ACTIVE_INVERT (V4L2_CID_CAMERA_CLASS_BASE+46) ++ ++/* Trigger mode to ON/OFF */ ++#define V4L2_CID_TRIGGER_MODE (V4L2_CID_CAMERA_CLASS_BASE+47) ++ ++/* trigger activation: edge_rising, edge_falling, edge_any, level_high, level_low */ ++#define V4L2_CID_TRIGGER_ACTIVATION (V4L2_CID_CAMERA_CLASS_BASE+48) ++ ++/* trigger source: software, gpio0, gpio1 */ ++#define V4L2_CID_TRIGGER_SOURCE (V4L2_CID_CAMERA_CLASS_BASE+49) ++ ++/* Execute a software trigger */ ++#define V4L2_CID_TRIGGER_SOFTWARE (V4L2_CID_CAMERA_CLASS_BASE+50) ++ ++/* Camera temperature readout */ ++#define V4L2_CID_DEVICE_TEMPERATURE (V4L2_CID_CAMERA_CLASS_BASE+51) ++ ++/* Binning mode: avg, sum */ ++#define V4L2_CID_BINNING_MODE (V4L2_CID_CAMERA_CLASS_BASE+52) ++ ++ ++const struct avt_ctrl_mapping avt_ctrl_mappings[] = { ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .attr = AV_ATTR_BRIGHTNESS, ++ .min_offset = BCRM_BLACK_LEVEL_MIN_32R, ++ .max_offset = BCRM_BLACK_LEVEL_MAX_32R, ++ .reg_offset = BCRM_BLACK_LEVEL_32RW, ++ .step_offset = BCRM_BLACK_LEVEL_INC_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_CONTRAST, ++ .attr = AV_ATTR_CONTRAST, ++ .min_offset = BCRM_CONTRAST_VALUE_MIN_32R, ++ .max_offset = BCRM_CONTRAST_VALUE_MAX_32R, ++ .reg_offset = BCRM_CONTRAST_VALUE_32RW, ++ .step_offset = BCRM_CONTRAST_VALUE_INC_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_SATURATION, ++ .attr = AV_ATTR_SATURATION, ++ .min_offset = BCRM_SATURATION_MIN_32R, ++ .max_offset = BCRM_SATURATION_MAX_32R, ++ .reg_offset = BCRM_SATURATION_32RW, ++ .step_offset = BCRM_SATURATION_INC_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_HUE, ++ .attr = AV_ATTR_HUE, ++ .min_offset = BCRM_HUE_MIN_32R, ++ .max_offset = BCRM_HUE_MAX_32R, ++ .reg_offset = BCRM_HUE_32RW, ++ .step_offset = BCRM_HUE_INC_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .attr = AV_ATTR_WHITEBALANCE_AUTO, ++ .reg_offset = BCRM_WHITE_BALANCE_AUTO_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_DO_WHITE_BALANCE, ++ .attr = AV_ATTR_WHITEBALANCE, ++ .reg_offset = BCRM_WHITE_BALANCE_AUTO_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BUTTON, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_RED_BALANCE, ++ .attr = AV_ATTR_RED_BALANCE, ++ .min_offset = BCRM_RED_BALANCE_RATIO_MIN_64R, ++ .max_offset = BCRM_RED_BALANCE_RATIO_MAX_64R, ++ .reg_offset = BCRM_RED_BALANCE_RATIO_64RW, ++ .step_offset = BCRM_RED_BALANCE_RATIO_INC_64R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_BLUE_BALANCE, ++ .attr = AV_ATTR_BLUE_BALANCE, ++ .min_offset = BCRM_BLUE_BALANCE_RATIO_MIN_64R, ++ .max_offset = BCRM_BLUE_BALANCE_RATIO_MAX_64R, ++ .reg_offset = BCRM_BLUE_BALANCE_RATIO_64RW, ++ .step_offset = BCRM_BLUE_BALANCE_RATIO_INC_64R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_GAMMA, ++ .attr = AV_ATTR_GAMMA, ++ .min_offset = BCRM_GAMMA_MIN_64R, ++ .max_offset = BCRM_GAMMA_MAX_64R, ++ .reg_offset = BCRM_GAMMA_64RW, ++ .step_offset = BCRM_GAMMA_INC_64R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_ABSOLUTE, ++ .attr = AV_ATTR_EXPOSURE_ABSOLUTE, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE, ++ .attr = AV_ATTR_EXPOSURE, ++ .min_offset = BCRM_EXPOSURE_TIME_MIN_64R, ++ .max_offset = BCRM_EXPOSURE_TIME_MAX_64R, ++ .reg_offset = BCRM_EXPOSURE_TIME_64RW, ++ .step_offset = BCRM_EXPOSURE_TIME_INC_64R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_AUTOGAIN, ++ .attr = AV_ATTR_AUTOGAIN, ++ .reg_offset = BCRM_GAIN_AUTO_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_GAIN, ++ .attr = AV_ATTR_GAIN, ++ .min_offset = BCRM_GAIN_MIN_64R, ++ .max_offset = BCRM_GAIN_MAX_64R, ++ .reg_offset = BCRM_GAIN_64RW, ++ .step_offset = BCRM_GAIN_INC_64R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_HFLIP, ++ .attr = AV_ATTR_REVERSE_X, ++ .reg_offset = BCRM_IMG_REVERSE_X_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_VFLIP, ++ .attr = AV_ATTR_REVERSE_Y, ++ .reg_offset = BCRM_IMG_REVERSE_Y_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_SHARPNESS, ++ .attr = AV_ATTR_SHARPNESS, ++ .min_offset = BCRM_SHARPNESS_MIN_32R, ++ .max_offset = BCRM_SHARPNESS_MAX_32R, ++ .reg_offset = BCRM_SHARPNESS_32RW, ++ .step_offset = BCRM_SHARPNESS_INC_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_AUTO, ++ .attr = AV_ATTR_EXPOSURE_AUTO, ++ .reg_offset = BCRM_EXPOSURE_AUTO_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_AUTO_MIN, ++ .attr = AV_ATTR_EXPOSURE_AUTO_MIN, ++ .reg_offset = BCRM_EXPOSURE_AUTO_MIN_64RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_AUTO_MAX, ++ .attr = AV_ATTR_EXPOSURE_AUTO_MAX, ++ .reg_offset = BCRM_EXPOSURE_AUTO_MAX_64RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_GAIN_AUTO_MIN, ++ .attr = AV_ATTR_GAIN_AUTO_MIN, ++ .reg_offset = BCRM_GAIN_AUTO_MIN_64RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_GAIN_AUTO_MAX, ++ .attr = AV_ATTR_GAIN_AUTO_MAX, ++ .reg_offset = BCRM_GAIN_AUTO_MAX_64RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_64, ++ .type = V4L2_CTRL_TYPE_INTEGER64, ++ .flags = 0, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_ACTIVE_LINE_MODE, ++ .attr = AV_ATTR_EXPOSURE_ACTIVE_LINE_MODE, ++ .reg_offset = BCRM_EXPOSURE_ACTIVE_LINE_MODE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_ACTIVE_LINE_SELECTOR, ++ .attr = AV_ATTR_EXPOSURE_ACTIVE_LINE_SELECTOR, ++ .reg_offset = BCRM_EXPOSURE_ACTIVE_OUTPUT_LINE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_EXPOSURE_ACTIVE_INVERT, ++ .attr = AV_ATTR_EXPOSURE_ACTIVE_INVERT, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_TRIGGER_MODE, ++ .attr = AV_ATTR_TRIGGER_MODE, ++ .reg_offset = BCRM_FRAME_START_TRIGGER_MODE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_TRIGGER_ACTIVATION, ++ .attr = AV_ATTR_TRIGGER_ACTIVATION, ++ .reg_offset = BCRM_FRAME_START_TRIGGER_ACTIVATION_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_TRIGGER_SOURCE, ++ .attr = AV_ATTR_TRIGGER_SOURCE, ++ .reg_offset = BCRM_FRAME_START_TRIGGER_SOURCE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .flags = 0, ++ .disabled_while_streaming = true, ++ }, ++ { ++ .id = V4L2_CID_TRIGGER_SOFTWARE, ++ .attr = AV_ATTR_TRIGGER_SOFTWARE, ++ .reg_offset = BCRM_FRAME_START_TRIGGER_SOURCE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_BUTTON, ++ .flags = V4L2_CTRL_FLAG_INACTIVE, ++ }, ++ { ++ .id = V4L2_CID_DEVICE_TEMPERATURE, ++ .attr = AV_ATTR_DEVICE_TEMPERATURE, ++ .reg_offset = BCRM_DEVICE_TEMPERATURE_32R, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_32, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, ++ }, ++ { ++ .id = V4L2_CID_BINNING_MODE, ++ .attr = AV_ATTR_BINNING_MODE, ++ .reg_offset = BCRM_DIGITAL_BINNIG_MODE_8RW, ++ .reg_size = AV_CAM_REG_SIZE, ++ .data_size = AV_CAM_DATA_SIZE_8, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .flags = 0, ++ }, ++}; ++ ++#define AVT_TEGRA_TIMEOUT_DEFAULT CAPTURE_TIMEOUT_MS ++#define AVT_TEGRA_TIMEOUT_DISABLED -1 ++ ++#define AVT_TEGRA_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) ++ ++#define AVT_TEGRA_TIMEOUT (AVT_TEGRA_CID_BASE + 200) ++#define AVT_TEGRA_TIMEOUT_VALUE (AVT_TEGRA_CID_BASE + 201) ++#define AVT_TEGRA_STRIDE_ALIGN (AVT_TEGRA_CID_BASE + 202) ++#define AVT_TEGRA_CROP_ALIGN (AVT_TEGRA_CID_BASE + 203) ++#define AVT_TEGRA_VALUE_UPDATE_INTERVAL (AVT_TEGRA_CID_BASE + 204) ++#define AVT_TEGRA_FORCE_VALUE_UPDATE (AVT_TEGRA_CID_BASE + 205) ++ ++#endif +diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c +index f21da11caf22..960406912665 100644 +--- a/drivers/media/i2c/tc358743.c ++++ b/drivers/media/i2c/tc358743.c +@@ -1,42 +1,55 @@ +-// SPDX-License-Identifier: GPL-2.0-only +-/* +- * tc358743 - Toshiba HDMI to CSI-2 bridge +- * +- * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights +- * reserved. +- */ +- + /* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ +- +-#include +-#include +-#include +-#include ++#define DEBUG + #include + #include + #include ++#include ++#include + #include +-#include +-#include ++#include ++#include ++#include ++#include + #include + #include +-#include +-#include +-#include +-#include +-#include ++#include + #include ++#include ++#include + #include +-#include +-#include ++//#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++// #include ++#include ++//#include ++#include + + #include "tc358743_regs.h" ++#include + +-static int debug; ++/* RGB ouput selection */ ++// #define TC358743_VOUT_RGB ++ ++static int debug = 3; + module_param(debug, int, 0644); + MODULE_PARM_DESC(debug, "debug level (0-3)"); + +@@ -46,2188 +59,2330 @@ MODULE_AUTHOR("Mikhail Khelik "); + MODULE_AUTHOR("Mats Randgaard "); + MODULE_LICENSE("GPL"); + +-#define EDID_NUM_BLOCKS_MAX 8 +-#define EDID_BLOCK_SIZE 128 +- +-#define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2) ++#define DELAY_ENABLE_INTERRUPT_MS 2000 + +-#define POLL_INTERVAL_CEC_MS 10 +-#define POLL_INTERVAL_MS 1000 ++/* mode */ ++enum { ++ tc358743_MODE_1280X720, ++ tc358743_MODE_1920X1080, ++}; + +-static const struct v4l2_dv_timings_cap tc358743_timings_cap = { +- .type = V4L2_DV_BT_656_1120, +- /* keep this initialization for compatibility with GCC < 4.4.6 */ +- .reserved = { 0 }, +- /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ +- V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 13000000, 165000000, +- V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | +- V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, +- V4L2_DV_BT_CAP_PROGRESSIVE | +- V4L2_DV_BT_CAP_REDUCED_BLANKING | +- V4L2_DV_BT_CAP_CUSTOM) ++/* frame rate */ ++static const int tc358743_30_60fps[] = { ++ 30, ++ 50, ++ 60, + }; + +-struct tc358743_state { +- struct tc358743_platform_data pdata; +- struct v4l2_fwnode_bus_mipi_csi2 bus; +- struct v4l2_subdev sd; +- struct media_pad pad; +- struct v4l2_ctrl_handler hdl; +- struct i2c_client *i2c_client; +- /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ +- struct mutex confctl_mutex; ++/* frame format */ ++static const struct camera_common_frmfmt tc358743_frmfmt[] = { ++ {{1280, 720}, tc358743_30_60fps, 3, 0, tc358743_MODE_1280X720}, ++ {{1920, 1080}, tc358743_30_60fps, 3, 0, tc358743_MODE_1920X1080}, ++}; + +- /* controls */ +- struct v4l2_ctrl *detect_tx_5v_ctrl; +- struct v4l2_ctrl *audio_sampling_rate_ctrl; +- struct v4l2_ctrl *audio_present_ctrl; ++// static const struct camera_common_colorfmt tc358743_color_fmts[] = { ++// { ++// MEDIA_BUS_FMT_SRGGB12_1X12, ++// V4L2_COLORSPACE_SRGB, ++// V4L2_PIX_FMT_SRGGB12, ++// }, ++// { ++// MEDIA_BUS_FMT_UYVY8_1X16, ++// V4L2_COLORSPACE_SRGB, ++// V4L2_PIX_FMT_UYVY, ++// }, ++// }; + +- struct delayed_work delayed_work_enable_hotplug; ++#define EDID_NUM_BLOCKS_MAX 8 ++#define EDID_BLOCK_SIZE 128 ++static u8 edid[] = { ++ ++ // #ifdef TC358743_VOUT_RGB ++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x50, 0x21, 0x9C, 0x27, ++ 0x00, 0x00, 0x00, 0x00, 0x19, 0x12, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, ++ 0x0E, 0x00, 0xB2, 0xA0, 0x57, 0x49, 0x9B, 0x26, 0x10, 0x48, 0x4F, 0x2F, ++ 0xCF, 0x00, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x81, 0x99, 0x01, 0x01, ++ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, ++ 0x2D, 0x40, 0x58, 0x2C, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, ++ 0x00, 0x00, 0x00, 0xFD, 0x00, 0x31, 0x55, 0x18, 0x5E, 0x11, 0x00, 0x0A, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x54, ++ 0x6F, 0x73, 0x68, 0x69, 0x62, 0x61, 0x2D, 0x48, 0x32, 0x43, 0x0A, 0x20, ++ 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc3, 0x02, 0x03, 0x1a, 0xc0, ++ 0x48, 0xa2, 0x10, 0x04, 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, ++ 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, 0x00, 0x2a, 0x01, 0x1d, ++ 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, ++ 0x10, 0x3e, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0xd7 ++ // #else ++ // 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, ++ // 0x52,0x62,0x88,0x88,0x00,0x88,0x88,0x88, ++ // 0x1C,0x15,0x01,0x03,0x80,0x00,0x00,0x78, ++ // 0x0A,0x0D,0xC9,0xA0,0x57,0x47,0x98,0x27, ++ // 0x12,0x48,0x4C,0x00,0x00,0x00,0x01,0x01, ++ // 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, ++ // 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A, ++ // 0x80,0xD0,0x72,0x38,0x2D,0x40,0x10,0x2C, ++ // 0x45,0x80,0x66,0x4C,0x00,0x00,0x00,0x1E, ++ // 0x01,0x1D,0x00,0xBC,0x52,0xD0,0x1E,0x20, ++ // 0xB8,0x28,0x55,0x40,0x66,0x4C,0x00,0x00, ++ // 0x00,0x1E,0x00,0x00,0x00,0xFC,0x00,0x54, ++ // 0x6F,0x73,0x68,0x69,0x62,0x61,0x2D,0x48, ++ // 0x32,0x43,0x0A,0x20,0x00,0x00,0x00,0xFD, ++ // 0x00,0x14,0x78,0x01,0xFF,0x10,0x00,0x0A, ++ // 0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xBA, ++ // 0x02,0x03,0x1A,0x71,0x47,0x9F,0x13,0x22, ++ // 0x1F,0x02,0x11,0x1F,0x23,0x09,0x07,0x01, ++ // 0x83,0x01,0x00,0x00,0x65,0x03,0x0C,0x00, ++ // 0x10,0x00,0x01,0x1D,0x80,0x18,0x71,0x38, ++ // 0x2D,0x40,0x58,0x2C,0x45,0x00,0x66,0x4C, ++ // 0x00,0x00,0x00,0x1E,0x02,0x3A,0x80,0xD0, ++ // 0x72,0x38,0x2D,0x40,0x10,0x2C,0x45,0x80, ++ // 0x66,0x4C,0x00,0x00,0x00,0x1E,0x8C,0x0A, ++ // 0xD0,0x8A,0x20,0xE0,0x2D,0x10,0x10,0x3E, ++ // 0x96,0x00,0x66,0x4C,0x00,0x00,0x00,0x18, ++ // 0x8C,0x0A,0xD0,0x90,0x20,0x40,0x31,0x20, ++ // 0x0C,0x40,0x55,0x00,0x66,0x4C,0x00,0x00, ++ // 0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, ++ // #endif ++}; ++/* Max transfer size done by I2C transfer functions */ ++#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2) + +- struct timer_list timer; +- struct work_struct work_i2c_poll; ++static const struct v4l2_dv_timings_cap tc358743_timings_cap = { ++ .type = V4L2_DV_BT_656_1120, ++ /* keep this initialization for compatibility with GCC < 4.4.6 */ ++ .reserved = {0}, ++ /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ ++ V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, ++ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, ++ V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | ++ V4L2_DV_BT_CAP_CUSTOM)}; + +- /* edid */ +- u8 edid_blocks_written; ++struct tc358743_state { ++ struct tc358743_platform_data pdata; ++ // struct v4l2_of_bus_mipi_csi2 bus; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler hdl; ++ struct i2c_client *i2c_client; ++ struct regmap *regmap; ++ /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ ++ struct mutex confctl_mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl *detect_tx_5v_ctrl; ++ struct v4l2_ctrl *audio_sampling_rate_ctrl; ++ struct v4l2_ctrl *audio_present_ctrl; ++ ++ /* work queues */ ++ struct workqueue_struct *work_queues; ++ struct delayed_work delayed_work_enable_hotplug; ++ struct delayed_work delayed_work_enable_interrupt; ++ struct work_struct process_isr; ++ struct mutex isr_lock; ++ ++ /* edid */ ++ u8 edid_blocks_written; ++ ++ /* used by i2c_wr() */ ++ u8 wr_data[MAX_XFER_SIZE]; ++ ++ struct v4l2_dv_timings timings; ++ u32 mbus_fmt_code; ++ ++ struct gpio_desc *reset_gpio; ++}; + +- struct v4l2_dv_timings timings; +- u32 mbus_fmt_code; +- u8 csi_lanes_in_use; ++static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) { ++ return container_of(sd, struct tc358743_state, sd); ++} ++/* ++static char * sdo_bit_len [] = { ++ [0b000] = "16bit (lower 8bit discarded)", ++ [0b001] = "16bit (lower 8bit + 1 discarded)", ++ [0b010] = "18bit (lower 6bit discarded)", ++ [0b011] = "18bit (lower 6bit + 1 discarded)", ++ [0b100] = "20bit (lower 4bit discarded)", ++ [0b101] = "20bit (lower 4bit + 1 discarded)", ++ [0b110] = "24bit no rounding", ++ [0b111] = "Output OFF (Mute)", ++}; + +- struct gpio_desc *reset_gpio; ++static char * sdo_fmt [] = { ++ [MASK_SDO_FMT_RIGHT] = "Right justified", ++ [MASK_SDO_FMT_LEFT] = "Left justified", ++ [MASK_SDO_FMT_I2S] = "I2S", ++ [0b011] = "I2S", ++}; + +- struct cec_adapter *cec_adap; ++static char * no_yes [] = { ++ [0] = "No", ++ [1] = "Yes", + }; + +-static void tc358743_enable_interrupts(struct v4l2_subdev *sd, +- bool cable_connected); +-static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd); ++static char * no_with [] = { ++ [0] = "No", ++ [1] = "With", ++}; + +-static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) +-{ +- return container_of(sd, struct tc358743_state, sd); +-} ++static char * off_on [] = { ++ [0b000] = "Off", ++ [0b001] = "On", ++}; + ++static char * audout_sel [] = { ++ [0b00] = "CSI2-TX", ++ [0b01] = "Reseved", ++ [0b10] = "I2S", ++ [0b11] = "TDM", ++}; ++*/ + /* --------------- I2C --------------- */ ++static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { ++ struct tc358743_state *state = to_state(sd); ++ struct i2c_client *client = state->i2c_client; ++ int err; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = buf, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = n, ++ .buf = values, ++ }, ++ }; + +-static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +-{ +- struct tc358743_state *state = to_state(sd); +- struct i2c_client *client = state->i2c_client; +- int err; +- u8 buf[2] = { reg >> 8, reg & 0xff }; +- struct i2c_msg msgs[] = { +- { +- .addr = client->addr, +- .flags = 0, +- .len = 2, +- .buf = buf, +- }, +- { +- .addr = client->addr, +- .flags = I2C_M_RD, +- .len = n, +- .buf = values, +- }, +- }; +- +- err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (err != ARRAY_SIZE(msgs)) { +- v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", +- __func__, reg, client->addr); +- } +-} +- +-static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +-{ +- struct tc358743_state *state = to_state(sd); +- struct i2c_client *client = state->i2c_client; +- int err, i; +- struct i2c_msg msg; +- u8 data[I2C_MAX_XFER_SIZE]; +- +- if ((2 + n) > I2C_MAX_XFER_SIZE) { +- n = I2C_MAX_XFER_SIZE - 2; +- v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", +- reg, 2 + n); +- } ++ err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (err != ARRAY_SIZE(msgs)) { ++ v4l2_err(sd, "%s: #### reading register0x%x from0x%x failed\n", __func__, ++ reg, client->addr); ++ return -1; ++ } ++ // udelay(10); ++ return 0; ++} + +- msg.addr = client->addr; +- msg.buf = data; +- msg.len = 2 + n; +- msg.flags = 0; ++static int i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { ++ struct tc358743_state *state = to_state(sd); ++ struct i2c_client *client = state->i2c_client; ++ u8 *data = state->wr_data; ++ int err, i; ++ struct i2c_msg msg; + +- data[0] = reg >> 8; +- data[1] = reg & 0xff; ++ if ((2 + n) > sizeof(state->wr_data)) { ++ v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", reg, 2 + n); ++ return -1; ++ } + +- for (i = 0; i < n; i++) +- data[2 + i] = values[i]; ++ msg.addr = client->addr; ++ msg.buf = data; ++ msg.len = 2 + n; ++ msg.flags = 0; + +- err = i2c_transfer(client->adapter, &msg, 1); +- if (err != 1) { +- v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", +- __func__, reg, client->addr); +- return; +- } ++ data[0] = reg >> 8; ++ data[1] = reg & 0xff; + +- if (debug < 3) +- return; ++ for (i = 0; i < n; i++) data[2 + i] = values[i]; + +- switch (n) { +- case 1: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x", +- reg, data[2]); +- break; +- case 2: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", +- reg, data[3], data[2]); +- break; +- case 4: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", +- reg, data[5], data[4], data[3], data[2]); +- break; +- default: +- v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", +- n, reg); +- } ++ err = i2c_transfer(client->adapter, &msg, 1); ++ if (err != 1) { ++ v4l2_err(sd, "%s: writing register0x%x from0x%x failed\n", __func__, reg, ++ client->addr); ++ return -1; ++ } ++ return 0; + } + +-static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) +-{ +- __le32 val = 0; ++static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) { ++ u8 val; + +- i2c_rd(sd, reg, (u8 __force *)&val, n); ++ i2c_rd(sd, reg, &val, 1); + +- return le32_to_cpu(val); ++ return val; + } + +-static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n) +-{ +- __le32 raw = cpu_to_le32(val); +- +- i2c_wr(sd, reg, (u8 __force *)&raw, n); ++static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) { ++ i2c_wr(sd, reg, &val, 1); + } + +-static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 1); ++static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, u8 mask, u8 val) { ++ i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); + } + +-static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) +-{ +- i2c_wrreg(sd, reg, val, 1); +-} ++static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) { ++ u16 val; ++ int ret; ++ // v4l2_info(sd, "Reading i2c_rd16\n"); + +-static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, +- u8 mask, u8 val) +-{ +- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1); +-} ++ ret = i2c_rd(sd, reg, (u8 *)&val, 2); ++ // v4l2_info(sd, "RET %d\n", ret); + +-static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 2); +-} ++ if (ret == -1) { ++ // Read failed ++ return 99; // TODO. Make this better! ++ } + +-static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) +-{ +- i2c_wrreg(sd, reg, val, 2); ++ return val; + } + +-static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) +-{ +- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); ++static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) { ++ i2c_wr(sd, reg, (u8 *)&val, 2); + } + +-static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 4); ++static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, ++ u16 val) { ++ i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); + } + +-static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) +-{ +- i2c_wrreg(sd, reg, val, 4); ++static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) { ++ u32 val; ++ ++ i2c_rd(sd, reg, (u8 *)&val, 4); ++ ++ return val; + } + ++static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) { ++ i2c_wr(sd, reg, (u8 *)&val, 4); ++} + /* --------------- STATUS --------------- */ + +-static inline bool is_hdmi(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; ++static inline bool is_hdmi(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; + } + +-static inline bool tx_5v_power_present(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; ++static inline bool tx_5v_power_present(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; + } + +-static inline bool no_signal(struct v4l2_subdev *sd) +-{ +- return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); ++static inline bool no_signal(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); + } + +-static inline bool no_sync(struct v4l2_subdev *sd) +-{ +- return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); ++static inline bool no_sync(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); + } + +-static inline bool audio_present(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; ++static inline bool audio_present(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; + } + +-static int get_audio_sampling_rate(struct v4l2_subdev *sd) +-{ +- static const int code_to_rate[] = { +- 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, +- 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 +- }; ++static int get_audio_sampling_rate(struct v4l2_subdev *sd) { ++ static const int code_to_rate[] = { ++ 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, ++ 88200, 768000, 96000, 705600, 176400, 0, 192000, 0}; ++ v4l2_info(sd, "function %s\n", __func__); ++ /* Register FS_SET is not cleared when the cable is disconnected */ ++ if (no_signal(sd)) return 0; + +- /* Register FS_SET is not cleared when the cable is disconnected */ +- if (no_signal(sd)) +- return 0; ++ return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; ++} + +- return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; ++static unsigned tc358743_num_csi_lanes_in_use(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return ((i2c_rd32(sd, CSI_CONTROL) & MASK_NOL) >> 1) + 1; + } + + /* --------------- TIMINGS --------------- */ + +-static inline unsigned fps(const struct v4l2_bt_timings *t) +-{ +- if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) +- return 0; ++static inline unsigned fps(const struct v4l2_bt_timings *t) { ++ if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) return 0; + +- return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, +- V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); ++ return DIV_ROUND_CLOSEST( ++ (unsigned)t->pixelclock, ++ V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); + } + + static int tc358743_get_detected_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct v4l2_bt_timings *bt = &timings->bt; +- unsigned width, height, frame_width, frame_height, frame_interval, fps; +- +- memset(timings, 0, sizeof(struct v4l2_dv_timings)); +- +- if (no_signal(sd)) { +- v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); +- return -ENOLINK; +- } +- if (no_sync(sd)) { +- v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); +- return -ENOLCK; +- } +- +- timings->type = V4L2_DV_BT_656_1120; +- bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? +- V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; +- +- width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + +- i2c_rd8(sd, DE_WIDTH_H_LO); +- height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + +- i2c_rd8(sd, DE_WIDTH_V_LO); +- frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + +- i2c_rd8(sd, H_SIZE_LO); +- frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + +- i2c_rd8(sd, V_SIZE_LO)) / 2; +- /* frame interval in milliseconds * 10 +- * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ +- frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + +- i2c_rd8(sd, FV_CNT_LO); +- fps = (frame_interval > 0) ? +- DIV_ROUND_CLOSEST(10000, frame_interval) : 0; +- +- bt->width = width; +- bt->height = height; +- bt->vsync = frame_height - height; +- bt->hsync = frame_width - width; +- bt->pixelclock = frame_width * frame_height * fps; +- if (bt->interlaced == V4L2_DV_INTERLACED) { +- bt->height *= 2; +- bt->il_vsync = bt->vsync + 1; +- bt->pixelclock /= 2; +- } +- +- return 0; ++ struct v4l2_dv_timings *timings) { ++ struct v4l2_bt_timings *bt = &timings->bt; ++ unsigned width, height, frame_width, frame_height, frame_interval, fps; ++ ++ memset(timings, 0, sizeof(struct v4l2_dv_timings)); ++ ++ if (no_signal(sd)) { ++ v4l2_info(sd, "%s: no valid signal\n", __func__); ++ return -ENOLINK; ++ } ++ if (no_sync(sd)) { ++ v4l2_info(sd, "%s: no sync on signal\n", __func__); ++ return -ENOLCK; ++ } ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ++ ? V4L2_DV_INTERLACED ++ : V4L2_DV_PROGRESSIVE; ++ ++ width = ++ ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + i2c_rd8(sd, DE_WIDTH_H_LO); ++ height = ++ ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + i2c_rd8(sd, DE_WIDTH_V_LO); ++ frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + i2c_rd8(sd, H_SIZE_LO); ++ frame_height = ++ (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + i2c_rd8(sd, V_SIZE_LO)) / 2; ++ /* frame interval in milliseconds * 10 ++ * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ ++ frame_interval = ++ ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + i2c_rd8(sd, FV_CNT_LO); ++ fps = (frame_interval > 0) ? DIV_ROUND_CLOSEST(10000, frame_interval) : 0; ++ ++ bt->width = width; ++ bt->height = height; ++ bt->vsync = frame_height - height; ++ bt->hsync = frame_width - width; ++ bt->pixelclock = frame_width * frame_height * fps; ++ if (bt->interlaced == V4L2_DV_INTERLACED) { ++ bt->height *= 2; ++ bt->il_vsync = bt->vsync + 1; ++ bt->pixelclock /= 2; ++ } ++ v4l2_info(sd, "%d:%s: width %d heigh %d interlaced %d\n", __LINE__, ++ __FUNCTION__, bt->width, bt->height, bt->interlaced); ++ return 0; + } +- + /* --------------- HOTPLUG / HDCP / EDID --------------- */ + +-static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) +-{ +- struct delayed_work *dwork = to_delayed_work(work); +- struct tc358743_state *state = container_of(dwork, +- struct tc358743_state, delayed_work_enable_hotplug); +- struct v4l2_subdev *sd = &state->sd; ++static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) { ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct tc358743_state *state = ++ container_of(dwork, struct tc358743_state, delayed_work_enable_hotplug); ++ struct v4l2_subdev *sd = &state->sd; + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ v4l2_info(sd, "%s:\n", __func__); + +- i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); ++ /*hainh ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_CTL0, MASK_HPD_CTL0); ++ */ + } + +-static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) +-{ +- v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? +- "enable" : "disable"); ++static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) { ++ v4l2_info(sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + +- if (enable) { +- i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, KEY_RD_CMD); ++ i2c_wr8_and_or(sd, HDCP_REG1, ~(MASK_AUTH_UNAUTH_SEL | MASK_AUTH_UNAUTH), ++ MASK_AUTH_UNAUTH_SEL_16_FRAMES | MASK_AUTH_UNAUTH_AUTO); + +- i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, 0); ++ i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, ++ SET_AUTO_P3_RESET_FRAMES(0x0f)); + +- i2c_wr8_and_or(sd, HDCP_REG1, 0xff, +- MASK_AUTH_UNAUTH_SEL_16_FRAMES | +- MASK_AUTH_UNAUTH_AUTO); ++ /* HDCP is disabled by configuring the receiver as HDCP repeater. The ++ * repeater mode require software support to work, so HDCP ++ * authentication will fail. */ ++ i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, enable ? KEY_RD_CMD : 0); ++ i2c_wr8_and_or(sd, HDCP_MODE, ~(MASK_AUTO_CLR | MASK_MODE_RST_TN), ++ enable ? (MASK_AUTO_CLR | MASK_MODE_RST_TN) : 0); + +- i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, +- SET_AUTO_P3_RESET_FRAMES(0x0f)); +- } else { +- i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, +- MASK_MANUAL_AUTHENTICATION); +- } ++ /* Apple MacBook Pro gen.8 has a bug that makes it freeze every fifth ++ * second when HDCP is disabled, but the MAX_EXCED bit is handled ++ * correctly and HDCP is disabled on the HDMI output. */ ++ i2c_wr8_and_or(sd, BSTATUS1, ~MASK_MAX_EXCED, enable ? 0 : MASK_MAX_EXCED); ++ i2c_wr8_and_or(sd, BCAPS, ~(MASK_REPEATER | MASK_READY), ++ enable ? 0 : MASK_REPEATER | MASK_READY); + } + +-static void tc358743_disable_edid(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++static void tc358743_disable_edid(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "%s:\n", __func__); + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + +- cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); ++ /* DDC access to EDID is also disabled when hotplug is disabled. See ++ * register DDC_CTL */ ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); ++} + +- /* DDC access to EDID is also disabled when hotplug is disabled. See +- * register DDC_CTL */ +- i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); ++static void tc358743_erase_bksv(struct v4l2_subdev *sd) { ++ int i; ++ v4l2_info(sd, "function %s\n", __func__); ++ for (i = 0; i < 5; i++) i2c_wr8(sd, BKSV + i, 0); + } + +-static void tc358743_enable_edid(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++/* --------------- AVI infoframe --------------- */ + +- if (state->edid_blocks_written == 0) { +- v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); +- tc358743_s_ctrl_detect_tx_5v(sd); +- return; +- } ++static void print_avi_infoframe(struct v4l2_subdev *sd) { ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct device *dev = &client->dev; ++ union hdmi_infoframe frame; ++ u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; ++ v4l2_info(sd, "function %s\n", __func__); ++ if (!is_hdmi(sd)) { ++ v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); ++ return; ++ } + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); + +- /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when +- * hotplug is enabled. See register DDC_CTL */ +- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); ++ if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { ++ v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); ++ return; ++ } + +- tc358743_enable_interrupts(sd, true); +- tc358743_s_ctrl_detect_tx_5v(sd); ++ hdmi_infoframe_log(KERN_INFO, dev, &frame); + } + +-static void tc358743_erase_bksv(struct v4l2_subdev *sd) +-{ +- int i; ++/* --------------- CTRLS --------------- */ + +- for (i = 0; i < 5; i++) +- i2c_wr8(sd, BKSV + i, 0); ++static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, tx_5v_power_present(sd)); ++} ++ ++static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, ++ get_audio_sampling_rate(sd)); ++} ++ ++static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, audio_present(sd)); ++} ++ ++static int tc358743_update_controls(struct v4l2_subdev *sd) { ++ int ret = 0; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ ret |= tc358743_s_ctrl_detect_tx_5v(sd); ++ ret |= tc358743_s_ctrl_audio_sampling_rate(sd); ++ ret |= tc358743_s_ctrl_audio_present(sd); ++ ++ return ret; ++} ++ ++static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) { ++ // return 2; ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_bt_timings *bt = &state->timings.bt; ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u32 bits_pr_pixel = ++ (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; ++ u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; ++ u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; ++ v4l2_info(sd, "function %s bps %d bps_pr_lane%d \n", __func__, bps, ++ bps_pr_lane); ++ return DIV_ROUND_UP(bps, bps_pr_lane); ++} ++ ++static int tc358743_get_edid(struct v4l2_subdev *sd) { ++ u8 edid_read[256]; ++ int result = 0; ++ u32 n = sizeof(edid_read); ++ ++ result = i2c_rd(sd, EDID_RAM, edid_read, n); ++ v4l2_info(sd, "%s i2c_rd return %d\r\n", __func__, result); ++ v4l2_info(sd, "%s done\r\n", __func__); ++ return 0; ++} ++ ++static int tc358743_log_status(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings timings; ++ uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); ++ uint16_t sysctl = i2c_rd16(sd, SYSCTL); ++ u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); ++ const int deep_color_mode[4] = {8, 10, 12, 16}; ++ static const char *const input_color_space[] = { ++ "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", ++ "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", ++ "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; ++ tc358743_get_edid(sd); ++ v4l2_info(sd, "-----Chip status-----\n"); ++ v4l2_info(sd, "Chip ID:0x%02x\n", (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); ++ v4l2_info(sd, "Chip revision:0x%02x\n", i2c_rd16(sd, CHIPID) & MASK_REVID); ++ v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", ++ !!(sysctl & MASK_IRRST), !!(sysctl & MASK_CECRST), ++ !!(sysctl & MASK_CTXRST), !!(sysctl & MASK_HDMIRST)); ++ v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); ++ v4l2_info(sd, "Cable detected (+5V power): %s\n", ++ hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); ++ v4l2_info(sd, "DDC lines enabled: %s\n", ++ (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? "yes" : "no"); ++ v4l2_info(sd, "Hotplug enabled: %s\n", ++ (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? "yes" : "no"); ++ v4l2_info(sd, "CEC enabled: %s\n", ++ (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); ++ v4l2_info(sd, "-----Signal status-----\n"); ++ v4l2_info(sd, "TMDS signal detected: %s\n", ++ hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); ++ v4l2_info(sd, "Stable sync signal: %s\n", ++ hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); ++ v4l2_info(sd, "PHY PLL locked: %s\n", ++ hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); ++ v4l2_info(sd, "PHY DE detected: %s\n", ++ hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); ++ ++ if (tc358743_get_detected_timings(sd, &timings)) { ++ v4l2_info(sd, "No video detected\n"); ++ } else { ++ v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, true); ++ } ++ v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, true); ++ ++ v4l2_info(sd, "-----CSI-TX status-----\n"); ++ v4l2_info(sd, "Lanes needed: %d\n", tc358743_num_csi_lanes_needed(sd)); ++ v4l2_info(sd, "Lanes in use: %d\n", tc358743_num_csi_lanes_in_use(sd)); ++ v4l2_info(sd, "Waiting for particular sync signal: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? "yes" : "no"); ++ v4l2_info(sd, "Transmit mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? "yes" : "no"); ++ v4l2_info(sd, "Receive mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? "yes" : "no"); ++ v4l2_info(sd, "Stopped: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? "yes" : "no"); ++ v4l2_info(sd, "Color space: %s\n", ++ state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ++ ? "YCbCr 422 16-bit" ++ : state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ++ ? "RGB 888 24-bit" ++ : "Unsupported"); ++ ++ v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); ++ v4l2_info(sd, "HDCP encrypted content: %s\n", ++ hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); ++ v4l2_info(sd, "Input color space: %s %s range\n", ++ input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], ++ (vi_status3 & MASK_LIMITED) ? "limited" : "full"); ++ if (!is_hdmi(sd)) return 0; ++ v4l2_info(sd, "AV Mute: %s\n", ++ hdmi_sys_status & MASK_S_AVMUTE ? "on" : "off"); ++ v4l2_info(sd, "Deep color mode: %d-bits per channel\n", ++ deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & MASK_S_DEEPCOLOR) >> 2]); ++ print_avi_infoframe(sd); ++ ++ return 0; + } + +-/* --------------- AVI infoframe --------------- */ ++/* --------------- INIT --------------- */ + +-static void print_avi_infoframe(struct v4l2_subdev *sd) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(sd); +- struct device *dev = &client->dev; +- union hdmi_infoframe frame; +- u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; ++static void tc358743_reset_phy(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); ++ i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); ++} ++ ++static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) { ++ u16 sysctl = i2c_rd16(sd, SYSCTL); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr16(sd, SYSCTL, sysctl | mask); ++ i2c_wr16(sd, SYSCTL, sysctl & ~mask); ++} ++ ++static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, enable ? MASK_SLEEP : 0); ++} ++ ++static inline void enable_stream(struct v4l2_subdev *sd, bool enable) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "LDS> Transmit mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? "yes" : "no"); ++ v4l2_info(sd, "LDS> %s: %sable\n", __func__, enable ? "en" : "dis"); ++ ++ if (enable) { ++ /* It is critical for CSI receiver to see lane transition ++ * LP11->HS. Set to non-continuous mode to enable clock lane ++ * LP11 state. */ ++ i2c_wr32(sd, TXOPTIONCNTRL, 0); ++ /* Set to continuous mode to trigger LP11->HS transition */ ++ i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); ++ /* Unmute video */ ++ i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); ++ } else { ++ /* Mute video so that all data lanes go to LSP11 state. ++ * No data is output to CSI Tx block. */ ++ i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); ++ } ++ ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), ++ enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); ++ mutex_unlock(&state->confctl_mutex); ++ v4l2_info(sd, "%d:%s: end\n", __LINE__, __FUNCTION__); ++} ++ ++static void tc358743_set_pll(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u16 pllctl0 = i2c_rd16(sd, PLLCTL0); ++ u16 pllctl1 = i2c_rd16(sd, PLLCTL1); ++ u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | SET_PLL_FBD(pdata->pll_fbd); ++ u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; ++ ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ /* Only rewrite when needed (new value or disabled), since rewriting ++ * triggers another format change event. */ ++ if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { ++ u16 pll_frs; ++ ++ if (hsck > 500000000) ++ pll_frs = 0x0; ++ else if (hsck > 250000000) ++ pll_frs = 0x1; ++ else if (hsck > 125000000) ++ pll_frs = 0x2; ++ else ++ pll_frs = 0x3; ++ v4l2_info(sd, "%s: updating PLL clock\n", __func__); ++ tc358743_sleep_mode(sd, true); ++ i2c_wr16(sd, PLLCTL0, pllctl0_new); ++ i2c_wr16_and_or(sd, PLLCTL1, ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), ++ (SET_PLL_FRS(pll_frs) | MASK_RESETB | MASK_PLL_EN)); ++ udelay(10); /* REF_02, Sheet "Source HDMI" */ ++ i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); ++ tc358743_sleep_mode(sd, false); ++ } ++} ++ ++static void tc358743_set_ref_clk(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u32 sys_freq; ++ u32 lockdet_ref; ++ u16 fh_min; ++ u16 fh_max; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ sys_freq = pdata->refclk_hz / 10000; ++ i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); ++ i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); ++ ++ i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, ++ (pdata->refclk_hz == 42000000) ? MASK_PHY_SYSCLK_IND : 0x0); ++ ++ fh_min = pdata->refclk_hz / 100000; ++ i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); ++ i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); ++ ++ fh_max = (fh_min * 66) / 10; ++ i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); ++ i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); ++ ++ lockdet_ref = pdata->refclk_hz / 100; ++ i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); ++ i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); ++ i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); ++ ++ i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, ++ (pdata->refclk_hz == 27000000) ? MASK_NCO_F0_MOD_27MHZ : 0x0); ++} ++ ++static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ switch (state->mbus_fmt_code) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ v4l2_info(sd, "%s: YCbCr 422 16-bit\n", __func__); ++ i2c_wr8_and_or(sd, VOUT_SET2, ++ ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, ++ MASK_SEL422 | MASK_VOUT_422FIL_100); ++ i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, ++ MASK_VOUT_COLOR_601_YCBCR_LIMITED); ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, MASK_YCBCRFMT_422_8_BIT); ++ mutex_unlock(&state->confctl_mutex); ++ break; ++ ++ // v4l2_info(sd, "LDS> %s: TEST pattern\n", __func__); ++ // i2c_wr8_and_or(sd, VOUT_SET2, ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, MASK_SEL422 | MASK_VOUT_422FIL_100); ++ // i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, MASK_VOUT_COLOR_601_YCBCR_LIMITED); ++ // mutex_lock(&state->confctl_mutex); ++ // i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, MASK_YCBCRFMT_COLORBAR); ++ // mutex_unlock(&state->confctl_mutex); ++ // break; ++ ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ v4l2_info(sd, "%s: RGB 888 24-bit\n", __func__); ++ i2c_wr8_and_or(sd, VOUT_SET2, ++ ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, 0x00); ++ i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, ++ MASK_VOUT_COLOR_RGB_FULL); ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); ++ mutex_unlock(&state->confctl_mutex); ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", __func__, ++ state->mbus_fmt_code); ++ break; ++ } ++ ++ // enable_stream(sd, true); // Just put here for testing ++} ++ ++static void tc358743_set_csi(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ unsigned lanes = tc358743_num_csi_lanes_needed(sd); ++ ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ tc358743_reset(sd, MASK_CTXRST); ++ ++ if (lanes < 1) i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); ++ if (lanes < 1) i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); ++ if (lanes < 2) i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); ++ if (lanes < 3) i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); ++ if (lanes < 4) i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); ++ ++ i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); ++ i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); ++ i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); ++ i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); ++ i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); ++ i2c_wr32(sd, TWAKEUP, pdata->twakeup); ++ i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); ++ i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); ++ i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); ++ ++ i2c_wr32(sd, HSTXVREGEN, ++ ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | ++ ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | ++ ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | ++ ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | ++ ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); ++ ++ i2c_wr32( ++ sd, TXOPTIONCNTRL, ++ (pdata->endpoint.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ++ ? MASK_CONTCLKMODE ++ : 0); ++ i2c_wr32(sd, STARTCNTRL, MASK_START); ++ i2c_wr32(sd, CSI_START, MASK_STRT); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_CONTROL | MASK_CSI_MODE | ++ MASK_TXHSMD | ++ ((lanes == 4) ++ ? MASK_NOL_4 ++ : (lanes == 3) ? MASK_NOL_3 ++ : (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | ++ MASK_QUNK | MASK_WCER | MASK_INER); ++ ++ i2c_wr32( ++ sd, CSI_CONFW, ++ MASK_MODE_CLEAR | MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); ++} ++ ++static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* Default settings from REF_02, sheet "Source HDMI" ++ * and custom settings as platform data */ ++ // turn of physics ++ i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); ++ i2c_wr8(sd, PHY_CTL1, ++ SET_PHY_AUTO_RST1_US(1600) | SET_FREQ_RANGE_MODE_CYCLES(1)); ++ i2c_wr8_and_or( ++ sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, ++ (pdata->hdmi_phy_auto_reset_tmds_detected ? MASK_PHY_AUTO_RST2 : 0) | ++ (pdata->hdmi_phy_auto_reset_tmds_in_range ? MASK_PHY_AUTO_RST3 : 0) | ++ (pdata->hdmi_phy_auto_reset_tmds_valid ? MASK_PHY_AUTO_RST4 : 0)); ++ i2c_wr8(sd, PHY_BIAS, 0x40); ++ i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); ++ i2c_wr8(sd, AVM_CTL, 45); ++ i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, ++ pdata->hdmi_detection_delay << 4); ++ ++ i2c_wr8_and_or( ++ sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), ++ (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? MASK_H_PI_RST : 0) | ++ (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? MASK_V_PI_RST : 0)); ++ // turn on physics ++ i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); ++} ++ ++static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* Default settings from REF_02, sheet "Source HDMI" */ ++ i2c_wr8(sd, FORCE_MUTE, 0x00); ++ i2c_wr8(sd, AUTO_CMD0, ++ MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | MASK_AUTO_MUTE5 | ++ MASK_AUTO_MUTE4 | MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); ++ i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); ++ i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); ++ i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); ++ i2c_wr8(sd, FS_MUTE, 0x00); ++ i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); ++ i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); ++ i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); ++ i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); ++ i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); ++ i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); ++ ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, 0xffff, ++ MASK_AUDCHNUM_2 | MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); ++ mutex_unlock(&state->confctl_mutex); ++} ++ ++static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) { ++ /* Default settings from REF_02, sheet "Source HDMI" */ ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr8(sd, PK_INT_MODE, ++ MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | MASK_ACP_INT_MODE | ++ MASK_VS_INT_MODE | MASK_SPD_INT_MODE | MASK_MS_INT_MODE | ++ MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); ++ i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); ++ i2c_wr8(sd, NO_PKT_CLR, 0x53); ++ i2c_wr8(sd, ERR_PK_LIMIT, 0x01); ++ i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); ++ i2c_wr8(sd, NO_GDB_LIMIT, 0x10); ++} ++ ++static void tc358743_initial_setup(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* CEC and IR are not supported by this driver */ ++ i2c_wr16_and_or(sd, SYSCTL, ~(MASK_CECRST | MASK_IRRST), ++ (MASK_CECRST | MASK_IRRST)); ++ ++ tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); ++ tc358743_sleep_mode(sd, false); ++ ++ i2c_wr16(sd, FIFOCTL, pdata->fifo_level); ++ ++ tc358743_set_ref_clk(sd); ++ ++ i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, ++ pdata->ddc5v_delay & MASK_DDC5V_MODE); ++ ++ i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); ++ ++ tc358743_set_hdmi_phy(sd); ++ tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); ++ tc358743_set_hdmi_audio(sd); ++ tc358743_set_hdmi_info_frame_mode(sd); ++ ++ /* All CE and IT formats are detected as RGB full range in DVI mode */ ++ i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); ++ ++ i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, MASK_VOUTCOLORMODE_AUTO); ++ i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); ++} + +- if (!is_hdmi(sd)) { +- v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); +- return; +- } ++/* --------------- IRQ --------------- */ + +- i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); ++static void tc358743_format_change(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings timings; ++ const struct v4l2_event tc358743_ev_fmt = { ++ .type = V4L2_EVENT_SOURCE_CHANGE, ++ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, ++ }; + +- if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { +- v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); +- return; +- } ++ v4l2_info(sd, "%s: Format changed\n", __func__); + +- hdmi_infoframe_log(KERN_INFO, dev, &frame); +-} ++ if (tc358743_get_detected_timings(sd, &timings)) { ++ enable_stream(sd, false); + +-/* --------------- CTRLS --------------- */ ++ v4l2_info(sd, "%s: Format changed. No signal\n", __func__); ++ } else { ++ if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false)) ++ enable_stream(sd, false); + +-static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++ v4l2_print_dv_timings( ++ sd->name, ++ "tc358743_format_change: Format change`d. New format: ", &timings, ++ false); ++ } + +- return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, +- tx_5v_power_present(sd)); ++ if (sd->devnode) v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); + } + +-static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++static void tc358743_init_interrupts(struct v4l2_subdev *sd) { ++ u16 i; ++ v4l2_info(sd, "function %s\n", __func__); + +- return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, +- get_audio_sampling_rate(sd)); +-} ++ /* clear interrupt status registers */ ++ for (i = SYS_INT; i <= KEY_INT; i++) i2c_wr8(sd, i, 0xff); + +-static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++ i2c_wr16(sd, INTSTATUS, 0xffff); ++} + +- return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, +- audio_present(sd)); ++static void tc358743_enable_interrupts(struct v4l2_subdev *sd, ++ bool cable_connected) { ++ v4l2_info(sd, "%s: cable connected = %d\n", __func__, cable_connected); ++ ++ if (cable_connected) { ++ i2c_wr8(sd, SYS_INTM, ++ ~(MASK_M_DDC | MASK_M_DVI_DET | MASK_M_HDMI_DET) & 0xff); ++ i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); ++ i2c_wr8(sd, CBIT_INTM, ++ ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | MASK_M_AF_UNLOCK) & 0xff); ++ i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); ++ i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); ++ } else { ++ i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); ++ i2c_wr8(sd, CLK_INTM, 0xff); ++ i2c_wr8(sd, CBIT_INTM, 0xff); ++ i2c_wr8(sd, AUDIO_INTM, 0xff); ++ i2c_wr8(sd, MISC_INTM, 0xff); ++ } + } + +-static int tc358743_update_controls(struct v4l2_subdev *sd) +-{ +- int ret = 0; ++static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); ++ u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; ++ ++ i2c_wr8(sd, AUDIO_INT, audio_int); + +- ret |= tc358743_s_ctrl_detect_tx_5v(sd); +- ret |= tc358743_s_ctrl_audio_sampling_rate(sd); +- ret |= tc358743_s_ctrl_audio_present(sd); ++ v4l2_info(sd, "%s: AUDIO_INT =0x%02x\n", __func__, audio_int); + +- return ret; ++ tc358743_s_ctrl_audio_sampling_rate(sd); ++ tc358743_s_ctrl_audio_present(sd); + } + +-/* --------------- INIT --------------- */ +- +-static void tc358743_reset_phy(struct v4l2_subdev *sd) +-{ +- v4l2_dbg(1, debug, sd, "%s:\n", __func__); +- +- i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); +- i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); +-} +- +-static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) +-{ +- u16 sysctl = i2c_rd16(sd, SYSCTL); +- +- i2c_wr16(sd, SYSCTL, sysctl | mask); +- i2c_wr16(sd, SYSCTL, sysctl & ~mask); +-} +- +-static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) +-{ +- i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, +- enable ? MASK_SLEEP : 0); +-} +- +-static inline void enable_stream(struct v4l2_subdev *sd, bool enable) +-{ +- struct tc358743_state *state = to_state(sd); +- +- v4l2_dbg(3, debug, sd, "%s: %sable\n", +- __func__, enable ? "en" : "dis"); +- +- if (enable) { +- /* It is critical for CSI receiver to see lane transition +- * LP11->HS. Set to non-continuous mode to enable clock lane +- * LP11 state. */ +- i2c_wr32(sd, TXOPTIONCNTRL, 0); +- /* Set to continuous mode to trigger LP11->HS transition */ +- i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); +- /* Unmute video */ +- i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); +- } else { +- /* Mute video so that all data lanes go to LSP11 state. +- * No data is output to CSI Tx block. */ +- i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); +- } +- +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), +- enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); +- mutex_unlock(&state->confctl_mutex); +-} +- +-static void tc358743_set_pll(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- u16 pllctl0 = i2c_rd16(sd, PLLCTL0); +- u16 pllctl1 = i2c_rd16(sd, PLLCTL1); +- u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | +- SET_PLL_FBD(pdata->pll_fbd); +- u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; +- +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); +- +- /* Only rewrite when needed (new value or disabled), since rewriting +- * triggers another format change event. */ +- if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { +- u16 pll_frs; +- +- if (hsck > 500000000) +- pll_frs = 0x0; +- else if (hsck > 250000000) +- pll_frs = 0x1; +- else if (hsck > 125000000) +- pll_frs = 0x2; +- else +- pll_frs = 0x3; +- +- v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); +- tc358743_sleep_mode(sd, true); +- i2c_wr16(sd, PLLCTL0, pllctl0_new); +- i2c_wr16_and_or(sd, PLLCTL1, +- ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), +- (SET_PLL_FRS(pll_frs) | MASK_RESETB | +- MASK_PLL_EN)); +- udelay(10); /* REF_02, Sheet "Source HDMI" */ +- i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); +- tc358743_sleep_mode(sd, false); +- } +-} +- +-static void tc358743_set_ref_clk(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- u32 sys_freq; +- u32 lockdet_ref; +- u32 cec_freq; +- u16 fh_min; +- u16 fh_max; +- +- BUG_ON(!(pdata->refclk_hz == 26000000 || +- pdata->refclk_hz == 27000000 || +- pdata->refclk_hz == 42000000)); +- +- sys_freq = pdata->refclk_hz / 10000; +- i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); +- i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); +- +- i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, +- (pdata->refclk_hz == 42000000) ? +- MASK_PHY_SYSCLK_IND : 0x0); +- +- fh_min = pdata->refclk_hz / 100000; +- i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); +- i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); +- +- fh_max = (fh_min * 66) / 10; +- i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); +- i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); +- +- lockdet_ref = pdata->refclk_hz / 100; +- i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); +- i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); +- i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); +- +- i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, +- (pdata->refclk_hz == 27000000) ? +- MASK_NCO_F0_MOD_27MHZ : 0x0); +- +- /* +- * Trial and error suggests that the default register value +- * of 656 is for a 42 MHz reference clock. Use that to derive +- * a new value based on the actual reference clock. +- */ +- cec_freq = (656 * sys_freq) / 4200; +- i2c_wr16(sd, CECHCLK, cec_freq); +- i2c_wr16(sd, CECLCLK, cec_freq); +-} +- +-static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- +- switch (state->mbus_fmt_code) { +- case MEDIA_BUS_FMT_UYVY8_1X16: +- v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); +- i2c_wr8_and_or(sd, VOUT_SET2, +- ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, +- MASK_SEL422 | MASK_VOUT_422FIL_100); +- i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, +- MASK_VOUT_COLOR_601_YCBCR_LIMITED); +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, +- MASK_YCBCRFMT_422_8_BIT); +- mutex_unlock(&state->confctl_mutex); +- break; +- case MEDIA_BUS_FMT_RGB888_1X24: +- v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); +- i2c_wr8_and_or(sd, VOUT_SET2, +- ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, +- 0x00); +- i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, +- MASK_VOUT_COLOR_RGB_FULL); +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); +- mutex_unlock(&state->confctl_mutex); +- break; +- default: +- v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", +- __func__, state->mbus_fmt_code); +- } +-} +- +-static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_bt_timings *bt = &state->timings.bt; +- struct tc358743_platform_data *pdata = &state->pdata; +- u32 bits_pr_pixel = +- (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; +- u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; +- u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; +- +- return DIV_ROUND_UP(bps, bps_pr_lane); +-} +- +-static void tc358743_set_csi(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- unsigned lanes = tc358743_num_csi_lanes_needed(sd); +- +- v4l2_dbg(3, debug, sd, "%s:\n", __func__); +- +- state->csi_lanes_in_use = lanes; +- +- tc358743_reset(sd, MASK_CTXRST); +- +- if (lanes < 1) +- i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); +- if (lanes < 1) +- i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); +- if (lanes < 2) +- i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); +- if (lanes < 3) +- i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); +- if (lanes < 4) +- i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); +- +- i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); +- i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); +- i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); +- i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); +- i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); +- i2c_wr32(sd, TWAKEUP, pdata->twakeup); +- i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); +- i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); +- i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); +- +- i2c_wr32(sd, HSTXVREGEN, +- ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | +- ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | +- ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | +- ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | +- ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); +- +- i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & +- V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); +- i2c_wr32(sd, STARTCNTRL, MASK_START); +- i2c_wr32(sd, CSI_START, MASK_STRT); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_CONTROL | +- MASK_CSI_MODE | +- MASK_TXHSMD | +- ((lanes == 4) ? MASK_NOL_4 : +- (lanes == 3) ? MASK_NOL_3 : +- (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | +- MASK_WCER | MASK_INER); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | +- MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); +-} +- +-static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- +- /* Default settings from REF_02, sheet "Source HDMI" +- * and custom settings as platform data */ +- i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); +- i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | +- SET_FREQ_RANGE_MODE_CYCLES(1)); +- i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, +- (pdata->hdmi_phy_auto_reset_tmds_detected ? +- MASK_PHY_AUTO_RST2 : 0) | +- (pdata->hdmi_phy_auto_reset_tmds_in_range ? +- MASK_PHY_AUTO_RST3 : 0) | +- (pdata->hdmi_phy_auto_reset_tmds_valid ? +- MASK_PHY_AUTO_RST4 : 0)); +- i2c_wr8(sd, PHY_BIAS, 0x40); +- i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); +- i2c_wr8(sd, AVM_CTL, 45); +- i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, +- pdata->hdmi_detection_delay << 4); +- i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), +- (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? +- MASK_H_PI_RST : 0) | +- (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? +- MASK_V_PI_RST : 0)); +- i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); +-} +- +-static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- +- /* Default settings from REF_02, sheet "Source HDMI" */ +- i2c_wr8(sd, FORCE_MUTE, 0x00); +- i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | +- MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | +- MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); +- i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); +- i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); +- i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); +- i2c_wr8(sd, FS_MUTE, 0x00); +- i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); +- i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); +- i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); +- i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); +- i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); +- i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); +- +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | +- MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); +- mutex_unlock(&state->confctl_mutex); +-} +- +-static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) +-{ +- /* Default settings from REF_02, sheet "Source HDMI" */ +- i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | +- MASK_ACP_INT_MODE | MASK_VS_INT_MODE | +- MASK_SPD_INT_MODE | MASK_MS_INT_MODE | +- MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); +- i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); +- i2c_wr8(sd, NO_PKT_CLR, 0x53); +- i2c_wr8(sd, ERR_PK_LIMIT, 0x01); +- i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); +- i2c_wr8(sd, NO_GDB_LIMIT, 0x10); +-} +- +-static void tc358743_initial_setup(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- +- /* +- * IR is not supported by this driver. +- * CEC is only enabled if needed. +- */ +- i2c_wr16_and_or(sd, SYSCTL, ~(MASK_IRRST | MASK_CECRST), +- (MASK_IRRST | MASK_CECRST)); +- +- tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); +-#ifdef CONFIG_VIDEO_TC358743_CEC +- tc358743_reset(sd, MASK_CECRST); +-#endif +- tc358743_sleep_mode(sd, false); +- +- i2c_wr16(sd, FIFOCTL, pdata->fifo_level); +- +- tc358743_set_ref_clk(sd); +- +- i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, +- pdata->ddc5v_delay & MASK_DDC5V_MODE); +- i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); +- +- tc358743_set_hdmi_phy(sd); +- tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); +- tc358743_set_hdmi_audio(sd); +- tc358743_set_hdmi_info_frame_mode(sd); +- +- /* All CE and IT formats are detected as RGB full range in DVI mode */ +- i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); +- +- i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, +- MASK_VOUTCOLORMODE_AUTO); +- i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); +-} +- +-/* --------------- CEC --------------- */ +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +-static int tc358743_cec_adap_enable(struct cec_adapter *adap, bool enable) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- +- i2c_wr32(sd, CECIMSK, enable ? MASK_CECTIM | MASK_CECRIM : 0); +- i2c_wr32(sd, CECICLR, MASK_CECTICLR | MASK_CECRICLR); +- i2c_wr32(sd, CECEN, enable); +- if (enable) +- i2c_wr32(sd, CECREN, MASK_CECREN); +- return 0; +-} +- +-static int tc358743_cec_adap_monitor_all_enable(struct cec_adapter *adap, +- bool enable) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- u32 reg; +- +- reg = i2c_rd32(sd, CECRCTL1); +- if (enable) +- reg |= MASK_CECOTH; +- else +- reg &= ~MASK_CECOTH; +- i2c_wr32(sd, CECRCTL1, reg); +- return 0; +-} +- +-static int tc358743_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- unsigned int la = 0; +- +- if (log_addr != CEC_LOG_ADDR_INVALID) { +- la = i2c_rd32(sd, CECADD); +- la |= 1 << log_addr; +- } +- i2c_wr32(sd, CECADD, la); +- return 0; +-} +- +-static int tc358743_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, +- u32 signal_free_time, struct cec_msg *msg) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- unsigned int i; +- +- i2c_wr32(sd, CECTCTL, +- (cec_msg_is_broadcast(msg) ? MASK_CECBRD : 0) | +- (signal_free_time - 1)); +- for (i = 0; i < msg->len; i++) +- i2c_wr32(sd, CECTBUF1 + i * 4, +- msg->msg[i] | ((i == msg->len - 1) ? MASK_CECTEOM : 0)); +- i2c_wr32(sd, CECTEN, MASK_CECTEN); +- return 0; +-} +- +-static const struct cec_adap_ops tc358743_cec_adap_ops = { +- .adap_enable = tc358743_cec_adap_enable, +- .adap_log_addr = tc358743_cec_adap_log_addr, +- .adap_transmit = tc358743_cec_adap_transmit, +- .adap_monitor_all_enable = tc358743_cec_adap_monitor_all_enable, +-}; ++static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ v4l2_err(sd, "%s: CSI_ERR =0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); + +-static void tc358743_cec_handler(struct v4l2_subdev *sd, u16 intstatus, +- bool *handled) +-{ +- struct tc358743_state *state = to_state(sd); +- unsigned int cec_rxint, cec_txint; +- unsigned int clr = 0; +- +- cec_rxint = i2c_rd32(sd, CECRSTAT); +- cec_txint = i2c_rd32(sd, CECTSTAT); +- +- if (intstatus & MASK_CEC_RINT) +- clr |= MASK_CECRICLR; +- if (intstatus & MASK_CEC_TINT) +- clr |= MASK_CECTICLR; +- i2c_wr32(sd, CECICLR, clr); +- +- if ((intstatus & MASK_CEC_TINT) && cec_txint) { +- if (cec_txint & MASK_CECTIEND) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_OK); +- else if (cec_txint & MASK_CECTIAL) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_ARB_LOST); +- else if (cec_txint & MASK_CECTIACK) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_NACK); +- else if (cec_txint & MASK_CECTIUR) { +- /* +- * Not sure when this bit is set. Treat +- * it as an error for now. +- */ +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_ERROR); +- } +- if (handled) +- *handled = true; +- } +- if ((intstatus & MASK_CEC_RINT) && +- (cec_rxint & MASK_CECRIEND)) { +- struct cec_msg msg = {}; +- unsigned int i; +- unsigned int v; +- +- v = i2c_rd32(sd, CECRCTR); +- msg.len = v & 0x1f; +- for (i = 0; i < msg.len; i++) { +- v = i2c_rd32(sd, CECRBUF1 + i * 4); +- msg.msg[i] = v & 0xff; +- } +- cec_received_msg(state->cec_adap, &msg); +- if (handled) +- *handled = true; +- } +- i2c_wr16(sd, INTSTATUS, +- intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); ++ i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); + } + +-#endif ++static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); ++ u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; + +-/* --------------- IRQ --------------- */ ++ i2c_wr8(sd, MISC_INT, misc_int); + +-static void tc358743_format_change(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_dv_timings timings; +- const struct v4l2_event tc358743_ev_fmt = { +- .type = V4L2_EVENT_SOURCE_CHANGE, +- .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +- }; ++ v4l2_info(sd, "%s: MISC_INT =0x%02x\n", __func__, misc_int); + +- if (tc358743_get_detected_timings(sd, &timings)) { +- enable_stream(sd, false); ++ if (misc_int & MASK_I_SYNC_CHG) { ++ /* Reset the HDMI PHY to try to trigger proper lock on the ++ * incoming video format. Erase BKSV to prevent that old keys ++ * are used when a new source is connected. */ ++ if (no_sync(sd) || no_signal(sd)) { ++ tc358743_reset_phy(sd); ++ tc358743_erase_bksv(sd); ++ } + +- v4l2_dbg(1, debug, sd, "%s: No signal\n", +- __func__); +- } else { +- if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false)) +- enable_stream(sd, false); ++ tc358743_format_change(sd); + +- if (debug) +- v4l2_print_dv_timings(sd->name, +- "tc358743_format_change: New format: ", +- &timings, false); +- } ++ misc_int &= ~MASK_I_SYNC_CHG; ++ if (handled) *handled = true; ++ } + +- if (sd->devnode) +- v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); ++ if (misc_int) { ++ v4l2_err(sd, "%s: Unhandled MISC_INT interrupts:0x%02x\n", __func__, ++ misc_int); ++ } + } + +-static void tc358743_init_interrupts(struct v4l2_subdev *sd) +-{ +- u16 i; ++static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); ++ u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; + +- /* clear interrupt status registers */ +- for (i = SYS_INT; i <= KEY_INT; i++) +- i2c_wr8(sd, i, 0xff); ++ i2c_wr8(sd, CBIT_INT, cbit_int); + +- i2c_wr16(sd, INTSTATUS, 0xffff); +-} ++ v4l2_info(sd, "%s: CBIT_INT =0x%02x\n", __func__, cbit_int); + +-static void tc358743_enable_interrupts(struct v4l2_subdev *sd, +- bool cable_connected) +-{ +- v4l2_dbg(2, debug, sd, "%s: cable connected = %d\n", __func__, +- cable_connected); +- +- if (cable_connected) { +- i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_DVI_DET | +- MASK_M_HDMI_DET) & 0xff); +- i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); +- i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | +- MASK_M_AF_UNLOCK) & 0xff); +- i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); +- i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); +- } else { +- i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); +- i2c_wr8(sd, CLK_INTM, 0xff); +- i2c_wr8(sd, CBIT_INTM, 0xff); +- i2c_wr8(sd, AUDIO_INTM, 0xff); +- i2c_wr8(sd, MISC_INTM, 0xff); +- } +-} ++ if (cbit_int & MASK_I_CBIT_FS) { ++ v4l2_info(sd, "%s: Audio sample rate changed\n", __func__); ++ tc358743_s_ctrl_audio_sampling_rate(sd); + +-static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); +- u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; ++ cbit_int &= ~MASK_I_CBIT_FS; ++ if (handled) *handled = true; ++ } + +- i2c_wr8(sd, AUDIO_INT, audio_int); ++ if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { ++ v4l2_info(sd, "%s: Audio present changed\n", __func__); ++ tc358743_s_ctrl_audio_present(sd); + +- v4l2_dbg(3, debug, sd, "%s: AUDIO_INT = 0x%02x\n", __func__, audio_int); ++ cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); ++ if (handled) *handled = true; ++ } + +- tc358743_s_ctrl_audio_sampling_rate(sd); +- tc358743_s_ctrl_audio_present(sd); ++ if (cbit_int) { ++ v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts:0x%02x\n", __func__, ++ cbit_int); ++ } + } + +-static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); ++static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); ++ u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; ++ ++ /* Bit 7 and bit 6 are set even when they are masked */ ++ i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); ++ ++ v4l2_info(sd, "%s: CLK_INT =0x%02x\n", __func__, clk_int); ++ ++ if (clk_int & (MASK_I_IN_DE_CHG)) { ++ v4l2_info(sd, "%s: DE size or position has changed\n", __func__); + +- i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); ++ /* If the source switch to a new resolution with the same pixel ++ * frequency as the existing (e.g. 1080p25 -> 720p50), the ++ * I_SYNC_CHG interrupt is not always triggered, while the ++ * I_IN_DE_CHG interrupt seems to work fine. Format change ++ * notifications are only sent when the signal is stable to ++ * reduce the number of notifications. */ ++ if (!no_signal(sd) && !no_sync(sd)) tc358743_format_change(sd); ++ ++ clk_int &= ~(MASK_I_IN_DE_CHG); ++ if (handled) *handled = true; ++ } ++ ++ if (clk_int) { ++ v4l2_err(sd, "%s: Unhandled CLK_INT interrupts:0x%02x\n", __func__, ++ clk_int); ++ } + } + +-static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); +- u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; ++static void tc358743_enable_edid(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "%s\n", __func__); ++ if (state->edid_blocks_written == 0) { ++ v4l2_info(sd, "%s: no EDID -> no hotplug\n", __func__); ++ return; ++ } ++ ++ v4l2_info(sd, "%s:\n", __func__); + +- i2c_wr8(sd, MISC_INT, misc_int); ++ /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when ++ * hotplug is enabled. See register DDC_CTL */ ++ queue_delayed_work(state->work_queues, &state->delayed_work_enable_hotplug, ++ HZ / 10); + +- v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); ++ tc358743_enable_interrupts(sd, true); ++ tc358743_s_ctrl_detect_tx_5v(sd); ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++} + +- if (misc_int & MASK_I_SYNC_CHG) { +- /* Reset the HDMI PHY to try to trigger proper lock on the +- * incoming video format. Erase BKSV to prevent that old keys +- * are used when a new source is connected. */ +- if (no_sync(sd) || no_signal(sd)) { +- tc358743_reset_phy(sd); +- tc358743_erase_bksv(sd); +- } ++static void tc358743_delayed_work_enable_interrupt(struct work_struct *work) { ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct tc358743_state *state = ++ container_of(dwork, struct tc358743_state, delayed_work_enable_interrupt); ++ struct v4l2_subdev *sd = &state->sd; + +- tc358743_format_change(sd); ++ v4l2_dbg(2, debug, sd, "%s:\n", __func__); + +- misc_int &= ~MASK_I_SYNC_CHG; +- if (handled) +- *handled = true; +- } ++ tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); + +- if (misc_int) { +- v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", +- __func__, misc_int); +- } ++ // /* Temporary EDID. Should be set by userspace */ ++ // tc358743_s_edid(sd, &sd_edid); + } + +-static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); +- u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; ++static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ struct tc358743_state *state = to_state(sd); ++ u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); ++ u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; + +- i2c_wr8(sd, CBIT_INT, cbit_int); ++ i2c_wr8(sd, SYS_INT, sys_int); + +- v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); ++ v4l2_info(sd, "%s: SYS_INT =0x%02x\n", __func__, sys_int); + +- if (cbit_int & MASK_I_CBIT_FS) { ++ if (sys_int & MASK_I_DDC) { ++ bool tx_5v = tx_5v_power_present(sd); + +- v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", +- __func__); +- tc358743_s_ctrl_audio_sampling_rate(sd); ++ v4l2_info(sd, "%s: Tx 5V power present: %s\n", __func__, ++ tx_5v ? "yes" : "no"); + +- cbit_int &= ~MASK_I_CBIT_FS; +- if (handled) +- *handled = true; +- } ++ if (tx_5v) { ++ tc358743_enable_edid(sd); ++ } else { ++ tc358743_enable_interrupts(sd, false); ++ tc358743_disable_edid(sd); ++ memset(&state->timings, 0, sizeof(state->timings)); ++ tc358743_erase_bksv(sd); ++ tc358743_update_controls(sd); ++ } + +- if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { ++ sys_int &= ~MASK_I_DDC; ++ if (handled) *handled = true; ++ } + +- v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", +- __func__); +- tc358743_s_ctrl_audio_present(sd); ++ if (sys_int & MASK_I_DVI) { ++ v4l2_info(sd, "%s: HDMI->DVI change detected\n", __func__); + +- cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); +- if (handled) +- *handled = true; +- } ++ /* Reset the HDMI PHY to try to trigger proper lock on the ++ * incoming video format. Erase BKSV to prevent that old keys ++ * are used when a new source is connected. */ ++ if (no_sync(sd) || no_signal(sd)) { ++ tc358743_reset_phy(sd); ++ tc358743_erase_bksv(sd); ++ } + +- if (cbit_int) { +- v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", +- __func__, cbit_int); +- } +-} ++ sys_int &= ~MASK_I_DVI; ++ if (handled) *handled = true; ++ } + +-static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); +- u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; ++ if (sys_int & MASK_I_HDMI) { ++ v4l2_info(sd, "%s: DVI->HDMI change detected\n", __func__); + +- /* Bit 7 and bit 6 are set even when they are masked */ +- i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); ++ /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ ++ i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); + +- v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); ++ sys_int &= ~MASK_I_HDMI; ++ if (handled) *handled = true; ++ } + +- if (clk_int & (MASK_I_IN_DE_CHG)) { +- +- v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", +- __func__); +- +- /* If the source switch to a new resolution with the same pixel +- * frequency as the existing (e.g. 1080p25 -> 720p50), the +- * I_SYNC_CHG interrupt is not always triggered, while the +- * I_IN_DE_CHG interrupt seems to work fine. Format change +- * notifications are only sent when the signal is stable to +- * reduce the number of notifications. */ +- if (!no_signal(sd) && !no_sync(sd)) +- tc358743_format_change(sd); +- +- clk_int &= ~(MASK_I_IN_DE_CHG); +- if (handled) +- *handled = true; +- } +- +- if (clk_int) { +- v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", +- __func__, clk_int); +- } +-} +- +-static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- struct tc358743_state *state = to_state(sd); +- u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); +- u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; +- +- i2c_wr8(sd, SYS_INT, sys_int); +- +- v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); +- +- if (sys_int & MASK_I_DDC) { +- bool tx_5v = tx_5v_power_present(sd); +- +- v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", +- __func__, tx_5v ? "yes" : "no"); +- +- if (tx_5v) { +- tc358743_enable_edid(sd); +- } else { +- tc358743_enable_interrupts(sd, false); +- tc358743_disable_edid(sd); +- memset(&state->timings, 0, sizeof(state->timings)); +- tc358743_erase_bksv(sd); +- tc358743_update_controls(sd); +- } +- +- sys_int &= ~MASK_I_DDC; +- if (handled) +- *handled = true; +- } +- +- if (sys_int & MASK_I_DVI) { +- v4l2_dbg(1, debug, sd, "%s: HDMI->DVI change detected\n", +- __func__); +- +- /* Reset the HDMI PHY to try to trigger proper lock on the +- * incoming video format. Erase BKSV to prevent that old keys +- * are used when a new source is connected. */ +- if (no_sync(sd) || no_signal(sd)) { +- tc358743_reset_phy(sd); +- tc358743_erase_bksv(sd); +- } +- +- sys_int &= ~MASK_I_DVI; +- if (handled) +- *handled = true; +- } +- +- if (sys_int & MASK_I_HDMI) { +- v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", +- __func__); +- +- /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ +- i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); +- +- sys_int &= ~MASK_I_HDMI; +- if (handled) +- *handled = true; +- } +- +- if (sys_int) { +- v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", +- __func__, sys_int); +- } +-} +- +-/* --------------- CORE OPS --------------- */ +- +-static int tc358743_log_status(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_dv_timings timings; +- uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); +- uint16_t sysctl = i2c_rd16(sd, SYSCTL); +- u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); +- const int deep_color_mode[4] = { 8, 10, 12, 16 }; +- static const char * const input_color_space[] = { +- "RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)", +- "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", +- "NA(10)", "NA(11)", "NA(12)", "opYCC 601"}; +- +- v4l2_info(sd, "-----Chip status-----\n"); +- v4l2_info(sd, "Chip ID: 0x%02x\n", +- (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); +- v4l2_info(sd, "Chip revision: 0x%02x\n", +- i2c_rd16(sd, CHIPID) & MASK_REVID); +- v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", +- !!(sysctl & MASK_IRRST), +- !!(sysctl & MASK_CECRST), +- !!(sysctl & MASK_CTXRST), +- !!(sysctl & MASK_HDMIRST)); +- v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); +- v4l2_info(sd, "Cable detected (+5V power): %s\n", +- hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); +- v4l2_info(sd, "DDC lines enabled: %s\n", +- (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? +- "yes" : "no"); +- v4l2_info(sd, "Hotplug enabled: %s\n", +- (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? +- "yes" : "no"); +- v4l2_info(sd, "CEC enabled: %s\n", +- (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); +- v4l2_info(sd, "-----Signal status-----\n"); +- v4l2_info(sd, "TMDS signal detected: %s\n", +- hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); +- v4l2_info(sd, "Stable sync signal: %s\n", +- hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); +- v4l2_info(sd, "PHY PLL locked: %s\n", +- hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); +- v4l2_info(sd, "PHY DE detected: %s\n", +- hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); +- +- if (tc358743_get_detected_timings(sd, &timings)) { +- v4l2_info(sd, "No video detected\n"); +- } else { +- v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, +- true); +- } +- v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, +- true); +- +- v4l2_info(sd, "-----CSI-TX status-----\n"); +- v4l2_info(sd, "Lanes needed: %d\n", +- tc358743_num_csi_lanes_needed(sd)); +- v4l2_info(sd, "Lanes in use: %d\n", +- state->csi_lanes_in_use); +- v4l2_info(sd, "Waiting for particular sync signal: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? +- "yes" : "no"); +- v4l2_info(sd, "Transmit mode: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? +- "yes" : "no"); +- v4l2_info(sd, "Receive mode: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? +- "yes" : "no"); +- v4l2_info(sd, "Stopped: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? +- "yes" : "no"); +- v4l2_info(sd, "Color space: %s\n", +- state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? +- "YCbCr 422 16-bit" : +- state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? +- "RGB 888 24-bit" : "Unsupported"); +- +- v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); +- v4l2_info(sd, "HDCP encrypted content: %s\n", +- hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); +- v4l2_info(sd, "Input color space: %s %s range\n", +- input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], +- (vi_status3 & MASK_LIMITED) ? "limited" : "full"); +- if (!is_hdmi(sd)) +- return 0; +- v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : +- "off"); +- v4l2_info(sd, "Deep color mode: %d-bits per channel\n", +- deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & +- MASK_S_DEEPCOLOR) >> 2]); +- print_avi_infoframe(sd); +- +- return 0; ++ if (sys_int) { ++ v4l2_err(sd, "%s: Unhandled SYS_INT interrupts:0x%02x\n", __func__, ++ sys_int); ++ } + } + + #ifdef CONFIG_VIDEO_ADV_DEBUG +-static void tc358743_print_register_map(struct v4l2_subdev *sd) +-{ +- v4l2_info(sd, "0x0000-0x00FF: Global Control Register\n"); +- v4l2_info(sd, "0x0100-0x01FF: CSI2-TX PHY Register\n"); +- v4l2_info(sd, "0x0200-0x03FF: CSI2-TX PPI Register\n"); +- v4l2_info(sd, "0x0400-0x05FF: Reserved\n"); +- v4l2_info(sd, "0x0600-0x06FF: CEC Register\n"); +- v4l2_info(sd, "0x0700-0x84FF: Reserved\n"); +- v4l2_info(sd, "0x8500-0x85FF: HDMIRX System Control Register\n"); +- v4l2_info(sd, "0x8600-0x86FF: HDMIRX Audio Control Register\n"); +- v4l2_info(sd, "0x8700-0x87FF: HDMIRX InfoFrame packet data Register\n"); +- v4l2_info(sd, "0x8800-0x88FF: HDMIRX HDCP Port Register\n"); +- v4l2_info(sd, "0x8900-0x89FF: HDMIRX Video Output Port & 3D Register\n"); +- v4l2_info(sd, "0x8A00-0x8BFF: Reserved\n"); +- v4l2_info(sd, "0x8C00-0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); +- v4l2_info(sd, "0x9000-0x90FF: HDMIRX GBD Extraction Control\n"); +- v4l2_info(sd, "0x9100-0x92FF: HDMIRX GBD RAM read\n"); +- v4l2_info(sd, "0x9300- : Reserved\n"); +-} +- +-static int tc358743_get_reg_size(u16 address) +-{ +- /* REF_01 p. 66-72 */ +- if (address <= 0x00ff) +- return 2; +- else if ((address >= 0x0100) && (address <= 0x06FF)) +- return 4; +- else if ((address >= 0x0700) && (address <= 0x84ff)) +- return 2; +- else +- return 1; ++static void tc358743_print_register_map(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); ++ v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); ++ v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); ++ v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); ++ v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); ++ v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); ++ v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); ++ v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); ++ v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); ++ v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); ++ v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); ++ v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); ++ v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); ++ v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); ++ v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); ++ v4l2_info(sd, "0x9300- : Reserved\n"); ++} ++ ++static int tc358743_get_reg_size(u16 address) { ++ /* REF_01 p. 66-72 */ ++ if (address <= 0x00ff) ++ return 2; ++ else if ((address >= 0x0100) && (address <= 0x06FF)) ++ return 4; ++ else if ((address >= 0x0700) && (address <= 0x84ff)) ++ return 2; ++ else ++ return 1; + } + + static int tc358743_g_register(struct v4l2_subdev *sd, +- struct v4l2_dbg_register *reg) +-{ +- if (reg->reg > 0xffff) { +- tc358743_print_register_map(sd); +- return -EINVAL; +- } ++ struct v4l2_dbg_register *reg) { ++ if (reg->reg > 0xffff) { ++ tc358743_print_register_map(sd); ++ return -EINVAL; ++ } + +- reg->size = tc358743_get_reg_size(reg->reg); ++ reg->size = tc358743_get_reg_size(reg->reg); + +- reg->val = i2c_rdreg(sd, reg->reg, reg->size); ++ i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + +- return 0; ++ return 0; + } + + static int tc358743_s_register(struct v4l2_subdev *sd, +- const struct v4l2_dbg_register *reg) +-{ +- if (reg->reg > 0xffff) { +- tc358743_print_register_map(sd); +- return -EINVAL; +- } +- +- /* It should not be possible for the user to enable HDCP with a simple +- * v4l2-dbg command. +- * +- * DO NOT REMOVE THIS unless all other issues with HDCP have been +- * resolved. +- */ +- if (reg->reg == HDCP_MODE || +- reg->reg == HDCP_REG1 || +- reg->reg == HDCP_REG2 || +- reg->reg == HDCP_REG3 || +- reg->reg == BCAPS) +- return 0; +- +- i2c_wrreg(sd, (u16)reg->reg, reg->val, +- tc358743_get_reg_size(reg->reg)); +- +- return 0; ++ const struct v4l2_dbg_register *reg) { ++ if (reg->reg > 0xffff) { ++ tc358743_print_register_map(sd); ++ return -EINVAL; ++ } ++ ++ /* It should not be possible for the user to enable HDCP with a simple ++ * v4l2-dbg command. ++ * ++ * DO NOT REMOVE THIS unless all other issues with HDCP have been ++ * resolved. ++ */ ++ if (reg->reg == HDCP_MODE || reg->reg == HDCP_REG1 || reg->reg == HDCP_REG2 || ++ reg->reg == HDCP_REG3 || reg->reg == BCAPS) ++ return 0; ++ ++ i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, tc358743_get_reg_size(reg->reg)); ++ ++ return 0; + } + #endif + +-static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +-{ +- u16 intstatus = i2c_rd16(sd, INTSTATUS); +- +- v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); +- +- if (intstatus & MASK_HDMI_INT) { +- u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); +- u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); +- +- if (hdmi_int0 & MASK_I_MISC) +- tc358743_hdmi_misc_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_CBIT) +- tc358743_hdmi_cbit_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_CLK) +- tc358743_hdmi_clk_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_SYS) +- tc358743_hdmi_sys_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_AUD) +- tc358743_hdmi_audio_int_handler(sd, handled); +- +- i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); +- intstatus &= ~MASK_HDMI_INT; +- } +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +- if (intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)) { +- tc358743_cec_handler(sd, intstatus, handled); +- i2c_wr16(sd, INTSTATUS, +- intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); +- intstatus &= ~(MASK_CEC_RINT | MASK_CEC_TINT); +- } +-#endif ++static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { ++ u16 intstatus = i2c_rd16(sd, INTSTATUS); + +- if (intstatus & MASK_CSI_INT) { +- u32 csi_int = i2c_rd32(sd, CSI_INT); ++ if (intstatus & MASK_HDMI_INT) { ++ u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); ++ u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); + +- if (csi_int & MASK_INTER) +- tc358743_csi_err_int_handler(sd, handled); ++ if (hdmi_int0 & MASK_I_MISC) tc358743_hdmi_misc_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_CBIT) tc358743_hdmi_cbit_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_CLK) tc358743_hdmi_clk_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_SYS) tc358743_hdmi_sys_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_AUD) tc358743_hdmi_audio_int_handler(sd, handled); + +- i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); +- } ++ i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); ++ intstatus &= ~MASK_HDMI_INT; ++ } + +- intstatus = i2c_rd16(sd, INTSTATUS); +- if (intstatus) { +- v4l2_dbg(1, debug, sd, +- "%s: Unhandled IntStatus interrupts: 0x%02x\n", +- __func__, intstatus); +- } ++ if (intstatus & MASK_CSI_INT) { ++ u32 csi_int = i2c_rd32(sd, CSI_INT); + +- return 0; +-} ++ if (csi_int & MASK_INTER) tc358743_csi_err_int_handler(sd, handled); + +-static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) +-{ +- struct tc358743_state *state = dev_id; +- bool handled = false; ++ i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); ++ intstatus &= ~MASK_CSI_INT; ++ } + +- tc358743_isr(&state->sd, 0, &handled); ++ intstatus = i2c_rd16(sd, INTSTATUS); ++ if (intstatus) { ++ v4l2_info(sd, "%s: Unhandled IntStatus interrupts:0x%02x\n", __func__, ++ intstatus); ++ } + +- return handled ? IRQ_HANDLED : IRQ_NONE; ++ return 0; + } + +-static void tc358743_irq_poll_timer(struct timer_list *t) +-{ +- struct tc358743_state *state = from_timer(state, t, timer); +- unsigned int msecs; ++static void tc358743_process_isr(struct work_struct *work) { ++ struct tc358743_state *state = ++ container_of(work, struct tc358743_state, process_isr); ++ struct v4l2_subdev *sd = &state->sd; ++ bool handled; ++ ++ v4l2_dbg(2, debug, sd, "%s:\n", __func__); + +- schedule_work(&state->work_i2c_poll); +- /* +- * If CEC is present, then we need to poll more frequently, +- * otherwise we will miss CEC messages. +- */ +- msecs = state->cec_adap ? POLL_INTERVAL_CEC_MS : POLL_INTERVAL_MS; +- mod_timer(&state->timer, jiffies + msecs_to_jiffies(msecs)); ++ mutex_lock(&state->isr_lock); ++ tc358743_isr(sd, 0, &handled); ++ mutex_unlock(&state->isr_lock); + } + +-static void tc358743_work_i2c_poll(struct work_struct *work) +-{ +- struct tc358743_state *state = container_of(work, +- struct tc358743_state, work_i2c_poll); +- bool handled; ++static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) { ++ struct tc358743_state *state = dev_id; + +- tc358743_isr(&state->sd, 0, &handled); ++ queue_work(state->work_queues, &state->process_isr); ++ ++ return IRQ_HANDLED; + } + + static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, +- struct v4l2_event_subscription *sub) +-{ +- switch (sub->type) { +- case V4L2_EVENT_SOURCE_CHANGE: +- return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); +- case V4L2_EVENT_CTRL: +- return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); +- default: +- return -EINVAL; +- } ++ struct v4l2_event_subscription *sub) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); ++ case V4L2_EVENT_CTRL: ++ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); ++ default: ++ return -EINVAL; ++ } + } + + /* --------------- VIDEO OPS --------------- */ + +-static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) +-{ +- *status = 0; +- *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; +- *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; +- +- v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); +- +- return 0; +-} +- + static int tc358743_s_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings *timings) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "1111 %s\n", __func__); ++ if (!timings) return -EINVAL; + +- if (!timings) +- return -EINVAL; ++ if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { ++ v4l2_info(sd, "%s: no change\n", __func__); ++ return 0; ++ } + +- if (debug) +- v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", +- timings, false); ++ if (!v4l2_valid_dv_timings(timings, &tc358743_timings_cap, NULL, NULL)) { ++ v4l2_err(sd, "%s: timings out of range\n", __func__); ++ return -ERANGE; ++ } + +- if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { +- v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); +- return 0; +- } ++ state->timings = *timings; + +- if (!v4l2_valid_dv_timings(timings, +- &tc358743_timings_cap, NULL, NULL)) { +- v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); +- return -ERANGE; +- } ++ enable_stream(sd, false); ++ tc358743_set_pll(sd); ++ tc358743_set_csi(sd); ++ v4l2_info(sd, "2222 %s\n", __func__); ++ return 0; ++} + +- state->timings = *timings; ++static int tc358743_g_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (state) { ++ *timings = state->timings; ++ return 0; ++ } ++ return -EINVAL; ++} + +- enable_stream(sd, false); +- tc358743_set_pll(sd); +- tc358743_set_csi(sd); ++static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_enum_dv_timings *timings) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (timings && timings->pad != 0) { ++ v4l2_err(sd, "%s: failed %d\n", __func__, EINVAL); ++ return -EINVAL; ++ } + +- return 0; ++ return v4l2_enum_dv_timings_cap(timings, &tc358743_timings_cap, NULL, NULL); + } + +-static int tc358743_g_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct tc358743_state *state = to_state(sd); ++static int tc358743_query_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) { ++ int ret; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); + +- *timings = state->timings; ++ ret = tc358743_get_detected_timings(sd, timings); ++ if (ret) { ++ v4l2_err(sd, "%s: @@@@@ timings detected error\n", __func__); ++ return ret; ++ } + +- return 0; +-} ++ if (debug) ++ v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", timings, ++ false); + +-static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_enum_dv_timings *timings) +-{ +- if (timings->pad != 0) +- return -EINVAL; ++ if (!v4l2_valid_dv_timings(timings, &tc358743_timings_cap, NULL, NULL)) { ++ v4l2_err(sd, "%s: @@@@@ timings out of range\n", __func__); ++ return -ERANGE; ++ } + +- return v4l2_enum_dv_timings_cap(timings, +- &tc358743_timings_cap, NULL, NULL); ++ return 0; + } + +-static int tc358743_query_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- int ret; ++static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings *timings = &(state->timings); + +- ret = tc358743_get_detected_timings(sd, timings); +- if (ret) +- return ret; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ *status = 0; ++ *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; ++ *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; + +- if (debug) +- v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", +- timings, false); ++ v4l2_info(sd, "%s: status =0x%x\n", __func__, *status); + +- if (!v4l2_valid_dv_timings(timings, +- &tc358743_timings_cap, NULL, NULL)) { +- v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); +- return -ERANGE; +- } ++ v4l2_info(sd, "Now getting and setting dv timings"); ++ tc358743_query_dv_timings(sd, timings); ++ tc358743_s_dv_timings(sd, timings); + +- return 0; ++ return 0; + } + + static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, +- struct v4l2_dv_timings_cap *cap) +-{ +- if (cap->pad != 0) +- return -EINVAL; ++ struct v4l2_dv_timings_cap *cap) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (cap && cap->pad != 0) return -EINVAL; + +- *cap = tc358743_timings_cap; ++ *cap = tc358743_timings_cap; + +- return 0; ++ return 0; + } + +-static int tc358743_get_mbus_config(struct v4l2_subdev *sd, +- unsigned int pad, +- struct v4l2_mbus_config *cfg) +-{ +- struct tc358743_state *state = to_state(sd); ++static int tc358743_g_mbus_config(struct v4l2_subdev *sd, ++ unsigned int pad, ++ struct v4l2_mbus_config *cfg) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++// cfg->type = V4L2_MBUS_CSI2; ++ cfg->type = V4L2_MBUS_CSI2_DPHY; + +- cfg->type = V4L2_MBUS_CSI2_DPHY; ++ /* Support for non-continuous CSI-2 clock is missing in the driver */ ++ cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + +- /* Support for non-continuous CSI-2 clock is missing in the driver */ +- cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ switch (tc358743_num_csi_lanes_in_use(sd)) { ++ case 1: ++ cfg->flags |= V4L2_MBUS_CSI2_1_LANE; ++ break; ++ case 2: ++ cfg->flags |= V4L2_MBUS_CSI2_2_LANE; ++ break; ++ case 3: ++ cfg->flags |= V4L2_MBUS_CSI2_3_LANE; ++ break; ++ case 4: ++ cfg->flags |= V4L2_MBUS_CSI2_4_LANE; ++ break; ++ default: ++ return -EINVAL; ++ } + +- switch (state->csi_lanes_in_use) { +- case 1: +- cfg->flags |= V4L2_MBUS_CSI2_1_LANE; +- break; +- case 2: +- cfg->flags |= V4L2_MBUS_CSI2_2_LANE; +- break; +- case 3: +- cfg->flags |= V4L2_MBUS_CSI2_3_LANE; +- break; +- case 4: +- cfg->flags |= V4L2_MBUS_CSI2_4_LANE; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; ++ return 0; + } + +-static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +-{ +- enable_stream(sd, enable); +- if (!enable) { +- /* Put all lanes in LP-11 state (STOPSTATE) */ +- tc358743_set_csi(sd); +- } +- +- return 0; ++static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ enable_stream(sd, true); ++ return 0; + } + + /* --------------- PAD OPS --------------- */ + +-static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_mbus_code_enum *code) +-{ +- switch (code->index) { +- case 0: +- code->code = MEDIA_BUS_FMT_RGB888_1X24; +- break; +- case 1: +- code->code = MEDIA_BUS_FMT_UYVY8_1X16; +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- + static int tc358743_get_fmt(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_format *format) +-{ +- struct tc358743_state *state = to_state(sd); +- u8 vi_rep = i2c_rd8(sd, VI_REP); +- +- if (format->pad != 0) +- return -EINVAL; +- +- format->format.code = state->mbus_fmt_code; +- format->format.width = state->timings.bt.width; +- format->format.height = state->timings.bt.height; +- format->format.field = V4L2_FIELD_NONE; +- +- switch (vi_rep & MASK_VOUT_COLOR_SEL) { +- case MASK_VOUT_COLOR_RGB_FULL: +- case MASK_VOUT_COLOR_RGB_LIMITED: +- format->format.colorspace = V4L2_COLORSPACE_SRGB; +- break; +- case MASK_VOUT_COLOR_601_YCBCR_LIMITED: +- case MASK_VOUT_COLOR_601_YCBCR_FULL: +- format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; +- break; +- case MASK_VOUT_COLOR_709_YCBCR_FULL: +- case MASK_VOUT_COLOR_709_YCBCR_LIMITED: +- format->format.colorspace = V4L2_COLORSPACE_REC709; +- break; +- default: +- format->format.colorspace = 0; +- break; +- } +- +- return 0; ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) { ++ struct tc358743_state *state = to_state(sd); ++ u8 vi_rep = i2c_rd8(sd, VI_REP); ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ ++ if (format->pad != 0) { ++ v4l2_err(sd, "%s Error\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ format->format.code = state->mbus_fmt_code; ++ format->format.width = state->timings.bt.width; ++ format->format.height = state->timings.bt.height; ++ format->format.field = V4L2_FIELD_NONE; ++ ++ switch (vi_rep & MASK_VOUT_COLOR_SEL) { ++ case MASK_VOUT_COLOR_RGB_FULL: ++ case MASK_VOUT_COLOR_RGB_LIMITED: ++ format->format.colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ case MASK_VOUT_COLOR_601_YCBCR_LIMITED: ++ case MASK_VOUT_COLOR_601_YCBCR_FULL: ++ v4l2_info(sd, "Here 6b, colorspace: %d\n", V4L2_COLORSPACE_SMPTE170M); ++ format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ break; ++ case MASK_VOUT_COLOR_709_YCBCR_FULL: ++ case MASK_VOUT_COLOR_709_YCBCR_LIMITED: ++ format->format.colorspace = V4L2_COLORSPACE_REC709; ++ break; ++ default: ++ format->format.colorspace = 0; ++ v4l2_info(sd, "%d:%s colorspace = 0\n", __LINE__, __FUNCTION__); ++ break; ++ } ++ ++ v4l2_info(sd, "get fmt complete\n"); ++ v4l2_info(sd, "format width %d\n", format->format.width); ++ v4l2_info(sd, "format height %d\n", format->format.height); ++ ++ v4l2_info(sd, "fmt_code: %d\n", format->format.code); ++ v4l2_info(sd, "RGB888 code: %d\n", MEDIA_BUS_FMT_RGB888_1X24); ++ v4l2_info(sd, "UYVY8 code: %d\n", MEDIA_BUS_FMT_UYVY8_1X16); ++ return 0; + } + + static int tc358743_set_fmt(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_format *format) +-{ +- struct tc358743_state *state = to_state(sd); ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) { ++ struct tc358743_state *state = to_state(sd); ++ u32 code = format->format.code; ++ int ret = tc358743_get_fmt(sd, cfg, format); + +- u32 code = format->format.code; /* is overwritten by get_fmt */ +- int ret = tc358743_get_fmt(sd, cfg, format); ++ v4l2_dbg(3, debug, sd, "%s(), ret: %d\n", __func__, ret); ++ v4l2_dbg(3, debug, sd, "Set format code: %d\n", code); + +- format->format.code = code; ++ format->format.code = code; + +- if (ret) +- return ret; ++ if (ret) return ret; + +- switch (code) { +- case MEDIA_BUS_FMT_RGB888_1X24: +- case MEDIA_BUS_FMT_UYVY8_1X16: +- break; +- default: +- return -EINVAL; +- } ++ switch (code) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ v4l2_dbg(3, debug, sd, "Good code %d\n", code); ++ break; ++ default: ++ v4l2_err(sd, "Bad code %d\n", code); ++ return -EINVAL; ++ } + +- if (format->which == V4L2_SUBDEV_FORMAT_TRY) +- return 0; ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; + +- state->mbus_fmt_code = format->format.code; ++ state->mbus_fmt_code = format->format.code; + +- enable_stream(sd, false); +- tc358743_set_pll(sd); +- tc358743_set_csi(sd); +- tc358743_set_csi_color_space(sd); +- +- return 0; ++ enable_stream(sd, false); ++ tc358743_set_pll(sd); ++ tc358743_set_csi(sd); ++ tc358743_set_csi_color_space(sd); ++ v4l2_info(sd, "Called %s, completed successfully\n", __FUNCTION__); ++ return 0; + } + + static int tc358743_g_edid(struct v4l2_subdev *sd, +- struct v4l2_subdev_edid *edid) +-{ +- struct tc358743_state *state = to_state(sd); +- +- memset(edid->reserved, 0, sizeof(edid->reserved)); ++ struct v4l2_subdev_edid *edid) { ++ struct tc358743_state *state = to_state(sd); ++ // int i=0; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); + +- if (edid->pad != 0) +- return -EINVAL; ++ if (edid->pad != 0) return -EINVAL; + +- if (edid->start_block == 0 && edid->blocks == 0) { +- edid->blocks = state->edid_blocks_written; +- return 0; +- } ++ if (edid->start_block == 0 && edid->blocks == 0) { ++ edid->blocks = state->edid_blocks_written; ++ return 0; ++ } + +- if (state->edid_blocks_written == 0) +- return -ENODATA; ++ if (state->edid_blocks_written == 0) return -ENODATA; + +- if (edid->start_block >= state->edid_blocks_written || +- edid->blocks == 0) +- return -EINVAL; ++ if (edid->start_block >= state->edid_blocks_written || edid->blocks == 0) ++ return -EINVAL; + +- if (edid->start_block + edid->blocks > state->edid_blocks_written) +- edid->blocks = state->edid_blocks_written - edid->start_block; ++ if (edid->start_block + edid->blocks > state->edid_blocks_written) ++ edid->blocks = state->edid_blocks_written - edid->start_block; + +- i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, +- edid->blocks * EDID_BLOCK_SIZE); +- +- return 0; ++ i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, ++ edid->blocks * EDID_BLOCK_SIZE); ++ v4l2_info(sd, "EDID_RAM has %d byte from: 0x%04x to 0x%04x \r\n", ++ edid->blocks * EDID_BLOCK_SIZE, ++ EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), ++ EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE) + ++ edid->blocks * EDID_BLOCK_SIZE); ++ // for(i=0;iblocks * EDID_BLOCK_SIZE;i++){ ++ // printk("%02x ",edid->edid[i]); ++ // } ++ // v4l2_info(sd,"\r\n"); ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++ return 0; + } + + static int tc358743_s_edid(struct v4l2_subdev *sd, +- struct v4l2_subdev_edid *edid) +-{ +- struct tc358743_state *state = to_state(sd); +- u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; +- u16 pa; +- int err; +- int i; +- +- v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", +- __func__, edid->pad, edid->start_block, edid->blocks); +- +- memset(edid->reserved, 0, sizeof(edid->reserved)); +- +- if (edid->pad != 0) +- return -EINVAL; +- +- if (edid->start_block != 0) +- return -EINVAL; +- +- if (edid->blocks > EDID_NUM_BLOCKS_MAX) { +- edid->blocks = EDID_NUM_BLOCKS_MAX; +- return -E2BIG; +- } +- pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); +- err = v4l2_phys_addr_validate(pa, &pa, NULL); +- if (err) +- return err; ++ struct v4l2_subdev_edid *edid) { ++ struct tc358743_state *state = to_state(sd); ++ u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; ++ ++ v4l2_info(sd, "%s, pad %d, start block %d, blocks %d\n", __func__, edid->pad, ++ edid->start_block, edid->blocks); ++ ++ if (edid->pad != 0) return -EINVAL; ++ ++ if (edid->start_block != 0) return -EINVAL; ++ ++ if (edid->blocks > EDID_NUM_BLOCKS_MAX) { ++ edid->blocks = EDID_NUM_BLOCKS_MAX; ++ return -E2BIG; ++ } ++ ++ tc358743_disable_edid(sd); ++ ++ i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); ++ i2c_wr8(sd, EDID_LEN2, edid_len >> 8); ++ ++ if (edid->blocks == 0) { ++ state->edid_blocks_written = 0; ++ return 0; ++ } ++ i2c_wr(sd, EDID_RAM, edid->edid, edid_len); ++ /* richardyou ++ for (i=0; iedid[i]); ++ } ++ */ ++ state->edid_blocks_written = edid->blocks; ++ ++ // if (tx_5v_power_present(sd)) ++ tc358743_enable_edid(sd); ++ ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++ return 0; ++} ++ ++// static int tc358743_mbus_fmt(struct v4l2_subdev *sd, ++// struct v4l2_mbus_framefmt *mf) { ++// struct tc358743_state *state = to_state(sd); ++// u8 vi_rep = i2c_rd8(sd, VI_REP); ++ ++// mf->code = state->mbus_fmt_code; ++// mf->width = state->timings.bt.width; ++// mf->height = state->timings.bt.height; ++// mf->field = V4L2_FIELD_NONE; ++// switch (vi_rep & MASK_VOUT_COLOR_SEL) { ++// case MASK_VOUT_COLOR_RGB_FULL: ++// case MASK_VOUT_COLOR_RGB_LIMITED: ++// mf->colorspace = V4L2_COLORSPACE_SRGB; ++// break; ++// case MASK_VOUT_COLOR_601_YCBCR_LIMITED: ++// case MASK_VOUT_COLOR_601_YCBCR_FULL: ++// mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++// break; ++// case MASK_VOUT_COLOR_709_YCBCR_FULL: ++// case MASK_VOUT_COLOR_709_YCBCR_LIMITED: ++// mf->colorspace = V4L2_COLORSPACE_REC709; ++// break; ++// default: ++// mf->colorspace = 0; ++// break; ++// } ++// return 0; ++// } + +- cec_phys_addr_invalidate(state->cec_adap); +- +- tc358743_disable_edid(sd); +- +- i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); +- i2c_wr8(sd, EDID_LEN2, edid_len >> 8); ++static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) { ++ v4l2_info(sd, "%s()\n", __func__); ++ ++ // if (code->index >= 2) { ++ // v4l2_err(sd, "Error in %s\n", __FUNCTION__); ++ // return -EINVAL; ++ // } ++ ++ switch (code->index) { ++ case 0: ++ code->code = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ case 1: ++ code->code = MEDIA_BUS_FMT_UYVY8_1X16; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Mbus code found succsefully (%d: %d)", code->index, ++ code->code); ++ ++ return 0; ++} ++ ++static int tc358743_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) { ++ const struct camera_common_frmfmt *frmfmt = tc358743_frmfmt; ++ int num_frmfmt = ARRAY_SIZE(tc358743_frmfmt); ++ ++ v4l2_info(sd, "%s()\n", __func__); ++ v4l2_info(sd, "fse->code %d, index %d\n", fse->code, fse->index); ++ v4l2_info(sd, "----------------------------------------\n"); ++ ++ // fse->min_width = fse->max_width = 1280; ++ // fse->min_height = fse->max_height = 720; ++ ++ v4l2_info(sd, ++ "Trying to find frmfmt that matches fse->code, code: %d (UYVY: %d, " ++ "ARGB32: %d, MEDIA_BUS_FMT_UYVY8_1X16: %d, " ++ "MEDIA_BUS_FMT_RGB888_1X24: %d)\n", ++ fse->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32, ++ MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_RGB888_1X24); ++ ++ if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16 && ++ fse->code != V4L2_PIX_FMT_ABGR32 && ++ fse->code != MEDIA_BUS_FMT_UYVY8_1X16) { ++ v4l2_err(sd, "Error in %s fse->code, code: %d, UYVY: %d, ARGB32: %d\n", ++ __FUNCTION__, fse->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Code ok"); ++ ++ if (fse->index >= num_frmfmt) { ++ v4l2_err(sd, "Error in %s, %d outside of num_frmfmt (%d)", __FUNCTION__, ++ fse->index, num_frmfmt); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Index ok"); ++ ++ fse->min_width = fse->max_width = frmfmt[fse->index].size.width; ++ fse->min_height = fse->max_height = frmfmt[fse->index].size.height; ++ v4l2_info(sd, "!!!!!!!!! %s() complete successfully, width: %d, height: %d\n", ++ __func__, fse->min_width, fse->min_height); ++ return 0; ++} ++ ++static int tc358743_enum_frame_interval( ++ struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_interval_enum *fie) { ++ const struct camera_common_frmfmt *frmfmt = tc358743_frmfmt; ++ int num_frmfmt = ARRAY_SIZE(tc358743_frmfmt); ++ int i; ++ ++ v4l2_info(sd, "%s()\n", __func__); ++ v4l2_info(sd, "----------------------------------------\n"); ++ ++ v4l2_info(sd, ++ "Trying to find frame interfval that matches fie->code, code: %d " ++ "(UYVY: %d, ARGB32: %d, MEDIA_BUS_FMT_UYVY8_1X16: %d)\n", ++ fie->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32, ++ MEDIA_BUS_FMT_UYVY8_1X16); ++ ++ if (fie->code != V4L2_PIX_FMT_UYVY && fie->code != V4L2_PIX_FMT_ABGR32 && ++ fie->code != MEDIA_BUS_FMT_UYVY8_1X16) { ++ v4l2_err(sd, "Unexpected code (%d), UYUV: %d, ABGR32: %d\n", fie->code, ++ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Code ok"); ++ ++ for (i = 0; i < num_frmfmt; i++) { ++ if (frmfmt[i].size.width == fie->width && ++ frmfmt[i].size.height == fie->height) { ++ v4l2_info(sd, "Matched width %d and %d, height %d and %d", ++ frmfmt[i].size.width, fie->width, frmfmt[i].size.height, ++ fie->height); ++ break; ++ } ++ } ++ ++ v4l2_info(sd, "w/h ok or end (i=%d, num=%d)", i, num_frmfmt); + +- if (edid->blocks == 0) { +- state->edid_blocks_written = 0; +- return 0; +- } ++ if (i >= num_frmfmt) { ++ v4l2_err(sd, "Error in %s, num frmfmt\n", __FUNCTION__); ++ return -EINVAL; ++ } + +- for (i = 0; i < edid_len; i += EDID_BLOCK_SIZE) +- i2c_wr(sd, EDID_RAM + i, edid->edid + i, EDID_BLOCK_SIZE); ++ v4l2_info(sd, "i ok"); + +- state->edid_blocks_written = edid->blocks; ++ if (fie->index >= frmfmt[i].num_framerates) { ++ v4l2_err(sd, "Error in %s num framerates (%d outside %d)\n", __FUNCTION__, ++ fie->index, frmfmt[i].num_framerates); ++ return -EINVAL; ++ } + +- cec_s_phys_addr(state->cec_adap, pa, false); ++ v4l2_info(sd, "index ok"); + +- if (tx_5v_power_present(sd)) +- tc358743_enable_edid(sd); ++ fie->interval.numerator = 1; ++ fie->interval.denominator = frmfmt[i].framerates[fie->index]; ++ fie->type = V4L2_SUBDEV_FRMIVAL_TYPE_DISCRETE; ++ v4l2_info(sd, "!!!!!!!!!! %s() completed successfully, interval: 1/%d\n", ++ __func__, fie->interval.denominator); ++ return 0; ++} + +- return 0; ++static int tc358743_s_power(struct v4l2_subdev *sd, int on) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ return 0; + } + +-/* -------------------------------------------------------------------------- */ +- + static const struct v4l2_subdev_core_ops tc358743_core_ops = { +- .log_status = tc358743_log_status, ++ .s_power = tc358743_s_power, ++ .log_status = tc358743_log_status, + #ifdef CONFIG_VIDEO_ADV_DEBUG +- .g_register = tc358743_g_register, +- .s_register = tc358743_s_register, ++ .g_register = tc358743_g_register, ++ .s_register = tc358743_s_register, + #endif +- .interrupt_service_routine = tc358743_isr, +- .subscribe_event = tc358743_subscribe_event, +- .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++ .interrupt_service_routine = tc358743_isr, ++ .subscribe_event = tc358743_subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, + }; + + static const struct v4l2_subdev_video_ops tc358743_video_ops = { +- .g_input_status = tc358743_g_input_status, +- .s_dv_timings = tc358743_s_dv_timings, +- .g_dv_timings = tc358743_g_dv_timings, +- .query_dv_timings = tc358743_query_dv_timings, +- .s_stream = tc358743_s_stream, ++ .g_input_status = tc358743_g_input_status, ++ .s_dv_timings = tc358743_s_dv_timings, ++ .g_dv_timings = tc358743_g_dv_timings, ++ .s_stream = tc358743_s_stream, ++ // .mbus_fmt = tc358743_mbus_fmt, ++ //.g_mbus_config = tc358743_g_mbus_config, ++ .query_dv_timings = tc358743_query_dv_timings, + }; + + static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { +- .enum_mbus_code = tc358743_enum_mbus_code, +- .set_fmt = tc358743_set_fmt, +- .get_fmt = tc358743_get_fmt, +- .get_edid = tc358743_g_edid, +- .set_edid = tc358743_s_edid, +- .enum_dv_timings = tc358743_enum_dv_timings, +- .dv_timings_cap = tc358743_dv_timings_cap, +- .get_mbus_config = tc358743_get_mbus_config, ++ .set_fmt = tc358743_set_fmt, ++ .get_fmt = tc358743_get_fmt, ++ .get_edid = tc358743_g_edid, ++ .set_edid = tc358743_s_edid, ++ .dv_timings_cap = tc358743_dv_timings_cap, ++ .get_mbus_config = tc358743_g_mbus_config, ++ ++ .enum_dv_timings = tc358743_enum_dv_timings, ++ .enum_mbus_code = tc358743_enum_mbus_code, ++ .enum_frame_size = tc358743_enum_frame_size, ++ .enum_frame_interval = tc358743_enum_frame_interval, + }; + + static const struct v4l2_subdev_ops tc358743_ops = { +- .core = &tc358743_core_ops, +- .video = &tc358743_video_ops, +- .pad = &tc358743_pad_ops, ++ .core = &tc358743_core_ops, ++ .video = &tc358743_video_ops, ++ .pad = &tc358743_pad_ops, + }; +- + /* --------------- CUSTOM CTRLS --------------- */ + + static const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { +- .id = TC358743_CID_AUDIO_SAMPLING_RATE, +- .name = "Audio sampling rate", +- .type = V4L2_CTRL_TYPE_INTEGER, +- .min = 0, +- .max = 768000, +- .step = 1, +- .def = 0, +- .flags = V4L2_CTRL_FLAG_READ_ONLY, ++ .id = TC358743_CID_AUDIO_SAMPLING_RATE, ++ .name = "Audio sampling rate", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0, ++ .max = 768000, ++ .step = 1, ++ .def = 0, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY, + }; + + static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { +- .id = TC358743_CID_AUDIO_PRESENT, +- .name = "Audio present", +- .type = V4L2_CTRL_TYPE_BOOLEAN, +- .min = 0, +- .max = 1, +- .step = 1, +- .def = 0, +- .flags = V4L2_CTRL_FLAG_READ_ONLY, ++ .id = TC358743_CID_AUDIO_PRESENT, ++ .name = "Audio present", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ .def = 0, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY, + }; + +-/* --------------- PROBE / REMOVE --------------- */ ++static bool tc358743_parse_dt(struct tc358743_platform_data *pdata, ++ struct i2c_client *client) { ++ struct device_node *node = client->dev.of_node; ++ const u32 *property; ++ pr_info("%s\n", __FUNCTION__); ++ pr_info("Device Tree Parameters:\n"); ++ ++ pdata->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0); ++ if (pdata->reset_gpio == 0) return false; ++ pr_info("reset_gpio = %d\n", pdata->reset_gpio); ++ ++ property = of_get_property(node, "refclk_hz", NULL); ++ if (property == NULL) return false; ++ pdata->refclk_hz = be32_to_cpup(property); ++ pr_info("refclk_hz = %d\n", be32_to_cpup(property)); ++ ++ return true; ++} + + #ifdef CONFIG_OF +-static void tc358743_gpio_reset(struct tc358743_state *state) +-{ +- usleep_range(5000, 10000); +- gpiod_set_value(state->reset_gpio, 1); +- usleep_range(1000, 2000); +- gpiod_set_value(state->reset_gpio, 0); +- msleep(20); +-} +- +-static int tc358743_probe_of(struct tc358743_state *state) +-{ +- struct device *dev = &state->i2c_client->dev; +- struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; +- struct device_node *ep; +- struct clk *refclk; +- u32 bps_pr_lane; +- int ret; +- +- refclk = devm_clk_get(dev, "refclk"); +- if (IS_ERR(refclk)) { +- if (PTR_ERR(refclk) != -EPROBE_DEFER) +- dev_err(dev, "failed to get refclk: %ld\n", +- PTR_ERR(refclk)); +- return PTR_ERR(refclk); +- } +- +- ep = of_graph_get_next_endpoint(dev->of_node, NULL); +- if (!ep) { +- dev_err(dev, "missing endpoint node\n"); +- return -EINVAL; +- } +- +- ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); +- if (ret) { +- dev_err(dev, "failed to parse endpoint\n"); +- goto put_node; +- } +- +- if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || +- endpoint.bus.mipi_csi2.num_data_lanes == 0 || +- endpoint.nr_of_link_frequencies == 0) { +- dev_err(dev, "missing CSI-2 properties in endpoint\n"); +- ret = -EINVAL; +- goto free_endpoint; +- } +- +- if (endpoint.bus.mipi_csi2.num_data_lanes > 4) { +- dev_err(dev, "invalid number of lanes\n"); +- ret = -EINVAL; +- goto free_endpoint; +- } +- +- state->bus = endpoint.bus.mipi_csi2; +- +- ret = clk_prepare_enable(refclk); +- if (ret) { +- dev_err(dev, "Failed! to enable clock\n"); +- goto free_endpoint; +- } +- +- state->pdata.refclk_hz = clk_get_rate(refclk); +- state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; +- state->pdata.enable_hdcp = false; +- /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ +- state->pdata.fifo_level = 16; +- /* +- * The PLL input clock is obtained by dividing refclk by pll_prd. +- * It must be between 6 MHz and 40 MHz, lower frequency is better. +- */ +- switch (state->pdata.refclk_hz) { +- case 26000000: +- case 27000000: +- case 42000000: +- state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; +- break; +- default: +- dev_err(dev, "unsupported refclk rate: %u Hz\n", +- state->pdata.refclk_hz); +- goto disable_clk; +- } +- +- /* +- * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. +- * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. +- */ +- bps_pr_lane = 2 * endpoint.link_frequencies[0]; +- if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { +- dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); +- ret = -EINVAL; +- goto disable_clk; +- } +- +- /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ +- state->pdata.pll_fbd = bps_pr_lane / +- state->pdata.refclk_hz * state->pdata.pll_prd; +- +- /* +- * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz +- * link frequency). In principle it should be possible to calculate +- * them based on link frequency and resolution. +- */ +- if (bps_pr_lane != 594000000U) +- dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); +- state->pdata.lineinitcnt = 0xe80; +- state->pdata.lptxtimecnt = 0x003; +- /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ +- state->pdata.tclk_headercnt = 0x1403; +- state->pdata.tclk_trailcnt = 0x00; +- /* ths-preparecnt: 3, ths-zerocnt: 1 */ +- state->pdata.ths_headercnt = 0x0103; +- state->pdata.twakeup = 0x4882; +- state->pdata.tclk_postcnt = 0x008; +- state->pdata.ths_trailcnt = 0x2; +- state->pdata.hstxvregcnt = 0; +- +- state->reset_gpio = devm_gpiod_get_optional(dev, "reset", +- GPIOD_OUT_LOW); +- if (IS_ERR(state->reset_gpio)) { +- dev_err(dev, "failed to get reset gpio\n"); +- ret = PTR_ERR(state->reset_gpio); +- goto disable_clk; +- } +- +- if (state->reset_gpio) +- tc358743_gpio_reset(state); +- +- ret = 0; +- goto free_endpoint; ++static void tc358743_gpio_reset(struct tc358743_state *state) { ++ usleep_range(5000, 10000); ++ // TODO: Re-implement the reset GPIO! ++ gpiod_set_value(state->reset_gpio, 0); ++ // gpio_set_value((int)state->reset_gpio, 1); ++ usleep_range(1000, 2000); ++ // gpio_set_value((int)state->reset_gpio, 0); ++ gpiod_set_value(state->reset_gpio, 1); ++ msleep(20); ++} ++ ++static int tc358743_probe_of(struct tc358743_state *state) { ++ struct device *dev = &state->i2c_client->dev; ++// struct v4l2_of_endpoint *endpoint; ++ struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; ++ ++ struct device_node *ep; ++ u32 bps_pr_lane; ++ int ret = -EINVAL; ++ pr_info("%s\n", __FUNCTION__); ++ ++ if ((state->pdata.refclk_hz != 26000000) && ++ (state->pdata.refclk_hz != 27000000) && ++ (state->pdata.refclk_hz != 42000000)) { ++ pr_info("refclk_hz error \n"); ++ return ret; ++ } ++ ++ ep = of_graph_get_next_endpoint(dev->of_node, NULL); ++ if (!ep) { ++ dev_err(dev, "missing endpoint node\n"); ++ return -EINVAL; ++ } ++ ++/* endpoint = v4l2_of_alloc_parse_endpoint(ep); ++ if (IS_ERR(endpoint)) { ++ dev_err(dev, "failed to parse endpoint\n"); ++ return PTR_ERR(endpoint); ++ }*/ ++ ++ ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); ++ if (ret) { ++ dev_err(dev, "failed to parse endpoint\n"); ++ goto put_node; ++ } ++ ++ ++// if (endpoint->bus_type != V4L2_MBUS_CSI2 || ++ ++ if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || ++ endpoint.bus.mipi_csi2.num_data_lanes == 0 || ++ endpoint.nr_of_link_frequencies == 0) { ++ dev_err(dev, "missing CSI-2 properties in endpoint\n"); ++ goto free_endpoint; ++ } ++ ++ pr_info("tc358743 endpoint.bus.mipi_csi2.flags %d\n", ++ endpoint.bus.mipi_csi2.flags); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.clock_lane %d\n", ++ endpoint.bus.mipi_csi2.clock_lane); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.num_data_lanes %d\n", ++ endpoint.bus.mipi_csi2.num_data_lanes); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.data_lanes [%d-%d-%d-%d]\n", ++ endpoint.bus.mipi_csi2.data_lanes[0], ++ endpoint.bus.mipi_csi2.data_lanes[1], ++ endpoint.bus.mipi_csi2.data_lanes[2], ++ endpoint.bus.mipi_csi2.data_lanes[3]); ++ pr_info("tc358743 endpoint.nr_of_link_frequencies %d\n", ++ endpoint.nr_of_link_frequencies); ++ ++ state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; ++ state->pdata.hdmi_detection_delay = HDMI_MODE_DELAY_100_MS; ++ state->pdata.enable_hdcp = false; ++ /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ ++ state->pdata.fifo_level = 16; ++ /* ++ * The PLL input clock is obtained by dividing refclk by pll_prd. ++ * It must be between 6 MHz and 40 MHz, lower frequency is better. ++ */ ++ switch (state->pdata.refclk_hz) { ++ //~ case 26322581: ++ //~ state->pdata.refclk_hz = 26322581; ++ case 26000000: ++ case 27000000: ++ //~ case 40800000: /* Tegra */ ++ case 42000000: ++ state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; ++ break; ++ default: ++ dev_err(dev,"Unsupported refclk rate: %u Hz\n", state->pdata.refclk_hz); ++ goto disable_clk; ++ } ++dev_err(dev, "HERE #1\n"); ++ /* ++ * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. ++ * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. ++ */ ++ bps_pr_lane = 2 * endpoint.link_frequencies[0]; ++ // if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { ++ if (bps_pr_lane < 62500000U || bps_pr_lane > 1188000000U) { ++ dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); ++ goto disable_clk; ++ } ++dev_err(dev, "HERE #2\n"); ++ /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ ++ state->pdata.pll_fbd = ++ bps_pr_lane / state->pdata.refclk_hz * state->pdata.pll_prd; ++ ++ /* ++ * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz ++ * link frequency). In principle it should be possible to calculate ++ * them based on link frequency and resolution. ++ */ ++ if (bps_pr_lane != 594000000U) ++ dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); ++ pr_info("tc358743 state->pdata.pll_prd=%d\r\n", state->pdata.pll_prd); ++ pr_info("tc358743 state->pdata.pll_fbd=%d\r\n", state->pdata.pll_fbd); ++dev_err(dev, "HERE #3\n"); ++ // freq = refclk / prd * fbd, default = 594 MHz ++// state->pdata.lineinitcnt = 0xe80; ++// state->pdata.lptxtimecnt = 0x003; ++// /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ ++// state->pdata.tclk_headercnt = 0x1403; ++// state->pdata.tclk_trailcnt = 0x00; ++// /* ths-preparecnt: 3, ths-zerocnt: 1 */ ++// state->pdata.ths_headercnt = 0x0103; ++// state->pdata.twakeup = 0x4882; ++// state->pdata.tclk_postcnt = 0x008; ++// state->pdata.ths_trailcnt = 0x2; ++// state->pdata.hstxvregcnt = 2; ++ ++ state->pdata.lineinitcnt = 0x1d01;// ++ state->pdata.lptxtimecnt = 0x008;// ++ /* tclk-preparecnt: 6, tclk-zerocnt: 45 */ ++ state->pdata.tclk_headercnt = 0x2D06;//0x0218 ++ state->pdata.tclk_trailcnt = 0x09;// ++ /* ths-preparecnt: 7, ths-zerocnt: 17 */ ++ state->pdata.ths_headercnt = 0xd06;//0x0220 ++ state->pdata.twakeup = 0x4883;// ++ state->pdata.tclk_postcnt = 0x010;// ++ state->pdata.ths_trailcnt = 0xA;// ++ state->pdata.hstxvregcnt = 5;//5 ++ state->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(state->reset_gpio)) { ++ dev_err(dev, "failed to get reset gpio\n"); ++ ret = PTR_ERR(state->reset_gpio); ++ goto disable_clk; ++ } ++ if(state->reset_gpio) { ++ pr_info("Calling reset GPIO but NOT IMPLEMENTED!"); ++ tc358743_gpio_reset(state); ++ } ++ ret = 0; ++ goto free_endpoint; + + disable_clk: +- clk_disable_unprepare(refclk); ++ // clk_disable_unprepare(refclk); + free_endpoint: +- v4l2_fwnode_endpoint_free(&endpoint); ++ // v4l2_of_free_endpoint(endpoint); ++ v4l2_fwnode_endpoint_free(&endpoint); + put_node: +- of_node_put(ep); +- return ret; ++ of_node_put(ep); ++ ++ return ret; + } + #else +-static inline int tc358743_probe_of(struct tc358743_state *state) +-{ +- return -ENODEV; ++static inline int tc358743_probe_of(struct tc358743_state *state) { ++ return -ENODEV; + } + #endif + +-static int tc358743_probe(struct i2c_client *client) +-{ +- static struct v4l2_dv_timings default_timing = +- V4L2_DV_BT_CEA_640X480P59_94; +- struct tc358743_state *state; +- struct tc358743_platform_data *pdata = client->dev.platform_data; +- struct v4l2_subdev *sd; +- u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK; +- int err; +- +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +- return -EIO; +- v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", +- client->addr << 1, client->adapter->name); +- +- state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), +- GFP_KERNEL); +- if (!state) +- return -ENOMEM; +- +- state->i2c_client = client; +- +- /* platform data */ +- if (pdata) { +- state->pdata = *pdata; +- state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; +- } else { +- err = tc358743_probe_of(state); +- if (err == -ENODEV) +- v4l_err(client, "No platform data!\n"); +- if (err) +- return err; +- } +- +- sd = &state->sd; +- v4l2_i2c_subdev_init(sd, client, &tc358743_ops); +- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; +- +- /* i2c access */ +- if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { +- v4l2_info(sd, "not a TC358743 on address 0x%x\n", +- client->addr << 1); +- return -ENODEV; +- } +- +- /* control handlers */ +- v4l2_ctrl_handler_init(&state->hdl, 3); +- +- state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, +- V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); +- +- /* custom controls */ +- state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, +- &tc358743_ctrl_audio_sampling_rate, NULL); +- +- state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, +- &tc358743_ctrl_audio_present, NULL); +- +- sd->ctrl_handler = &state->hdl; +- if (state->hdl.error) { +- err = state->hdl.error; +- goto err_hdl; +- } +- +- if (tc358743_update_controls(sd)) { +- err = -ENODEV; +- goto err_hdl; +- } +- +- state->pad.flags = MEDIA_PAD_FL_SOURCE; +- sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; +- err = media_entity_pads_init(&sd->entity, 1, &state->pad); +- if (err < 0) +- goto err_hdl; +- +- state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; +- +- sd->dev = &client->dev; +- err = v4l2_async_register_subdev(sd); +- if (err < 0) +- goto err_hdl; +- +- mutex_init(&state->confctl_mutex); +- +- INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, +- tc358743_delayed_work_enable_hotplug); +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +- state->cec_adap = cec_allocate_adapter(&tc358743_cec_adap_ops, +- state, dev_name(&client->dev), +- CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL, CEC_MAX_LOG_ADDRS); +- if (IS_ERR(state->cec_adap)) { +- err = PTR_ERR(state->cec_adap); +- goto err_hdl; +- } +- irq_mask |= MASK_CEC_RMSK | MASK_CEC_TMSK; ++static int tc358743_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { ++ return 0; ++} ++ ++static const struct v4l2_subdev_internal_ops tc358743_subdev_internal_ops = { ++ .open = tc358743_open, ++}; ++ ++static const struct media_entity_operations tc358743_media_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++static const struct regmap_config sensor_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static int tc358743_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) { ++ // static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_1280X720P30; ++ static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_1920X1080P50; ++ struct v4l2_subdev_edid sd_edid = { ++ .blocks = 2, ++ .edid = edid, ++ }; ++ struct tc358743_state *state; ++ struct tc358743_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_subdev *sd; ++ int err; ++ u16 chip_id_val; ++ ++ pr_info("%s\n", __FUNCTION__); ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ pr_err("i2c check functionality failed addres %02X name %s", client->addr, ++ client->adapter->name); ++ return -EIO; ++ } ++ ++ v4l2_info(sd, "chip found @0x%x (%s)\n", client->addr, client->adapter->name); ++ ++ state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), GFP_KERNEL); ++ if (!state) { ++ pr_err("devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ v4l2_info(sd, "dev of node %s\n", client->dev.of_node->full_name); ++ if (client->dev.of_node) { ++ if (!tc358743_parse_dt(&state->pdata, client)) { ++ pr_err("Couldn't parse device tree\n"); ++ return -ENODEV; ++ } ++ } ++ ++ state->i2c_client = client; ++ ++ /* platform data */ ++ if (pdata) { ++ state->pdata = *pdata; ++ pdata->endpoint.bus.mipi_csi2.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ } else { ++ err = tc358743_probe_of(state); ++ if (err == -ENODEV) { ++ v4l_err(client, "No platform data! err = %d\n", err); ++ return -ENODEV; ++ } ++ } ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &tc358743_ops); ++ v4l2_info(sd, "Subdev init done\n"); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ++ ++ /* i2c access */ ++ chip_id_val = i2c_rd16(sd, CHIPID); ++ v4l2_info(sd, "Chip ID val: %d\n", chip_id_val); ++ ++ if ((chip_id_val & MASK_CHIPID) != 0 || chip_id_val == 99) { ++ v4l2_info(sd, "tc358743: ERROR: not a TC358743 on address0x%x\n", ++ client->addr); ++ return -ENODEV; ++ } ++ ++ /* control handlers */ ++ v4l2_ctrl_handler_init(&state->hdl, 3); ++ v4l2_info(sd, "ctrl handler initied\n"); ++ ++ /* private controls */ ++ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std( ++ &state->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); ++ ++ /* custom controls */ ++ state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom( ++ &state->hdl, &tc358743_ctrl_audio_sampling_rate, NULL); ++ ++ state->audio_present_ctrl = ++ v4l2_ctrl_new_custom(&state->hdl, &tc358743_ctrl_audio_present, NULL); ++ ++ v4l2_info(sd, "A bunch of new cutoms done\n"); ++ ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ err = state->hdl.error; ++ goto err_hdl; ++ } ++ ++ if (tc358743_update_controls(sd)) { ++ err = -ENODEV; ++ goto err_hdl; ++ } ++ ++ v4l2_info(sd, "Controls updated\n"); ++ ++ /* work queues */ ++ state->work_queues = create_singlethread_workqueue(client->name); ++ if (!state->work_queues) { ++ v4l2_err(sd, "Could not create work queue\n"); ++ err = -ENOMEM; ++ goto err_hdl; ++ } ++ v4l2_info(sd, "Work queue created\n"); ++ // sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ sd->entity.ops = &tc358743_media_ops; ++ state->pad.flags = MEDIA_PAD_FL_SOURCE; ++ v4l2_info(sd, "About to call tegra_media_entity_init\n"); ++ err = tegra_media_entity_init(&sd->entity, 1, &state->pad, true, true); ++ if (err < 0) goto err_hdl; ++ v4l2_info(sd, "tegra_media_entity_init complete\n"); ++ ++#ifdef TC358743_VOUT_RGB ++ state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; ++#else ++ state->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_1X16; + #endif + +- tc358743_initial_setup(sd); ++ v4l2_info(sd, "Set mbus_fmt_code in probe to: %d\n", state->mbus_fmt_code); ++ ++ sd->dev = &client->dev; ++ v4l2_info(sd, "About to register subdev\n"); ++ ++v4l2_info(sd, "HERE #4\n"); ++ ++ err = v4l2_async_register_subdev(sd); ++ ++v4l2_info(sd, "HERE #5\n"); ++ ++ v4l2_info(sd, "Register subdev: %d\n", err); ++ ++ if (err < 0) goto err_hdl; ++ ++ mutex_init(&state->confctl_mutex); + +- tc358743_s_dv_timings(sd, &default_timing); ++v4l2_info(sd, "HERE #6\n"); + +- tc358743_set_csi_color_space(sd); ++ INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, ++ tc358743_delayed_work_enable_hotplug); ++ INIT_DELAYED_WORK(&state->delayed_work_enable_interrupt, ++ tc358743_delayed_work_enable_interrupt); ++ INIT_WORK(&state->process_isr, tc358743_process_isr); ++ mutex_init(&state->isr_lock); + +- tc358743_init_interrupts(sd); ++ v4l2_info(sd, "before tc358743_initial_setup\r\n"); ++ tc358743_initial_setup(sd); ++ v4l2_info(sd, "after tc358743_initial_setup\r\n"); + +- if (state->i2c_client->irq) { +- err = devm_request_threaded_irq(&client->dev, +- state->i2c_client->irq, +- NULL, tc358743_irq_handler, +- IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +- "tc358743", state); +- if (err) +- goto err_work_queues; +- } else { +- INIT_WORK(&state->work_i2c_poll, +- tc358743_work_i2c_poll); +- timer_setup(&state->timer, tc358743_irq_poll_timer, 0); +- state->timer.expires = jiffies + +- msecs_to_jiffies(POLL_INTERVAL_MS); +- add_timer(&state->timer); +- } ++ tc358743_set_csi_color_space(sd); ++ v4l2_info(sd, "before tc358743_s_dv_timings\r\n"); ++ tc358743_s_dv_timings(sd, &default_timing); + +- err = cec_register_adapter(state->cec_adap, &client->dev); +- if (err < 0) { +- pr_err("%s: failed to register the cec device\n", __func__); +- cec_delete_adapter(state->cec_adap); +- state->cec_adap = NULL; +- goto err_work_queues; +- } ++ v4l2_info(sd, "before tc358743_init_interrupts, irq: %d\r\n", ++ state->i2c_client->irq); ++ tc358743_init_interrupts(sd); ++ v4l2_info(sd, "after tc358743_init_interrupts, irq: %d\r\n", ++ state->i2c_client->irq); ++ if (state->i2c_client->irq) { ++ v4l2_info(sd, "IQR request\r\n"); ++ err = devm_request_threaded_irq( ++ &client->dev, state->i2c_client->irq, NULL, tc358743_irq_handler, ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "tc358743", state); ++ v4l2_err(sd, "err, %d\n", err); ++ if (err) goto err_work_queues; ++ } ++ queue_delayed_work(state->work_queues, &state->delayed_work_enable_interrupt, ++ msecs_to_jiffies(DELAY_ENABLE_INTERRUPT_MS)); + +- tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); +- i2c_wr16(sd, INTMASK, ~irq_mask); ++ tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); ++ i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); + +- err = v4l2_ctrl_handler_setup(sd->ctrl_handler); +- if (err) +- goto err_work_queues; ++ err = v4l2_ctrl_handler_setup(sd->ctrl_handler); + +- v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, +- client->addr << 1, client->adapter->name); ++ if (err) goto err_work_queues; + +- return 0; ++ v4l2_info(sd, "%s found @0x%x (%s)\n", client->name, client->addr, ++ client->adapter->name); ++ tc358743_s_edid(sd, &sd_edid); ++ tc358743_g_edid(sd, &sd_edid); ++ ++ tc358743_log_status(sd); ++ v4l2_info(sd, "Probe complete\n"); ++ return 0; + + err_work_queues: +- cec_unregister_adapter(state->cec_adap); +- if (!state->i2c_client->irq) +- flush_work(&state->work_i2c_poll); +- cancel_delayed_work(&state->delayed_work_enable_hotplug); +- mutex_destroy(&state->confctl_mutex); ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++ mutex_destroy(&state->confctl_mutex); + err_hdl: +- media_entity_cleanup(&sd->entity); +- v4l2_ctrl_handler_free(&state->hdl); +- return err; +-} +- +-static int tc358743_remove(struct i2c_client *client) +-{ +- struct v4l2_subdev *sd = i2c_get_clientdata(client); +- struct tc358743_state *state = to_state(sd); +- +- if (!state->i2c_client->irq) { +- del_timer_sync(&state->timer); +- flush_work(&state->work_i2c_poll); +- } +- cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); +- cec_unregister_adapter(state->cec_adap); +- v4l2_async_unregister_subdev(sd); +- v4l2_device_unregister_subdev(sd); +- mutex_destroy(&state->confctl_mutex); +- media_entity_cleanup(&sd->entity); +- v4l2_ctrl_handler_free(&state->hdl); +- +- return 0; +-} +- +-static const struct i2c_device_id tc358743_id[] = { +- {"tc358743", 0}, +- {} +-}; ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(&state->hdl); ++ return err; ++} ++ ++static int tc358743_remove(struct i2c_client *client) { ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct tc358743_state *state = to_state(sd); ++ ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++ v4l2_async_unregister_subdev(sd); ++ v4l2_device_unregister_subdev(sd); ++ mutex_destroy(&state->confctl_mutex); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(&state->hdl); ++ ++ return 0; ++} ++ ++static struct i2c_device_id tc358743_id[] = {{"tc358743", 0}, {}}; + + MODULE_DEVICE_TABLE(i2c, tc358743_id); + +-#if IS_ENABLED(CONFIG_OF) +-static const struct of_device_id tc358743_of_match[] = { +- { .compatible = "toshiba,tc358743" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, tc358743_of_match); ++#ifdef CONFIG_OF ++static const struct of_device_id tc358743_of_table[] = { ++ {.compatible = "toshiba,tc358743"}, {}}; ++MODULE_DEVICE_TABLE(of, tc358743_of_table); + #endif + + static struct i2c_driver tc358743_driver = { +- .driver = { +- .name = "tc358743", +- .of_match_table = of_match_ptr(tc358743_of_match), +- }, +- .probe_new = tc358743_probe, +- .remove = tc358743_remove, +- .id_table = tc358743_id, ++ .driver = ++ { ++ .of_match_table = of_match_ptr(tc358743_of_table), ++ .name = "tc358743", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tc358743_probe, ++ .remove = tc358743_remove, ++ .id_table = tc358743_id, + }; + +-module_i2c_driver(tc358743_driver); ++module_i2c_driver(tc358743_driver); +\ No newline at end of file +diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h +index 2495878dc358..665c3d3ecbfa 100644 +--- a/drivers/media/i2c/tc358743_regs.h ++++ b/drivers/media/i2c/tc358743_regs.h +@@ -1,9 +1,22 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks ++ * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * + */ + + /* +@@ -180,98 +193,8 @@ + #define CSI_START 0x0518 + #define MASK_STRT 0x00000001 + +-/* *** CEC (32 bit) *** */ +-#define CECHCLK 0x0028 /* 16 bits */ +-#define MASK_CECHCLK (0x7ff << 0) +- +-#define CECLCLK 0x002a /* 16 bits */ +-#define MASK_CECLCLK (0x7ff << 0) +- +-#define CECEN 0x0600 +-#define MASK_CECEN 0x0001 +- +-#define CECADD 0x0604 +-#define CECRST 0x0608 +-#define MASK_CECRESET 0x0001 +- +-#define CECREN 0x060c +-#define MASK_CECREN 0x0001 +- +-#define CECRCTL1 0x0614 +-#define MASK_CECACKDIS (1 << 24) +-#define MASK_CECHNC (3 << 20) +-#define MASK_CECLNC (7 << 16) +-#define MASK_CECMIN (7 << 12) +-#define MASK_CECMAX (7 << 8) +-#define MASK_CECDAT (7 << 4) +-#define MASK_CECTOUT (3 << 2) +-#define MASK_CECRIHLD (1 << 1) +-#define MASK_CECOTH (1 << 0) +- +-#define CECRCTL2 0x0618 +-#define MASK_CECSWAV3 (7 << 12) +-#define MASK_CECSWAV2 (7 << 8) +-#define MASK_CECSWAV1 (7 << 4) +-#define MASK_CECSWAV0 (7 << 0) +- +-#define CECRCTL3 0x061c +-#define MASK_CECWAV3 (7 << 20) +-#define MASK_CECWAV2 (7 << 16) +-#define MASK_CECWAV1 (7 << 12) +-#define MASK_CECWAV0 (7 << 8) +-#define MASK_CECACKEI (1 << 4) +-#define MASK_CECMINEI (1 << 3) +-#define MASK_CECMAXEI (1 << 2) +-#define MASK_CECRSTEI (1 << 1) +-#define MASK_CECWAVEI (1 << 0) +- +-#define CECTEN 0x0620 +-#define MASK_CECTBUSY (1 << 1) +-#define MASK_CECTEN (1 << 0) +- +-#define CECTCTL 0x0628 +-#define MASK_CECSTRS (7 << 20) +-#define MASK_CECSPRD (7 << 16) +-#define MASK_CECDTRS (7 << 12) +-#define MASK_CECDPRD (15 << 8) +-#define MASK_CECBRD (1 << 4) +-#define MASK_CECFREE (15 << 0) +- +-#define CECRSTAT 0x062c +-#define MASK_CECRIWA (1 << 6) +-#define MASK_CECRIOR (1 << 5) +-#define MASK_CECRIACK (1 << 4) +-#define MASK_CECRIMIN (1 << 3) +-#define MASK_CECRIMAX (1 << 2) +-#define MASK_CECRISTA (1 << 1) +-#define MASK_CECRIEND (1 << 0) +- +-#define CECTSTAT 0x0630 +-#define MASK_CECTIUR (1 << 4) +-#define MASK_CECTIACK (1 << 3) +-#define MASK_CECTIAL (1 << 2) +-#define MASK_CECTIEND (1 << 1) +- +-#define CECRBUF1 0x0634 +-#define MASK_CECRACK (1 << 9) +-#define MASK_CECEOM (1 << 8) +-#define MASK_CECRBYTE (0xff << 0) +- +-#define CECTBUF1 0x0674 +-#define MASK_CECTEOM (1 << 8) +-#define MASK_CECTBYTE (0xff << 0) +- +-#define CECRCTR 0x06b4 +-#define MASK_CECRCTR (0x1f << 0) +- +-#define CECIMSK 0x06c0 +-#define MASK_CECTIM (1 << 1) +-#define MASK_CECRIM (1 << 0) +- +-#define CECICLR 0x06cc +-#define MASK_CECTICLR (1 << 1) +-#define MASK_CECRICLR (1 << 0) +- ++#define CECEN 0x0600 ++#define MASK_CECEN 0x0001 + + #define HDMI_INT0 0x8500 + #define MASK_I_KEY 0x80 +@@ -497,7 +420,6 @@ + #define MASK_MODE_RST_TN 0x20 + #define MASK_LINE_REKEY 0x10 + #define MASK_AUTO_CLR 0x04 +-#define MASK_MANUAL_AUTHENTICATION 0x02 /* Not in REF_01 */ + + #define HDCP_REG1 0x8563 /* Not in REF_01 */ + #define MASK_AUTH_UNAUTH_SEL 0x70 +@@ -756,4 +678,4 @@ + #define EDID_RAM 0x8C00 + #define NO_GDB_LIMIT 0x9007 + +-#endif ++#endif +\ No newline at end of file +diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c +index a593ea0598b5..1925f28b40ed 100644 +--- a/drivers/media/v4l2-core/v4l2-dev.c ++++ b/drivers/media/v4l2-core/v4l2-dev.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #define VIDEO_NUM_DEVICES 256 + #define VIDEO_NAME "video4linux" +@@ -84,11 +85,63 @@ static ssize_t name_show(struct device *cd, + } + static DEVICE_ATTR_RO(name); + ++static ssize_t if_name_show(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *vdev = to_video_device(cd); ++ ++ return sprintf(buf, "%s\n", vdev->if_name); ++} ++static DEVICE_ATTR_RO(if_name); ++ ++static ssize_t bus_info_show(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *vdev = to_video_device(cd); ++ ++ return sprintf(buf, "%s\n", vdev->bus_info); ++} ++static DEVICE_ATTR_RO(bus_info); ++ ++static ssize_t flush_show(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *vdev = to_video_device(cd); ++ ++ return sprintf(buf, "%s\n", &vdev->flush); ++} ++static DEVICE_ATTR_RO(flush); ++ ++static ssize_t availability_show(struct device *cd, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct video_device *vdev = to_video_device(cd); ++ ++ return sprintf(buf, "%d\n", vdev->open_count > 0 ? 0 : 1); ++} ++static DEVICE_ATTR_RO(availability); ++ ++static ssize_t streamoff_show(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *vdev = to_video_device(cd); ++ struct vb2_queue *q = vdev->queue; ++ ++ return sprintf(buf, "%d\n", q->streaming ? 0 : 2); ++} ++static DEVICE_ATTR_RO(streamoff); ++ + static struct attribute *video_device_attrs[] = { +- &dev_attr_name.attr, +- &dev_attr_dev_debug.attr, +- &dev_attr_index.attr, +- NULL, ++ &dev_attr_name.attr, ++ &dev_attr_dev_debug.attr, ++ &dev_attr_index.attr, ++ &dev_attr_if_name.attr, ++ &dev_attr_bus_info.attr, ++ &dev_attr_flush.attr, ++ &dev_attr_availability.attr, ++ &dev_attr_streamoff.attr, ++ NULL, + }; + ATTRIBUTE_GROUPS(video_device); + +@@ -417,6 +470,9 @@ static int v4l2_open(struct inode *inode, struct file *filp) + } + /* and increase the device refcount */ + video_get(vdev); ++ if (vdev->open_count++ == 0) { ++ sysfs_notify(&vdev->dev.kobj, NULL, "availability"); ++ } + mutex_unlock(&videodev_lock); + if (vdev->fops->open) { + if (video_is_registered(vdev)) +@@ -429,8 +485,14 @@ static int v4l2_open(struct inode *inode, struct file *filp) + dprintk("%s: open (%d)\n", + video_device_node_name(vdev), ret); + /* decrease the refcount in case of an error */ +- if (ret) ++ if (ret) { ++ mutex_lock(&videodev_lock); ++ if (--vdev->open_count == 0) { ++ sysfs_notify(&vdev->dev.kobj, NULL, "availability"); ++ } ++ mutex_unlock(&videodev_lock); + video_put(vdev); ++ } + return ret; + } + +@@ -460,6 +522,13 @@ static int v4l2_release(struct inode *inode, struct file *filp) + dprintk("%s: release\n", + video_device_node_name(vdev)); + ++ mutex_lock(&videodev_lock); ++ if (--vdev->open_count == 0) { ++ sysfs_notify(&vdev->dev.kobj, NULL, "availability"); ++ } ++ mutex_unlock(&videodev_lock); ++ ++ + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + video_put(vdev); +diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c +index 62550fb6ca08..aca98f1f3074 100644 +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1403,6 +1403,27 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) + case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG (Exp.)"; break; + case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG (Exp.)"; break; + case V4L2_PIX_FMT_SRGGB16: descr = "16-bit Bayer RGRG/GBGB (Exp.)"; break; ++ case V4L2_PIX_FMT_CUSTOM: descr = "0x31 MIPI DATATYPE"; break; ++ case V4L2_PIX_FMT_TX2_Y10: descr = "10-bit/16-bit Greyscale"; break; ++ case V4L2_PIX_FMT_TX2_Y12: descr = "12-bit/16-bit Greyscale"; break; ++ case V4L2_PIX_FMT_TX2_SBGGR10: descr = "10-bit/16-bit Bayer BGBG/GRGR"; break; ++ case V4L2_PIX_FMT_TX2_SGBRG10: descr = "10-bit/16-bit Bayer GBGB/RGRG"; break; ++ case V4L2_PIX_FMT_TX2_SGRBG10: descr = "10-bit/16-bit Bayer GRGR/BGBG"; break; ++ case V4L2_PIX_FMT_TX2_SRGGB10: descr = "10-bit/16-bit Bayer RGRG/GBGB"; break; ++ case V4L2_PIX_FMT_TX2_SBGGR12: descr = "12-bit/16-bit Bayer BGBG/GRGR"; break; ++ case V4L2_PIX_FMT_TX2_SGBRG12: descr = "12-bit/16-bit Bayer GBGB/RGRG"; break; ++ case V4L2_PIX_FMT_TX2_SGRBG12: descr = "12-bit/16-bit Bayer GRGR/BGBG"; break; ++ case V4L2_PIX_FMT_TX2_SRGGB12: descr = "12-bit/16-bit Bayer RGRG/GBGB"; break; ++ case V4L2_PIX_FMT_XAVIER_Y10: descr = "10-bit/16-bit Greyscale"; break; ++ case V4L2_PIX_FMT_XAVIER_Y12: descr = "12-bit/16-bit Greyscale"; break; ++ case V4L2_PIX_FMT_XAVIER_SBGGR10: descr = "10-bit/16-bit Bayer BGBG/GRGR"; break; ++ case V4L2_PIX_FMT_XAVIER_SGBRG10: descr = "10-bit/16-bit Bayer GBGB/RGRG"; break; ++ case V4L2_PIX_FMT_XAVIER_SGRBG10: descr = "10-bit/16-bit Bayer GRGR/BGBG"; break; ++ case V4L2_PIX_FMT_XAVIER_SRGGB10: descr = "10-bit/16-bit Bayer RGRG/GBGB"; break; ++ case V4L2_PIX_FMT_XAVIER_SBGGR12: descr = "12-bit/16-bit Bayer BGBG/GRGR"; break; ++ case V4L2_PIX_FMT_XAVIER_SGBRG12: descr = "12-bit/16-bit Bayer GBGB/RGRG"; break; ++ case V4L2_PIX_FMT_XAVIER_SGRBG12: descr = "12-bit/16-bit Bayer GRGR/BGBG"; break; ++ case V4L2_PIX_FMT_XAVIER_SRGGB12: descr = "12-bit/16-bit Bayer RGRG/GBGB"; break; + case V4L2_PIX_FMT_SN9C20X_I420: descr = "GSPCA SN9C20X I420"; break; + case V4L2_PIX_FMT_SPCA501: descr = "GSPCA SPCA501"; break; + case V4L2_PIX_FMT_SPCA505: descr = "GSPCA SPCA505"; break; +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c +index f9ecbebe6442..4d6973da3917 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra.c ++++ b/drivers/pinctrl/tegra/pinctrl-tegra.c +@@ -106,6 +106,7 @@ static const struct cfg_param { + {"nvidia,drive-type", TEGRA_PINCONF_PARAM_DRIVE_TYPE}, + {"nvidia,func", TEGRA_PINCONF_PARAM_FUNCTION}, + {"nvidia,pad-power", TEGRA_PINCONF_PARAM_PAD_POWER}, ++ {"nvidia,lpdr", TEGRA_PINCONF_PARAM_LPDR}, + }; + + static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, +@@ -631,6 +632,12 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, + *bit = g->pad_bit; + *width = 1; + break; ++ case TEGRA_PINCONF_PARAM_LPDR: ++ *bank = g->mux_bank; ++ *reg = g->mux_reg; ++ *bit = g->lpdr_bit; ++ *width = 1; ++ break; + default: + dev_err(pmx->dev, "Invalid config param %04x\n", param); + return -ENOTSUPP; +diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.h b/drivers/pinctrl/tegra/pinctrl-tegra.h +index e6cc96abc3da..0b06a8951a65 100644 +--- a/drivers/pinctrl/tegra/pinctrl-tegra.h ++++ b/drivers/pinctrl/tegra/pinctrl-tegra.h +@@ -58,6 +58,8 @@ enum tegra_pinconf_param { + TEGRA_PINCONF_PARAM_FUNCTION, + /* argument: Boolean */ + TEGRA_PINCONF_PARAM_PAD_POWER, ++ /* argument: Boolean */ ++ TEGRA_PINCONF_PARAM_LPDR, + }; + + enum tegra_pinconf_pull { +diff --git a/include/media/avt_csi2_soc.h b/include/media/avt_csi2_soc.h +new file mode 100644 +index 000000000000..e89c586e8a55 +--- /dev/null ++++ b/include/media/avt_csi2_soc.h +@@ -0,0 +1,45 @@ ++/*============================================================================= ++ Copyright (C) 2021 Allied Vision Technologies. All Rights Reserved. ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ----------------------------------------------------------------------------- ++File: avt_csi2_soc.h ++version: 1.0.0 ++=============================================================================*/ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// DEFINES ++//////////////////////////////////////////////////////////////////////////////// ++ ++#ifndef AVT_CSI2_SOC_H ++#define AVT_CSI2_SOC_H ++ ++/* D-PHY 1.2 clock frequency range (up to 2.5 Gbps per lane, DDR) */ ++#define CSI_HOST_CLK_MIN_FREQ 40000000 ++#define CSI_HOST_CLK_MAX_FREQ 1250000000 ++ ++/* VI restrictions */ ++#define FRAMESIZE_MIN_W 256 ++#define FRAMESIZE_MAX_W 32768 ++#define FRAMESIZE_INC_W 192 ++#define FRAMESIZE_MIN_H 32 ++#define FRAMESIZE_MAX_H 32768 ++#define FRAMESIZE_INC_H 1 ++ ++#define OFFSET_INC_W 8 /* Tegra doesn't seem to accept offsets that are not divisible by 8. */ ++#define OFFSET_INC_H 8 ++ ++/* Support only 0x31 datatype */ ++#define DATA_IDENTIFIER_INQ_1 0x0002000000000000ull ++#define DATA_IDENTIFIER_INQ_2 0x0 ++#define DATA_IDENTIFIER_INQ_3 0x0 ++#define DATA_IDENTIFIER_INQ_4 0x0 ++#define MIN_ANNOUNCED_FRAMES 1 ++ ++#endif /* AVT_CSI2_SOC_H */ +\ No newline at end of file +diff --git a/include/media/i2c/tc358743.h b/include/media/i2c/tc358743.h +index b343650c2948..f90ae1a1f70e 100644 +--- a/include/media/i2c/tc358743.h ++++ b/include/media/i2c/tc358743.h +@@ -1,8 +1,22 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ + /* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * +- * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. ++ * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights ++ * reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * + */ + + /* +@@ -13,105 +27,122 @@ + + #ifndef _TC358743_ + #define _TC358743_ ++//#include ++#include ++ ++enum tc358743_csi_port { CSI_TX_NONE = 0, CSI_TX_0, CSI_TX_1, CSI_TX_BOTH }; + + enum tc358743_ddc5v_delays { +- DDC5V_DELAY_0_MS, +- DDC5V_DELAY_50_MS, +- DDC5V_DELAY_100_MS, +- DDC5V_DELAY_200_MS, ++ DDC5V_DELAY_0_MS, ++ DDC5V_DELAY_50_MS, ++ DDC5V_DELAY_100_MS, ++ DDC5V_DELAY_200_MS, ++ DDC5V_DELAY_MAX = DDC5V_DELAY_200_MS, + }; + + enum tc358743_hdmi_detection_delay { +- HDMI_MODE_DELAY_0_MS, +- HDMI_MODE_DELAY_25_MS, +- HDMI_MODE_DELAY_50_MS, +- HDMI_MODE_DELAY_100_MS, ++ HDMI_MODE_DELAY_0_MS, ++ HDMI_MODE_DELAY_25_MS, ++ HDMI_MODE_DELAY_50_MS, ++ HDMI_MODE_DELAY_100_MS, + }; + + struct tc358743_platform_data { +- /* System clock connected to REFCLK (pin H5) */ +- u32 refclk_hz; /* 26 MHz, 27 MHz or 42 MHz */ +- +- /* DDC +5V debounce delay to avoid spurious interrupts when the cable +- * is connected. +- * Sets DDC5V_MODE in register DDC_CTL. +- * Default: DDC5V_DELAY_0_MS +- */ +- enum tc358743_ddc5v_delays ddc5v_delay; +- +- bool enable_hdcp; +- +- /* +- * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO +- * level to somewhere in the middle (e.g. 300), so it can cover speed +- * mismatches in input and output ports. +- */ +- u16 fifo_level; +- +- /* Bps pr lane is (refclk_hz / pll_prd) * pll_fbd */ +- u16 pll_prd; +- u16 pll_fbd; +- +- /* CSI +- * Calculate CSI parameters with REF_02 for the highest resolution your +- * CSI interface can handle. The driver will adjust the number of CSI +- * lanes in use according to the pixel clock. +- * +- * The values in brackets are calculated with REF_02 when the number of +- * bps pr lane is 823.5 MHz, and can serve as a starting point. +- */ +- u32 lineinitcnt; /* (0x00001770) */ +- u32 lptxtimecnt; /* (0x00000005) */ +- u32 tclk_headercnt; /* (0x00001d04) */ +- u32 tclk_trailcnt; /* (0x00000000) */ +- u32 ths_headercnt; /* (0x00000505) */ +- u32 twakeup; /* (0x00004650) */ +- u32 tclk_postcnt; /* (0x00000000) */ +- u32 ths_trailcnt; /* (0x00000004) */ +- u32 hstxvregcnt; /* (0x00000005) */ +- +- /* DVI->HDMI detection delay to avoid unnecessary switching between DVI +- * and HDMI mode. +- * Sets HDMI_DET_V in register HDMI_DET. +- * Default: HDMI_MODE_DELAY_0_MS +- */ +- enum tc358743_hdmi_detection_delay hdmi_detection_delay; +- +- /* Reset PHY automatically when TMDS clock goes from DC to AC. +- * Sets PHY_AUTO_RST2 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_detected; +- +- /* Reset PHY automatically when TMDS clock passes 21 MHz. +- * Sets PHY_AUTO_RST3 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_in_range; +- +- /* Reset PHY automatically when TMDS clock is detected. +- * Sets PHY_AUTO_RST4 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_valid; +- +- /* Reset HDMI PHY automatically when hsync period is out of range. +- * Sets H_PI_RST in register HV_RST. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_hsync_out_of_range; +- +- /* Reset HDMI PHY automatically when vsync period is out of range. +- * Sets V_PI_RST in register HV_RST. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_vsync_out_of_range; ++ /* GPIOs */ ++ int reset_gpio; ++ ++#ifdef CONFIG_V4L2_FWNODE ++ struct v4l2_fwnode_endpoint endpoint; ++#else ++ struct v4l2_of_endpoint endpoint; ++#endif ++ ++ /* System clock connected to REFCLK (pin H5) */ ++ u32 refclk_hz; /* 26 MHz, 27 MHz or 42 MHz */ ++ ++ /* DDC +5V debounce delay to avoid spurious interrupts when the cable ++ * is connected. ++ * Sets DDC5V_MODE in register DDC_CTL. ++ * Default: DDC5V_DELAY_0_MS ++ */ ++ enum tc358743_ddc5v_delays ddc5v_delay; ++ ++ bool enable_hdcp; ++ ++ /* CSI Output */ ++ enum tc358743_csi_port csi_port; // TODO: Should this be port-index? ++ ++ /* ++ * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO ++ * level to somewhere in the middle (e.g. 300), so it can cover speed ++ * mismatches in input and output ports. ++ */ ++ u16 fifo_level; ++ ++ /* Bps pr lane is (refclk_hz / pll_prd) * pll_fbd */ ++ u16 pll_prd; ++ u16 pll_fbd; ++ ++ /* CSI ++ * Calculate CSI parameters with REF_02 for the highest resolution your ++ * CSI interface can handle. The driver will adjust the number of CSI ++ * lanes in use according to the pixel clock. ++ * ++ * The values in brackets are calculated with REF_02 when the number of ++ * bps pr lane is 823.5 MHz, and can serve as a starting point. ++ */ ++ u32 lineinitcnt; /* (0x00001770) */ ++ u32 lptxtimecnt; /* (0x00000005) */ ++ u32 tclk_headercnt; /* (0x00001d04) */ ++ u32 tclk_trailcnt; /* (0x00000000) */ ++ u32 ths_headercnt; /* (0x00000505) */ ++ u32 twakeup; /* (0x00004650) */ ++ u32 tclk_postcnt; /* (0x00000000) */ ++ u32 ths_trailcnt; /* (0x00000004) */ ++ u32 hstxvregcnt; /* (0x00000005) */ ++ ++ /* DVI->HDMI detection delay to avoid unnecessary switching between DVI ++ * and HDMI mode. ++ * Sets HDMI_DET_V in register HDMI_DET. ++ * Default: HDMI_MODE_DELAY_0_MS ++ */ ++ enum tc358743_hdmi_detection_delay hdmi_detection_delay; ++ ++ /* Reset PHY automatically when TMDS clock goes from DC to AC. ++ * Sets PHY_AUTO_RST2 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_detected; ++ ++ /* Reset PHY automatically when TMDS clock passes 21 MHz. ++ * Sets PHY_AUTO_RST3 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_in_range; ++ ++ /* Reset PHY automatically when TMDS clock is detected. ++ * Sets PHY_AUTO_RST4 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_valid; ++ ++ /* Reset HDMI PHY automatically when hsync period is out of range. ++ * Sets H_PI_RST in register HV_RST. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_hsync_out_of_range; ++ ++ /* Reset HDMI PHY automatically when vsync period is out of range. ++ * Sets V_PI_RST in register HV_RST. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_vsync_out_of_range; + }; + + /* custom controls */ + /* Audio sample rate in Hz */ + #define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) + /* Audio present status */ +-#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) ++#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) + +-#endif ++#endif +\ No newline at end of file +diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h +index ad2d41952442..055a3eae930c 100644 +--- a/include/media/v4l2-dev.h ++++ b/include/media/v4l2-dev.h +@@ -307,6 +307,11 @@ struct video_device + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); + + struct mutex *lock; ++ ++ char if_name[32]; ++ char bus_info[32]; ++ char flush; ++ int open_count; + }; + + /** +diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h +index bbb3f26fbde9..7d8983aefaea 100644 +--- a/include/media/videobuf2-core.h ++++ b/include/media/videobuf2-core.h +@@ -793,7 +793,11 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, + int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, + unsigned int *count, + unsigned int requested_planes, +- const unsigned int requested_sizes[]); ++ const unsigned int requested_sizes[], bool req_index, int index); ++ ++int vb2_core_create_single_buf(struct vb2_queue *q, enum vb2_memory memory, ++ unsigned int *count, unsigned int requested_planes, ++ const unsigned int requested_sizes[], bool req_index, int index); + + /** + * vb2_core_prepare_buf() - Pass ownership of a buffer from userspace +@@ -949,6 +953,9 @@ int vb2_core_queue_init(struct vb2_queue *q); + */ + void vb2_core_queue_release(struct vb2_queue *q); + ++void vb2_core_queue_cancel(struct vb2_queue *q); ++ ++ + /** + * vb2_queue_error() - signal a fatal error on the queue + * @q: pointer to &struct vb2_queue with videobuf2 queue. +diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h +index c203047eb834..2141583d8a69 100644 +--- a/include/media/videobuf2-v4l2.h ++++ b/include/media/videobuf2-v4l2.h +@@ -100,6 +100,14 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req); + */ + int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create); + ++/** ++ * vb2_buffer_free() - Free single vb2 buffer ++ * ++ * @q: videobuf2 queue ++ * @index: index of the buffer to be freed ++ */ ++int vb2_buffer_free(struct vb2_queue *q, unsigned int index); ++ + /** + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel + * +diff --git a/include/uapi/linux/libcsi_ioctl.h b/include/uapi/linux/libcsi_ioctl.h +new file mode 100644 +index 000000000000..ceb17021d9f9 +--- /dev/null ++++ b/include/uapi/linux/libcsi_ioctl.h +@@ -0,0 +1,349 @@ ++/*============================================================================= ++ Copyright (C) 2020 Allied Vision Technologies. All Rights Reserved. ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ ----------------------------------------------------------------------------- ++ ++File: libcsi_ioctl.h ++ ++version: 1.7.10 ++=============================================================================*/ ++ ++ ++ ++//////////////////////////////////////////////////////////////////////////////// ++// DEFINES ++//////////////////////////////////////////////////////////////////////////////// ++#ifndef LIBCSI_IOCTL_H ++#define LIBCSI_IOCTL_H ++ ++#include ++ ++/* Version of the libcsi - driver interface spec */ ++#define LIBCSI_DRV_SPEC_VERSION_MAJOR 1 ++#define LIBCSI_DRV_SPEC_VERSION_MINOR 0 ++#define LIBCSI_DRV_SPEC_VERSION_PATCH 8 ++ ++/* Buffer status reported by driver for returned frames */ ++#define V4L2_BUF_FLAG_INCOMPLETE 0x10000000 ++#define V4L2_BUF_FLAG_UNUSED 0x20000000 ++#define V4L2_BUF_FLAG_VALID 0x40000000 ++#define V4L2_BUF_FLAG_INVALID 0x80000000 ++#define V4L2_BUF_FLAG_INVALIDINCOMPLETE (V4L2_BUF_FLAG_INCOMPLETE | V4L2_BUF_FLAG_INVALID) ++/* Driver capabilities flags. See v4l2_csi_driver_info */ ++#define AVT_DRVCAP_USRPTR 0x00000001 ++#define AVT_DRVCAP_MMAP 0x00000002 ++ ++//////////////////////////////////////////////////////////////////////////////// ++// ENUMS ++//////////////////////////////////////////////////////////////////////////////// ++enum v4l2_lane_counts ++{ ++ V4L2_LANE_COUNT_1_LaneSupport = 0x1, ++ V4L2_LANE_COUNT_2_LaneSupport = 0x2, ++ V4L2_LANE_COUNT_3_LaneSupport = 0x4, ++ V4L2_LANE_COUNT_4_LaneSupport = 0x8, ++}; ++ ++enum v4l2_statistics_capability ++{ ++ V4L2_STATISTICS_CAPABILITY_FrameCount = 0x1, ++ V4L2_STATISTICS_CAPABILITY_PacketCRCError = 0x2, ++ V4L2_STATISTICS_CAPABILITY_FramesUnderrun = 0x4, ++ V4L2_STATISTICS_CAPABILITY_FramesIncomplete = 0x8, ++ V4L2_STATISTICS_CAPABILITY_CurrentFrameCount = 0x10, ++ V4L2_STATISTICS_CAPABILITY_CurrentFrameInterval = 0x20, ++}; ++ ++enum gencp_handshake_state ++{ ++ GENCP_HANDSHAKE_BUFFER_CLEARED = 0x0, ++ GENCP_HANDSHAKE_BUFFER_VALID = 0x1, ++ GENCP_HANDSHAKE_BUFFER_PROCESSED = 0x2, ++}; ++ ++enum manufacturer_id ++{ ++ MANUFACTURER_ID_NXP = 0x00, ++ MANUFACTURER_ID_NVIDIA = 0x01, ++}; ++ ++enum soc_family_id ++{ ++ SOC_FAMILY_ID_IMX6 = 0x00, ++ SOC_FAMILY_ID_TEGRA = 0x01, ++ SOC_FAMILY_ID_IMX8 = 0x02, ++ SOC_FAMILY_ID_IMX8M = 0x03, ++ SOC_FAMILY_ID_IMX8X = 0x04, ++}; ++ ++enum imx6_driver_id ++{ ++ IMX6_DRIVER_ID_NITROGEN = 0x00, ++ IMX6_DRIVER_ID_WANDBOARD = 0x01, ++}; ++ ++enum tegra_driver_id ++{ ++ TEGRA_DRIVER_ID_DEFAULT = 0x00, ++}; ++ ++enum imx8_driver_id ++{ ++ IMX8_DRIVER_ID_DEFAULT = 0x00, ++}; ++ ++enum imx8m_driver_id ++{ ++ IMX8M_DRIVER_ID_DEFAULT = 0x00, ++}; ++ ++enum imx8x_driver_id ++{ ++ IMX8X_DRIVER_ID_DEFAULT = 0x00, ++}; ++ ++enum v4l2_triggeractivation ++{ ++ V4L2_TRIGGER_ACTIVATION_RISING_EDGE = 0, ++ V4L2_TRIGGER_ACTIVATION_FALLING_EDGE = 1, ++ V4L2_TRIGGER_ACTIVATION_ANY_EDGE = 2, ++ V4L2_TRIGGER_ACTIVATION_LEVEL_HIGH = 3, ++ V4L2_TRIGGER_ACTIVATION_LEVEL_LOW = 4 ++}; ++ ++enum v4l2_triggersource ++{ ++ V4L2_TRIGGER_SOURCE_LINE0 = 0, ++ V4L2_TRIGGER_SOURCE_LINE1 = 1, ++ V4L2_TRIGGER_SOURCE_LINE2 = 2, ++ V4L2_TRIGGER_SOURCE_LINE3 = 3, ++ V4L2_TRIGGER_SOURCE_SOFTWARE = 4 ++}; ++ ++//////////////////////////////////////////////////////////////////////////////// ++// STRUCTS ++//////////////////////////////////////////////////////////////////////////////// ++struct v4l2_i2c ++{ ++ __u32 register_address; // Register ++ __u32 timeout; // Timeout value ++ const char* ptr_buffer; // I/O buffer ++ __u32 register_size; // Register address size (should be 2 for AVT Alvium 1500 and 1800) ++ __u32 num_bytes; // Bytes to read or write ++}; ++ ++struct v4l2_dma_mem ++{ ++ __u32 index; // index of the buffer ++ __u32 type; // enum v4l2_buf_type ++ __u32 memory; // enum v4l2_memory ++}; ++ ++struct v4l2_statistics_capabilities ++{ ++ __u64 statistics_capability; // Bitmask with statistics capabilities enum (v4l2_statistics_capability) ++}; ++ ++struct v4l2_min_announced_frames ++{ ++ __u32 min_announced_frames; // Minimum number of announced frames ++}; ++ ++struct v4l2_range ++{ ++ __u8 is_valid; // Indicates, if values are valid (1) or invalid (0) ++ __u32 min; // Minimum allowed value ++ __u32 max; // Maximum allowed value ++}; ++ ++struct v4l2_csi_host_clock_freq_ranges ++{ ++ struct v4l2_range lane_range_1; // Min and max value for 1 lane ++ struct v4l2_range lane_range_2; // Min and max value for 2 lanes ++ struct v4l2_range lane_range_3; // Min and max value for 3 lanes ++ struct v4l2_range lane_range_4; // Min and max value for 4 lanes ++}; ++ ++struct v4l2_supported_lane_counts ++{ ++ __u32 supported_lane_counts; // Bitfield with the supported lane counts from v4l2_lane_counts ++}; ++ ++struct v4l2_restriction ++{ ++ __u8 is_valid; // Indicates, if values are valid (1) or invalid (0) ++ __u32 min; // Minimum value ++ __u32 max; // Maximum value ++ __u32 inc; // Increment value ++}; ++ ++struct v4l2_ipu_restrictions ++{ ++ struct v4l2_restriction ipu_x; // X restriction ++ struct v4l2_restriction ipu_y; // Y restriction ++}; ++ ++struct v4l2_streamoff_ex ++{ ++ __u32 timeout; // Timeout value in ms ++}; ++ ++struct v4l2_gencp_buffer_sizes ++{ ++ __u32 gencp_in_buffer_size; // Size in bytes of the GenCP In buffer ++ __u32 gencp_out_buffer_size; // Size in bytes of the GenCP Out buffer ++}; ++ ++struct v4l2_csi_data_identifiers_inq ++{ ++ __u64 data_identifiers_inq_1; // Inquiry for data identifiers 0-63 ++ __u64 data_identifiers_inq_2; // Inquiry for data identifiers 64-127 ++ __u64 data_identifiers_inq_3; // Inquiry for data identifiers 128-191 ++ __u64 data_identifiers_inq_4; // Inquiry for data identifiers 192-255 ++}; ++ ++struct v4l2_stats_t ++{ ++ __u64 frames_count; // Total number of frames received ++ __u64 packet_crc_error; // Number of packets with CRC errors ++ __u64 frames_underrun; // Number of frames dropped because of buffer underrun ++ __u64 frames_incomplete; // Number of frames that were not completed ++ __u64 current_frame_count; // Number of frames received within CurrentFrameInterval (nec. to calculate fps value) ++ __u64 current_frame_interval; // Time interval between frames in µs ++}; ++ ++struct v4l2_csi_driver_info ++{ ++ union _id ++ { ++ __u32 board_id; // 32 Bit board id ++ struct ++ { ++ __u8 manufacturer_id; // 0x00 = Boundary Devices, 0x01= NVIDIA ++ __u8 soc_family_id; // 0x00 = i.MX6, 0x01=TEGRA, 0x02=i.MX8, 0x03=i.MX8X ++ __u8 driver_id; // Driver identifier for a certain soc family ++ __u8 reserved; // ++ }; ++ }id; ++ __u32 driver_version; // Driver version ++ __u32 driver_interface_version; // Used driver specification version ++ __u32 driver_caps; // Driver capabilities flags ++ __u32 usrptr_alignment; // Buffer alignment for user pointer mode in bytes ++}; ++ ++struct v4l2_csi_config ++{ ++ __u8 lane_count; // Number of lanes ++ __u32 csi_clock; // CSI clock in Hz ++}; ++ ++struct v4l2_trigger_status ++{ ++ __u8 trigger_source; // v4l2_triggersource enum value ++ __u8 trigger_activation; // v4l2_triggeractivation enum value ++ __u8 trigger_mode_enabled; // Enable (1) or disable (0) trigger mode ++}; ++ ++struct v4l2_trigger_rate ++{ ++ __u64 frames_per_period; // Number of frames per period ++ __u64 period_sec; // Period in seconds ++}; ++ ++//////////////////////////////////////////////////////////////////////////////// ++// DEFINES ++//////////////////////////////////////////////////////////////////////////////// ++// Custom ioctl definitions ++/* i2c read */ ++#define VIDIOC_R_I2C _IOWR('V', BASE_VIDIOC_PRIVATE + 0, struct v4l2_i2c) ++ ++/* i2c write */ ++#define VIDIOC_W_I2C _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct v4l2_i2c) ++ ++/* Memory alloc for a frame */ ++#define VIDIOC_MEM_ALLOC _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct v4l2_dma_mem) ++ ++/* Memory free for a frame */ ++#define VIDIOC_MEM_FREE _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_dma_mem) ++ ++/* Flush frames */ ++#define VIDIOC_FLUSH_FRAMES _IO('V', BASE_VIDIOC_PRIVATE + 4) ++ ++/* Stream statistics */ ++#define VIDIOC_STREAMSTAT _IOR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_stats_t) ++ ++/* Reset Stream statistics */ ++#define VIDIOC_RESET_STREAMSTAT _IO('V', BASE_VIDIOC_PRIVATE + 6) ++ ++/* Custom streamon */ ++#define VIDIOC_STREAMON_EX _IO('V', BASE_VIDIOC_PRIVATE + 7) ++ ++/* Custom streamoff */ ++#define VIDIOC_STREAMOFF_EX _IOW('V', BASE_VIDIOC_PRIVATE + 8, struct v4l2_streamoff_ex) ++ ++/* Get statistics capability */ ++#define VIDIOC_G_STATISTICS_CAPABILITIES _IOR('V', BASE_VIDIOC_PRIVATE + 9, struct v4l2_statistics_capabilities) ++ ++/* Get min number of announced frames*/ ++#define VIDIOC_G_MIN_ANNOUNCED_FRAMES _IOR('V', BASE_VIDIOC_PRIVATE + 10, struct v4l2_min_announced_frames) ++ ++/* Get supported lane value */ ++#define VIDIOC_G_SUPPORTED_LANE_COUNTS _IOR('V', BASE_VIDIOC_PRIVATE + 11, struct v4l2_supported_lane_counts) ++ ++/* Get CSI Host clock frequencies */ ++#define VIDIOC_G_CSI_HOST_CLK_FREQ _IOR('V', BASE_VIDIOC_PRIVATE + 12, struct v4l2_csi_host_clock_freq_ranges) ++ ++/* Get IPU restrictions */ ++#define VIDIOC_G_IPU_RESTRICTIONS _IOR('V', BASE_VIDIOC_PRIVATE + 13, struct v4l2_ipu_restrictions) ++ ++/* Get GenCPIn and GenCPOut buffer sizes */ ++#define VIDIOC_G_GENCP_BUFFER_SIZES _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct v4l2_gencp_buffer_sizes) ++ ++/* Retrieving the MIPI Data Identifier */ ++#define VIDIOC_G_SUPPORTED_DATA_IDENTIFIERS _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct v4l2_csi_data_identifiers_inq) ++ ++/* Retrieving i2c clock frequency */ ++#define VIDIOC_G_I2C_CLOCK_FREQ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, int) ++ ++/* Retrieving extended driver information */ ++#define VIDIOC_G_DRIVER_INFO _IOR('V', BASE_VIDIOC_PRIVATE + 17, struct v4l2_csi_driver_info) ++ ++/* Get CSI configuration */ ++#define VIDIOC_G_CSI_CONFIG _IOR('V', BASE_VIDIOC_PRIVATE + 18, struct v4l2_csi_config) ++ ++/* Set CSI configuration */ ++#define VIDIOC_S_CSI_CONFIG _IOWR('V', BASE_VIDIOC_PRIVATE + 19, struct v4l2_csi_config) ++ ++/* Set the Trigger mode to OFF */ ++#define VIDIOC_TRIGGER_MODE_OFF _IO('V', BASE_VIDIOC_PRIVATE + 20) ++ ++/* Set the Trigger mode to ON */ ++#define VIDIOC_TRIGGER_MODE_ON _IO('V', BASE_VIDIOC_PRIVATE + 21) ++ ++/* Set the trigger activation */ ++#define VIDIOC_S_TRIGGER_ACTIVATION _IOW('V', BASE_VIDIOC_PRIVATE + 22, int) ++ ++/* Get the trigger activation */ ++#define VIDIOC_G_TRIGGER_ACTIVATION _IOR('V', BASE_VIDIOC_PRIVATE + 23, int) ++ ++/* Set the trigger source */ ++#define VIDIOC_S_TRIGGER_SOURCE _IOW('V', BASE_VIDIOC_PRIVATE + 24, int) ++ ++/* Get the trigger source */ ++#define VIDIOC_G_TRIGGER_SOURCE _IOR('V', BASE_VIDIOC_PRIVATE + 25, int) ++ ++/* Execute a software trigger */ ++#define VIDIOC_TRIGGER_SOFTWARE _IO('V', BASE_VIDIOC_PRIVATE + 26) ++ ++ ++#endif /* LIBCSI_IOCTL_H */ +diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h +index b2362999fd3d..af27925d6347 100644 +--- a/include/uapi/linux/media-bus-format.h ++++ b/include/uapi/linux/media-bus-format.h +@@ -153,7 +153,8 @@ + /* JPEG compressed formats - next is 0x4002 */ + #define MEDIA_BUS_FMT_JPEG_1X8 0x4001 + +-/* Vendor specific formats - next is 0x5002 */ ++/* Vendor specific formats - next is 0x5003 */ ++#define MEDIA_BUS_FMT_CUSTOM 0x5002 + + /* S5C73M3 sensor specific interleaved UYVY and JPEG */ + #define MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 0x5001 +diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h +index a38454d9e0f5..a371adb18a6b 100644 +--- a/include/uapi/linux/v4l2-subdev.h ++++ b/include/uapi/linux/v4l2-subdev.h +@@ -39,6 +39,18 @@ enum v4l2_subdev_format_whence { + V4L2_SUBDEV_FORMAT_ACTIVE = 1, + }; + ++/** ++ * enum v4l2_subdev_format_whence - Media bus format type ++ * @V4L2_SUBDEV_FRMIVAL_TYPE_DISCRETE: discrete frame intervals ++ * @V4L2_SUBDEV_FRMIVAL_TYPE_CONTINUOUS: continuous frame intervals ++ * @V4L2_SUBDEV_FRMIVAL_TYPE_STEPWISE: stepwise frame intervals ++ */ ++enum v4l2_subdev_frame_interval_type{ ++ V4L2_SUBDEV_FRMIVAL_TYPE_DISCRETE = 0, ++ V4L2_SUBDEV_FRMIVAL_TYPE_CONTINUOUS = 1, ++ V4L2_SUBDEV_FRMIVAL_TYPE_STEPWISE = 2, ++}; ++ + /** + * struct v4l2_subdev_format - Pad-level media bus format + * @which: format type (from enum v4l2_subdev_format_whence) +@@ -125,8 +137,13 @@ struct v4l2_subdev_frame_interval { + * @code: format code (MEDIA_BUS_FMT_ definitions) + * @width: frame width in pixels + * @height: frame height in pixels +- * @interval: frame interval in seconds ++ * @interval: frame interval in seconds, if the type is continuous or stepwise ++ * this field contains the minimum frame interval + * @which: format type (from enum v4l2_subdev_format_whence) ++ * @type: frame interval type (from enum v4l2_subdev_frame_interval_type) ++ * @max_interval: maximum frame interval in seconds, only valid for types ++ * continuous and stepwise ++ * @step_interval: frame interval step in seconds, only valid for type stepwise + */ + struct v4l2_subdev_frame_interval_enum { + __u32 index; +@@ -136,7 +153,10 @@ struct v4l2_subdev_frame_interval_enum { + __u32 height; + struct v4l2_fract interval; + __u32 which; +- __u32 reserved[8]; ++ __u32 type; ++ struct v4l2_fract max_interval; ++ struct v4l2_fract step_interval; ++ __u32 reserved[3]; + }; + + /** +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index f750b6cce0e8..7b49d260bafa 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -760,6 +760,30 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_IPU3_SGRBG10 v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */ + #define V4L2_PIX_FMT_IPU3_SRGGB10 v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */ + ++#define V4L2_PIX_FMT_CUSTOM v4l2_fourcc('T', 'P', '3', '1') /* 0x31 mipi datatype */ ++/* TX2 */ ++#define V4L2_PIX_FMT_TX2_Y10 v4l2_fourcc('J', '2', 'Y', '0') /* 10 Greyscale */ ++#define V4L2_PIX_FMT_TX2_Y12 v4l2_fourcc('J', '2', 'Y', '2') /* 12 Greyscale */ ++#define V4L2_PIX_FMT_TX2_SBGGR10 v4l2_fourcc('J', '2', 'B', '0') /* 10 BGBG.. GRGR.. */ ++#define V4L2_PIX_FMT_TX2_SGBRG10 v4l2_fourcc('J', '2', 'G', '0') /* 10 GBGB.. RGRG.. */ ++#define V4L2_PIX_FMT_TX2_SGRBG10 v4l2_fourcc('J', '2', 'A', '0') /* 10 GRGR.. BGBG.. */ ++#define V4L2_PIX_FMT_TX2_SRGGB10 v4l2_fourcc('J', '2', 'R', '0') /* 10 RGRG.. GBGB.. */ ++#define V4L2_PIX_FMT_TX2_SBGGR12 v4l2_fourcc('J', '2', 'B', '2') /* 12 BGBG.. GRGR.. */ ++#define V4L2_PIX_FMT_TX2_SGBRG12 v4l2_fourcc('J', '2', 'G', '2') /* 12 GBGB.. RGRG.. */ ++#define V4L2_PIX_FMT_TX2_SGRBG12 v4l2_fourcc('J', '2', 'A', '2') /* 12 GRGR.. BGBG.. */ ++#define V4L2_PIX_FMT_TX2_SRGGB12 v4l2_fourcc('J', '2', 'R', '2') /* 12 RGRG.. GBGB.. */ ++/* Xavier */ ++#define V4L2_PIX_FMT_XAVIER_Y10 v4l2_fourcc('J', 'X', 'Y', '0') /* 10 Greyscale */ ++#define V4L2_PIX_FMT_XAVIER_Y12 v4l2_fourcc('J', 'X', 'Y', '2') /* 12 Greyscale */ ++#define V4L2_PIX_FMT_XAVIER_SBGGR10 v4l2_fourcc('J', 'X', 'B', '0') /* 10 BGBG.. GRGR.. */ ++#define V4L2_PIX_FMT_XAVIER_SGBRG10 v4l2_fourcc('J', 'X', 'G', '0') /* 10 GBGB.. RGRG.. */ ++#define V4L2_PIX_FMT_XAVIER_SGRBG10 v4l2_fourcc('J', 'X', 'A', '0') /* 10 GRGR.. BGBG.. */ ++#define V4L2_PIX_FMT_XAVIER_SRGGB10 v4l2_fourcc('J', 'X', 'R', '0') /* 10 RGRG.. GBGB.. */ ++#define V4L2_PIX_FMT_XAVIER_SBGGR12 v4l2_fourcc('J', 'X', 'B', '2') /* 12 BGBG.. GRGR.. */ ++#define V4L2_PIX_FMT_XAVIER_SGBRG12 v4l2_fourcc('J', 'X', 'G', '2') /* 12 GBGB.. RGRG.. */ ++#define V4L2_PIX_FMT_XAVIER_SGRBG12 v4l2_fourcc('J', 'X', 'A', '2') /* 12 GRGR.. BGBG.. */ ++#define V4L2_PIX_FMT_XAVIER_SRGGB12 v4l2_fourcc('J', 'X', 'R', '2') /* 12 RGRG.. GBGB.. */ ++ + /* SDR formats - used only for Software Defined Radio devices */ + #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ + #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +diff --git a/kernel-int-overlays.txt b/kernel-int-overlays.txt +index 6a59a925233b..c2de8e5e8f6e 100644 +--- a/kernel-int-overlays.txt ++++ b/kernel-int-overlays.txt +@@ -1,3 +1,4 @@ ++avt + nvidia + nvlink + nvgpu +diff --git a/kernel-overlays.txt b/kernel-overlays.txt +index 1a8cf218b2a9..c611ec6ae6a2 100644 +--- a/kernel-overlays.txt ++++ b/kernel-overlays.txt +@@ -1,3 +1,4 @@ ++avt + nvidia + nvlink + nvgpu +diff --git a/nvidia/drivers/media/platform/tegra/camera/csi/csi.c b/nvidia/drivers/media/platform/tegra/camera/csi/csi.c +index d788f7041ce0..2df6bcbd2c66 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/csi/csi.c ++++ b/nvidia/drivers/media/platform/tegra/camera/csi/csi.c +@@ -155,11 +155,19 @@ u64 read_mipi_clk_from_dt(struct tegra_csi_channel *chan) + u64 mipi_clk = 0; + int mode_idx; + ++ + if (chan && chan->s_data) { +- mode_idx = chan->s_data->mode_prop_idx; +- props = &chan->s_data->sensor_props; +- sig_props = &props->sensor_modes[mode_idx].signal_properties; +- mipi_clk = sig_props->mipi_clock.val; ++ struct v4l2_ctrl *link_freq_ctrl = v4l2_ctrl_find(chan->s_data->ctrl_handler,V4L2_CID_LINK_FREQ); ++ if (link_freq_ctrl != NULL) { ++ int idx = v4l2_ctrl_g_ctrl(link_freq_ctrl); ++ mipi_clk = link_freq_ctrl->qmenu_int[idx] / 2; ++ } ++ else { ++ mode_idx = chan->s_data->mode_prop_idx; ++ props = &chan->s_data->sensor_props; ++ sig_props = &props->sensor_modes[mode_idx].signal_properties; ++ mipi_clk = sig_props->mipi_clock.val; ++ } + } + + return mipi_clk; +diff --git a/nvidia/drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c b/nvidia/drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c +index 416b33ad1070..4420fc042553 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c ++++ b/nvidia/drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c +@@ -115,6 +115,16 @@ struct tegra_capture_vi_data { + /**< NVCSI stream-id & VI instance mapping, read from the DT */ + }; + ++int vi_stop_waiting(struct tegra_vi_channel *chan) ++{ ++ struct vi_capture *capture = chan->capture_data; ++ ++ complete_all(&capture->capture_resp); ++ ++ return 0; ++} ++ ++ + /** + * @brief Initialize a VI syncpoint and get its GoS backing. + * +@@ -1502,7 +1512,17 @@ int vi_capture_status( + + /* negative timeout means wait forever */ + if (timeout_ms < 0) { +- wait_for_completion(&capture->capture_resp); ++ // This is workaround for issue on Xavier that was ++ // rebooting the device after about 3 minutes. ++ // When we are executing wait_for_completion without timeout, ++ // waiting thread is marked as stalled and whole system is rebooted. ++ // In case of wait_for_completion_timeout we are executing ++ // schedule() after timeout, that fixes this problem. ++ do { ++ ret = wait_for_completion_timeout( ++ &capture->capture_resp, ++ msecs_to_jiffies(120000)); // set timeout to 2min ++ } while (ret == 0); // wait until return value is not timeout + } else { + ret = wait_for_completion_timeout( + &capture->capture_resp, +diff --git a/nvidia/drivers/media/platform/tegra/camera/vi/channel.c b/nvidia/drivers/media/platform/tegra/camera/vi/channel.c +index 03de768446ca..8be2d33e8584 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/vi/channel.c ++++ b/nvidia/drivers/media/platform/tegra/camera/vi/channel.c +@@ -45,6 +45,9 @@ + #include + #include + ++#include ++ ++ + #include + #define CREATE_TRACE_POINTS + #include +@@ -62,6 +65,25 @@ + + static s64 queue_init_ts; + ++struct camera_list_entry { ++ int channel_id; ++ struct list_head camera_list_head; ++}; ++ ++static struct list_head camera_list = LIST_HEAD_INIT(camera_list); ++ ++enum flush_state { ++ FLUSH_NOT_INITIATED = 0, ++ FLUSH_IN_PROGRESS, ++ FLUSH_DONE, ++}; ++ ++static void update_flush_state(struct tegra_channel *chan, ++ enum flush_state new_state) ++{ ++ sprintf(&chan->video->flush, "%d", new_state); ++} ++ + static bool tegra_channel_verify_focuser(struct tegra_channel *chan) + { + char *focuser; +@@ -193,6 +215,11 @@ static void tegra_channel_fmt_align(struct tegra_channel *chan, + + denominator = (!bpp->denominator) ? 1 : bpp->denominator; + numerator = (!bpp->numerator) ? 1 : bpp->numerator; ++ ++ bpl = (*width * numerator) / denominator; ++ if (!*bytesperline) ++ *bytesperline = bpl; ++ + /* The transfer alignment requirements are expressed in bytes. Compute + * the minimum and maximum values, clamp the requested width and convert + * it back to pixels. +@@ -204,10 +231,6 @@ static void tegra_channel_fmt_align(struct tegra_channel *chan, + align = align > 0 ? align : 1; + bpl = tegra_core_bytes_per_line(*width, align, vfmt); + +- /* Align stride */ +- if (chan->vi->fops->vi_stride_align) +- chan->vi->fops->vi_stride_align(&bpl); +- + if (!*bytesperline) + *bytesperline = bpl; + +@@ -541,8 +564,13 @@ void free_ring_buffers(struct tegra_channel *chan, int frames) + "%s: capture init latency is %lld ms\n", + __func__, (frame_arrived_ts - queue_init_ts)); + } +- vb2_buffer_done(&vbuf->vb2_buf, +- chan->buffer_state[chan->free_index++]); ++ /* Enable single buffer use */ ++ if (chan->capture_queue_depth == 2) ++ vb2_buffer_done(&vbuf->vb2_buf, ++ chan->buffer_state[chan->free_index]); ++ else ++ vb2_buffer_done(&vbuf->vb2_buf, ++ chan->buffer_state[chan->free_index++]); + + if (chan->free_index >= chan->capture_queue_depth) + chan->free_index = 0; +@@ -614,6 +642,33 @@ void tegra_channel_ring_buffer(struct tegra_channel *chan, + free_ring_buffers(chan, 1); + } + ++void tegra_channel_update_statistics(struct tegra_channel *chan) ++{ ++ uint64_t curr_frame_jiffies = 0; ++ ++ if (chan->capture_state != CAPTURE_GOOD) { ++ /* Mark frame as incomplete only after stopping stream */ ++ if (!atomic_read(&chan->is_streaming)) ++ { ++ chan->stream_stats.frames_incomplete++; ++ chan->incomplete_flag = true; ++ } ++ /* Frames counted as underrun doesn't have any flag, because they are considered as dropped */ ++ else ++ { ++ chan->stream_stats.frames_underrun++; ++ } ++ } ++ else ++ { ++ chan->stream_stats.frames_count++; ++ curr_frame_jiffies = get_jiffies_64(); ++ chan->stream_stats.current_frame_interval = jiffies_to_usecs(curr_frame_jiffies - chan->start_frame_jiffies); ++ chan->start_frame_jiffies = curr_frame_jiffies; ++ ++ } ++} ++ + void tegra_channel_ec_close(struct tegra_mc_vi *vi) + { + struct tegra_channel *chan; +@@ -712,14 +767,30 @@ tegra_channel_queue_setup(struct vb2_queue *vq, + { + struct tegra_channel *chan = vb2_get_drv_priv(vq); + struct tegra_mc_vi *vi = chan->vi; ++ bool const create_bufs = (*nplanes > 0); //Create buffers is used when nplanes is not zero ++ unsigned int buffer_count = *nbuffers; + + *nplanes = 1; + + sizes[0] = chan->format.sizeimage; + alloc_devs[0] = tegra_channel_get_vi_unit(chan); + +- if (vi->fops && vi->fops->vi_setup_queue) +- return vi->fops->vi_setup_queue(chan, nbuffers); ++ if (create_bufs) ++ buffer_count = vq->num_buffers + *nbuffers; ++ ++ if (vi->fops && vi->fops->vi_setup_queue) { ++ int ret = vi->fops->vi_setup_queue(chan, &buffer_count); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (create_bufs) ++ *nbuffers = buffer_count - vq->num_buffers; ++ else ++ *nbuffers = buffer_count; ++ ++ return 0; ++ } + else + return -EINVAL; + } +@@ -784,6 +855,11 @@ static void tegra_channel_buffer_queue(struct vb2_buffer *vb) + struct tegra_channel *chan = vb2_get_drv_priv(vb->vb2_queue); + struct tegra_channel_buffer *buf = to_tegra_channel_buffer(vbuf); + ++ /* Reset flush state, because new buffers ++ * are enqueued ++ */ ++ update_flush_state(chan, FLUSH_NOT_INITIATED); ++ + /* for bypass mode - do nothing */ + if (chan->bypass) + return; +@@ -1022,6 +1098,17 @@ static void tegra_channel_stop_streaming(struct vb2_queue *vq) + queue_init_ts = 0; + } + ++void tegra_channel_buf_finish(struct vb2_buffer *vb) ++{ ++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); ++ ++ if (vbuf->vb2_buf.state == VB2_BUF_STATE_ERROR) ++ { ++ vbuf->flags |= V4L2_BUF_FLAG_INVALID; ++ } ++ ++} ++ + static const struct vb2_ops tegra_channel_queue_qops = { + .queue_setup = tegra_channel_queue_setup, + .buf_prepare = tegra_channel_buffer_prepare, +@@ -1030,6 +1117,7 @@ static const struct vb2_ops tegra_channel_queue_qops = { + .wait_finish = vb2_ops_wait_finish, + .start_streaming = tegra_channel_start_streaming, + .stop_streaming = tegra_channel_stop_streaming, ++ .buf_finish = tegra_channel_buf_finish, + }; + + /* ----------------------------------------------------------------------------- +@@ -1042,11 +1130,11 @@ tegra_channel_querycap(struct file *file, void *fh, struct v4l2_capability *cap) + struct tegra_channel *chan = video_drvdata(file); + int ret = 0; + +- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + +- strlcpy(cap->driver, "tegra-video", sizeof(cap->driver)); ++ strlcpy(cap->driver, "avt_tegra_csi2", sizeof(cap->driver)); + strlcpy(cap->card, chan->video->name, sizeof(cap->card)); + ret = snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u", + dev_name(chan->vi->dev), chan->port[0]); +@@ -1109,9 +1197,25 @@ tegra_channel_enum_frameintervals(struct file *file, void *fh, + ret = v4l2_subdev_call(sd, pad, enum_frame_interval, &cfg, &fie); + + if (!ret) { +- intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; +- intervals->discrete.numerator = fie.interval.numerator; +- intervals->discrete.denominator = fie.interval.denominator; ++ if (fie.type == V4L2_SUBDEV_FRMIVAL_TYPE_DISCRETE) { ++ intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ intervals->discrete = fie.interval; ++ } ++ else if (fie.type == V4L2_SUBDEV_FRMIVAL_TYPE_STEPWISE) { ++ intervals->type = V4L2_FRMIVAL_TYPE_STEPWISE; ++ intervals->stepwise.min = fie.interval; ++ intervals->stepwise.max = fie.max_interval; ++ intervals->stepwise.step = fie.step_interval; ++ } ++ else if (fie.type == V4L2_SUBDEV_FRMIVAL_TYPE_CONTINUOUS) { ++ intervals->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ intervals->stepwise.min = fie.interval; ++ intervals->stepwise.max = fie.max_interval; ++ intervals->stepwise.step.denominator = 1; ++ intervals->stepwise.step.numerator = 1; ++ } ++ ++ + } + + return ret; +@@ -1916,6 +2020,8 @@ int tegra_channel_init_subdevices(struct tegra_channel *chan) + : chan->port[0] + 1; + int len = 0; + ++ update_flush_state(chan, FLUSH_NOT_INITIATED); ++ + /* set_stream of CSI */ + pad = media_entity_remote_pad(&chan->pad); + if (!pad) +@@ -1955,9 +2061,8 @@ int tegra_channel_init_subdevices(struct tegra_channel *chan) + v4l2_set_subdev_hostdata(sd, chan); + sd->grp_id = grp_id; + chan->subdev[num_sd++] = sd; +- /* Add subdev name to this video dev name with vi-output tag*/ +- len = snprintf(chan->video->name, sizeof(chan->video->name), "%s, %s", +- "vi-output", sd->name); ++ /* Add subdev name to this video dev name */ ++ len = snprintf(chan->video->name, sizeof(chan->video->name), "%s", sd->name); + if (len < 0) + return -EINVAL; + +@@ -2042,6 +2147,20 @@ tegra_channel_get_format(struct file *file, void *fh, + { + struct tegra_channel *chan = video_drvdata(file); + struct v4l2_pix_format *pix = &format->fmt.pix; ++ struct v4l2_subdev *sd = chan->subdev_on_csi; ++ struct v4l2_subdev_format fmt = {}; ++ int ret; ++ ++ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ fmt.pad = 0; ++ ++ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); ++ ++ if (ret) ++ return ret; ++ ++ tegra_channel_update_format(chan, fmt.format.width, fmt.format.height, ++ chan->fmtinfo->fourcc, &chan->fmtinfo->bpp, 0); + + *pix = chan->format; + +@@ -2058,8 +2177,18 @@ __tegra_channel_try_format(struct tegra_channel *chan, + struct v4l2_subdev_pad_config cfg = {}; + int ret = 0; + +- /* Use the channel format if pixformat is not supported */ +- vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); ++ /* Use the channel format if pixformat is not supported. ++ * If subdev is tc358743 and pixelformat is UYVY, then search specifically ++ * for UYVY8_1X16 media bus format since there are two UYVY formats ++ * UYVY8_1X16 and UYVY8_2X8 supported in vi5_formats and ++ * tegra_core_get_format_by_fourcc by nature only returns the first match. ++ */ ++ if (strstr(sd->name, "tc358743") != NULL && pix->pixelformat == V4L2_PIX_FMT_UYVY) { ++ vfmt = tegra_core_get_format_by_code(chan, MEDIA_BUS_FMT_UYVY8_1X16, 0); ++ } ++ else { ++ vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); ++ } + if (!vfmt) { + pix->pixelformat = chan->format.pixelformat; + vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); +@@ -2096,6 +2225,21 @@ tegra_channel_try_format(struct file *file, void *fh, + return __tegra_channel_try_format(chan, &format->fmt.pix); + } + ++static void tegra_channel_s_bypass_vi_dt_match(struct tegra_channel *chan, bool bypass) ++{ ++ struct tegra_csi_device *csi = tegra_get_mc_csi(); ++ struct tegra_csi_channel *csi_it; ++ int i = 0; ++ ++ list_for_each_entry(csi_it, &csi->csi_chans, list) { ++ for (i = 0; i < chan->num_subdevs; i++) ++ if (chan->subdev[i] == &csi_it->subdev) ++ csi_it->bypass_dt = bypass; ++ } ++ ++ chan->bypass_dt = bypass; ++} ++ + static int + __tegra_channel_set_format(struct tegra_channel *chan, + struct v4l2_pix_format *pix) +@@ -2106,7 +2250,18 @@ __tegra_channel_set_format(struct tegra_channel *chan, + struct v4l2_subdev_pad_config cfg = {}; + int ret = 0; + +- vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); ++ /* ++ * If subdev is tc358743 and pixelformat is UYVY, then search specifically ++ * for UYVY8_1X16 media bus format since there are two UYVY formats ++ * UYVY8_1X16 and UYVY8_2X8 supported in vi5_formats and ++ * tegra_core_get_format_by_fourcc by nature only returns the first match. ++ */ ++ if (strstr(sd->name, "tc358743") != NULL && pix->pixelformat == V4L2_PIX_FMT_UYVY) { ++ vfmt = tegra_core_get_format_by_code(chan, MEDIA_BUS_FMT_UYVY8_1X16, 0); ++ } ++ else { ++ vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); ++ } + if (!vfmt) + return -EINVAL; + +@@ -2114,6 +2269,12 @@ __tegra_channel_set_format(struct tegra_channel *chan, + fmt.pad = 0; + v4l2_fill_mbus_format(&fmt.format, pix, vfmt->code); + ++ if (chan->format.pixelformat == V4L2_PIX_FMT_CUSTOM) { ++ tegra_channel_s_bypass_vi_dt_match(chan, true); ++ } else { ++ tegra_channel_s_bypass_vi_dt_match(chan, false); ++ } ++ + ret = v4l2_subdev_call(sd, pad, set_fmt, &cfg, &fmt); + if (ret == -ENOIOCTLCMD) + return -ENOTTY; +@@ -2146,6 +2307,11 @@ tegra_channel_set_format(struct file *file, void *fh, + struct tegra_channel *chan = video_drvdata(file); + int ret = 0; + ++ if (format->fmt.pix.pixelformat == V4L2_PIX_FMT_CUSTOM) ++ { ++ chan->prev_format = chan->format; ++ } ++ + /* get the suppod format by try_fmt */ + ret = __tegra_channel_try_format(chan, &format->fmt.pix); + if (ret) +@@ -2154,7 +2320,13 @@ tegra_channel_set_format(struct file *file, void *fh, + if (vb2_is_busy(&chan->queue)) + return -EBUSY; + +- return __tegra_channel_set_format(chan, &format->fmt.pix); ++ ret = __tegra_channel_set_format(chan, &format->fmt.pix); ++ if (ret) ++ return ret; ++ ++ ret = __tegra_channel_set_format(chan, &chan->format); ++ ++ return ret; + } + + static int tegra_channel_subscribe_event(struct v4l2_fh *fh, +@@ -2224,15 +2396,227 @@ static int tegra_channel_log_status(struct file *file, void *priv) + return 0; + } + ++int tegra_channel_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct tegra_channel *chan = video_drvdata(file); ++ int ret = 0; ++ ++ ret = vb2_ioctl_dqbuf(file, priv, p); ++ ++ if (chan->incomplete_flag) { ++ p->flags |= V4L2_BUF_FLAG_INCOMPLETE; ++ chan->incomplete_flag = false; ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ p->flags |= V4L2_BUF_FLAG_VALID; ++ chan->dqbuf_count++; ++ ++ return 0; ++} ++ + static long tegra_channel_default_ioctl(struct file *file, void *fh, + bool use_prio, unsigned int cmd, void *arg) + { + struct tegra_channel *chan = video_drvdata(file); +- struct tegra_mc_vi *vi = chan->vi; +- long ret = 0; ++ struct v4l2_subdev *sd_on_csi = chan->subdev_on_csi; ++ struct video_device *vdev = chan->video; ++ long ret = -ENOTTY; ++ ++ switch(cmd) ++ { ++ case VIDIOC_MEM_ALLOC: { ++ struct v4l2_dma_mem *mem = arg; ++ int ret = 0; ++ int count = 1; ++ int plane_size[VIDEO_MAX_PLANES] = { chan->format.sizeimage }; ++ ++ if (chan->queue.owner && chan->queue.owner != file->private_data) ++ return -EBUSY; ++ ++ ret = vb2_core_create_single_buf(&chan->queue, mem->memory, &count, 1, plane_size, true, mem->index); ++ chan->queue.owner = file->private_data; ++ chan->created_bufs++; ++ ++ if (ret < 0) ++ return ret; ++ return 0; ++ ++ break; ++ } ++ ++ case VIDIOC_MEM_FREE: { ++ struct v4l2_dma_mem *mem = arg; ++ int ret = 0; ++ ++ if (chan->queue.owner && chan->queue.owner != file->private_data) ++ return -EBUSY; ++ ++ ret = vb2_buffer_free(&chan->queue, mem->index); ++ if (ret < 0) ++ return ret; ++ chan->created_bufs = 0; ++ return 0; ++ break; ++ } + +- if (vi->fops && vi->fops->vi_default_ioctl) +- ret = vi->fops->vi_default_ioctl(file, fh, use_prio, cmd, arg); ++ case VIDIOC_FLUSH_FRAMES: { ++ int i; ++ struct vb2_queue *q = &chan->queue; ++ if (chan->created_bufs > 0) ++ { ++ for (i = 0; i < q->num_buffers; ++i) ++ { ++ switch (q->bufs[i]->state) { ++ case VB2_BUF_STATE_QUEUED: ++ case VB2_BUF_STATE_ACTIVE: ++ to_vb2_v4l2_buffer(q->bufs[i])->flags |= V4L2_BUF_FLAG_UNUSED; ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ update_flush_state(chan, FLUSH_IN_PROGRESS); ++ vb2_core_queue_cancel(q); ++ update_flush_state(chan, FLUSH_DONE); ++ sysfs_notify(&vdev->dev.kobj, NULL, "flush"); ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_STREAMSTAT: { ++ struct v4l2_stats_t *stream_stats = arg; ++ chan->stream_stats.current_frame_count = 1; ++ *stream_stats = chan->stream_stats; ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_RESET_STREAMSTAT: ++ memset(&chan->stream_stats, 0x00, sizeof(struct v4l2_stats_t)); ++ chan->qbuf_count = 0; ++ chan->dqbuf_count = 0; ++ return 0; ++ break; ++ ++ case VIDIOC_STREAMON_EX: { ++ int ret = 0; ++ ++ ret = vb2_core_streamon(&chan->queue, chan->queue.type); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_STREAMOFF_EX: { ++ struct v4l2_streamoff_ex *streamoff = arg; ++ int ret = 0; ++ unsigned long curr_timeout = chan->timeout; ++ ++ chan->timeout = msecs_to_jiffies(streamoff->timeout); ++ ++ ret = vb2_core_streamoff(&chan->queue, chan->queue.type); ++ sysfs_notify(&vdev->dev.kobj, NULL, "streamoff"); ++ ++ /* Get back to the default timeout value */ ++ chan->timeout = curr_timeout; ++ /* Reset current values in order to reset the displayed current frame reate after stop*/ ++ chan->stream_stats.current_frame_count = 0; ++ chan->stream_stats.current_frame_interval = 0; ++ ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_G_STATISTICS_CAPABILITIES: { ++ struct v4l2_statistics_capabilities *statistics_capabilities = arg; ++ statistics_capabilities->statistics_capability = V4L2_STATISTICS_CAPABILITY_FrameCount | ++ V4L2_STATISTICS_CAPABILITY_FramesIncomplete | ++ V4L2_STATISTICS_CAPABILITY_PacketCRCError | ++ V4L2_STATISTICS_CAPABILITY_CurrentFrameInterval | ++ V4L2_STATISTICS_CAPABILITY_FramesUnderrun; ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_G_MIN_ANNOUNCED_FRAMES: { ++ struct v4l2_min_announced_frames *min_announced_frames = arg; ++ min_announced_frames->min_announced_frames = MIN_ANNOUNCED_FRAMES; ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_G_SUPPORTED_LANE_COUNTS: { ++ ++ struct v4l2_supported_lane_counts *lane_counts = arg; ++ ++ lane_counts->supported_lane_counts = ++ V4L2_LANE_COUNT_1_LaneSupport | ++ V4L2_LANE_COUNT_2_LaneSupport | ++ V4L2_LANE_COUNT_4_LaneSupport; ++ ++ return 0; ++ } ++ ++ case VIDIOC_G_CSI_HOST_CLK_FREQ: { ++ struct v4l2_csi_host_clock_freq_ranges *csi_clk_ranges = arg; ++ ++ csi_clk_ranges->lane_range_1.is_valid = ++ csi_clk_ranges->lane_range_2.is_valid = ++ csi_clk_ranges->lane_range_4.is_valid = 1; ++ csi_clk_ranges->lane_range_1.min = ++ csi_clk_ranges->lane_range_2.min = ++ csi_clk_ranges->lane_range_4.min = ++ CSI_HOST_CLK_MIN_FREQ; ++ csi_clk_ranges->lane_range_1.max = ++ csi_clk_ranges->lane_range_2.max = ++ csi_clk_ranges->lane_range_4.max = ++ CSI_HOST_CLK_MAX_FREQ; ++ ++ csi_clk_ranges->lane_range_3.is_valid = 0; ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_G_IPU_RESTRICTIONS: { ++ struct v4l2_ipu_restrictions *ipu_restrictions = arg; ++ ++ ipu_restrictions->ipu_x.is_valid = 1; ++ ipu_restrictions->ipu_x.min = FRAMESIZE_MIN_W; ++ ipu_restrictions->ipu_x.max = FRAMESIZE_MAX_W; ++ ipu_restrictions->ipu_x.inc = FRAMESIZE_INC_W; ++ ipu_restrictions->ipu_y.is_valid = 1; ++ ipu_restrictions->ipu_y.min = FRAMESIZE_MIN_H; ++ ipu_restrictions->ipu_y.max = FRAMESIZE_MAX_H; ++ ipu_restrictions->ipu_y.inc = FRAMESIZE_INC_H; ++ return 0; ++ break; ++ } ++ ++ case VIDIOC_G_SUPPORTED_DATA_IDENTIFIERS: { ++ struct v4l2_csi_data_identifiers_inq *data_ids = arg; ++ ++ data_ids->data_identifiers_inq_1 = DATA_IDENTIFIER_INQ_1; ++ data_ids->data_identifiers_inq_2 = DATA_IDENTIFIER_INQ_2; ++ data_ids->data_identifiers_inq_3 = DATA_IDENTIFIER_INQ_3; ++ data_ids->data_identifiers_inq_4 = DATA_IDENTIFIER_INQ_4; ++ return 0; ++ break; ++ } ++ ++ default: ++ if (v4l2_subdev_has_op(sd_on_csi, core, ioctl)) { ++ ret = v4l2_subdev_call(sd_on_csi, core, ioctl, cmd, arg); ++ } ++ break; ++ } + + return ret; + } +@@ -2262,6 +2646,150 @@ static long tegra_channel_compat_ioctl(struct file *filp, + #endif + #endif + ++static int tegra_channel_vidioc_g_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *parm) ++{ ++ struct tegra_channel *chan = video_drvdata(file); ++ struct v4l2_subdev *sd = chan->subdev_on_csi; ++ ++ parm->parm.capture.readbuffers = 0; ++ ++ if (v4l2_subdev_has_op(sd, video, g_frame_interval)) { ++ struct v4l2_subdev_frame_interval frame_interval = {0}; ++ int err = 0; ++ ++ err = v4l2_subdev_call(sd, video, g_frame_interval, &frame_interval); ++ ++ if (err == -ENOTTY) { ++ return 0; ++ } ++ else if (err) { ++ return err; ++ } ++ ++ parm->parm.capture.timeperframe = frame_interval.interval; ++ ++ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ } ++ ++ return 0; ++} ++ ++static int tegra_channel_vidioc_s_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *parm) ++{ ++ struct tegra_channel *chan = video_drvdata(file); ++ struct v4l2_subdev *sd = chan->subdev_on_csi; ++ ++ parm->parm.capture.readbuffers = 0; ++ ++ if (v4l2_subdev_has_op(sd, video, s_frame_interval)) { ++ struct v4l2_subdev_frame_interval frame_interval = {0}; ++ int err = 0; ++ ++ frame_interval.interval = parm->parm.capture.timeperframe; ++ ++ err = v4l2_subdev_call(sd, video, s_frame_interval, &frame_interval); ++ ++ if (err == -ENOTTY) { ++ return 0; ++ } ++ else if (err) { ++ return err; ++ } ++ ++ parm->parm.capture.timeperframe = frame_interval.interval; ++ ++ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ } ++ ++ return 0; ++} ++ ++int tegra_channel_create_bufs(struct file *file, void *priv, ++ struct v4l2_create_buffers *p) ++{ ++ int ret; ++ struct v4l2_format format = p->format; ++ ++ ++ ret = tegra_channel_try_format(file,priv,&format); ++ if (ret < 0) ++ return ret; ++ ++ if (format.fmt.pix.width != p->format.fmt.pix.width) ++ return -EINVAL; ++ ++ if (format.fmt.pix.height != p->format.fmt.pix.height) ++ return -EINVAL; ++ ++ if (format.fmt.pix.bytesperline > p->format.fmt.pix.bytesperline) ++ return -EINVAL; ++ ++ if (format.fmt.pix.sizeimage > p->format.fmt.pix.sizeimage) ++ return -EINVAL; ++ ++ return vb2_ioctl_create_bufs(file,priv,p); ++} ++ ++static int tegra_channel_vidioc_g_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct tegra_channel *chan = video_drvdata(file); ++ struct v4l2_subdev *sd = chan->subdev_on_csi; ++ struct v4l2_subdev_selection ss = {0}; ++ int retval; ++ ++ if (!v4l2_subdev_has_op(sd, pad, get_selection)) ++ return -ENOTTY; ++ ++ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ ++ ss.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ss.pad = 0; ++ ss.target = s->target; ++ ss.flags = s->flags; ++ memcpy(&ss.r, &s->r, sizeof(struct v4l2_rect)); ++ ++ retval = v4l2_subdev_call(sd, pad, get_selection, NULL, &ss); ++ ++ s->target = ss.target; ++ s->flags = ss.flags; ++ memcpy(&s->r, &ss.r, sizeof(struct v4l2_rect)); ++ ++ return retval; ++} ++ ++static int tegra_channel_vidioc_s_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct tegra_channel *chan = video_drvdata(file); ++ struct v4l2_subdev *sd = chan->subdev_on_csi; ++ struct v4l2_subdev_selection ss; ++ struct v4l2_format format; ++ int retval; ++ ++ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if (!v4l2_subdev_has_op(sd, pad, set_selection)) ++ return -ENOTTY; ++ ++ ss.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ss.pad = 0; ++ ss.target = s->target; ++ ss.flags = s->flags; ++ memcpy(&ss.r, &s->r, sizeof(struct v4l2_rect)); ++ ++ retval = v4l2_subdev_call(sd, pad, set_selection, NULL, &ss); ++ ++ (void) tegra_channel_get_format(file, fh, &format); ++ ++ return retval; ++} ++ + static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { + .vidioc_querycap = tegra_channel_querycap, + .vidioc_enum_framesizes = tegra_channel_enum_framesizes, +@@ -2274,8 +2802,8 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, +- .vidioc_dqbuf = vb2_ioctl_dqbuf, +- .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_dqbuf = tegra_channel_ioctl_dqbuf, ++ .vidioc_create_bufs = tegra_channel_create_bufs, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +@@ -2293,6 +2821,11 @@ static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { + .vidioc_s_input = tegra_channel_s_input, + .vidioc_log_status = tegra_channel_log_status, + .vidioc_default = tegra_channel_default_ioctl, ++ .vidioc_g_parm = tegra_channel_vidioc_g_parm, ++ .vidioc_s_parm = tegra_channel_vidioc_s_parm, ++ .vidioc_s_selection = tegra_channel_vidioc_s_selection, ++ .vidioc_g_selection = tegra_channel_vidioc_g_selection, ++ + }; + + static int tegra_channel_close(struct file *fp); +@@ -2304,9 +2837,13 @@ static int tegra_channel_open(struct file *fp) + struct tegra_mc_vi *vi; + struct tegra_csi_device *csi; + ++ if (chan->avt_cam_mode && atomic_read(&chan->open_count) > 0) ++ return -EBUSY; ++ + trace_tegra_channel_open(vdev->name); + mutex_lock(&chan->video_lock); + ret = v4l2_fh_open(fp); ++ + if (ret || !v4l2_fh_is_singular_file(fp)) { + mutex_unlock(&chan->video_lock); + return ret; +@@ -2328,6 +2865,7 @@ static int tegra_channel_open(struct file *fp) + return ret; + } + ++ atomic_inc(&chan->open_count); + + mutex_unlock(&chan->video_lock); + return 0; +@@ -2344,16 +2882,25 @@ static int tegra_channel_close(struct file *fp) + struct video_device *vdev = video_devdata(fp); + struct tegra_channel *chan = video_drvdata(fp); + struct tegra_mc_vi *vi = chan->vi; ++ struct v4l2_subdev *sd = chan->subdev_on_csi; + bool is_singular; ++ int was_streaming = atomic_read(&chan->is_streaming); ++ bool was_owner = chan->queue.owner == fp->private_data; + + trace_tegra_channel_close(vdev->name); + mutex_lock(&chan->video_lock); + is_singular = v4l2_fh_is_singular_file(fp); + ret = _vb2_fop_release(fp, NULL); + ++ if (was_owner && was_streaming && chan->avt_cam_mode) ++ { ++ dev_warn(vi->dev, "Called close while streaming in avt_cam_mode\n"); ++ dev_warn(vi->dev, "Resetting device!\n"); ++ v4l2_subdev_call(sd,core,reset,0); ++ } ++ + if (!is_singular) { +- mutex_unlock(&chan->video_lock); +- return ret; ++ goto exit; + } + + if (tegra_channel_verify_focuser(chan)) { +@@ -2362,6 +2909,17 @@ static int tegra_channel_close(struct file *fp) + dev_err(vi->dev, "Failed to power off subdevices\n"); + } + ++ if (chan->format.pixelformat == V4L2_PIX_FMT_CUSTOM) ++ { ++ struct v4l2_format format; ++ ++ dev_dbg(vi->dev,"Restore pixelformat"); ++ format.fmt.pix = chan->prev_format; ++ tegra_channel_set_format(fp,NULL,&format); ++ } ++ ++exit: ++ atomic_dec(&chan->open_count); + mutex_unlock(&chan->video_lock); + return ret; + } +@@ -2474,7 +3032,7 @@ int tegra_channel_init_video(struct tegra_channel *chan) + chan->video->vfl_type = VFL_TYPE_GRABBER; + #else + chan->video->vfl_type = VFL_TYPE_VIDEO; +- chan->video->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ chan->video->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + chan->video->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; + #endif + chan->video->vfl_dir = VFL_DIR_RX; +@@ -2569,6 +3127,7 @@ int tegra_channel_init(struct tegra_channel *chan) + #endif + chan->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC + | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; ++ chan->queue.min_buffers_needed = 1; + ret = vb2_queue_init(&chan->queue); + if (ret < 0) { + dev_err(chan->vi->dev, "failed to initialize VB2 queue\n"); +@@ -2582,6 +3141,10 @@ int tegra_channel_init(struct tegra_channel *chan) + goto deskew_ctx_err; + } + ++ chan->incomplete_flag = false; ++ chan->timeout = msecs_to_jiffies(CAPTURE_TIMEOUT_MS); ++ chan->created_bufs = 0; ++ + chan->init_done = true; + + return 0; +diff --git a/nvidia/drivers/media/platform/tegra/camera/vi/graph.c b/nvidia/drivers/media/platform/tegra/camera/vi/graph.c +index cb097442d6b1..1c0f3a163ee3 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/vi/graph.c ++++ b/nvidia/drivers/media/platform/tegra/camera/vi/graph.c +@@ -416,6 +416,22 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) + goto graph_error; + } + ++ if (chan->subdev_on_csi->flags & V4L2_SUBDEV_FL_IS_I2C) { ++ struct i2c_client *client = v4l2_get_subdevdata(chan->subdev_on_csi); ++ /* libcsi library requires this format, ++ * hence doubled adapter number ++ */ ++ sprintf(chan->video->bus_info, "%d:%d:%x", ++ client->adapter->nr, ++ client->adapter->nr, ++ client->addr); ++ } ++ ++ /* libcsi library requires interface with number 1, ++ * this may be subject to change in future ++ */ ++ sprintf(chan->video->if_name, "avt_csi2_if%d", chan->port[0] + 1); ++ + chan->link_status++; + + return 0; +diff --git a/nvidia/drivers/media/platform/tegra/camera/vi/vi5_fops.c b/nvidia/drivers/media/platform/tegra/camera/vi/vi5_fops.c +index b54833d3f887..995736dbfa5a 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/vi/vi5_fops.c ++++ b/nvidia/drivers/media/platform/tegra/camera/vi/vi5_fops.c +@@ -39,8 +39,6 @@ + #define VI_CHANNEL_DEV "/dev/capture-vi-channel" + #define VI_CHAN_PATH_MAX 40 + +-#define CAPTURE_TIMEOUT_MS 2500 +- + static const struct vi_capture_setup default_setup = { + .channel_flags = 0 + | CAPTURE_CHANNEL_FLAG_VIDEO +@@ -210,10 +208,10 @@ static const struct v4l2_ctrl_config vi5_custom_ctrls[] = { + + static int vi5_add_ctrls(struct tegra_channel *chan) + { +- int i; ++ //int i; + + /* Add vi5 custom controls */ +- for (i = 0; i < ARRAY_SIZE(vi5_custom_ctrls); i++) { ++ /*for (i = 0; i < ARRAY_SIZE(vi5_custom_ctrls); i++) { + v4l2_ctrl_new_custom(&chan->ctrl_handler, + &vi5_custom_ctrls[i], NULL); + if (chan->ctrl_handler.error) { +@@ -222,7 +220,7 @@ static int vi5_add_ctrls(struct tegra_channel *chan) + vi5_custom_ctrls[i].name); + return chan->ctrl_handler.error; + } +- } ++ }*/ + + return 0; + } +@@ -288,6 +286,22 @@ static int vi5_channel_setup_queue(struct tegra_channel *chan, + done: + return ret; + } ++static void vi5_bypass_datatype(struct tegra_channel *chan, ++ struct capture_descriptor *desc) ++{ ++ u32 data_type = chan->fmtinfo->img_dt; ++ ++ if(chan->bypass_dt) { ++ desc->ch_cfg.match.datatype = 0x0; ++ desc->ch_cfg.match.datatype_mask = 0x0; ++ desc->ch_cfg.dt_enable = 1; ++ desc->ch_cfg.dt_override = data_type; ++ } else { ++ desc->ch_cfg.match.datatype = data_type; ++ desc->ch_cfg.match.datatype_mask = 0x3f; ++ desc->ch_cfg.dt_enable = 0; ++ } ++} + + static struct tegra_csi_channel *find_linked_csi_channel( + struct tegra_channel *chan) +@@ -375,7 +389,7 @@ static void vi5_setup_surface(struct tegra_channel *chan, + u32 width = chan->format.width; + u32 format = chan->fmtinfo->img_fmt; + u32 bpl = chan->format.bytesperline; +- u32 data_type = chan->fmtinfo->img_dt; ++ + u32 nvcsi_stream = chan->port[vi_port]; + struct capture_descriptor_memoryinfo *desc_memoryinfo = + &chan->tegra_vi_channel[vi_port]-> +@@ -396,11 +410,11 @@ static void vi5_setup_surface(struct tegra_channel *chan, + desc->ch_cfg.match.vc = (1u << chan->virtual_channel); /* one-hot bit encoding */ + desc->ch_cfg.frame.frame_x = width; + desc->ch_cfg.frame.frame_y = height; +- desc->ch_cfg.match.datatype = data_type; +- desc->ch_cfg.match.datatype_mask = 0x3f; + desc->ch_cfg.pixfmt_enable = 1; + desc->ch_cfg.pixfmt.format = format; + ++ vi5_bypass_datatype(chan, desc); ++ + desc_memoryinfo->surface[0].base_address = offset; + desc_memoryinfo->surface[0].size = chan->format.bytesperline * height; + desc->ch_cfg.atomp.surface_stride[0] = bpl; +@@ -508,12 +522,12 @@ static void vi5_capture_dequeue(struct tegra_channel *chan, + goto rel_buf; + + /* Dequeue a frame and check its capture status */ +- err = vi_capture_status(chan->tegra_vi_channel[vi_port], CAPTURE_TIMEOUT_MS); ++ err = vi_capture_status(chan->tegra_vi_channel[vi_port], jiffies_to_msecs(chan->timeout)); + if (err) { + if (err == -ETIMEDOUT) { + dev_err(vi->dev, + "uncorr_err: request timed out after %d ms\n", +- CAPTURE_TIMEOUT_MS); ++ jiffies_to_msecs(chan->timeout)); + } else { + dev_err(vi->dev, "uncorr_err: request err %d\n", err); + } +@@ -591,6 +605,7 @@ static void vi5_capture_dequeue(struct tegra_channel *chan, + buf->vb2_state = VB2_BUF_STATE_ERROR; + + rel_buf: ++ tegra_channel_update_statistics(chan); + vi5_release_buffer(chan, buf); + } + +@@ -972,8 +987,13 @@ static int vi5_channel_stop_streaming(struct vb2_queue *vq) + struct tegra_channel *chan = vb2_get_drv_priv(vq); + long err; + int vi_port = 0; +- if (!chan->bypass) ++ ++ if (!chan->bypass) { ++ for (vi_port = 0; vi_port < chan->valid_ports; vi_port++) { ++ vi_stop_waiting(chan->tegra_vi_channel[vi_port]); ++ } + vi5_channel_stop_kthreads(chan); ++ } + + /* csi stream/sensor(s) devices to be closed before vi channel */ + tegra_channel_set_stream(chan, false); +@@ -994,6 +1014,9 @@ static int vi5_channel_stop_streaming(struct vb2_queue *vq) + + /* release all remaining buffers to v4l2 */ + tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR, false); ++ ++ //Set requests enqueued to zero, because vi_stop_waiting canceled all pending requests ++ chan->capture_reqs_enqueued = 0; + } + + return 0; +diff --git a/nvidia/drivers/media/platform/tegra/camera/vi/vi5_formats.h b/nvidia/drivers/media/platform/tegra/camera/vi/vi5_formats.h +index 51cbbad5ba25..5693a46d0fbc 100644 +--- a/nvidia/drivers/media/platform/tegra/camera/vi/vi5_formats.h ++++ b/nvidia/drivers/media/platform/tegra/camera/vi/vi5_formats.h +@@ -82,11 +82,17 @@ enum tegra_image_format { + }; + + static const struct tegra_video_format vi5_video_formats[] = { ++ /* CUSTOM MIPI DATATYPE */ ++ TEGRA_VIDEO_FORMAT(RAW8, 8, CUSTOM, 1, 1, T_R8, ++ RAW8, CUSTOM, "0x31 MIPI DATATYPE"), ++ + /* RAW 6: TODO */ + + /* RAW 7: TODO */ + + /* RAW 8 */ ++ TEGRA_VIDEO_FORMAT(RAW8, 8, Y8_1X8, 1, 1, T_R8, ++ RAW8, GREY, "GREY 8"), + TEGRA_VIDEO_FORMAT(RAW8, 8, SRGGB8_1X8, 1, 1, T_R8, + RAW8, SRGGB8, "RGRG.. GBGB.."), + TEGRA_VIDEO_FORMAT(RAW8, 8, SGRBG8_1X8, 1, 1, T_R8, +@@ -97,32 +103,57 @@ static const struct tegra_video_format vi5_video_formats[] = { + RAW8, SBGGR8, "BGBG.. GRGR.."), + + /* RAW 10 */ +- TEGRA_VIDEO_FORMAT(RAW10, 10, SRGGB10_1X10, 2, 1, T_R16, +- RAW10, SRGGB10, "RGRG.. GBGB.."), +- TEGRA_VIDEO_FORMAT(RAW10, 10, SGRBG10_1X10, 2, 1, T_R16, +- RAW10, SGRBG10, "GRGR.. BGBG.."), +- TEGRA_VIDEO_FORMAT(RAW10, 10, SGBRG10_1X10, 2, 1, T_R16, +- RAW10, SGBRG10, "GBGB.. RGRG.."), +- TEGRA_VIDEO_FORMAT(RAW10, 10, SBGGR10_1X10, 2, 1, T_R16, +- RAW10, SBGGR10, "BGBG.. GRGR.."), ++ TEGRA_VIDEO_FORMAT(RAW10, 10, Y10_1X10, 2, 1, T_R16_I, ++ RAW10, XAVIER_Y10, "GREY 10"), ++ TEGRA_VIDEO_FORMAT(RAW10, 10, SRGGB10_1X10, 2, 1, T_R16_I, ++ RAW10, XAVIER_SRGGB10, "RGRG.. GBGB.."), ++ TEGRA_VIDEO_FORMAT(RAW10, 10, SGRBG10_1X10, 2, 1, T_R16_I, ++ RAW10, XAVIER_SGRBG10, "GRGR.. BGBG.."), ++ TEGRA_VIDEO_FORMAT(RAW10, 10, SGBRG10_1X10, 2, 1, T_R16_I, ++ RAW10, XAVIER_SGBRG10, "GBGB.. RGRG.."), ++ TEGRA_VIDEO_FORMAT(RAW10, 10, SBGGR10_1X10, 2, 1, T_R16_I, ++ RAW10, XAVIER_SBGGR10, "BGBG.. GRGR.."), + + /* RAW 12 */ +- TEGRA_VIDEO_FORMAT(RAW12, 12, SRGGB12_1X12, 2, 1, T_R16, +- RAW12, SRGGB12, "RGRG.. GBGB.."), +- TEGRA_VIDEO_FORMAT(RAW12, 12, SGRBG12_1X12, 2, 1, T_R16, +- RAW12, SGRBG12, "GRGR.. BGBG.."), +- TEGRA_VIDEO_FORMAT(RAW12, 12, SGBRG12_1X12, 2, 1, T_R16, +- RAW12, SGBRG12, "GBGB.. RGRG.."), +- TEGRA_VIDEO_FORMAT(RAW12, 12, SBGGR12_1X12, 2, 1, T_R16, +- RAW12, SBGGR12, "BGBG.. GRGR.."), ++ TEGRA_VIDEO_FORMAT(RAW12, 12, Y12_1X12, 2, 1, T_R16_I, ++ RAW12, XAVIER_Y12, "GREY 12"), ++ TEGRA_VIDEO_FORMAT(RAW12, 12, SRGGB12_1X12, 2, 1, T_R16_I, ++ RAW12, XAVIER_SRGGB12, "RGRG.. GBGB.."), ++ TEGRA_VIDEO_FORMAT(RAW12, 12, SGRBG12_1X12, 2, 1, T_R16_I, ++ RAW12, XAVIER_SGRBG12, "GRGR.. BGBG.."), ++ TEGRA_VIDEO_FORMAT(RAW12, 12, SGBRG12_1X12, 2, 1, T_R16_I, ++ RAW12, XAVIER_SGBRG12, "GBGB.. RGRG.."), ++ TEGRA_VIDEO_FORMAT(RAW12, 12, SBGGR12_1X12, 2, 1, T_R16_I, ++ RAW12, XAVIER_SBGGR12, "BGBG.. GRGR.."), ++ ++ /* RGB444 */ ++ TEGRA_VIDEO_FORMAT(RGB444, 16, RGB444_1X12, 2, 1, T_A4B4G4R4, ++ RGB444, ARGB444, "RGB-4-4-4"), ++ ++ /* RGB565 */ ++ TEGRA_VIDEO_FORMAT(RGB565, 16, RGB565_1X16, 2, 1, T_B5G6R5, ++ RGB565, RGB565, "RGB-5-6-5"), + + /* RGB888 */ +- TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X24, 4, 1, T_A8R8G8B8, +- RGB888, ABGR32, "BGRA-8-8-8-8"), +- TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X32_PADHI, 4, 1, T_A8B8G8R8, +- RGB888, RGB32, "RGB-8-8-8-8"), ++ TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X24, 4, 1, T_B8G8R8A8, ++ RGB888, XRGB32, "RGB-8-8-8"), ++ TEGRA_VIDEO_FORMAT(RGB888, 24, BGR888_1X24, 4, 1, T_A8R8G8B8, ++ RGB888, XBGR32, "BGR-8-8-8"), ++ ++ /* RGB666 */ ++ TEGRA_VIDEO_FORMAT(RGB666, 24, RGB666_1X18, 4, 1, T_A8B8G8R8, ++ RGB666, ABGR32, "BGRA-8-8-8-8"), ++ + + /* YUV422 */ ++ TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8, ++ YUV422_8, UYVY, "YUV 4:2:2 UYVY"), ++ TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_2X8, 2, 1, T_V8_Y8__U8_Y8, ++ YUV422_8, VYUY, "YUV 4:2:2 VYUY"), ++ TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_2X8, 2, 1, T_Y8_U8__Y8_V8, ++ YUV422_8, YUYV, "YUV 4:2:2 YUYV"), ++ TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_2X8, 2, 1, T_Y8_V8__Y8_U8, ++ YUV422_8, YVYU, "YUV 4:2:2 YVYU"), + TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 2, 1, T_U8_Y8__V8_Y8, + YUV422_8, UYVY, "YUV 4:2:2"), + TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_1X16, 2, 1, T_V8_Y8__U8_Y8, +@@ -133,14 +164,6 @@ static const struct tegra_video_format vi5_video_formats[] = { + YUV422_8, YVYU, "YUV 4:2:2"), + TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 1, 1, T_Y8__V8U8_N422, + YUV422_8, NV16, "NV16"), +- TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8, +- YUV422_8, UYVY, "YUV 4:2:2 UYVY"), +- TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_2X8, 2, 1, T_V8_Y8__U8_Y8, +- YUV422_8, VYUY, "YUV 4:2:2 VYUY"), +- TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_2X8, 2, 1, T_Y8_U8__Y8_V8, +- YUV422_8, YUYV, "YUV 4:2:2 YUYV"), +- TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_2X8, 2, 1, T_Y8_V8__Y8_U8, +- YUV422_8, YVYU, "YUV 4:2:2 YVYU"), + }; + + #endif +diff --git a/nvidia/include/media/csi.h b/nvidia/include/media/csi.h +index c70087371381..5c99d08f15e2 100644 +--- a/nvidia/include/media/csi.h ++++ b/nvidia/include/media/csi.h +@@ -124,6 +124,8 @@ struct tegra_csi_channel { + atomic_t is_streaming; + + struct device_node *of_node; ++ ++ bool bypass_dt; + }; + + static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev) +diff --git a/nvidia/include/media/fusa-capture/capture-vi.h b/nvidia/include/media/fusa-capture/capture-vi.h +index 571d2a5b24c8..3d033ad3367a 100644 +--- a/nvidia/include/media/fusa-capture/capture-vi.h ++++ b/nvidia/include/media/fusa-capture/capture-vi.h +@@ -471,4 +471,6 @@ int vi_capture_set_progress_status_notifier( + struct tegra_vi_channel *chan, + struct vi_capture_progress_status_req *req); + ++int vi_stop_waiting(struct tegra_vi_channel *chan); ++ + #endif /* __FUSA_CAPTURE_VI_H__ */ +diff --git a/nvidia/include/media/mc_common.h b/nvidia/include/media/mc_common.h +index 18328db1d23a..0551c31423ea 100644 +--- a/nvidia/include/media/mc_common.h ++++ b/nvidia/include/media/mc_common.h +@@ -36,6 +36,10 @@ + #include + #include + ++#include ++ ++#define CAPTURE_TIMEOUT_MS 12000 ++ + #define MAX_FORMAT_NUM 64 + #define MAX_SUBDEVICES 4 + #define QUEUED_BUFFERS 4 +@@ -269,6 +273,23 @@ struct tegra_channel { + dma_addr_t emb_buf; + void *emb_buf_addr; + unsigned int emb_buf_size; ++ ++ ++ struct v4l2_stats_t stream_stats; ++ uint64_t qbuf_count; ++ uint64_t dqbuf_count; ++ bool incomplete_flag; ++ ++ bool trigger_mode; ++ bool pending_trigger; ++ uint64_t start_frame_jiffies; ++ unsigned int avt_cam_mode; ++ int created_bufs; ++ struct v4l2_pix_format prev_format; ++ atomic_t stop_streaming; ++ atomic_t open_count; ++ ++ bool bypass_dt; + }; + + #define to_tegra_channel(vdev) \ +@@ -390,6 +411,7 @@ void tegra_channel_ring_buffer(struct tegra_channel *chan, + #else + struct timespec64 *ts, int state); + #endif ++void tegra_channel_update_statistics(struct tegra_channel *chan); + struct tegra_channel_buffer *dequeue_buffer(struct tegra_channel *chan, + bool requeue); + struct tegra_channel_buffer *dequeue_dequeue_buffer(struct tegra_channel *chan); +diff --git a/scripts/setlocalversion b/scripts/setlocalversion +index bb709eda96cd..aed89c02f985 100755 +--- a/scripts/setlocalversion ++++ b/scripts/setlocalversion +@@ -44,44 +44,15 @@ scm_version() + fi + + # Check for git and a git repo. +- if test -z "$(git rev-parse --show-cdup 2>/dev/null)" && +- head=$(git rev-parse --verify HEAD 2>/dev/null); then +- +- # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore +- # it, because this version is defined in the top level Makefile. +- if [ -z "$(git describe --exact-match 2>/dev/null)" ]; then +- +- # If only the short version is requested, don't bother +- # running further git commands +- if $short; then +- echo "+" +- return +- fi +- # If we are past a tagged commit (like +- # "v2.6.30-rc5-302-g72357d5"), we pretty print it. +- # +- # Ensure the abbreviated sha1 has exactly 12 +- # hex characters, to make the output +- # independent of git version, local +- # core.abbrev settings and/or total number of +- # objects in the current repository - passing +- # --abbrev=12 ensures a minimum of 12, and the +- # awk substr() then picks the 'g' and first 12 +- # hex chars. +- if atag="$(git describe --abbrev=12 2>/dev/null)"; then +- echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),substr($(NF),0,13))}' +- +- # If we don't have a tag at all we print -g{commitish}, +- # again using exactly 12 hex chars. +- else +- head="$(echo $head | cut -c1-12)" +- printf '%s%s' -g $head +- fi +- fi ++ if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then ++ ++ # Regardless whether it is a tagged commit (like "v2.6.30-rc6"), ++ # we will put the commit info in. ++ printf '%s%s' -g $head + + # Is this git on svn? + if git config --get svn-remote.svn.url >/dev/null; then +- printf -- '-svn%s' "$(git svn find-rev $head)" ++ printf -- '-svn%s' "`git svn find-rev $head`" + fi + + # Check for uncommitted changes. +@@ -102,15 +73,15 @@ scm_version() + fi + + # Check for mercurial and a mercurial repo. +- if test -d .hg && hgid=$(hg id 2>/dev/null); then ++ if test -d .hg && hgid=`hg id 2>/dev/null`; then + # Do we have an tagged version? If so, latesttagdistance == 1 +- if [ "$(hg log -r . --template '{latesttagdistance}')" = "1" ]; then +- id=$(hg log -r . --template '{latesttag}') ++ if [ "`hg log -r . --template '{latesttagdistance}'`" == "1" ]; then ++ id=`hg log -r . --template '{latesttag}'` + printf '%s%s' -hg "$id" + else +- tag=$(printf '%s' "$hgid" | cut -d' ' -f2) ++ tag=`printf '%s' "$hgid" | cut -d' ' -f2` + if [ -z "$tag" -o "$tag" = tip ]; then +- id=$(printf '%s' "$hgid" | sed 's/[+ ].*//') ++ id=`printf '%s' "$hgid" | sed 's/[+ ].*//'` + printf '%s%s' -hg "$id" + fi + fi diff --git a/modules/hardware/nvidia-jetson-orin/camera-common.nix b/modules/hardware/nvidia-jetson-orin/camera-common.nix new file mode 100644 index 000000000..0ed91b413 --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/camera-common.nix @@ -0,0 +1,18 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.orin; + in { + options.ghaf.hardware.nvidia.orin.camera = + # Enabling camera support for Orin boards + lib.mkEnableOption "Orin Camera"; + config = lib.mkIf cfg.camera { + + }; + } + + \ No newline at end of file diff --git a/modules/hardware/nvidia-jetson-orin/nx-camera.nix b/modules/hardware/nvidia-jetson-orin/nx-camera.nix new file mode 100644 index 000000000..00b437040 --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/nx-camera.nix @@ -0,0 +1,53 @@ +# Copyright 2022-2023 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + ... +}: let + cfg = config.ghaf.hardware.nvidia.orin.nx; +in { + options.ghaf.hardware.nvidia.orin.nx.camera = + lib.mkEnableOption + "Enabling E-con camera driver for Orin NX "; + config = lib.mkIf cfg.camera { + # Orin NX camera driver + ghaf.hardware.nvidia.orin.camera = true; + + boot.kernelPatches = [ + # { + # name = "nx-camera-dtb-patch"; + # # This patch is for Toshiba HDMI camera dtb + # patch = ./toshiba_t358743_dtb.patch; + # } + # { + # name = "nx-camera-kernel-patch"; + # # This patch is for Toshiba HDMI camera kernel + # patch = ./toshiba_t358743_kernel.patch; + # } + { + name = "nx-camera-patch"; + # This patch is for Toshiba HDMI camera and Alvium CSI2 camera patch + patch = ./alvium_toshiba.patch; + # For Alvium Kernel configuration changes + extraStructuredConfig = with lib.kernel; { + CONFIG_LOCALVERSION_AUTO = yes; + CONFIG_FB_EFI=lib.mkForce unset; + CONFIG_PCI_SERIAL_CH384=lib.mkForce unset; + CONFIG_SENSORS_F75308=lib.mkForce unset; + CONFIG_USB_NET_CDC_MBIM=lib.mkForce unset; + CONFIG_TEGRA23X_OC_EVENT=lib.mkForce unset; + CONFIG_TEGRA19X_OC_EVENT=lib.mkForce unset; + CONFIG_VIDEO_ECAM =lib.mkForce unset; + CONFIG_VIDEO_AVT_CSI2=lib.mkForce unset; + CONFIG_NV_VIDEO_HAWK_OWL=lib.mkForce unset; + CONFIG_HID_SHIELD_REMOTE=lib.mkForce unset; + CONFIG_USB_WDM = module; + CONFIG_TYPEC_FUSB301=lib.mkForce unset; + CONFIG_ISO9660_FS=lib.mkForce unset; + CONFIG_SECURITY_DMESG_RESTRICT=lib.mkForce unset; + }; + } + ]; + }; +} diff --git a/modules/hardware/nvidia-jetson-orin/toshiba_t358743_dtb.patch b/modules/hardware/nvidia-jetson-orin/toshiba_t358743_dtb.patch new file mode 100644 index 000000000..4ba93709c --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/toshiba_t358743_dtb.patch @@ -0,0 +1,209 @@ +diff --git a/nvidia/platform/t23x/p3768/kernel-dts/cvb/tegra234-p3768-tc358743.dtsi b/nvidia/platform/t23x/p3768/kernel-dts/cvb/tegra234-p3768-tc358743.dtsi +new file mode 100644 +index 000000000000..ec4bdf7354cf +--- /dev/null ++++ b/nvidia/platform/t23x/p3768/kernel-dts/cvb/tegra234-p3768-tc358743.dtsi +@@ -0,0 +1,202 @@ ++#include ++#include ++ ++/* camera control gpio definitions */ ++#define TC358743_RST TEGRA234_MAIN_GPIO(AC, 0) /* to be checked */ ++#define TC358743_INT TEGRA234_MAIN_GPIO(P, 1) /* to be checked */ ++ ++/{ ++ gpio@2200000 { ++ camera-control-output-low { ++ gpio-hog; ++ output-low; ++ gpios = ; ++ label = "cam0-rst"; ++ }; ++ camera-control-input { ++ status = "okay"; ++ gpio-hog; ++ gpios = ; ++ input; ++ label = "tc358743-int"; ++ }; ++ }; ++ ++ tegra-capture-vi { ++ num-channels = <1>; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ port@0{ ++ status = "okay"; ++ reg = <0>; ++ tc358743_vi_in0: endpoint { ++ status = "okay"; ++ port-index = <2>; ++ bus-width = <4>; ++ remote-endpoint = <&tc358743_csi_out0>; ++ }; ++ }; ++ }; ++ }; ++ ++ host1x@13e00000 { ++ nvcsi@15a00000 { ++ num-channels = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ channel@0 { ++ status = "okay"; ++ reg = <0>; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ port@0 { ++ status = "okay"; ++ reg = <0>; ++ tc358743_i2c_csi_in0: endpoint@0 { ++ status = "okay"; ++ port-index = <2>; ++ bus-width = <4>; ++ remote-endpoint = <&tc358743_i2c_out0>; ++ }; ++ }; ++ port@1 { ++ status = "okay"; ++ reg = <1>; ++ tc358743_csi_out0: endpoint@1 { ++ status = "okay"; ++ remote-endpoint = <&tc358743_vi_in0>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ i2c@3180000 { /* I2C_PM, "adapter" 6 */ ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ tc358743@0f { ++ status = "okay"; ++ compatible = "tc358743"; ++ reg = <0x0F>; /* shifted by 2 */ ++ devnode = "video2"; ++ refclk_hz = <27000000>; // refclk_hz -> regclk ++ refclk = <27000000>; // refclk_hz -> regclk ++ ++ reset-gpios = <&tegra_main_gpio TC358743_RST GPIO_ACTIVE_HIGH>; ++ // interrupts = <&tegra_main_gpio TC358743_INT GPIO_ACTIVE_LOW>; ++ ++ #clock-cells = <0>; ++ clocks = <&bpmp_clks TEGRA234_CLK_EXTPERIPH1>; ++ clock-names = "extperiph1"; ++ // clock-names = "refclk"; ++ clock-frequency = <27000000>; ++ mclk = "extperiph1"; ++ // mclk = "27000000"; ++ ++ // mclk_khz ="27000"; ++ // dpcm_enable = "false"; ++ // mclk_multiplier = "1.35"; ++ ++ // pix_clk_hz = "300000000"; ++ // pixel_clk_hz = "576000000"; ++ ++ /* Physical dimensions of sensor */ ++ physical_w = "4.713"; ++ physical_h = "3.494"; ++ /* Sensor Model */ ++ sensor_model ="tc358743"; ++ use_sensor_mode_id = "true"; ++ ++ num_lanes = "4"; ++ tegra_sinterface = "serial_c"; ++ phy_mode = "DPHY"; ++ discontinuous_clk = "no"; ++ ++ ddc5v_delay = <2>; ++ enable_hdcp = "false"; ++ lineinitcnt = <0xe80>; ++ lptxtimecnt = <0x003>; ++ tclk_headercnt = <0x1403>; ++ tclk_trailcnt = <0x00>; ++ ths_headercnt = <0x0103>; ++ twakeup = <0x4882>; ++ tclk_postcnt = <0x008>; ++ ths_trailcnt = <0x02>; ++ hstxvregcnt = <0>; ++ ++ active_h = "720"; ++ active_w = "1280"; ++ default_framerate = "50000000"; /* 50.0 fps */ ++ lane_polarity = "0"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ tc358743_i2c_out0: endpoint { ++ port-index = <2>; ++ bus-width = <4>; ++ phy_mode = "DPHY"; ++ ++ cil_settletime = "30"; ++ ++ data-lanes = <1 2 3 4>; ++ clock-lanes = <0>; ++ link-frequencies = /bits/ 64 <297000000>; // <594000000>; // <297000000>; // <148500000>; ++ remote-endpoint = <&tc358743_i2c_csi_in0>; ++ };//end tc358743_i2c_out0: endpoint ++ };//end port@0 ++ };//end ports ++ };//end tc358743@0f ++ };//end i2c@3180000 ++}; ++ ++/{ ++ tegra-camera-platform { ++ compatible = "nvidia, tegra-camera-platform"; ++ ++ num_csi_lanes = <4>; ++ max_lane_speed = <1500000>; ++ min_bits_per_pixel = <16>; ++ vi_peak_byte_per_pixel = <2>; ++ vi_bw_margin_pct = <25>; ++ max_pixel_rate = <430000>; // <430000>; ++ isp_peak_byte_per_pixel = <5>; ++ isp_bw_margin_pct = <25>; ++ ++ ++ /** ++ * The general guideline for naming badge_info contains 3 parts, and is as follows, ++ * The first part is the camera_board_id for the module; if the module is in a FFD ++ * platform, then use the platform name for this part. ++ * The second part contains the position of the module, ex. rear or front. ++ * The third part contains the last 6 characters of a part number which is found ++ * in the module's specsheet from the vender. ++ */ ++ modules { ++ module0 { ++ status = "okay"; ++ badge = "tc358743_top_i2c2_c"; ++ position = "front"; ++ orientation = "1"; ++ ++ drivernode0 { ++ status = "okay"; ++ /* Declare PCL support driver (classically known as guid) */ ++ pcl_id = "v4l2_sensor"; ++ /* Driver's v4l2 device name */ ++ devname = "tc358743 20-000f"; ++ /* Declare the device-tree hierarchy to driver instance */ ++ proc-device-tree = "/proc/device-tree/i2c@3180000/tc358743@0f"; ++ }; ++ }; ++ }; ++ }; ++}; +\ No newline at end of file diff --git a/modules/hardware/nvidia-jetson-orin/toshiba_t358743_kernel.patch b/modules/hardware/nvidia-jetson-orin/toshiba_t358743_kernel.patch new file mode 100644 index 000000000..71c2eb041 --- /dev/null +++ b/modules/hardware/nvidia-jetson-orin/toshiba_t358743_kernel.patch @@ -0,0 +1,4669 @@ +diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c +index f21da11caf22..e6a01ffb140f 100644 +--- a/drivers/media/i2c/tc358743.c ++++ b/drivers/media/i2c/tc358743.c +@@ -1,42 +1,55 @@ +-// SPDX-License-Identifier: GPL-2.0-only +-/* +- * tc358743 - Toshiba HDMI to CSI-2 bridge +- * +- * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights +- * reserved. +- */ +- + /* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ +- +-#include +-#include +-#include +-#include ++#define DEBUG + #include + #include + #include ++#include ++#include + #include +-#include +-#include ++#include ++#include ++#include ++#include + #include + #include +-#include +-#include +-#include +-#include +-#include ++#include + #include ++#include ++#include + #include +-#include +-#include ++//#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++// #include ++#include ++//#include ++#include + + #include "tc358743_regs.h" ++#include + +-static int debug; ++/* RGB ouput selection */ ++// #define TC358743_VOUT_RGB ++ ++static int debug = 3; + module_param(debug, int, 0644); + MODULE_PARM_DESC(debug, "debug level (0-3)"); + +@@ -46,2188 +59,2329 @@ MODULE_AUTHOR("Mikhail Khelik "); + MODULE_AUTHOR("Mats Randgaard "); + MODULE_LICENSE("GPL"); + +-#define EDID_NUM_BLOCKS_MAX 8 +-#define EDID_BLOCK_SIZE 128 +- +-#define I2C_MAX_XFER_SIZE (EDID_BLOCK_SIZE + 2) ++#define DELAY_ENABLE_INTERRUPT_MS 2000 + +-#define POLL_INTERVAL_CEC_MS 10 +-#define POLL_INTERVAL_MS 1000 ++/* mode */ ++enum { ++ tc358743_MODE_1280X720, ++ tc358743_MODE_1920X1080, ++}; + +-static const struct v4l2_dv_timings_cap tc358743_timings_cap = { +- .type = V4L2_DV_BT_656_1120, +- /* keep this initialization for compatibility with GCC < 4.4.6 */ +- .reserved = { 0 }, +- /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ +- V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 13000000, 165000000, +- V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | +- V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, +- V4L2_DV_BT_CAP_PROGRESSIVE | +- V4L2_DV_BT_CAP_REDUCED_BLANKING | +- V4L2_DV_BT_CAP_CUSTOM) ++/* frame rate */ ++static const int tc358743_30_60fps[] = { ++ 30, ++ 50, ++ 60, + }; + +-struct tc358743_state { +- struct tc358743_platform_data pdata; +- struct v4l2_fwnode_bus_mipi_csi2 bus; +- struct v4l2_subdev sd; +- struct media_pad pad; +- struct v4l2_ctrl_handler hdl; +- struct i2c_client *i2c_client; +- /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ +- struct mutex confctl_mutex; ++/* frame format */ ++static const struct camera_common_frmfmt tc358743_frmfmt[] = { ++ {{1280, 720}, tc358743_30_60fps, 3, 0, tc358743_MODE_1280X720}, ++ // {{1920, 1080}, tc358743_30_60fps, 3, 0, tc358743_MODE_1920X1080}, ++}; + +- /* controls */ +- struct v4l2_ctrl *detect_tx_5v_ctrl; +- struct v4l2_ctrl *audio_sampling_rate_ctrl; +- struct v4l2_ctrl *audio_present_ctrl; ++// static const struct camera_common_colorfmt tc358743_color_fmts[] = { ++// { ++// MEDIA_BUS_FMT_SRGGB12_1X12, ++// V4L2_COLORSPACE_SRGB, ++// V4L2_PIX_FMT_SRGGB12, ++// }, ++// { ++// MEDIA_BUS_FMT_UYVY8_1X16, ++// V4L2_COLORSPACE_SRGB, ++// V4L2_PIX_FMT_UYVY, ++// }, ++// }; + +- struct delayed_work delayed_work_enable_hotplug; ++#define EDID_NUM_BLOCKS_MAX 8 ++#define EDID_BLOCK_SIZE 128 ++static u8 edid[] = { ++ ++ // #ifdef TC358743_VOUT_RGB ++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x50, 0x21, 0x9C, 0x27, ++ 0x00, 0x00, 0x00, 0x00, 0x19, 0x12, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, ++ 0x0E, 0x00, 0xB2, 0xA0, 0x57, 0x49, 0x9B, 0x26, 0x10, 0x48, 0x4F, 0x2F, ++ 0xCF, 0x00, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x81, 0x99, 0x01, 0x01, ++ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, ++ 0x2D, 0x40, 0x58, 0x2C, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, ++ 0x00, 0x00, 0x00, 0xFD, 0x00, 0x31, 0x55, 0x18, 0x5E, 0x11, 0x00, 0x0A, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x54, ++ 0x6F, 0x73, 0x68, 0x69, 0x62, 0x61, 0x2D, 0x48, 0x32, 0x43, 0x0A, 0x20, ++ 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc3, 0x02, 0x03, 0x1a, 0xc0, ++ 0x48, 0xa2, 0x10, 0x04, 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, ++ 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, 0x00, 0x2a, 0x01, 0x1d, ++ 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, ++ 0x10, 0x3e, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0xd7 ++ // #else ++ // 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, ++ // 0x52,0x62,0x88,0x88,0x00,0x88,0x88,0x88, ++ // 0x1C,0x15,0x01,0x03,0x80,0x00,0x00,0x78, ++ // 0x0A,0x0D,0xC9,0xA0,0x57,0x47,0x98,0x27, ++ // 0x12,0x48,0x4C,0x00,0x00,0x00,0x01,0x01, ++ // 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, ++ // 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A, ++ // 0x80,0xD0,0x72,0x38,0x2D,0x40,0x10,0x2C, ++ // 0x45,0x80,0x66,0x4C,0x00,0x00,0x00,0x1E, ++ // 0x01,0x1D,0x00,0xBC,0x52,0xD0,0x1E,0x20, ++ // 0xB8,0x28,0x55,0x40,0x66,0x4C,0x00,0x00, ++ // 0x00,0x1E,0x00,0x00,0x00,0xFC,0x00,0x54, ++ // 0x6F,0x73,0x68,0x69,0x62,0x61,0x2D,0x48, ++ // 0x32,0x43,0x0A,0x20,0x00,0x00,0x00,0xFD, ++ // 0x00,0x14,0x78,0x01,0xFF,0x10,0x00,0x0A, ++ // 0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xBA, ++ // 0x02,0x03,0x1A,0x71,0x47,0x9F,0x13,0x22, ++ // 0x1F,0x02,0x11,0x1F,0x23,0x09,0x07,0x01, ++ // 0x83,0x01,0x00,0x00,0x65,0x03,0x0C,0x00, ++ // 0x10,0x00,0x01,0x1D,0x80,0x18,0x71,0x38, ++ // 0x2D,0x40,0x58,0x2C,0x45,0x00,0x66,0x4C, ++ // 0x00,0x00,0x00,0x1E,0x02,0x3A,0x80,0xD0, ++ // 0x72,0x38,0x2D,0x40,0x10,0x2C,0x45,0x80, ++ // 0x66,0x4C,0x00,0x00,0x00,0x1E,0x8C,0x0A, ++ // 0xD0,0x8A,0x20,0xE0,0x2D,0x10,0x10,0x3E, ++ // 0x96,0x00,0x66,0x4C,0x00,0x00,0x00,0x18, ++ // 0x8C,0x0A,0xD0,0x90,0x20,0x40,0x31,0x20, ++ // 0x0C,0x40,0x55,0x00,0x66,0x4C,0x00,0x00, ++ // 0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, ++ // #endif ++}; ++/* Max transfer size done by I2C transfer functions */ ++#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2) + +- struct timer_list timer; +- struct work_struct work_i2c_poll; ++static const struct v4l2_dv_timings_cap tc358743_timings_cap = { ++ .type = V4L2_DV_BT_656_1120, ++ /* keep this initialization for compatibility with GCC < 4.4.6 */ ++ .reserved = {0}, ++ /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ ++ V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, ++ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, ++ V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | ++ V4L2_DV_BT_CAP_CUSTOM)}; + +- /* edid */ +- u8 edid_blocks_written; ++struct tc358743_state { ++ struct tc358743_platform_data pdata; ++ // struct v4l2_of_bus_mipi_csi2 bus; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler hdl; ++ struct i2c_client *i2c_client; ++ struct regmap *regmap; ++ /* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */ ++ struct mutex confctl_mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl *detect_tx_5v_ctrl; ++ struct v4l2_ctrl *audio_sampling_rate_ctrl; ++ struct v4l2_ctrl *audio_present_ctrl; ++ ++ /* work queues */ ++ struct workqueue_struct *work_queues; ++ struct delayed_work delayed_work_enable_hotplug; ++ struct delayed_work delayed_work_enable_interrupt; ++ struct work_struct process_isr; ++ struct mutex isr_lock; ++ ++ /* edid */ ++ u8 edid_blocks_written; ++ ++ /* used by i2c_wr() */ ++ u8 wr_data[MAX_XFER_SIZE]; ++ ++ struct v4l2_dv_timings timings; ++ u32 mbus_fmt_code; ++ ++ struct gpio_desc *reset_gpio; ++}; + +- struct v4l2_dv_timings timings; +- u32 mbus_fmt_code; +- u8 csi_lanes_in_use; ++static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) { ++ return container_of(sd, struct tc358743_state, sd); ++} ++/* ++static char * sdo_bit_len [] = { ++ [0b000] = "16bit (lower 8bit discarded)", ++ [0b001] = "16bit (lower 8bit + 1 discarded)", ++ [0b010] = "18bit (lower 6bit discarded)", ++ [0b011] = "18bit (lower 6bit + 1 discarded)", ++ [0b100] = "20bit (lower 4bit discarded)", ++ [0b101] = "20bit (lower 4bit + 1 discarded)", ++ [0b110] = "24bit no rounding", ++ [0b111] = "Output OFF (Mute)", ++}; + +- struct gpio_desc *reset_gpio; ++static char * sdo_fmt [] = { ++ [MASK_SDO_FMT_RIGHT] = "Right justified", ++ [MASK_SDO_FMT_LEFT] = "Left justified", ++ [MASK_SDO_FMT_I2S] = "I2S", ++ [0b011] = "I2S", ++}; + +- struct cec_adapter *cec_adap; ++static char * no_yes [] = { ++ [0] = "No", ++ [1] = "Yes", + }; + +-static void tc358743_enable_interrupts(struct v4l2_subdev *sd, +- bool cable_connected); +-static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd); ++static char * no_with [] = { ++ [0] = "No", ++ [1] = "With", ++}; + +-static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) +-{ +- return container_of(sd, struct tc358743_state, sd); +-} ++static char * off_on [] = { ++ [0b000] = "Off", ++ [0b001] = "On", ++}; + ++static char * audout_sel [] = { ++ [0b00] = "CSI2-TX", ++ [0b01] = "Reseved", ++ [0b10] = "I2S", ++ [0b11] = "TDM", ++}; ++*/ + /* --------------- I2C --------------- */ ++static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { ++ struct tc358743_state *state = to_state(sd); ++ struct i2c_client *client = state->i2c_client; ++ int err; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = buf, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = n, ++ .buf = values, ++ }, ++ }; + +-static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +-{ +- struct tc358743_state *state = to_state(sd); +- struct i2c_client *client = state->i2c_client; +- int err; +- u8 buf[2] = { reg >> 8, reg & 0xff }; +- struct i2c_msg msgs[] = { +- { +- .addr = client->addr, +- .flags = 0, +- .len = 2, +- .buf = buf, +- }, +- { +- .addr = client->addr, +- .flags = I2C_M_RD, +- .len = n, +- .buf = values, +- }, +- }; +- +- err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (err != ARRAY_SIZE(msgs)) { +- v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", +- __func__, reg, client->addr); +- } +-} +- +-static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +-{ +- struct tc358743_state *state = to_state(sd); +- struct i2c_client *client = state->i2c_client; +- int err, i; +- struct i2c_msg msg; +- u8 data[I2C_MAX_XFER_SIZE]; +- +- if ((2 + n) > I2C_MAX_XFER_SIZE) { +- n = I2C_MAX_XFER_SIZE - 2; +- v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", +- reg, 2 + n); +- } ++ err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (err != ARRAY_SIZE(msgs)) { ++ v4l2_err(sd, "%s: #### reading register0x%x from0x%x failed\n", __func__, ++ reg, client->addr); ++ return -1; ++ } ++ // udelay(10); ++ return 0; ++} + +- msg.addr = client->addr; +- msg.buf = data; +- msg.len = 2 + n; +- msg.flags = 0; ++static int i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) { ++ struct tc358743_state *state = to_state(sd); ++ struct i2c_client *client = state->i2c_client; ++ u8 *data = state->wr_data; ++ int err, i; ++ struct i2c_msg msg; + +- data[0] = reg >> 8; +- data[1] = reg & 0xff; ++ if ((2 + n) > sizeof(state->wr_data)) { ++ v4l2_warn(sd, "i2c wr reg=%04x: len=%d is too big!\n", reg, 2 + n); ++ return -1; ++ } + +- for (i = 0; i < n; i++) +- data[2 + i] = values[i]; ++ msg.addr = client->addr; ++ msg.buf = data; ++ msg.len = 2 + n; ++ msg.flags = 0; + +- err = i2c_transfer(client->adapter, &msg, 1); +- if (err != 1) { +- v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", +- __func__, reg, client->addr); +- return; +- } ++ data[0] = reg >> 8; ++ data[1] = reg & 0xff; + +- if (debug < 3) +- return; ++ for (i = 0; i < n; i++) data[2 + i] = values[i]; + +- switch (n) { +- case 1: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x", +- reg, data[2]); +- break; +- case 2: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", +- reg, data[3], data[2]); +- break; +- case 4: +- v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", +- reg, data[5], data[4], data[3], data[2]); +- break; +- default: +- v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", +- n, reg); +- } ++ err = i2c_transfer(client->adapter, &msg, 1); ++ if (err != 1) { ++ v4l2_err(sd, "%s: writing register0x%x from0x%x failed\n", __func__, reg, ++ client->addr); ++ return -1; ++ } ++ return 0; + } + +-static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) +-{ +- __le32 val = 0; ++static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) { ++ u8 val; + +- i2c_rd(sd, reg, (u8 __force *)&val, n); ++ i2c_rd(sd, reg, &val, 1); + +- return le32_to_cpu(val); ++ return val; + } + +-static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n) +-{ +- __le32 raw = cpu_to_le32(val); +- +- i2c_wr(sd, reg, (u8 __force *)&raw, n); ++static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) { ++ i2c_wr(sd, reg, &val, 1); + } + +-static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 1); ++static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, u8 mask, u8 val) { ++ i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); + } + +-static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) +-{ +- i2c_wrreg(sd, reg, val, 1); +-} ++static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) { ++ u16 val; ++ int ret; ++ // v4l2_info(sd, "Reading i2c_rd16\n"); + +-static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, +- u8 mask, u8 val) +-{ +- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 1) & mask) | val, 1); +-} ++ ret = i2c_rd(sd, reg, (u8 *)&val, 2); ++ // v4l2_info(sd, "RET %d\n", ret); + +-static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 2); +-} ++ if (ret == -1) { ++ // Read failed ++ return 99; // TODO. Make this better! ++ } + +-static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) +-{ +- i2c_wrreg(sd, reg, val, 2); ++ return val; + } + +-static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) +-{ +- i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); ++static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) { ++ i2c_wr(sd, reg, (u8 *)&val, 2); + } + +-static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) +-{ +- return i2c_rdreg(sd, reg, 4); ++static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, ++ u16 val) { ++ i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); + } + +-static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) +-{ +- i2c_wrreg(sd, reg, val, 4); ++static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) { ++ u32 val; ++ ++ i2c_rd(sd, reg, (u8 *)&val, 4); ++ ++ return val; + } + ++static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) { ++ i2c_wr(sd, reg, (u8 *)&val, 4); ++} + /* --------------- STATUS --------------- */ + +-static inline bool is_hdmi(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; ++static inline bool is_hdmi(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; + } + +-static inline bool tx_5v_power_present(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; ++static inline bool tx_5v_power_present(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; + } + +-static inline bool no_signal(struct v4l2_subdev *sd) +-{ +- return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); ++static inline bool no_signal(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); + } + +-static inline bool no_sync(struct v4l2_subdev *sd) +-{ +- return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); ++static inline bool no_sync(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); + } + +-static inline bool audio_present(struct v4l2_subdev *sd) +-{ +- return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; ++static inline bool audio_present(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; + } + +-static int get_audio_sampling_rate(struct v4l2_subdev *sd) +-{ +- static const int code_to_rate[] = { +- 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, +- 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 +- }; ++static int get_audio_sampling_rate(struct v4l2_subdev *sd) { ++ static const int code_to_rate[] = { ++ 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, ++ 88200, 768000, 96000, 705600, 176400, 0, 192000, 0}; ++ v4l2_info(sd, "function %s\n", __func__); ++ /* Register FS_SET is not cleared when the cable is disconnected */ ++ if (no_signal(sd)) return 0; + +- /* Register FS_SET is not cleared when the cable is disconnected */ +- if (no_signal(sd)) +- return 0; ++ return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; ++} + +- return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; ++static unsigned tc358743_num_csi_lanes_in_use(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "function %s\n", __func__); ++ return ((i2c_rd32(sd, CSI_CONTROL) & MASK_NOL) >> 1) + 1; + } + + /* --------------- TIMINGS --------------- */ + +-static inline unsigned fps(const struct v4l2_bt_timings *t) +-{ +- if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) +- return 0; ++static inline unsigned fps(const struct v4l2_bt_timings *t) { ++ if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) return 0; + +- return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, +- V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); ++ return DIV_ROUND_CLOSEST( ++ (unsigned)t->pixelclock, ++ V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); + } + + static int tc358743_get_detected_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct v4l2_bt_timings *bt = &timings->bt; +- unsigned width, height, frame_width, frame_height, frame_interval, fps; +- +- memset(timings, 0, sizeof(struct v4l2_dv_timings)); +- +- if (no_signal(sd)) { +- v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); +- return -ENOLINK; +- } +- if (no_sync(sd)) { +- v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); +- return -ENOLCK; +- } +- +- timings->type = V4L2_DV_BT_656_1120; +- bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? +- V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; +- +- width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + +- i2c_rd8(sd, DE_WIDTH_H_LO); +- height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + +- i2c_rd8(sd, DE_WIDTH_V_LO); +- frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + +- i2c_rd8(sd, H_SIZE_LO); +- frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + +- i2c_rd8(sd, V_SIZE_LO)) / 2; +- /* frame interval in milliseconds * 10 +- * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ +- frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + +- i2c_rd8(sd, FV_CNT_LO); +- fps = (frame_interval > 0) ? +- DIV_ROUND_CLOSEST(10000, frame_interval) : 0; +- +- bt->width = width; +- bt->height = height; +- bt->vsync = frame_height - height; +- bt->hsync = frame_width - width; +- bt->pixelclock = frame_width * frame_height * fps; +- if (bt->interlaced == V4L2_DV_INTERLACED) { +- bt->height *= 2; +- bt->il_vsync = bt->vsync + 1; +- bt->pixelclock /= 2; +- } +- +- return 0; ++ struct v4l2_dv_timings *timings) { ++ struct v4l2_bt_timings *bt = &timings->bt; ++ unsigned width, height, frame_width, frame_height, frame_interval, fps; ++ ++ memset(timings, 0, sizeof(struct v4l2_dv_timings)); ++ ++ if (no_signal(sd)) { ++ v4l2_info(sd, "%s: no valid signal\n", __func__); ++ return -ENOLINK; ++ } ++ if (no_sync(sd)) { ++ v4l2_info(sd, "%s: no sync on signal\n", __func__); ++ return -ENOLCK; ++ } ++ ++ timings->type = V4L2_DV_BT_656_1120; ++ bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ++ ? V4L2_DV_INTERLACED ++ : V4L2_DV_PROGRESSIVE; ++ ++ width = ++ ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + i2c_rd8(sd, DE_WIDTH_H_LO); ++ height = ++ ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + i2c_rd8(sd, DE_WIDTH_V_LO); ++ frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + i2c_rd8(sd, H_SIZE_LO); ++ frame_height = ++ (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + i2c_rd8(sd, V_SIZE_LO)) / 2; ++ /* frame interval in milliseconds * 10 ++ * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ ++ frame_interval = ++ ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + i2c_rd8(sd, FV_CNT_LO); ++ fps = (frame_interval > 0) ? DIV_ROUND_CLOSEST(10000, frame_interval) : 0; ++ ++ bt->width = width; ++ bt->height = height; ++ bt->vsync = frame_height - height; ++ bt->hsync = frame_width - width; ++ bt->pixelclock = frame_width * frame_height * fps; ++ if (bt->interlaced == V4L2_DV_INTERLACED) { ++ bt->height *= 2; ++ bt->il_vsync = bt->vsync + 1; ++ bt->pixelclock /= 2; ++ } ++ v4l2_info(sd, "%d:%s: width %d heigh %d interlaced %d\n", __LINE__, ++ __FUNCTION__, bt->width, bt->height, bt->interlaced); ++ return 0; + } +- + /* --------------- HOTPLUG / HDCP / EDID --------------- */ + +-static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) +-{ +- struct delayed_work *dwork = to_delayed_work(work); +- struct tc358743_state *state = container_of(dwork, +- struct tc358743_state, delayed_work_enable_hotplug); +- struct v4l2_subdev *sd = &state->sd; ++static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) { ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct tc358743_state *state = ++ container_of(dwork, struct tc358743_state, delayed_work_enable_hotplug); ++ struct v4l2_subdev *sd = &state->sd; + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ v4l2_info(sd, "%s:\n", __func__); + +- i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); ++ /*hainh ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_CTL0, MASK_HPD_CTL0); ++ */ + } + +-static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) +-{ +- v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? +- "enable" : "disable"); ++static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) { ++ v4l2_info(sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + +- if (enable) { +- i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, KEY_RD_CMD); ++ i2c_wr8_and_or(sd, HDCP_REG1, ~(MASK_AUTH_UNAUTH_SEL | MASK_AUTH_UNAUTH), ++ MASK_AUTH_UNAUTH_SEL_16_FRAMES | MASK_AUTH_UNAUTH_AUTO); + +- i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, 0); ++ i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, ++ SET_AUTO_P3_RESET_FRAMES(0x0f)); + +- i2c_wr8_and_or(sd, HDCP_REG1, 0xff, +- MASK_AUTH_UNAUTH_SEL_16_FRAMES | +- MASK_AUTH_UNAUTH_AUTO); ++ /* HDCP is disabled by configuring the receiver as HDCP repeater. The ++ * repeater mode require software support to work, so HDCP ++ * authentication will fail. */ ++ i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, enable ? KEY_RD_CMD : 0); ++ i2c_wr8_and_or(sd, HDCP_MODE, ~(MASK_AUTO_CLR | MASK_MODE_RST_TN), ++ enable ? (MASK_AUTO_CLR | MASK_MODE_RST_TN) : 0); + +- i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, +- SET_AUTO_P3_RESET_FRAMES(0x0f)); +- } else { +- i2c_wr8_and_or(sd, HDCP_MODE, ~MASK_MANUAL_AUTHENTICATION, +- MASK_MANUAL_AUTHENTICATION); +- } ++ /* Apple MacBook Pro gen.8 has a bug that makes it freeze every fifth ++ * second when HDCP is disabled, but the MAX_EXCED bit is handled ++ * correctly and HDCP is disabled on the HDMI output. */ ++ i2c_wr8_and_or(sd, BSTATUS1, ~MASK_MAX_EXCED, enable ? 0 : MASK_MAX_EXCED); ++ i2c_wr8_and_or(sd, BCAPS, ~(MASK_REPEATER | MASK_READY), ++ enable ? 0 : MASK_REPEATER | MASK_READY); + } + +-static void tc358743_disable_edid(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++static void tc358743_disable_edid(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "%s:\n", __func__); + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + +- cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); ++ /* DDC access to EDID is also disabled when hotplug is disabled. See ++ * register DDC_CTL */ ++ i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); ++} + +- /* DDC access to EDID is also disabled when hotplug is disabled. See +- * register DDC_CTL */ +- i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); ++static void tc358743_erase_bksv(struct v4l2_subdev *sd) { ++ int i; ++ v4l2_info(sd, "function %s\n", __func__); ++ for (i = 0; i < 5; i++) i2c_wr8(sd, BKSV + i, 0); + } + +-static void tc358743_enable_edid(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++/* --------------- AVI infoframe --------------- */ + +- if (state->edid_blocks_written == 0) { +- v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); +- tc358743_s_ctrl_detect_tx_5v(sd); +- return; +- } ++static void print_avi_infoframe(struct v4l2_subdev *sd) { ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct device *dev = &client->dev; ++ union hdmi_infoframe frame; ++ u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; ++ v4l2_info(sd, "function %s\n", __func__); ++ if (!is_hdmi(sd)) { ++ v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); ++ return; ++ } + +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); ++ i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); + +- /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when +- * hotplug is enabled. See register DDC_CTL */ +- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10); ++ if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { ++ v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); ++ return; ++ } + +- tc358743_enable_interrupts(sd, true); +- tc358743_s_ctrl_detect_tx_5v(sd); ++ hdmi_infoframe_log(KERN_INFO, dev, &frame); + } + +-static void tc358743_erase_bksv(struct v4l2_subdev *sd) +-{ +- int i; ++/* --------------- CTRLS --------------- */ + +- for (i = 0; i < 5; i++) +- i2c_wr8(sd, BKSV + i, 0); ++static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, tx_5v_power_present(sd)); ++} ++ ++static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, ++ get_audio_sampling_rate(sd)); ++} ++ ++static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, audio_present(sd)); ++} ++ ++static int tc358743_update_controls(struct v4l2_subdev *sd) { ++ int ret = 0; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ ret |= tc358743_s_ctrl_detect_tx_5v(sd); ++ ret |= tc358743_s_ctrl_audio_sampling_rate(sd); ++ ret |= tc358743_s_ctrl_audio_present(sd); ++ ++ return ret; ++} ++ ++static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) { ++ // return 2; ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_bt_timings *bt = &state->timings.bt; ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u32 bits_pr_pixel = ++ (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; ++ u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; ++ u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; ++ v4l2_info(sd, "function %s bps %d bps_pr_lane%d \n", __func__, bps, ++ bps_pr_lane); ++ return DIV_ROUND_UP(bps, bps_pr_lane); ++} ++ ++static int tc358743_get_edid(struct v4l2_subdev *sd) { ++ u8 edid_read[256]; ++ int result = 0; ++ u32 n = sizeof(edid_read); ++ ++ result = i2c_rd(sd, EDID_RAM, edid_read, n); ++ v4l2_info(sd, "%s i2c_rd return %d\r\n", __func__, result); ++ v4l2_info(sd, "%s done\r\n", __func__); ++ return 0; ++} ++ ++static int tc358743_log_status(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings timings; ++ uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); ++ uint16_t sysctl = i2c_rd16(sd, SYSCTL); ++ u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); ++ const int deep_color_mode[4] = {8, 10, 12, 16}; ++ static const char *const input_color_space[] = { ++ "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", ++ "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", ++ "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; ++ tc358743_get_edid(sd); ++ v4l2_info(sd, "-----Chip status-----\n"); ++ v4l2_info(sd, "Chip ID:0x%02x\n", (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); ++ v4l2_info(sd, "Chip revision:0x%02x\n", i2c_rd16(sd, CHIPID) & MASK_REVID); ++ v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", ++ !!(sysctl & MASK_IRRST), !!(sysctl & MASK_CECRST), ++ !!(sysctl & MASK_CTXRST), !!(sysctl & MASK_HDMIRST)); ++ v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); ++ v4l2_info(sd, "Cable detected (+5V power): %s\n", ++ hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); ++ v4l2_info(sd, "DDC lines enabled: %s\n", ++ (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? "yes" : "no"); ++ v4l2_info(sd, "Hotplug enabled: %s\n", ++ (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? "yes" : "no"); ++ v4l2_info(sd, "CEC enabled: %s\n", ++ (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); ++ v4l2_info(sd, "-----Signal status-----\n"); ++ v4l2_info(sd, "TMDS signal detected: %s\n", ++ hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); ++ v4l2_info(sd, "Stable sync signal: %s\n", ++ hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); ++ v4l2_info(sd, "PHY PLL locked: %s\n", ++ hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); ++ v4l2_info(sd, "PHY DE detected: %s\n", ++ hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); ++ ++ if (tc358743_get_detected_timings(sd, &timings)) { ++ v4l2_info(sd, "No video detected\n"); ++ } else { ++ v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, true); ++ } ++ v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, true); ++ ++ v4l2_info(sd, "-----CSI-TX status-----\n"); ++ v4l2_info(sd, "Lanes needed: %d\n", tc358743_num_csi_lanes_needed(sd)); ++ v4l2_info(sd, "Lanes in use: %d\n", tc358743_num_csi_lanes_in_use(sd)); ++ v4l2_info(sd, "Waiting for particular sync signal: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? "yes" : "no"); ++ v4l2_info(sd, "Transmit mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? "yes" : "no"); ++ v4l2_info(sd, "Receive mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? "yes" : "no"); ++ v4l2_info(sd, "Stopped: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? "yes" : "no"); ++ v4l2_info(sd, "Color space: %s\n", ++ state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ++ ? "YCbCr 422 16-bit" ++ : state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ++ ? "RGB 888 24-bit" ++ : "Unsupported"); ++ ++ v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); ++ v4l2_info(sd, "HDCP encrypted content: %s\n", ++ hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); ++ v4l2_info(sd, "Input color space: %s %s range\n", ++ input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], ++ (vi_status3 & MASK_LIMITED) ? "limited" : "full"); ++ if (!is_hdmi(sd)) return 0; ++ v4l2_info(sd, "AV Mute: %s\n", ++ hdmi_sys_status & MASK_S_AVMUTE ? "on" : "off"); ++ v4l2_info(sd, "Deep color mode: %d-bits per channel\n", ++ deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & MASK_S_DEEPCOLOR) >> 2]); ++ print_avi_infoframe(sd); ++ ++ return 0; + } + +-/* --------------- AVI infoframe --------------- */ ++/* --------------- INIT --------------- */ + +-static void print_avi_infoframe(struct v4l2_subdev *sd) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(sd); +- struct device *dev = &client->dev; +- union hdmi_infoframe frame; +- u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; ++static void tc358743_reset_phy(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); ++ i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); ++} ++ ++static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) { ++ u16 sysctl = i2c_rd16(sd, SYSCTL); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr16(sd, SYSCTL, sysctl | mask); ++ i2c_wr16(sd, SYSCTL, sysctl & ~mask); ++} ++ ++static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, enable ? MASK_SLEEP : 0); ++} ++ ++static inline void enable_stream(struct v4l2_subdev *sd, bool enable) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "LDS> Transmit mode: %s\n", ++ (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? "yes" : "no"); ++ v4l2_info(sd, "LDS> %s: %sable\n", __func__, enable ? "en" : "dis"); ++ ++ if (enable) { ++ /* It is critical for CSI receiver to see lane transition ++ * LP11->HS. Set to non-continuous mode to enable clock lane ++ * LP11 state. */ ++ i2c_wr32(sd, TXOPTIONCNTRL, 0); ++ /* Set to continuous mode to trigger LP11->HS transition */ ++ i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); ++ /* Unmute video */ ++ i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); ++ } else { ++ /* Mute video so that all data lanes go to LSP11 state. ++ * No data is output to CSI Tx block. */ ++ i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); ++ } ++ ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), ++ enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); ++ mutex_unlock(&state->confctl_mutex); ++ v4l2_info(sd, "%d:%s: end\n", __LINE__, __FUNCTION__); ++} ++ ++static void tc358743_set_pll(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u16 pllctl0 = i2c_rd16(sd, PLLCTL0); ++ u16 pllctl1 = i2c_rd16(sd, PLLCTL1); ++ u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | SET_PLL_FBD(pdata->pll_fbd); ++ u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; ++ ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ /* Only rewrite when needed (new value or disabled), since rewriting ++ * triggers another format change event. */ ++ if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { ++ u16 pll_frs; ++ ++ if (hsck > 500000000) ++ pll_frs = 0x0; ++ else if (hsck > 250000000) ++ pll_frs = 0x1; ++ else if (hsck > 125000000) ++ pll_frs = 0x2; ++ else ++ pll_frs = 0x3; ++ v4l2_info(sd, "%s: updating PLL clock\n", __func__); ++ tc358743_sleep_mode(sd, true); ++ i2c_wr16(sd, PLLCTL0, pllctl0_new); ++ i2c_wr16_and_or(sd, PLLCTL1, ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), ++ (SET_PLL_FRS(pll_frs) | MASK_RESETB | MASK_PLL_EN)); ++ udelay(10); /* REF_02, Sheet "Source HDMI" */ ++ i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); ++ tc358743_sleep_mode(sd, false); ++ } ++} ++ ++static void tc358743_set_ref_clk(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ u32 sys_freq; ++ u32 lockdet_ref; ++ u16 fh_min; ++ u16 fh_max; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ sys_freq = pdata->refclk_hz / 10000; ++ i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); ++ i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); ++ ++ i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, ++ (pdata->refclk_hz == 42000000) ? MASK_PHY_SYSCLK_IND : 0x0); ++ ++ fh_min = pdata->refclk_hz / 100000; ++ i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); ++ i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); ++ ++ fh_max = (fh_min * 66) / 10; ++ i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); ++ i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); ++ ++ lockdet_ref = pdata->refclk_hz / 100; ++ i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); ++ i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); ++ i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); ++ ++ i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, ++ (pdata->refclk_hz == 27000000) ? MASK_NCO_F0_MOD_27MHZ : 0x0); ++} ++ ++static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ switch (state->mbus_fmt_code) { ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ v4l2_info(sd, "%s: YCbCr 422 16-bit\n", __func__); ++ i2c_wr8_and_or(sd, VOUT_SET2, ++ ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, ++ MASK_SEL422 | MASK_VOUT_422FIL_100); ++ i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, ++ MASK_VOUT_COLOR_601_YCBCR_LIMITED); ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, MASK_YCBCRFMT_422_8_BIT); ++ mutex_unlock(&state->confctl_mutex); ++ break; ++ ++ // v4l2_info(sd, "LDS> %s: TEST pattern\n", __func__); ++ // i2c_wr8_and_or(sd, VOUT_SET2, ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, MASK_SEL422 | MASK_VOUT_422FIL_100); ++ // i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, MASK_VOUT_COLOR_601_YCBCR_LIMITED); ++ // mutex_lock(&state->confctl_mutex); ++ // i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, MASK_YCBCRFMT_COLORBAR); ++ // mutex_unlock(&state->confctl_mutex); ++ // break; ++ ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ v4l2_info(sd, "%s: RGB 888 24-bit\n", __func__); ++ i2c_wr8_and_or(sd, VOUT_SET2, ++ ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, 0x00); ++ i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, ++ MASK_VOUT_COLOR_RGB_FULL); ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); ++ mutex_unlock(&state->confctl_mutex); ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", __func__, ++ state->mbus_fmt_code); ++ break; ++ } ++ ++ // enable_stream(sd, true); // Just put here for testing ++} ++ ++static void tc358743_set_csi(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ unsigned lanes = tc358743_num_csi_lanes_needed(sd); ++ ++ v4l2_info(sd, "%s:\n", __func__); ++ ++ tc358743_reset(sd, MASK_CTXRST); ++ ++ if (lanes < 1) i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); ++ if (lanes < 1) i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); ++ if (lanes < 2) i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); ++ if (lanes < 3) i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); ++ if (lanes < 4) i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); ++ ++ i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); ++ i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); ++ i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); ++ i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); ++ i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); ++ i2c_wr32(sd, TWAKEUP, pdata->twakeup); ++ i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); ++ i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); ++ i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); ++ ++ i2c_wr32(sd, HSTXVREGEN, ++ ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | ++ ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | ++ ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | ++ ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | ++ ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); ++ ++ i2c_wr32( ++ sd, TXOPTIONCNTRL, ++ (pdata->endpoint.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ++ ? MASK_CONTCLKMODE ++ : 0); ++ i2c_wr32(sd, STARTCNTRL, MASK_START); ++ i2c_wr32(sd, CSI_START, MASK_STRT); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_CONTROL | MASK_CSI_MODE | ++ MASK_TXHSMD | ++ ((lanes == 4) ++ ? MASK_NOL_4 ++ : (lanes == 3) ? MASK_NOL_3 ++ : (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | ++ MASK_QUNK | MASK_WCER | MASK_INER); ++ ++ i2c_wr32( ++ sd, CSI_CONFW, ++ MASK_MODE_CLEAR | MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); ++ ++ i2c_wr32(sd, CSI_CONFW, ++ MASK_MODE_SET | MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); ++} ++ ++static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* Default settings from REF_02, sheet "Source HDMI" ++ * and custom settings as platform data */ ++ // turn of physics ++ i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); ++ i2c_wr8(sd, PHY_CTL1, ++ SET_PHY_AUTO_RST1_US(1600) | SET_FREQ_RANGE_MODE_CYCLES(1)); ++ i2c_wr8_and_or( ++ sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, ++ (pdata->hdmi_phy_auto_reset_tmds_detected ? MASK_PHY_AUTO_RST2 : 0) | ++ (pdata->hdmi_phy_auto_reset_tmds_in_range ? MASK_PHY_AUTO_RST3 : 0) | ++ (pdata->hdmi_phy_auto_reset_tmds_valid ? MASK_PHY_AUTO_RST4 : 0)); ++ i2c_wr8(sd, PHY_BIAS, 0x40); ++ i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); ++ i2c_wr8(sd, AVM_CTL, 45); ++ i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, ++ pdata->hdmi_detection_delay << 4); ++ ++ i2c_wr8_and_or( ++ sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), ++ (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? MASK_H_PI_RST : 0) | ++ (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? MASK_V_PI_RST : 0)); ++ // turn on physics ++ i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); ++} ++ ++static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* Default settings from REF_02, sheet "Source HDMI" */ ++ i2c_wr8(sd, FORCE_MUTE, 0x00); ++ i2c_wr8(sd, AUTO_CMD0, ++ MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | MASK_AUTO_MUTE5 | ++ MASK_AUTO_MUTE4 | MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); ++ i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); ++ i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); ++ i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); ++ i2c_wr8(sd, FS_MUTE, 0x00); ++ i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); ++ i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); ++ i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); ++ i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); ++ i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); ++ i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); ++ ++ mutex_lock(&state->confctl_mutex); ++ i2c_wr16_and_or(sd, CONFCTL, 0xffff, ++ MASK_AUDCHNUM_2 | MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); ++ mutex_unlock(&state->confctl_mutex); ++} ++ ++static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) { ++ /* Default settings from REF_02, sheet "Source HDMI" */ ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ i2c_wr8(sd, PK_INT_MODE, ++ MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | MASK_ACP_INT_MODE | ++ MASK_VS_INT_MODE | MASK_SPD_INT_MODE | MASK_MS_INT_MODE | ++ MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); ++ i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); ++ i2c_wr8(sd, NO_PKT_CLR, 0x53); ++ i2c_wr8(sd, ERR_PK_LIMIT, 0x01); ++ i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); ++ i2c_wr8(sd, NO_GDB_LIMIT, 0x10); ++} ++ ++static void tc358743_initial_setup(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct tc358743_platform_data *pdata = &state->pdata; ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ /* CEC and IR are not supported by this driver */ ++ i2c_wr16_and_or(sd, SYSCTL, ~(MASK_CECRST | MASK_IRRST), ++ (MASK_CECRST | MASK_IRRST)); ++ ++ tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); ++ tc358743_sleep_mode(sd, false); ++ ++ i2c_wr16(sd, FIFOCTL, pdata->fifo_level); ++ ++ tc358743_set_ref_clk(sd); ++ ++ i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, ++ pdata->ddc5v_delay & MASK_DDC5V_MODE); ++ ++ i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); ++ ++ tc358743_set_hdmi_phy(sd); ++ tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); ++ tc358743_set_hdmi_audio(sd); ++ tc358743_set_hdmi_info_frame_mode(sd); ++ ++ /* All CE and IT formats are detected as RGB full range in DVI mode */ ++ i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); ++ ++ i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, MASK_VOUTCOLORMODE_AUTO); ++ i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); ++} + +- if (!is_hdmi(sd)) { +- v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); +- return; +- } ++/* --------------- IRQ --------------- */ + +- i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); ++static void tc358743_format_change(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings timings; ++ const struct v4l2_event tc358743_ev_fmt = { ++ .type = V4L2_EVENT_SOURCE_CHANGE, ++ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, ++ }; + +- if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { +- v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); +- return; +- } ++ v4l2_info(sd, "%s: Format changed\n", __func__); + +- hdmi_infoframe_log(KERN_INFO, dev, &frame); +-} ++ if (tc358743_get_detected_timings(sd, &timings)) { ++ enable_stream(sd, false); + +-/* --------------- CTRLS --------------- */ ++ v4l2_info(sd, "%s: Format changed. No signal\n", __func__); ++ } else { ++ if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false)) ++ enable_stream(sd, false); + +-static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++ v4l2_print_dv_timings( ++ sd->name, ++ "tc358743_format_change: Format change`d. New format: ", &timings, ++ false); ++ } + +- return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, +- tx_5v_power_present(sd)); ++ if (sd->devnode) v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); + } + +-static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++static void tc358743_init_interrupts(struct v4l2_subdev *sd) { ++ u16 i; ++ v4l2_info(sd, "function %s\n", __func__); + +- return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, +- get_audio_sampling_rate(sd)); +-} ++ /* clear interrupt status registers */ ++ for (i = SYS_INT; i <= KEY_INT; i++) i2c_wr8(sd, i, 0xff); + +-static int tc358743_s_ctrl_audio_present(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); ++ i2c_wr16(sd, INTSTATUS, 0xffff); ++} + +- return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, +- audio_present(sd)); ++static void tc358743_enable_interrupts(struct v4l2_subdev *sd, ++ bool cable_connected) { ++ v4l2_info(sd, "%s: cable connected = %d\n", __func__, cable_connected); ++ ++ if (cable_connected) { ++ i2c_wr8(sd, SYS_INTM, ++ ~(MASK_M_DDC | MASK_M_DVI_DET | MASK_M_HDMI_DET) & 0xff); ++ i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); ++ i2c_wr8(sd, CBIT_INTM, ++ ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | MASK_M_AF_UNLOCK) & 0xff); ++ i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); ++ i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); ++ } else { ++ i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); ++ i2c_wr8(sd, CLK_INTM, 0xff); ++ i2c_wr8(sd, CBIT_INTM, 0xff); ++ i2c_wr8(sd, AUDIO_INTM, 0xff); ++ i2c_wr8(sd, MISC_INTM, 0xff); ++ } + } + +-static int tc358743_update_controls(struct v4l2_subdev *sd) +-{ +- int ret = 0; ++static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); ++ u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; ++ ++ i2c_wr8(sd, AUDIO_INT, audio_int); + +- ret |= tc358743_s_ctrl_detect_tx_5v(sd); +- ret |= tc358743_s_ctrl_audio_sampling_rate(sd); +- ret |= tc358743_s_ctrl_audio_present(sd); ++ v4l2_info(sd, "%s: AUDIO_INT =0x%02x\n", __func__, audio_int); + +- return ret; ++ tc358743_s_ctrl_audio_sampling_rate(sd); ++ tc358743_s_ctrl_audio_present(sd); + } + +-/* --------------- INIT --------------- */ +- +-static void tc358743_reset_phy(struct v4l2_subdev *sd) +-{ +- v4l2_dbg(1, debug, sd, "%s:\n", __func__); +- +- i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); +- i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); +-} +- +-static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) +-{ +- u16 sysctl = i2c_rd16(sd, SYSCTL); +- +- i2c_wr16(sd, SYSCTL, sysctl | mask); +- i2c_wr16(sd, SYSCTL, sysctl & ~mask); +-} +- +-static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) +-{ +- i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, +- enable ? MASK_SLEEP : 0); +-} +- +-static inline void enable_stream(struct v4l2_subdev *sd, bool enable) +-{ +- struct tc358743_state *state = to_state(sd); +- +- v4l2_dbg(3, debug, sd, "%s: %sable\n", +- __func__, enable ? "en" : "dis"); +- +- if (enable) { +- /* It is critical for CSI receiver to see lane transition +- * LP11->HS. Set to non-continuous mode to enable clock lane +- * LP11 state. */ +- i2c_wr32(sd, TXOPTIONCNTRL, 0); +- /* Set to continuous mode to trigger LP11->HS transition */ +- i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); +- /* Unmute video */ +- i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE); +- } else { +- /* Mute video so that all data lanes go to LSP11 state. +- * No data is output to CSI Tx block. */ +- i2c_wr8(sd, VI_MUTE, MASK_AUTO_MUTE | MASK_VI_MUTE); +- } +- +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), +- enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); +- mutex_unlock(&state->confctl_mutex); +-} +- +-static void tc358743_set_pll(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- u16 pllctl0 = i2c_rd16(sd, PLLCTL0); +- u16 pllctl1 = i2c_rd16(sd, PLLCTL1); +- u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | +- SET_PLL_FBD(pdata->pll_fbd); +- u32 hsck = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; +- +- v4l2_dbg(2, debug, sd, "%s:\n", __func__); +- +- /* Only rewrite when needed (new value or disabled), since rewriting +- * triggers another format change event. */ +- if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { +- u16 pll_frs; +- +- if (hsck > 500000000) +- pll_frs = 0x0; +- else if (hsck > 250000000) +- pll_frs = 0x1; +- else if (hsck > 125000000) +- pll_frs = 0x2; +- else +- pll_frs = 0x3; +- +- v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); +- tc358743_sleep_mode(sd, true); +- i2c_wr16(sd, PLLCTL0, pllctl0_new); +- i2c_wr16_and_or(sd, PLLCTL1, +- ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), +- (SET_PLL_FRS(pll_frs) | MASK_RESETB | +- MASK_PLL_EN)); +- udelay(10); /* REF_02, Sheet "Source HDMI" */ +- i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); +- tc358743_sleep_mode(sd, false); +- } +-} +- +-static void tc358743_set_ref_clk(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- u32 sys_freq; +- u32 lockdet_ref; +- u32 cec_freq; +- u16 fh_min; +- u16 fh_max; +- +- BUG_ON(!(pdata->refclk_hz == 26000000 || +- pdata->refclk_hz == 27000000 || +- pdata->refclk_hz == 42000000)); +- +- sys_freq = pdata->refclk_hz / 10000; +- i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); +- i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); +- +- i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, +- (pdata->refclk_hz == 42000000) ? +- MASK_PHY_SYSCLK_IND : 0x0); +- +- fh_min = pdata->refclk_hz / 100000; +- i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); +- i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); +- +- fh_max = (fh_min * 66) / 10; +- i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); +- i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); +- +- lockdet_ref = pdata->refclk_hz / 100; +- i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); +- i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); +- i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); +- +- i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, +- (pdata->refclk_hz == 27000000) ? +- MASK_NCO_F0_MOD_27MHZ : 0x0); +- +- /* +- * Trial and error suggests that the default register value +- * of 656 is for a 42 MHz reference clock. Use that to derive +- * a new value based on the actual reference clock. +- */ +- cec_freq = (656 * sys_freq) / 4200; +- i2c_wr16(sd, CECHCLK, cec_freq); +- i2c_wr16(sd, CECLCLK, cec_freq); +-} +- +-static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- +- switch (state->mbus_fmt_code) { +- case MEDIA_BUS_FMT_UYVY8_1X16: +- v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); +- i2c_wr8_and_or(sd, VOUT_SET2, +- ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, +- MASK_SEL422 | MASK_VOUT_422FIL_100); +- i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, +- MASK_VOUT_COLOR_601_YCBCR_LIMITED); +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, +- MASK_YCBCRFMT_422_8_BIT); +- mutex_unlock(&state->confctl_mutex); +- break; +- case MEDIA_BUS_FMT_RGB888_1X24: +- v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); +- i2c_wr8_and_or(sd, VOUT_SET2, +- ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, +- 0x00); +- i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, +- MASK_VOUT_COLOR_RGB_FULL); +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); +- mutex_unlock(&state->confctl_mutex); +- break; +- default: +- v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", +- __func__, state->mbus_fmt_code); +- } +-} +- +-static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_bt_timings *bt = &state->timings.bt; +- struct tc358743_platform_data *pdata = &state->pdata; +- u32 bits_pr_pixel = +- (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; +- u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; +- u32 bps_pr_lane = (pdata->refclk_hz / pdata->pll_prd) * pdata->pll_fbd; +- +- return DIV_ROUND_UP(bps, bps_pr_lane); +-} +- +-static void tc358743_set_csi(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- unsigned lanes = tc358743_num_csi_lanes_needed(sd); +- +- v4l2_dbg(3, debug, sd, "%s:\n", __func__); +- +- state->csi_lanes_in_use = lanes; +- +- tc358743_reset(sd, MASK_CTXRST); +- +- if (lanes < 1) +- i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); +- if (lanes < 1) +- i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); +- if (lanes < 2) +- i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); +- if (lanes < 3) +- i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); +- if (lanes < 4) +- i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); +- +- i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); +- i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); +- i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); +- i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); +- i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); +- i2c_wr32(sd, TWAKEUP, pdata->twakeup); +- i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); +- i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); +- i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); +- +- i2c_wr32(sd, HSTXVREGEN, +- ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | +- ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | +- ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | +- ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | +- ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); +- +- i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & +- V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); +- i2c_wr32(sd, STARTCNTRL, MASK_START); +- i2c_wr32(sd, CSI_START, MASK_STRT); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_CONTROL | +- MASK_CSI_MODE | +- MASK_TXHSMD | +- ((lanes == 4) ? MASK_NOL_4 : +- (lanes == 3) ? MASK_NOL_3 : +- (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | +- MASK_WCER | MASK_INER); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | +- MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); +- +- i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | +- MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); +-} +- +-static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- +- /* Default settings from REF_02, sheet "Source HDMI" +- * and custom settings as platform data */ +- i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); +- i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | +- SET_FREQ_RANGE_MODE_CYCLES(1)); +- i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, +- (pdata->hdmi_phy_auto_reset_tmds_detected ? +- MASK_PHY_AUTO_RST2 : 0) | +- (pdata->hdmi_phy_auto_reset_tmds_in_range ? +- MASK_PHY_AUTO_RST3 : 0) | +- (pdata->hdmi_phy_auto_reset_tmds_valid ? +- MASK_PHY_AUTO_RST4 : 0)); +- i2c_wr8(sd, PHY_BIAS, 0x40); +- i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); +- i2c_wr8(sd, AVM_CTL, 45); +- i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, +- pdata->hdmi_detection_delay << 4); +- i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), +- (pdata->hdmi_phy_auto_reset_hsync_out_of_range ? +- MASK_H_PI_RST : 0) | +- (pdata->hdmi_phy_auto_reset_vsync_out_of_range ? +- MASK_V_PI_RST : 0)); +- i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); +-} +- +-static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- +- /* Default settings from REF_02, sheet "Source HDMI" */ +- i2c_wr8(sd, FORCE_MUTE, 0x00); +- i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | +- MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | +- MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); +- i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); +- i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); +- i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); +- i2c_wr8(sd, FS_MUTE, 0x00); +- i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); +- i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); +- i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); +- i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); +- i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); +- i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); +- +- mutex_lock(&state->confctl_mutex); +- i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | +- MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); +- mutex_unlock(&state->confctl_mutex); +-} +- +-static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) +-{ +- /* Default settings from REF_02, sheet "Source HDMI" */ +- i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | +- MASK_ACP_INT_MODE | MASK_VS_INT_MODE | +- MASK_SPD_INT_MODE | MASK_MS_INT_MODE | +- MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); +- i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); +- i2c_wr8(sd, NO_PKT_CLR, 0x53); +- i2c_wr8(sd, ERR_PK_LIMIT, 0x01); +- i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); +- i2c_wr8(sd, NO_GDB_LIMIT, 0x10); +-} +- +-static void tc358743_initial_setup(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct tc358743_platform_data *pdata = &state->pdata; +- +- /* +- * IR is not supported by this driver. +- * CEC is only enabled if needed. +- */ +- i2c_wr16_and_or(sd, SYSCTL, ~(MASK_IRRST | MASK_CECRST), +- (MASK_IRRST | MASK_CECRST)); +- +- tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); +-#ifdef CONFIG_VIDEO_TC358743_CEC +- tc358743_reset(sd, MASK_CECRST); +-#endif +- tc358743_sleep_mode(sd, false); +- +- i2c_wr16(sd, FIFOCTL, pdata->fifo_level); +- +- tc358743_set_ref_clk(sd); +- +- i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, +- pdata->ddc5v_delay & MASK_DDC5V_MODE); +- i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); +- +- tc358743_set_hdmi_phy(sd); +- tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); +- tc358743_set_hdmi_audio(sd); +- tc358743_set_hdmi_info_frame_mode(sd); +- +- /* All CE and IT formats are detected as RGB full range in DVI mode */ +- i2c_wr8_and_or(sd, VI_MODE, ~MASK_RGB_DVI, 0); +- +- i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, +- MASK_VOUTCOLORMODE_AUTO); +- i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); +-} +- +-/* --------------- CEC --------------- */ +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +-static int tc358743_cec_adap_enable(struct cec_adapter *adap, bool enable) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- +- i2c_wr32(sd, CECIMSK, enable ? MASK_CECTIM | MASK_CECRIM : 0); +- i2c_wr32(sd, CECICLR, MASK_CECTICLR | MASK_CECRICLR); +- i2c_wr32(sd, CECEN, enable); +- if (enable) +- i2c_wr32(sd, CECREN, MASK_CECREN); +- return 0; +-} +- +-static int tc358743_cec_adap_monitor_all_enable(struct cec_adapter *adap, +- bool enable) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- u32 reg; +- +- reg = i2c_rd32(sd, CECRCTL1); +- if (enable) +- reg |= MASK_CECOTH; +- else +- reg &= ~MASK_CECOTH; +- i2c_wr32(sd, CECRCTL1, reg); +- return 0; +-} +- +-static int tc358743_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- unsigned int la = 0; +- +- if (log_addr != CEC_LOG_ADDR_INVALID) { +- la = i2c_rd32(sd, CECADD); +- la |= 1 << log_addr; +- } +- i2c_wr32(sd, CECADD, la); +- return 0; +-} +- +-static int tc358743_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, +- u32 signal_free_time, struct cec_msg *msg) +-{ +- struct tc358743_state *state = adap->priv; +- struct v4l2_subdev *sd = &state->sd; +- unsigned int i; +- +- i2c_wr32(sd, CECTCTL, +- (cec_msg_is_broadcast(msg) ? MASK_CECBRD : 0) | +- (signal_free_time - 1)); +- for (i = 0; i < msg->len; i++) +- i2c_wr32(sd, CECTBUF1 + i * 4, +- msg->msg[i] | ((i == msg->len - 1) ? MASK_CECTEOM : 0)); +- i2c_wr32(sd, CECTEN, MASK_CECTEN); +- return 0; +-} +- +-static const struct cec_adap_ops tc358743_cec_adap_ops = { +- .adap_enable = tc358743_cec_adap_enable, +- .adap_log_addr = tc358743_cec_adap_log_addr, +- .adap_transmit = tc358743_cec_adap_transmit, +- .adap_monitor_all_enable = tc358743_cec_adap_monitor_all_enable, +-}; ++static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ v4l2_err(sd, "%s: CSI_ERR =0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); + +-static void tc358743_cec_handler(struct v4l2_subdev *sd, u16 intstatus, +- bool *handled) +-{ +- struct tc358743_state *state = to_state(sd); +- unsigned int cec_rxint, cec_txint; +- unsigned int clr = 0; +- +- cec_rxint = i2c_rd32(sd, CECRSTAT); +- cec_txint = i2c_rd32(sd, CECTSTAT); +- +- if (intstatus & MASK_CEC_RINT) +- clr |= MASK_CECRICLR; +- if (intstatus & MASK_CEC_TINT) +- clr |= MASK_CECTICLR; +- i2c_wr32(sd, CECICLR, clr); +- +- if ((intstatus & MASK_CEC_TINT) && cec_txint) { +- if (cec_txint & MASK_CECTIEND) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_OK); +- else if (cec_txint & MASK_CECTIAL) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_ARB_LOST); +- else if (cec_txint & MASK_CECTIACK) +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_NACK); +- else if (cec_txint & MASK_CECTIUR) { +- /* +- * Not sure when this bit is set. Treat +- * it as an error for now. +- */ +- cec_transmit_attempt_done(state->cec_adap, +- CEC_TX_STATUS_ERROR); +- } +- if (handled) +- *handled = true; +- } +- if ((intstatus & MASK_CEC_RINT) && +- (cec_rxint & MASK_CECRIEND)) { +- struct cec_msg msg = {}; +- unsigned int i; +- unsigned int v; +- +- v = i2c_rd32(sd, CECRCTR); +- msg.len = v & 0x1f; +- for (i = 0; i < msg.len; i++) { +- v = i2c_rd32(sd, CECRBUF1 + i * 4); +- msg.msg[i] = v & 0xff; +- } +- cec_received_msg(state->cec_adap, &msg); +- if (handled) +- *handled = true; +- } +- i2c_wr16(sd, INTSTATUS, +- intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); ++ i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); + } + +-#endif ++static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); ++ u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; + +-/* --------------- IRQ --------------- */ ++ i2c_wr8(sd, MISC_INT, misc_int); + +-static void tc358743_format_change(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_dv_timings timings; +- const struct v4l2_event tc358743_ev_fmt = { +- .type = V4L2_EVENT_SOURCE_CHANGE, +- .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +- }; ++ v4l2_info(sd, "%s: MISC_INT =0x%02x\n", __func__, misc_int); + +- if (tc358743_get_detected_timings(sd, &timings)) { +- enable_stream(sd, false); ++ if (misc_int & MASK_I_SYNC_CHG) { ++ /* Reset the HDMI PHY to try to trigger proper lock on the ++ * incoming video format. Erase BKSV to prevent that old keys ++ * are used when a new source is connected. */ ++ if (no_sync(sd) || no_signal(sd)) { ++ tc358743_reset_phy(sd); ++ tc358743_erase_bksv(sd); ++ } + +- v4l2_dbg(1, debug, sd, "%s: No signal\n", +- __func__); +- } else { +- if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false)) +- enable_stream(sd, false); ++ tc358743_format_change(sd); + +- if (debug) +- v4l2_print_dv_timings(sd->name, +- "tc358743_format_change: New format: ", +- &timings, false); +- } ++ misc_int &= ~MASK_I_SYNC_CHG; ++ if (handled) *handled = true; ++ } + +- if (sd->devnode) +- v4l2_subdev_notify_event(sd, &tc358743_ev_fmt); ++ if (misc_int) { ++ v4l2_err(sd, "%s: Unhandled MISC_INT interrupts:0x%02x\n", __func__, ++ misc_int); ++ } + } + +-static void tc358743_init_interrupts(struct v4l2_subdev *sd) +-{ +- u16 i; ++static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); ++ u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; + +- /* clear interrupt status registers */ +- for (i = SYS_INT; i <= KEY_INT; i++) +- i2c_wr8(sd, i, 0xff); ++ i2c_wr8(sd, CBIT_INT, cbit_int); + +- i2c_wr16(sd, INTSTATUS, 0xffff); +-} ++ v4l2_info(sd, "%s: CBIT_INT =0x%02x\n", __func__, cbit_int); + +-static void tc358743_enable_interrupts(struct v4l2_subdev *sd, +- bool cable_connected) +-{ +- v4l2_dbg(2, debug, sd, "%s: cable connected = %d\n", __func__, +- cable_connected); +- +- if (cable_connected) { +- i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_DVI_DET | +- MASK_M_HDMI_DET) & 0xff); +- i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); +- i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | +- MASK_M_AF_UNLOCK) & 0xff); +- i2c_wr8(sd, AUDIO_INTM, ~MASK_M_BUFINIT_END); +- i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); +- } else { +- i2c_wr8(sd, SYS_INTM, ~MASK_M_DDC & 0xff); +- i2c_wr8(sd, CLK_INTM, 0xff); +- i2c_wr8(sd, CBIT_INTM, 0xff); +- i2c_wr8(sd, AUDIO_INTM, 0xff); +- i2c_wr8(sd, MISC_INTM, 0xff); +- } +-} ++ if (cbit_int & MASK_I_CBIT_FS) { ++ v4l2_info(sd, "%s: Audio sample rate changed\n", __func__); ++ tc358743_s_ctrl_audio_sampling_rate(sd); + +-static void tc358743_hdmi_audio_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 audio_int_mask = i2c_rd8(sd, AUDIO_INTM); +- u8 audio_int = i2c_rd8(sd, AUDIO_INT) & ~audio_int_mask; ++ cbit_int &= ~MASK_I_CBIT_FS; ++ if (handled) *handled = true; ++ } + +- i2c_wr8(sd, AUDIO_INT, audio_int); ++ if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { ++ v4l2_info(sd, "%s: Audio present changed\n", __func__); ++ tc358743_s_ctrl_audio_present(sd); + +- v4l2_dbg(3, debug, sd, "%s: AUDIO_INT = 0x%02x\n", __func__, audio_int); ++ cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); ++ if (handled) *handled = true; ++ } + +- tc358743_s_ctrl_audio_sampling_rate(sd); +- tc358743_s_ctrl_audio_present(sd); ++ if (cbit_int) { ++ v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts:0x%02x\n", __func__, ++ cbit_int); ++ } + } + +-static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); ++static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); ++ u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; ++ ++ /* Bit 7 and bit 6 are set even when they are masked */ ++ i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); ++ ++ v4l2_info(sd, "%s: CLK_INT =0x%02x\n", __func__, clk_int); ++ ++ if (clk_int & (MASK_I_IN_DE_CHG)) { ++ v4l2_info(sd, "%s: DE size or position has changed\n", __func__); + +- i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); ++ /* If the source switch to a new resolution with the same pixel ++ * frequency as the existing (e.g. 1080p25 -> 720p50), the ++ * I_SYNC_CHG interrupt is not always triggered, while the ++ * I_IN_DE_CHG interrupt seems to work fine. Format change ++ * notifications are only sent when the signal is stable to ++ * reduce the number of notifications. */ ++ if (!no_signal(sd) && !no_sync(sd)) tc358743_format_change(sd); ++ ++ clk_int &= ~(MASK_I_IN_DE_CHG); ++ if (handled) *handled = true; ++ } ++ ++ if (clk_int) { ++ v4l2_err(sd, "%s: Unhandled CLK_INT interrupts:0x%02x\n", __func__, ++ clk_int); ++ } + } + +-static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); +- u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; ++static void tc358743_enable_edid(struct v4l2_subdev *sd) { ++ struct tc358743_state *state = to_state(sd); ++ ++ v4l2_info(sd, "%s\n", __func__); ++ if (state->edid_blocks_written == 0) { ++ v4l2_info(sd, "%s: no EDID -> no hotplug\n", __func__); ++ return; ++ } ++ ++ v4l2_info(sd, "%s:\n", __func__); + +- i2c_wr8(sd, MISC_INT, misc_int); ++ /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when ++ * hotplug is enabled. See register DDC_CTL */ ++ queue_delayed_work(state->work_queues, &state->delayed_work_enable_hotplug, ++ HZ / 10); + +- v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); ++ tc358743_enable_interrupts(sd, true); ++ tc358743_s_ctrl_detect_tx_5v(sd); ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++} + +- if (misc_int & MASK_I_SYNC_CHG) { +- /* Reset the HDMI PHY to try to trigger proper lock on the +- * incoming video format. Erase BKSV to prevent that old keys +- * are used when a new source is connected. */ +- if (no_sync(sd) || no_signal(sd)) { +- tc358743_reset_phy(sd); +- tc358743_erase_bksv(sd); +- } ++static void tc358743_delayed_work_enable_interrupt(struct work_struct *work) { ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct tc358743_state *state = ++ container_of(dwork, struct tc358743_state, delayed_work_enable_interrupt); ++ struct v4l2_subdev *sd = &state->sd; + +- tc358743_format_change(sd); ++ v4l2_dbg(2, debug, sd, "%s:\n", __func__); + +- misc_int &= ~MASK_I_SYNC_CHG; +- if (handled) +- *handled = true; +- } ++ tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); + +- if (misc_int) { +- v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", +- __func__, misc_int); +- } ++ // /* Temporary EDID. Should be set by userspace */ ++ // tc358743_s_edid(sd, &sd_edid); + } + +-static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, +- bool *handled) +-{ +- u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); +- u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; ++static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, ++ bool *handled) { ++ struct tc358743_state *state = to_state(sd); ++ u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); ++ u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; + +- i2c_wr8(sd, CBIT_INT, cbit_int); ++ i2c_wr8(sd, SYS_INT, sys_int); + +- v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); ++ v4l2_info(sd, "%s: SYS_INT =0x%02x\n", __func__, sys_int); + +- if (cbit_int & MASK_I_CBIT_FS) { ++ if (sys_int & MASK_I_DDC) { ++ bool tx_5v = tx_5v_power_present(sd); + +- v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", +- __func__); +- tc358743_s_ctrl_audio_sampling_rate(sd); ++ v4l2_info(sd, "%s: Tx 5V power present: %s\n", __func__, ++ tx_5v ? "yes" : "no"); + +- cbit_int &= ~MASK_I_CBIT_FS; +- if (handled) +- *handled = true; +- } ++ if (tx_5v) { ++ tc358743_enable_edid(sd); ++ } else { ++ tc358743_enable_interrupts(sd, false); ++ tc358743_disable_edid(sd); ++ memset(&state->timings, 0, sizeof(state->timings)); ++ tc358743_erase_bksv(sd); ++ tc358743_update_controls(sd); ++ } + +- if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { ++ sys_int &= ~MASK_I_DDC; ++ if (handled) *handled = true; ++ } + +- v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", +- __func__); +- tc358743_s_ctrl_audio_present(sd); ++ if (sys_int & MASK_I_DVI) { ++ v4l2_info(sd, "%s: HDMI->DVI change detected\n", __func__); + +- cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); +- if (handled) +- *handled = true; +- } ++ /* Reset the HDMI PHY to try to trigger proper lock on the ++ * incoming video format. Erase BKSV to prevent that old keys ++ * are used when a new source is connected. */ ++ if (no_sync(sd) || no_signal(sd)) { ++ tc358743_reset_phy(sd); ++ tc358743_erase_bksv(sd); ++ } + +- if (cbit_int) { +- v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", +- __func__, cbit_int); +- } +-} ++ sys_int &= ~MASK_I_DVI; ++ if (handled) *handled = true; ++ } + +-static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); +- u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; ++ if (sys_int & MASK_I_HDMI) { ++ v4l2_info(sd, "%s: DVI->HDMI change detected\n", __func__); + +- /* Bit 7 and bit 6 are set even when they are masked */ +- i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); ++ /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ ++ i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); + +- v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); ++ sys_int &= ~MASK_I_HDMI; ++ if (handled) *handled = true; ++ } + +- if (clk_int & (MASK_I_IN_DE_CHG)) { +- +- v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", +- __func__); +- +- /* If the source switch to a new resolution with the same pixel +- * frequency as the existing (e.g. 1080p25 -> 720p50), the +- * I_SYNC_CHG interrupt is not always triggered, while the +- * I_IN_DE_CHG interrupt seems to work fine. Format change +- * notifications are only sent when the signal is stable to +- * reduce the number of notifications. */ +- if (!no_signal(sd) && !no_sync(sd)) +- tc358743_format_change(sd); +- +- clk_int &= ~(MASK_I_IN_DE_CHG); +- if (handled) +- *handled = true; +- } +- +- if (clk_int) { +- v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", +- __func__, clk_int); +- } +-} +- +-static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) +-{ +- struct tc358743_state *state = to_state(sd); +- u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); +- u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; +- +- i2c_wr8(sd, SYS_INT, sys_int); +- +- v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); +- +- if (sys_int & MASK_I_DDC) { +- bool tx_5v = tx_5v_power_present(sd); +- +- v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", +- __func__, tx_5v ? "yes" : "no"); +- +- if (tx_5v) { +- tc358743_enable_edid(sd); +- } else { +- tc358743_enable_interrupts(sd, false); +- tc358743_disable_edid(sd); +- memset(&state->timings, 0, sizeof(state->timings)); +- tc358743_erase_bksv(sd); +- tc358743_update_controls(sd); +- } +- +- sys_int &= ~MASK_I_DDC; +- if (handled) +- *handled = true; +- } +- +- if (sys_int & MASK_I_DVI) { +- v4l2_dbg(1, debug, sd, "%s: HDMI->DVI change detected\n", +- __func__); +- +- /* Reset the HDMI PHY to try to trigger proper lock on the +- * incoming video format. Erase BKSV to prevent that old keys +- * are used when a new source is connected. */ +- if (no_sync(sd) || no_signal(sd)) { +- tc358743_reset_phy(sd); +- tc358743_erase_bksv(sd); +- } +- +- sys_int &= ~MASK_I_DVI; +- if (handled) +- *handled = true; +- } +- +- if (sys_int & MASK_I_HDMI) { +- v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", +- __func__); +- +- /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ +- i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); +- +- sys_int &= ~MASK_I_HDMI; +- if (handled) +- *handled = true; +- } +- +- if (sys_int) { +- v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", +- __func__, sys_int); +- } +-} +- +-/* --------------- CORE OPS --------------- */ +- +-static int tc358743_log_status(struct v4l2_subdev *sd) +-{ +- struct tc358743_state *state = to_state(sd); +- struct v4l2_dv_timings timings; +- uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); +- uint16_t sysctl = i2c_rd16(sd, SYSCTL); +- u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); +- const int deep_color_mode[4] = { 8, 10, 12, 16 }; +- static const char * const input_color_space[] = { +- "RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)", +- "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", +- "NA(10)", "NA(11)", "NA(12)", "opYCC 601"}; +- +- v4l2_info(sd, "-----Chip status-----\n"); +- v4l2_info(sd, "Chip ID: 0x%02x\n", +- (i2c_rd16(sd, CHIPID) & MASK_CHIPID) >> 8); +- v4l2_info(sd, "Chip revision: 0x%02x\n", +- i2c_rd16(sd, CHIPID) & MASK_REVID); +- v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", +- !!(sysctl & MASK_IRRST), +- !!(sysctl & MASK_CECRST), +- !!(sysctl & MASK_CTXRST), +- !!(sysctl & MASK_HDMIRST)); +- v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); +- v4l2_info(sd, "Cable detected (+5V power): %s\n", +- hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); +- v4l2_info(sd, "DDC lines enabled: %s\n", +- (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? +- "yes" : "no"); +- v4l2_info(sd, "Hotplug enabled: %s\n", +- (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? +- "yes" : "no"); +- v4l2_info(sd, "CEC enabled: %s\n", +- (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); +- v4l2_info(sd, "-----Signal status-----\n"); +- v4l2_info(sd, "TMDS signal detected: %s\n", +- hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); +- v4l2_info(sd, "Stable sync signal: %s\n", +- hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); +- v4l2_info(sd, "PHY PLL locked: %s\n", +- hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); +- v4l2_info(sd, "PHY DE detected: %s\n", +- hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); +- +- if (tc358743_get_detected_timings(sd, &timings)) { +- v4l2_info(sd, "No video detected\n"); +- } else { +- v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, +- true); +- } +- v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, +- true); +- +- v4l2_info(sd, "-----CSI-TX status-----\n"); +- v4l2_info(sd, "Lanes needed: %d\n", +- tc358743_num_csi_lanes_needed(sd)); +- v4l2_info(sd, "Lanes in use: %d\n", +- state->csi_lanes_in_use); +- v4l2_info(sd, "Waiting for particular sync signal: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? +- "yes" : "no"); +- v4l2_info(sd, "Transmit mode: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? +- "yes" : "no"); +- v4l2_info(sd, "Receive mode: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? +- "yes" : "no"); +- v4l2_info(sd, "Stopped: %s\n", +- (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? +- "yes" : "no"); +- v4l2_info(sd, "Color space: %s\n", +- state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? +- "YCbCr 422 16-bit" : +- state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? +- "RGB 888 24-bit" : "Unsupported"); +- +- v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); +- v4l2_info(sd, "HDCP encrypted content: %s\n", +- hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); +- v4l2_info(sd, "Input color space: %s %s range\n", +- input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], +- (vi_status3 & MASK_LIMITED) ? "limited" : "full"); +- if (!is_hdmi(sd)) +- return 0; +- v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : +- "off"); +- v4l2_info(sd, "Deep color mode: %d-bits per channel\n", +- deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & +- MASK_S_DEEPCOLOR) >> 2]); +- print_avi_infoframe(sd); +- +- return 0; ++ if (sys_int) { ++ v4l2_err(sd, "%s: Unhandled SYS_INT interrupts:0x%02x\n", __func__, ++ sys_int); ++ } + } + + #ifdef CONFIG_VIDEO_ADV_DEBUG +-static void tc358743_print_register_map(struct v4l2_subdev *sd) +-{ +- v4l2_info(sd, "0x0000-0x00FF: Global Control Register\n"); +- v4l2_info(sd, "0x0100-0x01FF: CSI2-TX PHY Register\n"); +- v4l2_info(sd, "0x0200-0x03FF: CSI2-TX PPI Register\n"); +- v4l2_info(sd, "0x0400-0x05FF: Reserved\n"); +- v4l2_info(sd, "0x0600-0x06FF: CEC Register\n"); +- v4l2_info(sd, "0x0700-0x84FF: Reserved\n"); +- v4l2_info(sd, "0x8500-0x85FF: HDMIRX System Control Register\n"); +- v4l2_info(sd, "0x8600-0x86FF: HDMIRX Audio Control Register\n"); +- v4l2_info(sd, "0x8700-0x87FF: HDMIRX InfoFrame packet data Register\n"); +- v4l2_info(sd, "0x8800-0x88FF: HDMIRX HDCP Port Register\n"); +- v4l2_info(sd, "0x8900-0x89FF: HDMIRX Video Output Port & 3D Register\n"); +- v4l2_info(sd, "0x8A00-0x8BFF: Reserved\n"); +- v4l2_info(sd, "0x8C00-0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); +- v4l2_info(sd, "0x9000-0x90FF: HDMIRX GBD Extraction Control\n"); +- v4l2_info(sd, "0x9100-0x92FF: HDMIRX GBD RAM read\n"); +- v4l2_info(sd, "0x9300- : Reserved\n"); +-} +- +-static int tc358743_get_reg_size(u16 address) +-{ +- /* REF_01 p. 66-72 */ +- if (address <= 0x00ff) +- return 2; +- else if ((address >= 0x0100) && (address <= 0x06FF)) +- return 4; +- else if ((address >= 0x0700) && (address <= 0x84ff)) +- return 2; +- else +- return 1; ++static void tc358743_print_register_map(struct v4l2_subdev *sd) { ++ v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); ++ v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); ++ v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); ++ v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); ++ v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); ++ v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); ++ v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); ++ v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); ++ v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); ++ v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); ++ v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); ++ v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); ++ v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); ++ v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); ++ v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); ++ v4l2_info(sd, "0x9300- : Reserved\n"); ++} ++ ++static int tc358743_get_reg_size(u16 address) { ++ /* REF_01 p. 66-72 */ ++ if (address <= 0x00ff) ++ return 2; ++ else if ((address >= 0x0100) && (address <= 0x06FF)) ++ return 4; ++ else if ((address >= 0x0700) && (address <= 0x84ff)) ++ return 2; ++ else ++ return 1; + } + + static int tc358743_g_register(struct v4l2_subdev *sd, +- struct v4l2_dbg_register *reg) +-{ +- if (reg->reg > 0xffff) { +- tc358743_print_register_map(sd); +- return -EINVAL; +- } ++ struct v4l2_dbg_register *reg) { ++ if (reg->reg > 0xffff) { ++ tc358743_print_register_map(sd); ++ return -EINVAL; ++ } + +- reg->size = tc358743_get_reg_size(reg->reg); ++ reg->size = tc358743_get_reg_size(reg->reg); + +- reg->val = i2c_rdreg(sd, reg->reg, reg->size); ++ i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + +- return 0; ++ return 0; + } + + static int tc358743_s_register(struct v4l2_subdev *sd, +- const struct v4l2_dbg_register *reg) +-{ +- if (reg->reg > 0xffff) { +- tc358743_print_register_map(sd); +- return -EINVAL; +- } +- +- /* It should not be possible for the user to enable HDCP with a simple +- * v4l2-dbg command. +- * +- * DO NOT REMOVE THIS unless all other issues with HDCP have been +- * resolved. +- */ +- if (reg->reg == HDCP_MODE || +- reg->reg == HDCP_REG1 || +- reg->reg == HDCP_REG2 || +- reg->reg == HDCP_REG3 || +- reg->reg == BCAPS) +- return 0; +- +- i2c_wrreg(sd, (u16)reg->reg, reg->val, +- tc358743_get_reg_size(reg->reg)); +- +- return 0; ++ const struct v4l2_dbg_register *reg) { ++ if (reg->reg > 0xffff) { ++ tc358743_print_register_map(sd); ++ return -EINVAL; ++ } ++ ++ /* It should not be possible for the user to enable HDCP with a simple ++ * v4l2-dbg command. ++ * ++ * DO NOT REMOVE THIS unless all other issues with HDCP have been ++ * resolved. ++ */ ++ if (reg->reg == HDCP_MODE || reg->reg == HDCP_REG1 || reg->reg == HDCP_REG2 || ++ reg->reg == HDCP_REG3 || reg->reg == BCAPS) ++ return 0; ++ ++ i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, tc358743_get_reg_size(reg->reg)); ++ ++ return 0; + } + #endif + +-static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +-{ +- u16 intstatus = i2c_rd16(sd, INTSTATUS); +- +- v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); +- +- if (intstatus & MASK_HDMI_INT) { +- u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); +- u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); +- +- if (hdmi_int0 & MASK_I_MISC) +- tc358743_hdmi_misc_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_CBIT) +- tc358743_hdmi_cbit_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_CLK) +- tc358743_hdmi_clk_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_SYS) +- tc358743_hdmi_sys_int_handler(sd, handled); +- if (hdmi_int1 & MASK_I_AUD) +- tc358743_hdmi_audio_int_handler(sd, handled); +- +- i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); +- intstatus &= ~MASK_HDMI_INT; +- } +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +- if (intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)) { +- tc358743_cec_handler(sd, intstatus, handled); +- i2c_wr16(sd, INTSTATUS, +- intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); +- intstatus &= ~(MASK_CEC_RINT | MASK_CEC_TINT); +- } +-#endif ++static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { ++ u16 intstatus = i2c_rd16(sd, INTSTATUS); + +- if (intstatus & MASK_CSI_INT) { +- u32 csi_int = i2c_rd32(sd, CSI_INT); ++ if (intstatus & MASK_HDMI_INT) { ++ u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); ++ u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); + +- if (csi_int & MASK_INTER) +- tc358743_csi_err_int_handler(sd, handled); ++ if (hdmi_int0 & MASK_I_MISC) tc358743_hdmi_misc_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_CBIT) tc358743_hdmi_cbit_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_CLK) tc358743_hdmi_clk_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_SYS) tc358743_hdmi_sys_int_handler(sd, handled); ++ if (hdmi_int1 & MASK_I_AUD) tc358743_hdmi_audio_int_handler(sd, handled); + +- i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); +- } ++ i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); ++ intstatus &= ~MASK_HDMI_INT; ++ } + +- intstatus = i2c_rd16(sd, INTSTATUS); +- if (intstatus) { +- v4l2_dbg(1, debug, sd, +- "%s: Unhandled IntStatus interrupts: 0x%02x\n", +- __func__, intstatus); +- } ++ if (intstatus & MASK_CSI_INT) { ++ u32 csi_int = i2c_rd32(sd, CSI_INT); + +- return 0; +-} ++ if (csi_int & MASK_INTER) tc358743_csi_err_int_handler(sd, handled); + +-static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) +-{ +- struct tc358743_state *state = dev_id; +- bool handled = false; ++ i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); ++ intstatus &= ~MASK_CSI_INT; ++ } + +- tc358743_isr(&state->sd, 0, &handled); ++ intstatus = i2c_rd16(sd, INTSTATUS); ++ if (intstatus) { ++ v4l2_info(sd, "%s: Unhandled IntStatus interrupts:0x%02x\n", __func__, ++ intstatus); ++ } + +- return handled ? IRQ_HANDLED : IRQ_NONE; ++ return 0; + } + +-static void tc358743_irq_poll_timer(struct timer_list *t) +-{ +- struct tc358743_state *state = from_timer(state, t, timer); +- unsigned int msecs; ++static void tc358743_process_isr(struct work_struct *work) { ++ struct tc358743_state *state = ++ container_of(work, struct tc358743_state, process_isr); ++ struct v4l2_subdev *sd = &state->sd; ++ bool handled; ++ ++ v4l2_dbg(2, debug, sd, "%s:\n", __func__); + +- schedule_work(&state->work_i2c_poll); +- /* +- * If CEC is present, then we need to poll more frequently, +- * otherwise we will miss CEC messages. +- */ +- msecs = state->cec_adap ? POLL_INTERVAL_CEC_MS : POLL_INTERVAL_MS; +- mod_timer(&state->timer, jiffies + msecs_to_jiffies(msecs)); ++ mutex_lock(&state->isr_lock); ++ tc358743_isr(sd, 0, &handled); ++ mutex_unlock(&state->isr_lock); + } + +-static void tc358743_work_i2c_poll(struct work_struct *work) +-{ +- struct tc358743_state *state = container_of(work, +- struct tc358743_state, work_i2c_poll); +- bool handled; ++static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) { ++ struct tc358743_state *state = dev_id; + +- tc358743_isr(&state->sd, 0, &handled); ++ queue_work(state->work_queues, &state->process_isr); ++ ++ return IRQ_HANDLED; + } + + static int tc358743_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, +- struct v4l2_event_subscription *sub) +-{ +- switch (sub->type) { +- case V4L2_EVENT_SOURCE_CHANGE: +- return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); +- case V4L2_EVENT_CTRL: +- return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); +- default: +- return -EINVAL; +- } ++ struct v4l2_event_subscription *sub) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); ++ case V4L2_EVENT_CTRL: ++ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); ++ default: ++ return -EINVAL; ++ } + } + + /* --------------- VIDEO OPS --------------- */ + +-static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) +-{ +- *status = 0; +- *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; +- *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; +- +- v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); +- +- return 0; +-} +- + static int tc358743_s_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings *timings) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "1111 %s\n", __func__); ++ if (!timings) return -EINVAL; + +- if (!timings) +- return -EINVAL; ++ if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { ++ v4l2_info(sd, "%s: no change\n", __func__); ++ return 0; ++ } + +- if (debug) +- v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", +- timings, false); ++ if (!v4l2_valid_dv_timings(timings, &tc358743_timings_cap, NULL, NULL)) { ++ v4l2_err(sd, "%s: timings out of range\n", __func__); ++ return -ERANGE; ++ } + +- if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) { +- v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); +- return 0; +- } ++ state->timings = *timings; + +- if (!v4l2_valid_dv_timings(timings, +- &tc358743_timings_cap, NULL, NULL)) { +- v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); +- return -ERANGE; +- } ++ enable_stream(sd, false); ++ tc358743_set_pll(sd); ++ tc358743_set_csi(sd); ++ v4l2_info(sd, "2222 %s\n", __func__); ++ return 0; ++} + +- state->timings = *timings; ++static int tc358743_g_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) { ++ struct tc358743_state *state = to_state(sd); ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (state) { ++ *timings = state->timings; ++ return 0; ++ } ++ return -EINVAL; ++} + +- enable_stream(sd, false); +- tc358743_set_pll(sd); +- tc358743_set_csi(sd); ++static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_enum_dv_timings *timings) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (timings && timings->pad != 0) { ++ v4l2_err(sd, "%s: failed %d\n", __func__, EINVAL); ++ return -EINVAL; ++ } + +- return 0; ++ return v4l2_enum_dv_timings_cap(timings, &tc358743_timings_cap, NULL, NULL); + } + +-static int tc358743_g_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- struct tc358743_state *state = to_state(sd); ++static int tc358743_query_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) { ++ int ret; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); + +- *timings = state->timings; ++ ret = tc358743_get_detected_timings(sd, timings); ++ if (ret) { ++ v4l2_err(sd, "%s: @@@@@ timings detected error\n", __func__); ++ return ret; ++ } + +- return 0; +-} ++ if (debug) ++ v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", timings, ++ false); + +-static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_enum_dv_timings *timings) +-{ +- if (timings->pad != 0) +- return -EINVAL; ++ if (!v4l2_valid_dv_timings(timings, &tc358743_timings_cap, NULL, NULL)) { ++ v4l2_err(sd, "%s: @@@@@ timings out of range\n", __func__); ++ return -ERANGE; ++ } + +- return v4l2_enum_dv_timings_cap(timings, +- &tc358743_timings_cap, NULL, NULL); ++ return 0; + } + +-static int tc358743_query_dv_timings(struct v4l2_subdev *sd, +- struct v4l2_dv_timings *timings) +-{ +- int ret; ++static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) { ++ struct tc358743_state *state = to_state(sd); ++ struct v4l2_dv_timings *timings = &(state->timings); + +- ret = tc358743_get_detected_timings(sd, timings); +- if (ret) +- return ret; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ *status = 0; ++ *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; ++ *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; + +- if (debug) +- v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", +- timings, false); ++ v4l2_info(sd, "%s: status =0x%x\n", __func__, *status); + +- if (!v4l2_valid_dv_timings(timings, +- &tc358743_timings_cap, NULL, NULL)) { +- v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); +- return -ERANGE; +- } ++ v4l2_info(sd, "Now getting and setting dv timings"); ++ tc358743_query_dv_timings(sd, timings); ++ tc358743_s_dv_timings(sd, timings); + +- return 0; ++ return 0; + } + + static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, +- struct v4l2_dv_timings_cap *cap) +-{ +- if (cap->pad != 0) +- return -EINVAL; ++ struct v4l2_dv_timings_cap *cap) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ if (cap && cap->pad != 0) return -EINVAL; + +- *cap = tc358743_timings_cap; ++ *cap = tc358743_timings_cap; + +- return 0; ++ return 0; + } + +-static int tc358743_get_mbus_config(struct v4l2_subdev *sd, +- unsigned int pad, +- struct v4l2_mbus_config *cfg) +-{ +- struct tc358743_state *state = to_state(sd); ++static int tc358743_g_mbus_config(struct v4l2_subdev *sd, ++ unsigned int pad, ++ struct v4l2_mbus_config *cfg) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++// cfg->type = V4L2_MBUS_CSI2; ++ cfg->type = V4L2_MBUS_CSI2_DPHY; + +- cfg->type = V4L2_MBUS_CSI2_DPHY; ++ /* Support for non-continuous CSI-2 clock is missing in the driver */ ++ cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + +- /* Support for non-continuous CSI-2 clock is missing in the driver */ +- cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ switch (tc358743_num_csi_lanes_in_use(sd)) { ++ case 1: ++ cfg->flags |= V4L2_MBUS_CSI2_1_LANE; ++ break; ++ case 2: ++ cfg->flags |= V4L2_MBUS_CSI2_2_LANE; ++ break; ++ case 3: ++ cfg->flags |= V4L2_MBUS_CSI2_3_LANE; ++ break; ++ case 4: ++ cfg->flags |= V4L2_MBUS_CSI2_4_LANE; ++ break; ++ default: ++ return -EINVAL; ++ } + +- switch (state->csi_lanes_in_use) { +- case 1: +- cfg->flags |= V4L2_MBUS_CSI2_1_LANE; +- break; +- case 2: +- cfg->flags |= V4L2_MBUS_CSI2_2_LANE; +- break; +- case 3: +- cfg->flags |= V4L2_MBUS_CSI2_3_LANE; +- break; +- case 4: +- cfg->flags |= V4L2_MBUS_CSI2_4_LANE; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; ++ return 0; + } + +-static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +-{ +- enable_stream(sd, enable); +- if (!enable) { +- /* Put all lanes in LP-11 state (STOPSTATE) */ +- tc358743_set_csi(sd); +- } +- +- return 0; ++static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) { ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ enable_stream(sd, true); ++ return 0; + } + + /* --------------- PAD OPS --------------- */ + +-static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_mbus_code_enum *code) +-{ +- switch (code->index) { +- case 0: +- code->code = MEDIA_BUS_FMT_RGB888_1X24; +- break; +- case 1: +- code->code = MEDIA_BUS_FMT_UYVY8_1X16; +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- + static int tc358743_get_fmt(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_format *format) +-{ +- struct tc358743_state *state = to_state(sd); +- u8 vi_rep = i2c_rd8(sd, VI_REP); +- +- if (format->pad != 0) +- return -EINVAL; +- +- format->format.code = state->mbus_fmt_code; +- format->format.width = state->timings.bt.width; +- format->format.height = state->timings.bt.height; +- format->format.field = V4L2_FIELD_NONE; +- +- switch (vi_rep & MASK_VOUT_COLOR_SEL) { +- case MASK_VOUT_COLOR_RGB_FULL: +- case MASK_VOUT_COLOR_RGB_LIMITED: +- format->format.colorspace = V4L2_COLORSPACE_SRGB; +- break; +- case MASK_VOUT_COLOR_601_YCBCR_LIMITED: +- case MASK_VOUT_COLOR_601_YCBCR_FULL: +- format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; +- break; +- case MASK_VOUT_COLOR_709_YCBCR_FULL: +- case MASK_VOUT_COLOR_709_YCBCR_LIMITED: +- format->format.colorspace = V4L2_COLORSPACE_REC709; +- break; +- default: +- format->format.colorspace = 0; +- break; +- } +- +- return 0; ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) { ++ struct tc358743_state *state = to_state(sd); ++ u8 vi_rep = i2c_rd8(sd, VI_REP); ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); ++ ++ if (format->pad != 0) { ++ v4l2_err(sd, "%s Error\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ format->format.code = state->mbus_fmt_code; ++ format->format.width = state->timings.bt.width; ++ format->format.height = state->timings.bt.height; ++ format->format.field = V4L2_FIELD_NONE; ++ ++ switch (vi_rep & MASK_VOUT_COLOR_SEL) { ++ case MASK_VOUT_COLOR_RGB_FULL: ++ case MASK_VOUT_COLOR_RGB_LIMITED: ++ format->format.colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ case MASK_VOUT_COLOR_601_YCBCR_LIMITED: ++ case MASK_VOUT_COLOR_601_YCBCR_FULL: ++ v4l2_info(sd, "Here 6b, colorspace: %d\n", V4L2_COLORSPACE_SMPTE170M); ++ format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ break; ++ case MASK_VOUT_COLOR_709_YCBCR_FULL: ++ case MASK_VOUT_COLOR_709_YCBCR_LIMITED: ++ format->format.colorspace = V4L2_COLORSPACE_REC709; ++ break; ++ default: ++ format->format.colorspace = 0; ++ v4l2_info(sd, "%d:%s colorspace = 0\n", __LINE__, __FUNCTION__); ++ break; ++ } ++ ++ v4l2_info(sd, "get fmt complete\n"); ++ v4l2_info(sd, "format width %d\n", format->format.width); ++ v4l2_info(sd, "format height %d\n", format->format.height); ++ ++ v4l2_info(sd, "fmt_code: %d\n", format->format.code); ++ v4l2_info(sd, "RGB888 code: %d\n", MEDIA_BUS_FMT_RGB888_1X24); ++ v4l2_info(sd, "UYVY8 code: %d\n", MEDIA_BUS_FMT_UYVY8_1X16); ++ return 0; + } + + static int tc358743_set_fmt(struct v4l2_subdev *sd, +- struct v4l2_subdev_pad_config *cfg, +- struct v4l2_subdev_format *format) +-{ +- struct tc358743_state *state = to_state(sd); ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) { ++ struct tc358743_state *state = to_state(sd); ++ u32 code = format->format.code; ++ int ret = tc358743_get_fmt(sd, cfg, format); + +- u32 code = format->format.code; /* is overwritten by get_fmt */ +- int ret = tc358743_get_fmt(sd, cfg, format); ++ v4l2_dbg(3, debug, sd, "%s(), ret: %d\n", __func__, ret); ++ v4l2_dbg(3, debug, sd, "Set format code: %d\n", code); + +- format->format.code = code; ++ format->format.code = code; + +- if (ret) +- return ret; ++ if (ret) return ret; + +- switch (code) { +- case MEDIA_BUS_FMT_RGB888_1X24: +- case MEDIA_BUS_FMT_UYVY8_1X16: +- break; +- default: +- return -EINVAL; +- } ++ switch (code) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ v4l2_dbg(3, debug, sd, "Good code %d\n", code); ++ break; ++ default: ++ v4l2_err(sd, "Bad code %d\n", code); ++ return -EINVAL; ++ } + +- if (format->which == V4L2_SUBDEV_FORMAT_TRY) +- return 0; ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; + +- state->mbus_fmt_code = format->format.code; ++ state->mbus_fmt_code = format->format.code; + +- enable_stream(sd, false); +- tc358743_set_pll(sd); +- tc358743_set_csi(sd); +- tc358743_set_csi_color_space(sd); +- +- return 0; ++ enable_stream(sd, false); ++ tc358743_set_pll(sd); ++ tc358743_set_csi(sd); ++ tc358743_set_csi_color_space(sd); ++ v4l2_info(sd, "Called %s, completed successfully\n", __FUNCTION__); ++ return 0; + } + + static int tc358743_g_edid(struct v4l2_subdev *sd, +- struct v4l2_subdev_edid *edid) +-{ +- struct tc358743_state *state = to_state(sd); +- +- memset(edid->reserved, 0, sizeof(edid->reserved)); ++ struct v4l2_subdev_edid *edid) { ++ struct tc358743_state *state = to_state(sd); ++ // int i=0; ++ v4l2_info(sd, "Calling %s\n", __FUNCTION__); + +- if (edid->pad != 0) +- return -EINVAL; ++ if (edid->pad != 0) return -EINVAL; + +- if (edid->start_block == 0 && edid->blocks == 0) { +- edid->blocks = state->edid_blocks_written; +- return 0; +- } ++ if (edid->start_block == 0 && edid->blocks == 0) { ++ edid->blocks = state->edid_blocks_written; ++ return 0; ++ } + +- if (state->edid_blocks_written == 0) +- return -ENODATA; ++ if (state->edid_blocks_written == 0) return -ENODATA; + +- if (edid->start_block >= state->edid_blocks_written || +- edid->blocks == 0) +- return -EINVAL; ++ if (edid->start_block >= state->edid_blocks_written || edid->blocks == 0) ++ return -EINVAL; + +- if (edid->start_block + edid->blocks > state->edid_blocks_written) +- edid->blocks = state->edid_blocks_written - edid->start_block; ++ if (edid->start_block + edid->blocks > state->edid_blocks_written) ++ edid->blocks = state->edid_blocks_written - edid->start_block; + +- i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, +- edid->blocks * EDID_BLOCK_SIZE); +- +- return 0; ++ i2c_rd(sd, EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), edid->edid, ++ edid->blocks * EDID_BLOCK_SIZE); ++ v4l2_info(sd, "EDID_RAM has %d byte from: 0x%04x to 0x%04x \r\n", ++ edid->blocks * EDID_BLOCK_SIZE, ++ EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE), ++ EDID_RAM + (edid->start_block * EDID_BLOCK_SIZE) + ++ edid->blocks * EDID_BLOCK_SIZE); ++ // for(i=0;iblocks * EDID_BLOCK_SIZE;i++){ ++ // printk("%02x ",edid->edid[i]); ++ // } ++ // v4l2_info(sd,"\r\n"); ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++ return 0; + } + + static int tc358743_s_edid(struct v4l2_subdev *sd, +- struct v4l2_subdev_edid *edid) +-{ +- struct tc358743_state *state = to_state(sd); +- u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; +- u16 pa; +- int err; +- int i; +- +- v4l2_dbg(2, debug, sd, "%s, pad %d, start block %d, blocks %d\n", +- __func__, edid->pad, edid->start_block, edid->blocks); +- +- memset(edid->reserved, 0, sizeof(edid->reserved)); +- +- if (edid->pad != 0) +- return -EINVAL; +- +- if (edid->start_block != 0) +- return -EINVAL; +- +- if (edid->blocks > EDID_NUM_BLOCKS_MAX) { +- edid->blocks = EDID_NUM_BLOCKS_MAX; +- return -E2BIG; +- } +- pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); +- err = v4l2_phys_addr_validate(pa, &pa, NULL); +- if (err) +- return err; ++ struct v4l2_subdev_edid *edid) { ++ struct tc358743_state *state = to_state(sd); ++ u16 edid_len = edid->blocks * EDID_BLOCK_SIZE; ++ ++ v4l2_info(sd, "%s, pad %d, start block %d, blocks %d\n", __func__, edid->pad, ++ edid->start_block, edid->blocks); ++ ++ if (edid->pad != 0) return -EINVAL; ++ ++ if (edid->start_block != 0) return -EINVAL; ++ ++ if (edid->blocks > EDID_NUM_BLOCKS_MAX) { ++ edid->blocks = EDID_NUM_BLOCKS_MAX; ++ return -E2BIG; ++ } ++ ++ tc358743_disable_edid(sd); ++ ++ i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); ++ i2c_wr8(sd, EDID_LEN2, edid_len >> 8); ++ ++ if (edid->blocks == 0) { ++ state->edid_blocks_written = 0; ++ return 0; ++ } ++ i2c_wr(sd, EDID_RAM, edid->edid, edid_len); ++ /* richardyou ++ for (i=0; iedid[i]); ++ } ++ */ ++ state->edid_blocks_written = edid->blocks; ++ ++ // if (tx_5v_power_present(sd)) ++ tc358743_enable_edid(sd); ++ ++ v4l2_info(sd, "%s completed successfully", __FUNCTION__); ++ return 0; ++} ++ ++// static int tc358743_mbus_fmt(struct v4l2_subdev *sd, ++// struct v4l2_mbus_framefmt *mf) { ++// struct tc358743_state *state = to_state(sd); ++// u8 vi_rep = i2c_rd8(sd, VI_REP); ++ ++// mf->code = state->mbus_fmt_code; ++// mf->width = state->timings.bt.width; ++// mf->height = state->timings.bt.height; ++// mf->field = V4L2_FIELD_NONE; ++// switch (vi_rep & MASK_VOUT_COLOR_SEL) { ++// case MASK_VOUT_COLOR_RGB_FULL: ++// case MASK_VOUT_COLOR_RGB_LIMITED: ++// mf->colorspace = V4L2_COLORSPACE_SRGB; ++// break; ++// case MASK_VOUT_COLOR_601_YCBCR_LIMITED: ++// case MASK_VOUT_COLOR_601_YCBCR_FULL: ++// mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++// break; ++// case MASK_VOUT_COLOR_709_YCBCR_FULL: ++// case MASK_VOUT_COLOR_709_YCBCR_LIMITED: ++// mf->colorspace = V4L2_COLORSPACE_REC709; ++// break; ++// default: ++// mf->colorspace = 0; ++// break; ++// } ++// return 0; ++// } + +- cec_phys_addr_invalidate(state->cec_adap); +- +- tc358743_disable_edid(sd); ++static int tc358743_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) { ++ v4l2_info(sd, "%s()\n", __func__); ++ ++ // if (code->index >= 2) { ++ // v4l2_err(sd, "Error in %s\n", __FUNCTION__); ++ // return -EINVAL; ++ // } ++ ++ switch (code->index) { ++ case 0: ++ code->code = MEDIA_BUS_FMT_RGB888_1X24; ++ break; ++ case 1: ++ code->code = MEDIA_BUS_FMT_UYVY8_1X16; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Mbus code found succsefully (%d: %d)", code->index, ++ code->code); ++ ++ return 0; ++} ++ ++static int tc358743_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) { ++ const struct camera_common_frmfmt *frmfmt = tc358743_frmfmt; ++ int num_frmfmt = ARRAY_SIZE(tc358743_frmfmt); ++ ++ v4l2_info(sd, "%s()\n", __func__); ++ v4l2_info(sd, "fse->code %d, index %d\n", fse->code, fse->index); ++ v4l2_info(sd, "----------------------------------------\n"); ++ ++ // fse->min_width = fse->max_width = 1280; ++ // fse->min_height = fse->max_height = 720; ++ ++ v4l2_info(sd, ++ "Trying to find frmfmt that matches fse->code, code: %d (UYVY: %d, " ++ "ARGB32: %d, MEDIA_BUS_FMT_UYVY8_1X16: %d, " ++ "MEDIA_BUS_FMT_RGB888_1X24: %d)\n", ++ fse->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32, ++ MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_RGB888_1X24); ++ ++ if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16 && ++ fse->code != V4L2_PIX_FMT_ABGR32 && ++ fse->code != MEDIA_BUS_FMT_UYVY8_1X16) { ++ v4l2_err(sd, "Error in %s fse->code, code: %d, UYVY: %d, ARGB32: %d\n", ++ __FUNCTION__, fse->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Code ok"); ++ ++ if (fse->index >= num_frmfmt) { ++ v4l2_err(sd, "Error in %s, %d outside of num_frmfmt (%d)", __FUNCTION__, ++ fse->index, num_frmfmt); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Index ok"); ++ ++ fse->min_width = fse->max_width = frmfmt[fse->index].size.width; ++ fse->min_height = fse->max_height = frmfmt[fse->index].size.height; ++ v4l2_info(sd, "!!!!!!!!! %s() complete successfully, width: %d, height: %d\n", ++ __func__, fse->min_width, fse->min_height); ++ return 0; ++} ++ ++static int tc358743_enum_frame_interval( ++ struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_interval_enum *fie) { ++ const struct camera_common_frmfmt *frmfmt = tc358743_frmfmt; ++ int num_frmfmt = ARRAY_SIZE(tc358743_frmfmt); ++ int i; ++ ++ v4l2_info(sd, "%s()\n", __func__); ++ v4l2_info(sd, "----------------------------------------\n"); ++ ++ v4l2_info(sd, ++ "Trying to find frame interfval that matches fie->code, code: %d " ++ "(UYVY: %d, ARGB32: %d, MEDIA_BUS_FMT_UYVY8_1X16: %d)\n", ++ fie->code, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32, ++ MEDIA_BUS_FMT_UYVY8_1X16); ++ ++ if (fie->code != V4L2_PIX_FMT_UYVY && fie->code != V4L2_PIX_FMT_ABGR32 && ++ fie->code != MEDIA_BUS_FMT_UYVY8_1X16) { ++ v4l2_err(sd, "Unexpected code (%d), UYUV: %d, ABGR32: %d\n", fie->code, ++ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_ABGR32); ++ return -EINVAL; ++ } ++ ++ v4l2_info(sd, "Code ok"); ++ ++ for (i = 0; i < num_frmfmt; i++) { ++ if (frmfmt[i].size.width == fie->width && ++ frmfmt[i].size.height == fie->height) { ++ v4l2_info(sd, "Matched width %d and %d, height %d and %d", ++ frmfmt[i].size.width, fie->width, frmfmt[i].size.height, ++ fie->height); ++ break; ++ } ++ } + +- i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); +- i2c_wr8(sd, EDID_LEN2, edid_len >> 8); ++ v4l2_info(sd, "w/h ok or end (i=%d, num=%d)", i, num_frmfmt); + +- if (edid->blocks == 0) { +- state->edid_blocks_written = 0; +- return 0; +- } ++ if (i >= num_frmfmt) { ++ v4l2_err(sd, "Error in %s, num frmfmt\n", __FUNCTION__); ++ return -EINVAL; ++ } + +- for (i = 0; i < edid_len; i += EDID_BLOCK_SIZE) +- i2c_wr(sd, EDID_RAM + i, edid->edid + i, EDID_BLOCK_SIZE); ++ v4l2_info(sd, "i ok"); + +- state->edid_blocks_written = edid->blocks; ++ if (fie->index >= frmfmt[i].num_framerates) { ++ v4l2_err(sd, "Error in %s num framerates (%d outside %d)\n", __FUNCTION__, ++ fie->index, frmfmt[i].num_framerates); ++ return -EINVAL; ++ } + +- cec_s_phys_addr(state->cec_adap, pa, false); ++ v4l2_info(sd, "index ok"); + +- if (tx_5v_power_present(sd)) +- tc358743_enable_edid(sd); ++ fie->interval.numerator = 1; ++ fie->interval.denominator = frmfmt[i].framerates[fie->index]; ++ v4l2_info(sd, "!!!!!!!!!! %s() completed successfully, interval: 1/%d\n", ++ __func__, fie->interval.denominator); ++ return 0; ++} + +- return 0; ++static int tc358743_s_power(struct v4l2_subdev *sd, int on) { ++ v4l2_info(sd, "function %s\n", __func__); ++ ++ return 0; + } + +-/* -------------------------------------------------------------------------- */ +- + static const struct v4l2_subdev_core_ops tc358743_core_ops = { +- .log_status = tc358743_log_status, ++ .s_power = tc358743_s_power, ++ .log_status = tc358743_log_status, + #ifdef CONFIG_VIDEO_ADV_DEBUG +- .g_register = tc358743_g_register, +- .s_register = tc358743_s_register, ++ .g_register = tc358743_g_register, ++ .s_register = tc358743_s_register, + #endif +- .interrupt_service_routine = tc358743_isr, +- .subscribe_event = tc358743_subscribe_event, +- .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++ .interrupt_service_routine = tc358743_isr, ++ .subscribe_event = tc358743_subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, + }; + + static const struct v4l2_subdev_video_ops tc358743_video_ops = { +- .g_input_status = tc358743_g_input_status, +- .s_dv_timings = tc358743_s_dv_timings, +- .g_dv_timings = tc358743_g_dv_timings, +- .query_dv_timings = tc358743_query_dv_timings, +- .s_stream = tc358743_s_stream, ++ .g_input_status = tc358743_g_input_status, ++ .s_dv_timings = tc358743_s_dv_timings, ++ .g_dv_timings = tc358743_g_dv_timings, ++ .s_stream = tc358743_s_stream, ++ // .mbus_fmt = tc358743_mbus_fmt, ++ //.g_mbus_config = tc358743_g_mbus_config, ++ .query_dv_timings = tc358743_query_dv_timings, + }; + + static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { +- .enum_mbus_code = tc358743_enum_mbus_code, +- .set_fmt = tc358743_set_fmt, +- .get_fmt = tc358743_get_fmt, +- .get_edid = tc358743_g_edid, +- .set_edid = tc358743_s_edid, +- .enum_dv_timings = tc358743_enum_dv_timings, +- .dv_timings_cap = tc358743_dv_timings_cap, +- .get_mbus_config = tc358743_get_mbus_config, ++ .set_fmt = tc358743_set_fmt, ++ .get_fmt = tc358743_get_fmt, ++ .get_edid = tc358743_g_edid, ++ .set_edid = tc358743_s_edid, ++ .dv_timings_cap = tc358743_dv_timings_cap, ++ .get_mbus_config = tc358743_g_mbus_config, ++ ++ .enum_dv_timings = tc358743_enum_dv_timings, ++ .enum_mbus_code = tc358743_enum_mbus_code, ++ .enum_frame_size = tc358743_enum_frame_size, ++ .enum_frame_interval = tc358743_enum_frame_interval, + }; + + static const struct v4l2_subdev_ops tc358743_ops = { +- .core = &tc358743_core_ops, +- .video = &tc358743_video_ops, +- .pad = &tc358743_pad_ops, ++ .core = &tc358743_core_ops, ++ .video = &tc358743_video_ops, ++ .pad = &tc358743_pad_ops, + }; +- + /* --------------- CUSTOM CTRLS --------------- */ + + static const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { +- .id = TC358743_CID_AUDIO_SAMPLING_RATE, +- .name = "Audio sampling rate", +- .type = V4L2_CTRL_TYPE_INTEGER, +- .min = 0, +- .max = 768000, +- .step = 1, +- .def = 0, +- .flags = V4L2_CTRL_FLAG_READ_ONLY, ++ .id = TC358743_CID_AUDIO_SAMPLING_RATE, ++ .name = "Audio sampling rate", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0, ++ .max = 768000, ++ .step = 1, ++ .def = 0, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY, + }; + + static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { +- .id = TC358743_CID_AUDIO_PRESENT, +- .name = "Audio present", +- .type = V4L2_CTRL_TYPE_BOOLEAN, +- .min = 0, +- .max = 1, +- .step = 1, +- .def = 0, +- .flags = V4L2_CTRL_FLAG_READ_ONLY, ++ .id = TC358743_CID_AUDIO_PRESENT, ++ .name = "Audio present", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ .def = 0, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY, + }; + +-/* --------------- PROBE / REMOVE --------------- */ ++static bool tc358743_parse_dt(struct tc358743_platform_data *pdata, ++ struct i2c_client *client) { ++ struct device_node *node = client->dev.of_node; ++ const u32 *property; ++ pr_info("%s\n", __FUNCTION__); ++ pr_info("Device Tree Parameters:\n"); ++ ++ pdata->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0); ++ if (pdata->reset_gpio == 0) return false; ++ pr_info("reset_gpio = %d\n", pdata->reset_gpio); ++ ++ property = of_get_property(node, "refclk_hz", NULL); ++ if (property == NULL) return false; ++ pdata->refclk_hz = be32_to_cpup(property); ++ pr_info("refclk_hz = %d\n", be32_to_cpup(property)); ++ ++ return true; ++} + + #ifdef CONFIG_OF +-static void tc358743_gpio_reset(struct tc358743_state *state) +-{ +- usleep_range(5000, 10000); +- gpiod_set_value(state->reset_gpio, 1); +- usleep_range(1000, 2000); +- gpiod_set_value(state->reset_gpio, 0); +- msleep(20); +-} +- +-static int tc358743_probe_of(struct tc358743_state *state) +-{ +- struct device *dev = &state->i2c_client->dev; +- struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; +- struct device_node *ep; +- struct clk *refclk; +- u32 bps_pr_lane; +- int ret; +- +- refclk = devm_clk_get(dev, "refclk"); +- if (IS_ERR(refclk)) { +- if (PTR_ERR(refclk) != -EPROBE_DEFER) +- dev_err(dev, "failed to get refclk: %ld\n", +- PTR_ERR(refclk)); +- return PTR_ERR(refclk); +- } +- +- ep = of_graph_get_next_endpoint(dev->of_node, NULL); +- if (!ep) { +- dev_err(dev, "missing endpoint node\n"); +- return -EINVAL; +- } +- +- ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); +- if (ret) { +- dev_err(dev, "failed to parse endpoint\n"); +- goto put_node; +- } +- +- if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || +- endpoint.bus.mipi_csi2.num_data_lanes == 0 || +- endpoint.nr_of_link_frequencies == 0) { +- dev_err(dev, "missing CSI-2 properties in endpoint\n"); +- ret = -EINVAL; +- goto free_endpoint; +- } +- +- if (endpoint.bus.mipi_csi2.num_data_lanes > 4) { +- dev_err(dev, "invalid number of lanes\n"); +- ret = -EINVAL; +- goto free_endpoint; +- } +- +- state->bus = endpoint.bus.mipi_csi2; +- +- ret = clk_prepare_enable(refclk); +- if (ret) { +- dev_err(dev, "Failed! to enable clock\n"); +- goto free_endpoint; +- } +- +- state->pdata.refclk_hz = clk_get_rate(refclk); +- state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; +- state->pdata.enable_hdcp = false; +- /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ +- state->pdata.fifo_level = 16; +- /* +- * The PLL input clock is obtained by dividing refclk by pll_prd. +- * It must be between 6 MHz and 40 MHz, lower frequency is better. +- */ +- switch (state->pdata.refclk_hz) { +- case 26000000: +- case 27000000: +- case 42000000: +- state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; +- break; +- default: +- dev_err(dev, "unsupported refclk rate: %u Hz\n", +- state->pdata.refclk_hz); +- goto disable_clk; +- } +- +- /* +- * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. +- * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. +- */ +- bps_pr_lane = 2 * endpoint.link_frequencies[0]; +- if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { +- dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); +- ret = -EINVAL; +- goto disable_clk; +- } +- +- /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ +- state->pdata.pll_fbd = bps_pr_lane / +- state->pdata.refclk_hz * state->pdata.pll_prd; +- +- /* +- * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz +- * link frequency). In principle it should be possible to calculate +- * them based on link frequency and resolution. +- */ +- if (bps_pr_lane != 594000000U) +- dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); +- state->pdata.lineinitcnt = 0xe80; +- state->pdata.lptxtimecnt = 0x003; +- /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ +- state->pdata.tclk_headercnt = 0x1403; +- state->pdata.tclk_trailcnt = 0x00; +- /* ths-preparecnt: 3, ths-zerocnt: 1 */ +- state->pdata.ths_headercnt = 0x0103; +- state->pdata.twakeup = 0x4882; +- state->pdata.tclk_postcnt = 0x008; +- state->pdata.ths_trailcnt = 0x2; +- state->pdata.hstxvregcnt = 0; +- +- state->reset_gpio = devm_gpiod_get_optional(dev, "reset", +- GPIOD_OUT_LOW); +- if (IS_ERR(state->reset_gpio)) { +- dev_err(dev, "failed to get reset gpio\n"); +- ret = PTR_ERR(state->reset_gpio); +- goto disable_clk; +- } +- +- if (state->reset_gpio) +- tc358743_gpio_reset(state); +- +- ret = 0; +- goto free_endpoint; ++static void tc358743_gpio_reset(struct tc358743_state *state) { ++ usleep_range(5000, 10000); ++ // TODO: Re-implement the reset GPIO! ++ gpiod_set_value(state->reset_gpio, 0); ++ // gpio_set_value((int)state->reset_gpio, 1); ++ usleep_range(1000, 2000); ++ // gpio_set_value((int)state->reset_gpio, 0); ++ gpiod_set_value(state->reset_gpio, 1); ++ msleep(20); ++} ++ ++static int tc358743_probe_of(struct tc358743_state *state) { ++ struct device *dev = &state->i2c_client->dev; ++// struct v4l2_of_endpoint *endpoint; ++ struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; ++ ++ struct device_node *ep; ++ u32 bps_pr_lane; ++ int ret = -EINVAL; ++ pr_info("%s\n", __FUNCTION__); ++ ++ if ((state->pdata.refclk_hz != 26000000) && ++ (state->pdata.refclk_hz != 27000000) && ++ (state->pdata.refclk_hz != 42000000)) { ++ pr_info("refclk_hz error \n"); ++ return ret; ++ } ++ ++ ep = of_graph_get_next_endpoint(dev->of_node, NULL); ++ if (!ep) { ++ dev_err(dev, "missing endpoint node\n"); ++ return -EINVAL; ++ } ++ ++/* endpoint = v4l2_of_alloc_parse_endpoint(ep); ++ if (IS_ERR(endpoint)) { ++ dev_err(dev, "failed to parse endpoint\n"); ++ return PTR_ERR(endpoint); ++ }*/ ++ ++ ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); ++ if (ret) { ++ dev_err(dev, "failed to parse endpoint\n"); ++ goto put_node; ++ } ++ ++ ++// if (endpoint->bus_type != V4L2_MBUS_CSI2 || ++ ++ if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || ++ endpoint.bus.mipi_csi2.num_data_lanes == 0 || ++ endpoint.nr_of_link_frequencies == 0) { ++ dev_err(dev, "missing CSI-2 properties in endpoint\n"); ++ goto free_endpoint; ++ } ++ ++ pr_info("tc358743 endpoint.bus.mipi_csi2.flags %d\n", ++ endpoint.bus.mipi_csi2.flags); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.clock_lane %d\n", ++ endpoint.bus.mipi_csi2.clock_lane); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.num_data_lanes %d\n", ++ endpoint.bus.mipi_csi2.num_data_lanes); ++ pr_info("tc358743 endpoint.bus.mipi_csi2.data_lanes [%d-%d-%d-%d]\n", ++ endpoint.bus.mipi_csi2.data_lanes[0], ++ endpoint.bus.mipi_csi2.data_lanes[1], ++ endpoint.bus.mipi_csi2.data_lanes[2], ++ endpoint.bus.mipi_csi2.data_lanes[3]); ++ pr_info("tc358743 endpoint.nr_of_link_frequencies %d\n", ++ endpoint.nr_of_link_frequencies); ++ ++ state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS; ++ state->pdata.hdmi_detection_delay = HDMI_MODE_DELAY_100_MS; ++ state->pdata.enable_hdcp = false; ++ /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ ++ state->pdata.fifo_level = 16; ++ /* ++ * The PLL input clock is obtained by dividing refclk by pll_prd. ++ * It must be between 6 MHz and 40 MHz, lower frequency is better. ++ */ ++ switch (state->pdata.refclk_hz) { ++ //~ case 26322581: ++ //~ state->pdata.refclk_hz = 26322581; ++ case 26000000: ++ case 27000000: ++ //~ case 40800000: /* Tegra */ ++ case 42000000: ++ state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; ++ break; ++ default: ++ dev_err(dev,"Unsupported refclk rate: %u Hz\n", state->pdata.refclk_hz); ++ goto disable_clk; ++ } ++dev_err(dev, "HERE #1\n"); ++ /* ++ * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. ++ * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. ++ */ ++ bps_pr_lane = 2 * endpoint.link_frequencies[0]; ++ // if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { ++ if (bps_pr_lane < 62500000U || bps_pr_lane > 1188000000U) { ++ dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); ++ goto disable_clk; ++ } ++dev_err(dev, "HERE #2\n"); ++ /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ ++ state->pdata.pll_fbd = ++ bps_pr_lane / state->pdata.refclk_hz * state->pdata.pll_prd; ++ ++ /* ++ * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz ++ * link frequency). In principle it should be possible to calculate ++ * them based on link frequency and resolution. ++ */ ++ if (bps_pr_lane != 594000000U) ++ dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); ++ pr_info("tc358743 state->pdata.pll_prd=%d\r\n", state->pdata.pll_prd); ++ pr_info("tc358743 state->pdata.pll_fbd=%d\r\n", state->pdata.pll_fbd); ++dev_err(dev, "HERE #3\n"); ++ // freq = refclk / prd * fbd, default = 594 MHz ++// state->pdata.lineinitcnt = 0xe80; ++// state->pdata.lptxtimecnt = 0x003; ++// /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ ++// state->pdata.tclk_headercnt = 0x1403; ++// state->pdata.tclk_trailcnt = 0x00; ++// /* ths-preparecnt: 3, ths-zerocnt: 1 */ ++// state->pdata.ths_headercnt = 0x0103; ++// state->pdata.twakeup = 0x4882; ++// state->pdata.tclk_postcnt = 0x008; ++// state->pdata.ths_trailcnt = 0x2; ++// state->pdata.hstxvregcnt = 2; ++ ++ state->pdata.lineinitcnt = 0x1d01;// ++ state->pdata.lptxtimecnt = 0x008;// ++ /* tclk-preparecnt: 6, tclk-zerocnt: 45 */ ++ state->pdata.tclk_headercnt = 0x2D06;//0x0218 ++ state->pdata.tclk_trailcnt = 0x09;// ++ /* ths-preparecnt: 7, ths-zerocnt: 17 */ ++ state->pdata.ths_headercnt = 0xd06;//0x0220 ++ state->pdata.twakeup = 0x4883;// ++ state->pdata.tclk_postcnt = 0x010;// ++ state->pdata.ths_trailcnt = 0xA;// ++ state->pdata.hstxvregcnt = 5;//5 ++ state->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(state->reset_gpio)) { ++ dev_err(dev, "failed to get reset gpio\n"); ++ ret = PTR_ERR(state->reset_gpio); ++ goto disable_clk; ++ } ++ if(state->reset_gpio) { ++ pr_info("Calling reset GPIO but NOT IMPLEMENTED!"); ++ tc358743_gpio_reset(state); ++ } ++ ret = 0; ++ goto free_endpoint; + + disable_clk: +- clk_disable_unprepare(refclk); ++ // clk_disable_unprepare(refclk); + free_endpoint: +- v4l2_fwnode_endpoint_free(&endpoint); ++ // v4l2_of_free_endpoint(endpoint); ++ v4l2_fwnode_endpoint_free(&endpoint); + put_node: +- of_node_put(ep); +- return ret; ++ of_node_put(ep); ++ ++ return ret; + } + #else +-static inline int tc358743_probe_of(struct tc358743_state *state) +-{ +- return -ENODEV; ++static inline int tc358743_probe_of(struct tc358743_state *state) { ++ return -ENODEV; + } + #endif + +-static int tc358743_probe(struct i2c_client *client) +-{ +- static struct v4l2_dv_timings default_timing = +- V4L2_DV_BT_CEA_640X480P59_94; +- struct tc358743_state *state; +- struct tc358743_platform_data *pdata = client->dev.platform_data; +- struct v4l2_subdev *sd; +- u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK; +- int err; +- +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +- return -EIO; +- v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", +- client->addr << 1, client->adapter->name); +- +- state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), +- GFP_KERNEL); +- if (!state) +- return -ENOMEM; +- +- state->i2c_client = client; +- +- /* platform data */ +- if (pdata) { +- state->pdata = *pdata; +- state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; +- } else { +- err = tc358743_probe_of(state); +- if (err == -ENODEV) +- v4l_err(client, "No platform data!\n"); +- if (err) +- return err; +- } +- +- sd = &state->sd; +- v4l2_i2c_subdev_init(sd, client, &tc358743_ops); +- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; +- +- /* i2c access */ +- if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) { +- v4l2_info(sd, "not a TC358743 on address 0x%x\n", +- client->addr << 1); +- return -ENODEV; +- } +- +- /* control handlers */ +- v4l2_ctrl_handler_init(&state->hdl, 3); +- +- state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, +- V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); +- +- /* custom controls */ +- state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, +- &tc358743_ctrl_audio_sampling_rate, NULL); +- +- state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, +- &tc358743_ctrl_audio_present, NULL); +- +- sd->ctrl_handler = &state->hdl; +- if (state->hdl.error) { +- err = state->hdl.error; +- goto err_hdl; +- } +- +- if (tc358743_update_controls(sd)) { +- err = -ENODEV; +- goto err_hdl; +- } +- +- state->pad.flags = MEDIA_PAD_FL_SOURCE; +- sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; +- err = media_entity_pads_init(&sd->entity, 1, &state->pad); +- if (err < 0) +- goto err_hdl; +- +- state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; +- +- sd->dev = &client->dev; +- err = v4l2_async_register_subdev(sd); +- if (err < 0) +- goto err_hdl; +- +- mutex_init(&state->confctl_mutex); +- +- INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, +- tc358743_delayed_work_enable_hotplug); +- +-#ifdef CONFIG_VIDEO_TC358743_CEC +- state->cec_adap = cec_allocate_adapter(&tc358743_cec_adap_ops, +- state, dev_name(&client->dev), +- CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL, CEC_MAX_LOG_ADDRS); +- if (IS_ERR(state->cec_adap)) { +- err = PTR_ERR(state->cec_adap); +- goto err_hdl; +- } +- irq_mask |= MASK_CEC_RMSK | MASK_CEC_TMSK; ++static int tc358743_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { ++ return 0; ++} ++ ++static const struct v4l2_subdev_internal_ops tc358743_subdev_internal_ops = { ++ .open = tc358743_open, ++}; ++ ++static const struct media_entity_operations tc358743_media_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++static const struct regmap_config sensor_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 8, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static int tc358743_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) { ++ // static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_1280X720P30; ++ static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_1920X1080P50; ++ struct v4l2_subdev_edid sd_edid = { ++ .blocks = 2, ++ .edid = edid, ++ }; ++ struct tc358743_state *state; ++ struct tc358743_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_subdev *sd; ++ int err; ++ u16 chip_id_val; ++ ++ pr_info("%s\n", __FUNCTION__); ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ pr_err("i2c check functionality failed addres %02X name %s", client->addr, ++ client->adapter->name); ++ return -EIO; ++ } ++ ++ v4l2_info(sd, "chip found @0x%x (%s)\n", client->addr, client->adapter->name); ++ ++ state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), GFP_KERNEL); ++ if (!state) { ++ pr_err("devm_kzalloc failed"); ++ return -ENOMEM; ++ } ++ v4l2_info(sd, "dev of node %s\n", client->dev.of_node->full_name); ++ if (client->dev.of_node) { ++ if (!tc358743_parse_dt(&state->pdata, client)) { ++ pr_err("Couldn't parse device tree\n"); ++ return -ENODEV; ++ } ++ } ++ ++ state->i2c_client = client; ++ ++ /* platform data */ ++ if (pdata) { ++ state->pdata = *pdata; ++ pdata->endpoint.bus.mipi_csi2.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ } else { ++ err = tc358743_probe_of(state); ++ if (err == -ENODEV) { ++ v4l_err(client, "No platform data! err = %d\n", err); ++ return -ENODEV; ++ } ++ } ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &tc358743_ops); ++ v4l2_info(sd, "Subdev init done\n"); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; ++ ++ /* i2c access */ ++ chip_id_val = i2c_rd16(sd, CHIPID); ++ v4l2_info(sd, "Chip ID val: %d\n", chip_id_val); ++ ++ if ((chip_id_val & MASK_CHIPID) != 0 || chip_id_val == 99) { ++ v4l2_info(sd, "tc358743: ERROR: not a TC358743 on address0x%x\n", ++ client->addr); ++ return -ENODEV; ++ } ++ ++ /* control handlers */ ++ v4l2_ctrl_handler_init(&state->hdl, 3); ++ v4l2_info(sd, "ctrl handler initied\n"); ++ ++ /* private controls */ ++ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std( ++ &state->hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); ++ ++ /* custom controls */ ++ state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom( ++ &state->hdl, &tc358743_ctrl_audio_sampling_rate, NULL); ++ ++ state->audio_present_ctrl = ++ v4l2_ctrl_new_custom(&state->hdl, &tc358743_ctrl_audio_present, NULL); ++ ++ v4l2_info(sd, "A bunch of new cutoms done\n"); ++ ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ err = state->hdl.error; ++ goto err_hdl; ++ } ++ ++ if (tc358743_update_controls(sd)) { ++ err = -ENODEV; ++ goto err_hdl; ++ } ++ ++ v4l2_info(sd, "Controls updated\n"); ++ ++ /* work queues */ ++ state->work_queues = create_singlethread_workqueue(client->name); ++ if (!state->work_queues) { ++ v4l2_err(sd, "Could not create work queue\n"); ++ err = -ENOMEM; ++ goto err_hdl; ++ } ++ v4l2_info(sd, "Work queue created\n"); ++ // sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ sd->entity.ops = &tc358743_media_ops; ++ state->pad.flags = MEDIA_PAD_FL_SOURCE; ++ v4l2_info(sd, "About to call tegra_media_entity_init\n"); ++ err = tegra_media_entity_init(&sd->entity, 1, &state->pad, true, true); ++ if (err < 0) goto err_hdl; ++ v4l2_info(sd, "tegra_media_entity_init complete\n"); ++ ++#ifdef TC358743_VOUT_RGB ++ state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; ++#else ++ state->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_1X16; + #endif + +- tc358743_initial_setup(sd); ++ v4l2_info(sd, "Set mbus_fmt_code in probe to: %d\n", state->mbus_fmt_code); ++ ++ sd->dev = &client->dev; ++ v4l2_info(sd, "About to register subdev\n"); ++ ++v4l2_info(sd, "HERE #4\n"); ++ ++ err = v4l2_async_register_subdev(sd); ++ ++v4l2_info(sd, "HERE #5\n"); ++ ++ v4l2_info(sd, "Register subdev: %d\n", err); ++ ++ if (err < 0) goto err_hdl; ++ ++ mutex_init(&state->confctl_mutex); + +- tc358743_s_dv_timings(sd, &default_timing); ++v4l2_info(sd, "HERE #6\n"); + +- tc358743_set_csi_color_space(sd); ++ INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, ++ tc358743_delayed_work_enable_hotplug); ++ INIT_DELAYED_WORK(&state->delayed_work_enable_interrupt, ++ tc358743_delayed_work_enable_interrupt); ++ INIT_WORK(&state->process_isr, tc358743_process_isr); ++ mutex_init(&state->isr_lock); + +- tc358743_init_interrupts(sd); ++ v4l2_info(sd, "before tc358743_initial_setup\r\n"); ++ tc358743_initial_setup(sd); ++ v4l2_info(sd, "after tc358743_initial_setup\r\n"); + +- if (state->i2c_client->irq) { +- err = devm_request_threaded_irq(&client->dev, +- state->i2c_client->irq, +- NULL, tc358743_irq_handler, +- IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +- "tc358743", state); +- if (err) +- goto err_work_queues; +- } else { +- INIT_WORK(&state->work_i2c_poll, +- tc358743_work_i2c_poll); +- timer_setup(&state->timer, tc358743_irq_poll_timer, 0); +- state->timer.expires = jiffies + +- msecs_to_jiffies(POLL_INTERVAL_MS); +- add_timer(&state->timer); +- } ++ tc358743_set_csi_color_space(sd); ++ v4l2_info(sd, "before tc358743_s_dv_timings\r\n"); ++ tc358743_s_dv_timings(sd, &default_timing); + +- err = cec_register_adapter(state->cec_adap, &client->dev); +- if (err < 0) { +- pr_err("%s: failed to register the cec device\n", __func__); +- cec_delete_adapter(state->cec_adap); +- state->cec_adap = NULL; +- goto err_work_queues; +- } ++ v4l2_info(sd, "before tc358743_init_interrupts, irq: %d\r\n", ++ state->i2c_client->irq); ++ tc358743_init_interrupts(sd); ++ v4l2_info(sd, "after tc358743_init_interrupts, irq: %d\r\n", ++ state->i2c_client->irq); ++ if (state->i2c_client->irq) { ++ v4l2_info(sd, "IQR request\r\n"); ++ err = devm_request_threaded_irq( ++ &client->dev, state->i2c_client->irq, NULL, tc358743_irq_handler, ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "tc358743", state); ++ v4l2_err(sd, "err, %d\n", err); ++ if (err) goto err_work_queues; ++ } ++ queue_delayed_work(state->work_queues, &state->delayed_work_enable_interrupt, ++ msecs_to_jiffies(DELAY_ENABLE_INTERRUPT_MS)); + +- tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); +- i2c_wr16(sd, INTMASK, ~irq_mask); ++ tc358743_enable_interrupts(sd, tx_5v_power_present(sd)); ++ i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); + +- err = v4l2_ctrl_handler_setup(sd->ctrl_handler); +- if (err) +- goto err_work_queues; ++ err = v4l2_ctrl_handler_setup(sd->ctrl_handler); + +- v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, +- client->addr << 1, client->adapter->name); ++ if (err) goto err_work_queues; + +- return 0; ++ v4l2_info(sd, "%s found @0x%x (%s)\n", client->name, client->addr, ++ client->adapter->name); ++ tc358743_s_edid(sd, &sd_edid); ++ tc358743_g_edid(sd, &sd_edid); ++ ++ tc358743_log_status(sd); ++ v4l2_info(sd, "Probe complete\n"); ++ return 0; + + err_work_queues: +- cec_unregister_adapter(state->cec_adap); +- if (!state->i2c_client->irq) +- flush_work(&state->work_i2c_poll); +- cancel_delayed_work(&state->delayed_work_enable_hotplug); +- mutex_destroy(&state->confctl_mutex); ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++ mutex_destroy(&state->confctl_mutex); + err_hdl: +- media_entity_cleanup(&sd->entity); +- v4l2_ctrl_handler_free(&state->hdl); +- return err; +-} +- +-static int tc358743_remove(struct i2c_client *client) +-{ +- struct v4l2_subdev *sd = i2c_get_clientdata(client); +- struct tc358743_state *state = to_state(sd); +- +- if (!state->i2c_client->irq) { +- del_timer_sync(&state->timer); +- flush_work(&state->work_i2c_poll); +- } +- cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); +- cec_unregister_adapter(state->cec_adap); +- v4l2_async_unregister_subdev(sd); +- v4l2_device_unregister_subdev(sd); +- mutex_destroy(&state->confctl_mutex); +- media_entity_cleanup(&sd->entity); +- v4l2_ctrl_handler_free(&state->hdl); +- +- return 0; +-} +- +-static const struct i2c_device_id tc358743_id[] = { +- {"tc358743", 0}, +- {} +-}; ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(&state->hdl); ++ return err; ++} ++ ++static int tc358743_remove(struct i2c_client *client) { ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct tc358743_state *state = to_state(sd); ++ ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++ v4l2_async_unregister_subdev(sd); ++ v4l2_device_unregister_subdev(sd); ++ mutex_destroy(&state->confctl_mutex); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(&state->hdl); ++ ++ return 0; ++} ++ ++static struct i2c_device_id tc358743_id[] = {{"tc358743", 0}, {}}; + + MODULE_DEVICE_TABLE(i2c, tc358743_id); + +-#if IS_ENABLED(CONFIG_OF) +-static const struct of_device_id tc358743_of_match[] = { +- { .compatible = "toshiba,tc358743" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, tc358743_of_match); ++#ifdef CONFIG_OF ++static const struct of_device_id tc358743_of_table[] = { ++ {.compatible = "toshiba,tc358743"}, {}}; ++MODULE_DEVICE_TABLE(of, tc358743_of_table); + #endif + + static struct i2c_driver tc358743_driver = { +- .driver = { +- .name = "tc358743", +- .of_match_table = of_match_ptr(tc358743_of_match), +- }, +- .probe_new = tc358743_probe, +- .remove = tc358743_remove, +- .id_table = tc358743_id, ++ .driver = ++ { ++ .of_match_table = of_match_ptr(tc358743_of_table), ++ .name = "tc358743", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tc358743_probe, ++ .remove = tc358743_remove, ++ .id_table = tc358743_id, + }; + +-module_i2c_driver(tc358743_driver); ++module_i2c_driver(tc358743_driver); +\ No newline at end of file +diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h +index 2495878dc358..665c3d3ecbfa 100644 +--- a/drivers/media/i2c/tc358743_regs.h ++++ b/drivers/media/i2c/tc358743_regs.h +@@ -1,9 +1,22 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks ++ * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * + */ + + /* +@@ -180,98 +193,8 @@ + #define CSI_START 0x0518 + #define MASK_STRT 0x00000001 + +-/* *** CEC (32 bit) *** */ +-#define CECHCLK 0x0028 /* 16 bits */ +-#define MASK_CECHCLK (0x7ff << 0) +- +-#define CECLCLK 0x002a /* 16 bits */ +-#define MASK_CECLCLK (0x7ff << 0) +- +-#define CECEN 0x0600 +-#define MASK_CECEN 0x0001 +- +-#define CECADD 0x0604 +-#define CECRST 0x0608 +-#define MASK_CECRESET 0x0001 +- +-#define CECREN 0x060c +-#define MASK_CECREN 0x0001 +- +-#define CECRCTL1 0x0614 +-#define MASK_CECACKDIS (1 << 24) +-#define MASK_CECHNC (3 << 20) +-#define MASK_CECLNC (7 << 16) +-#define MASK_CECMIN (7 << 12) +-#define MASK_CECMAX (7 << 8) +-#define MASK_CECDAT (7 << 4) +-#define MASK_CECTOUT (3 << 2) +-#define MASK_CECRIHLD (1 << 1) +-#define MASK_CECOTH (1 << 0) +- +-#define CECRCTL2 0x0618 +-#define MASK_CECSWAV3 (7 << 12) +-#define MASK_CECSWAV2 (7 << 8) +-#define MASK_CECSWAV1 (7 << 4) +-#define MASK_CECSWAV0 (7 << 0) +- +-#define CECRCTL3 0x061c +-#define MASK_CECWAV3 (7 << 20) +-#define MASK_CECWAV2 (7 << 16) +-#define MASK_CECWAV1 (7 << 12) +-#define MASK_CECWAV0 (7 << 8) +-#define MASK_CECACKEI (1 << 4) +-#define MASK_CECMINEI (1 << 3) +-#define MASK_CECMAXEI (1 << 2) +-#define MASK_CECRSTEI (1 << 1) +-#define MASK_CECWAVEI (1 << 0) +- +-#define CECTEN 0x0620 +-#define MASK_CECTBUSY (1 << 1) +-#define MASK_CECTEN (1 << 0) +- +-#define CECTCTL 0x0628 +-#define MASK_CECSTRS (7 << 20) +-#define MASK_CECSPRD (7 << 16) +-#define MASK_CECDTRS (7 << 12) +-#define MASK_CECDPRD (15 << 8) +-#define MASK_CECBRD (1 << 4) +-#define MASK_CECFREE (15 << 0) +- +-#define CECRSTAT 0x062c +-#define MASK_CECRIWA (1 << 6) +-#define MASK_CECRIOR (1 << 5) +-#define MASK_CECRIACK (1 << 4) +-#define MASK_CECRIMIN (1 << 3) +-#define MASK_CECRIMAX (1 << 2) +-#define MASK_CECRISTA (1 << 1) +-#define MASK_CECRIEND (1 << 0) +- +-#define CECTSTAT 0x0630 +-#define MASK_CECTIUR (1 << 4) +-#define MASK_CECTIACK (1 << 3) +-#define MASK_CECTIAL (1 << 2) +-#define MASK_CECTIEND (1 << 1) +- +-#define CECRBUF1 0x0634 +-#define MASK_CECRACK (1 << 9) +-#define MASK_CECEOM (1 << 8) +-#define MASK_CECRBYTE (0xff << 0) +- +-#define CECTBUF1 0x0674 +-#define MASK_CECTEOM (1 << 8) +-#define MASK_CECTBYTE (0xff << 0) +- +-#define CECRCTR 0x06b4 +-#define MASK_CECRCTR (0x1f << 0) +- +-#define CECIMSK 0x06c0 +-#define MASK_CECTIM (1 << 1) +-#define MASK_CECRIM (1 << 0) +- +-#define CECICLR 0x06cc +-#define MASK_CECTICLR (1 << 1) +-#define MASK_CECRICLR (1 << 0) +- ++#define CECEN 0x0600 ++#define MASK_CECEN 0x0001 + + #define HDMI_INT0 0x8500 + #define MASK_I_KEY 0x80 +@@ -497,7 +420,6 @@ + #define MASK_MODE_RST_TN 0x20 + #define MASK_LINE_REKEY 0x10 + #define MASK_AUTO_CLR 0x04 +-#define MASK_MANUAL_AUTHENTICATION 0x02 /* Not in REF_01 */ + + #define HDCP_REG1 0x8563 /* Not in REF_01 */ + #define MASK_AUTH_UNAUTH_SEL 0x70 +@@ -756,4 +678,4 @@ + #define EDID_RAM 0x8C00 + #define NO_GDB_LIMIT 0x9007 + +-#endif ++#endif +\ No newline at end of file +diff --git a/include/media/i2c/tc358743.h b/include/media/i2c/tc358743.h +index b343650c2948..f90ae1a1f70e 100644 +--- a/include/media/i2c/tc358743.h ++++ b/include/media/i2c/tc358743.h +@@ -1,8 +1,22 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ + /* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * +- * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. ++ * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights ++ * reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * + */ + + /* +@@ -13,105 +27,122 @@ + + #ifndef _TC358743_ + #define _TC358743_ ++//#include ++#include ++ ++enum tc358743_csi_port { CSI_TX_NONE = 0, CSI_TX_0, CSI_TX_1, CSI_TX_BOTH }; + + enum tc358743_ddc5v_delays { +- DDC5V_DELAY_0_MS, +- DDC5V_DELAY_50_MS, +- DDC5V_DELAY_100_MS, +- DDC5V_DELAY_200_MS, ++ DDC5V_DELAY_0_MS, ++ DDC5V_DELAY_50_MS, ++ DDC5V_DELAY_100_MS, ++ DDC5V_DELAY_200_MS, ++ DDC5V_DELAY_MAX = DDC5V_DELAY_200_MS, + }; + + enum tc358743_hdmi_detection_delay { +- HDMI_MODE_DELAY_0_MS, +- HDMI_MODE_DELAY_25_MS, +- HDMI_MODE_DELAY_50_MS, +- HDMI_MODE_DELAY_100_MS, ++ HDMI_MODE_DELAY_0_MS, ++ HDMI_MODE_DELAY_25_MS, ++ HDMI_MODE_DELAY_50_MS, ++ HDMI_MODE_DELAY_100_MS, + }; + + struct tc358743_platform_data { +- /* System clock connected to REFCLK (pin H5) */ +- u32 refclk_hz; /* 26 MHz, 27 MHz or 42 MHz */ +- +- /* DDC +5V debounce delay to avoid spurious interrupts when the cable +- * is connected. +- * Sets DDC5V_MODE in register DDC_CTL. +- * Default: DDC5V_DELAY_0_MS +- */ +- enum tc358743_ddc5v_delays ddc5v_delay; +- +- bool enable_hdcp; +- +- /* +- * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO +- * level to somewhere in the middle (e.g. 300), so it can cover speed +- * mismatches in input and output ports. +- */ +- u16 fifo_level; +- +- /* Bps pr lane is (refclk_hz / pll_prd) * pll_fbd */ +- u16 pll_prd; +- u16 pll_fbd; +- +- /* CSI +- * Calculate CSI parameters with REF_02 for the highest resolution your +- * CSI interface can handle. The driver will adjust the number of CSI +- * lanes in use according to the pixel clock. +- * +- * The values in brackets are calculated with REF_02 when the number of +- * bps pr lane is 823.5 MHz, and can serve as a starting point. +- */ +- u32 lineinitcnt; /* (0x00001770) */ +- u32 lptxtimecnt; /* (0x00000005) */ +- u32 tclk_headercnt; /* (0x00001d04) */ +- u32 tclk_trailcnt; /* (0x00000000) */ +- u32 ths_headercnt; /* (0x00000505) */ +- u32 twakeup; /* (0x00004650) */ +- u32 tclk_postcnt; /* (0x00000000) */ +- u32 ths_trailcnt; /* (0x00000004) */ +- u32 hstxvregcnt; /* (0x00000005) */ +- +- /* DVI->HDMI detection delay to avoid unnecessary switching between DVI +- * and HDMI mode. +- * Sets HDMI_DET_V in register HDMI_DET. +- * Default: HDMI_MODE_DELAY_0_MS +- */ +- enum tc358743_hdmi_detection_delay hdmi_detection_delay; +- +- /* Reset PHY automatically when TMDS clock goes from DC to AC. +- * Sets PHY_AUTO_RST2 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_detected; +- +- /* Reset PHY automatically when TMDS clock passes 21 MHz. +- * Sets PHY_AUTO_RST3 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_in_range; +- +- /* Reset PHY automatically when TMDS clock is detected. +- * Sets PHY_AUTO_RST4 in register PHY_CTL2. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_tmds_valid; +- +- /* Reset HDMI PHY automatically when hsync period is out of range. +- * Sets H_PI_RST in register HV_RST. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_hsync_out_of_range; +- +- /* Reset HDMI PHY automatically when vsync period is out of range. +- * Sets V_PI_RST in register HV_RST. +- * Default: false +- */ +- bool hdmi_phy_auto_reset_vsync_out_of_range; ++ /* GPIOs */ ++ int reset_gpio; ++ ++#ifdef CONFIG_V4L2_FWNODE ++ struct v4l2_fwnode_endpoint endpoint; ++#else ++ struct v4l2_of_endpoint endpoint; ++#endif ++ ++ /* System clock connected to REFCLK (pin H5) */ ++ u32 refclk_hz; /* 26 MHz, 27 MHz or 42 MHz */ ++ ++ /* DDC +5V debounce delay to avoid spurious interrupts when the cable ++ * is connected. ++ * Sets DDC5V_MODE in register DDC_CTL. ++ * Default: DDC5V_DELAY_0_MS ++ */ ++ enum tc358743_ddc5v_delays ddc5v_delay; ++ ++ bool enable_hdcp; ++ ++ /* CSI Output */ ++ enum tc358743_csi_port csi_port; // TODO: Should this be port-index? ++ ++ /* ++ * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO ++ * level to somewhere in the middle (e.g. 300), so it can cover speed ++ * mismatches in input and output ports. ++ */ ++ u16 fifo_level; ++ ++ /* Bps pr lane is (refclk_hz / pll_prd) * pll_fbd */ ++ u16 pll_prd; ++ u16 pll_fbd; ++ ++ /* CSI ++ * Calculate CSI parameters with REF_02 for the highest resolution your ++ * CSI interface can handle. The driver will adjust the number of CSI ++ * lanes in use according to the pixel clock. ++ * ++ * The values in brackets are calculated with REF_02 when the number of ++ * bps pr lane is 823.5 MHz, and can serve as a starting point. ++ */ ++ u32 lineinitcnt; /* (0x00001770) */ ++ u32 lptxtimecnt; /* (0x00000005) */ ++ u32 tclk_headercnt; /* (0x00001d04) */ ++ u32 tclk_trailcnt; /* (0x00000000) */ ++ u32 ths_headercnt; /* (0x00000505) */ ++ u32 twakeup; /* (0x00004650) */ ++ u32 tclk_postcnt; /* (0x00000000) */ ++ u32 ths_trailcnt; /* (0x00000004) */ ++ u32 hstxvregcnt; /* (0x00000005) */ ++ ++ /* DVI->HDMI detection delay to avoid unnecessary switching between DVI ++ * and HDMI mode. ++ * Sets HDMI_DET_V in register HDMI_DET. ++ * Default: HDMI_MODE_DELAY_0_MS ++ */ ++ enum tc358743_hdmi_detection_delay hdmi_detection_delay; ++ ++ /* Reset PHY automatically when TMDS clock goes from DC to AC. ++ * Sets PHY_AUTO_RST2 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_detected; ++ ++ /* Reset PHY automatically when TMDS clock passes 21 MHz. ++ * Sets PHY_AUTO_RST3 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_in_range; ++ ++ /* Reset PHY automatically when TMDS clock is detected. ++ * Sets PHY_AUTO_RST4 in register PHY_CTL2. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_tmds_valid; ++ ++ /* Reset HDMI PHY automatically when hsync period is out of range. ++ * Sets H_PI_RST in register HV_RST. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_hsync_out_of_range; ++ ++ /* Reset HDMI PHY automatically when vsync period is out of range. ++ * Sets V_PI_RST in register HV_RST. ++ * Default: false ++ */ ++ bool hdmi_phy_auto_reset_vsync_out_of_range; + }; + + /* custom controls */ + /* Audio sample rate in Hz */ + #define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) + /* Audio present status */ +-#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) ++#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) + +-#endif ++#endif +\ No newline at end of file diff --git a/modules/jetpack/nvidia-jetson-orin/default.nix b/modules/jetpack/nvidia-jetson-orin/default.nix index b97ede26b..2049441a8 100644 --- a/modules/jetpack/nvidia-jetson-orin/default.nix +++ b/modules/jetpack/nvidia-jetson-orin/default.nix @@ -9,6 +9,10 @@ ./pci-passthrough-common.nix + ../../hardware/nvidia-jetson-orin/camera-common.nix + ../../hardware/nvidia-jetson-orin/agx-camera.nix + ../../hardware/nvidia-jetson-orin/nx-camera.nix + ./ota-utils-fix.nix ./virtualization diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index 4b9d00e84..00bfc6f02 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -54,7 +54,9 @@ enable = true; somType = som; agx.enableNetvmWlanPCIPassthrough = som == "agx"; + agx.camera = (som == "agx"); nx.enableNetvmEthernetPCIPassthrough = som == "nx"; + nx.camera = (som == "nx"); }; hardware.nvidia = {