├── README.md ├── lt6911uxc.txt └── source ├── lt6911uxc_regs_zhaw.h └── lt6911uxc_zhaw.c /README.md: -------------------------------------------------------------------------------- 1 | # Lontium_lt6911uxc 2 | 3 | This repository contains a driver for the lt6911uxc HDMI2.0 to MIPI CSI converter from Lontium. The driver was tested on an Nvidia Jetson Orin NX running L4T 36.X (Jetpack 6). 4 | 5 | --- 6 | 7 | [![logo](https://github.com/InES-HPMM/FPD-LinkIII_Raspberry_HW/blob/master/images/ines_logo.png)](https://www.zhaw.ch/en/engineering/institutes-centres/ines/ "Homepage") 8 | 9 | __The group High Performance Multimedia from the Institute of Embedded Systems associated with ZHAW School of Engineering proudly presents the new version for L4T 36.X of the open source driver for lontium device LT6911UXC.__ 10 | 11 | > For recent news check out our [Blog](https://blog.zhaw.ch/high-performance/). 12 | 13 | ## Table of contents 14 | 15 | [Insert Driver into L4T Sources](#insert-driver-into-your-l4t-sources): Instructions on how to add this driver to your Jetson Linux Driver Package (L4T). 16 | 17 | [Information about the usage with LT6911](#information-about-the-usage-with-lt6911): Information on how to use the Lontium lt6911uxc in your Jetson Linux environment. 18 | 19 | --- 20 | 21 | ## Insert Driver into your L4T Sources 22 | 23 | The driver can be used with the Linux Driver Packages (L4T) for NVIDIA Jetson Modules and was tested using a Jetson Orin NX 16GB on a custom carrier board. 24 | In order to adapt the driver to your hardware setup use the detailed description regarding device tree parameters in the file `lt6911uxc.txt`. 25 | 26 | For geneneral information and instructions regarding the NVIDIA L4T release or how to manually build the linux kernel for your Jetson device consult: [L4T release package](https://developer.nvidia.com/embedded/jetson-linux-r3643) , [NVIDIA Kernel Customization](https://docs.nvidia.com/jetson/archives/r36.4.3/DeveloperGuide/SD/Kernel/KernelCustomization.html). 27 | 28 | ### Insert Driver into Kernel Sources 29 | 1) Locate the subdirectory of multimedia drivers for encoder, decoder and other helper chips as part of the NVIDIA overlay in (`{L4T_Workspace}/source/nvidia-oot/drivers/media/i2c/`). 30 | 31 | 2) Add the driver source files to the other present drivers 32 | 33 | 3) **Makefile:** Open the file `nvidia/drivers/media/i2c/Makefile` and add the following line: 34 | ``` 35 | obj-m += lt6911uxc_zhaw.o 36 | ``` 37 | 4) Rebuild your kernel according to NVIDIA's developer Guide mentioned above. 38 | 39 | 40 | With correct hardware description in the device-tree, the driver should automatically load on boot-up and should add a video device accordingly. 41 | 42 | ## Information about the usage with LT6911 43 | Even though this kernel driver implements lt6911uxc to be used within your Jetson Linux environment, there are a couple important points to mention: 44 | 1. In Addition to implementing this driver, the lt6911uxc IC has to be flashed with a working firmware provided by Lontium directly. 45 | 2. Upon HDMI device connection or format change, the Lontium emits an interrupt over the specified interrupt line. Make sure that this interrupt is routed to a Jetson GPIO and configured accordingly in the device tree. To apply formats detected by the lt6911uxc, you need to apply the timings to the VI using v4l2-ctl: 46 | ```bash 47 | v4l2-ctl --device= --query-dv-timings 48 | v4l2-ctl --device=/dev/video0 --set-dv-bt-timings query 49 | ``` 50 | 3. With a suiting firmware running on the IC it is capable streaming 4k60 HDMI input sources via 8-lane CSI. For this specific use case, feel free to [contact us](mailto:blog.zhaw.ch). 51 | -------------------------------------------------------------------------------- /lt6911uxc.txt: -------------------------------------------------------------------------------- 1 | Device tree examples for the Lontium HDMI to CSI converter lt6911uxc. One 2 | example shows the basic chip configuration and in the second example we 3 | provide our configuration to integrate this custom chip into the device tree 4 | of the Nvidia Jetson Orin NX 5 | 6 | /*------------------------------------------------------------------------------ 7 | * ------------------------------------------------------------------------------ 8 | * 9 | * Device Tree Examples 10 | * 11 | * ------------------------------------------------------------------------------ 12 | *-----------------------------------------------------------------------------*/ 13 | 14 | /*------------------------------------------------------------------------------ 15 | * Example 1 (Chip) 16 | *-----------------------------------------------------------------------------*/ 17 | 18 | lt6911uxc_zhaw_a@2b { 19 | compatible = "lontium,lt6911uxc_zhaw"; 20 | reg = <0x2b>; 21 | status = LT6911_EN; 22 | devnode = "video0"; 23 | sensor_model="lt6911"; 24 | 25 | /* Interrupt */ 26 | interrupt-parent = <&gpio_aon>; 27 | interrupts = ; 28 | 29 | 30 | 31 | /* Physical dimensions of sensor */ 32 | physical_w = "3.674"; 33 | physical_h = "2.738"; 34 | 35 | 36 | refclk_hz = <27000000>; 37 | 38 | mode0 { // 1920 x 1080, 60 FPS 39 | num_lanes = "4"; 40 | tegra_sinterface = "serial_a"; 41 | phy_mode = "DPHY"; 42 | discontinuous_clk = "yes"; 43 | dpcm_enable = "false"; 44 | cil_settletime = "0"; 45 | lane_polarity = "6"; 46 | 47 | active_w = "1920"; 48 | active_h = "1080"; 49 | mode_type = "yuv"; 50 | pixel_phase = "uyvy"; 51 | csi_pixel_bit_depth = "16"; 52 | readout_orientation = "0"; 53 | line_length = "1920"; 54 | pix_clk_hz = "220000000"; 55 | default_framerate = "60000000"; /* 60.0 fps */ 56 | }; 57 | 58 | mode1 { // 3840 x 2160, 30 FPS 59 | num_lanes = "4"; 60 | tegra_sinterface = "serial_a"; 61 | phy_mode = "DPHY"; 62 | discontinuous_clk = "yes"; 63 | dpcm_enable = "false"; 64 | cil_settletime = "0"; 65 | lane_polarity = "6"; 66 | 67 | active_w = "3840"; 68 | active_h = "2160"; 69 | mode_type = "yuv"; 70 | pixel_phase = "uyvy"; 71 | csi_pixel_bit_depth = "16"; 72 | readout_orientation = "0"; 73 | line_length = "3840"; 74 | pix_clk_hz = "297000000"; 75 | default_framerate = "30000000"; /* 30.0 fps */ 76 | }; 77 | 78 | mode2 { // 1920 x 2160, 60 FPS 79 | num_lanes = "4"; 80 | tegra_sinterface = "serial_a"; 81 | phy_mode = "DPHY"; 82 | discontinuous_clk = "yes"; 83 | dpcm_enable = "false"; 84 | cil_settletime = "0"; 85 | lane_polarity = "6"; 86 | 87 | active_w = "1920"; 88 | active_h = "2160"; 89 | mode_type = "yuv"; 90 | pixel_phase = "uyvy"; 91 | csi_pixel_bit_depth = "16"; 92 | readout_orientation = "0"; 93 | line_length = "1920"; 94 | pix_clk_hz = "297000000"; 95 | default_framerate = "60000000"; /* 60.0 fps */ 96 | }; 97 | 98 | ports { 99 | #address-cells = <1>; 100 | #size-cells = <0>; 101 | port@0 { 102 | reg = <0>; 103 | status = LT6911_EN; 104 | hdmi2csi_lt6911_out0: endpoint { 105 | status = LT6911_EN; 106 | port-index = <0>; 107 | bus-width = <4>; 108 | remote-endpoint = <&hdmi2csi_csi_in0>; 109 | }; 110 | }; 111 | }; 112 | }; 113 | 114 | /*------------------------------------------------------------------------------ 115 | * Example 2 (Integration Nvidia Jetson Orin NX) 116 | *-----------------------------------------------------------------------------*/ 117 | 118 | /dts-v1/; 119 | /plugin/; 120 | 121 | #define LT6911_EN "okay" 122 | 123 | / { 124 | 125 | 126 | fragment@1 { 127 | target-path="/bus@0"; 128 | __overlay__{ 129 | 130 | i2c@3160000 { 131 | clock-frequency = <100000>; 132 | 133 | lt6911uxc_zhaw_a@2b { 134 | compatible = "lontium,lt6911uxc_zhaw"; 135 | reg = <0x2b>; 136 | status = LT6911_EN; 137 | devnode = "video0"; 138 | sensor_model="lt6911"; 139 | 140 | /* Interrupt */ 141 | interrupt-parent = <&gpio_aon>; 142 | interrupts = ; 143 | 144 | 145 | 146 | /* Physical dimensions of sensor */ 147 | physical_w = "3.674"; 148 | physical_h = "2.738"; 149 | 150 | 151 | refclk_hz = <27000000>; 152 | 153 | mode0 { // 1920 x 1080, 60 FPS 154 | num_lanes = "4"; 155 | tegra_sinterface = "serial_a"; 156 | phy_mode = "DPHY"; 157 | discontinuous_clk = "yes"; 158 | dpcm_enable = "false"; 159 | cil_settletime = "0"; 160 | lane_polarity = "6"; 161 | 162 | active_w = "1920"; 163 | active_h = "1080"; 164 | mode_type = "yuv"; 165 | pixel_phase = "uyvy"; 166 | csi_pixel_bit_depth = "16"; 167 | readout_orientation = "0"; 168 | line_length = "1920"; 169 | pix_clk_hz = "220000000"; 170 | default_framerate = "60000000"; /* 60.0 fps */ 171 | }; 172 | 173 | 174 | /* Physical dimensions of sensor */ 175 | p 176 | 177 | /* Physical dimensions of sensor */ 178 | physical_w = "3.674"; discontinuous_clk = "yes"; 179 | dpcm_enable = "false"; 180 | cil_settletime = "0"; 181 | lane_polarity = "6"; 182 | 183 | active_w = "3840"; 184 | active_h = "2160"; 185 | mode_type = "yuv"; 186 | pixel_phase = "uyvy"; 187 | csi_pixel_bit_depth = "16"; 188 | readout_orientation = "0"; 189 | line_length = "3840"; 190 | pix_clk_hz = "297000000"; 191 | default_framerate = "30000000"; /* 30.0 fps */ 192 | }; 193 | 194 | mode2 { // 1920 x 2160, 60 FPS 195 | num_lanes = "4"; 196 | tegra_sinterface = "serial_a"; 197 | phy_mode = "DPHY"; 198 | discontinuous_clk = "yes"; 199 | dpcm_enable = "false"; 200 | cil_settletime = "0"; 201 | lane_polarity = "6"; 202 | 203 | active_w = "1920"; 204 | active_h = "2160"; 205 | mode_type = "yuv"; 206 | pixel_phase = "uyvy"; 207 | csi_pixel_bit_depth = "16"; 208 | readout_orientation = "0"; 209 | line_length = "1920"; 210 | pix_clk_hz = "297000000"; 211 | default_framerate = "60000000"; /* 60.0 fps */ 212 | }; 213 | 214 | ports { 215 | #address-cells = <1>; 216 | #size-cells = <0>; 217 | port@0 { 218 | reg = <0>; 219 | status = LT6911_EN; 220 | hdmi2csi_lt6911_out0: endpoint { 221 | status = LT6911_EN; 222 | port-index = <0>; 223 | bus-width = <4>; 224 | remote-endpoint = <&hdmi2csi_csi_in0>; 225 | }; 226 | }; 227 | }; 228 | }; 229 | }; 230 | 231 | 232 | host1x@13e00000 { 233 | nvcsi@15a00000 { 234 | num-channels = <1>; 235 | #address-cells = <1>; 236 | #size-cells = <0>; 237 | status = LT6911_EN; 238 | channel@0 { 239 | status = LT6911_EN; 240 | reg = <0>; 241 | discontinuous_clk = "no"; 242 | ports { 243 | #address-cells = <1>; 244 | #size-cells = <0>; 245 | port@0 { 246 | status = LT6911_EN; 247 | reg = <0>; 248 | hdmi2csi_csi_in0: endpoint@0 { 249 | status = LT6911_EN; 250 | port-index = <0>; 251 | bus-width = <4>; 252 | remote-endpoint = <&hdmi2csi_lt6911_out0>; 253 | }; 254 | }; 255 | port@1 { 256 | status = LT6911_EN; 257 | reg = <1>; 258 | hdmi2csi_csi_out0: endpoint@1 { 259 | status = LT6911_EN; 260 | remote-endpoint = <&hdmi2csi_vi_in0>; 261 | }; 262 | }; 263 | }; 264 | }; 265 | }; 266 | }; 267 | }; 268 | }; 269 | 270 | fragment@2 { 271 | target-path="/"; 272 | __overlay__{ 273 | tegra-capture-vi { 274 | num-channels = <1>; 275 | status = LT6911_EN; 276 | ports { 277 | #address-cells = <1>; 278 | #size-cells = <0>; 279 | port@0 { 280 | status = LT6911_EN; 281 | reg = <0>; 282 | hdmi2csi_vi_in0: endpoint { 283 | status = LT6911_EN; 284 | port-index =<0>; 285 | bus-width = <4>; 286 | remote-endpoint = <&hdmi2csi_csi_out0>; 287 | }; 288 | }; 289 | }; 290 | }; 291 | 292 | 293 | 294 | 295 | tegra-camera-platform { 296 | compatible = "nvidia, tegra-camera-platform"; 297 | /** 298 | * Physical settings to calculate max ISO BW 299 | * 300 | * num_csi_lanes = <>; 301 | * Total number of CSI lanes when all cameras are active 302 | * 303 | * max_lane_speed = <>; 304 | * Max lane speed in Kbit/s 305 | * 306 | * min_bits_per_pixel = <>; 307 | * Min bits per pixel 308 | * 309 | * vi_peak_byte_per_pixel = <>; 310 | * Max byte per pixel for the VI ISO case 311 | * 312 | * vi_bw_margin_pct = <>; 313 | * Vi bandwidth margin in percentage 314 | * 315 | * max_pixel_rate = <>; 316 | * Max pixel rate in Kpixel/s for the ISP ISO case 317 | * 318 | * isp_peak_byte_per_pixel = <>; 319 | * Max byte per pixel for the ISP ISO case 320 | * 321 | * isp_bw_margin_pct = <>; 322 | * Isp bandwidth margin in percentage 323 | */ 324 | num_csi_lanes = <4>; 325 | max_lane_speed = <2500000>; 326 | min_bits_per_pixel = <16>; 327 | vi_peak_byte_per_pixel = <3>; 328 | vi_bw_margin_pct = <25>; 329 | isp_peak_byte_per_pixel = <3>; 330 | isp_bw_margin_pct = <25>; 331 | status = LT6911_EN; 332 | 333 | modules { 334 | module0 { 335 | status = LT6911_EN; 336 | badge = "hdmi2csi_left_6911"; 337 | position = "left"; 338 | orientation = "1"; 339 | drivernode0 { 340 | /* Declare PCL support driver (classically known as guid) */ 341 | pcl_id = "v4l2_sensor"; 342 | /* Driver v4l2 device name */ 343 | devname = "lt6911uxc_zhaw 9-002b"; 344 | /* Declare the device-tree hierarchy to driver instance */ 345 | // proc-device-tree = "/proc/device-tree/i2c@3160000/pca9543@70/i2c@0/lt6911uxc_zhaw_a@2b"; 346 | sysfs-device-tree = "/sys/firmware/devicetree/base/bus@0/i2c@3160000/pca9543@70/i2c@0/lt6911uxc_zhaw_a@2b"; 347 | }; 348 | }; 349 | }; 350 | }; 351 | }; 352 | }; 353 | }; 354 | 355 | 356 | -------------------------------------------------------------------------------- /source/lt6911uxc_regs_zhaw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lt6911uxc_regs.h - Lontium 4k60 HDMI-CSI bridge register definitions 3 | * 4 | * Copyright (c) 2020, Alexey Gromov 5 | * 6 | * This program is free software; you can redistribute it and/or modify it 7 | * under the terms and conditions of the GNU General Public License, 8 | * version 2, as published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __LT6911UXC_REGS_H__ 20 | #define __LT6911UXC_REGS_H__ 21 | 22 | /* Control */ 23 | #define SW_BANK 0xFF 24 | 25 | #define ENABLE_I2C 0x80EE 26 | #define DISABLE_WD 0x8010 27 | 28 | /* Resolution registers */ 29 | #define H_TOTAL_0P5 0x867C /* horizontal half total pixel */ 30 | #define H_ACTIVE_0P5 0x8680 /* horizontal half active pixel */ 31 | #define H_FP_0P5 0x8678 /* horizontal half front porch pixel */ 32 | #define H_BP_0P5 0x8676 /* horizontal half back porch pixel */ 33 | #define H_SW_0P5 0x8672 /* hsync half length pixel */ 34 | #define V_TOTAL 0x867A /* vertical total lines */ 35 | #define V_ACTIVE 0x867E /* vertical active lines */ 36 | #define V_BP 0x8674 /* vertical back porch lines */ 37 | #define V_FP 0x8675 /* vertical front porch lines */ 38 | #define V_SW 0x8671 /* vsync length lines */ 39 | 40 | #define SYNC_POL 0x8670 /* hsync/vsync polarity flags */ 41 | #define MASK_VSYNC_POL (1 << 1) 42 | #define MASK_HSYNC_POL (1 << 0) 43 | 44 | /* FPS registers */ 45 | #define MASK_FMI_FREQ2 0x0F 46 | 47 | #define FM1_FREQ_IN2 0x8548 48 | #define FM1_FREQ_IN1 0x8549 49 | #define FM1_FREQ_IN0 0x854A 50 | 51 | #define AD_HALF_PCLK 0x8540 52 | 53 | /* MIPI-TX */ 54 | #define MIPI_TX_CTRL 0x811D 55 | #define MIPI_LANES 0x86A2 56 | #define MIPI_CLK_MODE 0xD468 57 | 58 | /* Audio sample rate */ 59 | #define AUDIO_SR 0xB0AB 60 | 61 | /* Interrupts */ 62 | #define INT_HDMI 0x86A3 63 | #define INT_HDMI_STABLE 0x55 64 | #define INT_HDMI_DISCONNECT 0x88 65 | 66 | #define INT_AUDIO 0x86A5 67 | #define INT_AUDIO_DISCONNECT 0x88 68 | #define INT_AUDIO_SR_HIGH 0x55 69 | #define INT_AUDIO_SR_LOW 0xAA 70 | 71 | #endif /* __LT6911UXC_REGS_H__ */ -------------------------------------------------------------------------------- /source/lt6911uxc_zhaw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lt6911uxc.c - Lontium 4k60 HDMI-CSI bridge driver 3 | * 4 | * Copyright (c) 2020, Lukas Neuner 5 | * 6 | * This program is based on the tc358840 - Toshiba HDMI to CSI-2 bridge from 7 | * Armin Weiss 8 | * 9 | * This program is free software; you can redistribute it and/or modify it 10 | * under the terms and conditions of the GNU General Public License, 11 | * version 2, as published by the Free Software Foundation. 12 | * 13 | * This program is distributed in the hope it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 | * more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | #define DEBUG 1 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #ifdef CONFIG_V4L2_FWNODE 43 | #include 44 | #else 45 | #include 46 | #endif 47 | #include "lt6911uxc_regs_zhaw.h" 48 | 49 | /* v4l2 debug level */ 50 | static int debug; 51 | module_param(debug, int, 0644); 52 | MODULE_PARM_DESC(debug, "debug level (0-3)"); 53 | 54 | /* custom v4l2 controls */ 55 | #define V4L2_CID_USER_LT6911UXC_BASE (V4L2_CID_USER_BASE + 0x1090) 56 | #define LT6911UXC_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_LT6911UXC_BASE + 1) 57 | #define LT6911UXC_CID_AUDIO_PRESENT (V4L2_CID_USER_LT6911UXC_BASE + 2) 58 | #define LT6911UXC_CID_NUM_MIPI_LANES (V4L2_CID_USER_LT6911UXC_BASE + 3) 59 | #define LT6911UXC_NUM_CTRLS 3 60 | 61 | #define LT6911UXC_DEFAULT_MODE 0 62 | #define LT6911UXC_DEFAULT_DATAFMT MEDIA_BUS_FMT_UYVY8_1X16 63 | 64 | /* v4l2 dv timings */ 65 | static struct v4l2_dv_timings default_timing = V4L2_DV_BT_CEA_1920X1080P60; 66 | 67 | static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap_4kp30 = { 68 | .type = V4L2_DV_BT_656_1120, 69 | /* keep this initialization for compatibility with GCC < 4.4.6 */ 70 | .reserved = { 0 }, 71 | /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ 72 | V4L2_INIT_BT_TIMINGS( 73 | 160, 3840, /* min/max width */ 74 | 120, 2160, /* min/max height */ 75 | 25000000, 300000000, /* min/max pixelclock */ 76 | V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 77 | V4L2_DV_BT_STD_CVT, 78 | V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_CUSTOM | 79 | V4L2_DV_BT_CAP_REDUCED_BLANKING) 80 | }; 81 | 82 | 83 | static const int lt6911uxc_60fps[] = { 84 | 60, 85 | }; 86 | 87 | static const int lt6911uxc_30fps[] = { 88 | 30, 89 | }; 90 | 91 | 92 | static const struct camera_common_frmfmt lt6911uxc_frmfmt[] = { 93 | 94 | {{1920, 1080}, lt6911uxc_60fps, 1, 0, 0}, 95 | {{3840, 2160}, lt6911uxc_30fps, 1, 0, 1}, 96 | {{1920, 2160}, lt6911uxc_60fps, 1, 0, 2}, 97 | }; 98 | 99 | struct lt6911uxc { 100 | struct v4l2_ctrl_handler ctrl_handler; 101 | struct i2c_client *i2c_client; 102 | struct v4l2_subdev *sd; 103 | struct media_pad pad[1]; 104 | 105 | struct camera_common_data *s_data; 106 | struct camera_common_pdata *pdata; 107 | 108 | struct v4l2_ctrl *audio_sampling_rate_ctrl; 109 | struct v4l2_ctrl *audio_present_ctrl; 110 | struct v4l2_ctrl *num_mipi_lanes_ctrl; 111 | 112 | struct v4l2_ctrl *ctrls[LT6911UXC_NUM_CTRLS]; // [0]: same as audio_sampling_rate_ctrl, [1]: same as audio_present_ctrl , [2]: same as num_mipi_lanes_ctrl 113 | /* timing / media format */ 114 | struct v4l2_dv_timings timings; 115 | struct v4l2_dv_timings detected_timings;/* timings detected from phy */ 116 | 117 | struct mutex lock; 118 | 119 | /* controls */ 120 | u8 bank; /* active reg-bank for I2C */ 121 | bool enable_i2c; 122 | bool signal_present; 123 | /* expose audio capabilties */ 124 | u32 mbus_fmt_code; /* current media bus format */ 125 | 126 | //struct lt6911uxc_platform_data *pdata; 127 | }; 128 | 129 | static const struct camera_common_colorfmt lt6911uxc_color_fmts[] = { 130 | { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, V4L2_PIX_FMT_UYVY}, 131 | }; 132 | 133 | static const struct v4l2_event lt6911uxc_ev_source_change = { 134 | .type = V4L2_EVENT_SOURCE_CHANGE, 135 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 136 | }; 137 | 138 | static inline struct lt6911uxc *to_state(struct v4l2_subdev *sd) 139 | { 140 | struct camera_common_data *tmp = container_of(sd, struct camera_common_data, subdev); 141 | return (struct lt6911uxc *)(tmp->priv); 142 | } 143 | 144 | /* ------ I2C --------------------------------------------------------------- */ 145 | 146 | static void lt6911uxc_reg_bank(struct v4l2_subdev *sd, u8 bank) 147 | { 148 | struct lt6911uxc *state = to_state(sd); 149 | struct i2c_client *client = state->i2c_client; 150 | int err; 151 | struct i2c_msg msg; 152 | u8 data[2]; 153 | u8 address; 154 | 155 | if(state->bank == bank) 156 | return; 157 | dev_dbg(&client->dev, "i2c: change register bank to 0x%02X\n", 158 | bank); 159 | 160 | address = 0xFF; 161 | msg.addr = client->addr; 162 | msg.buf = data; 163 | msg.len = 2; 164 | msg.flags = 0; 165 | 166 | data[0] = address; 167 | data[1] = bank; 168 | 169 | err = i2c_transfer(client->adapter, &msg, 1); 170 | if (err != 1) { 171 | dev_err(&client->dev, "%s: switch to bank 0x%x from 0x%x failed\n", 172 | __func__, bank, client->addr); 173 | return; 174 | } 175 | state->bank = bank; 176 | } 177 | 178 | static void lt6911uxc_i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) 179 | { 180 | struct lt6911uxc *state = to_state(sd); 181 | struct i2c_client *client = state->i2c_client; 182 | int err; 183 | struct i2c_msg msg; 184 | u8 data[2]; 185 | u8 address; 186 | 187 | /* write register bank offset */ 188 | u8 bank = (reg >> 8) & 0xFF; 189 | lt6911uxc_reg_bank(sd, bank); 190 | 191 | address = reg & 0xFF; 192 | msg.addr = client->addr; 193 | msg.buf = data; 194 | msg.len = 2; 195 | msg.flags = 0; 196 | 197 | data[0] = address; 198 | data[1] = val; 199 | 200 | err = i2c_transfer(client->adapter, &msg, 1); 201 | 202 | if (err != 1) { 203 | dev_err(&client->dev, "%s: write register 0x%x from 0x%x failed\n", 204 | __func__, reg, client->addr); 205 | return; 206 | } 207 | dev_dbg(&client->dev, "i2c: write register: 0x%04X = 0x%02X\n", 208 | reg, val); 209 | } 210 | 211 | static void lt6911uxc_i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) 212 | { 213 | struct lt6911uxc *state = to_state(sd); 214 | struct i2c_client *client = state->i2c_client; 215 | int err; 216 | u8 reg_addr[1] = { (u8)(reg & 0xff) }; 217 | u8 bank_addr = (u8)((reg >> 8) & 0xFF); 218 | 219 | struct i2c_msg msgs[] = { 220 | { 221 | .addr = client->addr, 222 | .flags = 0, /* write */ 223 | .len = 1, 224 | .buf = reg_addr, 225 | }, 226 | { 227 | .addr = client->addr, 228 | .flags = I2C_M_RD, /* read n bytes */ 229 | .len = n, 230 | .buf = values, 231 | }, 232 | }; 233 | 234 | /* write register bank offset */ 235 | lt6911uxc_reg_bank(sd, bank_addr); 236 | 237 | err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 238 | if (err != ARRAY_SIZE(msgs)) { 239 | dev_err(&client->dev, "%s: read register 0x%04X from 0x%x failed\n", 240 | __func__, reg, client->addr); 241 | } 242 | } 243 | 244 | static u8 lt6911uxc_i2c_rd8(struct v4l2_subdev *sd, u16 reg) 245 | { 246 | u8 val; 247 | 248 | lt6911uxc_i2c_rd(sd, reg, &val, 1); 249 | 250 | dev_dbg(sd->dev, "i2c: read 0x%04X = 0x%02X\n", reg, val); 251 | return val; 252 | } 253 | 254 | static u16 lt6911uxc_i2c_rd16(struct v4l2_subdev *sd, u16 reg) 255 | { 256 | u16 val; 257 | u8 tmp; 258 | 259 | lt6911uxc_i2c_rd(sd, reg, (u8 *)&val, 2); 260 | /* high byte always at lower address -> swap */ 261 | tmp = val & 0xFF; 262 | val = (val >> 8) | tmp << 8; 263 | 264 | dev_dbg(sd->dev, "i2c: read 0x%04X = 0x%04X\n", reg, val); 265 | return val; 266 | } 267 | 268 | /* ------ STATUS / CTRL ----------------------------------------------------- */ 269 | 270 | static inline bool no_signal(struct v4l2_subdev *sd) 271 | { 272 | struct lt6911uxc *state = to_state(sd); 273 | return !state->signal_present; 274 | } 275 | 276 | static void lt6911uxc_ext_control(struct v4l2_subdev *sd, bool enable) 277 | { 278 | struct lt6911uxc *state = to_state(sd); 279 | 280 | if(state->enable_i2c == enable) 281 | return; 282 | 283 | state->enable_i2c = enable; 284 | if (enable) { 285 | dev_dbg(sd->dev, "%s(): enable external i2c control\n", __func__); 286 | lt6911uxc_i2c_wr8(sd, ENABLE_I2C, 0x01); 287 | lt6911uxc_i2c_wr8(sd, DISABLE_WD, 0x00); 288 | } else { 289 | dev_dbg(sd->dev, "%s(): disable external i2c control\n", __func__); 290 | lt6911uxc_i2c_wr8(sd, ENABLE_I2C, 0x00); 291 | } 292 | } 293 | 294 | static int lt6911uxc_csi_enable(struct v4l2_subdev *sd, bool enable) 295 | { 296 | dev_dbg(sd->dev, "%s():\n", __func__); 297 | 298 | if (enable) { 299 | lt6911uxc_i2c_wr8(sd, MIPI_TX_CTRL, 0xFB); 300 | } else { 301 | lt6911uxc_i2c_wr8(sd, MIPI_TX_CTRL, 0x00); 302 | } 303 | return 0; 304 | } 305 | 306 | static int lt6911uxc_get_audio_sampling_rate(struct lt6911uxc* state) 307 | { 308 | int audio_fs, idx; 309 | static const int eps = 1500; 310 | static const int rates_default [] = { 311 | 32000, 44100, 48000, 88200, 96000, 176400, 192000 312 | }; 313 | 314 | audio_fs = lt6911uxc_i2c_rd8(state->sd, AUDIO_SR) * 1000; 315 | dev_dbg(&state->i2c_client->dev, "%s: Audio sample rate %d [Hz]\n", 316 | __func__, audio_fs); 317 | 318 | /* audio_fs is an approximation of sample rate - search nearest */ 319 | for(idx = 0; idx < ARRAY_SIZE(rates_default); ++idx) 320 | { 321 | if ((rates_default[idx] - eps < audio_fs) && 322 | (rates_default[idx] + eps > audio_fs)) 323 | return rates_default[idx]; 324 | } 325 | dev_err(&state->i2c_client->dev, "%s: unhandled sampling rate %d [Hz]", 326 | __func__, audio_fs); 327 | return 0; 328 | } 329 | 330 | /* ------ TIMINGS ----------------------------------------------------------- */ 331 | 332 | static int lt6911uxc_detect_timings(struct v4l2_subdev *sd, 333 | struct v4l2_dv_timings *timings, u8 lanes) 334 | { 335 | struct v4l2_bt_timings *bt = &timings->bt; 336 | u32 htot, vtot, fps; 337 | u16 width, height; 338 | u8 fm2, fm1, fm0, pol; 339 | int half_pixel_clk, frame_interval; 340 | 341 | struct lt6911uxc *state = to_state(sd); 342 | struct device *dev = &state->i2c_client->dev; 343 | struct camera_common_data *s_data = to_camera_common_data(dev); 344 | 345 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); 346 | 347 | if (no_signal(sd)) { 348 | v4l2_err(sd, "%s: no valid signal\n", __func__); 349 | return -ENOLINK; 350 | } 351 | 352 | timings->type = V4L2_DV_BT_656_1120; 353 | bt->interlaced = V4L2_DV_PROGRESSIVE; 354 | 355 | /* video frame size */ 356 | width = lt6911uxc_i2c_rd16(sd, H_ACTIVE_0P5); 357 | if (lanes <= 4) { 358 | width = width * 2; 359 | } 360 | height = lt6911uxc_i2c_rd16(sd, V_ACTIVE); 361 | v4l2_dbg(1, debug, sd, "frame active - width %u height %u\n", 362 | width, height); 363 | bt->width = width; 364 | bt->height = height; 365 | 366 | /* front/back porch, sync pulse */ 367 | bt->hfrontporch = lt6911uxc_i2c_rd16(sd, H_FP_0P5); 368 | bt->hbackporch = lt6911uxc_i2c_rd16(sd, H_BP_0P5); 369 | bt->hsync = lt6911uxc_i2c_rd16(sd, H_SW_0P5); 370 | if (lanes <= 4) { 371 | bt->hfrontporch = bt->hfrontporch * 2; 372 | bt->hbackporch = bt->hbackporch * 2; 373 | bt->hsync = bt->hsync * 2; 374 | } 375 | 376 | bt->vfrontporch = lt6911uxc_i2c_rd8(sd, V_FP); 377 | bt->vbackporch = lt6911uxc_i2c_rd8(sd, V_BP); 378 | bt->vsync = lt6911uxc_i2c_rd8(sd, V_SW); 379 | 380 | pol = lt6911uxc_i2c_rd8(sd, SYNC_POL); 381 | if (pol & MASK_HSYNC_POL) 382 | bt->polarities |= V4L2_DV_HSYNC_POS_POL; 383 | if (pol & MASK_VSYNC_POL) 384 | bt->polarities |= V4L2_DV_VSYNC_POS_POL; 385 | 386 | /* ------ pixelclock ------ */ 387 | 388 | /* set frequency meter to half pixel clock */ 389 | lt6911uxc_i2c_wr8(sd, AD_HALF_PCLK, 0x21); 390 | usleep_range(10000,10100); /* needed by manufacturer */ 391 | 392 | fm2 = lt6911uxc_i2c_rd8(sd, FM1_FREQ_IN2) & MASK_FMI_FREQ2; 393 | fm1 = lt6911uxc_i2c_rd8(sd, FM1_FREQ_IN1); 394 | fm0 = lt6911uxc_i2c_rd8(sd, FM1_FREQ_IN0); 395 | 396 | if (lanes <= 4) { 397 | half_pixel_clk = fm2 << 16 | fm1 << 8 | fm0; 398 | } else { 399 | half_pixel_clk = (fm2 << 16 | fm1 << 8 | fm0) / 2; 400 | } 401 | 402 | v4l2_dbg(1, debug, sd, "pixel clock %d\n", half_pixel_clk*2); 403 | 404 | htot = V4L2_DV_BT_FRAME_WIDTH(bt); 405 | vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); 406 | 407 | /* frameinterval in ms */ 408 | frame_interval = DIV_ROUND_CLOSEST((htot * vtot), (half_pixel_clk * 2)); 409 | fps = DIV_ROUND_CLOSEST((half_pixel_clk * 2 * 1000 * 100), (htot * vtot)); 410 | v4l2_dbg(1, debug, sd, "frame_interval %d ms, fps %d.%02d\n", 411 | frame_interval, (int)(fps/100), (int)(fps%100)); 412 | bt->pixelclock = half_pixel_clk * 2 * 1000; 413 | 414 | // Separate <250 MHz from >250 MHz for deskewing calibration 415 | if (bt->pixelclock >= 250000000) { 416 | s_data->mode = 1; 417 | } else { 418 | s_data->mode = 0; 419 | } 420 | 421 | /* sanity check */ 422 | if ((bt->width < 640) || (bt->height < 480) || 423 | (htot <= width) || (vtot <= height)) { 424 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); 425 | return -ENOLCK; 426 | } 427 | return 0; 428 | } 429 | 430 | /* ------ CORE OPS ---------------------------------------------------------- */ 431 | 432 | static int lt6911uxc_log_status(struct v4l2_subdev *sd) 433 | { 434 | struct lt6911uxc *state = to_state(sd); 435 | 436 | v4l2_info(sd, "----- Timings -----\n"); 437 | if (!&state->detected_timings.bt.width) { 438 | v4l2_info(sd, "no video detected\n"); 439 | } else { 440 | v4l2_print_dv_timings(sd->name, "detected format: ", 441 | &state->detected_timings, true); 442 | } 443 | v4l2_print_dv_timings(sd->name, "configured format: ", &state->timings, 444 | true); 445 | 446 | return 0; 447 | } 448 | 449 | static int lt6911uxc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, 450 | struct v4l2_event_subscription *sub) 451 | { 452 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 453 | 454 | switch (sub->type) { 455 | case V4L2_EVENT_SOURCE_CHANGE: 456 | return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); 457 | default: 458 | return -EINVAL; 459 | } 460 | } 461 | 462 | /* ------ IRQ --------------------------------------------------------------- */ 463 | 464 | static void lt6911uxc_hdmi_int_handler(struct lt6911uxc *state, 465 | bool *handled) 466 | { 467 | u8 int_event; 468 | struct v4l2_dv_timings timings = {}; 469 | u8 fm2, fm1, fm0, lanes; 470 | int byte_clock; 471 | struct device *dev = &state->i2c_client->dev; 472 | 473 | /* Read interrupt event */ 474 | int_event = lt6911uxc_i2c_rd8(state->sd, INT_HDMI); 475 | dev_dbg(dev, "%s: HDMI event = 0x%02X\n", __func__, int_event); 476 | 477 | switch(int_event) { 478 | case INT_HDMI_DISCONNECT: 479 | /* stop MIPI output */ 480 | dev_info(dev,"HDMI signal disconnected\n"); 481 | lt6911uxc_csi_enable(state->sd, false); 482 | v4l2_ctrl_s_ctrl(state->num_mipi_lanes_ctrl, 0); 483 | 484 | if (state->signal_present) { 485 | state->signal_present = false; 486 | v4l2_subdev_notify_event(state->sd, 487 | <6911uxc_ev_source_change); 488 | dev_dbg(dev,"event: no signal\n"); 489 | 490 | memset(&state->timings, 0, sizeof(state->timings)); 491 | memset(&state->detected_timings, 0, 492 | sizeof(state->detected_timings)); 493 | } 494 | if (handled) 495 | *handled = true; 496 | break; 497 | 498 | case INT_HDMI_STABLE: 499 | dev_info(dev, "HDMI signal stable\n"); 500 | 501 | /* at each HDMI-stable event renew timings */ 502 | state->signal_present = true; 503 | lanes = lt6911uxc_i2c_rd8(state->sd, MIPI_LANES); 504 | dev_dbg(dev, "MIPI lanes %d\n", lanes); 505 | v4l2_ctrl_s_ctrl(state->num_mipi_lanes_ctrl, lanes); 506 | lt6911uxc_detect_timings(state->sd, &timings, lanes); 507 | 508 | /* byte clock / MIPI clock */ 509 | lt6911uxc_i2c_wr8(state->sd, AD_HALF_PCLK, 0x1B); 510 | usleep_range(10000,10100); 511 | fm2 = lt6911uxc_i2c_rd8(state->sd, FM1_FREQ_IN2) & MASK_FMI_FREQ2; 512 | fm1 = lt6911uxc_i2c_rd8(state->sd, FM1_FREQ_IN1); 513 | fm0 = lt6911uxc_i2c_rd8(state->sd, FM1_FREQ_IN0); 514 | 515 | byte_clock = fm2 << 16 | fm1 << 8 | fm0; 516 | dev_dbg(dev, "byte clock %d [kHz], MIPI clock %d [kHz]\n", 517 | byte_clock, byte_clock*4); 518 | 519 | /* MIPI */ 520 | //lanes = lt6911uxc_i2c_rd8(state->sd, MIPI_LANES); 521 | //dev_dbg(dev, "MIPI lanes %d\n", lanes); 522 | 523 | lt6911uxc_csi_enable(state->sd, true); 524 | 525 | /* store newly detected timings (if any) if those are 526 | * detected for the first time */ 527 | if (!state->detected_timings.bt.width) { 528 | state->detected_timings = timings; 529 | dev_dbg(dev,"store new timings"); 530 | } else if (tegra_v4l2_match_dv_timings(&timings, 531 | &state->detected_timings, 250000, false)) { 532 | dev_dbg(dev,"ignore timings change"); 533 | } else { 534 | state->detected_timings = timings; 535 | dev_dbg(dev,"detected timings updated"); 536 | } 537 | 538 | 539 | if (handled) 540 | *handled = true; 541 | break; 542 | default: 543 | dev_err(dev, "%s: unhandled = 0x%02X\n", __func__, int_event); 544 | return; 545 | } 546 | } 547 | 548 | static void lt6911uxc_audio_int_handler(struct lt6911uxc *state, 549 | bool *handled) 550 | { 551 | u8 int_event; 552 | int audio_fs = 0; 553 | struct device *dev = &state->i2c_client->dev; 554 | 555 | /* read interrupt event */ 556 | int_event = lt6911uxc_i2c_rd8(state->sd, INT_AUDIO); 557 | dev_dbg(dev,"%s: Audio event = 0x%02X\n", __func__, int_event); 558 | 559 | switch(int_event) { 560 | case INT_AUDIO_DISCONNECT: 561 | dev_info(dev,"Audio signal disconnected"); 562 | audio_fs = 0; 563 | break; 564 | case INT_AUDIO_SR_HIGH: 565 | case INT_AUDIO_SR_LOW: 566 | if (state->signal_present) { 567 | dev_info(dev,"Audio sampling rate changed"); 568 | audio_fs = lt6911uxc_get_audio_sampling_rate(state); 569 | } 570 | else 571 | audio_fs = 0; 572 | break; 573 | default: 574 | dev_err(dev,"%s: unhandled = 0x%02X\n", __func__, int_event); 575 | return; 576 | } 577 | 578 | v4l2_ctrl_s_ctrl(state->audio_present_ctrl, (audio_fs != 0)); 579 | v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, audio_fs); 580 | 581 | if (handled) 582 | *handled = true; 583 | return; 584 | } 585 | 586 | static int lt6911uxc_isr(struct v4l2_subdev *sd, bool *handled) 587 | { 588 | struct lt6911uxc *state = to_state(sd); 589 | 590 | mutex_lock(&state->lock); 591 | dev_dbg(sd->dev, "%s in kthread %d\n", __func__, current->pid); 592 | 593 | lt6911uxc_ext_control(sd, true); 594 | 595 | /* Retrieve interrupt event */ 596 | lt6911uxc_hdmi_int_handler(state, handled); 597 | 598 | lt6911uxc_audio_int_handler(state, handled); 599 | 600 | lt6911uxc_log_status(sd); 601 | 602 | lt6911uxc_ext_control(sd, false); 603 | 604 | mutex_unlock(&state->lock); 605 | return 0; 606 | } 607 | 608 | static irqreturn_t lt6911uxc_irq_handler(int irq, void *dev_id) 609 | { 610 | struct v4l2_subdev *sd = dev_id; 611 | bool handled = false; 612 | 613 | lt6911uxc_isr(sd, &handled); 614 | 615 | return handled ? IRQ_HANDLED : IRQ_NONE; 616 | } 617 | 618 | /* ------ VIDEO OPS --------------------------------------------------------- */ 619 | 620 | static const struct v4l2_dv_timings_cap* lt6911uxc_g_timings_cap( 621 | struct lt6911uxc *state) 622 | { 623 | return <6911uxc_timings_cap_4kp30; 624 | } 625 | 626 | static int lt6911uxc_g_input_status(struct v4l2_subdev *sd, u32 *status) 627 | { 628 | *status = 0; 629 | *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; 630 | 631 | v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); 632 | return 0; 633 | } 634 | 635 | static int lt6911uxc_s_dv_timings(struct v4l2_subdev *sd, 636 | struct v4l2_dv_timings *timings) 637 | { 638 | struct lt6911uxc *state = to_state(sd); 639 | 640 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 641 | 642 | if (!v4l2_valid_dv_timings(timings, lt6911uxc_g_timings_cap(state), 643 | NULL, NULL)) { 644 | v4l2_err(sd, "%s: timings out of range\n", __func__); 645 | return -EINVAL; 646 | } 647 | 648 | /* Fill optional fields .standards and .flags if format is part of 649 | * CEA-861 / VESA-DMT timings */ 650 | v4l2_find_dv_timings_cap(timings, lt6911uxc_g_timings_cap(state), 0, 651 | NULL, NULL); 652 | 653 | /* Verify if new timings match current timings */ 654 | if (tegra_v4l2_match_dv_timings(timings, &state->timings, 0, false)) { 655 | v4l2_info(sd, "%s: no change\n", __func__); 656 | return 0; 657 | } 658 | 659 | memset(timings->bt.reserved, 0, sizeof(timings->bt.reserved)); 660 | state->timings = *timings; 661 | 662 | if (debug) 663 | v4l2_print_dv_timings(sd->name, "s_dv_timings: ", 664 | &state->timings, true); 665 | return 0; 666 | } 667 | 668 | static int lt6911uxc_g_dv_timings(struct v4l2_subdev *sd, 669 | struct v4l2_dv_timings *timings) 670 | { 671 | struct lt6911uxc *state = to_state(sd); 672 | 673 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 674 | 675 | *timings = state->timings; 676 | return 0; 677 | } 678 | 679 | static int lt6911uxc_query_dv_timings(struct v4l2_subdev *sd, 680 | struct v4l2_dv_timings *timings) 681 | { 682 | struct lt6911uxc *state = to_state(sd); 683 | 684 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 685 | 686 | if (no_signal(sd)) { 687 | v4l2_warn(sd, "%s: no valid signal\n", __func__); 688 | return -ENOLINK; 689 | } 690 | 691 | if (!v4l2_valid_dv_timings(&state->detected_timings, 692 | lt6911uxc_g_timings_cap(state), NULL, NULL)) { 693 | v4l2_warn(sd, "%s: timings out of range\n", __func__); 694 | return -ERANGE; 695 | } 696 | 697 | *timings = state->detected_timings; 698 | if (debug) 699 | v4l2_print_dv_timings(sd->name, "query_dv_timings: ", 700 | timings, true); 701 | return 0; 702 | } 703 | 704 | static int lt6911uxc_s_stream(struct v4l2_subdev *sd, int enable) 705 | { 706 | v4l2_dbg(3, debug, sd, "%s(): enable %d \n", __func__, enable); 707 | 708 | /* handled by ISR */ 709 | return 0; 710 | } 711 | 712 | /* ------ PAD OPS ----------------------------------------------------------- */ 713 | 714 | static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, 715 | struct v4l2_subdev_state *cfg, 716 | struct v4l2_subdev_format *format) 717 | { 718 | struct lt6911uxc *state = to_state(sd); 719 | struct v4l2_mbus_framefmt *fmt = &format->format; 720 | int i = 0; 721 | 722 | v4l2_dbg(3, debug, sd,"%s():\n", __func__); 723 | 724 | if (format->pad != 0) 725 | return -EINVAL; 726 | 727 | /* retrieve mbus pixelcode and active video frame size */ 728 | fmt->code = state->mbus_fmt_code; 729 | fmt->width = state->timings.bt.width; 730 | fmt->height = state->timings.bt.height; 731 | fmt->field = V4L2_FIELD_NONE; 732 | 733 | for (i = 0; i < ARRAY_SIZE(lt6911uxc_color_fmts); i++) { 734 | if (lt6911uxc_color_fmts[i].code == fmt->code) { 735 | fmt->colorspace = lt6911uxc_color_fmts[i].colorspace; 736 | break; 737 | } 738 | } 739 | 740 | switch (fmt->code) { 741 | case MEDIA_BUS_FMT_UYVY8_1X16: 742 | default: 743 | fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; 744 | fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; 745 | break; 746 | } 747 | 748 | return 0; 749 | } 750 | 751 | static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, 752 | struct v4l2_subdev_state *cfg, 753 | struct v4l2_subdev_format *format) 754 | { 755 | struct lt6911uxc *state = to_state(sd); 756 | u32 code = format->format.code; /* is overwritten by get_fmt */ 757 | int ret; 758 | 759 | v4l2_dbg(2, debug, sd, 760 | "%s(): query format - width=%d, height=%d, code=0x%08X\n", 761 | __func__, format->format.width, format->format.height, code); 762 | 763 | /* adjust requested format based on current DV timings */ 764 | ret = lt6911uxc_get_fmt(sd, cfg, format); 765 | format->format.code = code; 766 | 767 | if (ret) 768 | return ret; 769 | 770 | switch (code) { 771 | case MEDIA_BUS_FMT_UYVY8_1X16: 772 | break; 773 | default: 774 | return -EINVAL; 775 | } 776 | 777 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) 778 | return 0; 779 | 780 | state->mbus_fmt_code = format->format.code; 781 | v4l2_dbg(2, debug, sd, 782 | "%s(): current format - width=%d, height=%d, code=0x%08X\n", 783 | __func__, format->format.width, format->format.height, 784 | state->mbus_fmt_code); 785 | return 0; 786 | } 787 | 788 | static int lt6911uxc_enum_mbus_code(struct v4l2_subdev *sd, 789 | struct v4l2_subdev_state *cfg, 790 | struct v4l2_subdev_mbus_code_enum *code) 791 | { 792 | v4l2_dbg(3, debug, sd, "%s()\n", __func__); 793 | 794 | if (code->index >= ARRAY_SIZE(lt6911uxc_color_fmts)) 795 | return -EINVAL; 796 | 797 | code->code = lt6911uxc_color_fmts[code->index].code; 798 | v4l2_dbg(2, debug, sd, "%s(): fmt-code 0x%04X\n", __func__, code->code); 799 | 800 | return 0; 801 | } 802 | 803 | static int lt6911uxc_dv_timings_cap(struct v4l2_subdev *sd, 804 | struct v4l2_dv_timings_cap *cap) 805 | { 806 | struct lt6911uxc *state = to_state(sd); 807 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 808 | 809 | if (cap->pad != 0) 810 | return -EINVAL; 811 | 812 | *cap = *lt6911uxc_g_timings_cap(state); 813 | return 0; 814 | } 815 | 816 | static int lt6911uxc_enum_dv_timings(struct v4l2_subdev *sd, 817 | struct v4l2_enum_dv_timings *timings) 818 | { 819 | struct lt6911uxc *state = to_state(sd); 820 | v4l2_dbg(3, debug, sd, "%s():\n", __func__); 821 | 822 | if (timings->pad != 0) 823 | return -EINVAL; 824 | 825 | /* filter non supported DV timings */ 826 | return v4l2_enum_dv_timings_cap(timings, 827 | lt6911uxc_g_timings_cap(state), NULL, NULL); 828 | } 829 | 830 | /* ------ Register OPS ------------------------------------------------------ */ 831 | 832 | static int lt6911uxc_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 833 | { 834 | struct i2c_client *client = v4l2_get_subdevdata(sd); 835 | 836 | dev_dbg(&client->dev, "%s:\n", __func__); 837 | return 0; 838 | } 839 | 840 | static const struct v4l2_subdev_internal_ops lt6911uxc_subdev_internal_ops = { 841 | .open = lt6911uxc_open, 842 | }; 843 | 844 | static struct v4l2_subdev_core_ops lt6911uxc_subdev_core_ops = { 845 | .log_status = lt6911uxc_log_status, 846 | .subscribe_event = lt6911uxc_subscribe_event, 847 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, 848 | }; 849 | 850 | static struct v4l2_subdev_video_ops lt6911uxc_subdev_video_ops = { 851 | .g_input_status = lt6911uxc_g_input_status, 852 | .s_dv_timings = lt6911uxc_s_dv_timings, 853 | .g_dv_timings = lt6911uxc_g_dv_timings, 854 | .query_dv_timings = lt6911uxc_query_dv_timings, 855 | .s_stream = lt6911uxc_s_stream, 856 | }; 857 | 858 | static const struct v4l2_subdev_pad_ops lt6911uxc_pad_ops = { 859 | .set_fmt = lt6911uxc_set_fmt, 860 | .get_fmt = lt6911uxc_get_fmt, 861 | .enum_mbus_code = lt6911uxc_enum_mbus_code, 862 | .dv_timings_cap = lt6911uxc_dv_timings_cap, 863 | .enum_dv_timings = lt6911uxc_enum_dv_timings, 864 | }; 865 | 866 | static struct v4l2_subdev_ops lt6911uxc_ops = { 867 | .core = <6911uxc_subdev_core_ops, 868 | .video = <6911uxc_subdev_video_ops, 869 | .pad = <6911uxc_pad_ops, 870 | }; 871 | 872 | #ifdef CONFIG_MEDIA_CONTROLLER 873 | static const struct media_entity_operations lt6911uxc_media_ops = { 874 | .link_validate = v4l2_subdev_link_validate, 875 | }; 876 | #endif 877 | 878 | /* ------ CUSTOM CTRLS ------------------------------------------------------ */ 879 | 880 | static const struct v4l2_ctrl_config lt6911uxc_ctrl_audio_sampling_rate = { 881 | .id = LT6911UXC_CID_AUDIO_SAMPLING_RATE, 882 | .name = "Audio Sampling Rate", 883 | .type = V4L2_CTRL_TYPE_INTEGER, 884 | .min = 0, 885 | .max = 192000, 886 | .step = 1, 887 | .def = 0, 888 | .flags = V4L2_CTRL_FLAG_READ_ONLY, 889 | }; 890 | 891 | static const struct v4l2_ctrl_config lt6911uxc_ctrl_audio_present = { 892 | .id = LT6911UXC_CID_AUDIO_PRESENT, 893 | .name = "Audio Present", 894 | .type = V4L2_CTRL_TYPE_BOOLEAN, 895 | .min = 0, 896 | .max = 1, 897 | .step = 1, 898 | .def = 0, 899 | .flags = V4L2_CTRL_FLAG_READ_ONLY, 900 | }; 901 | 902 | static const struct v4l2_ctrl_config lt6911uxc_ctrl_num_mipi_lanes = { 903 | .id = LT6911UXC_CID_NUM_MIPI_LANES, 904 | .name = "Number of MIPI Lanes Used", 905 | .type = V4L2_CTRL_TYPE_INTEGER, 906 | .min = 0, 907 | .max = 8, 908 | .step = 4, 909 | .def = 0, 910 | .flags = V4L2_CTRL_FLAG_READ_ONLY, 911 | }; 912 | 913 | /* ------ Driver setup ------------------------------------------------------ */ 914 | 915 | static void lt6911uxc_initial_setup(struct lt6911uxc *state) 916 | { 917 | state->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_1X16; 918 | state->signal_present = false; 919 | state->enable_i2c = false; 920 | mutex_init(&state->lock); 921 | 922 | /* Init Timings */ 923 | lt6911uxc_s_dv_timings(state->sd, &default_timing); 924 | } 925 | 926 | static const struct of_device_id lt6911uxc_of_match[] = { 927 | { .compatible = "lontium,lt6911uxc_zhaw" }, 928 | { } 929 | }; 930 | MODULE_DEVICE_TABLE(of, lt6911uxc_of_match); 931 | 932 | static struct camera_common_pdata* lt6911uxc_parse_dt(struct i2c_client *client, 933 | struct camera_common_data *s_data) 934 | { 935 | struct device_node *np = client->dev.of_node; 936 | struct camera_common_pdata *board_priv_pdata; 937 | const struct of_device_id *match; 938 | int err = 0; 939 | int gpio; 940 | const char *str; 941 | 942 | if (!np) 943 | return NULL; 944 | 945 | match = of_match_device(lt6911uxc_of_match, &client->dev); 946 | if (!match) { 947 | dev_err(&client->dev, "Failed to find matching dt id\n"); 948 | return NULL; 949 | } 950 | 951 | err = of_property_read_string(np, "use_sensor_mode_id", &str); 952 | if (!err) { 953 | if (!strcmp(str, "true")) 954 | s_data->use_sensor_mode_id = true; 955 | else 956 | s_data->use_sensor_mode_id = false; 957 | } else { 958 | dev_info(&client->dev, "use_sensor_mode_id not found, setting to false\n"); 959 | s_data->use_sensor_mode_id = false; 960 | } 961 | board_priv_pdata = devm_kzalloc(&client->dev, 962 | sizeof(*board_priv_pdata), GFP_KERNEL); 963 | 964 | gpio = of_get_named_gpio(np, "reset-gpio", 0); 965 | if(gpio < 0) { 966 | if(gpio == -EPROBE_DEFER) { 967 | dev_err(&client->dev, "reset-gpio read failed: (%d)\n", 968 | gpio); 969 | goto prop_err; 970 | } 971 | dev_info(&client->dev, "reset-gpio not found, ignoring\n"); 972 | } 973 | board_priv_pdata->reset_gpio = (unsigned int)gpio; 974 | return board_priv_pdata; 975 | 976 | prop_err: 977 | dev_err(&client->dev, "Could not parse DT parameters\n"); 978 | devm_kfree(&client->dev, board_priv_pdata); 979 | return NULL; 980 | } 981 | 982 | static struct camera_common_sensor_ops lt6911uxc_common_ops = { 983 | .numfrmfmts = ARRAY_SIZE(lt6911uxc_frmfmt), 984 | .frmfmt_table = lt6911uxc_frmfmt, 985 | }; 986 | 987 | 988 | 989 | static int lt6911uxc_probe(struct i2c_client *client, 990 | const struct i2c_device_id *id) 991 | { 992 | struct camera_common_data *common_data; 993 | struct lt6911uxc *priv; 994 | struct v4l2_subdev *sd; 995 | int err = 0; 996 | 997 | dev_info(&client->dev, "Probing lt6911uxc\n"); 998 | 999 | if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) 1000 | return -EINVAL; 1001 | 1002 | priv = devm_kzalloc(&client->dev, 1003 | sizeof(struct lt6911uxc), 1004 | GFP_KERNEL); 1005 | if (!priv) 1006 | { 1007 | dev_err(&client->dev, "unable to allocate priv memory!\n"); 1008 | return -ENOMEM; 1009 | } 1010 | 1011 | common_data = devm_kzalloc(&client->dev, 1012 | sizeof(struct camera_common_data), 1013 | GFP_KERNEL); 1014 | 1015 | if (!common_data) { 1016 | dev_err(&client->dev, "unable to allocate common data memory!\n"); 1017 | return -ENOMEM; 1018 | } 1019 | 1020 | if (client->dev.of_node) 1021 | priv->pdata = lt6911uxc_parse_dt(client, common_data); 1022 | 1023 | if (!priv->pdata) { 1024 | dev_err(&client->dev, "unable to get platform data\n"); 1025 | return -EFAULT; 1026 | } 1027 | 1028 | 1029 | common_data->ops = <6911uxc_common_ops; 1030 | common_data->ctrl_handler = &priv->ctrl_handler; 1031 | common_data->dev = &client->dev; 1032 | common_data->frmfmt = <6911uxc_frmfmt[0]; 1033 | common_data->colorfmt = lt6911uxc_color_fmts; 1034 | common_data->ctrls = priv->ctrls; 1035 | common_data->priv = (void *)priv; 1036 | common_data->numctrls = LT6911UXC_NUM_CTRLS; 1037 | common_data->numfmts = ARRAY_SIZE(lt6911uxc_frmfmt); 1038 | common_data->def_mode = 0; 1039 | 1040 | common_data->def_width = 1920; 1041 | common_data->def_height = 1080; 1042 | common_data->fmt_width = common_data->def_width; 1043 | common_data->fmt_height = common_data->def_height; 1044 | 1045 | 1046 | priv->i2c_client = client; 1047 | priv->s_data = common_data; 1048 | priv->sd = &common_data->subdev; 1049 | priv->sd->dev = &client->dev; 1050 | priv->s_data->dev = &client->dev; 1051 | sd = priv->sd; 1052 | 1053 | 1054 | /* initial setup */ 1055 | lt6911uxc_initial_setup(priv); 1056 | 1057 | err = camera_common_initialize(common_data, "lt6911uxc_zhaw"); 1058 | if (err) { 1059 | dev_err(&client->dev, "Failed to initialize lt6911uxc.\n"); 1060 | return err; 1061 | } 1062 | 1063 | v4l2_i2c_subdev_init(sd, client, <6911uxc_ops); 1064 | 1065 | dev_info(&client->dev, "Chip found @ 7h%02X (%s)\n", client->addr, 1066 | client->adapter->name); 1067 | 1068 | /* get interrupt */ 1069 | if (client->irq) { 1070 | err = devm_request_threaded_irq(&priv->i2c_client->dev, 1071 | client->irq, NULL, 1072 | lt6911uxc_irq_handler, 1073 | IRQF_TRIGGER_FALLING | 1074 | IRQF_ONESHOT, sd->name, 1075 | (void *)sd); 1076 | if (err) { 1077 | dev_err(&client->dev,"Could not request interrupt %d!\n", 1078 | client->irq); 1079 | return err; 1080 | } 1081 | } 1082 | 1083 | /* custom v4l2 controls */ 1084 | v4l2_ctrl_handler_init(&priv->ctrl_handler, LT6911UXC_NUM_CTRLS); 1085 | 1086 | priv->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom( 1087 | &priv->ctrl_handler, <6911uxc_ctrl_audio_sampling_rate, NULL); 1088 | priv->ctrls[0] = priv->audio_sampling_rate_ctrl; 1089 | 1090 | priv->audio_present_ctrl = v4l2_ctrl_new_custom(&priv->ctrl_handler, 1091 | <6911uxc_ctrl_audio_present, NULL); 1092 | priv->ctrls[1] = priv->audio_present_ctrl; 1093 | 1094 | priv->num_mipi_lanes_ctrl = v4l2_ctrl_new_custom(&priv->ctrl_handler, 1095 | <6911uxc_ctrl_num_mipi_lanes, NULL); 1096 | priv->ctrls[2] = priv->num_mipi_lanes_ctrl; 1097 | 1098 | sd->ctrl_handler = &priv->ctrl_handler; 1099 | 1100 | if (priv->ctrl_handler.error) { 1101 | dev_err(&client->dev, "Error %d adding controls\n", 1102 | priv->ctrl_handler.error); 1103 | err = priv->ctrl_handler.error; 1104 | goto err_ctrl_handler; 1105 | } 1106 | 1107 | err = v4l2_ctrl_handler_setup(sd->ctrl_handler); 1108 | if (err) { 1109 | dev_err(&client->dev, 1110 | "Error %d setting default controls\n", err); 1111 | goto err_ctrl_handler; 1112 | } 1113 | 1114 | /* register v4l2_subdev device */ 1115 | 1116 | sd->internal_ops = <6911uxc_subdev_internal_ops; 1117 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 1118 | 1119 | 1120 | 1121 | /* media entitiy: define pad as output -> origins of link */ 1122 | if (IS_ENABLED(CONFIG_MEDIA_CONTROLLER)) { 1123 | priv->pad[0].flags = MEDIA_PAD_FL_SOURCE; 1124 | sd->entity.ops = <6911uxc_media_ops; 1125 | 1126 | err = tegra_media_entity_init(&sd->entity, 1, 1127 | priv->pad, true, false); 1128 | if (err < 0) { 1129 | dev_err(&client->dev, "unable to init media entity\n"); 1130 | goto err_ctrl_handler; 1131 | } 1132 | } 1133 | 1134 | 1135 | err = v4l2_async_register_subdev(sd); 1136 | if (err) { 1137 | dev_err(&client->dev, "lt6911uxc subdev registration failed\n"); 1138 | goto err_ctrl_handler; 1139 | } 1140 | 1141 | dev_info(&client->dev, "Detected lt6911uxc device\n"); 1142 | return 0; 1143 | 1144 | err_ctrl_handler: 1145 | v4l2_ctrl_handler_free(&priv->ctrl_handler); 1146 | return err; 1147 | } 1148 | 1149 | static int lt6911uxc_remove(struct i2c_client *client) 1150 | { 1151 | struct camera_common_data *s_data = to_camera_common_data(&client->dev); 1152 | struct lt6911uxc *priv = (struct lt6911uxc *)s_data->priv; 1153 | 1154 | dev_dbg(&client->dev, "%s \n", __func__); 1155 | 1156 | v4l2_async_unregister_subdev(priv->sd); 1157 | 1158 | if (IS_ENABLED(CONFIG_MEDIA_CONTROLLER)) { 1159 | media_entity_cleanup(&priv->sd->entity); 1160 | } 1161 | 1162 | v4l2_ctrl_handler_free(&priv->ctrl_handler); 1163 | camera_common_cleanup(s_data); 1164 | 1165 | dev_info(&client->dev, "removed lt6911uxc instance \n"); 1166 | return 0; 1167 | } 1168 | 1169 | static const struct i2c_device_id lt6911uxc_id[] = { 1170 | { "lt6911uxc_zhaw", 0 }, 1171 | { } 1172 | }; 1173 | 1174 | MODULE_DEVICE_TABLE(i2c, lt6911uxc_id); 1175 | 1176 | static struct i2c_driver lt6911uxc_driver = { 1177 | .driver = { 1178 | .of_match_table = of_match_ptr(lt6911uxc_of_match), 1179 | .name = "lt6911uxc_zhaw", 1180 | .owner = THIS_MODULE, 1181 | }, 1182 | .id_table = lt6911uxc_id, 1183 | .probe = lt6911uxc_probe, 1184 | .remove = lt6911uxc_remove, 1185 | }; 1186 | module_i2c_driver(lt6911uxc_driver); 1187 | 1188 | MODULE_DESCRIPTION("Driver for Lontium lt6911uxc HDMI to CSI-2 Bridge"); 1189 | MODULE_AUTHOR("Lukas Neuner "); 1190 | MODULE_AUTHOR("Alexey Gromov "); 1191 | MODULE_AUTHOR("Gianluca Pargaetzi "); 1192 | MODULE_AUTHOR("Michael Wäspe "); 1193 | MODULE_LICENSE("GPL v2"); --------------------------------------------------------------------------------