io: light: tcs3472: Add TMD3782 proximity support#445
Conversation
Note that a5-zt no longer exists in newer branches and it's now a5ltezt. |
Add devicetree binding for the AMS/TAOS TCS3472 RGBC color light sensor and TMD3782 RGBC + proximity sensor. The TCS3472 has no existing binding despite having a mainline driver since 2013. The TMD3782 is a superset of TCS3472 adding an integrated proximity detector with IR LED. Additional properties for proximity configuration (pulse count, LED current, LED supply) are optional and only relevant for TMD3782.
Add of_device_id table for devicetree-based probing of TCS3472 and TMD3782 sensors. Add optional vdd/vddio regulator support with 2.4ms power-on settling delay. Update tcs3472_powerdown() to write 0x00 for full chip power-down instead of only clearing AEN+PON. This is a behavior change: the old code preserved AIEN, the new code clears everything. This is safe because resume now performs a full register restore. Update resume to rewrite all configuration registers (ATIME, WTIME, thresholds, PERS, CONFIG, CONTROL, ENABLE) and clear stale interrupt flags. This hardens against potential register state loss during PON=0. Remove hardcoded IRQF_TRIGGER_FALLING from request_threaded_irq to let the devicetree specify the interrupt trigger type.
Add tcs3472_chip_info struct to hold per-variant channel arrays, channel counts, and feature flags. This prepares for adding TMD3782 proximity support without scattering variant checks throughout the driver. No functional change — only TCS3472 is populated. The TMD3782 entry will be added when proximity support lands.
Add proximity register definitions for the TMD3782 (PILT, PIHT, PPULSE, PDATA, PIEN, PEN, WEN, PINT, PVALID) and interrupt clear commands. Detect TMD3782 chip IDs (0x60 for TMD37821, 0x69 for TMD37823) in probe. Initialize the CONTROL register with the mandatory bit 5 and LED drive current from the led-max-microamp DT property. Configure proximity pulse count from DT (default 8) and set PPERS to 3 to match downstream behavior. Fix the PERS register write to merge both APERS and PPERS nibbles, preventing the existing bug where writing APERS clobbers PPERS. Extend resume to restore proximity thresholds, pulse count, and clear stale interrupt flags from both ALS and proximity.
Add proximity channel (IIO_PROXIMITY, scan_index 4) to TMD3782 channel array. The proximity data register at 0x1C fits the existing trigger handler's CDATA+2*i arithmetic naturally: 0xB4+8 = 0xBC. Add proximity threshold events (rising/falling/enable) with a separate event spec — no IIO_EV_INFO_PERIOD since PPERS uses a linear count, not the non-linear APERS mapping. PPERS is fixed at 3 (not configurable by userspace in v1). Extend event value read/write for proximity thresholds (PILT/PIHT). Extend write_event_config to manage PEN+WEN+PIEN for proximity events, with proper coordination against buffer enable state. Guard against enabling interrupts when no IRQ is configured. Extend interrupt handler to check both AINT and PINT in STATUS and clear only the interrupts actually observed, preventing a race where a new interrupt between STATUS read and clear would be silently lost.
Extend tcs3472_req_data() to accept a status mask parameter for polling AVALID, PVALID, or both. For direct-mode proximity reads (in_proximity_raw), temporarily enable PEN+WEN when no events or buffer are active. Uses a re-check-under-lock pattern on teardown to avoid clobbering concurrent event enables. Add buffer preenable/postdisable callbacks that manage PEN+WEN when the proximity channel (scan_index 4) is in the active scan mask. This prevents the trigger handler from hanging on PVALID when PEN is off. Coordinates with event enable state so neither path clobbers the other.
Add TMD3782 RGBC + proximity sensor on blsp_i2c2 at address 0x39. Uses GPIO113 as level-triggered interrupt input and GPIO8 as VDD enable for the sensor (modeled as a fixed regulator). The sensor shares the I2C bus with the existing BMC150 accelerometer and magnetometer. VDD (GPIO8) and VDDIO (pm8916_l5) need explicit control. The a5-zt variant inherits this node via its include of a5u-eur.dts.
Add TMD3782 RGBC + proximity sensor on the bit-banged i2c-sensor bus at address 0x39. Uses GPIO113 as level-triggered interrupt input. Unlike the A5 which uses a GPIO-controlled fixed regulator for sensor VDD, the A7 powers the sensor directly from pm8916_l17.
803ff70 to
8798788
Compare
Well, I was working against same kernel version that was in pmOS edge for Galaxy A5, couldn't get latest kernel from source running, so, was writing patches against same version that was on device. |
|
You should rebase this on the latest kernel branch available. At the time of writing this comment, it's wip/msm8916/7.0. |
| struct i2c_client *client; | ||
| const struct tcs3472_chip_info *chip_info; | ||
| struct mutex lock; | ||
| bool prox_event_enabled; |
There was a problem hiding this comment.
That's quite a lot of variables here, have you considered switching to regmap and then enabling regcache? It'd replace your current caching mechanism in a cleaner manner, for all variables.
See https://github.com/msm8916-mainline/linux/blob/wip/msm8916/7.0/drivers/iio/light/ltr501.c on how it's done. Keep in mind that your regmap settings will be different, for example, at the very least, you will need REGMAP_LITTLE_ENDIAN
linux/drivers/iio/light/ltr501.c
Lines 150 to 165 in ea3a5c5
linux/drivers/iio/light/ltr501.c
Lines 197 to 231 in ea3a5c5
linux/drivers/iio/light/ltr501.c
Lines 1400 to 1407 in ea3a5c5
linux/drivers/iio/light/ltr501.c
Lines 1432 to 1436 in ea3a5c5
linux/drivers/iio/light/ltr501.c
Lines 1452 to 1457 in ea3a5c5
|
Glad I made PR here first, before trying to upstream, I imagine it would take much longer to iterate on issues and improvements using mailing lists. I'll look into all comments and reviews, for now I'll make code changes as new commits, but after that I'll squash them into respective current commits to keep history clean. If everything will look good by y'all then I'll close that PR and will try to make upstream contribution. Thanks |
The PDRIVE field exists only in TMD3782, not TCS3472. Rename TCS3472_CONTROL_PDRIVE_MASK to TMD3782_CONTROL_PDRIVE_MASK to make this explicit.
… table Replace the enum-indexed tcs3472_chip_info_tbl[] array with standalone tcs3472_chip_info and tmd3782_chip_info structs, following the pattern recommended for modern IIO drivers (cf. veml6030). Add driver_data to the i2c_device_id table so i2c_get_match_data() works for non-OF paths.
Check AINT and PINT status bits independently and clear each interrupt separately, rather than using ALL_INTR_CLEAR when both fire simultaneously. Both interrupt sources can be pending at the same time and should each be cleared after being handled.
Add a reference to the TMD3782 v2 datasheet hosted on DigiKey.
…ement Replace all manual mutex_lock/mutex_unlock pairs with guard(mutex), which auto-releases on scope exit. This eliminates goto-based error cleanup in write_event and resume, and prevents potential missed unlocks on early-return paths.
…bute Expose the TMD3782 proximity LED drive current (PDRIVE) as an IIO_CHAN_INFO_CALIBBIAS attribute on the proximity channel, allowing runtime control from userspace. Remove the led-max-microamp DT property since the hardware default (100mA) applies and userspace can override via calibbias. Accepted values: 12500, 25000, 50000, 100000 (microamps).
…tribute Expose the TMD3782 proximity pulse count (PPULSE) as an IIO_CHAN_INFO_OVERSAMPLING_RATIO attribute on the proximity channel, allowing runtime tuning from userspace. Remove the amstaos,proximity-pulse-count DT property since the driver defaults to 6 pulses and userspace can override. Accepted values: 1-255.
erikas9987
left a comment
There was a problem hiding this comment.
Right now if you run monitor-sensor on your device, you will notice that it reports no proximity sensor, even though if there is one. That is because iio-sensor-proxy, the userspace daemon reading measurements from your sensors, doesn't understand how to interpret data from your proximity sensor.
You can add proximity-near-level device tree property to your driver, like it's done so in vcnl4000. It's needed so iio-sensor-proxy can properly pick up far/near measurements and expose them to userspace.
linux/drivers/iio/light/vcnl4000.c
Line 2025 in ea3a5c5
linux/drivers/iio/light/vcnl4000.c
Lines 1592 to 1600 in ea3a5c5
Though in the function above, you should use sysfs_emit instead of sprintf.
linux/drivers/iio/light/vcnl4000.c
Lines 1733 to 1740 in ea3a5c5
linux/drivers/iio/light/vcnl4000.c
Lines 1790 to 1800 in ea3a5c5
|
|
||
| data->enable_saved = data->enable; | ||
|
|
||
| ret = iio_triggered_buffer_setup(indio_dev, NULL, |
There was a problem hiding this comment.
This function has a managed (devm_*) counterpart. Use it so the resources are cleaned up after the driver's lifetime.
| return ret; | ||
|
|
||
| if (client->irq) { | ||
| ret = request_threaded_irq(client->irq, NULL, |
| struct mutex lock; | ||
| bool prox_event_enabled; | ||
| bool prox_buf_enabled; | ||
| u16 low_thresh; |
There was a problem hiding this comment.
If you are making a distinction between color variables and proximity variables, maybe name them accordingly? For example, you can name this variable cs_low_thresh
| if (chan->type == IIO_PROXIMITY) { | ||
| bool cold; | ||
|
|
||
| { |
There was a problem hiding this comment.
Are you sure you need this scope here? If not, drop it. If you do, scoped_guard is a better choice.
| *val2 = (256 - data->atime) * 2400; | ||
| return IIO_VAL_INT_PLUS_MICRO; | ||
| case IIO_CHAN_INFO_CALIBBIAS: | ||
| *val = tmd3782_pdrive_uamp[FIELD_GET(TMD3782_CONTROL_PDRIVE_MASK, |
There was a problem hiding this comment.
You should split this into two declarations for easier readability, like so:
i = FIELD_GET(TMD3782_CONTROL_PDRIVE_MASK, data->control);
*val = tmd3782_pdrive_uamp[i]| goto buffer_cleanup; | ||
| } | ||
|
|
||
| ret = iio_device_register(indio_dev); |
|
|
||
| free_irq: | ||
| if (client->irq) | ||
| free_irq(client->irq, indio_dev); |
There was a problem hiding this comment.
Once you convert the resource allocating calls to their managed counterparts, don't forget to remove the cleanup calls as well.
| return ret; | ||
| } | ||
|
|
||
| static void tcs3472_remove(struct i2c_client *client) |
There was a problem hiding this comment.
This function can be dropped as well afterwards.
| if (ret < 0) | ||
| return ret; | ||
| if (ret & TCS3472_STATUS_AVALID) | ||
| if ((ret & status_mask) == status_mask) |
There was a problem hiding this comment.
This can be simplified:
if (ret & status_mask)| @@ -455,29 +770,56 @@ | |||
| i2c_set_clientdata(client, indio_dev); | |||
| data->client = client; | |||
| mutex_init(&data->lock); | |||
There was a problem hiding this comment.
This function has a managed (devm_*) counterpart. Use it so the mutex is freed after driver's lifetime.
|
Oh, and any cleanups of the driver should come before addition of new functionality and/or hardware. |
|
Damn dude, thanks, you seem far more knowledgeable about contributing to Linux. I'll look again maybe in 1-2 days, will try to build it again and thoroughly test it on real hardware. |
Add TMD3782 RGBC + proximity sensor support to the tcs3472 driver and enable it on Samsung Galaxy A5/A7 device trees.
Driver changes (patches 1-6):
Device tree changes (patches 7-8):
Based on msm8916/6.12.1 from msm8916-mainline/linux.