Re: [PATCH] max9286: Improve mux-state readbility

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

 



Hi Jacopo,

On 29/11/2019 11:35, Jacopo Mondi wrote:
> Hi Kieran,
> 
> On Fri, Nov 29, 2019 at 11:13:00AM +0000, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> Thanks for reviewing this :D
>>
>> On 29/11/2019 09:14, Jacopo Mondi wrote:
>>> Hi Kieran,
>>>
>>> On Thu, Nov 28, 2019 at 04:27:06PM +0000, Kieran Bingham wrote:
>>>> The MAX9286 implements an I2C mux which we maintain in an open state
>>>> while we are streaming from the cameras.
>>>>
>>>> The development code for the MAX9286 uses an integer value with
>>>> arbitrary state values to control these state transitions. This is
>>>> highlighted ith a FIXME and is difficult to interpret the meaning of the
>>>
>>> s/ith/with
>>
>> ack.
>>
>>>
>>>> values 0, 1, 2.
>>>>
>>>> Introduce an enum to declare the intent of each state, and comment the
>>>> states accordingly.
>>>>
>>>> This state transition is only needed for the multi-channel support, and
>>>> will not be included in the single-channel max9286 posting.
>>>>
>>>> Signed-off-by: Kieran Bingham <kieran.bingham@xxxxxxxxxxxxxxxx>
>>>> ---
>>>>  drivers/media/i2c/max9286.c | 63 +++++++++++++++++++++++--------------
>>>>  1 file changed, 40 insertions(+), 23 deletions(-)
>>>>
>>>> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
>>>> index eed00ff1dee4..a9c3e7411bda 100644
>>>> --- a/drivers/media/i2c/max9286.c
>>>> +++ b/drivers/media/i2c/max9286.c
>>>> @@ -144,7 +144,7 @@ struct max9286_priv {
>>>>  	struct media_pad pads[MAX9286_N_PADS];
>>>>  	struct regulator *regulator;
>>>>  	bool poc_enabled;
>>>> -	int streaming;
>>>> +	int mux_state;
>>>>
>>>>  	struct i2c_mux_core *mux;
>>>>  	unsigned int mux_channel;
>>>> @@ -221,16 +221,39 @@ static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
>>>>   * I2C Multiplexer
>>>>   */
>>>>
>>>> +enum max9286_i2c_mux_state {
>>>
>>> Let the bikeshedding begin here
>>>
>>>> +	/*
>>>> +	 * The I2C Mux will enable only a single channel (both forward, and
>>>
>>> s/Mux/mux
>>
>> Ack.
>>
>>>
>>>> +	 * reverse) at a time, and to reduce I2C bus bandwidth, it will only
>>>> +	 * reconfigure the channel when a write is requested to a different
>>>> +	 * channel.
>>>
>>> I won't here explain what a mux channel select does
>>
>> I was trying to explain what /this state/ does ...
>> I can streamline this.
>>
>>>
>>>> +	 */
>>>> +	MAX9286_I2C_MUX_STATE_CHANNEL = 0,
>>>
>>> To me, this should be the initial state of the mux, where all channels
>>> are closed.
>>>
>>
>> I actually started with a _CLOSED here, but I determined that _CLOSED
>> was redundant, as _CLOSED is simply _CHANNEL with a channel id of -1.
>>
>> And when in _CLOSED, the next 'write' should be of type _CHANNEL, as in
>> it should configure only a single channel, and open only that channel.
>>
>>
>>> The state machine to me should look like:
>>>
>>>         init() -> i2c_mux_close() -> mux_state = CLOSED;
>>>         all transaction selects/deselect a single channel>
>>>         s_stream(1) -> mux_state = REQUEST_OPEN
>>
>> I also had a REQUEST_OPEN, but I deemed it also to be a bit redundant,
>> as the external (not mux components) which adapt the mux_state should
>> only care about two states - Either it's open or on channel.
>>
>> I almost wonder if I should put in a helper function to make mux_state
>> private to the i2c_mux functions ... but I think that's overkill.
>>
>>
>>>         first transaction opens all channels -> mux_state = OPEN
>>>         all successive transactions with (mux_state = OPEN) are nop
>>>
>>>         s_stream(0) -> i2c_mux_close() -> mux_state = CLOSED
>>>         all transaction selects/deselect a single channel until next s_stream(1)
>>>
>>> For this state I propose MAX9286_I2C_MUX_STATE_CLOSED
>>>
>>>> +
>>>> +	/*
>>>> +	 * The I2C mux will be configured with all ports open. All I2C writes
>>>> +	 * will be transmitted to all remote I2C devices, and where multiple
>>>> +	 * devices have the same address, the write will be received by all of
>>>> +	 * them.
>>>> +	 */
>>>> +	MAX9286_I2C_MUX_STATE_OPEN,
>>>
>>> I propose MAX9286_I2C_MUX_STATE_REQUEST_OPEN
>>>
>>>> +
>>>> +	/*
>>>> +	 * The I2C mux is configured with all ports open.
>>>> +	 *
>>>> +	 * No reconfiguration of the I2C mux channel select is required.
>>>> +	 */
>>>> +	MAX9286_I2C_MUX_STATE_DISABLE_SELECT,
>>>
>>> I propose MAX9286_I2C_MUX_STATE_OPEN
>>
>> I had this as 'MUX_STATE_OPENED', but it felt like what it was really
>> doing was just 'disabling select' operations, hence I renamed it.
>>
>> It's also 'internal' and I wouldn't expect the no nmax9286_i2c_mux_
>> functions to reference this enum value.
>>
>> My further reasoning to keep this as DISABLE_SELECT is that ifsomeone
>> set this state (not that anyone ever should), when the mux was closed -
>> it would remain closed.
>>
> 
> Ok, let's see what other thinks... anyway, that's your code so if you
> feel strong about this, I'm fine with what you have

Ok,


>>> Could all these be shorten to MAX9286_MUX_.... ?
>>
>> I think so, I was just following the function naming.
>>
>>
>>>> +};
>>>> +
>>>>  static int max9286_i2c_mux_close(struct max9286_priv *priv)
>>>>  {
>>>> -	/* FIXME: See note in max9286_i2c_mux_select() */
>>>> -	if (priv->streaming)
>>>> -		return 0;
>>>
>>> I don't get why we had this one here. Do you agree it was not
>>> necessary ? I guess so, since you dropped it...
>>
>>
>> Exactly, I couldn't see any reason for this to be here, and I also
>> couldn't see it being used, as _close It doesn't get called after an
>> s_stream operation as far as I can tell. It's only currently closed
>> during _probe and _init.
>>
>> However, if at some other point, someone wanted to call _close, I would
>> expect it to close all of the channels.
>>
>>>
>>>>  	/*
>>>>  	 * Ensure that both the forward and reverse channel are disabled on the
>>>> -	 * mux, and that the channel ID is invalidated to ensure we reconfigure
>>>> -	 * on the next select call.
>>>> +	 * mux, and that the channel state and ID is invalidated to ensure we
>>>> +	 * reconfigure on the next max9286_i2c_mux_select() call.
>>>>  	 */
>>>> +	priv->mux_state = MAX9286_I2C_MUX_STATE_CHANNEL;
>>
>> Note here that we set the mux_channel to -1, and thus the state is set
>> to _CHANNEL as discussed above, because on the next operation - we
>> expect either the write to go to the specific channel, /or/ if someone
>> has set MAX9286_I2C_MUX_STATE_OPEN explicitly the select call will send
>> it to all channels.
>>
>> Those are the only two options as far as I can tell, so adding extra
>> states of '_CLOSED' seems redundant, and adds unecessary complexity to me.
> 
> _CLOSED was meant to replace _CHANNEL in my proposal.
> No worries though

Ah I see, as in just a different name. My concern with _CLOSED though is
that we do not close the channel after each write, and _CLOSED could
potentially give the impression that we do ...

>>>>  	priv->mux_channel = -1;
>>>>  	max9286_write(priv, 0x0a, 0x00);
>>>>  	usleep_range(3000, 5000);
>>>> @@ -242,23 +265,19 @@ static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
>>>>  {
>>>>  	struct max9286_priv *priv = i2c_mux_priv(muxc);
>>>>
>>>> -	/*
>>>> -	 * FIXME: This state keeping is a hack and do the job. It should
>>>> -	 * be should be reworked. One option to consider is that once all
>>>> -	 * cameras are programmed the mux selection logic should be disabled
>>>> -	 * and all all reverse and forward channels enable all the time.
>>>> -	 *
>>>> -	 * In any case this logic with a int that have two states should be
>>>> -	 * reworked!
>>>> -	 */
>>>> -	if (priv->streaming == 1) {
>>>> -		max9286_write(priv, 0x0a, 0xff);
>>>> -		priv->streaming = 2;
>>>> +	/* channel select is disabled when configured in the opened state. */
>>>
>>> Channel
>>
>> Ack.
>>
>>
>>>
>>>> +	if (priv->mux_state == MAX9286_I2C_MUX_STATE_DISABLE_SELECT)
>>>>  		return 0;
>>>> -	} else if (priv->streaming == 2) {
>>>> +
>>>> +	if (priv->mux_state == MAX9286_I2C_MUX_STATE_OPEN) {
>>>> +		/* Open all channels on the MAX9286 */
>>>> +		max9286_write(priv, 0x0a, 0xff);
>>>> +		priv->mux_state = MAX9286_I2C_MUX_STATE_DISABLE_SELECT;
>>>
>>> Shouldn't we sleep 3-5ms when changing the forward/reverse channel
>>> configuration ?
>>
>> Based on the comments below, we probably do - and this has been missing.
>>
> 
> Yes, was not there in first place
> 
>>>
>>>>  		return 0;
>>>>  	}
>>>>
>>>> +	/* Handling for MAX9286_I2C_MUX_STATE_CHANNEL */
>>>> +
>>>
>>> Empty line
>>> Do you need this comment ?
>>
>> I wanted to clarify that of the 3 states, the lines above explicitly
>> handle the MAX9286_I2C_MUX_STATE_DISABLE_SELECT,  and the
>> MAX9286_I2C_MUX_STATE_OPEN states, so it's left 'implicit' that the code
>> below is MAX9286_I2C_MUX_STATE_CHANNEL.
>>
>> I added the comment to make it more explicit.
>>
>> I didn't want to move all the code into a switch statement which would
>> be my only alternative otherwise I think.
>>
>>
>>
>>
>>>>  	if (priv->mux_channel == chan)
>>>>  		return 0;
>>>>
>>>> @@ -441,8 +460,7 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
>>>>  	int ret;
>>>>
>>>>  	if (enable) {
>>>> -		/* FIXME: See note in max9286_i2c_mux_select() */
>>>> -		priv->streaming = 1;
>>>> +		priv->mux_state = MAX9286_I2C_MUX_STATE_OPEN;
>>>>
>>>>  		/* Start all cameras. */
>>>>  		for_each_source(priv, source) {
>>>> @@ -490,8 +508,7 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
>>>>  		for_each_source(priv, source)
>>>>  			v4l2_subdev_call(source->sd, video, s_stream, 0);
>>>>
>>>> -		/* FIXME: See note in max9286_i2c_mux_select() */
>>>> -		priv->streaming = 0;
>>>> +		priv->mux_state = MAX9286_I2C_MUX_STATE_CHANNEL;
>>>
>>> Shouldn't we call i2c_mux_close() here, and let it close all channels
>>> and reset the mux state ? If the mux is not closed by writing
>>> 0x0a = 0x00 but the state is here reset to STATE_CHANNEL all
>>> successive i2c_mux_select() call will re-open channel-by-channel a mux
>>> that is already open, won't they ?
>>
>> I have not modified the actual state transitions from your original
>> implementation, so I think you're the expert here. This is your code,
>> just renamed.
> 
> I know, this was a question not strictly related to your changes
> 
>>
>> (Ok perhaps that's not true, I removed the state check at
>> max9286_i2c_mux_close above)
>>
>> So - thinking it through ... Yes, you are right - this will leave all
>> the channels open. This is the behaviour we had before this patch though
>> so I wonder if this could explain any of the issues we've had.
>>
>> I don't /think/ so - because
>>   A) we probably reset the board a lot,
>>   B) I don't think we've had issues starting and stopping streams, but
>>      we haven't done enough testing there.
> 
> Also, it won't hurt to have the mux open all the time after we have
> configured addresses properly. It just does not feel 'right'

Indeed, and while it may not hurt, it leads to a point where we get the
following conditions :

(O=Open, _=Closed)

1 2 3 4
O _ _ _ // Write to 1
_ O _ _ // Write to 2
_ _ O _ // Write to 3
_ _ _ O // Write to 4

O O O O // StreamOn   // MAX9286_I2C_MUX_STATE_OPEN
O O O O // StreamOff  // MAX9286_I2C_MUX_STATE_CHANNEL

O O O O // Write to 1
_ O O O // Write to 2
_ _ O O // Write to 3
_ _ _ O // Write to 4

O _ _ _ // Write to 1

O O O O // StreamOn   // MAX9286_I2C_MUX_STATE_OPEN
O O O O // StreamOff  // MAX9286_I2C_MUX_STATE_CHANNEL

... Continues

I will indeed fix this by calling mux_close when we stop streaming.
At least then we will have a consistent state as we expect.


In fact from all that I think that means we would likely be better to have:


/* Opens all channels on the MUX and disables individual select lines */
static int max9286_i2c_mux_open(struct max9286_priv *priv)
{
	/* Open all channels on the MAX9286 */
	max9286_write(priv, 0x0a, 0xff);
	usleep_range(3000, 5000);

	priv->mux_state = MAX9286_I2C_MUX_STATE_DISABLE_SELECT;

	return 0;
}

static int max9286_i2c_mux_close(struct max9286_priv *priv)
{
	priv->mux_state = MAX9286_I2C_MUX_STATE_CHANNEL;
	priv->mux_channel = -1;
	max9286_write(priv, 0x0a, 0x00);
	usleep_range(3000, 5000);
	return 0;
}

static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
	struct max9286_priv *priv = i2c_mux_priv(muxc);

	/* channel select is disabled when configured in the opend state. */
	if (priv->mux_state == MAX9286_I2C_MUX_STATE_DISABLE_SELECT)
		return 0;

	/* Handling for MAX9286_I2C_MUX_STATE_CHANNEL */

	if (priv->mux_channel == chan)
		return 0;

	...
}

Which makes the intentions much clearer, and stops other sections of the
driver from directly modifying the 'private' mux_state.


>>> Overall, I very much agree we need this patch, so thanks for having
>>> addressed it!
>>
>> No problem, I needed to go through to understand what the three states
>> (0, 1, 2) were, so this is what I came up with.
>>
>> Thanks for your comments, I'll await any further comments to the above
>> then do a respin before collapsing it into the multi-stream support patch.
>>
>> Or do you think we should keep things as separate patches on top of the
>> 'single' camera support? I don't want to end up in a GMSL==100 patches
>> to track case again if we can avoid it .., So I'd like to keep it down
>> to three managable topics :
>>
>>  Patch 1) A single camera support, (should apply and run on linux-media)
>>  Patch 2) Support for multiple streams (requires v4l2-mux)
>>  Patch 3) Support for 2 MAX9286 on one bus (not upstreamable currently)
> 
> I very much agree with this plan!

Great, I'm on the right track then.

> Thanks
>    j
> 

-- 
Regards
--
Kieran



[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux