Re: [PATCH 2/2] leds: tps6131x: add support for Texas Instruments TPS6131X flash LED driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 14 Mar 2025, Matthias Fend wrote:

> Hi Lee,
> 
> thanks a lot for your feedback!
> 
> Am 10.03.2025 um 15:49 schrieb Lee Jones:
> > On Fri, 28 Feb 2025, Matthias Fend wrote:
> > 
> > > The TPS61310/TPS61311 is a flash LED driver with I2C interface. Its power
> > > stage is capable of supplying a maximum total current of roughly 1500mA.
> > > The TPS6131x provides three constant-current sinks, capable of sinking up
> > > to 2 × 400mA (LED1 and LED3) and 800mA (LED2) in flash mode. In torch mode
> > > each sink (LED1, LED2, LED3) supports currents up to 175mA.
> > > 
> > > Signed-off-by: Matthias Fend <matthias.fend@xxxxxxxxx>
> > > ---
> > >   MAINTAINERS                        |   7 +
> > >   drivers/leds/flash/Kconfig         |  11 +
> > >   drivers/leds/flash/Makefile        |   1 +
> > >   drivers/leds/flash/leds-tps6131x.c | 798 +++++++++++++++++++++++++++++++++++++
> > >   4 files changed, 817 insertions(+)

[...]

