From nobody Wed Oct 8 04:19:41 2025 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F199B225768; Wed, 2 Jul 2025 06:38:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438332; cv=none; b=rT/wP/1MlFMrbSk2UoIFNFIThUrXTvlT6kRjLiCJe/QfWa20y3QO0VFWdNcgKKWamTrbTko+ftYORL5+qAq8SJFN2RAZJHkmbCbzKX6VO6as0LXt3MzZK+KR4k3GdfLwsQSvyDjyBG2rzCwPjLFewMXxGlpPFI3EIIdYonO8kPo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438332; c=relaxed/simple; bh=rtoFbXp7Djoc/ChcSP8tyoBS3f73Vjhw9eYQaDbOueU=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=J1iUNOaKvSo0ujv9nrmVJwHiUJEp/uRaNfI4iEFvvyGEyULzKjIIDDS+BvwkRkLtP/CpElunscGrXklHsXnGUmW62aizvur4+OvSu+WhYAfHiNueT+iBCvvzJ1ZdzHqGJMxeatuKDfnAcR50YyTjsQR2c/Qu7NJi9EddMt6MKvU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.210.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-748d982e97cso4159057b3a.1; Tue, 01 Jul 2025 23:38:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751438330; x=1752043130; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NldJir8W9VPLyugiOm1F2+u63+x8UhSg95hgPGqSiag=; b=R8NVcHKJJC2ZZopfPvCOMILKgqZb/62BURV3ZRu7yZn+GbDqLQCbTWRiu4I/chuJc5 w3uivV753rKL1O3UPHyamnqcZABqktFnPYCgRbBFrebNICBiZzTDjmhBWd6HEo1o0xsp 44ianZVck0YG6qhSeBRr+1vUsuiQVFcaeHsy9QacnRlDiZzR3bVcetNInYmj3p74JByA GW+2QaFQzQQqNcCWnsizHYUAJfJUJoruX2TqQPOMnl8H4waI+38zq5uK26U5TitAwU7H y34VArFQUUogrdo6Mn4asbD/FotRd5Mgci3eEc2/qcfjjKd3IvvXa37tjIDq7SEE1/kQ Qf4w== X-Forwarded-Encrypted: i=1; AJvYcCU+6cbTC6OV9cWpJbPhUWaI1oAqxsLJyXusrxluW02hulOLHZ+uDtqz9FPd4gXPFJ21qQei0sAzpMJe5Qa+@vger.kernel.org, AJvYcCXKQLqmPIk/mnwpMWQWu6EtdK3n96meoDDTOjRZbqZFNUIgRlHM4kQYWn9KiQwanDKBOoL4IXnN84U3@vger.kernel.org, AJvYcCXMdlQ0z+D3S/auICWZtx4EbqvJ42GfDwrNR1cw2pMJpnqtvnYLCciojvAeEVSXwPu//Qup8Cdp4Zn0LMA=@vger.kernel.org X-Gm-Message-State: AOJu0YwebQ4lRJnJje/ImryZULaOl0Jy3iBRC7BPUttsknGPp8Ow/9JD kbcHUZtDvgkUmz14k4mMGQatHqvcKcHbLTS+jHUs9CLse/nn0U7b+H5LxJBeSwI4 X-Gm-Gg: ASbGnct2LyrZxiruciYYRtEQsk9f7HkZgfqJ3Lx11wqursUrhBbEnz5pbZfc9FWC7CL uDyhpDViLsJPVmbk9bpNqU+q/w1kfsBELfKIpC+c+b7KiYU2CnXdCsGe6voGB7wVM+7Q9sqh0Pi B5b3zan9A5KHgbAbmnr1bWqXTLEm9Wk+jSYQarYl0t8xDDx2Li06RdjBPBHn0XZG2cZmOy1XC+K 2UPG8b+8IZpESPJ1z09ge+dU004WqmNofnOMfzwJKs3eFZp6uXYu7tGMnLGz/TRT9mAv0CPG5hP 0B51qYNLR3UW5t8UVCm0ViFj9+YSCfrpmHOB1XYYudAtdwZS8gVW++3bcbX5y6SCWM4L3jaFY7g rjsN1QDlLyOmr+ZozAhSPBpdtnH+w1Q== X-Google-Smtp-Source: AGHT+IFFqVZs9mujvPGzuMDR/UlBWy0mPlxEYy0vjW4QxIDngMqnf7l4HCksWE5zzzrVltIxn7fkZg== X-Received: by 2002:a05:6a21:6d95:b0:21f:e9be:7c3f with SMTP id adf61e73a8af0-222d7def90fmr2956864637.11.1751438330131; Tue, 01 Jul 2025 23:38:50 -0700 (PDT) Received: from localhost.localdomain (c-24-4-34-163.hsd1.ca.comcast.net. [24.4.34.163]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74af541bd0fsm13044966b3a.40.2025.07.01.23.38.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 23:38:48 -0700 (PDT) From: Will Whang To: Will Whang , Sakari Ailus , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-media@vger.kernel.org (open list:SONY IMX585 SENSOR DRIVER), linux-kernel@vger.kernel.org (open list), devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS) Subject: [PATCH v1 1/4] dt-bindings: media: Add Sony IMX585 CMOS image sensor Date: Wed, 2 Jul 2025 07:38:33 +0100 Message-Id: <20250702063836.3984-2-will@willwhang.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250702063836.3984-1-will@willwhang.com> References: <20250702063836.3984-1-will@willwhang.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Document the devicetree binding for the Sony IMX585. The schema covers the CSI-2 data-lanes, the optional 'mono-mode' flag, and the internal-sync properties used by the driver. Signed-off-by: Will Whang --- .../bindings/media/i2c/sony,imx585.yaml | 120 ++++++++++++++++++ MAINTAINERS | 8 ++ 2 files changed, 128 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx585= .yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx585.yaml b= /Documentation/devicetree/bindings/media/i2c/sony,imx585.yaml new file mode 100644 index 000000000..d050d1642 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx585.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Ideas on Board Oy +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx585.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX585 Sensor + +maintainers: + - Will Whang + +description: + IMX585 sensor is a Sony CMOS sensor with 4K and FHD outputs. + +properties: + compatible: + const: sony,imx585 + + clocks: + maxItems: 1 + + clock-names: + const: xclk + + clock-frequency: + enum: [ 74250000, 37125000, 72000000, 27000000, 24000000 ] + + reg: + maxItems: 1 + description: I2C Address for IMX585 + + VANA-supply: + description: Analog power supply (3.3V) + + VDDL-supply: + description: Interface power supply (1.8V) + + VDIG-supply: + description: Digital power supply (1.1V) + + reset-gpios: + description: Sensor reset (XCLR) GPIO + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + anyOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + sync-mode: + description: | + Select the synchronisation mode of the sensor + 0 =E2=80=93 internal sync, leader (default) + 1 =E2=80=93 internal sync, follower + 2 =E2=80=93 external sync + $ref: /schemas/types.yaml#/definitions/uint8 + enum: [ 0, 1, 2 ] + + link-frequencies: + description: Select the MIPI-CSI2 link speed in Mhz + items: + enum: [ 297000000, 360000000, 445500000, 594000000, + 720000000, 891000000, 1039500000 ] + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - clock-frequency + - port + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + imx585@1a { + compatible =3D "sony,imx585"; + reg =3D <0x1a>; + clocks =3D <&imx585_clk>; + clock-frequency =3D <24000000>; + + VANA-supply =3D <&camera_vadd_3v3>; + VDDL-supply =3D <&camera_vdd1_1v8>; + VDIG-supply =3D <&camera_vdd2_1v1>; + + port { + imx585: endpoint { + remote-endpoint =3D <&cam>; + data-lanes =3D <1 2 3 4>; + link-frequencies =3D /bits/ 64 <720000000>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index da34c7227..9cc404790 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23150,6 +23150,14 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml F: drivers/media/i2c/imx415.c =20 +SONY IMX585 SENSOR DRIVER +M: Will Whang +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: Documentation/devicetree/bindings/media/i2c/sony,imx585.yaml +F: drivers/media/i2c/imx585.c + SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky M: Alex Dubov --=20 2.39.5 From nobody Wed Oct 8 04:19:41 2025 Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D20ED2288E3; Wed, 2 Jul 2025 06:38:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438334; cv=none; b=JkhoTv3aj/DnFz042FBLEJ8iSIJhPPveNsAmoDpwYeF4WERsEpMxftMp3juVLlnQuNQuiS/5Q2RCjHGgcRxk5djslBc/QRdjq2RUD0FwYu/X/mAq1dB13c8FzhSxaMpsAOb3fjnylDoWVS2CdI7ac1D2xVo4hVM1JWXzBKu3elE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438334; c=relaxed/simple; bh=fXb0I7IfMOyzonYIlW1dn/b+rY/r3WcXXrPbiFSCh1Y=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=T7ljE3g0XBSgEBTrr9jX70OeEHOTb1P1CEbA3qK5u3vhgRu6T9Z98eATGgLXoNjxpQa4IM7gHEL2UX4IQHlnUJagQRr4igaOx6f5M4RxnMuqAwNA72roZIFWkXFGrj9sygXxWC6mS/6YwaBiWHkRt8lDsIDmpBciIS3kPalUkSI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.215.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-b34a71d9208so3117058a12.3; Tue, 01 Jul 2025 23:38:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751438332; x=1752043132; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DOKZQZFJxlzIK2NfeOqsvol6QqRTu35rT3af2i6E6xs=; b=CYf1y2cwSoNlh/lIJQPkMKl9NAY8tXv4iOdJ5TZKBeyi6mKXYvrzJzpo4EmqObaLD8 tYsU3l6zk0HD2FoOAKKAjeDGuqMjPotw8alvL5V5Gkx5dMYz2g6DSKLQvnLppfk4QoOa Zbvrga7HoljCWbsfp9oXeuTj2vs1kGbqUuQqlm4TbyDJoElxmDDEC4TVMSS4M63qCjZV tdPRIhaE7F0yMmuDHfs57F7bpX5Cuzp8izlbXVG8ejQkgNhGBqz3aHR1VqOAtzxvKz4O wFbZ1vB98/WWfuU/4bI74yC9OEd/JShmHmdW0TlyDA/l+lVyRItPM653/wHlBaz751ky zVqQ== X-Forwarded-Encrypted: i=1; AJvYcCUS6SphcUT0TB0m61TVYVFue444IH0Or5hvDNfHCPB9+aU2f64PV8hNcC4Eq1uExROwhN1aNWsqR3k3Hss=@vger.kernel.org, AJvYcCWiS7vwZ5CWtTw1aMf32O9ptO3BrdaBfRSo0QTYuNlWgTpUIwyHUhyAvD21iTFgrV3Yro/A1K9o/EG0j4ld@vger.kernel.org, AJvYcCXtkYjJtdNPoJb9RGoMvQqdr0F6N4hUjF/p9ZMusAUgzvrDWQozSvOem86sB0eChpCbxT0YVcQO1msU@vger.kernel.org X-Gm-Message-State: AOJu0YyNc0ME3aPOzlbiyFogt2wsP0V/skbn7GaGfh0u7oP3oHOl0PnG +Ux7hhpvqTBPSfjVDkfKsyEOoBNPcM0ZOy5MntHmc6y53agjJIremy6Nm5NXPbui X-Gm-Gg: ASbGncsLxmlqrlU/Ys7gntPODIbfrv27Eda0Lvs9ZzZO9on1R1vM8G0NYl7dxw2vwTg LTnq4EryN5q4ViJgwAATdK855hIxgUFzCzMQBWtf4AmLpyRifJ5qDfQcefHqZEhd0MMhU3uHrIk lGjISH8U7fY6/RpjpV+k5b2DSuG9LZAzYdsmzhYKqf0IARI4OWBDwOxqFySFG9fIAFEZscoj1fe 85KYN1aB1GY93XM+qGLAjmHYYRRDMxwj8TAidCAb9KTdyCdVnk0QYz64sT3UsHq3xddtMYR1CCA obLPzRCj3jB4R9bfl5O4BMmb2nRah5du5vUilLmHyIw/dFFOKQ1TJmTasdDEFDfnDeiYAgKakAN fq/0ssXcuCl6cummkEt3WiAN1/f63Zw== X-Google-Smtp-Source: AGHT+IFn5mnE5q8jbIcXZzmzV4ptZGfBiTuVRfBgz+SoRRNB4A98QbdY+OIBl5NeE+r+tQnIGfO7wg== X-Received: by 2002:a05:6a21:48b:b0:21d:1fbf:c71a with SMTP id adf61e73a8af0-222d7dc5062mr3182489637.4.1751438332076; Tue, 01 Jul 2025 23:38:52 -0700 (PDT) Received: from localhost.localdomain (c-24-4-34-163.hsd1.ca.comcast.net. [24.4.34.163]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74af541bd0fsm13044966b3a.40.2025.07.01.23.38.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 23:38:50 -0700 (PDT) From: Will Whang To: Will Whang , Sakari Ailus , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-media@vger.kernel.org (open list:SONY IMX585 SENSOR DRIVER), linux-kernel@vger.kernel.org (open list), devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS) Subject: [PATCH v1 2/4] media: uapi: Add custom IMX585 control IDs Date: Wed, 2 Jul 2025 07:38:34 +0100 Message-Id: <20250702063836.3984-3-will@willwhang.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250702063836.3984-1-will@willwhang.com> References: <20250702063836.3984-1-will@willwhang.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Reserve a private control ID block and define the helper enums used by the upcoming IMX585 driver. Signed-off-by: Will Whang --- include/uapi/linux/imx585.h | 20 ++++++++++++++++++++ include/uapi/linux/v4l2-controls.h | 6 ++++++ 2 files changed, 26 insertions(+) create mode 100644 include/uapi/linux/imx585.h diff --git a/include/uapi/linux/imx585.h b/include/uapi/linux/imx585.h new file mode 100644 index 000000000..30e12df88 --- /dev/null +++ b/include/uapi/linux/imx585.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * IMX585 V4L2 controls user space header file. + * + */ + +#ifndef __UAPI_IMX585_H_ +#define __UAPI_IMX585_H_ + +#include + +#define V4L2_CID_IMX585_HDR_DATASEL_TH (V4L2_CID_USER_IMX585_BASE + 0) +#define V4L2_CID_IMX585_HDR_DATASEL_BK (V4L2_CID_USER_IMX585_BASE + 1) +#define V4L2_CID_IMX585_HDR_GRAD_TH (V4L2_CID_USER_IMX585_BASE + 2) +#define V4L2_CID_IMX585_HDR_GRAD_COMP_L (V4L2_CID_USER_IMX585_BASE + 3) +#define V4L2_CID_IMX585_HDR_GRAD_COMP_H (V4L2_CID_USER_IMX585_BASE + 4) +#define V4L2_CID_IMX585_HDR_GAIN (V4L2_CID_USER_IMX585_BASE + 5) +#define V4L2_CID_IMX585_HCG_GAIN (V4L2_CID_USER_IMX585_BASE + 6) + +#endif /* __UAPI_IMX585_H_ */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-c= ontrols.h index f836512e9..091a044e5 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -228,6 +228,12 @@ enum v4l2_colorfx { */ #define V4L2_CID_USER_RKISP1_BASE (V4L2_CID_USER_BASE + 0x1220) =20 +/* + * The base for IMX585 driver controls. + * We reserve 16 controls for this driver. + */ +#define V4L2_CID_USER_IMX585_BASE (V4L2_CID_USER_BASE + 0x12= 30) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ --=20 2.39.5 From nobody Wed Oct 8 04:19:41 2025 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B23DF22F152; Wed, 2 Jul 2025 06:38:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438339; cv=none; b=axDiClr9cpiS2I/A+YW2MVlKtXtvsOJTLEX6odpffZehO8hTiKDdGZO4oENjaGxN4xCGtokmuwhOJbPqYFwcR/EGIYoOBxm/p+wCeFraWMDkqRhsUh8s0ywOP+IuchR6KK8p3xzKBxujfA6Rx5MbBRO6aQsA1K6AgfOcJJFNgtI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438339; c=relaxed/simple; bh=QN/pCn9fTwuG4chAh0Pxa9sut53YfD07zZWym3xG1Bg=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=RMgHwFZ10Bo+uztQHd+FexQKaGIGmEa6gyVP7wGQkBo3G1loavL8VuPsE/gLQN6XMg1NouND/rXa+6iFas+uo0KdRg6vEUdj92BnvHli2hTIwWl11ekG005JSqmqmGAMzzG33aQqKgPH2TCKOe7gEDVsIOeGd/6we1YLMYWTa38= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.210.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-7494999de5cso4963234b3a.3; Tue, 01 Jul 2025 23:38:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751438335; x=1752043135; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=a/M1cnlEWBCZl4D+ffydehimMzLXTi3eqlQm1v4KuFk=; b=PkuR5h1n3QMjshEp3kZC4+t8+bXXa+eh/kH48YgSYNGiVXlAgR4osSaNAL4ghxpTrO kjlTnfCTWceVt4iw1fjggfEYE58HkMRU8yPzfROoCC9oK2Iio5DjJF7hJBZ/DA+NOiku 9EInl0OhO0yFHAugiayikPpu2vrhCkPGTwSMwRCIZNePt/R0dSE9gtlsgGMqvylavtJJ lG/naD9lUHwwZsCG2do/VtDIhM+zGueh186vVUDq6HpHWR184gaK1bNA+dKVQyjrxFD7 aimQlerLJsjH4XH3r2aFicIVayO5dhizQLVd4pnuDK4rJJTy7cETE8UXRLVvnHO+gcBA tn8g== X-Forwarded-Encrypted: i=1; AJvYcCVZQXfzOGe+RDl+mvIKlHRnoty67QPZTqY8U8qeWgdxpZaAvP7FEocLwtZYdlZR4lXyGHHswO3bbBUDZGU=@vger.kernel.org, AJvYcCVgO8egomjEiQOjQqs2rQlaPS38KpP53WvfUiSnU8E5lDuqc7XUUaCbDQnHtAj+LVogpnPCPRvXigkb@vger.kernel.org, AJvYcCWImMf9/F0wS3xEasnhNnhspOROmIfT6F30jc2+kPsuEE7HfbuqZIuf6A4dGEgRKTxFBdxZb+3bG0uV2/p5@vger.kernel.org X-Gm-Message-State: AOJu0YxsedmLwsWNVGiEiB68HrTdEt/8TH/NroeUBRVExGUHtHagk0GL cvlEnEAh/LT/GPB5eE22Lix4ivA9+Ibt5o0kIKt//hmJLWb/f0HBVPDU X-Gm-Gg: ASbGnct1qxgyjZzJPZTRtwoGTvgVd+TZdWzTy8v8dD3RGXSMOKSb0aQcwqUaOgwK3hj kV1WcV5/ElbFGGOPZPNBSFGMH3/p+AR8+99TuZcxoSavcAOvbEyZPikynoIv1kStG5xISLPfCAI M3l0WtGrCBY206d29g20Jb4NqD2EH1mczMnxHddvoJHciK55RoPA5rp5aioArik0Y0PMtNifkJP FnLeZsGhroZA1wjpcJ4GcZxh4IRPqxtSP8KGuaYo6UcaXbSHk/n/YerhfeL/boqzfpAjnTY2Z1t EZWeeEFDQYwm1Jv2k46qFLPEOYylYNDuhrrKTpAMjI57Jv7AChiuGoKDP5kxwZh+j2N+/xdDco2 c9Jv51HDEIf4/xMCSfMHVezkWSW3z5g== X-Google-Smtp-Source: AGHT+IGQHOJHw/QkVXJsObI/BBIVTQpslyFVG12+C3LIVND4jMsRP4fmNTwhtwxYSqiwUFC690ncww== X-Received: by 2002:a05:6a00:138b:b0:748:e1e4:71ec with SMTP id d2e1a72fcca58-74b510480abmr2220690b3a.12.1751438334442; Tue, 01 Jul 2025 23:38:54 -0700 (PDT) Received: from localhost.localdomain (c-24-4-34-163.hsd1.ca.comcast.net. [24.4.34.163]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74af541bd0fsm13044966b3a.40.2025.07.01.23.38.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 23:38:53 -0700 (PDT) From: Will Whang To: Will Whang , Sakari Ailus , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-media@vger.kernel.org (open list:SONY IMX585 SENSOR DRIVER), linux-kernel@vger.kernel.org (open list), devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS) Subject: [PATCH v1 3/4] media: i2c: imx585: Add Sony IMX585 image-sensor driver Date: Wed, 2 Jul 2025 07:38:35 +0100 Message-Id: <20250702063836.3984-4-will@willwhang.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250702063836.3984-1-will@willwhang.com> References: <20250702063836.3984-1-will@willwhang.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Implements support for: * 4-lane / 2-lane CSI-2 * 12-bit linear, 12-bit HDR-GC and 16-bit Clear-HDR modes * Mono variant switch, HCG, custom HDR controls * Tested on Raspberry Pi 4/5 with 24 MHz XCLK. Signed-off-by: Will Whang --- drivers/media/i2c/Kconfig | 9 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx585.c | 2466 ++++++++++++++++++++++++++++++++++++ 3 files changed, 2476 insertions(+) create mode 100644 drivers/media/i2c/imx585.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e68202954..34eb1c19a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -266,6 +266,15 @@ config VIDEO_IMX415 To compile this driver as a module, choose M here: the module will be called imx415. =20 +config VIDEO_IMX585 + tristate "Sony IMX585 sensor support" + help + This is a Video4Linux2 sensor driver for the Sony + IMX585 camera. + + To compile this driver as a module, choose M here: the + module will be called imx585. + config VIDEO_MAX9271_LIB tristate =20 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5873d2943..887d19ca7 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_VIDEO_IMX335) +=3D imx335.o obj-$(CONFIG_VIDEO_IMX355) +=3D imx355.o obj-$(CONFIG_VIDEO_IMX412) +=3D imx412.o obj-$(CONFIG_VIDEO_IMX415) +=3D imx415.o +obj-$(CONFIG_VIDEO_IMX585) +=3D imx585.o obj-$(CONFIG_VIDEO_IR_I2C) +=3D ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) +=3D isl7998x.o obj-$(CONFIG_VIDEO_KS0127) +=3D ks0127.o diff --git a/drivers/media/i2c/imx585.c b/drivers/media/i2c/imx585.c new file mode 100644 index 000000000..2c4212290 --- /dev/null +++ b/drivers/media/i2c/imx585.c @@ -0,0 +1,2466 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony imx585 cameras. + * + * Based on Sony imx477 camera driver + * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd + * Modified by Will WHANG + * Modified by sohonomura2020 in Soho Enterprise Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Support for rpi kernel pre git commit 314a685 +#ifndef MEDIA_BUS_FMT_SENSOR_DATA +#define MEDIA_BUS_FMT_SENSOR_DATA 0x7002 +#endif + +#define V4L2_CID_IMX585_HDR_DATASEL_TH (V4L2_CID_USER_IMX585_BASE + 0) +#define V4L2_CID_IMX585_HDR_DATASEL_BK (V4L2_CID_USER_IMX585_BASE + 1) +#define V4L2_CID_IMX585_HDR_GRAD_TH (V4L2_CID_USER_IMX585_BASE + 2) +#define V4L2_CID_IMX585_HDR_GRAD_COMP_L (V4L2_CID_USER_IMX585_BASE + 3) +#define V4L2_CID_IMX585_HDR_GRAD_COMP_H (V4L2_CID_USER_IMX585_BASE + 4) +#define V4L2_CID_IMX585_HDR_GAIN (V4L2_CID_USER_IMX585_BASE + 5) +#define V4L2_CID_IMX585_HCG_GAIN (V4L2_CID_USER_IMX585_BASE + 6) + +/* Standby or streaming mode */ +#define IMX585_REG_MODE_SELECT 0x3000 +#define IMX585_MODE_STANDBY 0x01 +#define IMX585_MODE_STREAMING 0x00 +#define IMX585_STREAM_DELAY_US 25000 +#define IMX585_STREAM_DELAY_RANGE_US 1000 + +/* + * Initialisation delay between XCLR low->high and the moment when the sen= sor + * can start capture (i.e. can leave software standby) + */ +#define IMX585_XCLR_MIN_DELAY_US 500000 +#define IMX585_XCLR_DELAY_RANGE_US 1000 + +/* Leader mode and XVS/XHS direction */ +#define IMX585_REG_XMSTA 0x3002 +#define IMX585_REG_XXS_DRV 0x30A6 +#define IMX585_REG_EXTMODE 0x30CE +#define IMX585_REG_XXS_OUTSEL 0x30A4 + +/*XVS pulse length, 2^n H with n=3D0~3*/ +#define IMX585_REG_XVSLNG 0x30CC +/*XHS pulse length, 16*(2^n) Clock with n=3D0~3*/ +#define IMX585_REG_XHSLNG 0x30CD + +/* Clk selection */ +#define IMX585_INCK_SEL 0x3014 + +/* Link Speed */ +#define IMX585_DATARATE_SEL 0x3015 + +/* BIN mode */ +/* 2x2 Bin mode selection, 0x01 =3D> Mono, 0x00 =3D> Color */ +#define IMX585_BIN_MODE 0x3019 + +/* Lane Count */ +#define IMX585_LANEMODE 0x3040 + +/* VMAX internal VBLANK*/ +#define IMX585_REG_VMAX 0x3028 +#define IMX585_VMAX_MAX 0xfffff +#define IMX585_VMAX_DEFAULT 2250 + +/* HMAX internal HBLANK*/ +#define IMX585_REG_HMAX 0x302C +#define IMX585_HMAX_MAX 0xffff + +/* SHR internal */ +#define IMX585_REG_SHR 0x3050 +#define IMX585_SHR_MIN 8 +#define IMX585_SHR_MIN_HDR 10 +#define IMX585_SHR_MAX 0xfffff + +/* Exposure control */ +#define IMX585_EXPOSURE_MIN 2 +#define IMX585_EXPOSURE_STEP 1 +#define IMX585_EXPOSURE_DEFAULT 1000 +#define IMX585_EXPOSURE_MAX 49865 + +/* HDR threshold */ +#define IMX585_REG_EXP_TH_H 0x36D0 +#define IMX585_REG_EXP_TH_L 0x36D4 +#define IMX585_REG_EXP_BK 0x36E2 + +/* Gradation compression control */ +#define IMX595_REG_CCMP_EN 0x36EF +#define IMX585_REG_CCMP1_EXP 0x36E8 +#define IMX585_REG_CCMP2_EXP 0x36E4 +#define IMX585_REG_ACMP1_EXP 0x36EE +#define IMX585_REG_ACMP2_EXP 0x36EC + +/* HDR Gain Adder */ +#define IMX585_REG_EXP_GAIN 0x3081 + +/* Black level control */ +#define IMX585_REG_BLKLEVEL 0x30DC +#define IMX585_BLKLEVEL_DEFAULT 50 + +/* Digital Clamp */ +#define IMX585_REG_DIGITAL_CLAMP 0x3458 + +/* Analog gain control */ +#define IMX585_REG_ANALOG_GAIN 0x306C +#define IMX585_REG_FDG_SEL0 0x3030 +#define IMX585_ANA_GAIN_MIN_NORMAL 0 +#define IMX585_ANA_GAIN_MIN_HCG 34 +#define IMX585_ANA_GAIN_MAX_HDR 80 +#define IMX585_ANA_GAIN_MAX_NORMAL 240 +#define IMX585_ANA_GAIN_STEP 1 +#define IMX585_ANA_GAIN_DEFAULT 0 + +/* Flip */ +#define IMX585_FLIP_WINMODEH 0x3020 +#define IMX585_FLIP_WINMODEV 0x3021 + +/* Embedded metadata stream structure */ +#define IMX585_EMBEDDED_LINE_WIDTH 16384 +#define IMX585_NUM_EMBEDDED_LINES 1 + +#define IMX585_PIXEL_RATE 74250000 + +enum pad_types { + IMAGE_PAD, + METADATA_PAD, + NUM_PADS +}; + +/* imx585 native and active pixel array size. */ +#define IMX585_NATIVE_WIDTH 3856U +#define IMX585_NATIVE_HEIGHT 2180U +#define IMX585_PIXEL_ARRAY_LEFT 8U +#define IMX585_PIXEL_ARRAY_TOP 8U +#define IMX585_PIXEL_ARRAY_WIDTH 3840U +#define IMX585_PIXEL_ARRAY_HEIGHT 2160U + +/* Link frequency setup */ +enum { + IMX585_LINK_FREQ_297MHZ, // 594Mbps/lane + IMX585_LINK_FREQ_360MHZ, // 720Mbps/lane + IMX585_LINK_FREQ_445MHZ, // 891Mbps/lane + IMX585_LINK_FREQ_594MHZ, // 1188Mbps/lane + IMX585_LINK_FREQ_720MHZ, // 1440Mbps/lane + IMX585_LINK_FREQ_891MHZ, // 1782Mbps/lane + IMX585_LINK_FREQ_1039MHZ, // 2079Mbps/lane + IMX585_LINK_FREQ_1188MHZ, // 2376Mbps/lane +}; + +static const u8 link_freqs_reg_value[] =3D { + [IMX585_LINK_FREQ_297MHZ] =3D 0x07, + [IMX585_LINK_FREQ_360MHZ] =3D 0x06, + [IMX585_LINK_FREQ_445MHZ] =3D 0x05, + [IMX585_LINK_FREQ_594MHZ] =3D 0x04, + [IMX585_LINK_FREQ_720MHZ] =3D 0x03, + [IMX585_LINK_FREQ_891MHZ] =3D 0x02, + [IMX585_LINK_FREQ_1039MHZ] =3D 0x01, + [IMX585_LINK_FREQ_1188MHZ] =3D 0x00, +}; + +static const u64 link_freqs[] =3D { + [IMX585_LINK_FREQ_297MHZ] =3D 297000000, + [IMX585_LINK_FREQ_360MHZ] =3D 360000000, + [IMX585_LINK_FREQ_445MHZ] =3D 445500000, + [IMX585_LINK_FREQ_594MHZ] =3D 594000000, + [IMX585_LINK_FREQ_720MHZ] =3D 720000000, + [IMX585_LINK_FREQ_891MHZ] =3D 891000000, + [IMX585_LINK_FREQ_1039MHZ] =3D 1039500000, + [IMX585_LINK_FREQ_1188MHZ] =3D 1188000000, +}; + +//min HMAX for 4-lane 4K full res mode, x2 for 2-lane, /2 for FHD +static const u16 HMAX_table_4lane_4K[] =3D { + [IMX585_LINK_FREQ_297MHZ] =3D 1584, + [IMX585_LINK_FREQ_360MHZ] =3D 1320, + [IMX585_LINK_FREQ_445MHZ] =3D 1100, + [IMX585_LINK_FREQ_594MHZ] =3D 792, + [IMX585_LINK_FREQ_720MHZ] =3D 660, + [IMX585_LINK_FREQ_891MHZ] =3D 550, + [IMX585_LINK_FREQ_1039MHZ] =3D 440, + [IMX585_LINK_FREQ_1188MHZ] =3D 396, +}; + +struct imx585_inck_cfg { + u32 xclk_hz; /* platform clock rate */ + u8 inck_sel; /* value for reg */ +}; + +static const struct imx585_inck_cfg imx585_inck_table[] =3D { + { 74250000, 0x00 }, + { 37125000, 0x01 }, + { 72000000, 0x02 }, + { 27000000, 0x03 }, + { 24000000, 0x04 }, +}; + +static const char * const hdr_gain_adder_menu[] =3D { + "+0dB", + "+6dB", + "+12dB", + "+18dB", + "+24dB", + "+29.1dB", +}; + +/*Honestly I don't know why there are two 50% 50% blend + * but it is in the datasheet + */ +static const char * const hdr_data_blender_menu[] =3D { + "HG 1/2, LG 1/2", + "HG 3/4, LG 1/4", + "HG 1/2, LG 1/2", + "HG 7/8, LG 1/8", + "HG 15/16, LG 1/16", + "2nd HG 1/2, LG 1/2", + "HG 1/16, LG 15/16", + "HG 1/8, LG 7/8", + "HG 1/4, LG 3/4", +}; + +static const char * const grad_compression_slope_menu[] =3D { + "1/1", + "1/2", + "1/4", + "1/8", + "1/16", + "1/32", + "1/64", + "1/128", + "1/256", + "1/512", + "1/1024", + "1/2048", +}; + +static const char * const sync_mode_menu[] =3D { + "Internal Sync Leader Mode", + "External Sync Leader Mode", + "Follower Mode", +}; + +struct imx585_reg { + u16 address; + u8 val; +}; + +struct IMX585_reg_list { + unsigned int num_of_regs; + const struct imx585_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx585_mode { + /* Frame width */ + unsigned int width; + + /* Frame height */ + unsigned int height; + + /* mode HMAX Scaling */ + u8 hmax_div; + + /* minimum H-timing */ + u16 min_HMAX; + + /* minimum V-timing */ + u64 min_VMAX; + + /* default H-timing */ + u16 default_HMAX; + + /* default V-timing */ + u64 default_VMAX; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* Default register values */ + struct IMX585_reg_list reg_list; +}; + +/* IMX585 Register List */ +/* Common Modes */ +static struct imx585_reg common_regs[] =3D { + {0x3002, 0x01}, + {0x3069, 0x00}, + {0x3074, 0x64}, + {0x30D5, 0x04},// DIG_CLP_VSTART + {0x3030, 0x00},// FDG_SEL0 LCG, HCG:0x01 + {0x30A6, 0x00},// XVS_DRV [1:0] Hi-Z + {0x3081, 0x00},// EXP_GAIN, Reset to 0 + {0x3460, 0x21},// - + {0x3478, 0xA1},// - + {0x347C, 0x01},// - + {0x3480, 0x01},// - + {0x3A4E, 0x14},// - + {0x3A52, 0x14},// - + {0x3A56, 0x00},// - + {0x3A5A, 0x00},// - + {0x3A5E, 0x00},// - + {0x3A62, 0x00},// - + {0x3A6A, 0x20},// - + {0x3A6C, 0x42},// - + {0x3A6E, 0xA0},// - + {0x3B2C, 0x0C},// - + {0x3B30, 0x1C},// - + {0x3B34, 0x0C},// - + {0x3B38, 0x1C},// - + {0x3BA0, 0x0C},// - + {0x3BA4, 0x1C},// - + {0x3BA8, 0x0C},// - + {0x3BAC, 0x1C},// - + {0x3D3C, 0x11},// - + {0x3D46, 0x0B},// - + {0x3DE0, 0x3F},// - + {0x3DE1, 0x08},// - + {0x3E14, 0x87},// - + {0x3E16, 0x91},// - + {0x3E18, 0x91},// - + {0x3E1A, 0x87},// - + {0x3E1C, 0x78},// - + {0x3E1E, 0x50},// - + {0x3E20, 0x50},// - + {0x3E22, 0x50},// - + {0x3E24, 0x87},// - + {0x3E26, 0x91},// - + {0x3E28, 0x91},// - + {0x3E2A, 0x87},// - + {0x3E2C, 0x78},// - + {0x3E2E, 0x50},// - + {0x3E30, 0x50},// - + {0x3E32, 0x50},// - + {0x3E34, 0x87},// - + {0x3E36, 0x91},// - + {0x3E38, 0x91},// - + {0x3E3A, 0x87},// - + {0x3E3C, 0x78},// - + {0x3E3E, 0x50},// - + {0x3E40, 0x50},// - + {0x3E42, 0x50},// - + {0x4054, 0x64},// - + {0x4148, 0xFE},// - + {0x4149, 0x05},// - + {0x414A, 0xFF},// - + {0x414B, 0x05},// - + {0x420A, 0x03},// - + {0x4231, 0x08},// - + {0x423D, 0x9C},// - + {0x4242, 0xB4},// - + {0x4246, 0xB4},// - + {0x424E, 0xB4},// - + {0x425C, 0xB4},// - + {0x425E, 0xB6},// - + {0x426C, 0xB4},// - + {0x426E, 0xB6},// - + {0x428C, 0xB4},// - + {0x428E, 0xB6},// - + {0x4708, 0x00},// - + {0x4709, 0x00},// - + {0x470A, 0xFF},// - + {0x470B, 0x03},// - + {0x470C, 0x00},// - + {0x470D, 0x00},// - + {0x470E, 0xFF},// - + {0x470F, 0x03},// - + {0x47EB, 0x1C},// - + {0x47F0, 0xA6},// - + {0x47F2, 0xA6},// - + {0x47F4, 0xA0},// - + {0x47F6, 0x96},// - + {0x4808, 0xA6},// - + {0x480A, 0xA6},// - + {0x480C, 0xA0},// - + {0x480E, 0x96},// - + {0x492C, 0xB2},// - + {0x4930, 0x03},// - + {0x4932, 0x03},// - + {0x4936, 0x5B},// - + {0x4938, 0x82},// - + {0x493E, 0x23},// - + {0x4BA8, 0x1C},// - + {0x4BA9, 0x03},// - + {0x4BAC, 0x1C},// - + {0x4BAD, 0x1C},// - + {0x4BAE, 0x1C},// - + {0x4BAF, 0x1C},// - + {0x4BB0, 0x1C},// - + {0x4BB1, 0x1C},// - + {0x4BB2, 0x1C},// - + {0x4BB3, 0x1C},// - + {0x4BB4, 0x1C},// - + {0x4BB8, 0x03},// - + {0x4BB9, 0x03},// - + {0x4BBA, 0x03},// - + {0x4BBB, 0x03},// - + {0x4BBC, 0x03},// - + {0x4BBD, 0x03},// - + {0x4BBE, 0x03},// - + {0x4BBF, 0x03},// - + {0x4BC0, 0x03},// - + {0x4C14, 0x87},// - + {0x4C16, 0x91},// - + {0x4C18, 0x91},// - + {0x4C1A, 0x87},// - + {0x4C1C, 0x78},// - + {0x4C1E, 0x50},// - + {0x4C20, 0x50},// - + {0x4C22, 0x50},// - + {0x4C24, 0x87},// - + {0x4C26, 0x91},// - + {0x4C28, 0x91},// - + {0x4C2A, 0x87},// - + {0x4C2C, 0x78},// - + {0x4C2E, 0x50},// - + {0x4C30, 0x50},// - + {0x4C32, 0x50},// - + {0x4C34, 0x87},// - + {0x4C36, 0x91},// - + {0x4C38, 0x91},// - + {0x4C3A, 0x87},// - + {0x4C3C, 0x78},// - + {0x4C3E, 0x50},// - + {0x4C40, 0x50},// - + {0x4C42, 0x50},// - + {0x4D12, 0x1F},// - + {0x4D13, 0x1E},// - + {0x4D26, 0x33},// - + {0x4E0E, 0x59},// - + {0x4E14, 0x55},// - + {0x4E16, 0x59},// - + {0x4E1E, 0x3B},// - + {0x4E20, 0x47},// - + {0x4E22, 0x54},// - + {0x4E26, 0x81},// - + {0x4E2C, 0x7D},// - + {0x4E2E, 0x81},// - + {0x4E36, 0x63},// - + {0x4E38, 0x6F},// - + {0x4E3A, 0x7C},// - + {0x4F3A, 0x3C},// - + {0x4F3C, 0x46},// - + {0x4F3E, 0x59},// - + {0x4F42, 0x64},// - + {0x4F44, 0x6E},// - + {0x4F46, 0x81},// - + {0x4F4A, 0x82},// - + {0x4F5A, 0x81},// - + {0x4F62, 0xAA},// - + {0x4F72, 0xA9},// - + {0x4F78, 0x36},// - + {0x4F7A, 0x41},// - + {0x4F7C, 0x61},// - + {0x4F7D, 0x01},// - + {0x4F7E, 0x7C},// - + {0x4F7F, 0x01},// - + {0x4F80, 0x77},// - + {0x4F82, 0x7B},// - + {0x4F88, 0x37},// - + {0x4F8A, 0x40},// - + {0x4F8C, 0x62},// - + {0x4F8D, 0x01},// - + {0x4F8E, 0x76},// - + {0x4F8F, 0x01},// - + {0x4F90, 0x5E},// - + {0x4F91, 0x02},// - + {0x4F92, 0x69},// - + {0x4F93, 0x02},// - + {0x4F94, 0x89},// - + {0x4F95, 0x02},// - + {0x4F96, 0xA4},// - + {0x4F97, 0x02},// - + {0x4F98, 0x9F},// - + {0x4F99, 0x02},// - + {0x4F9A, 0xA3},// - + {0x4F9B, 0x02},// - + {0x4FA0, 0x5F},// - + {0x4FA1, 0x02},// - + {0x4FA2, 0x68},// - + {0x4FA3, 0x02},// - + {0x4FA4, 0x8A},// - + {0x4FA5, 0x02},// - + {0x4FA6, 0x9E},// - + {0x4FA7, 0x02},// - + {0x519E, 0x79},// - + {0x51A6, 0xA1},// - + {0x51F0, 0xAC},// - + {0x51F2, 0xAA},// - + {0x51F4, 0xA5},// - + {0x51F6, 0xA0},// - + {0x5200, 0x9B},// - + {0x5202, 0x91},// - + {0x5204, 0x87},// - + {0x5206, 0x82},// - + {0x5208, 0xAC},// - + {0x520A, 0xAA},// - + {0x520C, 0xA5},// - + {0x520E, 0xA0},// - + {0x5210, 0x9B},// - + {0x5212, 0x91},// - + {0x5214, 0x87},// - + {0x5216, 0x82},// - + {0x5218, 0xAC},// - + {0x521A, 0xAA},// - + {0x521C, 0xA5},// - + {0x521E, 0xA0},// - + {0x5220, 0x9B},// - + {0x5222, 0x91},// - + {0x5224, 0x87},// - + {0x5226, 0x82},// - +}; + +/* Common Registers for ClearHDR. */ +static const struct imx585_reg common_clearHDR_mode[] =3D { + {0x301A, 0x10}, // WDMODE: Clear HDR mode + {0x3024, 0x02}, // COMBI_EN: 0x02 + {0x3069, 0x02}, // Clear HDR mode + {0x3074, 0x63}, // Clear HDR mode + {0x3930, 0xE6}, // DUR[15:8]: Clear HDR mode (12bit) + {0x3931, 0x00}, // DUR[7:0]: Clear HDR mode (12bit) + {0x3A4C, 0x61}, // WAIT_ST0[7:0]: Clear HDR mode + {0x3A4D, 0x02}, // WAIT_ST0[15:8]: Clear HDR mode + {0x3A50, 0x70}, // WAIT_ST1[7:0]: Clear HDR mode + {0x3A51, 0x02}, // WAIT_ST1[15:8]: Clear HDR mode + {0x3E10, 0x17}, // ADTHEN: Clear HDR mode + {0x493C, 0x41}, // WAIT_10_SHF AD 10-bit 0x0C disable + {0x4940, 0x41}, // WAIT_12_SHF AD 12-bit 0x41 enable + {0x3081, 0x02}, // EXP_GAIN: High gain setting +12dB default +}; + +/* Common Registers for non-ClearHDR. */ +static const struct imx585_reg common_normal_mode[] =3D { + {0x301A, 0x00}, // WDMODE: Normal mode + {0x3024, 0x00}, // COMBI_EN: 0x00 + {0x3069, 0x00}, // Normal mode + {0x3074, 0x64}, // Normal mode + {0x3930, 0x0C}, // DUR[15:8]: Normal mode (12bit) + {0x3931, 0x01}, // DUR[7:0]: Normal mode (12bit) + {0x3A4C, 0x39}, // WAIT_ST0[7:0]: Normal mode + {0x3A4D, 0x01}, // WAIT_ST0[15:8]: Normal mode + {0x3A50, 0x48}, // WAIT_ST1[7:0]: Normal mode + {0x3A51, 0x01}, // WAIT_ST1[15:8]: Normal mode + {0x3E10, 0x10}, // ADTHEN: Normal mode + {0x493C, 0x23}, // WAIT_10_SHF AD 10-bit 0x23 Normal mode + {0x4940, 0x23}, // WAIT_12_SHF AD 12-bit 0x23 Normal mode +}; + +/* All pixel 4K60. 12-bit */ +static const struct imx585_reg mode_4k_regs_12bit[] =3D { + {0x301B, 0x00}, // ADDMODE non-binning + {0x3022, 0x02}, // ADBIT 12-bit + {0x3023, 0x01}, // MDBIT 12-bit + {0x30D5, 0x04}, // DIG_CLP_VSTART non-binning +}; + +/* 2x2 binned 1080p60. 12-bit */ +static const struct imx585_reg mode_1080_regs_12bit[] =3D { + {0x301B, 0x01}, // ADDMODE binning + {0x3022, 0x02}, // ADBIT 12-bit + {0x3023, 0x01}, // MDBIT 12-bit + {0x30D5, 0x02}, // DIG_CLP_VSTART binning +}; + +/* IMX585 Register List - END*/ + +/* For Mode List: + * Default: + * 12Bit - FHD, 4K + * ClearHDR Enabled: + * 12bit + Gradation compression + * 16bit - FHD, 4K + * + * Gradation compression is available on 12bit + * With Default option, only 12bit mode is exposed + * With ClearHDR enabled via parameters, + * 12bit will be with Gradation compression enabled + * 16bit mode exposed + * + * Technically, because the sensor is actually binning + * in digital domain, its readout speed is the same + * between 4K and FHD. However, through testing it is + * possible to "overclock" the FHD mode, thus leaving the + * hmax_div option for those who want to try. + * Also, note that FHD and 4K mode shared the same VMAX. + */ + +/* Mode configs */ +struct imx585_mode supported_modes[] =3D { + { + /* 1080p60 2x2 binning */ + .width =3D 1928, + .height =3D 1090, + .hmax_div =3D 1, + .min_HMAX =3D 366, + .min_VMAX =3D IMX585_VMAX_DEFAULT, + .default_HMAX =3D 366, + .default_VMAX =3D IMX585_VMAX_DEFAULT, + .crop =3D { + .left =3D IMX585_PIXEL_ARRAY_LEFT, + .top =3D IMX585_PIXEL_ARRAY_TOP, + .width =3D IMX585_PIXEL_ARRAY_WIDTH, + .height =3D IMX585_PIXEL_ARRAY_HEIGHT, + }, + .reg_list =3D { + .num_of_regs =3D ARRAY_SIZE(mode_1080_regs_12bit), + .regs =3D mode_1080_regs_12bit, + }, + }, + { + /* 4K60 All pixel */ + .width =3D 3856, + .height =3D 2180, + .min_HMAX =3D 550, + .min_VMAX =3D IMX585_VMAX_DEFAULT, + .default_HMAX =3D 550, + .default_VMAX =3D IMX585_VMAX_DEFAULT, + .hmax_div =3D 1, + .crop =3D { + .left =3D IMX585_PIXEL_ARRAY_LEFT, + .top =3D IMX585_PIXEL_ARRAY_TOP, + .width =3D IMX585_PIXEL_ARRAY_WIDTH, + .height =3D IMX585_PIXEL_ARRAY_HEIGHT, + }, + .reg_list =3D { + .num_of_regs =3D ARRAY_SIZE(mode_4k_regs_12bit), + .regs =3D mode_4k_regs_12bit, + }, + }, +}; + +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ + +/* 12bit Only */ +static const u32 codes_normal[] =3D { + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, +}; + +/* 12bit + 16bit for Clear HDR */ +static const u32 codes_clearhdr[] =3D { + /* 16-bit modes. */ + MEDIA_BUS_FMT_SRGGB16_1X16, + MEDIA_BUS_FMT_SGRBG16_1X16, + MEDIA_BUS_FMT_SGBRG16_1X16, + MEDIA_BUS_FMT_SBGGR16_1X16, + /* 12-bit modes. */ + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, +}; + +/* Flip isn=E2=80=99t relevant for mono */ +static const u32 mono_codes[] =3D { + MEDIA_BUS_FMT_Y16_1X16, /* 16-bit mono */ + MEDIA_BUS_FMT_Y12_1X12, /* 12-bit mono */ +}; + +/* regulator supplies */ +static const char * const imx585_supply_name[] =3D { + /* Supplies can be enabled in any order */ + "VANA", /* Analog (3.3V) supply */ + "VDIG", /* Digital Core (1.1V) supply */ + "VDDL", /* IF (1.8V) supply */ +}; + +#define imx585_NUM_SUPPLIES ARRAY_SIZE(imx585_supply_name) + +struct imx585 { + struct v4l2_subdev sd; + struct media_pad pad[NUM_PADS]; + + unsigned int fmt_code; + + struct clk *xclk; + u32 xclk_freq; + + /* chosen INCK_SEL register value */ + u8 inck_sel_val; + + /* Link configurations */ + unsigned int lane_count; + unsigned int link_freq_idx; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[imx585_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *hcg_ctrl; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *blacklevel; + + /* V4L2 HDR Controls */ + struct v4l2_ctrl *hdr_mode; + struct v4l2_ctrl *datasel_th_ctrl; + struct v4l2_ctrl *datasel_bk_ctrl; + struct v4l2_ctrl *gdc_th_ctrl; + struct v4l2_ctrl *gdc_exp_ctrl_l; + struct v4l2_ctrl *gdc_exp_ctrl_h; + struct v4l2_ctrl *hdr_gain_ctrl; + + /* V4L2 IR Cut filter switch Controls */ + bool has_ircut; + struct v4l2_ctrl *ircut_ctrl; + struct i2c_client *ircut_client; + + /* Current mode */ + const struct imx585_mode *mode; + + /* HCG enabled flag*/ + bool hcg; + + /* Mono mode */ + bool mono; + + /* Clear HDR mode */ + bool clear_HDR; + + /* Sync Mode*/ + /* 0 =3D Internal Sync Leader Mode + * 1 =3D External Sync Leader Mode + * 2 =3D Follower Mode + * The datasheet wording is very confusing but basically: + * Leader Mode =3D Sensor using internal clock to drive the sensor + * But with external sync mode you can send a XVS input so the sensor + * will try to align with it. + * For Follower mode it is purely driven by external clock. + * In this case you need to drive both XVS and XHS. + */ + u32 sync_mode; + + /* Tracking sensor VMAX/HMAX value */ + u16 HMAX; + u32 VMAX; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* Rewrite common registers on stream on? */ + bool common_regs_written; +}; + +static inline struct imx585 *to_imx585(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx585, sd); +} + +static inline void get_mode_table(struct imx585 *imx585, unsigned int code, + const struct imx585_mode **mode_list, + unsigned int *num_modes) +{ + *mode_list =3D NULL; + *num_modes =3D 0; + + if (imx585->mono) { + /* --- Mono paths --- */ + if (code =3D=3D MEDIA_BUS_FMT_Y16_1X16 && imx585->clear_HDR) { + *mode_list =3D supported_modes; + *num_modes =3D ARRAY_SIZE(supported_modes); + } + if (code =3D=3D MEDIA_BUS_FMT_Y12_1X12) { + *mode_list =3D supported_modes; + *num_modes =3D ARRAY_SIZE(supported_modes); + } + } else { + /* --- Color paths --- */ + switch (code) { + /* 16-bit */ + case MEDIA_BUS_FMT_SRGGB16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SBGGR16_1X16: + /* 12-bit */ + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + *mode_list =3D supported_modes; + *num_modes =3D ARRAY_SIZE(supported_modes); + break; + default: + *mode_list =3D NULL; + *num_modes =3D 0; + } + } +} + +/* ------------------------------------------------------------------ + * Optional IR-cut helper + * ------------------------------------------------------------------ + */ + +/* One-byte =E2=80=9Ccommand=E2=80=9D sent to the IR-cut MCU at imx585->ir= cut_client */ +static int imx585_ircut_write(struct imx585 *imx585, u8 cmd) +{ + struct i2c_client *client =3D imx585->ircut_client; + int ret; + + ret =3D i2c_smbus_write_byte(client, cmd); + if (ret < 0) + dev_err(&client->dev, "IR-cut write failed (%d)\n", ret); + + return ret; +} + +static int imx585_ircut_set(struct imx585 *imx585, int on) +{ + return imx585_ircut_write(imx585, on ? 0x01 : 0x00); +} + +/* Read registers up to 2 at a time */ +static int imx585_read_reg(struct imx585 *imx585, u16 reg, u32 len, u32 *v= al) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] =3D { reg >> 8, reg & 0xff }; + u8 data_buf[4] =3D { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + /* Write register address */ + msgs[0].addr =3D client->addr; + msgs[0].flags =3D 0; + msgs[0].len =3D ARRAY_SIZE(addr_buf); + msgs[0].buf =3D addr_buf; + + /* Read data from register */ + msgs[1].addr =3D client->addr; + msgs[1].flags =3D I2C_M_RD; + msgs[1].len =3D len; + msgs[1].buf =3D &data_buf[4 - len]; + + ret =3D i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret !=3D ARRAY_SIZE(msgs)) + return -EIO; + + *val =3D get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers 1 byte at a time */ +static int imx585_write_reg_1byte(struct imx585 *imx585, u16 reg, u8 val) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + u8 buf[3]; + int ret; + + put_unaligned_be16(reg, buf); + buf[2] =3D val; + ret =3D i2c_master_send(client, buf, 3); + if (ret !=3D 3) + return ret; + + return 0; +} + +/* Write registers 2 byte at a time */ +static int imx585_write_reg_2byte(struct imx585 *imx585, u16 reg, u16 val) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + u8 buf[4]; + int ret; + + put_unaligned_be16(reg, buf); + buf[2] =3D val; + buf[3] =3D val >> 8; + ret =3D i2c_master_send(client, buf, 4); + if (ret !=3D 4) + return ret; + + return 0; +} + +/* Write registers 3 byte at a time */ +static int imx585_write_reg_3byte(struct imx585 *imx585, u16 reg, u32 val) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + u8 buf[5]; + + put_unaligned_be16(reg, buf); + buf[2] =3D val; + buf[3] =3D val >> 8; + buf[4] =3D val >> 16; + if (i2c_master_send(client, buf, 5) !=3D 5) + return -EIO; + + return 0; +} + +/* Write a list of 1 byte registers */ +static int imx585_write_regs(struct imx585 *imx585, + const struct imx585_reg *regs, u32 len) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + unsigned int i; + int ret; + + for (i =3D 0; i < len; i++) { + ret =3D imx585_write_reg_1byte(imx585, regs[i].address, + regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Hold register values until hold is disabled */ +static inline void imx585_register_hold(struct imx585 *imx585, bool hold) +{ + imx585_write_reg_1byte(imx585, 0x3001, hold ? 1 : 0); +} + +/* Get bayer order based on flip setting. */ +static u32 imx585_get_format_code(struct imx585 *imx585, u32 code) +{ + unsigned int i; + + lockdep_assert_held(&imx585->mutex); + + if (imx585->mono) { + for (i =3D 0; i < ARRAY_SIZE(mono_codes); i++) + if (mono_codes[i] =3D=3D code) + break; + return mono_codes[i]; + } + + if (imx585->clear_HDR) { + for (i =3D 0; i < ARRAY_SIZE(codes_clearhdr); i++) + if (codes_clearhdr[i] =3D=3D code) + break; + return codes_clearhdr[i]; + } + + for (i =3D 0; i < ARRAY_SIZE(codes_normal); i++) + if (codes_normal[i] =3D=3D code) + break; + return codes_normal[i]; +} + +static void imx585_set_default_format(struct imx585 *imx585) +{ + /* Set default mode to max resolution */ + imx585->mode =3D &supported_modes[0]; + if (imx585->mono) + imx585->fmt_code =3D MEDIA_BUS_FMT_Y12_1X12; + else + imx585->fmt_code =3D MEDIA_BUS_FMT_SRGGB12_1X12; +} + +static int imx585_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx585 *imx585 =3D to_imx585(sd); + struct v4l2_mbus_framefmt *try_fmt_img =3D + v4l2_subdev_state_get_format(fh->state, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta =3D + v4l2_subdev_state_get_format(fh->state, METADATA_PAD); + struct v4l2_rect *try_crop; + + mutex_lock(&imx585->mutex); + + /* Initialize try_fmt for the image pad */ + try_fmt_img->width =3D supported_modes[0].width; + try_fmt_img->height =3D supported_modes[0].height; + if (imx585->mono) + try_fmt_img->code =3D imx585_get_format_code(imx585, MEDIA_BUS_FMT_Y12_1= X12); + else + try_fmt_img->code =3D imx585_get_format_code(imx585, MEDIA_BUS_FMT_SRGGB= 12_1X12); + + try_fmt_img->field =3D V4L2_FIELD_NONE; + + /* Initialize try_fmt for the embedded metadata pad */ + try_fmt_meta->width =3D IMX585_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height =3D IMX585_NUM_EMBEDDED_LINES; + try_fmt_meta->code =3D MEDIA_BUS_FMT_SENSOR_DATA; + try_fmt_meta->field =3D V4L2_FIELD_NONE; + + /* Initialize try_crop */ + try_crop =3D v4l2_subdev_state_get_crop(fh->state, IMAGE_PAD); + try_crop->left =3D IMX585_PIXEL_ARRAY_LEFT; + try_crop->top =3D IMX585_PIXEL_ARRAY_TOP; + try_crop->width =3D IMX585_PIXEL_ARRAY_WIDTH; + try_crop->height =3D IMX585_PIXEL_ARRAY_HEIGHT; + + mutex_unlock(&imx585->mutex); + + return 0; +} + +/* For HDR mode, Gain is limited to 0~80 and HCG is disabled + * For Normal mode, Gain is limited to 0~240 + */ +static void imx585_update_gain_limits(struct imx585 *imx585) +{ + bool hcg_on =3D imx585->hcg; + bool clear_hdr =3D imx585->clear_HDR; + u32 min =3D hcg_on ? IMX585_ANA_GAIN_MIN_HCG : IMX585_ANA_GAIN_MIN_NORMA= L; + u32 max =3D clear_hdr ? IMX585_ANA_GAIN_MAX_HDR : IMX585_ANA_GAIN_MAX_NO= RMAL; + u32 cur =3D imx585->gain->val; + + __v4l2_ctrl_modify_range(imx585->gain, + min, max, + IMX585_ANA_GAIN_STEP, + clamp(cur, min, max)); + + if (cur < min || cur > max) + __v4l2_ctrl_s_ctrl(imx585->gain, + clamp(cur, min, max)); +} + +static void imx585_update_hmax(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + + const u32 base_4lane =3D HMAX_table_4lane_4K[imx585->link_freq_idx]; + const u32 lane_scale =3D (imx585->lane_count =3D=3D 2) ? 2 : 1; + const u32 factor =3D base_4lane * lane_scale; + const u32 hdr_factor =3D (imx585->clear_HDR) ? 2 : 1; + + dev_info(&client->dev, "Upadte minimum HMAX\n"); + dev_info(&client->dev, "\tbase_4lane: %d\n", base_4lane); + dev_info(&client->dev, "\tlane_scale: %d\n", lane_scale); + dev_info(&client->dev, "\tfactor: %d\n", factor); + dev_info(&client->dev, "\thdr_factor: %d\n", hdr_factor); + + for (unsigned int i =3D 0; i < ARRAY_SIZE(supported_modes); ++i) { + u32 h =3D factor / supported_modes[i].hmax_div; + u64 v =3D IMX585_VMAX_DEFAULT * hdr_factor; + + supported_modes[i].min_HMAX =3D h; + supported_modes[i].default_HMAX =3D h; + supported_modes[i].min_VMAX =3D v; + supported_modes[i].default_VMAX =3D v; + dev_info(&client->dev, "\tvmax: %lld x hmax: %d\n", v, h); + } +} + +static void imx585_set_framing_limits(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + const struct imx585_mode *mode =3D imx585->mode; + u64 default_hblank, max_hblank; + u64 pixel_rate; + + imx585_update_hmax(imx585); + + dev_info(&client->dev, "mode: %d x %d\n", mode->width, mode->height); + + imx585->VMAX =3D mode->default_VMAX; + imx585->HMAX =3D mode->default_HMAX; + + pixel_rate =3D (u64)mode->width * IMX585_PIXEL_RATE; + /* In the case where ClearHDR is enabled, HMAX is effectly doubled */ + /* So pixel rate is half with the same HMAX with ClearHDR */ + do_div(pixel_rate, mode->min_HMAX); + __v4l2_ctrl_modify_range(imx585->pixel_rate, pixel_rate, pixel_rate, 1, p= ixel_rate); + + //int default_hblank =3D mode->default_HMAX*IMX585_PIXEL_RATE/72000000-IM= X585_NATIVE_WIDTH; + default_hblank =3D mode->default_HMAX * pixel_rate; + do_div(default_hblank, IMX585_PIXEL_RATE); + default_hblank =3D default_hblank - mode->width; + + max_hblank =3D IMX585_HMAX_MAX * pixel_rate; + do_div(max_hblank, IMX585_PIXEL_RATE); + max_hblank =3D max_hblank - mode->width; + + __v4l2_ctrl_modify_range(imx585->hblank, 0, max_hblank, 1, default_hblank= ); + __v4l2_ctrl_s_ctrl(imx585->hblank, default_hblank); + + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx585->vblank, mode->min_VMAX - mode->height, + IMX585_VMAX_MAX - mode->height, + 1, mode->default_VMAX - mode->height); + __v4l2_ctrl_s_ctrl(imx585->vblank, mode->default_VMAX - mode->height); + + __v4l2_ctrl_modify_range(imx585->exposure, IMX585_EXPOSURE_MIN, + imx585->VMAX - IMX585_SHR_MIN_HDR, 1, + IMX585_EXPOSURE_DEFAULT); + dev_info(&client->dev, "default vmax: %lld x hmax: %d\n", mode->min_VMAX,= mode->min_HMAX); + dev_info(&client->dev, "Setting default HBLANK : %llu, VBLANK : %llu Pixe= lRate: %lld\n", + default_hblank, mode->default_VMAX - mode->height, pixel_rate); +} + +static int imx585_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx585 *imx585 =3D container_of(ctrl->handler, struct imx585, ctrl= _handler); + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + const struct imx585_mode *mode =3D imx585->mode; + const struct imx585_mode *mode_list; + unsigned int code, num_modes; + + int ret =3D 0; + /* + * Applying V4L2 control value that + * doesn't need to be in streaming mode + */ + switch (ctrl->id) { + case V4L2_CID_WIDE_DYNAMIC_RANGE: + if (imx585->clear_HDR !=3D ctrl->val) { + imx585->clear_HDR =3D ctrl->val; + v4l2_ctrl_activate(imx585->datasel_th_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->datasel_bk_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_th_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_exp_ctrl_h, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_exp_ctrl_l, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->hdr_gain_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->hcg_ctrl, !imx585->clear_HDR); + imx585_update_gain_limits(imx585); + if (imx585->mono) + code =3D imx585_get_format_code(imx585, MEDIA_BUS_FMT_Y12_1X12); + else + code =3D imx585_get_format_code(imx585, MEDIA_BUS_FMT_SRGGB12_1X12); + get_mode_table(imx585, code, &mode_list, &num_modes); + imx585->mode =3D v4l2_find_nearest_size(mode_list, + num_modes, + width, height, + imx585->mode->width, + imx585->mode->height); + imx585_set_framing_limits(imx585); + } + break; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) =3D=3D 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + u32 shr; + + shr =3D (imx585->VMAX - ctrl->val) & ~1u; //Always a multiple of 2 + dev_info(&client->dev, "V4L2_CID_EXPOSURE : %d\n", ctrl->val); + dev_info(&client->dev, "\tVMAX:%d, HMAX:%d\n", imx585->VMAX, imx585->HM= AX); + dev_info(&client->dev, "\tSHR:%d\n", shr); + + ret =3D imx585_write_reg_3byte(imx585, IMX585_REG_SHR, shr); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_SHR, ret); + break; + } + case V4L2_CID_IMX585_HCG_GAIN: + { + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + break; + imx585->hcg =3D ctrl->val; + imx585_update_gain_limits(imx585); + + // Set HCG/LCG channel + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_FDG_SEL0, ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_FDG_SEL0, ret); + dev_info(&client->dev, "V4L2_CID_HCG_ENABLE: %d\n", ctrl->val); + break; + } + case V4L2_CID_ANALOGUE_GAIN: + { + u32 gain =3D ctrl->val; + + dev_info(&client->dev, "analogue gain =3D %u (%s)\n", + gain, imx585->hcg ? "HCG" : "LCG"); + + ret =3D imx585_write_reg_2byte(imx585, IMX585_REG_ANALOG_GAIN, gain); + if (ret) + dev_err_ratelimited(&client->dev, + "ANALOG_GAIN write failed (%d)\n", ret); + break; + } + case V4L2_CID_VBLANK: + { + u32 current_exposure =3D imx585->exposure->cur.val; + u32 min_SHR =3D (imx585->clear_HDR) ? IMX585_SHR_MIN_HDR : IMX585_SHR_M= IN; + /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + imx585->VMAX =3D (mode->height + ctrl->val) & ~1u; //Always a multiple = of 2 + + /* New maximum exposure limits, + * modifying the range and make sure we are not exceed the new maximum. + */ + current_exposure =3D clamp_t(u32, current_exposure, IMX585_EXPOSURE_MIN, + imx585->VMAX - min_SHR); + __v4l2_ctrl_modify_range(imx585->exposure, IMX585_EXPOSURE_MIN, + imx585->VMAX - min_SHR, 1, + current_exposure); + + dev_info(&client->dev, "V4L2_CID_VBLANK : %d\n", ctrl->val); + dev_info(&client->dev, "\tVMAX:%d, HMAX:%d\n", imx585->VMAX, imx585->HM= AX); + dev_info(&client->dev, "Update exposure limits: max:%d, min:%d, current= :%d\n", + imx585->VMAX - min_SHR, + IMX585_EXPOSURE_MIN, current_exposure); + + ret =3D imx585_write_reg_3byte(imx585, IMX585_REG_VMAX, imx585->VMAX); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_VMAX, ret); + break; + } + case V4L2_CID_HBLANK: + { + u64 pixel_rate; + u64 hmax; + + pixel_rate =3D (u64)mode->width * IMX585_PIXEL_RATE; + do_div(pixel_rate, mode->min_HMAX); + hmax =3D (u64)(mode->width + ctrl->val) * IMX585_PIXEL_RATE; + do_div(hmax, pixel_rate); + imx585->HMAX =3D hmax; + + dev_info(&client->dev, "V4L2_CID_HBLANK : %d\n", ctrl->val); + dev_info(&client->dev, "\tHMAX : %d\n", imx585->HMAX); + + ret =3D imx585_write_reg_2byte(imx585, IMX585_REG_HMAX, hmax); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_HMAX, ret); + break; + } + case V4L2_CID_HFLIP: + dev_info(&client->dev, "V4L2_CID_HFLIP : %d\n", ctrl->val); + ret =3D imx585_write_reg_1byte(imx585, IMX585_FLIP_WINMODEH, ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_FLIP_WINMODEH, ret); + break; + case V4L2_CID_VFLIP: + dev_info(&client->dev, "V4L2_CID_VFLIP : %d\n", ctrl->val); + ret =3D imx585_write_reg_1byte(imx585, IMX585_FLIP_WINMODEV, ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_FLIP_WINMODEV, ret); + break; + case V4L2_CID_BRIGHTNESS: + { + u16 blacklevel =3D ctrl->val; + + dev_info(&client->dev, "V4L2_CID_BRIGHTNESS : %d\n", ctrl->val); + + if (blacklevel > 4095) + blacklevel =3D 4095; + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_BLKLEVEL, blacklevel); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_BLKLEVEL, ret); + break; + } + case V4L2_CID_BAND_STOP_FILTER: + if (imx585->has_ircut) { + dev_info(&client->dev, "V4L2_CID_BAND_STOP_FILTER : %d\n", ctrl->val); + imx585_ircut_set(imx585, ctrl->val); + } + break; + case V4L2_CID_IMX585_HDR_DATASEL_TH:{ + const u16 *th =3D (const u16 *)ctrl->p_new.p; + + ret =3D imx585_write_reg_2byte(imx585, IMX585_REG_EXP_TH_H, th[0]); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_EXP_TH_H, ret); + ret =3D imx585_write_reg_2byte(imx585, IMX585_REG_EXP_TH_L, th[1]); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_EXP_TH_L, ret); + dev_info(&client->dev, "V4L2_CID_IMX585_HDR_DATASEL_TH : %d, %d\n", th[0= ], th[1]); + break; + } + case V4L2_CID_IMX585_HDR_DATASEL_BK: + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_EXP_BK, ctrl->val); + dev_info(&client->dev, "V4L2_CID_IMX585_HDR_DATASEL_BK : %d\n", ctrl->va= l); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_EXP_BK, ret); + break; + case V4L2_CID_IMX585_HDR_GRAD_TH:{ + const u32 *thr =3D (const u32 *)ctrl->p_new.p; + + ret =3D imx585_write_reg_3byte(imx585, IMX585_REG_CCMP1_EXP, thr[0]); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_CCMP1_EXP, ret); + ret =3D imx585_write_reg_3byte(imx585, IMX585_REG_CCMP2_EXP, thr[1]); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_CCMP2_EXP, ret); + dev_info(&client->dev, "V4L2_CID_IMX585_HDR_GRAD_TH : %d, %d\n", thr[0],= thr[1]); + break; + } + case V4L2_CID_IMX585_HDR_GRAD_COMP_L:{ + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_ACMP1_EXP, ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_ACMP1_EXP, ret); + dev_info(&client->dev, "V4L2_CID_IMX585_HDR_GRAD_COMP_L : %d\n", ctrl->v= al); + break; + } + case V4L2_CID_IMX585_HDR_GRAD_COMP_H:{ + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_ACMP2_EXP, ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_ACMP2_EXP, ret); + dev_info(&client->dev, "V4L2_CID_IMX585_HDR_GRAD_COMP_H : %d\n", ctrl->v= al); + break; + } + case V4L2_CID_IMX585_HDR_GAIN: + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_EXP_GAIN, ctrl->val); + dev_info(&client->dev, "IMX585_REG_EXP_GAIN : %d\n", ctrl->val); + if (ret) + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error =3D %d\n", + IMX585_REG_EXP_GAIN, ret); + break; + case V4L2_CID_WIDE_DYNAMIC_RANGE: + /* Already handled above. */ + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx585_ctrl_ops =3D { + .s_ctrl =3D imx585_set_ctrl, +}; + +static const u16 hdr_thresh_def[2] =3D { 512, 1024 }; +static const struct v4l2_ctrl_config imx585_cfg_datasel_th =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_DATASEL_TH, + .name =3D "HDR Data selection Threshold", + .type =3D V4L2_CTRL_TYPE_U16, + .min =3D 0, + .max =3D 0x0FFF, + .step =3D 1, + .def =3D 0, + .dims =3D { 2 }, + .elem_size =3D sizeof(u16), +}; + +static const struct v4l2_ctrl_config imx585_cfg_datasel_bk =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_DATASEL_BK, + .name =3D "HDR Data Blending Mode", + .type =3D V4L2_CTRL_TYPE_MENU, + .max =3D ARRAY_SIZE(hdr_data_blender_menu) - 1, + .menu_skip_mask =3D 0, + .def =3D 0, + .qmenu =3D hdr_data_blender_menu, +}; + +static const u32 grad_thresh_def[2] =3D { 500, 11500 }; +static const struct v4l2_ctrl_config imx585_cfg_grad_th =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_GRAD_TH, + .name =3D "Gradiant Compression Threshold (16bit)", + .type =3D V4L2_CTRL_TYPE_U32, + .min =3D 0, + .max =3D 0x1FFFF, + .step =3D 1, + .def =3D 0, + .dims =3D { 2 }, + .elem_size =3D sizeof(u32), +}; + +static const struct v4l2_ctrl_config imx585_cfg_grad_exp_l =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_GRAD_COMP_L, + .name =3D "Gradiant Compression Ratio Low", + .type =3D V4L2_CTRL_TYPE_MENU, + .min =3D 0, + .max =3D ARRAY_SIZE(grad_compression_slope_menu) - 1, + .menu_skip_mask =3D 0, + .def =3D 2, + .qmenu =3D grad_compression_slope_menu, +}; + +static const struct v4l2_ctrl_config imx585_cfg_grad_exp_h =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_GRAD_COMP_H, + .name =3D "Gradiant Compression Ratio High", + .type =3D V4L2_CTRL_TYPE_MENU, + .min =3D 0, + .max =3D ARRAY_SIZE(grad_compression_slope_menu) - 1, + .menu_skip_mask =3D 0, + .def =3D 6, + .qmenu =3D grad_compression_slope_menu, +}; + +static const struct v4l2_ctrl_config imx585_cfg_hdr_gain =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HDR_GAIN, + .name =3D "HDR Gain Adder (dB)", + .type =3D V4L2_CTRL_TYPE_MENU, + .min =3D 0, + .max =3D ARRAY_SIZE(hdr_gain_adder_menu) - 1, + .menu_skip_mask =3D 0, + .def =3D 2, + .qmenu =3D hdr_gain_adder_menu, +}; + +static const struct v4l2_ctrl_config imx585_cfg_hcg =3D { + .ops =3D &imx585_ctrl_ops, + .id =3D V4L2_CID_IMX585_HCG_GAIN, + .name =3D "HCG Enable", + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .min =3D 0, + .max =3D 1, + .step =3D 1, + .def =3D 0, +}; + +static int imx585_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx585 *imx585 =3D to_imx585(sd); + unsigned int entries; + const u32 *tbl; + + if (code->pad >=3D NUM_PADS) + return -EINVAL; + + if (code->pad =3D=3D IMAGE_PAD) { + if (imx585->mono) { + if (imx585->clear_HDR) { + if (code->index > 1) + return -EINVAL; + code->code =3D mono_codes[code->index]; + return 0; + } + /* HDR off: expose Y12 only */ + if (code->index) + return -EINVAL; + + code->code =3D MEDIA_BUS_FMT_Y12_1X12; + return 0; + } + + if (imx585->clear_HDR) { + tbl =3D codes_clearhdr; /* << 16bit + 12bit */ + entries =3D ARRAY_SIZE(codes_clearhdr) / 4; + } else { + tbl =3D codes_normal; /* << ONLY 12bit */ + entries =3D ARRAY_SIZE(codes_normal) / 4; + } + + if (code->index >=3D entries) + return -EINVAL; + + code->code =3D imx585_get_format_code(imx585, tbl[code->index * 4]); + return 0; + } + /* --- Metadata pad ------------------------------------------------- */ + if (code->index) + return -EINVAL; + + code->code =3D MEDIA_BUS_FMT_SENSOR_DATA; + return 0; +} + +static int imx585_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx585 *imx585 =3D to_imx585(sd); + + if (fse->pad >=3D NUM_PADS) + return -EINVAL; + + if (fse->pad =3D=3D IMAGE_PAD) { + const struct imx585_mode *mode_list; + unsigned int num_modes; + + get_mode_table(imx585, fse->code, &mode_list, &num_modes); + + if (fse->index >=3D num_modes) + return -EINVAL; + + if (fse->code !=3D imx585_get_format_code(imx585, fse->code)) + return -EINVAL; + + fse->min_width =3D mode_list[fse->index].width; + fse->max_width =3D fse->min_width; + fse->min_height =3D mode_list[fse->index].height; + fse->max_height =3D fse->min_height; + } else { + if (fse->code !=3D MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + return -EINVAL; + + fse->min_width =3D IMX585_EMBEDDED_LINE_WIDTH; + fse->max_width =3D fse->min_width; + fse->min_height =3D IMX585_NUM_EMBEDDED_LINES; + fse->max_height =3D fse->min_height; + } + + return 0; +} + +static void imx585_reset_colorspace(const struct imx585_mode *mode, struct= v4l2_mbus_framefmt *fmt) +{ + fmt->colorspace =3D V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc =3D V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization =3D V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func =3D V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +} + +static void imx585_update_image_pad_format(struct imx585 *imx585, + const struct imx585_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width =3D mode->width; + fmt->format.height =3D mode->height; + fmt->format.field =3D V4L2_FIELD_NONE; + imx585_reset_colorspace(mode, &fmt->format); +} + +static void imx585_update_metadata_pad_format(struct v4l2_subdev_format *f= mt) +{ + fmt->format.width =3D IMX585_EMBEDDED_LINE_WIDTH; + fmt->format.height =3D IMX585_NUM_EMBEDDED_LINES; + fmt->format.code =3D MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format.field =3D V4L2_FIELD_NONE; +} + +static int imx585_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx585 *imx585 =3D to_imx585(sd); + + if (fmt->pad >=3D NUM_PADS) + return -EINVAL; + + mutex_lock(&imx585->mutex); + + if (fmt->which =3D=3D V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt =3D + v4l2_subdev_state_get_format(sd_state, fmt->pad); + /* update the code which could change due to vflip or hflip: */ + try_fmt->code =3D fmt->pad =3D=3D IMAGE_PAD ? + imx585_get_format_code(imx585, try_fmt->code) : + MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format =3D *try_fmt; + } else { + if (fmt->pad =3D=3D IMAGE_PAD) { + imx585_update_image_pad_format(imx585, imx585->mode, fmt); + fmt->format.code =3D + imx585_get_format_code(imx585, imx585->fmt_code); + } else { + imx585_update_metadata_pad_format(fmt); + } + } + + mutex_unlock(&imx585->mutex); + return 0; +} + +static int imx585_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + const struct imx585_mode *mode; + struct imx585 *imx585 =3D to_imx585(sd); + + if (fmt->pad >=3D NUM_PADS) + return -EINVAL; + + mutex_lock(&imx585->mutex); + + if (fmt->pad =3D=3D IMAGE_PAD) { + const struct imx585_mode *mode_list; + unsigned int num_modes; + + /* Bayer order varies with flips */ + fmt->format.code =3D imx585_get_format_code(imx585, fmt->format.code); + get_mode_table(imx585, fmt->format.code, &mode_list, &num_modes); + mode =3D v4l2_find_nearest_size(mode_list, + num_modes, + width, height, + fmt->format.width, + fmt->format.height); + imx585_update_image_pad_format(imx585, mode, fmt); + if (fmt->which =3D=3D V4L2_SUBDEV_FORMAT_TRY) { + framefmt =3D v4l2_subdev_state_get_format(sd_state, fmt->pad); + *framefmt =3D fmt->format; + } else if (imx585->mode !=3D mode || + imx585->fmt_code !=3D fmt->format.code) { + imx585->mode =3D mode; + imx585->fmt_code =3D fmt->format.code; + imx585_set_framing_limits(imx585); + } + } else { + if (fmt->which =3D=3D V4L2_SUBDEV_FORMAT_TRY) { + framefmt =3D v4l2_subdev_state_get_format(sd_state, fmt->pad); + *framefmt =3D fmt->format; + } else { + /* Only one embedded data mode is supported */ + imx585_update_metadata_pad_format(fmt); + } + } + + mutex_unlock(&imx585->mutex); + + return 0; +} + +static const struct v4l2_rect * +__imx585_get_pad_crop(struct imx585 *imx585, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_state_get_crop(sd_state, IMAGE_PAD); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx585->mode->crop; + } + + return NULL; +} + +/* Start streaming */ +static int imx585_start_streaming(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + const struct IMX585_reg_list *reg_list; + int ret; + + if (!imx585->common_regs_written) { + ret =3D imx585_write_regs(imx585, common_regs, ARRAY_SIZE(common_regs)); + if (ret) { + dev_err(&client->dev, "%s failed to set common settings\n", __func__); + return ret; + } + + imx585_write_reg_1byte(imx585, IMX585_INCK_SEL, imx585->inck_sel_val); + imx585_write_reg_2byte(imx585, IMX585_REG_BLKLEVEL, IMX585_BLKLEVEL_DEFA= ULT); + imx585_write_reg_1byte(imx585, IMX585_DATARATE_SEL, + link_freqs_reg_value[imx585->link_freq_idx]); + + if (imx585->lane_count =3D=3D 2) + imx585_write_reg_1byte(imx585, IMX585_LANEMODE, 0x01); + else + imx585_write_reg_1byte(imx585, IMX585_LANEMODE, 0x03); + + if (imx585->mono) + imx585_write_reg_1byte(imx585, IMX585_BIN_MODE, 0x01); + else + imx585_write_reg_1byte(imx585, IMX585_BIN_MODE, 0x00); + + if (imx585->sync_mode =3D=3D 1) { //External Sync Leader Mode + dev_info(&client->dev, "External Sync Leader Mode, enable XVS input\n"); + imx585_write_reg_1byte(imx585, IMX585_REG_EXTMODE, 0x01); + // Enable XHS output, but XVS is input + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_DRV, 0x03); + // Disable XVS OUT + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_OUTSEL, 0x08); + } else if (imx585->sync_mode =3D=3D 0) { //Internal Sync Leader Mode + dev_info(&client->dev, "Internal Sync Leader Mode, enable output\n"); + imx585_write_reg_1byte(imx585, IMX585_REG_EXTMODE, 0x00); + // Enable XHS and XVS output + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_DRV, 0x00); + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_OUTSEL, 0x0A); + } else { + dev_info(&client->dev, "Follower Mode, enable XVS/XHS input\n"); + //For follower mode, switch both of them to input + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_DRV, 0x0F); + imx585_write_reg_1byte(imx585, IMX585_REG_XXS_OUTSEL, 0x00); + } + imx585->common_regs_written =3D true; + dev_info(&client->dev, "common_regs_written\n"); + } + + /* Apply default values of current mode */ + reg_list =3D &imx585->mode->reg_list; + ret =3D imx585_write_regs(imx585, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + if (imx585->clear_HDR) { + ret =3D imx585_write_regs(imx585, common_clearHDR_mode, + ARRAY_SIZE(common_clearHDR_mode)); + if (ret) { + dev_err(&client->dev, "%s failed to set ClearHDR settings\n", __func__); + return ret; + } + //16bit mode is linear, 12bit mode we need to enable gradation compressi= on + switch (imx585->fmt_code) { + /* 16-bit */ + case MEDIA_BUS_FMT_SRGGB16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_Y16_1X16: + imx585_write_reg_1byte(imx585, IMX595_REG_CCMP_EN, 0); + imx585_write_reg_1byte(imx585, 0x3023, 0x03); // MDBIT 16-bit + dev_info(&client->dev, "16bit HDR written\n"); + break; + /* 12-bit */ + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_Y12_1X12: + imx585_write_reg_1byte(imx585, IMX595_REG_CCMP_EN, 1); + dev_info(&client->dev, "12bit HDR written\n"); + break; + default: + break; + } + dev_info(&client->dev, "ClearHDR_regs_written\n"); + + } else { + ret =3D imx585_write_regs(imx585, common_normal_mode, ARRAY_SIZE(common_= normal_mode)); + if (ret) { + dev_err(&client->dev, "%s failed to set Normal settings\n", __func__); + return ret; + } + dev_info(&client->dev, "normal_regs_written\n"); + } + + /* Disable digital clamp */ + imx585_write_reg_1byte(imx585, IMX585_REG_DIGITAL_CLAMP, 0); + + /* Apply customized values from user */ + ret =3D __v4l2_ctrl_handler_setup(imx585->sd.ctrl_handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply user values\n", __func__); + return ret; + } + + if (imx585->sync_mode <=3D 1) { + dev_info(&client->dev, "imx585 Leader mode enabled\n"); + imx585_write_reg_1byte(imx585, IMX585_REG_XMSTA, 0x00); + } + + /* Set stream on register */ + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_MODE_SELECT, IMX585_MOD= E_STREAMING); + + dev_info(&client->dev, "Start Streaming\n"); + usleep_range(IMX585_STREAM_DELAY_US, IMX585_STREAM_DELAY_US + IMX585_STRE= AM_DELAY_RANGE_US); + return ret; +} + +/* Stop streaming */ +static void imx585_stop_streaming(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + int ret; + + dev_info(&client->dev, "Stop Streaming\n"); + + /* set stream off register */ + ret =3D imx585_write_reg_1byte(imx585, IMX585_REG_MODE_SELECT, IMX585_MOD= E_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to stop stream\n", __func__); +} + +static int imx585_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx585 *imx585 =3D to_imx585(sd); + struct i2c_client *client =3D v4l2_get_subdevdata(sd); + int ret =3D 0; + + mutex_lock(&imx585->mutex); + if (imx585->streaming =3D=3D enable) { + mutex_unlock(&imx585->mutex); + return 0; + } + + if (enable) { + ret =3D pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret =3D imx585_start_streaming(imx585); + if (ret) + goto err_rpm_put; + } else { + imx585_stop_streaming(imx585); + pm_runtime_put(&client->dev); + } + + imx585->streaming =3D enable; + + /* vflip/hflip and hdr mode cannot change during streaming */ + __v4l2_ctrl_grab(imx585->vflip, enable); + __v4l2_ctrl_grab(imx585->hflip, enable); + __v4l2_ctrl_grab(imx585->hdr_mode, enable); + + mutex_unlock(&imx585->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx585->mutex); + + return ret; +} + +/* Power/clock management functions */ +static int imx585_power_on(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx585 *imx585 =3D to_imx585(sd); + int ret; + + ret =3D regulator_bulk_enable(imx585_NUM_SUPPLIES, + imx585->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret =3D clk_prepare_enable(imx585->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + gpiod_set_value_cansleep(imx585->reset_gpio, 1); + usleep_range(IMX585_XCLR_MIN_DELAY_US, + IMX585_XCLR_MIN_DELAY_US + IMX585_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(imx585_NUM_SUPPLIES, imx585->supplies); + return ret; +} + +static int imx585_power_off(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx585 *imx585 =3D to_imx585(sd); + + gpiod_set_value_cansleep(imx585->reset_gpio, 0); + regulator_bulk_disable(imx585_NUM_SUPPLIES, imx585->supplies); + clk_disable_unprepare(imx585->xclk); + + /* Force reprogramming of the common registers when powered up again. */ + imx585->common_regs_written =3D false; + + return 0; +} + +static int __maybe_unused imx585_suspend(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx585 *imx585 =3D to_imx585(sd); + + if (imx585->streaming) + imx585_stop_streaming(imx585); + + return 0; +} + +static int __maybe_unused imx585_resume(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx585 *imx585 =3D to_imx585(sd); + int ret; + + if (imx585->streaming) { + ret =3D imx585_start_streaming(imx585); + if (ret) + goto error; + } + + return 0; + +error: + imx585_stop_streaming(imx585); + imx585->streaming =3D 0; + return ret; +} + +static int imx585_get_regulators(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + unsigned int i; + + for (i =3D 0; i < imx585_NUM_SUPPLIES; i++) + imx585->supplies[i].supply =3D imx585_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + imx585_NUM_SUPPLIES, + imx585->supplies); +} + +/* Verify chip ID */ +static int imx585_check_module_exists(struct imx585 *imx585) +{ + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + int ret; + u32 val; + + /* We don't actually have a CHIP ID register so we try to read from BLKLE= VEL instead*/ + ret =3D imx585_read_reg(imx585, IMX585_REG_BLKLEVEL, + 1, &val); + if (ret) { + dev_err(&client->dev, "failed to read chip reg, with error %d\n", ret); + return ret; + } + + dev_info(&client->dev, "Reg read success, Device found\n"); + + return 0; +} + +static int imx585_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx585 *imx585 =3D to_imx585(sd); + + mutex_lock(&imx585->mutex); + sel->r =3D *__imx585_get_pad_crop(imx585, sd_state, sel->pad, sel->which= ); + mutex_unlock(&imx585->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left =3D 0; + sel->r.top =3D 0; + sel->r.width =3D IMX585_NATIVE_WIDTH; + sel->r.height =3D IMX585_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left =3D IMX585_PIXEL_ARRAY_LEFT; + sel->r.top =3D IMX585_PIXEL_ARRAY_TOP; + sel->r.width =3D IMX585_PIXEL_ARRAY_WIDTH; + sel->r.height =3D IMX585_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_subdev_core_ops imx585_core_ops =3D { + .subscribe_event =3D v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event =3D v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx585_video_ops =3D { + .s_stream =3D imx585_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx585_pad_ops =3D { + .enum_mbus_code =3D imx585_enum_mbus_code, + .get_fmt =3D imx585_get_pad_format, + .set_fmt =3D imx585_set_pad_format, + .get_selection =3D imx585_get_selection, + .enum_frame_size =3D imx585_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx585_subdev_ops =3D { + .core =3D &imx585_core_ops, + .video =3D &imx585_video_ops, + .pad =3D &imx585_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx585_internal_ops =3D { + .open =3D imx585_open, +}; + +/* Initialize control handlers */ +static int imx585_init_controls(struct imx585 *imx585) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + struct i2c_client *client =3D v4l2_get_subdevdata(&imx585->sd); + struct v4l2_fwnode_device_properties props; + int ret; + + ctrl_hdlr =3D &imx585->ctrl_handler; + ret =3D v4l2_ctrl_handler_init(ctrl_hdlr, 32); + if (ret) + return ret; + + mutex_init(&imx585->mutex); + ctrl_hdlr->lock =3D &imx585->mutex; + + /* + * Create the controls here, but mode specific limits are setup + * in the imx585_set_framing_limits() call below. + */ + /* By default, PIXEL_RATE is read only */ + imx585->pixel_rate =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 0xffff, + 0xffff, 1, + 0xffff); + + /* LINK_FREQ is also read only */ + imx585->link_freq =3D + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_LINK_FREQ, 0, 0, + &link_freqs[imx585->link_freq_idx]); + if (imx585->link_freq) + imx585->link_freq->flags |=3D V4L2_CTRL_FLAG_READ_ONLY; + + imx585->vblank =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_VBLANK, 0, 0xfffff, 1, 0); + imx585->hblank =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_HBLANK, 0, 0xffff, 1, 0); + imx585->blacklevel =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xffff, 1, + IMX585_BLKLEVEL_DEFAULT); + + imx585->exposure =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX585_EXPOSURE_MIN, + IMX585_EXPOSURE_MAX, + IMX585_EXPOSURE_STEP, + IMX585_EXPOSURE_DEFAULT); + + imx585->gain =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, V4L2_CID_= ANALOGUE_GAIN, + IMX585_ANA_GAIN_MIN_NORMAL, IMX585_ANA_GAIN_MAX_NORMAL, + IMX585_ANA_GAIN_STEP, IMX585_ANA_GAIN_DEFAULT); + + imx585->hflip =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, V4L2_CID= _HFLIP, 0, 1, 1, 0); + imx585->vflip =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, V4L2_CID= _VFLIP, 0, 1, 1, 0); + + if (imx585->has_ircut) { + imx585->ircut_ctrl =3D + v4l2_ctrl_new_std(&imx585->ctrl_handler, &imx585_ctrl_ops, + V4L2_CID_BAND_STOP_FILTER, + 0, 1, 1, 1); + } + + imx585->hdr_mode =3D v4l2_ctrl_new_std(ctrl_hdlr, &imx585_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, + 0, 1, 1, 0); + imx585->datasel_th_ctrl =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_datasel_th, NULL); + imx585->datasel_bk_ctrl =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_datasel_bk, NULL); + imx585->gdc_th_ctrl =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_grad_th, NULL); + imx585->gdc_exp_ctrl_l =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_grad_exp_l, NULL); + imx585->gdc_exp_ctrl_h =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_grad_exp_h, NULL); + imx585->hdr_gain_ctrl =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_hdr_gain, NULL); + imx585->hcg_ctrl =3D v4l2_ctrl_new_custom(ctrl_hdlr, + &imx585_cfg_hcg, NULL); + + v4l2_ctrl_activate(imx585->datasel_th_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->datasel_bk_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_th_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_exp_ctrl_l, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->gdc_exp_ctrl_h, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->hdr_gain_ctrl, imx585->clear_HDR); + v4l2_ctrl_activate(imx585->hcg_ctrl, !imx585->clear_HDR); + + if (ctrl_hdlr->error) { + ret =3D ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + ret =3D v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret =3D v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx585_ctrl_ops, &pro= ps); + if (ret) + goto error; + + memcpy(imx585->datasel_th_ctrl->p_cur.p, hdr_thresh_def, sizeof(hdr_thres= h_def)); + memcpy(imx585->datasel_th_ctrl->p_new.p, hdr_thresh_def, sizeof(hdr_thres= h_def)); + memcpy(imx585->gdc_th_ctrl->p_cur.p, grad_thresh_def, sizeof(grad_thresh_= def)); + memcpy(imx585->gdc_th_ctrl->p_new.p, grad_thresh_def, sizeof(grad_thresh_= def)); + + imx585->hdr_mode->flags |=3D V4L2_CTRL_FLAG_UPDATE; + imx585->hdr_mode->flags |=3D V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx585->sd.ctrl_handler =3D ctrl_hdlr; + + /* Setup exposure and frame/line length limits. */ + imx585_set_framing_limits(imx585); + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx585->mutex); + + return ret; +} + +static void imx585_free_controls(struct imx585 *imx585) +{ + v4l2_ctrl_handler_free(imx585->sd.ctrl_handler); + mutex_destroy(&imx585->mutex); +} + +static const struct of_device_id imx585_dt_ids[] =3D { + { .compatible =3D "sony,imx585"}, + { /* sentinel */ } +}; + +static int imx585_check_hwcfg(struct device *dev, struct imx585 *imx585) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg =3D { + .bus_type =3D V4L2_MBUS_CSI2_DPHY + }; + int ret =3D -EINVAL; + int i; + + endpoint =3D fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes !=3D 2 && ep_cfg.bus.mipi_csi2.nu= m_data_lanes !=3D 4) { + dev_err(dev, "only 2 or 4 data lanes are currently supported\n"); + goto error_out; + } + imx585->lane_count =3D ep_cfg.bus.mipi_csi2.num_data_lanes; + dev_info(dev, "Data lanes: %d\n", imx585->lane_count); + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + for (i =3D 0; i < ARRAY_SIZE(link_freqs); i++) { + if (link_freqs[i] =3D=3D ep_cfg.link_frequencies[0]) { + imx585->link_freq_idx =3D i; + break; + } + } + + if (i =3D=3D ARRAY_SIZE(link_freqs)) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + ret =3D -EINVAL; + goto error_out; + } + + dev_info(dev, "Link Speed: %lld Mhz\n", ep_cfg.link_frequencies[0]); + + ret =3D 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx585_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct device_node *np; + struct imx585 *imx585; + const struct of_device_id *match; + int ret, i; + u32 sync_mode; + + imx585 =3D devm_kzalloc(&client->dev, sizeof(*imx585), GFP_KERNEL); + if (!imx585) + return -ENOMEM; + + v4l2_i2c_subdev_init(&imx585->sd, client, &imx585_subdev_ops); + + match =3D of_match_device(imx585_dt_ids, dev); + if (!match) + return -ENODEV; + + dev_info(dev, "Reading dtoverlay config:\n"); + imx585->mono =3D of_property_read_bool(dev->of_node, "mono-mode"); + if (imx585->mono) + dev_info(dev, "Mono Mode Selected, make sure you have the correct sensor= variant\n"); + + imx585->clear_HDR =3D of_property_read_bool(dev->of_node, "clearHDR-mode"= ); + dev_info(dev, "ClearHDR: %d\n", imx585->clear_HDR); + + imx585->sync_mode =3D 0; + ret =3D of_property_read_u32(dev->of_node, "sync-mode", &sync_mode); + if (!ret) { + if (sync_mode > 2) { + dev_warn(dev, "sync-mode out of range, using 0\n"); + sync_mode =3D 0; + } + imx585->sync_mode =3D sync_mode; + } else if (ret !=3D -EINVAL) { /* property present but bad */ + dev_err(dev, "sync-mode malformed (%pe)\n", ERR_PTR(ret)); + return ret; + } + dev_info(dev, "Sync Mode: %s\n", sync_mode_menu[imx585->sync_mode]); + + /* Check the hardware configuration in device tree */ + if (imx585_check_hwcfg(dev, imx585)) + return -EINVAL; + + /* Get system clock (xclk) */ + imx585->xclk =3D devm_clk_get(dev, NULL); + if (IS_ERR(imx585->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(imx585->xclk); + } + + imx585->xclk_freq =3D clk_get_rate(imx585->xclk); + + for (i =3D 0; i < ARRAY_SIZE(imx585_inck_table); ++i) { + if (imx585_inck_table[i].xclk_hz =3D=3D imx585->xclk_freq) { + imx585->inck_sel_val =3D imx585_inck_table[i].inck_sel; + break; + } + } + + if (i =3D=3D ARRAY_SIZE(imx585_inck_table)) { + dev_err(dev, "unsupported XCLK rate %u Hz\n", + imx585->xclk_freq); + return -EINVAL; + } + + dev_info(dev, "XCLK %u Hz =E2=86=92 INCK_SEL 0x%02x\n", + imx585->xclk_freq, imx585->inck_sel_val); + + ret =3D imx585_get_regulators(imx585); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Request optional enable pin */ + imx585->reset_gpio =3D devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + /* + * The sensor must be powered for imx585_check_module_exists() + * to be able to read register + */ + ret =3D imx585_power_on(dev); + if (ret) + return ret; + + ret =3D imx585_check_module_exists(imx585); + if (ret) + goto error_power_off; + + imx585->has_ircut =3D false; + imx585->ircut_client =3D NULL; + + if (of_property_read_bool(dev->of_node, "ircut-mode")) { + np =3D of_parse_phandle(dev->of_node, "ircut-controller", 0); + if (np) { + imx585->ircut_client =3D of_find_i2c_device_by_node(np); + of_node_put(np); + ret =3D imx585_ircut_write(imx585, 0x01); + if (!ret) { + imx585->has_ircut =3D true; + dev_info(dev, "IR-cut controller present at 0x%02x\n", + imx585->ircut_client->addr); + } else { + dev_info(dev, + "Can't communication with IR-cut control, dropping\n"); + } + } + } else { + dev_info(dev, "No IR-cut controller\n"); + } + + /* Initialize default format */ + imx585_set_default_format(imx585); + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + /* This needs the pm runtime to be registered. */ + ret =3D imx585_init_controls(imx585); + if (ret) + goto error_pm_runtime; + + /* Initialize subdev */ + imx585->sd.internal_ops =3D &imx585_internal_ops; + imx585->sd.flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx585->sd.entity.function =3D MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + imx585->pad[IMAGE_PAD].flags =3D MEDIA_PAD_FL_SOURCE; + imx585->pad[METADATA_PAD].flags =3D MEDIA_PAD_FL_SOURCE; + + ret =3D media_entity_pads_init(&imx585->sd.entity, NUM_PADS, imx585->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + ret =3D v4l2_async_register_subdev_sensor(&imx585->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + return 0; + +error_media_entity: + media_entity_cleanup(&imx585->sd.entity); + +error_handler_free: + imx585_free_controls(imx585); + +error_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_power_off: + imx585_power_off(&client->dev); + + return ret; +} + +static void imx585_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd =3D i2c_get_clientdata(client); + struct imx585 *imx585 =3D to_imx585(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx585_free_controls(imx585); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx585_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +MODULE_DEVICE_TABLE(of, imx585_dt_ids); + +static const struct dev_pm_ops imx585_pm_ops =3D { + SET_SYSTEM_SLEEP_PM_OPS(imx585_suspend, imx585_resume) + SET_RUNTIME_PM_OPS(imx585_power_off, imx585_power_on, NULL) +}; + +static struct i2c_driver imx585_i2c_driver =3D { + .driver =3D { + .name =3D "imx585", + .of_match_table =3D imx585_dt_ids, + .pm =3D &imx585_pm_ops, + }, + .probe =3D imx585_probe, + .remove =3D imx585_remove, +}; + +module_i2c_driver(imx585_i2c_driver); + +MODULE_AUTHOR("Will Whang "); +MODULE_AUTHOR("Tetsuya NOMURA "); +MODULE_DESCRIPTION("Sony imx585 sensor driver"); +MODULE_LICENSE("GPL"); --=20 2.39.5 From nobody Wed Oct 8 04:19:41 2025 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D78962236F3; Wed, 2 Jul 2025 06:38:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438340; cv=none; b=fb/Yg1kSsgWNoG63O1AP+IN5NG9OvzvPW7nKK3g351lNpH63fOUJCFVXuiu5yreDZxYI+zglQ52xjzS3vf1njQVEZuhRP7WbwcTEXBz8FSvX38HIXzcjkr6k6Ugrk4FRma/NrrIUYVFNCM2H6Sk9W9An+Fk7uN4oK6oPcx0IH8U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751438340; c=relaxed/simple; bh=pL0ZMotY71CP45hBo02QTexa6+MqXmOEhp0hVHal67Q=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=UmkDrpLCBJlsn8A9fEyykp4oC5pVP4zB/AqedU/5/ftHr63pwDcUVkpsVCdNVXKrSITs8dB86dHbvYXGZ5Bug9LjQXD+qcXv+GVFVtWpD38PQ5Okd1v6ELEmFnE2k/ved7oz1056rj+cOMftobgWNeJRx5D6LzaitrWm8uAOLI0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.210.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=willwhang.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-pf1-f175.google.com with SMTP id d2e1a72fcca58-7426c44e014so6588697b3a.3; Tue, 01 Jul 2025 23:38:57 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751438337; x=1752043137; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TXwezkP7c8pGscSKlBvamtKez7/0qfT6TtR30gL2C34=; b=TrbcNwrKPqY+lklj25d9czk2tXL4DS9mj6/cRp4ZbGgH3pyMpKGS+yuSs4/QWNjyG3 1j5JNLQ3ikY3IwrpydRPGj+2dWM9g61Rnmi2r7ztjL1cnWYLjtfGbIl5NIAt0EUbqnIM 2lRuNccXizYXNlNiAtk6WSeGa1+zqQiPMPjqk/1PmgNW4bP0onUR04lN3Upsil83vzu3 9A0zYBENO5oFlYKW3560FFFkTixjoHiaXbr/jlD7bHl6WuzJqKUubeF1ldCbnceL6Qpl KF6PXCepNTytx4X7IBEsPsHv8uSscYAcgz1vdj9CerIq13Z7ba8+6jA9lMPxomKK1x/M DI7A== X-Forwarded-Encrypted: i=1; AJvYcCXP6znjVQwd9ePWZ0I/0pZPp7uVbT6MjGSSfkP7KEI7VssDWRGFE1TOSx1R84X4QDzphjbUe1g3jQa61T4=@vger.kernel.org, AJvYcCXXshEbibhaKk3lyZSfyvolaLd+zq1aB3uk3ZM9QTdhtQ4lzHfqMraFiJwZZrKMQ2LOAfe6qq/j4VTvUFN5@vger.kernel.org, AJvYcCXatJmz6ndIr8G+QQF8QvfCyjsLp3pYVYe3uHk8uOfP4X85PA4ErzTG4CvcqCyqhnWbAyOmH0QfKdKw@vger.kernel.org X-Gm-Message-State: AOJu0Yz0HuXnaVph3JnXcV0wtWpvAbnVWSfPUJsqVUx2mC41LGl9HsKA lf9jeOoNXCQhq3YOp4dVFDI8r9Ec21jZ4Zsyfec5k6Uhs0M0+cxQ5S2k X-Gm-Gg: ASbGncvgVYBmXbpLux6n4fcgv+I17UovONpKxAuoHeMcSD253avFoDSECAcBFHwZriV rW3psI9uz1JxGWGaXwJAYY8yrfV6BGsVFLHG64wS1MG6MTYRHYWZt4OnY3njHC8/QiQZqn+asQp ObFZV7kMMYnY8H5O0dLnGkGM2Mpvd7Iv0JR0I0W0onjcOkPthuQlalqi4ADtB7kvzA3e5env3Bn P3M9CXxVNPIMg5XpNsAIIu8Py3ho15FFrVohu9Ykki4l4A9sFp1E+EPi6p4eR9CLQHJ+aX9rUjr brBFV5Yvoutom3K9DwUWJEfakDNBYaZCZ1c2X9670LkNm+smIl7LDGtibfsYzSUswc24WM11OQm EHbdeqdgTnPBOnEfcNPcnjQ8IDMazXw== X-Google-Smtp-Source: AGHT+IEuZYwGM2AS0Kzh5zxNMXbQXDcl00Lu716BusQDkwurQQA7D5GDryC+Vasxb2gvxpSwIi3/6Q== X-Received: by 2002:a05:6a00:1253:b0:736:5e28:cfba with SMTP id d2e1a72fcca58-74b50f54180mr2680527b3a.18.1751438337114; Tue, 01 Jul 2025 23:38:57 -0700 (PDT) Received: from localhost.localdomain (c-24-4-34-163.hsd1.ca.comcast.net. [24.4.34.163]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-74af541bd0fsm13044966b3a.40.2025.07.01.23.38.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Jul 2025 23:38:55 -0700 (PDT) From: Will Whang To: Will Whang , Sakari Ailus , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , linux-media@vger.kernel.org (open list:SONY IMX585 SENSOR DRIVER), linux-kernel@vger.kernel.org (open list), devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS) Subject: [PATCH v1 4/4] media: docs: Add userspace-API guide for the IMX585 driver Date: Wed, 2 Jul 2025 07:38:36 +0100 Message-Id: <20250702063836.3984-5-will@willwhang.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250702063836.3984-1-will@willwhang.com> References: <20250702063836.3984-1-will@willwhang.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The new IMX585 V4L2 sub-device driver introduces several driver-specific controls for configuring Clear-HDR blending, gradation compression thresholds, and HCG enabling. This patch adds an rst document under Documentation/userspace-api/media/drivers/ that details each control, allowed values, and their effect Signed-off-by: Will Whang --- .../userspace-api/media/drivers/imx585.rst | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/userspace-api/media/drivers/imx585.rst diff --git a/Documentation/userspace-api/media/drivers/imx585.rst b/Documen= tation/userspace-api/media/drivers/imx585.rst new file mode 100644 index 000000000..bb08afa93 --- /dev/null +++ b/Documentation/userspace-api/media/drivers/imx585.rst @@ -0,0 +1,95 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Sony IMX585 driver +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The IMX585 image-sensor driver provides the following *driver-specific* +V4L2 controls. They are visible only when the IMX585 driver is loaded +and sit in the sensor-private control class. + +HDR data blending +----------------- + +``V4L2_CID_IMX585_HDR_DATASEL_TH`` (``U16[2]``) + Lower/upper **thresholds** (0 =E2=80=93 4095) that decide which exposu= re is + chosen=E2=80=94or blended=E2=80=94for each pixel in Clear-HDR mode. + +``V4L2_CID_IMX585_HDR_DATASEL_BK`` (menu) + **Blending ratio** between the long-gain (LG) and + high-gain (HG) read-outs. + + .. flat-table:: + :stub-columns: 0 + :widths: 1 5 + + * - ``0`` - HG =C2=BD, LG =C2=BD + * - ``1`` - HG =C2=BE, LG =C2=BC + * - ``2`` - HG =C2=BD, LG =C2=BD *(duplicate ratio present in the + datasheet)* + * - ``3`` - HG =E2=85=9E, LG =E2=85=9B + * - ``4`` - HG 15=E2=81=8416, LG 1=E2=81=8416 + * - ``5`` - **2=E2=81=BF=E1=B5=88** HG =C2=BD, LG =C2=BD *(secon= d 50 %-50 % entry as + documented)* + * - ``6`` - HG 1=E2=81=8416, LG 15=E2=81=8416 + * - ``7`` - HG =E2=85=9B, LG =E2=85=9E + * - ``8`` - HG =C2=BC, LG =C2=BE + +Gradation compression +--------------------- + +``V4L2_CID_IMX585_HDR_GRAD_TH`` (``U32[2]``) + 17-bit **break-points** (0 =E2=80=93 0x1ffff) that shape the 16-bit + gradation-compression curve. + +``V4L2_CID_IMX585_HDR_GRAD_COMP_L`` (menu) +``V4L2_CID_IMX585_HDR_GRAD_COMP_H`` (menu) + **Compression ratios** below the first break-point and between the + two break-points, respectively. + + .. flat-table:: + :stub-columns: 0 + :widths: 1 4 + + * - ``0`` - 1 : 1 + * - ``1`` - 1 : 2 + * - ``2`` - 1 : 4 *(default for COMP_L)* + * - ``3`` - 1 : 8 + * - ``4`` - 1 : 16 + * - ``5`` - 1 : 32 + * - ``6`` - 1 : 64 *(default for COMP_H)* + * - ``7`` - 1 : 128 + * - ``8`` - 1 : 256 + * - ``9`` - 1 : 512 + * - ``10`` - 1 : 1024 + * - ``11`` - 1 : 2048 + +Gain settings +------------- + +``V4L2_CID_IMX585_HDR_GAIN`` (menu) + **Additional gain** (in dB) applied to the high-gain path when + Clear-HDR is active. + + .. flat-table:: + :stub-columns: 0 + :widths: 1 3 + + * - ``0`` - +0 dB + * - ``1`` - +6 dB + * - ``2`` - +12 dB *(default)* + * - ``3`` - +18 dB + * - ``4`` - +24 dB + * - ``5`` - +29.1 dB + +``V4L2_CID_IMX585_HCG_GAIN`` (boolean) + Toggle **High-Conversion-Gain** mode. + + *0 =3D LCG (default), 1 =3D HCG.* + +Notes +----- + +* Controls are writable while streaming; changes take effect from the + next frame. +* HDR-specific controls are hidden when HDR is disabled. +* Inter-control dependencies are enforced by the driver. --=20 2.39.5