leds-pca9633.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
/*
 * Copyright 2011 bct electronic GmbH
 *
 * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
 *
 * Based on leds-pca955x.c
 *
 * This file is subject to the terms and conditions of version 2 of
 * the GNU General Public License.  See the file COPYING in the main
 * directory of this archive for more details.
 *
 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
 *
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/slab.h>

/* LED select registers determine the source that drives LED outputs */
#define PCA9633_LED_OFF		0x0	/* LED driver off */
#define PCA9633_LED_ON		0x1	/* LED driver on */
#define PCA9633_LED_PWM		0x2	/* Controlled through PWM */
#define PCA9633_LED_GRP_PWM	0x3	/* Controlled through PWM/GRPPWM */

#define PCA9633_MODE1		0x00
#define PCA9633_MODE2		0x01
#define PCA9633_PWM_BASE	0x02
#define PCA9633_LEDOUT		0x08

static const struct i2c_device_id pca9633_id[] = {
	{ "pca9633", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, pca9633_id);

struct pca9633_led {
	struct i2c_client *client;
	struct work_struct work;
	enum led_brightness brightness;
	struct led_classdev led_cdev;
	int led_num; /* 0 .. 3 potentially */
	char name[32];
};

static void pca9633_led_work(struct work_struct *work)
{
	struct pca9633_led *pca9633 = container_of(work,
		struct pca9633_led, work);
	u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
	int shift = 2 * pca9633->led_num;
	u8 mask = 0x3 << shift;

	switch (pca9633->brightness) {
	case LED_FULL:
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			(ledout & ~mask) | (PCA9633_LED_ON << shift));
		break;
	case LED_OFF:
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			ledout & ~mask);
		break;
	default:
		i2c_smbus_write_byte_data(pca9633->client,
			PCA9633_PWM_BASE + pca9633->led_num,
			pca9633->brightness);
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			(ledout & ~mask) | (PCA9633_LED_PWM << shift));
		break;
	}
}

static void pca9633_led_set(struct led_classdev *led_cdev,
	enum led_brightness value)
{
	struct pca9633_led *pca9633;

	pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);

	pca9633->brightness = value;

	/*
	 * Must use workqueue for the actual I/O since I2C operations
	 * can sleep.
	 */
	schedule_work(&pca9633->work);
}

static int __devinit pca9633_probe(struct i2c_client *client,
					const struct i2c_device_id *id)
{
	struct pca9633_led *pca9633;
	struct led_platform_data *pdata;
	int i, err;

	pdata = client->dev.platform_data;

	if (pdata) {
		if (pdata->num_leds <= 0 || pdata->num_leds > 4) {
			dev_err(&client->dev, "board info must claim at most 4 LEDs");
			return -EINVAL;
		}
	}

111
	pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	if (!pca9633)
		return -ENOMEM;

	i2c_set_clientdata(client, pca9633);

	for (i = 0; i < 4; i++) {
		pca9633[i].client = client;
		pca9633[i].led_num = i;

		/* Platform data can specify LED names and default triggers */
		if (pdata && i < pdata->num_leds) {
			if (pdata->leds[i].name)
				snprintf(pca9633[i].name,
					 sizeof(pca9633[i].name), "pca9633:%s",
					 pdata->leds[i].name);
			if (pdata->leds[i].default_trigger)
				pca9633[i].led_cdev.default_trigger =
					pdata->leds[i].default_trigger;
		} else {
			snprintf(pca9633[i].name, sizeof(pca9633[i].name),
				 "pca9633:%d", i);
		}

		pca9633[i].led_cdev.name = pca9633[i].name;
		pca9633[i].led_cdev.brightness_set = pca9633_led_set;

		INIT_WORK(&pca9633[i].work, pca9633_led_work);

		err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
		if (err < 0)
			goto exit;
	}

	/* Disable LED all-call address and set normal mode */
	i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);

	/* Turn off LEDs */
	i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);

	return 0;

exit:
	while (i--) {
		led_classdev_unregister(&pca9633[i].led_cdev);
		cancel_work_sync(&pca9633[i].work);
	}

	return err;
}

static int __devexit pca9633_remove(struct i2c_client *client)
{
	struct pca9633_led *pca9633 = i2c_get_clientdata(client);
	int i;

	for (i = 0; i < 4; i++) {
		led_classdev_unregister(&pca9633[i].led_cdev);
		cancel_work_sync(&pca9633[i].work);
	}

	return 0;
}

static struct i2c_driver pca9633_driver = {
	.driver = {
		.name	= "leds-pca9633",
		.owner	= THIS_MODULE,
	},
	.probe	= pca9633_probe,
	.remove	= __devexit_p(pca9633_remove),
	.id_table = pca9633_id,
};

module_i2c_driver(pca9633_driver);

MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
MODULE_DESCRIPTION("PCA9633 LED driver");
MODULE_LICENSE("GPL v2");