> > > +static int tps6131x_led_class_setup(struct tps6131x *tps6131x)
> > > +{
> > > +	struct led_classdev *led_cdev;
> > > +	struct led_flash_setting *setting;
> > > +	struct led_init_data init_data = {};
> > > +	static const struct tps6131x_timer_config *timer_config;
> > > +	int ret;
> > > +
> > > +	tps6131x->fled_cdev.ops = &flash_ops;
> > > +
> > > +	setting = &tps6131x->fled_cdev.timeout;
> > > +	timer_config = tps6131x_find_closest_timer_config(0);
> > > +	setting->min = timer_config->time_us;
> > > +	setting->max = tps6131x->max_timeout_us;
> > > +	setting->step = 1; /* Only some specific time periods are supported. No fixed step size. */
> > > +	setting->val = setting->min;
> > > +
> > > +	setting = &tps6131x->fled_cdev.brightness;
> > > +	setting->min = tps6131x->step_flash_current_ma;
> > > +	setting->max = tps6131x->max_flash_current_ma;
> > > +	setting->step = tps6131x->step_flash_current_ma;
> > > +	setting->val = setting->min;
> > > +
> > > +	led_cdev = &tps6131x->fled_cdev.led_cdev;
> > > +	led_cdev->brightness_set_blocking = tps6131x_brightness_set;
> > > +	led_cdev->max_brightness = tps6131x->max_torch_current_ma;
> > > +	led_cdev->flags |= LED_DEV_CAP_FLASH;
> > > +
> > > +	init_data.fwnode = tps6131x->led_node;
> > > +	init_data.devicename = NULL;
> > > +	init_data.default_label = NULL;
> > > +	init_data.devname_mandatory = false;
> > > +
> > > +	ret = devm_led_classdev_flash_register_ext(&tps6131x->client->dev, &tps6131x->fled_cdev,
> > > +						   &init_data);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> > 
> > Not keen on #ifery in C files.
> > 
> > Can you use is_defined() and return early instead?
> > 
> > I see that there is a precedent for this already. :(
> 
> Me neither, but since it is done this way in about 9 out of 10 flash
> controllers, I wanted to continue doing it consistently.
> But since the required v4l2_flash_* functions are also available as dummies
> if this option is not activated, I could do it like this:
> 
> if (!IS_BUILTIN(CONFIG_V4L2_FLASH_LED_CLASS))
>   return 0;
> 
> Would you prefer this solution?

I would, yes.  Thank you.

> > > +static int tps6131x_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, bool enable)
> > > +{
> > > +	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> > > +	struct tps6131x *tps6131x = fled_cdev_to_tps6131x(fled_cdev);
> > > +
> > > +	guard(mutex)(&tps6131x->lock);
> > > +
> > /> +	return tps6131x_set_mode(tps6131x, enable ? TPS6131X_MODE_FLASH : TPS6131X_MODE_SHUTDOWN,
> > > +				 false);
> > > +}
> > > +
> > > +static const struct v4l2_flash_ops tps6131x_v4l2_flash_ops = {
> > > +	.external_strobe_set = tps6131x_flash_external_strobe_set,
> > > +};
> > > +
> > > +static int tps6131x_v4l2_setup(struct tps6131x *tps6131x)
> > > +{
> > > +	struct v4l2_flash_config v4l2_cfg = { 0 };
> > > +	struct led_flash_setting *intensity = &v4l2_cfg.intensity;
> > > +
> > > +	intensity->min = tps6131x->step_torch_current_ma;
> > > +	intensity->max = tps6131x->max_torch_current_ma;
> > > +	intensity->step = tps6131x->step_torch_current_ma;
> > > +	intensity->val = intensity->min;
> > > +
> > > +	strscpy(v4l2_cfg.dev_name, tps6131x->fled_cdev.led_cdev.dev->kobj.name,
> > 
> > tps6131x->client->dev?
> 
> Do you mean the name should be taken from the I2C device?
> The current name, for example, is 'white:flash-0', while the I2C device name
> would be '4-0033'. So I think the current version is appropriate, don't you
> think?

No, I'm implying that:

  tps6131x->client->dev == tps6131x->fled_cdev.led_cdev.dev

... and that the former is shorter / neater.

> > > +		sizeof(v4l2_cfg.dev_name));
> > > +
> > > +	v4l2_cfg.has_external_strobe = true;
> > > +	v4l2_cfg.flash_faults = LED_FAULT_TIMEOUT | LED_FAULT_OVER_TEMPERATURE |
> > > +				LED_FAULT_SHORT_CIRCUIT | LED_FAULT_UNDER_VOLTAGE |
> > > +				LED_FAULT_LED_OVER_TEMPERATURE;
> > > +
> > > +	tps6131x->v4l2_flash = v4l2_flash_init(&tps6131x->client->dev, tps6131x->led_node,
> > > +					       &tps6131x->fled_cdev, &tps6131x_v4l2_flash_ops,
> > > +					       &v4l2_cfg);
> > > +	if (IS_ERR(tps6131x->v4l2_flash)) {
> > > +		dev_err(&tps6131x->client->dev, "v4l2_flash_init failed\n");
> > 
> > "Failed to initialise V4L2 flash LED" ?
> 
> ACK
> 
> > 
> > > +		return PTR_ERR(tps6131x->v4l2_flash);
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +#else
> > > +
> > > +static int tps6131x_v4l2_setup(struct tps6131x *tps6131x)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > > +#endif
> > > +
> > > +static int tps6131x_probe(struct i2c_client *client)
> > > +{
> > > +	struct tps6131x *tps6131x;
> > > +	int ret;
> > > +
> > > +	tps6131x = devm_kzalloc(&client->dev, sizeof(*tps6131x), GFP_KERNEL);
> > > +	if (!tps6131x)
> > > +		return -ENOMEM;
> > > +
> > > +	tps6131x->client = client;
> > 
> > What are you planning on using client for?
> > 
> > > +	i2c_set_clientdata(client, tps6131x);
> > 
> > How are you going to _get_ this without client?
> 
> Maybe I didn't understand the question correctly, but in tps6131x_remove() I
> get the device data via the client.

Right, which uses 'client' to obtain it, so you don't need to save 'client'.

> > Why not save dev and reduce the amount of dereferencing levels required.
> 
> Absolutely. Good idea.
> 
> > 
> > > +	mutex_init(&tps6131x->lock);
> > > +	INIT_DELAYED_WORK(&tps6131x->torch_refresh_work, tps6131x_torch_refresh_handler);
> > > +
> > > +	ret = tps6131x_parse_node(tps6131x);
> > > +	if (ret)
> > > +		return -ENODEV;
> > > +
> > > +	tps6131x->regmap = devm_regmap_init_i2c(client, &tps6131x_regmap);
> > > +	if (IS_ERR(tps6131x->regmap)) {
> > > +		ret = PTR_ERR(tps6131x->regmap);
> > > +		dev_err(&client->dev, "Failed to allocate register map\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	tps6131x->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
> > > +	ret = tps6131x_reset_chip(tps6131x);
> > > +	if (ret)
> > > +		return dev_err_probe(&client->dev, ret, "Failed to reset LED controller\n");
> > 
> > How do you manage the optional part?
> 
> If there is a reset line, then tps6131x_reset_chip() uses it to reset the
> chip. If there is none, the software reset (via an I2C register) is used.
> Therefore the reset pin can be optional.

Right, but didn't you just fail if one is not provided, or is that
accounted for in tps6131x_reset_chip()?

-- 
Lee Jones [李琼斯]




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux