18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tcs3414.c - Support for TAOS TCS3414 digital color sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Digital color sensor with 16-bit channels for red, green, blue, clear); 88c2ecf20Sopenharmony_ci * 7-bit I2C slave address 0x39 (TCS3414) or 0x29, 0x49, 0x59 (TCS3413, 98c2ecf20Sopenharmony_ci * TCS3415, TCS3416, resp.) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO: sync, interrupt support, thresholds, prescaler 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/pm.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define TCS3414_DRV_NAME "tcs3414" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define TCS3414_COMMAND BIT(7) 288c2ecf20Sopenharmony_ci#define TCS3414_COMMAND_WORD (TCS3414_COMMAND | BIT(5)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define TCS3414_CONTROL (TCS3414_COMMAND | 0x00) 318c2ecf20Sopenharmony_ci#define TCS3414_TIMING (TCS3414_COMMAND | 0x01) 328c2ecf20Sopenharmony_ci#define TCS3414_ID (TCS3414_COMMAND | 0x04) 338c2ecf20Sopenharmony_ci#define TCS3414_GAIN (TCS3414_COMMAND | 0x07) 348c2ecf20Sopenharmony_ci#define TCS3414_DATA_GREEN (TCS3414_COMMAND_WORD | 0x10) 358c2ecf20Sopenharmony_ci#define TCS3414_DATA_RED (TCS3414_COMMAND_WORD | 0x12) 368c2ecf20Sopenharmony_ci#define TCS3414_DATA_BLUE (TCS3414_COMMAND_WORD | 0x14) 378c2ecf20Sopenharmony_ci#define TCS3414_DATA_CLEAR (TCS3414_COMMAND_WORD | 0x16) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define TCS3414_CONTROL_ADC_VALID BIT(4) 408c2ecf20Sopenharmony_ci#define TCS3414_CONTROL_ADC_EN BIT(1) 418c2ecf20Sopenharmony_ci#define TCS3414_CONTROL_POWER BIT(0) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define TCS3414_INTEG_MASK GENMASK(1, 0) 448c2ecf20Sopenharmony_ci#define TCS3414_INTEG_12MS 0x0 458c2ecf20Sopenharmony_ci#define TCS3414_INTEG_100MS 0x1 468c2ecf20Sopenharmony_ci#define TCS3414_INTEG_400MS 0x2 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define TCS3414_GAIN_MASK GENMASK(5, 4) 498c2ecf20Sopenharmony_ci#define TCS3414_GAIN_SHIFT 4 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct tcs3414_data { 528c2ecf20Sopenharmony_ci struct i2c_client *client; 538c2ecf20Sopenharmony_ci u8 control; 548c2ecf20Sopenharmony_ci u8 gain; 558c2ecf20Sopenharmony_ci u8 timing; 568c2ecf20Sopenharmony_ci /* Ensure timestamp is naturally aligned */ 578c2ecf20Sopenharmony_ci struct { 588c2ecf20Sopenharmony_ci u16 chans[4]; 598c2ecf20Sopenharmony_ci s64 timestamp __aligned(8); 608c2ecf20Sopenharmony_ci } scan; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define TCS3414_CHANNEL(_color, _si, _addr) { \ 648c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 658c2ecf20Sopenharmony_ci .modified = 1, \ 668c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 678c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 688c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 698c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_##_color, \ 708c2ecf20Sopenharmony_ci .address = _addr, \ 718c2ecf20Sopenharmony_ci .scan_index = _si, \ 728c2ecf20Sopenharmony_ci .scan_type = { \ 738c2ecf20Sopenharmony_ci .sign = 'u', \ 748c2ecf20Sopenharmony_ci .realbits = 16, \ 758c2ecf20Sopenharmony_ci .storagebits = 16, \ 768c2ecf20Sopenharmony_ci .endianness = IIO_CPU, \ 778c2ecf20Sopenharmony_ci }, \ 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* scale factors: 1/gain */ 818c2ecf20Sopenharmony_cistatic const int tcs3414_scales[][2] = { 828c2ecf20Sopenharmony_ci {1, 0}, {0, 250000}, {0, 62500}, {0, 15625} 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* integration time in ms */ 868c2ecf20Sopenharmony_cistatic const int tcs3414_times[] = { 12, 100, 400 }; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tcs3414_channels[] = { 898c2ecf20Sopenharmony_ci TCS3414_CHANNEL(GREEN, 0, TCS3414_DATA_GREEN), 908c2ecf20Sopenharmony_ci TCS3414_CHANNEL(RED, 1, TCS3414_DATA_RED), 918c2ecf20Sopenharmony_ci TCS3414_CHANNEL(BLUE, 2, TCS3414_DATA_BLUE), 928c2ecf20Sopenharmony_ci TCS3414_CHANNEL(CLEAR, 3, TCS3414_DATA_CLEAR), 938c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(4), 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int tcs3414_req_data(struct tcs3414_data *data) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int tries = 25; 998c2ecf20Sopenharmony_ci int ret; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 1028c2ecf20Sopenharmony_ci data->control | TCS3414_CONTROL_ADC_EN); 1038c2ecf20Sopenharmony_ci if (ret < 0) 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci while (tries--) { 1078c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, TCS3414_CONTROL); 1088c2ecf20Sopenharmony_ci if (ret < 0) 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci if (ret & TCS3414_CONTROL_ADC_VALID) 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci msleep(20); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 1168c2ecf20Sopenharmony_ci data->control); 1178c2ecf20Sopenharmony_ci if (ret < 0) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (tries < 0) { 1218c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "data not ready\n"); 1228c2ecf20Sopenharmony_ci return -EIO; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int tcs3414_read_raw(struct iio_dev *indio_dev, 1298c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1308c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(indio_dev); 1338c2ecf20Sopenharmony_ci int i, ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (mask) { 1368c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1378c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci ret = tcs3414_req_data(data); 1418c2ecf20Sopenharmony_ci if (ret < 0) { 1428c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, chan->address); 1468c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 1478c2ecf20Sopenharmony_ci if (ret < 0) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci *val = ret; 1508c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1518c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1528c2ecf20Sopenharmony_ci i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT; 1538c2ecf20Sopenharmony_ci *val = tcs3414_scales[i][0]; 1548c2ecf20Sopenharmony_ci *val2 = tcs3414_scales[i][1]; 1558c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1568c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 1578c2ecf20Sopenharmony_ci *val = 0; 1588c2ecf20Sopenharmony_ci *val2 = tcs3414_times[data->timing & TCS3414_INTEG_MASK] * 1000; 1598c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci return -EINVAL; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int tcs3414_write_raw(struct iio_dev *indio_dev, 1658c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1668c2ecf20Sopenharmony_ci int val, int val2, long mask) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(indio_dev); 1698c2ecf20Sopenharmony_ci int i; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci switch (mask) { 1728c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcs3414_scales); i++) { 1748c2ecf20Sopenharmony_ci if (val == tcs3414_scales[i][0] && 1758c2ecf20Sopenharmony_ci val2 == tcs3414_scales[i][1]) { 1768c2ecf20Sopenharmony_ci data->gain &= ~TCS3414_GAIN_MASK; 1778c2ecf20Sopenharmony_ci data->gain |= i << TCS3414_GAIN_SHIFT; 1788c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data( 1798c2ecf20Sopenharmony_ci data->client, TCS3414_GAIN, 1808c2ecf20Sopenharmony_ci data->gain); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 1858c2ecf20Sopenharmony_ci if (val != 0) 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcs3414_times); i++) { 1888c2ecf20Sopenharmony_ci if (val2 == tcs3414_times[i] * 1000) { 1898c2ecf20Sopenharmony_ci data->timing &= ~TCS3414_INTEG_MASK; 1908c2ecf20Sopenharmony_ci data->timing |= i; 1918c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data( 1928c2ecf20Sopenharmony_ci data->client, TCS3414_TIMING, 1938c2ecf20Sopenharmony_ci data->timing); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci default: 1988c2ecf20Sopenharmony_ci return -EINVAL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic irqreturn_t tcs3414_trigger_handler(int irq, void *p) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 2058c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 2068c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(indio_dev); 2078c2ecf20Sopenharmony_ci int i, j = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for_each_set_bit(i, indio_dev->active_scan_mask, 2108c2ecf20Sopenharmony_ci indio_dev->masklength) { 2118c2ecf20Sopenharmony_ci int ret = i2c_smbus_read_word_data(data->client, 2128c2ecf20Sopenharmony_ci TCS3414_DATA_GREEN + 2*i); 2138c2ecf20Sopenharmony_ci if (ret < 0) 2148c2ecf20Sopenharmony_ci goto done; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci data->scan.chans[j++] = ret; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 2208c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cidone: 2238c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(scale_available, "1 0.25 0.0625 0.015625"); 2298c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL("0.012 0.1 0.4"); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct attribute *tcs3414_attributes[] = { 2328c2ecf20Sopenharmony_ci &iio_const_attr_scale_available.dev_attr.attr, 2338c2ecf20Sopenharmony_ci &iio_const_attr_integration_time_available.dev_attr.attr, 2348c2ecf20Sopenharmony_ci NULL 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const struct attribute_group tcs3414_attribute_group = { 2388c2ecf20Sopenharmony_ci .attrs = tcs3414_attributes, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct iio_info tcs3414_info = { 2428c2ecf20Sopenharmony_ci .read_raw = tcs3414_read_raw, 2438c2ecf20Sopenharmony_ci .write_raw = tcs3414_write_raw, 2448c2ecf20Sopenharmony_ci .attrs = &tcs3414_attribute_group, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int tcs3414_buffer_postenable(struct iio_dev *indio_dev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(indio_dev); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci data->control |= TCS3414_CONTROL_ADC_EN; 2528c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 2538c2ecf20Sopenharmony_ci data->control); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int tcs3414_buffer_predisable(struct iio_dev *indio_dev) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(indio_dev); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci data->control &= ~TCS3414_CONTROL_ADC_EN; 2618c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 2628c2ecf20Sopenharmony_ci data->control); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops tcs3414_buffer_setup_ops = { 2668c2ecf20Sopenharmony_ci .postenable = tcs3414_buffer_postenable, 2678c2ecf20Sopenharmony_ci .predisable = tcs3414_buffer_predisable, 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int tcs3414_probe(struct i2c_client *client, 2718c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct tcs3414_data *data; 2748c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 2788c2ecf20Sopenharmony_ci if (indio_dev == NULL) 2798c2ecf20Sopenharmony_ci return -ENOMEM; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2828c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2838c2ecf20Sopenharmony_ci data->client = client; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci indio_dev->info = &tcs3414_info; 2868c2ecf20Sopenharmony_ci indio_dev->name = TCS3414_DRV_NAME; 2878c2ecf20Sopenharmony_ci indio_dev->channels = tcs3414_channels; 2888c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(tcs3414_channels); 2898c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, TCS3414_ID); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci switch (ret & 0xf0) { 2968c2ecf20Sopenharmony_ci case 0x00: 2978c2ecf20Sopenharmony_ci dev_info(&client->dev, "TCS3404 found\n"); 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case 0x10: 3008c2ecf20Sopenharmony_ci dev_info(&client->dev, "TCS3413/14/15/16 found\n"); 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci return -ENODEV; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci data->control = TCS3414_CONTROL_POWER; 3078c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 3088c2ecf20Sopenharmony_ci data->control); 3098c2ecf20Sopenharmony_ci if (ret < 0) 3108c2ecf20Sopenharmony_ci return ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci data->timing = TCS3414_INTEG_12MS; /* free running */ 3138c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, TCS3414_TIMING, 3148c2ecf20Sopenharmony_ci data->timing); 3158c2ecf20Sopenharmony_ci if (ret < 0) 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, TCS3414_GAIN); 3198c2ecf20Sopenharmony_ci if (ret < 0) 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci data->gain = ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = iio_triggered_buffer_setup(indio_dev, NULL, 3248c2ecf20Sopenharmony_ci tcs3414_trigger_handler, &tcs3414_buffer_setup_ops); 3258c2ecf20Sopenharmony_ci if (ret < 0) 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 3298c2ecf20Sopenharmony_ci if (ret < 0) 3308c2ecf20Sopenharmony_ci goto buffer_cleanup; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cibuffer_cleanup: 3358c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int tcs3414_powerdown(struct tcs3414_data *data) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 3428c2ecf20Sopenharmony_ci data->control & ~(TCS3414_CONTROL_POWER | 3438c2ecf20Sopenharmony_ci TCS3414_CONTROL_ADC_EN)); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int tcs3414_remove(struct i2c_client *client) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 3518c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 3528c2ecf20Sopenharmony_ci tcs3414_powerdown(iio_priv(indio_dev)); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3588c2ecf20Sopenharmony_cistatic int tcs3414_suspend(struct device *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(i2c_get_clientdata( 3618c2ecf20Sopenharmony_ci to_i2c_client(dev))); 3628c2ecf20Sopenharmony_ci return tcs3414_powerdown(data); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int tcs3414_resume(struct device *dev) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct tcs3414_data *data = iio_priv(i2c_get_clientdata( 3688c2ecf20Sopenharmony_ci to_i2c_client(dev))); 3698c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL, 3708c2ecf20Sopenharmony_ci data->control); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci#endif 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend, tcs3414_resume); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic const struct i2c_device_id tcs3414_id[] = { 3778c2ecf20Sopenharmony_ci { "tcs3414", 0 }, 3788c2ecf20Sopenharmony_ci { } 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tcs3414_id); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic struct i2c_driver tcs3414_driver = { 3838c2ecf20Sopenharmony_ci .driver = { 3848c2ecf20Sopenharmony_ci .name = TCS3414_DRV_NAME, 3858c2ecf20Sopenharmony_ci .pm = &tcs3414_pm_ops, 3868c2ecf20Sopenharmony_ci }, 3878c2ecf20Sopenharmony_ci .probe = tcs3414_probe, 3888c2ecf20Sopenharmony_ci .remove = tcs3414_remove, 3898c2ecf20Sopenharmony_ci .id_table = tcs3414_id, 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_cimodule_i2c_driver(tcs3414_driver); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 3948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TCS3414 digital color sensors driver"); 3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 396