Re: [PATCH v6 19/19] auxdisplay: ht16k33: Add LED support

From: Geert Uytterhoeven
Date: Fri Oct 01 2021 - 11:51:32 EST


Hoi Robin,

On Thu, Sep 30, 2021 at 12:57 PM Robin van der Gracht <robin@xxxxxxxxxxx> wrote:
> On 2021-09-14 16:38, Geert Uytterhoeven wrote:
> > Instantiate a single LED based on the "led" subnode in DT.
> > This allows the user to control display brightness and blinking (backed
> > by hardware support) through the LED class API and triggers, and exposes
> > the display color. The LED will be named
> > "auxdisplay:<color>:<function>".
> >
> > When running in dot-matrix mode and if no "led" subnode is found, the
> > driver falls back to the traditional backlight mode, to preserve
> > backwards compatibility.
> >
> > Signed-off-by: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>
> > Reviewed-by: Marek Behún <kabel@xxxxxxxxxx>
> > ---
> > v6:
> > - Add Reviewed-by,
> > - Reorder operations in ht16k33_led_probe() to ease future conversion
> > to device properties,

> > --- a/drivers/auxdisplay/ht16k33.c
> > +++ b/drivers/auxdisplay/ht16k33.c
> > @@ -425,6 +477,35 @@ static void ht16k33_seg14_update(struct work_struct
> > *work)
> > i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
> > }
> >
> > +static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
> > + unsigned int brightness)
> > +{
> > + struct led_init_data init_data = {};
> > + struct device_node *node;
> > + int err;
> > +
> > + /* The LED is optional */
> > + node = of_get_child_by_name(dev->of_node, "led");
> > + if (!node)
> > + return 0;
> > +
> > + init_data.fwnode = of_fwnode_handle(node);
> > + init_data.devicename = "auxdisplay";
> > + init_data.devname_mandatory = true;
> > +
> > + led->brightness_set_blocking = ht16k33_brightness_set_blocking;
> > + led->blink_set = ht16k33_blink_set;
> > + led->flags = LED_CORE_SUSPENDRESUME;
> > + led->brightness = brightness;
> > + led->max_brightness = MAX_BRIGHTNESS;
>
> What do you think about adding a default trigger and making it 'backlight'?
>
> led->default_trigger = "blacklight";
>
> Or as an alternative, suggesting linux,default-trigger = "backlight" in the
> docs? Since the led class won't respond to blank events by just making it's
> function LED_FUNCTION_BACKLIGHT.
>
> led {
> function = LED_FUNCTION_BACKLIGHT;
> color = <LED_COLOR_ID_GREEN>;
> linux,default-trigger = "backlight";
> };

The latter makes perfect sense to me. Will do.

> I noticed blanking is broken. The backlight device (or LED device with
> backlight trigger) doens't get notified when the framebuffer is blanked since
> the driver doesn't implement fb_blank.
>
> Right now:
>
> echo 1 > /sys/class/graphics/fb0/blank
> |
> sh: write error: Invalid argument
>
> Due to:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/video/fbdev/core/fbmem.c?h=v5.15-rc3#n1078

That's a pre-existing problem, righ? ;-)

> Something like this fixes it.
>
> diff --git a/drivers/auxdisplay/ht16k33.c b/drivers/auxdisplay/ht16k33.c
> index 89ee5b4b3dfc..0883d5252c81 100644
> --- a/drivers/auxdisplay/ht16k33.c
> +++ b/drivers/auxdisplay/ht16k33.c
> @@ -346,6 +346,15 @@ static int ht16k33_mmap(struct fb_info *info, struct
> vm_area_struct *vma)
> return vm_map_pages_zero(vma, &pages, 1);
> }
>
> +/*
> + * Blank events will be passed to the backlight device (or the LED device if
> + * it's trigger is 'backlight') when we return 0 here.
> + */
> +static int ht16k33_blank(int blank, struct fb_info *info)
> +{
> + return 0;
> +}
> +
> static const struct fb_ops ht16k33_fb_ops = {
> .owner = THIS_MODULE,
> .fb_read = fb_sys_read,
> @@ -354,6 +363,7 @@ static const struct fb_ops ht16k33_fb_ops = {
> .fb_copyarea = sys_copyarea,
> .fb_imageblit = sys_imageblit,
> .fb_mmap = ht16k33_mmap,
> + .fb_blank = ht16k33_blank,
> };
>
> /*
>
> Feel free to include (something like) this in the patch stack.

Thanks, will do.

> > +
> > + err = devm_led_classdev_register_ext(dev, led, &init_data);
> > + if (err)
> > + dev_err(dev, "Failed to register LED\n");
>
> You might want to call ht16k33_brightness_set(priv, brightness) here to get a
> know value into the display setup register (0x80).
>
> Right now if I enable hardware blinking and (soft)reboot my board it keeps on
> blinking even after a re-probe.

I don't have that issue.
Aha, ht16k33_seg_probe() calls ht16k33_brightness_set(), but
ht16k33_fbdev_probe() doesn't. The latter should do that, too,
when not using backwards compatibility mode.

> > @@ -575,7 +660,7 @@ static int ht16k33_seg_probe(struct device *dev, struct
> > ht16k33_priv *priv,
> > struct ht16k33_seg *seg = &priv->seg;
> > int err;
> >
> > - err = ht16k33_brightness_set(priv, MAX_BRIGHTNESS);
> > + err = ht16k33_brightness_set(priv, brightness);
>
> This looks like a bugfix for patch 17, maybe move this change there?

Indeed. Bad rebase. Will move.

Thanks a lot for your comments!

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds