Re: [PATCH 3/3] iio: light: bd27008: Support BD27010 RGB

From: Jonathan Cameron
Date: Sat Jun 17 2023 - 15:57:28 EST


On Tue, 13 Jun 2023 13:20:26 +0300
Matti Vaittinen <mazziesaccount@xxxxxxxxx> wrote:

> The ROHM BU27010 is an RGBC sensor with a flickering detection FIFO. The
> RGBC+IR sensor functionality is largely similar to what the BU27008 has.
> There are some notable things though:
> - gain setting is once again new and exotic. Now, there is 6bit gain
> setting where 4 of the bits are common to all channels and 2 bits
> can be configured separately for each channel. The BU27010 has
> similar "1X on other channels vs 2X on IR when selector is 0x0"
> gain design as BU27008 had. So, we use same gain setting policy for
> BU27010 as we did for BU27008 - driver sets same gain selector for all
> channels but shows the gains separately for all channels so users
> can (at least in theory) detect this 1X vs 2X madness...
> - BU27010 has suffled all the control register bitfields to new
> addresses and bit positions while still keeping the register naming
> same.
> - Some more power/reset control is added.
> - FIFO for "flickering detection" is added.
>
> The control register suffling made this slightly nasty. Still, it is
> easier for maintenance perspective to add the BU27010 support in BU27008
> driver because - even though the bit positions/addresses were changed -
> most of the driver structure can be re-used. Writing own driver for
> BU27010 would mean plenty of duplicate code albeit a tad more clarity.
>
> The flickering FIFO is not supported by the driver.
>
> Add BU27010 RGBC+IR support to rohm-bu27008 driver.
>
> Signed-off-by: Matti Vaittinen <mazziesaccount@xxxxxxxxx>
>

Resulting code looks more or less fine, but there is stuff in here that
belongs in previous patch - so send a v2 with the refactors all done
there and just support for the new part in here.

Thanks,

Jonathan

...


> +
> static int bu27008_chip_init(struct bu27008_data *data);
> static int bu27008_write_gain_sel(struct bu27008_data *data, int sel);
> static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel);
>
> +static int bu27010_chip_init(struct bu27008_data *data);
> +static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel);
> +static int bu27010_write_gain_sel(struct bu27008_data *data, int sel);
> +
> +static const struct bu27_chip_data bu27010_chip = {
> + .name = "bu27010",
> + .chip_init = bu27010_chip_init,
> + .get_gain_sel = bu27010_get_gain_sel,
> + .write_gain_sel = bu27010_write_gain_sel,
> + .scale1x = BU27010_SCALE_1X,
> + .part_id = BU27010_ID,
> + .regmap_cfg = &bu27010_regmap,
> + .drdy_en_reg = BU27010_REG_MODE_CONTROL4,
> + .drdy_en_mask = BU27010_DRDY_EN,
> + .valid_reg = BU27010_REG_MODE_CONTROL5,
> + .meas_en_reg = BU27010_REG_MODE_CONTROL5,
> + .meas_en_mask = BU27010_MASK_MEAS_EN,
> + .chan_sel_reg = BU27008_REG_MODE_CONTROL1,
> + .chan_sel_mask = BU27010_MASK_CHAN_SEL,
> + .int_time_mask = BU27010_MASK_MEAS_MODE,
> + .gains = &bu27010_gains[0],
> + .num_gains = ARRAY_SIZE(bu27010_gains),
> + .gains_ir = &bu27010_gains_ir[0],
> + .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir),
> + .itimes = &bu27010_itimes[0],
> + .num_itimes = ARRAY_SIZE(bu27010_itimes),
> +};
> +
> static const struct bu27_chip_data bu27008_chip = {
> .name = "bu27008",
> .chip_init = bu27008_chip_init,
> @@ -332,6 +529,8 @@ static const struct bu27_chip_data bu27008_chip = {
> .num_gains = ARRAY_SIZE(bu27008_gains),
> .gains_ir = &bu27008_gains_ir[0],
> .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir),
> + .itimes = &bu27008_itimes[0],
> + .num_itimes = ARRAY_SIZE(bu27008_itimes),
> };
>
> #define BU27008_MAX_VALID_RESULT_WAIT_US 50000
> @@ -358,6 +557,31 @@ static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
> return ret;
> }
>
> +static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
> +{
> + int ret;
> +
> + /*
> + * We always "lock" the gain selectors for all channels to prevent
> + * unsupported configs. It does not matter which channel is used
> + * we can just return selector from any of them.
> + */
> + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
> + if (!ret) {
if (ret)
return ret;

....

> + int tmp;
> +
> + *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel);
> +
> + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp);
> + if (ret)
> + return ret;
> +
> + *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN);
> + }
> +
> + return ret;
> +}
> +
> static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
> {
> int ret;
> @@ -388,7 +612,7 @@ static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int
> {
> int ret, sel;
>
> - ret = bu27008_get_gain_sel(data, &sel);
> + ret = data->cd->get_gain_sel(data, &sel);
Again, belongs in previous patch.
> if (ret)
> return ret;
>
> @@ -403,6 +627,35 @@ static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int
> return 0;
> }
>
> +static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
> +{
> + unsigned int regval;
> + int ret;
> +
> + /*
> + * Gain 'selector' is composed of two registers. Selector is 6bit value,
> + * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and
> + * two low bits being the channel specific gain in MODE_CONTROL2.
> + *
> + * Let's take the 4 high bits of whole 6 bit selector, and prepare
> + * the MODE_CONTROL1 value (RGBC gain part).
> + */
> + regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2));
> +
> + ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
> + BU27010_MASK_RGBC_GAIN, regval);

ret not checked.

> + /*
> + * Two low two bits must be set for all 4 channels in the
> + * MODE_CONTROL2 register. Copy these two bits for all channels.
> + */
> + regval = sel & GENMASK(1, 0);

FIELD_PREP and all fields explicitly set.

> + regval = regval | regval >> 2 | regval >> 4 | regval >> 6;
> +
> + ret = regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval);
return regmap_write...

> +
> + return ret;
> +}
> +
> static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
> {
> int regval;
> @@ -452,7 +705,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain)
> if (ret < 0)
> return ret;
>
> - return bu27008_write_gain_sel(data, ret);
> + return data->cd->write_gain_sel(data, ret);
Another one that wants to be in the refactor patch - not here.

> }
>
> static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
> @@ -460,13 +713,15 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
> int ret, val;
>
> ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val);
> + if (ret)
> + return ret;
>
> val &= data->cd->int_time_mask;
> val >>= ffs(data->cd->int_time_mask) - 1;
>
> *sel = val;
>
> - return ret;
> + return 0;

This looks unrelated / fix for previous patch?

> }
>
> static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
> @@ -759,7 +1014,7 @@ static int bu27008_set_scale(struct bu27008_data *data,
> goto unlock_out;
>
> }
> - ret = bu27008_write_gain_sel(data, gain_sel);
> + ret = data->cd->write_gain_sel(data, gain_sel);

As below - make all these sort of refactoring changes in previous patch.

>
> unlock_out:
> mutex_unlock(&data->mutex);
> @@ -867,6 +1122,55 @@ static const struct iio_info bu27008_info = {
> .validate_trigger = iio_validate_own_trigger,
> };
>
> +static int bu27010_chip_init(struct bu27008_data *data)
> +{
> + int ret;
> +
> + /* Reset the IC to initial power-off state */

I'd argue the code is clear enough the comment adds little.

> + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
> + BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET);
> + if (ret)
> + return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
> +
> + msleep(1);
> +
> + /* Power ON*/
> + ret = regmap_write_bits(data->regmap, BU27010_REG_POWER,
> + BU27010_MASK_POWER, BU27010_MASK_POWER);
> + if (ret)
> + return dev_err_probe(data->dev, ret, "Sensor power-on failed\n");
> +
> + msleep(1);
> +
> + /* Release blocks from reset */
> + ret = regmap_write_bits(data->regmap, BU27010_REG_RESET,
> + BU27010_MASK_RESET, BU27010_RESET_RELEASE);
> + if (ret)
> + return dev_err_probe(data->dev, ret, "Sensor powering failed\n");
> +
> + msleep(1);
> +
> + /*
> + * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ
> + * enabling is not a bit mask where individual IRQs could be enabled but
> + * a field which values are:
> + * 00 => IRQs disabled
> + * 01 => Data-ready (RGBC/IR)
> + * 10 => Data-ready (flicker)
> + * 11 => Flicker FIFO
> + *

That's nasty :)

> + * So, only one IRQ can be enabled at a time and enabling for example
> + * flicker FIFO would automagically disable data-ready IRQ.
> + *
> + * Currently the driver does not support the flicker. Hence, we can
> + * just treat the RGBC data-ready as single bit which can be enabled /
> + * disabled. This works for as long as the second bit in the field
> + * stays zero. Here we ensure it gets zeroed.
> + */
> + return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4,
> + BU27010_IRQ_DIS_ALL);
> +}
> +
> static int bu27008_chip_init(struct bu27008_data *data)
> {
> int ret;
> @@ -1068,14 +1372,14 @@ static int bu27008_probe(struct i2c_client *i2c)
> dev_warn(dev, "unknown device 0x%x\n", part_id);
>
> ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains,
> - data->cd->num_gains, bu27008_itimes,
> - ARRAY_SIZE(bu27008_itimes), &data->gts);
> + data->cd->num_gains, data->cd->itimes,
> + data->cd->num_itimes, &data->gts);
> if (ret)
> return ret;
>
> ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir,
> - data->cd->num_gains_ir, bu27008_itimes,
> - ARRAY_SIZE(bu27008_itimes), &data->gts_ir);
> + data->cd->num_gains_ir, data->cd->itimes,
> + data->cd->num_itimes, &data->gts_ir);

I'd expect the changes to make things part specific to all be in previous patch
- not mixed across that one and this one.


> if (ret)
> return ret;