Re: [PATCH v3 4/5] iio: light: ROHM BU27008 color sensor

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

 



On 5/1/23 17:20, Jonathan Cameron wrote:
> On Wed, 26 Apr 2023 11:08:17 +0300
> Matti Vaittinen <mazziesaccount@xxxxxxxxx> wrote:
> 
>> The ROHM BU27008 is a sensor with 5 photodiodes (red, green, blue, clear
>> and IR) with four configurable channels. Red and green being always
>> available and two out of the rest three (blue, clear, IR) can be
>> selected to be simultaneously measured. Typical application is adjusting
>> LCD backlight of TVs, mobile phones and tablet PCs.
>>
>> Add initial support for the ROHM BU27008 color sensor.
>>   - raw_read() of RGB and clear channels
>>   - triggered buffer w/ DRDY interrtupt
>>
>> Signed-off-by: Matti Vaittinen <mazziesaccount@xxxxxxxxx>
> 
> Hi Matti,
> 
> Mostly trivial stuff, but some confusion has occurred with respect to the
> two interrupts involve in an IIO trigger.  Specifically the pollfunc stuff
> occurs on the downward side of the irqchip that hides on the consumer side
> of an IIO trigger) and is set up by devm_iio_trigered_buffer_setup() not
> of the interrupt that calls iio_trigger_poll[_nested]()
> 

> 
>> +
>> +static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val,
>> +					  int val2, int *gain_sel)
>> +{
>> +	/* Could not support new scale with existing int-time */
> 
> I'd move that above the function and change it to
> 	/* Called if the new scale could not be supported with existing int-time */
> Down here it is not clear that this applies to the whole funciton.
> 
> 
>> +	int i, ret, new_time_sel;
>> +
>> +	for (i = 0; i < data->gts.num_itime; i++) {
>> +		new_time_sel = data->gts.itime_table[i].sel;
>> +		ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
>> +					new_time_sel, val, val2 * 1000, gain_sel);
>> +		if (!ret)
>> +			break;
>> +	}
>> +	if (i == data->gts.num_itime) {
>> +		dev_err(data->dev, "Can't support scale %u %u\n", val,
>> +			val2);
> 
> Line wrapping inconsistent.  I like short lines with appropriate flexibility where
> longer ones are more readable. However, I am fairly sure this one fits under 80
> chars as a single line.

Thanks! Both the comment and the line-wrapping were just left like this 
when I pulled this part of a functionality into this new function. Well 
spotted!

> 
>> +
>> +		return -EINVAL;
>> +	}
>> +
>> +	return bu27008_set_int_time_sel(data, new_time_sel);
>> +}
>> +
>> +static int bu27008_set_scale(struct bu27008_data *data,
>> +			     struct iio_chan_spec const *chan,
>> +			     int val, int val2)
>> +{
>> +	int ret, gain_sel, time_sel;
>> +
>> +	if (chan->scan_index == BU27008_IR)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&data->mutex);
>> +
>> +	ret = bu27008_get_int_time_sel(data, &time_sel);
>> +	if (ret < 0)
>> +		goto unlock_out;
>> +
>> +	ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel,
>> +						val, val2 * 1000, &gain_sel);
>> +	if (ret)
>> +		ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel);
> 
> Obviously it is code that doesn't make any functional difference, but I'd prefer to see
> 	if (ret) {
> 		ret = bu27....
> 		if (ret)
> 			goto unlock_out;
> 	}
> 	ret = bu27008_write_gain_sel();
> 
> so that each error path is out of line, but the good path is the linear flow.

... Yuck! The difference of tastes ... ;)
Well, not worth fighting I guess.

> 
>> +
>> +	if (!ret)
>> +		ret = bu27008_write_gain_sel(data, gain_sel);
>> +
>> +unlock_out:
>> +	mutex_unlock(&data->mutex);
>> +
>> +	return ret;
>> +}
> 
> 
>> +static irqreturn_t bu27008_irq_thread_handler(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct bu27008_data *data = iio_priv(idev);
>> +
>> +	iio_trigger_poll_nested(data->trig);
> 
> See below but this is what alerted me to something unusual.
> It never makes sense to have iio_trigger_poll_nested() called unless
> there is a check on whether it should be called!  If there isn't
> iio_trigger_poll() in the top half is the right thing to do. >
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
> 
> 
>> +static int bu27008_probe(struct i2c_client *i2c)
>> +{
> 
> ...
> 
>> +
>> +	if (i2c->irq) {
>> +		ret = devm_iio_triggered_buffer_setup(dev, idev,
>> +						      &iio_pollfunc_store_time,
>> +						      bu27008_trigger_handler,
>> +						      &bu27008_buffer_ops);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret,
>> +				     "iio_triggered_buffer_setup_ext FAIL\n");
>> +
>> +		itrig = devm_iio_trigger_alloc(dev, "%sdata-rdy-dev%d",
>> +					       idev->name, iio_device_id(idev));
>> +		if (!itrig)
>> +			return -ENOMEM;
>> +
>> +		data->trig = itrig;
>> +
>> +		itrig->ops = &bu27008_trigger_ops;
>> +		iio_trigger_set_drvdata(itrig, data);
>> +
>> +		name = devm_kasprintf(dev, GFP_KERNEL, "%s-bu27008",
>> +				      dev_name(dev));
>> +
>> +		ret = devm_request_threaded_irq(dev, i2c->irq,
>> +						iio_pollfunc_store_time,
> 
> This is on the wrong irq. 

Seems like I have some homework to do :)

Right. I now see I pass the iio_pollfunc_store_time() as top half for 
both the "real IRQ" generated by the device (here), as well as a 
top-half for the devm_iio_triggered_buffer_setup(). Ideally I like the 
idea of taking the timestamp in the top half for the device-generated 
IRQ as it is closer the moment HW did acquire the sample - but it really 
would make no difference here (even if I did it correctly).

  iio_pollfunc_store_time is used with the trigger not
> here.  Basically what happens is the caller of iio_poll_trigger() fires the input
> to a software irq chip that then signals all the of the downstream irqs (which
> are the individual consumers of the triggers).  If that's triggered from the
> top half / non threaded bit of the interrupt the iio_pollfunc_store_time()
> will be called in that non threaded context before the individual threads
> for the trigger consumer are started.

Oh. So, you mean the iio_pollfunc_store_time() is automatically called 
already before kicking the SW-IRQ? So we don't need it in 
devm_iio_triggered_buffer_setup() anymore?

> If there is nothing to do in the actual interrupt as it's a data ready
> only signal, then you should just call iio_trigger_poll() in the top half and
> use devm_request_irq() only as there is no thread in this interrupt (though
> there is one for the interrupt below the software interrupt chip).

I haven't tested this yet so please ignore me if I am writing nonsense - 
but... The BU27008 will keep the IRQ line asserted until a register is 
read. We can't read the register form HW-IRQ so we need to keep the IRQ 
disabled until the threaded trigger handler is ran. With the setup we 
have here, the IRQF_ONESHOT, took care of this. I assume that changing 
to call the iio_poll_trigger() from top-half means I need to explicitly 
disable the IRQ and re-enable it at the end of the trigger thread after 
reading the register which debounces the IRQ line?

> 
> 
>> +						&bu27008_irq_thread_handler,
>> +						IRQF_ONESHOT, name, idev->pollfunc);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret,
>> +					     "Could not request IRQ\n");
>> +
>> +
>> +		ret = devm_iio_trigger_register(dev, itrig);
>> +		if (ret)
>> +			return dev_err_probe(dev, ret,
>> +					     "Trigger registration failed\n");
>> +	} else {
>> +		dev_warn(dev, "No IRQ configured\n");
> 
> Why is it a warning?  Either driver works without an IRQ, or it doesn't.
> dev_dbg() or dev_info() at most.

Some of it works. Well, maybe I'll change it to tell that device works 
in raw_read only mode.

Thanks again for the help!

Yours,
	-- Matti

-- 
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland

~~ When things go utterly wrong vim users can always type :help! ~~





[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux