Commit 92a81562 authored by Dan Murphy's avatar Dan Murphy Committed by Pavel Machek
Browse files

leds: lp55xx: Add multicolor framework support to lp55xx



Add multicolor framework support for the lp55xx family.

Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Acked-by: default avatarJacek Anaszewski <jacek.anaszewski@gmail.com>
Signed-off-by: default avatarDan Murphy <dmurphy@ti.com>
Signed-off-by: default avatarPavel Machek <pavel@ucw.cz>
parent c732eaf0
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -386,7 +386,8 @@ config LEDS_LP3952

config LEDS_LP55XX_COMMON
	tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
	depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
	depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
	depends on OF
	select FW_LOADER
	select FW_LOADER_USER_HELPER
	help
@@ -396,7 +397,7 @@ config LEDS_LP55XX_COMMON
config LEDS_LP5521
	tristate "LED Support for N.S. LP5521 LED driver chip"
	depends on LEDS_CLASS && I2C
	select LEDS_LP55XX_COMMON
	depends on LEDS_LP55XX_COMMON
	help
	  If you say yes here you get support for the National Semiconductor
	  LP5521 LED driver. It is 3 channel chip with programmable engines.
@@ -406,7 +407,7 @@ config LEDS_LP5521
config LEDS_LP5523
	tristate "LED Support for TI/National LP5523/55231 LED driver chip"
	depends on LEDS_CLASS && I2C
	select LEDS_LP55XX_COMMON
	depends on LEDS_LP55XX_COMMON
	help
	  If you say yes here you get support for TI/National Semiconductor
	  LP5523/55231 LED driver.
@@ -417,7 +418,7 @@ config LEDS_LP5523
config LEDS_LP5562
	tristate "LED Support for TI LP5562 LED driver chip"
	depends on LEDS_CLASS && I2C
	select LEDS_LP55XX_COMMON
	depends on LEDS_LP55XX_COMMON
	help
	  If you say yes here you get support for TI LP5562 LED driver.
	  It is 4 channels chip with programmable engines.
@@ -427,7 +428,7 @@ config LEDS_LP5562
config LEDS_LP8501
	tristate "LED Support for TI LP8501 LED driver chip"
	depends on LEDS_CLASS && I2C
	select LEDS_LP55XX_COMMON
	depends on LEDS_LP55XX_COMMON
	help
	  If you say yes here you get support for TI LP8501 LED driver.
	  It is 9 channel chip with programmable engines.
