Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * AL3320A - Dyna Image Ambient Light Sensor
  3 *
  4 * Copyright (c) 2014, Intel Corporation.
  5 *
  6 * This file is subject to the terms and conditions of version 2 of
  7 * the GNU General Public License.  See the file COPYING in the main
  8 * directory of this archive for more details.
  9 *
 10 * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
 11 *
 12 * TODO: interrupt support, thresholds
 13 *
 14 */
 15
 16#include <linux/module.h>
 17#include <linux/init.h>
 18#include <linux/i2c.h>
 19
 20#include <linux/iio/iio.h>
 21#include <linux/iio/sysfs.h>
 22
 23#define AL3320A_DRV_NAME "al3320a"
 24
 25#define AL3320A_REG_CONFIG		0x00
 26#define AL3320A_REG_STATUS		0x01
 27#define AL3320A_REG_INT			0x02
 28#define AL3320A_REG_WAIT		0x06
 29#define AL3320A_REG_CONFIG_RANGE	0x07
 30#define AL3320A_REG_PERSIST		0x08
 31#define AL3320A_REG_MEAN_TIME		0x09
 32#define AL3320A_REG_ADUMMY		0x0A
 33#define AL3320A_REG_DATA_LOW		0x22
 34
 35#define AL3320A_REG_LOW_THRESH_LOW	0x30
 36#define AL3320A_REG_LOW_THRESH_HIGH	0x31
 37#define AL3320A_REG_HIGH_THRESH_LOW	0x32
 38#define AL3320A_REG_HIGH_THRESH_HIGH	0x33
 39
 40#define AL3320A_CONFIG_DISABLE		0x00
 41#define AL3320A_CONFIG_ENABLE		0x01
 42
 43#define AL3320A_GAIN_SHIFT		1
 44#define AL3320A_GAIN_MASK		(BIT(2) | BIT(1))
 45
 46/* chip params default values */
 47#define AL3320A_DEFAULT_MEAN_TIME	4
 48#define AL3320A_DEFAULT_WAIT_TIME	0 /* no waiting */
 49
 50#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
 51
 52enum al3320a_range {
 53	AL3320A_RANGE_1, /* 33.28 Klx */
 54	AL3320A_RANGE_2, /* 8.32 Klx  */
 55	AL3320A_RANGE_3, /* 2.08 Klx  */
 56	AL3320A_RANGE_4  /* 0.65 Klx  */
 57};
 58
 59static const int al3320a_scales[][2] = {
 60	{0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
 61};
 62
 63struct al3320a_data {
 64	struct i2c_client *client;
 65};
 66
 67static const struct iio_chan_spec al3320a_channels[] = {
 68	{
 69		.type	= IIO_LIGHT,
 70		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 71				      BIT(IIO_CHAN_INFO_SCALE),
 72	}
 73};
 74
 75static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
 76
 77static struct attribute *al3320a_attributes[] = {
 78	&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
 79	NULL,
 80};
 81
 82static const struct attribute_group al3320a_attribute_group = {
 83	.attrs = al3320a_attributes,
 84};
 85
 86static int al3320a_init(struct al3320a_data *data)
 87{
 88	int ret;
 89
 90	/* power on */
 91	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
 92					AL3320A_CONFIG_ENABLE);
 93	if (ret < 0)
 94		return ret;
 95
 96	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
 97					AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
 98	if (ret < 0)
 99		return ret;
100
101	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
102					AL3320A_DEFAULT_MEAN_TIME);
103	if (ret < 0)
104		return ret;
105
106	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
107					AL3320A_DEFAULT_WAIT_TIME);
108	if (ret < 0)
109		return ret;
110
111	return 0;
112}
113
114static int al3320a_read_raw(struct iio_dev *indio_dev,
115			    struct iio_chan_spec const *chan, int *val,
116			    int *val2, long mask)
117{
118	struct al3320a_data *data = iio_priv(indio_dev);
119	int ret;
120
121	switch (mask) {
122	case IIO_CHAN_INFO_RAW:
123		/*
124		 * ALS ADC value is stored in two adjacent registers:
125		 * - low byte of output is stored at AL3320A_REG_DATA_LOW
126		 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
127		 */
128		ret = i2c_smbus_read_word_data(data->client,
129					       AL3320A_REG_DATA_LOW);
130		if (ret < 0)
131			return ret;
132		*val = ret;
133		return IIO_VAL_INT;
134	case IIO_CHAN_INFO_SCALE:
135		ret = i2c_smbus_read_byte_data(data->client,
136					       AL3320A_REG_CONFIG_RANGE);
137		if (ret < 0)
138			return ret;
139
140		ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
141		*val = al3320a_scales[ret][0];
142		*val2 = al3320a_scales[ret][1];
143
144		return IIO_VAL_INT_PLUS_MICRO;
145	}
146	return -EINVAL;
147}
148
149static int al3320a_write_raw(struct iio_dev *indio_dev,
150			     struct iio_chan_spec const *chan, int val,
151			     int val2, long mask)
152{
153	struct al3320a_data *data = iio_priv(indio_dev);
154	int i;
155
156	switch (mask) {
157	case IIO_CHAN_INFO_SCALE:
158		for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
159			if (val == al3320a_scales[i][0] &&
160			    val2 == al3320a_scales[i][1])
161				return i2c_smbus_write_byte_data(data->client,
162					AL3320A_REG_CONFIG_RANGE,
163					i << AL3320A_GAIN_SHIFT);
164		}
165		break;
166	}
167	return -EINVAL;
168}
169
170static const struct iio_info al3320a_info = {
171	.read_raw	= al3320a_read_raw,
172	.write_raw	= al3320a_write_raw,
173	.attrs		= &al3320a_attribute_group,
174};
175
176static int al3320a_probe(struct i2c_client *client,
177			 const struct i2c_device_id *id)
178{
179	struct al3320a_data *data;
180	struct iio_dev *indio_dev;
181	int ret;
182
183	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
184	if (!indio_dev)
185		return -ENOMEM;
186
187	data = iio_priv(indio_dev);
188	i2c_set_clientdata(client, indio_dev);
189	data->client = client;
190
191	indio_dev->dev.parent = &client->dev;
192	indio_dev->info = &al3320a_info;
193	indio_dev->name = AL3320A_DRV_NAME;
194	indio_dev->channels = al3320a_channels;
195	indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
196	indio_dev->modes = INDIO_DIRECT_MODE;
197
198	ret = al3320a_init(data);
199	if (ret < 0) {
200		dev_err(&client->dev, "al3320a chip init failed\n");
201		return ret;
202	}
203	return devm_iio_device_register(&client->dev, indio_dev);
204}
205
206static int al3320a_remove(struct i2c_client *client)
207{
208	return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
209					 AL3320A_CONFIG_DISABLE);
210}
211
212static const struct i2c_device_id al3320a_id[] = {
213	{"al3320a", 0},
214	{}
215};
216MODULE_DEVICE_TABLE(i2c, al3320a_id);
217
218static struct i2c_driver al3320a_driver = {
219	.driver = {
220		.name = AL3320A_DRV_NAME,
221	},
222	.probe		= al3320a_probe,
223	.remove		= al3320a_remove,
224	.id_table	= al3320a_id,
225};
226
227module_i2c_driver(al3320a_driver);
228
229MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
230MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
231MODULE_LICENSE("GPL v2");