Re: [PATCH v5 2/4] leds: Add driver for Qualcomm LPG

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

 



On Tue, Oct 20, 2020 at 7:29 AM Bjorn Andersson
<bjorn.andersson@xxxxxxxxxx> wrote:
> On Sun 18 Oct 15:12 CDT 2020, Andy Shevchenko wrote:
> > On Sat, Oct 17, 2020 at 8:41 AM Bjorn Andersson
> > <bjorn.andersson@xxxxxxxxxx> wrote:

...

> > > +static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern,
> > > +                        size_t len, unsigned int *lo_idx, unsigned int *hi_idx)
> > > +{
> > > +       unsigned int idx;
> > > +       u8 val[2];
> >
> > __be16 val;
> >
> > > +       int i;
> > > +
> > > +       /* Hardware does not behave when LO_IDX == HI_IDX */
> > > +       if (len == 1)
> > > +               return -EINVAL;
> > > +
> > > +       idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size,
> > > +                                        0, len, 0);
> > > +       if (idx >= lpg->lut_size)
> > > +               return -ENOMEM;
> > > +
> > > +       for (i = 0; i < len; i++) {
> > > +               val[0] = pattern[i].brightness & 0xff;
> > > +               val[1] = pattern[i].brightness >> 8;
> >
> > cpu_to_be16();
> >
>
> I like it, but isn't that a le16?

Oh, yes.

> > > +               regmap_bulk_write(lpg->map,
> > > +                                 lpg->lut_base + LPG_LUT_REG(idx + i), val, 2);
> > > +       }
> > > +
> > > +       bitmap_set(lpg->lut_bitmap, idx, len);
> > > +
> > > +       *lo_idx = idx;
> > > +       *hi_idx = idx + len - 1;
> > > +
> > > +       return 0;
> > > +}

...

> > > +               period_n = (period_us * NSEC_PER_USEC) >> 6;
> > > +               n = 6;
> > > +       } else {
> > > +               period_n = (period_us >> 9) * NSEC_PER_USEC;
> > > +               n = 9;

> > Why inconsistency in branches? Can you rather derive n and calculate
> > only once like
> >
> >            period_n = (period_us >> n) * NSEC_PER_USEC;
> >
> > ?
>
> I inherited this piece from the downstream driver and I assume that the
> purpose was to avoid loss of precision. I will review this and if
> nothing else it seems like I would be able to cast period_us to more
> bits, do the multiply and then shift - in both cases.

Understood. Yes, please check if precision doesn't suffer and update
accordingly.

...

> > > +static void lpg_calc_duty(struct lpg_channel *chan, unsigned int duty_us)
> > > +{
> > > +       unsigned long max = (1 << chan->pwm_size) - 1;
> >
> > BIT() ?

Actually if you don't use BIT() here (or U suffix) it is UB for pwm_size == 31.

> >
> > > +       unsigned long val;
> > > +
> > > +       /* Figure out pwm_value with overflow handling */
> >
> > > +       if (duty_us < 1 << (sizeof(val) * 8 - chan->pwm_size))
> >
> > BITS_PER_TYPE, but actually BITS_PER_LONG here.
> >
> > BIT(BITS_PER_LONG - ...)
> >
>
> Again, this seems to just be a question of avoiding overflow of the 32
> bit duty_us. I'll double check the math here as well.

Can pwm_size be equal to 0?

> > > +               val = (duty_us << chan->pwm_size) / chan->period_us;
> > > +       else
> > > +               val = duty_us / (chan->period_us >> chan->pwm_size);
> > > +
> > > +       if (val > max)
> > > +               val = max;
> > > +
> > > +       chan->pwm_value = val;
> > > +}

...

> > > +static int lpg_blink_set(struct lpg_led *led,
> > > +                        unsigned long delay_on, unsigned long delay_off)
> > > +{
> > > +       struct lpg_channel *chan;
> > > +       unsigned int period_us;
> > > +       unsigned int duty_us;
> > > +       int i;
> > > +
> > > +       if (!delay_on && !delay_off) {
> > > +               delay_on = 500;
> > > +               delay_off = 500;
> > > +       }
> >
> > Homegrown duty cycle?
> > I mean, why simply not to pass the duty cycle in percentage in the first place?
>
> Can you explain what you're saying here.

Why not to use duty cycle (in %) and period (in us) as a parameter to
the function directly?

> > > +       duty_us = delay_on * USEC_PER_MSEC;
> > > +       period_us = (delay_on + delay_off) * USEC_PER_MSEC;
> > > +
> > > +       for (i = 0; i < led->num_channels; i++) {
> > > +               chan = led->channels[i];
> > > +
> > > +               lpg_calc_freq(chan, period_us);
> > > +               lpg_calc_duty(chan, duty_us);
> > > +
> > > +               chan->enabled = true;
> > > +               chan->ramp_enabled = false;
> > > +
> > > +               lpg_apply(chan);
> > > +       }
> > > +
> > > +       return 0;
> > > +}

...

> > Can you rather create a generic one under lib/ or start include/linux/math.h ?

> Forgot about this, but I've seen one on LKML, will find it and work on
> getting that accepted.

Note, I have submitted the patch that splits out math.h from kernel.h
(it's in Andrew's quilt and in Linux Next as of today), you may send a
follow up patch that adds this functionality.

...

> > > +       ret = of_property_read_u32(np, "color", &color);
> > > +       if (ret < 0 && ret != -EINVAL)
> >
> > This check is fishy. Either you have optional property or not, in the
> > latter case return any error code.
> >
>
> There's three possible outcomes here:
> 1) We found _one_ integer in the property, color is assigned and 0 is
> returned.

I didn't get this. Doesn't your YAML schema say that it must be a
single integer?

> 2) We found no property named "color", -EINVAL is returned without color
> being modified.
> 3) We found a property but it wasn't a single u32 value so a negative
> error (not EINVAL) is returned.
>
> > > +               return ret;
> > > +
> > > +       chan->color = color;
> >
> > So, it may be -EINVAL?!
> >
>
> So color will either be the value or the property color, or if omitted
> LED_COLOR_ID_GREEN.

If property is optional, we do simple

ret = of_read_property_...(&x);
if (ret)
 x = default_value;

Otherwise simple
ret = ...
if (ret)
  return ret;

is sufficient.

What you have done is a little bit unusual.

...

> > > +       ret = of_property_read_u32_array(np, "qcom,dtest", dtest, 2);
> > > +       if (ret < 0 && ret != -EINVAL) {
> > > +               dev_err(lpg->dev, "malformed qcom,dtest of %pOFn\n", np);
> > > +               return ret;
> > > +       } else if (!ret) {
> > > +               chan->dtest_line = dtest[0];
> > > +               chan->dtest_value = dtest[1];
> > > +       }
> >
> > Ditto.
> >
>
> We're in !ret and as such dtest is initialized.

As above.

...

> > > +       ret = of_property_read_u32(np, "color", &color);
> > > +       if (ret < 0 && ret != -EINVAL)
> > > +               return ret;
> >
> > Ditto.
> >
>
> As above, if no property color is specified, color remains 0 here which
> is not LED_COLOR_ID_MULTI and this is a single channel LED without its
> color specified.

As above.

-- 
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux