From nobody Sat Jun 13 04:48:46 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0BC032D5925; Sun, 10 May 2026 18:09:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778436592; cv=none; b=rphKMjuZWttKoiUU9IHSwbH7XtF7QHhzZi/C1IoCQ/swzXnq9fy66jMlwos4O7YFdZR6j04O+Xlxk/W+LbPYrMblJ3bqkbZuIootUeK6klpcAnBoB5Sy0KvJm3qnZX03yvnO7bHyiUPJVwqNWdjj6/AGNGJsPtrXt8qimHMM3tw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778436592; c=relaxed/simple; bh=p918NNAfKESHUaESEupz7gVBnaccniWtXudL4dFa/Bk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sWmLZWpTrE4FxwSeWoV9TyrvlVPUMICFjWlaDAe4OWbsrg4y30hVc/qxtKsVEIWObthbBoRWsVTbaNtavCjgHu3CA5Fiuz4SccJRqJkZoh42T7ax62aNeuxEX4EZoG0z7Wr1zRvmB6Pr8sloNuXNiELRscoSnS3PYHXRTgdYao0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uWB0QG/r; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="uWB0QG/r" Received: by smtp.kernel.org (Postfix) with ESMTPS id B0FF5C2BCF7; Sun, 10 May 2026 18:09:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1778436591; bh=p918NNAfKESHUaESEupz7gVBnaccniWtXudL4dFa/Bk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=uWB0QG/rOz9wOPr9DIP/Jm3hs0ZzgvpNcAlOGm3rStwBdIYQKWndScS66XW1HP/Mj 38IzKjxx2SRd0uhTcdMFVrdSx3aiymhgwAJm6TKs1jREbKOtpixvQtTLhG8m4AX5Kg fptGoKkNzD+R1Zn2GIe+fEnj+INRSFp5xN6niixobWBPABjAHhjY3ZTyu75a9SbAYG m2m75t6+whiHfqjQLU2o6ViaJz+mGtVhxMbQJU0Sn7L74APzhr/zsCJG19JtJezGjK P8U2dvCqMsTY4NyUH2Rlso4C3/6/15yX3Sx8xspOnEqUwVl0ZnlpeGFGsfLmblJOjE Xg09UHYPqP+4Q== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A0E05CD3427; Sun, 10 May 2026 18:09:51 +0000 (UTC) From: Alexandre Hamamdjian via B4 Relay Date: Mon, 11 May 2026 01:09:49 +0700 Subject: [PATCH 1/2] dt-bindings: iio: light: Add ROHM BH1730FVC binding 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 Message-Id: <20260511-bh1730-v1-1-e0df1f499135@gmail.com> References: <20260511-bh1730-v1-0-e0df1f499135@gmail.com> In-Reply-To: <20260511-bh1730-v1-0-e0df1f499135@gmail.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , CTCaer Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Hamamdjian X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778436589; l=3793; i=azkali.limited@gmail.com; s=20260510; h=from:subject:message-id; bh=jQHKR47Y8URKhOfFc/PxeUwRtzxZuqi9u6qUMfwgT9g=; b=ObpFPEaz1gMJZrJKNnAZL+40c304k1+WO2lqMgrq91nU3A5nKTrMr0nYckD7+iHo7qyNij7ZN Y5dEaGJwKdxDczrGylqUt+yNwxh5kvsPLKgpW7qXwFETXJPQ9AlRB3T X-Developer-Key: i=azkali.limited@gmail.com; a=ed25519; pk=I0Z0IdCdQJqNGX+FQUnXhrHg950u3cM6Xzz3YT6JOyQ= X-Endpoint-Received: by B4 Relay for azkali.limited@gmail.com/20260510 with auth_id=774 X-Original-From: Alexandre Hamamdjian Reply-To: azkali.limited@gmail.com From: CTCaer Add a YAML binding for the ROHM BH1730FVC ambient light sensor. Documents the required compatible string, the als-vdd/als-vid regulators, and the rohm,integration-cycle, rohm,lux-multiplier, rohm,opt-win-coeff and rohm,gain-coeff calibration properties consumed by the driver. Signed-off-by: CTCaer Signed-off-by: Alexandre Hamamdjian --- .../bindings/iio/light/rohm,bh1730fvc.yaml | 95 ++++++++++++++++++= ++++ 1 file changed, 95 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bh1730fvc.yam= l b/Documentation/devicetree/bindings/iio/light/rohm,bh1730fvc.yaml new file mode 100644 index 000000000000..6273b69e82ab --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/rohm,bh1730fvc.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/rohm,bh1730fvc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BH1730FVC ambient light sensor + +maintainers: + - CTCaer + +description: + Digital 16-bit ambient light sensor with an I2C interface. The device has + two photodiodes (visible and infrared) and supports four gain settings a= nd + programmable integration time. + +properties: + compatible: + const: rohm,bh1730fvc + + reg: + maxItems: 1 + + als-vdd-supply: + description: Regulator for the analog/digital supply (VDD). + + als-vid-supply: + description: Regulator for the LED indicator supply (VID). + + rohm,integration-cycle: + description: + Number of internal clock cycles used for the ADC integration time. + Used together with rohm,lux-multiplier to calibrate the lux output. + $ref: /schemas/types.yaml#/definitions/uint32 + + rohm,lux-multiplier: + description: + Lux scaling multiplier applied after integration. Used together with + rohm,integration-cycle to calibrate the lux output. + $ref: /schemas/types.yaml#/definitions/uint32 + + rohm,opt-win-coeff: + description: + Optical-window calibration coefficients. Specified as a flat list of + triplets , one triplet per window region, where rc is the + visible/IR ratio cutoff and cv/ci are the visible and IR weighting + factors used in that region. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + minItems: 3 + maxItems: 3 + + rohm,gain-coeff: + description: + Per-gain sensitivity coefficients. Eight u32 values arranged as four + pairs, one pair for each supported gain (1x, 2x, 64x, 128x). + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 8 + maxItems: 8 + +required: + - compatible + - reg + +dependencies: + rohm,integration-cycle: ['rohm,lux-multiplier'] + rohm,lux-multiplier: ['rohm,integration-cycle'] + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + light-sensor@29 { + compatible =3D "rohm,bh1730fvc"; + reg =3D <0x29>; + als-vdd-supply =3D <&vdd_als>; + als-vid-supply =3D <&vid_als>; + rohm,integration-cycle =3D <38>; + rohm,lux-multiplier =3D <1000>; + rohm,opt-win-coeff =3D <260 1290 2733>, + <550 795 859>, + <1090 510 345>, + <2130 276 130>; + rohm,gain-coeff =3D <3000 0xffffffff + 2000 9800 + 15 60000 + 0 1300>; + }; + }; + +... --=20 2.54.0 From nobody Sat Jun 13 04:48:46 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0BB732C08DC; Sun, 10 May 2026 18:09:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778436592; cv=none; b=Y8Nyzo/kw304KjSakhhFqJtnJwZkt8XFn011OvtaWkAydpTr184ckHz5KdPq/eWv/xZSb8NWi3LFAlx7BXdKykco4LpLqhdI9lU3/vEKF3Gl1QrY1VTSKc9GAhHZhsft9bza2oU8OE8KslQOxa/ssTgQb8TLl/NOdAITwpW/k6Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778436592; c=relaxed/simple; bh=fJy1niSp4bSAe0oB5nnVrHI4pgagN+d+CDStZQ5N200=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ummIc+fIC0QnqyXJQuTEmN0k5bYQxBmMQxHR1KgQCyo1CXOg1zXfJoWHsJKEbQASlTZ115WAMIr0uJbLsoyGEv/L/sAxtcCM2H0Wb5aq9yaadnV0fzmMEbq0yhel4LERXVWiprzCmew1nM60sSQoTeD2e+77BmEE9jAJHGO+3MI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VcHR0HjQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="VcHR0HjQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id C4043C2BCC7; Sun, 10 May 2026 18:09:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1778436591; bh=fJy1niSp4bSAe0oB5nnVrHI4pgagN+d+CDStZQ5N200=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=VcHR0HjQ5liTZ1d8JS2dlJ2YH2/1zrL0MHDXsLGow1poZmYlq82V1gcdido7WJ0mc Ni5a8RaR3OK27Qvt+7VzghQoFfZM/8zrNuXugsXE64T7ZA+Txkp2q+n86kaR4g/zzT bcDOram10IW158/9xHQNqB6GO9+gusb3E/g0hqL4olckt6OtKtZ61QbVPZs0QohW9s BtT22kFssVYI5n05Y8BYqN/iF19Vf2Li1L9INelXv1HPkGwr7u9KtmCMnIwoC8zn83 9S2xcBPzwjZMnLqdwEBuVYuo9LM2Mp6jsfiPa5iJCSv0aVuVOtJ4Edx4QFgrsiwL+g 7hOboHaeyAaxA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2C85CD37B2; Sun, 10 May 2026 18:09:51 +0000 (UTC) From: Alexandre Hamamdjian via B4 Relay Date: Mon, 11 May 2026 01:09:50 +0700 Subject: [PATCH 2/2] iio: light: bh1730: Add bh1730 light sensor driver 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 Message-Id: <20260511-bh1730-v1-2-e0df1f499135@gmail.com> References: <20260511-bh1730-v1-0-e0df1f499135@gmail.com> In-Reply-To: <20260511-bh1730-v1-0-e0df1f499135@gmail.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , CTCaer Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Hamamdjian X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778436589; l=19034; i=azkali.limited@gmail.com; s=20260510; h=from:subject:message-id; bh=ksZX5QAXT+duO+t5tIB8ie6HGoEo6QtEYkuCEUIi9NU=; b=gU01r6TMHv7mI+yTXA7pjT5HojTmv3H5NAAHRP2QLf+ajepGv7srw/9zCbACMgEHFXb+tyzo9 mllnAOFc6yOCStmXYQ1uTrD9laYzN/V5U1aH2AOR+8zTLyTY7Jymv/W X-Developer-Key: i=azkali.limited@gmail.com; a=ed25519; pk=I0Z0IdCdQJqNGX+FQUnXhrHg950u3cM6Xzz3YT6JOyQ= X-Endpoint-Received: by B4 Relay for azkali.limited@gmail.com/20260510 with auth_id=774 X-Original-From: Alexandre Hamamdjian Reply-To: azkali.limited@gmail.com From: CTCaer Add a driver for the ROHM BH1730FVC ambient light sensor. The device is a 16-bit I2C digital sensor with separate visible and infrared photodiodes, four selectable gains (1x/2x/64x/128x) and a programmable integration time. The driver exposes illuminance via IIO, performs runtime gain and integration-time tracking to keep the ADC in range, and supports optional als-vdd / als-vid regulators. Per-board lux calibration data (integration cycles, lux multiplier, optical-window coefficients, and gain sensitivity coefficients) can be supplied via device tree; sensible defaults are used otherwise. Signed-off-by: CTCaer Signed-off-by: Alexandre Hamamdjian --- drivers/iio/light/Kconfig | 9 + drivers/iio/light/Makefile | 1 + drivers/iio/light/bh1730.c | 686 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 696 insertions(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index eff33e456c70..3869060567dd 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -135,6 +135,15 @@ config AS73211 This driver can also be built as a module. If so, the module will be called as73211. =20 +config BH1730 + tristate "ROHM BH1730 ambient light sensor" + depends on I2C + help + Say Y here to build support for the ROHM BH1730 ambient light sensor. + + To compile this driver as a module, choose M here: the module will + be called bh1730. + config BH1745 tristate "ROHM BH1745 colour sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c0048e0d5ca8..27ef7906a02a 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_APDS9300) +=3D apds9300.o obj-$(CONFIG_APDS9306) +=3D apds9306.o obj-$(CONFIG_APDS9960) +=3D apds9960.o obj-$(CONFIG_AS73211) +=3D as73211.o +obj-$(CONFIG_BH1730) +=3D bh1730.o obj-$(CONFIG_BH1745) +=3D bh1745.o obj-$(CONFIG_BH1750) +=3D bh1750.o obj-$(CONFIG_BH1780) +=3D bh1780.o diff --git a/drivers/iio/light/bh1730.c b/drivers/iio/light/bh1730.c new file mode 100644 index 000000000000..c93290ff5661 --- /dev/null +++ b/drivers/iio/light/bh1730.c @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ROHM BH1730 ambient light sensor driver + * + * Copyright (c) 2021 CTCaer + * + * Based on previous iio BH1730FVC drivers from: + * Copyright (c) 2018 Google, Inc. + * Author: Pierre Bourdon + * + * Copyright (C) 2012 Samsung Electronics. All rights reserved. + * Author: Won Huh + * + * Data sheets: + * http://www.rohm.com/web/global/datasheet/BH1730FVC/bh1730fvc-e + */ + +#include +#include +#include +#include +#include +#include +#include + +#define BH1730_NAME "bh1730fvc" + +#define BH1730_CMD_BIT BIT(7) + +#define BH1730_REG_CONTROL 0x00 +#define BH1730_REG_TIMING 0x01 +#define BH1730_REG_INTERRUPT 0x02 +#define BH1730_REG_THLLOW 0x03 +#define BH1730_REG_THLHIGH 0x04 +#define BH1730_REG_THHLOW 0x05 +#define BH1730_REG_THHHIGH 0x06 +#define BH1730_REG_GAIN 0x07 +#define BH1730_REG_ID 0x12 +#define BH1730_REG_DATA0LOW 0x14 +#define BH1730_REG_DATA0HIGH 0x15 +#define BH1730_REG_DATA1LOW 0x16 +#define BH1730_REG_DATA1HIGH 0x17 + +#define BH1730_CONTROL_POWER BIT(0) +#define BH1730_CONTROL_ADC_EN BIT(1) +#define BH1730_CONTROL_DATA0_ONLY BIT(2) +#define BH1730_CONTROL_ONE_TIME BIT(3) +#define BH1730_CONTROL_ADC_VALID BIT(4) +#define BH1730_CONTROL_INTR BIT(5) + +#define BH1730_INTERNAL_CLOCK_NS 2800 +/* BH1730_INTERNAL_CLOCK_MS * 714 */ +#define BH1730_ADC_CALC_DELAY_US 2000 +/* BH1730_INTERNAL_CLOCK_MS * 964 */ +#define BH1730_ITIME_TO_US 2700 + +#define BH1730_DEFAULT_INTEG_CYCLE 38 +#define BH1730_DEFAULT_ITIME_MS 100 + +#define BH1730_POWER_ON_DELAY_US 10000 + +#define BH1730_MAX_MEASURED_LUX 100000 + +enum bh1730_gain { + BH1730_GAIN_1X =3D 0, + BH1730_GAIN_2X, + BH1730_GAIN_64X, + BH1730_GAIN_128X, +}; +#define BH1730_MAX_GAIN_MULTIPLIER 128 + +struct gain_coeff_t { + u32 cl; + u32 fl; +}; + +struct opt_win_coeff_t { + u32 rc; + u32 cv; + u32 ci; +}; + +struct lux_cal_data_t { + struct gain_coeff_t *gain_coeff; + struct opt_win_coeff_t *opt_win_coeff; + u32 opt_win_coeff_count; + u32 itime_cycle; + u32 mul; +}; + +/* + * No optical window or optical window that has flat transmittance + * from visible light to infrared light. + */ +static struct opt_win_coeff_t def_lux_coeff[] =3D { + { 260, 1290, 2733 }, + { 550, 795, 859 }, + { 1090, 510, 345 }, + { 2130, 276, 130 } +}; + +static struct gain_coeff_t def_gain_coeff[] =3D { + { 3000, -1 }, /* 1x */ + { 2000, 9800 }, /* 2x */ + { 15, 60000 }, /* 64x */ + { 0, 1300 } /* 128x */ +}; + +static struct lux_cal_data_t def_lux_data =3D { + .gain_coeff =3D def_gain_coeff, + .opt_win_coeff =3D def_lux_coeff, + .opt_win_coeff_count =3D ARRAY_SIZE(def_lux_coeff), + .itime_cycle =3D BH1730_DEFAULT_INTEG_CYCLE, + .mul =3D 1000 +}; + +struct bh1730_data { + struct i2c_client *client; + struct lux_cal_data_t cal; + struct regulator *reg_vdd; + struct regulator *reg_vid; + enum bh1730_gain gain; + u32 itime_us; + u32 tmt_us; +}; + +static int bh1730_read_word(struct bh1730_data *bh1730, u8 reg) +{ + int ret =3D i2c_smbus_read_word_data(bh1730->client, + BH1730_CMD_BIT | reg); + if (ret < 0) + dev_err(&bh1730->client->dev, + "i2c read failed error %d, register %01x\n", + ret, reg); + + return ret; +} + +static int bh1730_write(struct bh1730_data *bh1730, u8 reg, u8 val) +{ + int ret =3D i2c_smbus_write_byte_data(bh1730->client, + BH1730_CMD_BIT | reg, + val); + if (ret < 0) + dev_err(&bh1730->client->dev, + "i2c write failed error %d, register %01x\n", + ret, reg); + + return ret; +} + +static int bh1730_gain_multiplier(struct bh1730_data *bh1730) +{ + int multiplier; + + switch (bh1730->gain) { + case BH1730_GAIN_1X: + multiplier =3D 1; + break; + case BH1730_GAIN_2X: + multiplier =3D 2; + break; + case BH1730_GAIN_64X: + multiplier =3D 64; + break; + case BH1730_GAIN_128X: + multiplier =3D 128; + break; + default: + multiplier =3D -EINVAL; + break; + } + + if (multiplier < 0) { + dev_warn(&bh1730->client->dev, + "invalid gain multiplier settings: %d\n", + bh1730->gain); + bh1730->gain =3D BH1730_GAIN_1X; + multiplier =3D 1; + } + + return multiplier; +} + +static int bh1730_set_gain(struct bh1730_data *bh1730, enum bh1730_gain ga= in) +{ + int ret =3D bh1730_write(bh1730, BH1730_REG_GAIN, gain); + + if (ret < 0) + return ret; + + bh1730->gain =3D gain; + + return 0; +} + +static int bh1730_itime_us(struct bh1730_data *bh1730, int cycle) +{ + return (BH1730_ITIME_TO_US * cycle); +} + + +static int bh1730_set_integration_time_cycle(struct bh1730_data *bh1730, + u32 cycle) +{ + int ret, itime; + + itime =3D 256 - cycle; + + /* ITIME =3D=3D 0 is reserved for manual integration mode. */ + if (itime <=3D 0 || itime > 255) { + dev_warn(&bh1730->client->dev, + "integration time out of range: %d cycles\n", + cycle); + + return -ERANGE; + } + + ret =3D bh1730_write(bh1730, BH1730_REG_TIMING, itime); + if (ret < 0) + return ret; + + bh1730->itime_us =3D bh1730_itime_us(bh1730, cycle); + bh1730->tmt_us =3D bh1730->itime_us + BH1730_ADC_CALC_DELAY_US; + + return 0; +} + +static int bh1730_adjust_gain(struct bh1730_data *bh1730) +{ + int visible, ir, highest, gain, ret; + + visible =3D bh1730_read_word(bh1730, BH1730_REG_DATA0LOW); + if (visible < 0) + return visible; + + ir =3D bh1730_read_word(bh1730, BH1730_REG_DATA1LOW); + if (ir < 0) + return ir; + + highest =3D max(visible, ir); + + /* Adjust gain based on sensitivity calibration */ + gain =3D bh1730->gain; + if (highest > bh1730->cal.gain_coeff[gain].fl && + gain !=3D BH1730_GAIN_1X) { + gain--; /* Decrease sensitivity */ + } else if (highest < bh1730->cal.gain_coeff[gain].cl && + gain !=3D BH1730_GAIN_128X) { + gain++; /* Increase sensitivity */ + } + + /* Clamp to proper gain values */ + if (gain < 0) + gain =3D BH1730_GAIN_1X; + else if (gain > BH1730_GAIN_128X) + gain =3D BH1730_GAIN_128X; + + if (gain !=3D bh1730->gain) { + ret =3D bh1730_set_gain(bh1730, gain); + if (ret < 0) + return ret; + + usleep_range(bh1730->tmt_us, bh1730->tmt_us + 1000); + } + + return 0; +} + +static int bh1730_get_lux(struct bh1730_data *bh1730) +{ + int i, visible, ir; + struct opt_win_coeff_t *opt_win_coeff; + u64 lux =3D 0; + + visible =3D bh1730_read_word(bh1730, BH1730_REG_DATA0LOW); + + /* If visible is 0, skip calculations */ + if (visible <=3D 0) + return visible; + + ir =3D bh1730_read_word(bh1730, BH1730_REG_DATA1LOW); + if (ir < 0) + return ir; + + /* Calibrate based on optical window */ + for (i =3D 0; i < bh1730->cal.opt_win_coeff_count; i++) { + opt_win_coeff =3D &bh1730->cal.opt_win_coeff[i]; + if (1000 * ir / visible < opt_win_coeff->rc) { + lux =3D ((u64)opt_win_coeff->cv * visible) - + (opt_win_coeff->ci * ir); + break; + } + } + + /* Calculate lux */ + lux *=3D BH1730_DEFAULT_ITIME_MS; + lux /=3D bh1730_gain_multiplier(bh1730) * bh1730->itime_us; + lux =3D (lux * bh1730->cal.mul) / 1000; + + if (lux > BH1730_MAX_MEASURED_LUX) + lux =3D BH1730_MAX_MEASURED_LUX; + + return (int)lux; +} + +static int bh1730_power_ctrl(struct bh1730_data *bh1730, bool enable) +{ + struct device *dev =3D &bh1730->client->dev; + static bool enabled; + int ret =3D 0; + + if (enabled =3D=3D enable) + return ret; + + if (enable) { + if (!IS_ERR_OR_NULL(bh1730->reg_vdd)) { + ret =3D regulator_enable(bh1730->reg_vdd); + if (ret) { + dev_err(dev, "%s: Failed to enable vdd: %d\n", + __func__, ret); + return ret; + } + } + + if (!IS_ERR_OR_NULL(bh1730->reg_vid)) { + ret =3D regulator_enable(bh1730->reg_vid); + if (ret) { + dev_err(dev, "%s: Failed to enable vid: %d\n", + __func__, ret); + return ret; + } + } + + usleep_range(BH1730_POWER_ON_DELAY_US, BH1730_POWER_ON_DELAY_US + 1000); + } else { + if (!IS_ERR_OR_NULL(bh1730->reg_vdd)) { + ret =3D regulator_disable(bh1730->reg_vdd); + if (ret) { + dev_err(dev, "%s: Failed to disable vdd: %d\n", + __func__, ret); + return ret; + } + } + + if (!IS_ERR_OR_NULL(bh1730->reg_vid)) { + ret =3D regulator_disable(bh1730->reg_vid); + if (ret) { + dev_err(dev, "%s: Failed to disable vid: %d\n", + __func__, ret); + return ret; + } + } + } + + enabled =3D enable; + + return ret; +} + +static int bh1730_power_on(struct bh1730_data *bh1730) +{ + int ret =3D bh1730_power_ctrl(bh1730, true); + + if (ret < 0) + return ret; + + return bh1730_write(bh1730, BH1730_REG_CONTROL, + BH1730_CONTROL_POWER | BH1730_CONTROL_ADC_EN); +} + +static int bh1730_init_config(struct bh1730_data *bh1730) +{ + int ret; + + ret =3D bh1730_set_gain(bh1730, BH1730_GAIN_64X); + if (ret < 0) + return ret; + + return bh1730_set_integration_time_cycle(bh1730, + bh1730->cal.itime_cycle); +} + +static int bh1730_power_off(struct bh1730_data *bh1730) +{ + int ret =3D bh1730_write(bh1730, BH1730_REG_CONTROL, 0); + + if (ret < 0) + return ret; + + return bh1730_power_ctrl(bh1730, false); +} + +static int bh1730_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bh1730_data *bh1730 =3D iio_priv(indio_dev); + int data_reg, ret; + + ret =3D bh1730_adjust_gain(bh1730); + if (ret < 0) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret =3D bh1730_get_lux(bh1730); + if (ret < 0) + return ret; + *val =3D ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + switch (chan->channel2) { + case IIO_MOD_LIGHT_CLEAR: + data_reg =3D BH1730_REG_DATA0LOW; + break; + case IIO_MOD_LIGHT_IR: + data_reg =3D BH1730_REG_DATA1LOW; + break; + default: + return -EINVAL; + } + ret =3D bh1730_read_word(bh1730, data_reg); + if (ret < 0) + return ret; + ret =3D ret * 1000 / bh1730_gain_multiplier(bh1730); + *val =3D ret / 1000; + *val2 =3D (ret % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_INT_TIME: + *val =3D 0; + *val2 =3D bh1730->itime_us; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int bh1730_parse_dt(struct bh1730_data *bh1730, struct device_node = *dn) +{ + struct device *dev =3D &bh1730->client->dev; + int ret; + u32 *opt_win_coeff =3D NULL; + u32 *gain_coeff =3D NULL; + int opt_win_coeff_count =3D 0, gain_coeff_count =3D 0, cycle =3D 0, mul = =3D 0; + + memcpy(&bh1730->cal, &def_lux_data, sizeof(struct lux_cal_data_t)); + + if (dn) { + /* Get regulators */ + bh1730->reg_vdd =3D regulator_get(dev, "als-vdd"); + if (IS_ERR_OR_NULL(bh1730->reg_vdd)) { + bh1730->reg_vdd =3D NULL; + dev_warn(dev, "failed to get als-vdd"); + } + + bh1730->reg_vid =3D regulator_get(dev, "als-vid"); + if (IS_ERR_OR_NULL(bh1730->reg_vid)) { + bh1730->reg_vid =3D NULL; + dev_warn(dev, "failed to get als-vid"); + } + + /* Get calibration */ + ret =3D of_property_read_u32(dn, + "rohm,integration-cycle", &cycle); + if (ret < 0) + goto out; + + ret =3D of_property_read_u32(dn, + "rohm,lux-multiplier", &mul); + if (ret < 0) + goto out; + + if (cycle =3D=3D 0 || mul =3D=3D 0) + goto out; + + /* Get optical window coefficients */ + opt_win_coeff_count =3D of_property_count_elems_of_size(dn, + "rohm,opt-win-coeff", sizeof(u32)); + if (opt_win_coeff_count > 0) { + opt_win_coeff =3D devm_kzalloc(dev, + sizeof(u32) * opt_win_coeff_count, + GFP_KERNEL); + if (!opt_win_coeff) { + dev_err(dev, "failed to allocate mem for opt_win_coeff"); + return -ENOMEM; + } + + ret =3D of_property_read_u32_array(dn, "rohm,opt-win-coeff", + opt_win_coeff, opt_win_coeff_count); + + if (ret) { + devm_kfree(dev, opt_win_coeff); + goto out; + } + } + + /* Get gain sensitivity coefficients */ + gain_coeff_count =3D of_property_count_elems_of_size(dn, + "rohm,gain-coeff", sizeof(u32)); + + if (gain_coeff_count =3D=3D 8) { /* 2 for each gain supported */ + gain_coeff =3D devm_kzalloc(dev, + sizeof(u32) * gain_coeff_count, + GFP_KERNEL); + if (!gain_coeff) { + dev_err(dev, "failed to allocate mem for gain_coeff"); + return -ENOMEM; + } + + ret =3D of_property_read_u32_array(dn, "rohm,gain-coeff", + gain_coeff, gain_coeff_count); + if (ret) { + if (opt_win_coeff) + devm_kfree(dev, opt_win_coeff); + devm_kfree(dev, gain_coeff); + goto out; + } + } + + if (opt_win_coeff) { + bh1730->cal.opt_win_coeff =3D + (struct opt_win_coeff_t *)opt_win_coeff; + bh1730->cal.opt_win_coeff_count =3D + opt_win_coeff_count / + (sizeof(struct opt_win_coeff_t) / + sizeof(u32)); + } + + if (gain_coeff) + bh1730->cal.gain_coeff =3D (struct gain_coeff_t *)gain_coeff; + + bh1730->cal.itime_cycle =3D cycle; + bh1730->cal.mul =3D mul; + + return 0; + } + +out: + dev_info(&bh1730->client->dev, "using default calibration"); + + return 0; +} + +static const struct iio_info bh1730_info =3D { + .read_raw =3D bh1730_read_raw, +}; + +static const struct iio_chan_spec bh1730_channels[] =3D { + { + .type =3D IIO_LIGHT, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_INT_TIME), + }, + { + .type =3D IIO_INTENSITY, + .modified =3D 1, + .channel2 =3D IIO_MOD_LIGHT_CLEAR, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW), + }, + { + .type =3D IIO_INTENSITY, + .modified =3D 1, + .channel2 =3D IIO_MOD_LIGHT_IR, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW), + }, +}; + +static int bh1730_probe(struct i2c_client *client) +{ + struct bh1730_data *bh1730; + struct i2c_adapter *adapter =3D to_i2c_adapter(client->dev.parent); + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*bh1730)); + if (!indio_dev) + return -ENOMEM; + + bh1730 =3D iio_priv(indio_dev); + bh1730->client =3D client; + i2c_set_clientdata(client, indio_dev); + + ret =3D bh1730_parse_dt(bh1730, client->dev.of_node); + if (ret < 0) + return ret; + + ret =3D bh1730_power_on(bh1730); + if (ret < 0) + return ret; + + ret =3D bh1730_init_config(bh1730); + if (ret < 0) + return ret; + + indio_dev->dev.parent =3D &client->dev; + indio_dev->info =3D &bh1730_info; + indio_dev->name =3D "bh1730"; + indio_dev->channels =3D bh1730_channels; + indio_dev->num_channels =3D ARRAY_SIZE(bh1730_channels); + indio_dev->modes =3D INDIO_DIRECT_MODE; + + ret =3D iio_device_register(indio_dev); + if (ret) + goto out_power_off; + + return 0; + +out_power_off: + dev_info(&client->dev, "%s: failed\n", __func__); + bh1730_power_off(bh1730); + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int bh1730_suspend(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct iio_dev *indio_dev =3D i2c_get_clientdata(client); + struct bh1730_data *bh1730 =3D iio_priv(indio_dev); + + bh1730_power_off(bh1730); + + return 0; +} + +static int bh1730_resume(struct device *dev) +{ + struct i2c_client *client =3D to_i2c_client(dev); + struct iio_dev *indio_dev =3D i2c_get_clientdata(client); + struct bh1730_data *bh1730 =3D iio_priv(indio_dev); + int ret; + + ret =3D bh1730_power_on(bh1730); + if (ret < 0) + return ret; + + return bh1730_init_config(bh1730); +} + +static SIMPLE_DEV_PM_OPS(bh1730_pm_ops, bh1730_suspend, bh1730_resume); +#endif + +static void bh1730_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev =3D i2c_get_clientdata(client); + struct bh1730_data *bh1730 =3D iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + bh1730_power_off(bh1730); +} + +static const struct i2c_device_id bh1730_i2c_device_id[] =3D { + { BH1730_NAME, 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bh1730_i2c_device_id); + +static const struct of_device_id of_bh1730_match[] =3D { + { .compatible =3D "rohm,bh1730fvc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_bh1730_match); + +static struct i2c_driver bh1730_driver =3D { + .probe =3D bh1730_probe, + .remove =3D bh1730_remove, + .driver =3D { + .name =3D BH1730_NAME, + .owner =3D THIS_MODULE, + .of_match_table =3D of_bh1730_match, +#ifdef CONFIG_PM_SLEEP + .pm =3D &bh1730_pm_ops, +#endif + }, + .id_table =3D bh1730_i2c_device_id, +}; +module_i2c_driver(bh1730_driver); + +MODULE_AUTHOR("CTCaer "); +MODULE_DESCRIPTION("ROHM BH1730FVC driver"); +MODULE_LICENSE("GPL v2"); --=20 2.54.0