Message ID | 20231121202835.28152-1-ddrokosov@salutedevices.com |
---|---|
Headers | show |
Series | leds: aw200xx: several driver updates | expand |
On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > HWEN is hardware control, which is used for enable/disable aw200xx chip. > It's high active, internally pulled down to GND. > > After HWEN pin set high the chip begins to load the OTP information, > which takes 200us to complete. About 200us wait time is needed for > internal oscillator startup and display SRAM initialization. After > display SRAM initialization, the registers in page 1 to page 5 can be > configured via i2c interface. > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > --- > drivers/leds/leds-aw200xx.c | 33 +++++++++++++++++++++++++++++++++ > 1 file changed, 33 insertions(+) > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > index 842a22087b16..7762b3a132ac 100644 > --- a/drivers/leds/leds-aw200xx.c > +++ b/drivers/leds/leds-aw200xx.c > @@ -10,6 +10,7 @@ > #include <linux/bitfield.h> > #include <linux/bits.h> > #include <linux/container_of.h> > +#include <linux/gpio/consumer.h> > #include <linux/i2c.h> > #include <linux/leds.h> > #include <linux/mod_devicetable.h> > @@ -116,6 +117,7 @@ struct aw200xx { > struct mutex mutex; > u32 num_leds; > u32 display_rows; > + struct gpio_desc *hwen; > struct aw200xx_led leds[] __counted_by(num_leds); > }; > > @@ -358,6 +360,25 @@ static int aw200xx_chip_check(const struct aw200xx *const chip) > return 0; > } > > +static void aw200xx_enable(const struct aw200xx *const chip) > +{ > + gpiod_set_value_cansleep(chip->hwen, 1); > + > + /* > + * After HWEN pin set high the chip begins to load the OTP information, > + * which takes 200us to complete. About 200us wait time is needed for > + * internal oscillator startup and display SRAM initialization. After > + * display SRAM initialization, the registers in page1 to page5 can be > + * configured via i2c interface. > + */ > + fsleep(400); > +} > + > +static void aw200xx_disable(const struct aw200xx *const chip) > +{ > + return gpiod_set_value_cansleep(chip->hwen, 0); > +} > + > static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) > { > struct fwnode_handle *child; > @@ -517,6 +538,14 @@ static int aw200xx_probe(struct i2c_client *client) > if (IS_ERR(chip->regmap)) > return PTR_ERR(chip->regmap); > > + chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", > + GPIOD_OUT_HIGH); > + if (IS_ERR(chip->hwen)) > + return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), > + "Cannot get enable gpio"); Nit: "GPIO" > + > + aw200xx_enable(chip); > + > ret = aw200xx_chip_check(chip); > if (ret) > return ret; > @@ -537,6 +566,9 @@ static int aw200xx_probe(struct i2c_client *client) > ret = aw200xx_chip_init(chip); > > out_unlock: > + if (ret) > + aw200xx_disable(chip); > + > mutex_unlock(&chip->mutex); > return ret; > } > @@ -546,6 +578,7 @@ static void aw200xx_remove(struct i2c_client *client) > struct aw200xx *chip = i2c_get_clientdata(client); > > aw200xx_chip_reset(chip); > + aw200xx_disable(chip); > mutex_destroy(&chip->mutex); > } > > -- > 2.36.0 >
On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > From: George Stark <gnstark@salutedevices.com> > > Get rid of device tree property "awinic,display-rows". The property > value actually means number of current switches and depends on how leds Nit: LEDs > are connected to the device. It should be calculated manually by max > used led number. In the same way it is computed automatically now. As above - I won't mention this again. > Max used led is taken from led definition subnodes. > > Signed-off-by: George Stark <gnstark@salutedevices.com> > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > --- > drivers/leds/leds-aw200xx.c | 39 +++++++++++++++++++++++++------------ > 1 file changed, 27 insertions(+), 12 deletions(-) > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > index 7762b3a132ac..4bce5e7381c0 100644 > --- a/drivers/leds/leds-aw200xx.c > +++ b/drivers/leds/leds-aw200xx.c > @@ -379,6 +379,30 @@ static void aw200xx_disable(const struct aw200xx *const chip) > return gpiod_set_value_cansleep(chip->hwen, 0); > } > > +static bool aw200xx_probe_get_display_rows(struct device *dev, struct aw200xx *chip) > +{ > + struct fwnode_handle *child; > + u32 max_source = 0; > + > + device_for_each_child_node(dev, child) { > + u32 source; > + int ret; > + > + ret = fwnode_property_read_u32(child, "reg", &source); > + if (ret || source >= chip->cdef->channels) Shouldn't the second clause fail instantly? > + continue; > + > + max_source = max(max_source, source); > + } > + > + if (!max_source) Since max_source is an integer, please use an '== 0' comparison. > + return false; > + > + chip->display_rows = max_source / chip->cdef->display_size_columns + 1; > + > + return true; > +} > + > static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) > { > struct fwnode_handle *child; > @@ -386,18 +410,9 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) > int ret; > int i; > > - ret = device_property_read_u32(dev, "awinic,display-rows", > - &chip->display_rows); > - if (ret) > - return dev_err_probe(dev, ret, > - "Failed to read 'display-rows' property\n"); > - > - if (!chip->display_rows || > - chip->display_rows > chip->cdef->display_size_rows_max) { > - return dev_err_probe(dev, ret, > - "Invalid leds display size %u\n", > - chip->display_rows); > - } > + if (!aw200xx_probe_get_display_rows(dev, chip)) Function calls in side if() statements in general is rough. Please break it out and use 'ret' as we usually do. > + return dev_err_probe(dev, -EINVAL, Make this the return value from aw200xx_probe_get_display_rows() and use 'ret' instead. > + "No valid led definitions found\n"); > > current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); > current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); > -- > 2.36.0 >
On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > From: George Stark <gnstark@salutedevices.com> > > According to datasheets of aw200xx devices software reset takes at > least 1ms so add delay after reset before issuing commands to device. Are you able to use an auto-correct tool to sharpen the grammar a little? > Signed-off-by: George Stark <gnstark@salutedevices.com> > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > --- > drivers/leds/leds-aw200xx.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > index 4bce5e7381c0..bb17e48b3e2a 100644 > --- a/drivers/leds/leds-aw200xx.c > +++ b/drivers/leds/leds-aw200xx.c > @@ -321,6 +321,9 @@ static int aw200xx_chip_reset(const struct aw200xx *const chip) > if (ret) > return ret; > > + /* according to datasheet software reset takes at least 1ms */ Please start sentences with an uppercase char. "According to the datasheet, software resets take at least 1ms" ^ ^ ^ > + fsleep(1000); > + > regcache_mark_dirty(chip->regmap); > return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); > } > -- > 2.36.0 >
On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > From: George Stark <gnstark@salutedevices.com> > > Add support for Awinic aw20108 device from the same LED drivers family. > New device supports 108 LEDs using a matrix of 12x9 outputs. > > Signed-off-by: George Stark <gnstark@salutedevices.com> > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > --- > drivers/leds/Kconfig | 14 +++++++++----- > drivers/leds/leds-aw200xx.c | 10 +++++++++- > 2 files changed, 18 insertions(+), 6 deletions(-) > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig > index 6046dfeca16f..a879628e985c 100644 > --- a/drivers/leds/Kconfig > +++ b/drivers/leds/Kconfig > @@ -95,14 +95,18 @@ config LEDS_ARIEL > Say Y to if your machine is a Dell Wyse 3020 thin client. > > config LEDS_AW200XX > - tristate "LED support for Awinic AW20036/AW20054/AW20072" > + tristate "LED support for Awinic AW20036/AW20054/AW20072/AW20108" > depends on LEDS_CLASS > depends on I2C > help > - This option enables support for the AW20036/AW20054/AW20072 LED driver. > - It is a 3x12/6x9/6x12 matrix LED driver programmed via > - an I2C interface, up to 36/54/72 LEDs or 12/18/24 RGBs, > - 3 pattern controllers for auto breathing or group dimming control. > + This option enables support for Awinic AW200XX LED controller. "for ..." THE or AN. Or put an 's' at the end of "controller". > + It is a matrix LED driver programmed via an I2C interface. Devices have > + a set of individually controlled leds and support 3 pattern controllers LEDs > + for auto breathing or group dimming control. Supported devices: > + - AW20036 (3x12) 36 LEDs > + - AW20054 (6x9) 54 LEDs > + - AW20072 (6x12) 72 LEDs > + - AW20108 (9x12) 108 LEDs > > To compile this driver as a module, choose M here: the module > will be called leds-aw200xx. > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > index c48aa11fd411..4b5036360887 100644 > --- a/drivers/leds/leds-aw200xx.c > +++ b/drivers/leds/leds-aw200xx.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0 > /* > - * Awinic AW20036/AW20054/AW20072 LED driver > + * Awinic AW20036/AW20054/AW20072/AW20108 LED driver > * > * Copyright (c) 2023, SberDevices. All Rights Reserved. > * > @@ -620,10 +620,17 @@ static const struct aw200xx_chipdef aw20072_cdef = { > .display_size_columns = 12, > }; > > +static const struct aw200xx_chipdef aw20108_cdef = { > + .channels = 108, > + .display_size_rows_max = 9, > + .display_size_columns = 12, > +}; > + > static const struct i2c_device_id aw200xx_id[] = { > { "aw20036" }, > { "aw20054" }, > { "aw20072" }, > + { "aw20108" }, > {} > }; > MODULE_DEVICE_TABLE(i2c, aw200xx_id); > @@ -632,6 +639,7 @@ static const struct of_device_id aw200xx_match_table[] = { > { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, > { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, > { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, > + { .compatible = "awinic,aw20108", .data = &aw20108_cdef, }, > {} > }; > MODULE_DEVICE_TABLE(of, aw200xx_match_table); > -- > 2.36.0 >
Hello Lee, Thank you for the detailed review! Please find my answer below. On Thu, Nov 23, 2023 at 04:38:16PM +0000, Lee Jones wrote: > On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > > > From: George Stark <gnstark@salutedevices.com> > > > > According to datasheets of aw200xx devices software reset takes at > > least 1ms so add delay after reset before issuing commands to device. > > Are you able to use an auto-correct tool to sharpen the grammar a little? > > > Signed-off-by: George Stark <gnstark@salutedevices.com> > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > > Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > > --- > > drivers/leds/leds-aw200xx.c | 3 +++ > > 1 file changed, 3 insertions(+) > > > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > > index 4bce5e7381c0..bb17e48b3e2a 100644 > > --- a/drivers/leds/leds-aw200xx.c > > +++ b/drivers/leds/leds-aw200xx.c > > @@ -321,6 +321,9 @@ static int aw200xx_chip_reset(const struct aw200xx *const chip) > > if (ret) > > return ret; > > > > + /* according to datasheet software reset takes at least 1ms */ > > Please start sentences with an uppercase char. > > "According to the datasheet, software resets take at least 1ms" > ^ ^ ^ > Here it's only one 'software reset' mentioned. > > + fsleep(1000); > > + > > regcache_mark_dirty(chip->regmap); > > return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); > > } > > -- > > 2.36.0 > > > > -- > Lee Jones [李琼斯]
On Thu, Nov 23, 2023 at 04:32:52PM +0000, Lee Jones wrote: > On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > > > From: George Stark <gnstark@salutedevices.com> > > > > Get rid of device tree property "awinic,display-rows". The property > > value actually means number of current switches and depends on how leds > > Nit: LEDs > > > are connected to the device. It should be calculated manually by max > > used led number. In the same way it is computed automatically now. > > As above - I won't mention this again. > > > Max used led is taken from led definition subnodes. > > > > Signed-off-by: George Stark <gnstark@salutedevices.com> > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > > --- > > drivers/leds/leds-aw200xx.c | 39 +++++++++++++++++++++++++------------ > > 1 file changed, 27 insertions(+), 12 deletions(-) > > > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > > index 7762b3a132ac..4bce5e7381c0 100644 > > --- a/drivers/leds/leds-aw200xx.c > > +++ b/drivers/leds/leds-aw200xx.c > > @@ -379,6 +379,30 @@ static void aw200xx_disable(const struct aw200xx *const chip) > > return gpiod_set_value_cansleep(chip->hwen, 0); > > } > > > > +static bool aw200xx_probe_get_display_rows(struct device *dev, struct aw200xx *chip) > > +{ > > + struct fwnode_handle *child; > > + u32 max_source = 0; > > + > > + device_for_each_child_node(dev, child) { > > + u32 source; > > + int ret; > > + > > + ret = fwnode_property_read_u32(child, "reg", &source); > > + if (ret || source >= chip->cdef->channels) > > Shouldn't the second clause fail instantly? > We already have such logic in the aw200xx_probe_fw() function, which skips the LED node with the wrong reg value too. Furthermore, we have strict reg constraints in the dt-bindings parts (in the current patch series), so we assume that the DT developer will not create an LED with the wrong reg value. > > + continue; > > + > > + max_source = max(max_source, source); > > + } > > + > > + if (!max_source) > > Since max_source is an integer, please use an '== 0' comparison. > Okay > > + return false; > > + > > + chip->display_rows = max_source / chip->cdef->display_size_columns + 1; > > + > > + return true; > > +} > > + > > static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) > > { > > struct fwnode_handle *child; > > @@ -386,18 +410,9 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) > > int ret; > > int i; > > > > - ret = device_property_read_u32(dev, "awinic,display-rows", > > - &chip->display_rows); > > - if (ret) > > - return dev_err_probe(dev, ret, > > - "Failed to read 'display-rows' property\n"); > > - > > - if (!chip->display_rows || > > - chip->display_rows > chip->cdef->display_size_rows_max) { > > - return dev_err_probe(dev, ret, > > - "Invalid leds display size %u\n", > > - chip->display_rows); > > - } > > + if (!aw200xx_probe_get_display_rows(dev, chip)) > > Function calls in side if() statements in general is rough. > > Please break it out and use 'ret' as we usually do. > > > + return dev_err_probe(dev, -EINVAL, > > Make this the return value from aw200xx_probe_get_display_rows() and use > 'ret' instead. > No problem, I'll prepare a new version. > > + "No valid led definitions found\n"); > > > > current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); > > current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); > > -- > > 2.36.0 > > > > -- > Lee Jones [李琼斯]
On Fri, 24 Nov 2023, Dmitry Rokosov wrote: > On Thu, Nov 23, 2023 at 04:32:52PM +0000, Lee Jones wrote: > > On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > > > > > From: George Stark <gnstark@salutedevices.com> > > > > > > Get rid of device tree property "awinic,display-rows". The property > > > value actually means number of current switches and depends on how leds > > > > Nit: LEDs > > > > > are connected to the device. It should be calculated manually by max > > > used led number. In the same way it is computed automatically now. > > > > As above - I won't mention this again. > > > > > Max used led is taken from led definition subnodes. > > > > > > Signed-off-by: George Stark <gnstark@salutedevices.com> > > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > > > --- > > > drivers/leds/leds-aw200xx.c | 39 +++++++++++++++++++++++++------------ > > > 1 file changed, 27 insertions(+), 12 deletions(-) > > > > > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > > > index 7762b3a132ac..4bce5e7381c0 100644 > > > --- a/drivers/leds/leds-aw200xx.c > > > +++ b/drivers/leds/leds-aw200xx.c > > > @@ -379,6 +379,30 @@ static void aw200xx_disable(const struct aw200xx *const chip) > > > return gpiod_set_value_cansleep(chip->hwen, 0); > > > } > > > > > > +static bool aw200xx_probe_get_display_rows(struct device *dev, struct aw200xx *chip) > > > +{ > > > + struct fwnode_handle *child; > > > + u32 max_source = 0; > > > + > > > + device_for_each_child_node(dev, child) { > > > + u32 source; > > > + int ret; > > > + > > > + ret = fwnode_property_read_u32(child, "reg", &source); > > > + if (ret || source >= chip->cdef->channels) > > > > Shouldn't the second clause fail instantly? > > > > We already have such logic in the aw200xx_probe_fw() function, which > skips the LED node with the wrong reg value too. Furthermore, we have > strict reg constraints in the dt-bindings parts (in the current patch > series), so we assume that the DT developer will not create an LED with > the wrong reg value. Why is it being checked again then?
On Fri, 24 Nov 2023, Dmitry Rokosov wrote: > Hello Lee, > > Thank you for the detailed review! > > Please find my answer below. > > On Thu, Nov 23, 2023 at 04:38:16PM +0000, Lee Jones wrote: > > On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > > > > > From: George Stark <gnstark@salutedevices.com> > > > > > > According to datasheets of aw200xx devices software reset takes at > > > least 1ms so add delay after reset before issuing commands to device. > > > > Are you able to use an auto-correct tool to sharpen the grammar a little? > > > > > Signed-off-by: George Stark <gnstark@salutedevices.com> > > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > > > Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > > > --- > > > drivers/leds/leds-aw200xx.c | 3 +++ > > > 1 file changed, 3 insertions(+) > > > > > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > > > index 4bce5e7381c0..bb17e48b3e2a 100644 > > > --- a/drivers/leds/leds-aw200xx.c > > > +++ b/drivers/leds/leds-aw200xx.c > > > @@ -321,6 +321,9 @@ static int aw200xx_chip_reset(const struct aw200xx *const chip) > > > if (ret) > > > return ret; > > > > > > + /* according to datasheet software reset takes at least 1ms */ > > > > Please start sentences with an uppercase char. > > > > "According to the datasheet, software resets take at least 1ms" > > ^ ^ ^ > > > > Here it's only one 'software reset' mentioned. That's okay. The English is still 100% valid, since this describes them happening more than once; say per week, per year, per lifetime of the H/W or some such. If you *really* want to describe one reset happening once, ever, then you can say "a software reset takes". > > > + fsleep(1000); > > > + > > > regcache_mark_dirty(chip->regmap); > > > return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); > > > }
Lee, Thank you for the quick reply! On Mon, Nov 27, 2023 at 08:57:55AM +0000, Lee Jones wrote: > On Fri, 24 Nov 2023, Dmitry Rokosov wrote: > > > On Thu, Nov 23, 2023 at 04:32:52PM +0000, Lee Jones wrote: > > > On Tue, 21 Nov 2023, Dmitry Rokosov wrote: > > > > > > > From: George Stark <gnstark@salutedevices.com> > > > > > > > > Get rid of device tree property "awinic,display-rows". The property > > > > value actually means number of current switches and depends on how leds > > > > > > Nit: LEDs > > > > > > > are connected to the device. It should be calculated manually by max > > > > used led number. In the same way it is computed automatically now. > > > > > > As above - I won't mention this again. > > > > > > > Max used led is taken from led definition subnodes. > > > > > > > > Signed-off-by: George Stark <gnstark@salutedevices.com> > > > > Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com> > > > > --- > > > > drivers/leds/leds-aw200xx.c | 39 +++++++++++++++++++++++++------------ > > > > 1 file changed, 27 insertions(+), 12 deletions(-) > > > > > > > > diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c > > > > index 7762b3a132ac..4bce5e7381c0 100644 > > > > --- a/drivers/leds/leds-aw200xx.c > > > > +++ b/drivers/leds/leds-aw200xx.c > > > > @@ -379,6 +379,30 @@ static void aw200xx_disable(const struct aw200xx *const chip) > > > > return gpiod_set_value_cansleep(chip->hwen, 0); > > > > } > > > > > > > > +static bool aw200xx_probe_get_display_rows(struct device *dev, struct aw200xx *chip) > > > > +{ > > > > + struct fwnode_handle *child; > > > > + u32 max_source = 0; > > > > + > > > > + device_for_each_child_node(dev, child) { > > > > + u32 source; > > > > + int ret; > > > > + > > > > + ret = fwnode_property_read_u32(child, "reg", &source); > > > > + if (ret || source >= chip->cdef->channels) > > > > > > Shouldn't the second clause fail instantly? > > > > > > > We already have such logic in the aw200xx_probe_fw() function, which > > skips the LED node with the wrong reg value too. Furthermore, we have > > strict reg constraints in the dt-bindings parts (in the current patch > > series), so we assume that the DT developer will not create an LED with > > the wrong reg value. > > Why is it being checked again then? Hmmm, aw200xx_probe_get_display_rows() executes before the old implementation... So we need to check it again. Do you think it should be reworked? I've already sent a new patchset. Could you please take a look at the other fixes?