+8 −6
Original line number Diff line number Diff line
@@ -505,9 +505,16 @@ static int lp5521_probe(struct i2c_client *client,
	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
	struct device_node *np = client->dev.of_node;

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->cfg = &lp5521_cfg;

	if (!pdata) {
		if (np) {
			pdata = lp55xx_of_populate_pdata(&client->dev, np);
			pdata = lp55xx_of_populate_pdata(&client->dev, np,
							 chip);
			if (IS_ERR(pdata))
				return PTR_ERR(pdata);
		} else {
@@ -516,10 +523,6 @@ static int lp5521_probe(struct i2c_client *client,
		}
	}

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	led = devm_kcalloc(&client->dev,
			pdata->num_channels, sizeof(*led), GFP_KERNEL);
	if (!led)
@@ -527,7 +530,6 @@ static int lp5521_probe(struct i2c_client *client,

	chip->cl = client;
	chip->pdata = pdata;
	chip->cfg = &lp5521_cfg;

	mutex_init(&chip->lock);

+8 −6
Original line number Diff line number Diff line
@@ -873,9 +873,16 @@ static int lp5523_probe(struct i2c_client *client,
	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
	struct device_node *np = client->dev.of_node;

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->cfg = &lp5523_cfg;

	if (!pdata) {
		if (np) {
			pdata = lp55xx_of_populate_pdata(&client->dev, np);
			pdata = lp55xx_of_populate_pdata(&client->dev, np,
							 chip);
			if (IS_ERR(pdata))
				return PTR_ERR(pdata);
		} else {
@@ -884,10 +891,6 @@ static int lp5523_probe(struct i2c_client *client,
		}
	}

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	led = devm_kcalloc(&client->dev,
			pdata->num_channels, sizeof(*led), GFP_KERNEL);
	if (!led)
@@ -895,7 +898,6 @@ static int lp5523_probe(struct i2c_client *client,

	chip->cl = client;
	chip->pdata = pdata;
	chip->cfg = &lp5523_cfg;

	mutex_init(&chip->lock);

+8 −5
Original line number Diff line number Diff line
@@ -520,9 +520,16 @@ static int lp5562_probe(struct i2c_client *client,
	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
	struct device_node *np = client->dev.of_node;

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->cfg = &lp5562_cfg;

	if (!pdata) {
		if (np) {
			pdata = lp55xx_of_populate_pdata(&client->dev, np);
			pdata = lp55xx_of_populate_pdata(&client->dev, np,
							 chip);
			if (IS_ERR(pdata))
				return PTR_ERR(pdata);
		} else {
@@ -531,9 +538,6 @@ static int lp5562_probe(struct i2c_client *client,
		}
	}

	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	led = devm_kcalloc(&client->dev,
			pdata->num_channels, sizeof(*led), GFP_KERNEL);
@@ -542,7 +546,6 @@ static int lp5562_probe(struct i2c_client *client,

	chip->cl = client;
	chip->pdata = pdata;
	chip->cfg = &lp5562_cfg;

	mutex_init(&chip->lock);

+155 −22
Original line number Diff line number Diff line
@@ -34,6 +34,11 @@ static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
	return cdev_to_lp55xx_led(dev_get_drvdata(dev));
}

static struct lp55xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
{
	return container_of(mc_cdev, struct lp55xx_led, mc_cdev);
}

static void lp55xx_reset_device(struct lp55xx_chip *chip)
{
	struct lp55xx_device_config *cfg = chip->cfg;
@@ -129,6 +134,18 @@ static struct attribute *lp55xx_led_attrs[] = {
};
ATTRIBUTE_GROUPS(lp55xx_led);

static int lp55xx_set_mc_brightness(struct led_classdev *cdev,
				    enum led_brightness brightness)
{
	struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
	struct lp55xx_led *led = mcled_cdev_to_led(mc_dev);
	struct lp55xx_device_config *cfg = led->chip->cfg;

	led_mc_calc_color_components(&led->mc_cdev, brightness);
	return cfg->multicolor_brightness_fn(led);

}

static int lp55xx_set_brightness(struct led_classdev *cdev,
			     enum led_brightness brightness)
{
@@ -145,9 +162,12 @@ static int lp55xx_init_led(struct lp55xx_led *led,
	struct lp55xx_platform_data *pdata = chip->pdata;
	struct lp55xx_device_config *cfg = chip->cfg;
	struct device *dev = &chip->cl->dev;
	int max_channel = cfg->max_channel;
	struct mc_subled *mc_led_info;
	struct led_classdev *led_cdev;
	char name[32];
	int i, j = 0;
	int ret;
	int max_channel = cfg->max_channel;

	if (chan >= max_channel) {
		dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
@@ -157,10 +177,43 @@ static int lp55xx_init_led(struct lp55xx_led *led,
	if (pdata->led_config[chan].led_current == 0)
		return 0;

	if (pdata->led_config[chan].name) {
		led->cdev.name = pdata->led_config[chan].name;
	} else {
		snprintf(name, sizeof(name), "%s:channel%d",
			pdata->label ? : chip->cl->name, chan);
		led->cdev.name = name;
	}

	if (pdata->led_config[chan].num_colors > 1) {
		mc_led_info = devm_kcalloc(dev,
					   pdata->led_config[chan].num_colors,
					   sizeof(*mc_led_info), GFP_KERNEL);
		if (!mc_led_info)
			return -ENOMEM;

		led_cdev = &led->mc_cdev.led_cdev;
		led_cdev->name = led->cdev.name;
		led_cdev->brightness_set_blocking = lp55xx_set_mc_brightness;
		led->mc_cdev.num_colors = pdata->led_config[chan].num_colors;
		for (i = 0; i < led->mc_cdev.num_colors; i++) {
			mc_led_info[i].color_index =
				pdata->led_config[chan].color_id[i];
			mc_led_info[i].channel =
					pdata->led_config[chan].output_num[i];
			j++;
		}

		led->mc_cdev.subled_info = mc_led_info;
	} else {
		led->cdev.brightness_set_blocking = lp55xx_set_brightness;
	}

	led->cdev.groups = lp55xx_led_groups;
	led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
	led->led_current = pdata->led_config[chan].led_current;
	led->max_current = pdata->led_config[chan].max_current;
	led->chan_nr = pdata->led_config[chan].chan_nr;
	led->cdev.default_trigger = pdata->led_config[chan].default_trigger;

	if (led->chan_nr >= max_channel) {
		dev_err(dev, "Use channel numbers between 0 and %d\n",
@@ -168,18 +221,11 @@ static int lp55xx_init_led(struct lp55xx_led *led,
		return -EINVAL;
	}

	led->cdev.brightness_set_blocking = lp55xx_set_brightness;
	led->cdev.groups = lp55xx_led_groups;

	if (pdata->led_config[chan].name) {
		led->cdev.name = pdata->led_config[chan].name;
	} else {
		snprintf(name, sizeof(name), "%s:channel%d",
			pdata->label ? : chip->cl->name, chan);
		led->cdev.name = name;
	}

	if (pdata->led_config[chan].num_colors > 1)
		ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
	else
		ret = devm_led_classdev_register(dev, &led->cdev);

	if (ret) {
		dev_err(dev, "led register err: %d\n", ret);
		return ret;
@@ -515,14 +561,105 @@ void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);

static int lp55xx_parse_common_child(struct device_node *np,
				     struct lp55xx_led_config *cfg,
				     int led_number, int *chan_nr)
{
	int ret;

	of_property_read_string(np, "chan-name",
				&cfg[led_number].name);
	of_property_read_u8(np, "led-cur",
			    &cfg[led_number].led_current);
	of_property_read_u8(np, "max-cur",
			    &cfg[led_number].max_current);

	ret = of_property_read_u32(np, "reg", chan_nr);
	if (ret)
		return ret;

	if (*chan_nr < 0 || *chan_nr > cfg->max_channel)
		return -EINVAL;

	return 0;
}

static int lp55xx_parse_multi_led_child(struct device_node *child,
					 struct lp55xx_led_config *cfg,
					 int child_number, int color_number)
{
	int chan_nr, color_id, ret;

	ret = lp55xx_parse_common_child(child, cfg, child_number, &chan_nr);
	if (ret)
		return ret;

	ret = of_property_read_u32(child, "color", &color_id);
	if (ret)
		return ret;

	cfg[child_number].color_id[color_number] = color_id;
	cfg[child_number].output_num[color_number] = chan_nr;

	return 0;
}

static int lp55xx_parse_multi_led(struct device_node *np,
				  struct lp55xx_led_config *cfg,
				  int child_number)
{
	struct device_node *child;
	int num_colors = 0, ret;

	for_each_child_of_node(np, child) {
		ret = lp55xx_parse_multi_led_child(child, cfg, child_number,
						   num_colors);
		if (ret)
			return ret;
		num_colors++;
	}

	cfg[child_number].num_colors = num_colors;

	return 0;
}

static int lp55xx_parse_logical_led(struct device_node *np,
				   struct lp55xx_led_config *cfg,
				   int child_number)
{
	int led_color, ret;
	int chan_nr = 0;

	cfg[child_number].default_trigger =
		of_get_property(np, "linux,default-trigger", NULL);

	ret = of_property_read_u32(np, "color", &led_color);
	if (ret)
		return ret;

	if (led_color == LED_COLOR_ID_MULTI)
		return lp55xx_parse_multi_led(np, cfg, child_number);

	ret =  lp55xx_parse_common_child(np, cfg, child_number, &chan_nr);
	if (ret < 0)
		return ret;

	cfg[child_number].chan_nr = chan_nr;

	return ret;
}

struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
						      struct device_node *np)
						      struct device_node *np,
						      struct lp55xx_chip *chip)
{
	struct device_node *child;
	struct lp55xx_platform_data *pdata;
	struct lp55xx_led_config *cfg;
	int num_channels;
	int i = 0;
	int ret;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
@@ -540,16 +677,12 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,

	pdata->led_config = &cfg[0];
	pdata->num_channels = num_channels;
	cfg->max_channel = chip->cfg->max_channel;

	for_each_child_of_node(np, child) {
		cfg[i].chan_nr = i;

		of_property_read_string(child, "chan-name", &cfg[i].name);
		of_property_read_u8(child, "led-cur", &cfg[i].led_current);
		of_property_read_u8(child, "max-cur", &cfg[i].max_current);
		cfg[i].default_trigger =
			of_get_property(child, "linux,default-trigger", NULL);

		ret = lp55xx_parse_logical_led(child, cfg, i);
		if (ret)
			return ERR_PTR(-EINVAL);
		i++;
	}

Loading