From nobody Sun Feb 8 23:27:15 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 45DBA320CCF; Mon, 19 Jan 2026 17:19:19 +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=1768843159; cv=none; b=fsS5thjUIViSKlQVEou22CE6ZKkkzpJf6pk4L5QMPvFg2G64BVXzauFbLrq6U9iivV5AWW/Kjtd4SkFvv6yugXXw7ol3GeMlvl2UBLOmivXYoPl+T8sXs5CJkR1Yn6QMCZIpa4GZmWQpCBeaglWOGVD9AUF7TemVOxEYq/m+VMA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768843159; c=relaxed/simple; bh=vgsWMnalXEzctyb/qHwzwgoCbMWvv9mKKThKgNHGnPc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=t5uaJJRDYjYGo5ymLz1vbgSmEzjUuNU1ezsruNiW8z7+uYL4rkd+KH+FQqe0rp6sgqdevI/5Yzg9WBGtXNzRzpXOeMmWyxJU0V1etmOvMA3PDrq7W+gu/hkY0ajMnremKFQirYo3ZV2Z3NEiO0wmnKoJ6gkJChVsqsYXWD6Jys4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=sjAB33Uy; 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="sjAB33Uy" Received: by smtp.kernel.org (Postfix) with ESMTPS id DBF59C19422; Mon, 19 Jan 2026 17:19:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768843158; bh=vgsWMnalXEzctyb/qHwzwgoCbMWvv9mKKThKgNHGnPc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=sjAB33UyurdZCvW+71NpzazGJRn+w29dq+R5jDje7RMUN68KoOf3cbY08Q2s/jS6I ik8+12b0/uSWeXDWed9BXoE6NdBrbKxCfoyHOc65iH0g33FIK4x7siqesFv9sfAwkL ioUEQiPiM39V2/96ornZL2+jxvfhfb8jAiGNNhe1DIPOSs6vt6P4AGBFOQa0xVXe6+ x+YG5RHlyQDzMhSKiuUCgQE5nB7TmDlvgP80V6M21E35NJsAe3n6hwq8uveKEIKEUX 6JV6AjbgIL4TAWUA9yTQV3hED4rGDyDNt9VK+LbG8a3oY+t2m3QBVdZBIxaELr7sst ytylfrl/+S/VQ== 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 CED6DD29C4D; Mon, 19 Jan 2026 17:19:18 +0000 (UTC) From: Petr Hodina via B4 Relay Date: Mon, 19 Jan 2026 18:19:06 +0100 Subject: [PATCH 1/3] doc: add Device Tree binding for AMS TCS3400 light sensor 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: <20260119-tsc3400-v1-1-82a65c5417aa@protonmail.com> References: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> In-Reply-To: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , David Heidelberg Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Petr Hodina X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1768843157; l=2537; i=petr.hodina@protonmail.com; s=20260107; h=from:subject:message-id; bh=1LDwA54n0tkYAGyvdpUdaNISfncR1cK5nESUpKkwwc8=; b=xKuI+yo90AJJ5IlxvKntYTEmP+ZcSeI/7Px4HhOzYrqfHogJT4MvIE2kGre8uJcqnP0tc2evl 0Rwcq3WXQmgALu+8yy7Jf0RVMqEoLMdrCGWQsEhne6kaSw+BwLxWCbV X-Developer-Key: i=petr.hodina@protonmail.com; a=ed25519; pk=3QaVc6AaAu1IsyyH86+LIOOFhD7kCws8Xhe+wwyE7Bg= X-Endpoint-Received: by B4 Relay for petr.hodina@protonmail.com/20260107 with auth_id=594 X-Original-From: Petr Hodina Reply-To: petr.hodina@protonmail.com From: Petr Hodina Adds a new YAML binding describing the AMS TCS3400 I2C light sensor, including compatible string, registers, interrupts, power supply, and an example node. Signed-off-by: Petr Hodina --- .../devicetree/bindings/iio/light/ams,tcs3400.yaml | 54 ++++++++++++++++++= ++++ MAINTAINERS | 6 +++ 2 files changed, 60 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/light/ams,tcs3400.yaml b= /Documentation/devicetree/bindings/iio/light/ams,tcs3400.yaml new file mode 100644 index 000000000000..2c5a9295af1a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/ams,tcs3400.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/iio/light/ams,tcs3400.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: AMS TCS3400 Color Light-to-Digital Converter + +maintainers: + - name: Petr Hodina + email: petr.hodina@protonmail.com + +description: | + The AMS TCS3400 is an I2C-connected color light sensor providing + RGBC or RGB-IR measurements with a programmable integration time + and gain. + +properties: + compatible: + const: ams,tcs3400 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: + Interrupt line signaling ALS data ready or threshold events. + + vdd-supply: + description: + Regulator supplying the main sensor power. + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + light-sensor@39 { + compatible =3D "ams,tcs3400"; + reg =3D <0x39>; + interrupt-parent =3D <&gpio1>; + interrupts =3D <5 IRQ_TYPE_EDGE_FALLING>; + vdd-supply =3D <&vdd_3v3>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 14a06f856b81..ab5307a34180 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22866,6 +22866,12 @@ S: Supported F: drivers/iio/adc/rohm-bd79112.c F: drivers/iio/adc/rohm-bd79124.c =20 +AMS TCS3400 AMBIENT LIGHT SENSOR DRIVER +M: Petr Hodina +L: Petr Hodina +S: Petr Hodina +F: Documentation/devicetree/bindings/iio/light/ams,tcs3400.yaml + ROHM BH1745 COLOUR SENSOR M: Mudit Sharma L: linux-iio@vger.kernel.org --=20 2.52.0 From nobody Sun Feb 8 23:27:15 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 45F00324712; Mon, 19 Jan 2026 17:19:19 +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=1768843159; cv=none; b=CxVkKGWb96cnCnver3aT3y3d5zWSHOyPBlatFEypj96Zc/IUvwqbDNTxfgWRUhFg3noPfT/+xHMdozeqgCzlo4l3G1o9orQJvqVA5fj8CVlhoRcleIAJWOf5XYCoDGuM970wxxauWA+vwA/G08OcKUnO5XcCMxI3bzxQ/O9aWXk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768843159; c=relaxed/simple; bh=pUYm/nLOsiRw0PP903KyRsznqNmrX6SkLHJrLMnxjjs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DjvX5Su/Xib+A27+oVdGtQNtyigxTEkL2gJgFAdov0uPFr3cF+5r7vq39MfuqlRuEUUEowA731GBC81RAMETpV1Av4owCWVB2+o/662zx0Lt2uBTf63U4N07IY0Zbow8ua3ziPfCthze9I1IEsQriK0SH0Bt04iUae+w2CJhiHE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jfRdJSIl; 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="jfRdJSIl" Received: by smtp.kernel.org (Postfix) with ESMTPS id EF25DC19424; Mon, 19 Jan 2026 17:19:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768843159; bh=pUYm/nLOsiRw0PP903KyRsznqNmrX6SkLHJrLMnxjjs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=jfRdJSIlMejbsKXIxgbUzJuLhdTXk/eumJh9mQNASHfDgVcCorsCzmB7ONlcwAHzQ Rk5GqQGF/hs+6ijbzPM9yF/uRHnIOnzZf4yGLmP+bkRSKwuQA0OoHe2l+pl7tyQxx7 N7HI5OOa7qL6X/imU3ZJfryMlPfZ5SPp3xIXBLFlYdB6cz0q/W1ZQH3yqvSHfy4PfI Z6BwnfQn0aco7N94b1MTbnXpGveHR2vElxTQWUwTubzSZ2xi8xZOjR0afgWfkLqJs2 Q+uNbWMHklyIyc15GPcnnRFrmA5NsGz4RqZWYdIoO8+JSArtmxtufQgdtvI/m27akh Lj5e/b71cyhvQ== 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 E0038D29C4F; Mon, 19 Jan 2026 17:19:18 +0000 (UTC) From: Petr Hodina via B4 Relay Date: Mon, 19 Jan 2026 18:19:07 +0100 Subject: [PATCH 2/3] iio: light: add AMS TCS3400 RGB and RGB-IR color 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: <20260119-tsc3400-v1-2-82a65c5417aa@protonmail.com> References: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> In-Reply-To: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , David Heidelberg Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Petr Hodina X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1768843157; l=15610; i=petr.hodina@protonmail.com; s=20260107; h=from:subject:message-id; bh=uBvJsX6zVtI/AhYhXh+YLd+6/zxC/fhMKZZvMXEiAdU=; b=M4QT8T5lcXzyvjf9LFjz6CB7XaW4WfViDTpmcXORZAMnwD4xZEQnd/t1EK1Q1KFXuoFBpGUOm 3gbVx02i4HyCN3LAiUu8wriGfn+xVM3gp0SRJPt1jfHOE7GZuj+LOMB X-Developer-Key: i=petr.hodina@protonmail.com; a=ed25519; pk=3QaVc6AaAu1IsyyH86+LIOOFhD7kCws8Xhe+wwyE7Bg= X-Endpoint-Received: by B4 Relay for petr.hodina@protonmail.com/20260107 with auth_id=594 X-Original-From: Petr Hodina Reply-To: petr.hodina@protonmail.com From: Petr Hodina Add support for the AMS TCS3400 I2C color light-to-digital converter. The driver supports RGBC and RGB-IR modes, programmable integration time, optional interrupt-driven buffered capture, and regulator-based power control. Signed-off-by: Petr Hodina --- MAINTAINERS | 1 + drivers/iio/light/Kconfig | 11 + drivers/iio/light/Makefile | 1 + drivers/iio/light/tcs3400.c | 505 ++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 518 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ab5307a34180..3d7d0aa10c55 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22871,6 +22871,7 @@ M: Petr Hodina L: Petr Hodina S: Petr Hodina F: Documentation/devicetree/bindings/iio/light/ams,tcs3400.yaml +F: drivers/iio/light/tcs3400.c =20 ROHM BH1745 COLOUR SENSOR M: Mudit Sharma diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ac1408d374c9..73419d80e3a7 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -580,6 +580,17 @@ config ST_UVIS25_SPI depends on ST_UVIS25 select REGMAP_SPI =20 +config TCS3400 + tristate "AMS TCS3400 color light-to-digital converter" + depends on I2C + default n + help + If you say yes here you get support for the AMS TCS3400. + This sensor can detect ambient light and color (RGB) values. + + This driver can also be built as a module. If so, the module + will be called tcs3400. + config TCS3414 tristate "TAOS TCS3414 digital color sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c0048e0d5ca8..847ef7bf0f57 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_STK3310) +=3D stk3310.o obj-$(CONFIG_ST_UVIS25) +=3D st_uvis25_core.o obj-$(CONFIG_ST_UVIS25_I2C) +=3D st_uvis25_i2c.o obj-$(CONFIG_ST_UVIS25_SPI) +=3D st_uvis25_spi.o +obj-$(CONFIG_TCS3400) +=3D tcs3400.o obj-$(CONFIG_TCS3414) +=3D tcs3414.o obj-$(CONFIG_TCS3472) +=3D tcs3472.o obj-$(CONFIG_SENSORS_TSL2563) +=3D tsl2563.o diff --git a/drivers/iio/light/tcs3400.c b/drivers/iio/light/tcs3400.c new file mode 100644 index 000000000000..22c8c4e803cf --- /dev/null +++ b/drivers/iio/light/tcs3400.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TCS3400 - AMS/TAOS color light sensor with RGBC and RGB-IR channels + * + * Copyright (c) 2025 Petr Hodina + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCS3400_DRV_NAME "tcs3400" +#define TCS3400_CMD_REG(reg) (0x80 | (reg)) +#define TCS3400_CMD_SPECIAL 0xE0 +#define TCS3400_CMD_ALS_INT_CLR 0xE6 +#define TCS3400_CMD_ALL_INT_CLR 0xE7 +#define TCS3400_ENABLE 0x00 +#define TCS3400_ATIME 0x01 +#define TCS3400_WTIME 0x03 +#define TCS3400_PERSIST 0x0C +#define TCS3400_CONTROL 0x0F /* Gain */ +#define TCS3400_STATUS 0x13 +#define TCS3400_CDATAL 0x14 /* Clear low */ +#define TCS3400_RDATAL 0x16 +#define TCS3400_GDATAL 0x18 +#define TCS3400_BDATAL 0x1A +#define TCS3400_ID 0x12 +#define TCS3400_CHSEL 0xC0 /* Access IR channel: 0x00 RGBC, 0x80 RGB-IR */ +#define TCS3400_EN_PON BIT(0) +#define TCS3400_EN_AEN BIT(1) +#define TCS3400_EN_AIEN BIT(4) +#define TCS3400_STATUS_AVALID BIT(0) +#define TCS3400_STATUS_AINT BIT(4) +#define TCS3400_GAIN_1X 0x00 +#define TCS3400_GAIN_4X 0x01 +#define TCS3400_GAIN_16X 0x02 +#define TCS3400_GAIN_64X 0x03 +#define TCS3400_MAX_ATIME 256 + +#define TCS3400_IIO_CHANNEL(_index, _mod) { \ + .type =3D IIO_INTENSITY, \ + .modified =3D 1, \ + .channel2 =3D IIO_MOD_##_mod, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .indexed =3D 1, \ + .channel =3D _index, \ + .scan_index =3D _index, \ + .scan_type =3D { \ + .sign =3D 'u', \ + .realbits =3D 16, \ + .storagebits =3D 16, \ + .endianness =3D IIO_LE, \ + }, \ +} + +struct tcs3400_data { + struct i2c_client *client; + struct mutex lock; + struct regulator *vdd_supply; + u8 atime; + u8 gain; + u8 channel_mode; /* 0x00 or 0x80 */ + u16 clear_ir; /* clear when mode=3D0x00, IR when mode=3D0x80 */ + u16 red; + u16 green; + u16 blue; +}; + +static const int tcs3400_gains[] =3D {1, 4, 16, 64}; + +static int tcs3400_power_on(struct tcs3400_data *data) +{ + int ret; + + ret =3D regulator_enable(data->vdd_supply); + if (ret) + return ret; + + msleep(20); + + return 0; +} + +static void tcs3400_power_off(struct tcs3400_data *data) +{ + regulator_disable(data->vdd_supply); +} + +static int tcs3400_write_reg(struct tcs3400_data *data, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(data->client, TCS3400_CMD_REG(reg), val); +} + +static int tcs3400_read_reg(struct tcs3400_data *data, u8 reg, u8 *val) +{ + int ret =3D i2c_smbus_read_byte_data(data->client, TCS3400_CMD_REG(reg)); + + if (ret < 0) + return ret; + *val =3D ret; + + return 0; +} + +static int tcs3400_read_word(struct tcs3400_data *data, u8 reg, u16 *val) +{ + + __le16 buf; + int ret =3D i2c_smbus_read_i2c_block_data(data->client, + TCS3400_CMD_REG(reg), 2, (u8 *)&buf); + if (ret < 0) + return ret; + *val =3D le16_to_cpu(buf); + return 0; +} +static int tcs3400_clear_interrupt(struct tcs3400_data *data) +{ + + return i2c_smbus_write_byte(data->client, TCS3400_CMD_ALS_INT_CLR); +} + +static int tcs3400_read_channels(struct tcs3400_data *data) +{ + + int ret, retries =3D 20; + u8 status; + + do { + ret =3D tcs3400_read_reg(data, TCS3400_STATUS, &status); + if (ret) + return ret; + if (status & TCS3400_STATUS_AVALID) + break; + usleep_range(5000, 6000); + } while (--retries); + if (!retries) { + dev_warn(&data->client->dev, "Timeout waiting for valid data\n"); + return -ETIMEDOUT; + } + ret =3D tcs3400_read_word(data, TCS3400_CDATAL, &data->clear_ir); + if (ret) + return ret; + + ret =3D tcs3400_read_word(data, TCS3400_RDATAL, &data->red); + if (ret) + return ret; + + ret =3D tcs3400_read_word(data, TCS3400_GDATAL, &data->green); + if (ret) + return ret; + + ret =3D tcs3400_read_word(data, TCS3400_BDATAL, &data->blue); + if (ret) + return ret; + return 0; +} + +static irqreturn_t tcs3400_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf =3D p; + struct iio_dev *indio_dev =3D pf->indio_dev; + struct tcs3400_data *data =3D iio_priv(indio_dev); + u16 buf[4]; + int ret; + + mutex_lock(&data->lock); + ret =3D tcs3400_read_channels(data); + if (!ret) { + buf[0] =3D data->clear_ir; + buf[1] =3D data->red; + buf[2] =3D data->green; + buf[3] =3D data->blue; + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); + } + mutex_unlock(&data->lock); + + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static irqreturn_t tcs3400_irq_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev =3D priv; + struct tcs3400_data *data =3D iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret =3D tcs3400_read_channels(data); + if (!ret) + iio_trigger_poll_nested(indio_dev->trig); + + tcs3400_clear_interrupt(data); + mutex_unlock(&data->lock); + + return IRQ_HANDLED; +} + +static int tcs3400_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tcs3400_data *data =3D iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret =3D tcs3400_read_channels(data); + if (ret) { + mutex_unlock(&data->lock); + return ret; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->channel2) { + case IIO_MOD_LIGHT_CLEAR: + *val =3D data->clear_ir; + break; + case IIO_MOD_LIGHT_RED: + *val =3D data->red; + break; + case IIO_MOD_LIGHT_GREEN: + *val =3D data->green; + break; + case IIO_MOD_LIGHT_BLUE: + *val =3D data->blue; + break; + default: + ret =3D -EINVAL; + break; + } + ret =3D IIO_VAL_INT; + break; + case IIO_CHAN_INFO_INT_TIME: + *val =3D 0; + *val2 =3D (TCS3400_MAX_ATIME - data->atime) * 2780000; /* 2.78 ms per cy= cle */ + ret =3D IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret =3D -EINVAL; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int tcs3400_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct tcs3400_data *data =3D iio_priv(indio_dev); + int i, ret =3D 0; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val !=3D 0) + return -EINVAL; + i =3D TCS3400_MAX_ATIME - DIV_ROUND_CLOSEST(val2, 2780000); + if (i < 1 || i >=3D TCS3400_MAX_ATIME) + return -EINVAL; + mutex_lock(&data->lock); + data->atime =3D i; + ret =3D tcs3400_write_reg(data, TCS3400_ATIME, data->atime); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec tcs3400_channels[] =3D { + TCS3400_IIO_CHANNEL(0, LIGHT_CLEAR), + TCS3400_IIO_CHANNEL(1, LIGHT_RED), + TCS3400_IIO_CHANNEL(2, LIGHT_GREEN), + TCS3400_IIO_CHANNEL(3, LIGHT_BLUE), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static ssize_t tcs3400_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev =3D dev_to_iio_dev(dev); + struct tcs3400_data *data =3D iio_priv(indio_dev); + u8 enable; + int ret; + + ret =3D tcs3400_read_reg(data, TCS3400_ENABLE, &enable); + if (ret) + return ret; + return sprintf(buf, "%d\n", !!(enable & (TCS3400_EN_PON | TCS3400_EN_AEN)= )); +} + +static ssize_t tcs3400_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev =3D dev_to_iio_dev(dev); + struct tcs3400_data *data =3D iio_priv(indio_dev); + bool enable; + int ret; + u8 val; + + ret =3D kstrtobool(buf, &enable); + if (ret) + return ret; + mutex_lock(&data->lock); + if (enable) + val =3D TCS3400_EN_PON | TCS3400_EN_AEN; + else + val =3D 0; + ret =3D tcs3400_write_reg(data, TCS3400_ENABLE, val); + mutex_unlock(&data->lock); + if (ret) + return ret; + + if (enable) + msleep(20); + return len; +} + +static ssize_t tcs3400_ir_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev =3D dev_to_iio_dev(dev); + struct tcs3400_data *data =3D iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!data->channel_mode); +} + +static ssize_t tcs3400_ir_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev =3D dev_to_iio_dev(dev); + struct tcs3400_data *data =3D iio_priv(indio_dev); + bool enable; + int ret; + + ret =3D kstrtobool(buf, &enable); + if (ret) + return ret; + mutex_lock(&data->lock); + data->channel_mode =3D enable ? 0x80 : 0x00; + ret =3D tcs3400_write_reg(data, TCS3400_CHSEL, data->channel_mode); + mutex_unlock(&data->lock); + return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(enable, 0644, tcs3400_enable_show, tcs3400_enable_s= tore, 0); +static IIO_DEVICE_ATTR(ir_mode, 0644, tcs3400_ir_mode_show, tcs3400_ir_mod= e_store, 0); + +static struct attribute *tcs3400_attributes[] =3D { + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_ir_mode.dev_attr.attr, + NULL +}; + +static const struct attribute_group tcs3400_attribute_group =3D { + .attrs =3D tcs3400_attributes, +}; + +static const struct iio_info tcs3400_info =3D { + .read_raw =3D tcs3400_read_raw, + .write_raw =3D tcs3400_write_raw, + .attrs =3D &tcs3400_attribute_group, +}; + +static int tcs3400_probe(struct i2c_client *client) +{ + struct tcs3400_data *data; + struct iio_dev *indio_dev; + int ret; + u8 id; + + indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data =3D iio_priv(indio_dev); + data->client =3D client; + mutex_init(&data->lock); + + i2c_set_clientdata(client, indio_dev); + + data->vdd_supply =3D devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply), + "Unable to get VDD regulator\n"); + + ret =3D tcs3400_power_on(data); + if (ret) + goto err_power_off; + + ret =3D i2c_smbus_read_byte_data(client, TCS3400_CMD_REG(TCS3400_ID)); + if (ret < 0) { + ret =3D -ENODEV; + goto err_power_off; + return ret; + } + + id =3D ret; + if (id =3D=3D 0x90) + dev_info(&client->dev, "TCS3401/5 Chip ID: 0x%02x\n", id); + if (id =3D=3D 0x93) + dev_info(&client->dev, "TCS3403/7 Chip ID: 0x%02x\n", id); + else { + dev_err(&client->dev, "Unknown chip ID: 0x%02x\n", id); + ret =3D -ENODEV; + } + + data->atime =3D 0xF6; /* ~27.8 ms integration */ + data->gain =3D TCS3400_GAIN_1X; + data->channel_mode =3D 0x00; + + tcs3400_write_reg(data, TCS3400_ATIME, data->atime); + tcs3400_write_reg(data, TCS3400_CONTROL, data->gain); + tcs3400_write_reg(data, TCS3400_PERSIST, 0x00); /* interrupt every cycle = */ + tcs3400_write_reg(data, TCS3400_CHSEL, data->channel_mode); /* RGBC mode = */ + + tcs3400_write_reg(data, TCS3400_ENABLE, + TCS3400_EN_PON | TCS3400_EN_AEN | TCS3400_EN_AIEN); + + indio_dev->name =3D TCS3400_DRV_NAME; + indio_dev->channels =3D tcs3400_channels; + indio_dev->num_channels =3D ARRAY_SIZE(tcs3400_channels); + indio_dev->info =3D &tcs3400_info; + indio_dev->modes =3D INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED; + + ret =3D devm_iio_triggered_buffer_setup(&client->dev, indio_dev, + NULL, + tcs3400_trigger_handler, + NULL); + if (ret) + goto err_power_off; + if (client->irq > 0) { + ret =3D devm_request_threaded_irq(&client->dev, client->irq, + NULL, tcs3400_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tcs3400_irq", indio_dev); + if (ret) + goto err_power_off; + } + + ret =3D devm_iio_device_register(&client->dev, indio_dev); + if (ret) + goto err_power_off; + return 0; +err_power_off: + tcs3400_write_reg(data, TCS3400_ENABLE, 0); + tcs3400_power_off(data); + return ret; +} + +static void tcs3400_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev =3D i2c_get_clientdata(client); + struct tcs3400_data *data =3D iio_priv(indio_dev); + + tcs3400_write_reg(data, TCS3400_ENABLE, 0); + tcs3400_power_off(data); +} + +static const struct of_device_id tcs3400_of_match[] =3D { + { .compatible =3D "ams,tcs3400" }, + { } +}; + +MODULE_DEVICE_TABLE(of, tcs3400_of_match); + +static const struct i2c_device_id tcs3400_id[] =3D { + { "tcs3400", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tcs3400_id); + +static struct i2c_driver tcs3400_driver =3D { + .driver =3D { + .name =3D TCS3400_DRV_NAME, + .of_match_table =3D tcs3400_of_match, + }, + .probe =3D tcs3400_probe, + .remove =3D tcs3400_remove, + .id_table =3D tcs3400_id, +}; + +module_i2c_driver(tcs3400_driver); +MODULE_AUTHOR("Petr Hodina "); +MODULE_DESCRIPTION("AMS TCS3400 RGB/IR color sensor IIO driver"); +MODULE_LICENSE("GPL"); --=20 2.52.0 From nobody Sun Feb 8 23:27:15 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 45E88322557; Mon, 19 Jan 2026 17:19:19 +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=1768843159; cv=none; b=Ahgwm5IIaLMcnLad35S/WeR5yFZvGaudTolydQnxeydvyhpjEBAvU41D9Q/mTPY5A1bddMgnRnpLPWGdhKTY9JdYxIt4N0HA3WwZffguOKhSB8UArF36C+Ji4hhvBjH6WuybkjFiW5TXma3ZjwSUxu8WYgCkdwRi7cFwP4VFR7Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768843159; c=relaxed/simple; bh=V3C1yvRX6bb7a06RtadQrxrf/kE11eVwt9lTWF0EcrU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tjJFmOsKKtp73bnbdjve3fzWFdV6qgWhEga6JuGjUHo98/+/Gk4PoJvs+JqMDGBRvEPqgh9BEGwUSZjF2vIbTF2OxD4DpyvZbgkjR421Ly9A9UxtpEHeXqefr66j9iBRm2xnEWGTyeADwZvbmBiEQ4v4fcUUy4IMXByc4wNGJYk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FlkDOD+V; 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="FlkDOD+V" Received: by smtp.kernel.org (Postfix) with ESMTPS id 0991BC2BCB0; Mon, 19 Jan 2026 17:19:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768843159; bh=V3C1yvRX6bb7a06RtadQrxrf/kE11eVwt9lTWF0EcrU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=FlkDOD+VQF6a6b45l1G0N/lMZjzr/Yb8XvQG8Hi+tajSKklCqv0T57NTwiZQ48iAZ adkSF+ev5oCGmhVGTvFVl0i2yXVpB8OWYYqQQHzl1h6XQmKayniC8kD9Yio/DIBbYt q/qhIXVAn0tdCYLSGe+3JbJ8G9uB4a9lBSM7yukviBlzPSX9iGIYcAdWOezLvcr+cM qsM1XxAXWRyFDQBMz2206UragoRBT3Gd/8vZOdnbFV+W8d5yPfSDTghHXTGX3wFnY0 vU/Grx4qvOU7dyT2RBb3vRN3OmMdgq7BMcQjR09oDBlDB+lWSvlKb9TYgIW4hvzkOE eHnSfPVYihJ1w== 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 F21C3D29C32; Mon, 19 Jan 2026 17:19:18 +0000 (UTC) From: Petr Hodina via B4 Relay Date: Mon, 19 Jan 2026 18:19:08 +0100 Subject: [PATCH 3/3] sdm845: tama: Add AMS TCS3400 ambient light sensor 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: <20260119-tsc3400-v1-3-82a65c5417aa@protonmail.com> References: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> In-Reply-To: <20260119-tsc3400-v1-0-82a65c5417aa@protonmail.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio , David Heidelberg Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Petr Hodina X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1768843157; l=1617; i=petr.hodina@protonmail.com; s=20260107; h=from:subject:message-id; bh=Q9E7GuxhgZ08JdbgPbICgzjn1Hy4d+2NDwK0eFbzpK4=; b=IPlvfSzMaVogNzIyBJOL3YLmXBH9HaRoJaSrYSVDZl1JUNZ1qdR+DMyeh2pkmnMzL6g6XE2I+ f3+Rw8osNwoBtlto0Ayw/blcLYEr3Cmn59eFeMOVU0lSKayRCyUfKK8 X-Developer-Key: i=petr.hodina@protonmail.com; a=ed25519; pk=3QaVc6AaAu1IsyyH86+LIOOFhD7kCws8Xhe+wwyE7Bg= X-Endpoint-Received: by B4 Relay for petr.hodina@protonmail.com/20260107 with auth_id=594 X-Original-From: Petr Hodina Reply-To: petr.hodina@protonmail.com From: Petr Hodina Add device tree node for TCS3400 ambient light sensor with power supplies, interrupt on GPIO11, and pinctrl states. Signed-off-by: Petr Hodina --- .../boot/dts/qcom/sdm845-sony-xperia-tama.dtsi | 36 ++++++++++++++++++= +++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi b/arch/a= rm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi index 7dc9349eedfd..15d56d6831c5 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi @@ -485,7 +485,23 @@ &i2c14 { clock-frequency =3D <400000>; =20 /* SONY ToF sensor @ 52 */ - /* AMS TCS3490 RGB+IR color sensor @ 72 */ + + tcs3400_sensor: tcs3400_sensor@39 { + compatible =3D "ams,tcs3400"; + reg =3D <0x39>; + + interrupts-extended =3D <&tlmm 11 IRQ_TYPE_EDGE_FALLING>; + + vdd-supply =3D <&vreg_l22a_2p8>; + vio-supply =3D <&cam_vio_vreg>; + + pinctrl-0 =3D <&ams_sensor_default>; + pinctrl-1 =3D <&ams_sensor_sleep>; + + ams,rgbcir-vdd-supply =3D <1>; + ams,rgbcir-gpio-vdd =3D <0>; + ams,rgbcir-vio-supply =3D <1>; + }; }; =20 &ibb { @@ -751,6 +767,24 @@ int-pins { bias-pull-down; }; }; + + ams_sensor_default: ams-sensor-default-state { + int-pins { + pins =3D "gpio11"; + function =3D "gpio"; + drive-strength =3D <2>; + bias-pull-up; + }; + }; + + ams_sensor_sleep: ams_sensor-sleep-state { + int-pins { + pins =3D "gpio11"; + function =3D "gpio"; + drive-strength =3D <2>; + bias-pull-down; + }; + }; }; =20 &uart6 { --=20 2.52.0