[PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical

Andrzej Hajda a.hajda at samsung.com
Wed Oct 15 04:22:26 PDT 2014


On 10/15/2014 01:11 PM, Thierry Reding wrote:
> On Wed, Oct 15, 2014 at 01:01:16PM +0200, Andrzej Hajda wrote:
>> On 10/14/2014 04:16 PM, Thierry Reding wrote:
>>> On Tue, Oct 14, 2014 at 03:53:26PM +0200, Andrzej Hajda wrote:
>>>> On 10/14/2014 01:29 PM, Thierry Reding wrote:
>>>>> On Tue, Oct 14, 2014 at 01:25:44PM +0200, Andrzej Hajda wrote:
>>>>>> On 10/14/2014 12:57 PM, Thierry Reding wrote:
>>>>>>> On Tue, Oct 14, 2014 at 12:38:15PM +0200, Andrzej Hajda wrote:
>>>>>>>> On 10/14/2014 11:44 AM, Thierry Reding wrote:
>>>>>>>>> On Tue, Oct 14, 2014 at 10:00:32AM +0200, Andrzej Hajda wrote:
>>>>>>>>>> On 10/13/2014 12:16 PM, Thierry Reding wrote:
>>>>>>>>>>> From: Thierry Reding <treding at nvidia.com>
>>>>>>>>>>>
>>>>>>>>>>> Currently the mipi_dsi_dcs_write() function requires the DCS command
>>>>>>>>>>> byte to be embedded within the write buffer whereas mipi_dsi_dcs_read()
>>>>>>>>>>> has a separate parameter. Make them more symmetrical by adding an extra
>>>>>>>>>>> command parameter to mipi_dsi_dcs_write().
>>>>>>>>>>>
>>>>>>>>>>> The S6E8AA0 driver relies on the old asymmetric API and there's concern
>>>>>>>>>>> that moving to the new API may be less efficient. Provide a new function
>>>>>>>>>>> with the old semantics for those cases and make the S6E8AA0 driver use
>>>>>>>>>>> it instead.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Thierry Reding <treding at nvidia.com>
>>>>>>>>>>> ---
>>>>>>>>>>> Changes in v2:
>>>>>>>>>>> - provide mipi_dsi_dcs_write_buffer() for backwards compatibility
>>>>>>>>>>>
>>>>>>>>>>>  drivers/gpu/drm/drm_mipi_dsi.c        | 127 +++++++++++++++++++++++++++++-----
>>>>>>>>>>>  drivers/gpu/drm/panel/panel-s6e8aa0.c |   2 +-
>>>>>>>>>>>  include/drm/drm_mipi_dsi.h            |   6 +-
>>>>>>>>>>>  3 files changed, 114 insertions(+), 21 deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
>>>>>>>>>>> index eb6dfe52cab2..1702ffd07986 100644
>>>>>>>>>>> --- a/drivers/gpu/drm/drm_mipi_dsi.c
>>>>>>>>>>> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
>>>>>>>>>>> @@ -199,33 +199,120 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi)
>>>>>>>>>>>  EXPORT_SYMBOL(mipi_dsi_detach);
>>>>>>>>>>>  
>>>>>>>>>>>  /**
>>>>>>>>>>> - * mipi_dsi_dcs_write - send DCS write command
>>>>>>>>>>> - * @dsi: DSI device
>>>>>>>>>>> - * @data: pointer to the command followed by parameters
>>>>>>>>>>> - * @len: length of @data
>>>>>>>>>>> + * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
>>>>>>>>>>> + * @dsi: DSI peripheral device
>>>>>>>>>>> + * @data: buffer containing data to be transmitted
>>>>>>>>>>> + * @len: size of transmission buffer
>>>>>>>>>>> + *
>>>>>>>>>>> + * This function will automatically choose the right data type depending on
>>>>>>>>>>> + * the command payload length.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: The number of bytes successfully transmitted or a negative error
>>>>>>>>>>> + * code on failure.
>>>>>>>>>>>   */
>>>>>>>>>>> -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
>>>>>>>>>>> -			    size_t len)
>>>>>>>>>>> +ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>>>>>>>>>>> +				  const void *data, size_t len)
>>>>>>>>>>>  {
>>>>>>>>>>> -	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
>>>>>>>>>>>  	struct mipi_dsi_msg msg = {
>>>>>>>>>>>  		.channel = dsi->channel,
>>>>>>>>>>>  		.tx_buf = data,
>>>>>>>>>>>  		.tx_len = len
>>>>>>>>>>>  	};
>>>>>>>>>>>  
>>>>>>>>>>> -	if (!ops || !ops->transfer)
>>>>>>>>>>> +	if (!dsi->host->ops || !dsi->host->ops->transfer)
>>>>>>>>>>>  		return -ENOSYS;
>>>>>>>>>>>  
>>>>>>>>>>>  	switch (len) {
>>>>>>>>>>>  	case 0:
>>>>>>>>>>>  		return -EINVAL;
>>>>>>>>>>> +
>>>>>>>>>>>  	case 1:
>>>>>>>>>>>  		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
>>>>>>>>>>>  		break;
>>>>>>>>>>> +
>>>>>>>>>>>  	case 2:
>>>>>>>>>>>  		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
>>>>>>>>>>>  		break;
>>>>>>>>>>> +
>>>>>>>>>>> +	default:
>>>>>>>>>>> +		msg.type = MIPI_DSI_DCS_LONG_WRITE;
>>>>>>>>>>> +		break;
>>>>>>>>>>> +	}
>>>>>>>>>>> +
>>>>>>>>>>> +	return dsi->host->ops->transfer(dsi->host, &msg);
>>>>>>>>>>> +}
>>>>>>>>>>> +EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer);
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * mipi_dsi_dcs_write() - send DCS write command
>>>>>>>>>>> + * @dsi: DSI peripheral device
>>>>>>>>>>> + * @cmd: DCS command
>>>>>>>>>>> + * @data: buffer containing the command payload
>>>>>>>>>>> + * @len: command payload length
>>>>>>>>>>> + *
>>>>>>>>>>> + * This function will automatically choose the right data type depending on
>>>>>>>>>>> + * the command payload length.
>>>>>>>>>>> + *
>>>>>>>>>>> + * Return: The number of bytes successfully transmitted or a negative error
>>>>>>>>>>> + * code on failure.
>>>>>>>>>>> + */
>>>>>>>>>>> +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
>>>>>>>>>>> +			   const void *data, size_t len)
>>>>>>>>>>> +{
>>>>>>>>>>> +	struct mipi_dsi_msg msg;
>>>>>>>>>>> +	ssize_t err;
>>>>>>>>>>> +	size_t size;
>>>>>>>>>>> +	u8 *tx;
>>>>>>>>>>> +
>>>>>>>>>>> +	if (!dsi->host->ops || !dsi->host->ops->transfer)
>>>>>>>>>>> +		return -ENOSYS;
>>>>>>>>>>> +
>>>>>>>>>>> +	if (len > 0) {
>>>>>>>>>>> +		unsigned int offset = 0;
>>>>>>>>>>> +
>>>>>>>>>>> +		/*
>>>>>>>>>>> +		 * DCS long write packets contain the word count in the header
>>>>>>>>>>> +		 * bytes 1 and 2 and have a payload containing the DCS command
>>>>>>>>>>> +		 * byte folowed by word count minus one bytes.
>>>>>>>>>>> +		 *
>>>>>>>>>>> +		 * DCS short write packets encode the DCS command and up to
>>>>>>>>>>> +		 * one parameter in header bytes 1 and 2.
>>>>>>>>>>> +		 */
>>>>>>>>>>> +		if (len > 1)
>>>>>>>>>>> +			size = 3 + len;
>>>>>>>>>>> +		else
>>>>>>>>>>> +			size = 1 + len;
>>>>>>>>>> I guess "size = 2" would be better here.
>>>>>>>>> This is on purpose because it documents the format. If len > 1, then the
>>>>>>>>> packet is a long write, so we need three bytes (command & word count) in
>>>>>>>>> addition to the payload. For short writes, len <= 1 and we only need one
>>>>>>>>> extra byte (command).
>>>>>>>>>
>>>>>>>>>>> +
>>>>>>>>>>> +		tx = kmalloc(size, GFP_KERNEL);
>>>>>>>>>>> +		if (!tx)
>>>>>>>>>>> +			return -ENOMEM;
>>>>>>>>>>> +
>>>>>>>>>>> +		/* write word count to header for DCS long write packets */
>>>>>>>>>>> +		if (len > 1) {
>>>>>>>>>>> +			tx[offset++] = ((1 + len) >> 0) & 0xff;
>>>>>>>>>>> +			tx[offset++] = ((1 + len) >> 8) & 0xff;
>>>>>>>>>>> +		}
>>>>>>>>>>> +
>>>>>>>>>>> +		/* write the DCS command byte followed by the payload */
>>>>>>>>>>> +		tx[offset++] = cmd;
>>>>>>>>>>> +		memcpy(tx + offset, data, len);
>>>>>>>>>>> +	} else {
>>>>>>>>>>> +		tx = &cmd;
>>>>>>>>>>> +		size = 1;
>>>>>>>>>>> +	}
>>>>>>>>>> Contents of this conditional is incompatible with the current API.
>>>>>>>>>> mipi_dsi_msg.tx_buf contains only data and mipi_dsi_msg.tx_len contains
>>>>>>>>>> lenght of this data. Now you try to embed length of data into tx_buf and
>>>>>>>>>> this breaks the API.
>>>>>>>>> Huh? Of course the new API has different semantics, but that's the whole
>>>>>>>>> point of it. The else branch here is to optimize for the case where a
>>>>>>>>> command has no payload. In that case there is no need for allocating an
>>>>>>>>> extra buffer, since the command byte is the only data transferred.
>>>>>>>> If this is the whole point of it why patch description says nothing
>>>>>>>> about it?
>>>>>>> I thought the patch description said this. What do you think needs to be
>>>>>>> added?
>>>>>> In short, that new API assumes that transfer callback must interpret
>>>>>> write buffer
>>>>>> differently than before :) Ie that sometimes at the beginning of the buffer
>>>>>> there will be additional bytes.
>>>>> Right, we never defined exactly which part of code would take which data
>>>>> and into what data it would be transformed. That obviously breaks as
>>>>> soon as two implementations make different assumptions. =)
>>>> In previous version of this patch [1] you have made different assumption,
>>>> and in the discussion you were clearly aware of the current implementation,
>>>> so your reaction here surprises me little bit. Maybe some misunderstanding.
>>>> Anyway I am glad we are now both aware of the problem.
>>>>
>>>> [1]: http://permalink.gmane.org/gmane.comp.video.dri.devel/110647
>>> It's possible I didn't realize the full complexity of the problem at the
>>> time. To summarize, I think the helpers in the core should do as much
>>> work as they possibly can, so that drivers don't have to duplicate the
>>> same over and over again. That is, the DCS helpers that are under
>>> discussion here should create a buffer that reflects the packet that is
>>> to be sent to the DSI peripheral, including the specific layout of the
>>> header. So a struct mipi_dsi_msg contains the following information:
>>>
>>> 	- .channel + .type make up the DI (Data ID) field in the packet
>>> 	  header
>>>
>>> 	for short packets:
>>> 	- .tx_buf[0] and .tx_buf[1] correspond to Data 0 and Data 1
>>> 	  fields in the packet header (both of these are only present if
>>> 	  .tx_len is larger than 0 and 1, and default to 0 otherwise)
>>>
>>> 	for long packets:
>>> 	- .tx_buf[0] and .tx_buf[1] correspond to the word count
>>> 	- .tx_buf[2..] represent the payload (word count - 2 bytes)
>>>
>>> That's pretty much what section 8.4 General Packet Structure of the DSI
>>> specification describes. This intentionally leaves out the header ECC
>>> and 16-bit packet footer (checksum). These are typically implemented in
>>> hardware, and if they aren't we can provide helpers that compute them
>>> based on the header, and the payload in .tx_buf. That way all the packet
>>> composition defined in the specification is handled by common code and a
>>> driver only needs to have the hardware-specific knowledge, namely where
>>> the various pieces need to be written in order to transmit them as
>>> desired.
>>>
>>> Does that make sense?
>> According to DSI specification we can describe DSI
>> message/command/transaction
>> on two levels:
>> 1. Application layer - message is described by a triplet (data_type,
>> channel_id, data).
>> 2. Protocol layer - message is described as a four byte packet header,
>> optionally
>> followed by packet data (payload) and checksum (which we can skip it
>> here as it should be generated by HW).
>>
>> In the current API the 1st approach is used. There is no defined
>> standard how to program
>> DSI host to generate specific message, so this approach seems to be the
>> most natural in general case.
>>
>> On the other side all DSI hosts I looked at use approach based on
>> protocol layer, ie.
>> packet header is written to one FIFO register and payload to another
>> (exynos, intel, omap) or the same (tegra).
>>
>> Your proposition is something close to 2nd approach, maybe it would be
>> better to convert to completely to 2nd approach:
>>
>> struct mipi_dsi_msg {
>>     u8 header[4]; /* u32 header;  ??? */
>>     void *payload; /* NULL in case of short packets */
>>     size_t payload_len;
>>     ...
>> };
>>
>> Anyway, I think conversion to protocol layer should be done by helper
>> but this helper should be called rather from dsi host,
>> otherwise we can encounter some day dsi host which we need to feed with
>> data differently and we will need to perform
>> back-conversion from protocol layer to app layer, it will not be
>> difficult it will be just ugly :)
>>
>> What about creating helpers to make dsi packets from current dsi
>> message. Sth like:
>>
>> ... drm_mipi_create_packet(struct mipi_dsi_packet *pkt, struct
>> mipi_dsi_msg *msg)
>> {
>>     if (long packet) {
>>         pkt->header = ...;
>>         pkt->payload = msg->tx_buf;
>>         pkt->len = msg->tx_len;
>>     } else {
>>         pkt->header = ...;
>>         pkt->payload = NULL;
>>         pkt->len = 0;
>>     }
>> }
>>
>> This way in dsi host we will have sth like:
>>
>> host_transfer(...msg) {
>>     struct mipi_dsi_packet pkt;
>>
>>     drm_mipi_create_packet(&pkt, msg);
>>
>>     writel(msg.header, REG_HDR);
>>
>>    for (i = 0; i < pkt.len; i += 4)
>>         writel(pkt.payload[i..i+3], REG_PAYLOAD);
>> }
>>
>> Please note that this way we can avoid dynamic memory
>> allocation/copy/deallocation, I know it is cheap, but it does not seems
>> to be necessary.
> Yes, that sounds pretty reasonable on a quick glance. I guess the
> mipi_dsi_create_packet() function could take an additional parameter at
> some point to generate the header ECC and checksum for hardware that
> can't do it on the fly.
>
> I'll give this a shot to see how it's going to work in practice. Given
> how Exynos currently uses the application layer interface it should be
> possible to use the helper as a means to transition more easily. Do you
> plan on converting to the helper once it's available?

Yes.

Regards
Andrzej
> It seems like it
> would fit your hardware better than the current approach.
>
> Thierry



More information about the dri-devel mailing list