All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
@ 2014-10-13 10:16 Thierry Reding
  2014-10-13 10:16 ` [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg Thierry Reding
                   ` (15 more replies)
  0 siblings, 16 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@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@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;
+
+		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;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.tx_len = size;
+	msg.tx_buf = tx;
+
+	switch (len) {
+	case 0:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 1:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
 	default:
 		msg.type = MIPI_DSI_DCS_LONG_WRITE;
 		break;
@@ -234,23 +321,27 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
 		msg.flags = MIPI_DSI_MSG_USE_LPM;
 
-	return ops->transfer(dsi->host, &msg);
+	err = dsi->host->ops->transfer(dsi->host, &msg);
+
+	if (len > 0)
+		kfree(tx);
+
+	return err;
 }
 EXPORT_SYMBOL(mipi_dsi_dcs_write);
 
 /**
- * mipi_dsi_dcs_read - send DCS read request command
- * @dsi: DSI device
- * @cmd: DCS read command
- * @data: pointer to read buffer
- * @len: length of @data
+ * mipi_dsi_dcs_read() - send DCS read request command
+ * @dsi: DSI peripheral device
+ * @cmd: DCS command
+ * @data: buffer in which to receive data
+ * @len: size of receive buffer
  *
- * Function returns number of read bytes or error code.
+ * Return: The number of bytes read or a negative error code on failure.
  */
 ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len)
 {
-	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
 	struct mipi_dsi_msg msg = {
 		.channel = dsi->channel,
 		.type = MIPI_DSI_DCS_READ,
@@ -260,13 +351,13 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 		.rx_len = len
 	};
 
-	if (!ops || !ops->transfer)
+	if (!dsi->host->ops || !dsi->host->ops->transfer)
 		return -ENOSYS;
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
 		msg.flags = MIPI_DSI_MSG_USE_LPM;
 
-	return ops->transfer(dsi->host, &msg);
+	return dsi->host->ops->transfer(dsi->host, &msg);
 }
 EXPORT_SYMBOL(mipi_dsi_dcs_read);
 
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
index b5217fe37f02..0f85a7c37687 100644
--- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
@@ -141,7 +141,7 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
 	if (ctx->error < 0)
 		return;
 
-	ret = mipi_dsi_dcs_write(dsi, data, len);
+	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
 	if (ret < 0) {
 		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len,
 			data);
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 8569dc5a1026..ccc3869e137b 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -132,8 +132,10 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
 
 int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
-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);
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
+			   const void *data, size_t len);
 ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len);
 
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 13:41   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper Thierry Reding
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda, linux-samsung-soc

From: Thierry Reding <treding@nvidia.com>

struct mipi_dsi_msg is a read-only structure, drivers should never need
to modify it. Make this explicit by making all references to the struct
const.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +-
 include/drm/drm_mipi_dsi.h              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 24741d8758e8..c5f3c76bfac3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1236,7 +1236,7 @@ static bool exynos_dsi_is_short_dsi_type(u8 type)
 }
 
 static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
-				       struct mipi_dsi_msg *msg)
+				        const struct mipi_dsi_msg *msg)
 {
 	struct exynos_dsi *dsi = host_to_dsi(host);
 	struct exynos_dsi_transfer xfer;
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index ccc3869e137b..836cc2b677d0 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -56,7 +56,7 @@ struct mipi_dsi_host_ops {
 	int (*detach)(struct mipi_dsi_host *host,
 		      struct mipi_dsi_device *dsi);
 	ssize_t (*transfer)(struct mipi_dsi_host *host,
-			    struct mipi_dsi_msg *msg);
+			    const struct mipi_dsi_msg *msg);
 };
 
 /**
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
  2014-10-13 10:16 ` [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 14:10   ` Andrzej Hajda
  2014-10-13 14:23   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function Thierry Reding
                   ` (13 subsequent siblings)
  15 siblings, 2 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: YoungJun Cho <yj44.cho@samsung.com>

This function can be used to set the maximum return packet size for a
MIPI DSI peripheral.

Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
[treding: endianess, kerneldoc, return value]
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 30 ++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  2 ++
 2 files changed, 32 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 1702ffd07986..27fc6dac5e4a 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -198,6 +198,36 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi)
 }
 EXPORT_SYMBOL(mipi_dsi_detach);
 
+/*
+ * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
+ *    the payload in a long packet transmitted from the peripheral back to the
+ *    host processor
+ * @dsi: DSI peripheral device
+ * @value: the maximum size of the payload
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
+					    u16 value)
+{
+	u8 tx[2] = { value & 0xff, value >> 8 };
+	struct mipi_dsi_msg msg;
+	ssize_t err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+	msg.tx_len = sizeof(tx);
+	msg.tx_buf = tx;
+
+	err = dsi->host->ops->transfer(dsi->host, &msg);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
+
 /**
  * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 836cc2b677d0..ef50b5d0de57 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -132,6 +132,8 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
 
 int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
+int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
+					    u16 value);
 ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
 				  const void *data, size_t len);
 ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
  2014-10-13 10:16 ` [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg Thierry Reding
  2014-10-13 10:16 ` [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 14:13   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 05/15] drm/dsi: Implement generic read and write commands Thierry Reding
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Use the newly introduced mipi_dsi_set_maximum_return_packet_size()
function to replace an open-coded version.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/panel/panel-s6e8aa0.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
index 0f85a7c37687..c31e2953f290 100644
--- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
@@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx)
 }
 
 static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx,
-						   int size)
+						   u16 size)
 {
 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
-	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
-	u8 buf[] = {size, 0};
-	struct mipi_dsi_msg msg = {
-		.channel = dsi->channel,
-		.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
-		.tx_len = sizeof(buf),
-		.tx_buf = buf
-	};
 	int ret;
 
 	if (ctx->error < 0)
 		return;
 
-	if (!ops || !ops->transfer)
-		ret = -EIO;
-	else
-		ret = ops->transfer(dsi->host, &msg);
-
+	ret = mipi_dsi_set_maximum_return_packet_size(dsi, size);
 	if (ret < 0) {
 		dev_err(ctx->dev,
 			"error %d setting maximum return packet size to %d\n",
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 05/15] drm/dsi: Implement generic read and write commands
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (2 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-14  8:59   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 06/15] drm/dsi: Implement some standard DCS commands Thierry Reding
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Implement generic read and write commands. Selection of the proper data
type for packets is done automatically based on the number of parameters
or payload length.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 115 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |   6 +++
 2 files changed, 121 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 27fc6dac5e4a..7cd69273dbad 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -229,6 +229,121 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
 EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
 
 /**
+ * mipi_dsi_generic_write() - transmit data using a generic write packet
+ * @dsi: DSI peripheral device
+ * @payload: buffer containing the payload
+ * @size: size of payload buffer
+ *
+ * This function will automatically choose the right data type depending on
+ * the payload length.
+ *
+ * Return: The number of bytes transmitted on success or a negative error code
+ * on failure.
+ */
+ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
+			       size_t size)
+{
+	struct mipi_dsi_msg msg;
+	ssize_t err;
+	u8 *tx;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.flags = MIPI_DSI_MSG_USE_LPM | MIPI_DSI_MSG_REQ_ACK;
+
+	if (size > 2) {
+		tx = kmalloc(2 + size, GFP_KERNEL);
+		if (!tx)
+			return -ENOMEM;
+
+		tx[0] = (size >> 0) & 0xff;
+		tx[1] = (size >> 8) & 0xff;
+
+		memcpy(tx + 2, payload, size);
+
+		msg.tx_len = 2 + size;
+		msg.tx_buf = tx;
+	} else {
+		msg.tx_buf = payload;
+		msg.tx_len = size;
+	}
+
+	switch (size) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
+		break;
+
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
+		break;
+
+	default:
+		msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
+		break;
+	}
+
+	err = dsi->host->ops->transfer(dsi->host, &msg);
+
+	if (size > 2)
+		kfree(tx);
+
+	return err;
+}
+EXPORT_SYMBOL(mipi_dsi_generic_write);
+
+/**
+ * mipi_dsi_generic_read() - receive data using a generic read packet
+ * @dsi: DSI peripheral device
+ * @params: buffer containing the request parameters
+ * @num_params: number of request parameters
+ * @data: buffer in which to return the received data
+ * @size: size of receive buffer
+ *
+ * This function will automatically choose the right data type depending on
+ * the number of parameters passed in.
+ *
+ * Return: The number of bytes successfully read or a negative error code on
+ * failure.
+ */
+ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
+			      size_t num_params, void *data, size_t size)
+{
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.flags = MIPI_DSI_MSG_USE_LPM;
+	msg.tx_len = num_params;
+	msg.tx_buf = params;
+	msg.rx_len = size;
+	msg.rx_buf = data;
+
+	switch (num_params) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
+		break;
+
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return dsi->host->ops->transfer(dsi->host, &msg);
+}
+EXPORT_SYMBOL(mipi_dsi_generic_read);
+
+/**
  * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
  * @dsi: DSI peripheral device
  * @data: buffer containing data to be transmitted
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index ef50b5d0de57..92ca66306702 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -134,6 +134,12 @@ int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
 int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
 					    u16 value);
+
+ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
+			       size_t size);
+ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
+			      size_t num_params, void *data, size_t size);
+
 ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
 				  const void *data, size_t len);
 ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 06/15] drm/dsi: Implement some standard DCS commands
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (3 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 05/15] drm/dsi: Implement generic read and write commands Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-21  8:36   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 07/15] drm/dsi: Add to DocBook documentation Thierry Reding
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: YoungJun Cho <yj44.cho@samsung.com>

Add helpers for the {enter,exit}_sleep_mode, set_display_{on,off} and
set_tear_{on,off} DCS commands.

Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
[treding: kerneldoc and other minor cleanup]
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 118 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  19 +++++++
 2 files changed, 137 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 7cd69273dbad..8bb38db861ec 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -506,6 +506,124 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 }
 EXPORT_SYMBOL(mipi_dsi_dcs_read);
 
+/**
+ * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
+ *    display module except interface communication
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
+
+/**
+ * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
+ *    module
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
+
+/**
+ * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
+ *    display device
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
+
+/**
+ * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
+ *    display device
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
+
+/**
+ * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
+ *    output signal on the TE signal line
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
+
+/**
+ * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
+ *    output signal on the TE signal line.
+ * @dsi: DSI peripheral device
+ * @mode: the Tearing Effect Output Line mode
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+			     enum mipi_dsi_dcs_tear_mode mode)
+{
+	u8 value = mode;
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
+				 sizeof(value));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
+
 static int mipi_dsi_drv_probe(struct device *dev)
 {
 	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 92ca66306702..bde663725a58 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -140,12 +140,31 @@ ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
 ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
 			      size_t num_params, void *data, size_t size);
 
+/**
+ * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
+ * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
+ *    information only
+ * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
+ *    V-Blanking and H-Blanking information
+ */
+enum mipi_dsi_dcs_tear_mode {
+	MIPI_DSI_DCS_TEAR_MODE_VBLANK,
+	MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
+};
+
 ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
 				  const void *data, size_t len);
 ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
 			   const void *data, size_t len);
 ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len);
+int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+			     enum mipi_dsi_dcs_tear_mode mode);
 
 /**
  * struct mipi_dsi_driver - DSI driver
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 07/15] drm/dsi: Add to DocBook documentation
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (4 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 06/15] drm/dsi: Implement some standard DCS commands Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-21  9:26   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 08/15] drm/dsi: Implement DCS nop command Thierry Reding
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Integrate the MIPI DSI helpers into DocBook and clean up various
kerneldoc warnings. Also add a brief DOC section and clarify some
aspects of the mipi_dsi_host struct's .transfer() operation.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 Documentation/DocBook/drm.tmpl |  6 ++++++
 drivers/gpu/drm/drm_mipi_dsi.c | 18 ++++++++++++++++--
 include/drm/drm_mipi_dsi.h     | 16 ++++++++++++++--
 3 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
index be35bc328b77..da733c28c92f 100644
--- a/Documentation/DocBook/drm.tmpl
+++ b/Documentation/DocBook/drm.tmpl
@@ -2343,6 +2343,12 @@ void intel_crt_init(struct drm_device *dev)
 !Edrivers/gpu/drm/drm_dp_mst_topology.c
     </sect2>
     <sect2>
+      <title>MIPI DSI Helper Functions Reference</title>
+!Pdrivers/gpu/drm/drm_mipi_dsi.c dsi helpers
+!Iinclude/drm/drm_mipi_dsi.h
+!Edrivers/gpu/drm/drm_mipi_dsi.c
+    </sect2>
+    <sect2>
       <title>EDID Helper Functions Reference</title>
 !Edrivers/gpu/drm/drm_edid.c
     </sect2>
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 8bb38db861ec..6749b88a6c72 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -35,6 +35,16 @@
 
 #include <video/mipi_display.h>
 
+/**
+ * DOC: dsi helpers
+ *
+ * These functions contain some common logic and helpers to deal with MIPI DSI
+ * peripherals.
+ *
+ * Helpers are provided for a number of standard MIPI DSI command as well as a
+ * subset of the MIPI DCS command set.
+ */
+
 static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv)
 {
 	return of_driver_match_device(dev, drv);
@@ -649,8 +659,10 @@ static void mipi_dsi_drv_shutdown(struct device *dev)
 }
 
 /**
- * mipi_dsi_driver_register - register a driver for DSI devices
+ * mipi_dsi_driver_register() - register a driver for DSI devices
  * @drv: DSI driver structure
+ *
+ * Return: 0 on success or a negative error code on failure.
  */
 int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
 {
@@ -667,8 +679,10 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
 EXPORT_SYMBOL(mipi_dsi_driver_register);
 
 /**
- * mipi_dsi_driver_unregister - unregister a driver for DSI devices
+ * mipi_dsi_driver_unregister() - unregister a driver for DSI devices
  * @drv: DSI driver structure
+ *
+ * Return: 0 on success or a negative error code on failure.
  */
 void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
 {
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index bde663725a58..8693711eb623 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -26,6 +26,7 @@ struct mipi_dsi_device;
  * struct mipi_dsi_msg - read/write DSI buffer
  * @channel: virtual channel id
  * @type: payload data type
+ * @flags: flags controlling this message transmission
  * @tx_len: length of @tx_buf
  * @tx_buf: data to be written
  * @rx_len: length of @rx_buf
@@ -47,8 +48,19 @@ struct mipi_dsi_msg {
  * struct mipi_dsi_host_ops - DSI bus operations
  * @attach: attach DSI device to DSI host
  * @detach: detach DSI device from DSI host
- * @transfer: send and/or receive DSI packet, return number of received bytes,
- * 	      or error
+ * @transfer: transmit a DSI packet
+ *
+ * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
+ * structures. This structure contains information about the type of packet
+ * being transmitted as well as the transmit and receive buffers. When an
+ * error is encountered during transmission, this function will return a
+ * negative error code. On success it shall return the number of bytes
+ * transmitted for write packets or the number of bytes received for read
+ * packets.
+ *
+ * Note that typically DSI packet transmission is atomic, so the .transfer()
+ * function will seldomly return anything other than the number of bytes
+ * contained in the transmit buffer on success.
  */
 struct mipi_dsi_host_ops {
 	int (*attach)(struct mipi_dsi_host *host,
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 08/15] drm/dsi: Implement DCS nop command
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (5 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 07/15] drm/dsi: Add to DocBook documentation Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-21  9:53   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 09/15] drm/dsi: Implement DCS soft_reset command Thierry Reding
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Provide a small convenience wrapper that transmits a DCS nop command.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 18 ++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  1 +
 2 files changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 6749b88a6c72..c88dcda2f4ac 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -517,6 +517,24 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 EXPORT_SYMBOL(mipi_dsi_dcs_read);
 
 /**
+ * mipi_dsi_dcs_nop() - send DCS nop packet
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_nop);
+
+/**
  * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
  *    display module except interface communication
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 8693711eb623..be16c8e769bd 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -170,6 +170,7 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
 			   const void *data, size_t len);
 ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len);
+int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 09/15] drm/dsi: Implement DCS soft_reset command
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (6 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 08/15] drm/dsi: Implement DCS nop command Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 10:16 ` [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command Thierry Reding
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Provide a small convenience wrapper that transmits a DCS soft_reset
command.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 18 ++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  1 +
 2 files changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index c88dcda2f4ac..60bd68b520ae 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -535,6 +535,24 @@ int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
 EXPORT_SYMBOL(mipi_dsi_dcs_nop);
 
 /**
+ * mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
+
+/**
  * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
  *    display module except interface communication
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index be16c8e769bd..2afbd5853f78 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -171,6 +171,7 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
 ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len);
 int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (7 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 09/15] drm/dsi: Implement DCS soft_reset command Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-21  9:48   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 11/15] drm/dsi: Implement DCS {get,set}_pixel_format commands Thierry Reding
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Provide a small convenience wrapper that transmits a DCS get_power_mode
command. A set of bitmasks for the mode bits is also provided.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 21 +++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  7 +++++++
 2 files changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 60bd68b520ae..4e016a6a9fc3 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -553,6 +553,27 @@ int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
 EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
 
 /**
+ * mipi_dsi_dcs_get_power_mode() - query the display module's current power
+ *    mode
+ * @dsi: DSI peripheral device
+ * @mode: return location for the current power mode
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
+				sizeof(*mode));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
+
+/**
  * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
  *    display module except interface communication
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 2afbd5853f78..b659b99ac890 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -164,6 +164,12 @@ enum mipi_dsi_dcs_tear_mode {
 	MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
 };
 
+#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
+#define MIPI_DSI_DCS_POWER_MODE_NORMAL  (1 << 3)
+#define MIPI_DSI_DCS_POWER_MODE_SLEEP   (1 << 4)
+#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
+#define MIPI_DSI_DCS_POWER_MODE_IDLE    (1 << 6)
+
 ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
 				  const void *data, size_t len);
 ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
@@ -172,6 +178,7 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 			  size_t len);
 int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
 int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 11/15] drm/dsi: Implement DCS {get,set}_pixel_format commands
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (8 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 10:16 ` [PATCH 12/15] drm/dsi: Implement DCS set_{column, page}_address commands Thierry Reding
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Provide small convenience wrappers to query or set the pixel format used
by the interface.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  2 ++
 2 files changed, 44 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 4e016a6a9fc3..d7ca57f1fdd3 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -574,6 +574,27 @@ int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
 EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
 
 /**
+ * mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
+ *    data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: return location for the pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
+				sizeof(*format));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format);
+
+/**
  * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
  *    display module except interface communication
  * @dsi: DSI peripheral device
@@ -691,6 +712,27 @@ int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 }
 EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
 
+/**
+ * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
+ *    data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
+				 sizeof(format));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
+
 static int mipi_dsi_drv_probe(struct device *dev)
 {
 	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index b659b99ac890..968085b1ef73 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -179,6 +179,7 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
+int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format);
 int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
@@ -186,6 +187,7 @@ int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 			     enum mipi_dsi_dcs_tear_mode mode);
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
 
 /**
  * struct mipi_dsi_driver - DSI driver
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 12/15] drm/dsi: Implement DCS set_{column, page}_address commands
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (9 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 11/15] drm/dsi: Implement DCS {get,set}_pixel_format commands Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-13 10:16 ` [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands Thierry Reding
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Provide small convenience wrappers to set the column and page extents of
the frame memory accessed by the host processors.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 46 ++++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  4 ++++
 2 files changed, 50 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index d7ca57f1fdd3..d4d3cf752be7 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -671,6 +671,52 @@ int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
 EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
 
 /**
+ * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
+ *    memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first column of frame memory
+ * @end: last column of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+				    u16 end)
+{
+	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
+				 sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
+ *    memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first page of frame memory
+ * @end: last page of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+				  u16 end)
+{
+	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+	ssize_t err;
+
+	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
+				 sizeof(payload));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/**
  * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
  *    output signal on the TE signal line
  * @dsi: DSI peripheral device
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 968085b1ef73..17ef2fbb09a5 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -184,6 +184,10 @@ int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+				    u16 end);
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+				  u16 end);
 int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 			     enum mipi_dsi_dcs_tear_mode mode);
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (10 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 12/15] drm/dsi: Implement DCS set_{column, page}_address commands Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-16  7:00   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle Thierry Reding
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Many peripherals require DCS commands to be sent in low power mode and
will fail to correctly process them in high speed mode. Section 5.2 of
the MIPI DSI specification also mandates that on bidirectional lanes,
data shall be transmitted in low power mode only. At worst this change
will make transmission of DCS commands slower than optimal on some DSI
peripherals, but it should enable DCS commands to be successfully
transmitted to any DSI peripheral.

If transmission in low power mode turns out to be too slow at some point
in the future, one possible solution would be to explicitly mark devices
that support high speed transmission of DCS commands.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index d4d3cf752be7..a0b9c7ea77a7 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -370,6 +370,7 @@ ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
 {
 	struct mipi_dsi_msg msg = {
 		.channel = dsi->channel,
+		.flags = MIPI_DSI_MSG_USE_LPM,
 		.tx_buf = data,
 		.tx_len = len
 	};
@@ -457,6 +458,7 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
 	}
 
 	memset(&msg, 0, sizeof(msg));
+	msg.flags = MIPI_DSI_MSG_USE_LPM;
 	msg.channel = dsi->channel;
 	msg.tx_len = size;
 	msg.tx_buf = tx;
@@ -500,6 +502,7 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
 	struct mipi_dsi_msg msg = {
 		.channel = dsi->channel,
 		.type = MIPI_DSI_DCS_READ,
+		.flags = MIPI_DSI_MSG_USE_LPM,
 		.tx_buf = &cmd,
 		.tx_len = 1,
 		.rx_buf = data,
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (11 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-16  7:03   ` Andrzej Hajda
  2014-10-13 10:16 ` [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support Thierry Reding
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel; +Cc: Andrzej Hajda

From: Thierry Reding <treding@nvidia.com>

Add a function, of_find_mipi_dsi_device_by_node(), that can be used to
resolve a phandle to a MIPI DSI device.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 23 +++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  1 +
 2 files changed, 24 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index a0b9c7ea77a7..0067f914c1ea 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -67,6 +67,29 @@ static struct bus_type mipi_dsi_bus_type = {
 	.pm = &mipi_dsi_device_pm_ops,
 };
 
+static int of_device_match(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+/**
+ * of_find_mipi_dsi_device_by_node() - find the MIPI DSI device matching a
+ *    device tree node
+ * @np: device tree node
+ *
+ * Return: A pointer to the MIPI DSI device corresponding to @np or NULL if no
+ *    such device exists (or has not been registered yet).
+ */
+struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np)
+{
+	struct device *dev;
+
+	dev = bus_find_device(&mipi_dsi_bus_type, NULL, np, of_device_match);
+
+	return dev ? to_mipi_dsi_device(dev) : NULL;
+}
+EXPORT_SYMBOL(of_find_mipi_dsi_device_by_node);
+
 static void mipi_dsi_dev_release(struct device *dev)
 {
 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 17ef2fbb09a5..8f025cc5ce99 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -142,6 +142,7 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
 	return container_of(dev, struct mipi_dsi_device, dev);
 }
 
+struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np);
 int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
 int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (12 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle Thierry Reding
@ 2014-10-13 10:16 ` Thierry Reding
  2014-10-20 15:56   ` Andrzej Hajda
  2014-10-13 12:29 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical Andrzej Hajda
  2014-10-14  8:00 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Andrzej Hajda
  15 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-13 10:16 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, Andrzej Hajda,
	Rob Herring, Kumar Gala

From: Thierry Reding <treding@nvidia.com>

This panel requires dual-channel mode. The device accepts command-mode
data on 8 lanes and will therefore need a dual-channel DSI controller.
The two interfaces that make up this device need to be instantiated in
the controllers that gang up to provide the dual-channel DSI host.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 .../bindings/panel/sharp,lq101r1sx01.txt           |  46 +++
 drivers/gpu/drm/panel/Kconfig                      |  13 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c    | 426 +++++++++++++++++++++
 4 files changed, 486 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
 create mode 100644 drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c

diff --git a/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
new file mode 100644
index 000000000000..4ab4380ddac8
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
@@ -0,0 +1,46 @@
+Sharp Microelectronics 10.1" WQXGA TFT LCD panel
+
+This panel requires a dual-channel DSI host to operate. It supports two modes:
+- left-right: each channel drives the left or right half of the screen
+- even-odd: each channel drives the even or odd lines of the screen
+
+Each of the DSI channels controls a separate DSI peripheral. The peripheral
+driven by the first link (DSI-LINK1), left or even, is considered the primary
+peripheral and controls the device. The 'link2' property contains a phandle
+to the peripheral driven by the second link (DSI-LINK2, right or odd).
+
+Note that in video mode the DSI-LINK1 interface always provides the left/even
+pixels and DSI-LINK2 always provides the right/odd pixels. In command mode it
+is possible to program either link to drive the left/even or right/odd pixels
+but for the sake of consistency this binding assumes that the same assignment
+is chosen as for video mode.
+
+Required properties:
+- compatible: should be "sharp,lq101r1sx01"
+- link2: phandle to the DSI peripheral on the secondary link. Note that the
+  presence of this property marks the containing node as DSI-LINK1.
+- power-supply: phandle of the regulator that provides the supply voltage
+
+Optional properties:
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	dsi@54300000 {
+		panel: panel@0 {
+			compatible = "sharp,lq101r1sx01";
+			reg = <0>;
+
+			link2 = <&secondary>;
+
+			power-supply = <...>;
+			backlight = <...>;
+		};
+	};
+
+	dsi@54400000 {
+		secondary: panel@0 {
+			compatible = "sharp,lq101r1sx01";
+			reg = <0>;
+		};
+	};
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index bee9f72b3a93..024e98ef8e4d 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -27,4 +27,17 @@ config DRM_PANEL_S6E8AA0
 	select DRM_MIPI_DSI
 	select VIDEOMODE_HELPERS
 
+config DRM_PANEL_SHARP_LQ101R1SX01
+	tristate "Sharp LQ101R1SX01 panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	help
+	  Say Y here if you want to enable support for Sharp LQ101R1SX01
+	  TFT-LCD modules. The panel has a 2560x1600 resolution and uses
+	  24 bit RGB per pixel. It provides a dual MIPI DSI interface to
+	  the host and has a built-in LED backlight.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called panel-sharp-lq101r1sx01.
+
 endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 8b929212fad7..4b2a0430804b 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
 obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o
+obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
new file mode 100644
index 000000000000..09356697d22f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2014 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+#include <linux/host1x.h>
+
+struct sharp_panel {
+	struct drm_panel base;
+	/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */
+	struct mipi_dsi_device *link1;
+	struct mipi_dsi_device *link2;
+
+	struct backlight_device *backlight;
+	struct regulator *supply;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *mode;
+};
+
+static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct sharp_panel, base);
+}
+
+static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
+{
+	u8 payload[3] = { offset >> 8, offset & 0xff, value };
+	struct mipi_dsi_device *dsi = sharp->link1;
+	ssize_t err;
+
+	err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
+	if (err < 0)
+		dev_err(&dsi->dev, "failed to write %02x to %04x: %d\n",
+			value, offset, err);
+
+	err = mipi_dsi_dcs_nop(dsi);
+	if (err < 0)
+		dev_err(&dsi->dev, "failed to send DCS nop: %d\n", err);
+
+	usleep_range(10, 20);
+
+	return err;
+}
+
+static __maybe_unused int sharp_panel_read(struct sharp_panel *sharp,
+					   u16 offset, u8 *value)
+{
+	ssize_t err;
+
+	cpu_to_be16s(&offset);
+
+	err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset),
+				    value, sizeof(*value));
+	if (err < 0)
+		dev_err(&sharp->link1->dev, "failed to read from %04x: %d\n",
+			offset, err);
+
+	return err;
+}
+
+static int sharp_panel_disable(struct drm_panel *panel)
+{
+	struct sharp_panel *sharp = to_sharp_panel(panel);
+
+	if (!sharp->enabled)
+		return 0;
+
+	if (sharp->backlight) {
+		sharp->backlight->props.power = FB_BLANK_POWERDOWN;
+		backlight_update_status(sharp->backlight);
+	}
+
+	sharp->enabled = false;
+
+	return 0;
+}
+
+static int sharp_panel_unprepare(struct drm_panel *panel)
+{
+	struct sharp_panel *sharp = to_sharp_panel(panel);
+	int err;
+
+	if (!sharp->prepared)
+		return 0;
+
+	err = mipi_dsi_dcs_set_display_off(sharp->link1);
+	if (err < 0)
+		dev_err(panel->dev, "failed to set display off: %d\n", err);
+
+	err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1);
+	if (err < 0)
+		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
+
+	msleep(120);
+
+	regulator_disable(sharp->supply);
+
+	sharp->prepared = false;
+
+	return 0;
+}
+
+static int sharp_setup_symmetrical_split(struct mipi_dsi_device *left,
+					 struct mipi_dsi_device *right,
+					 const struct drm_display_mode *mode)
+{
+	int err;
+
+	err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);
+	if (err < 0)
+		dev_err(&left->dev, "failed to set column address: %d\n", err);
+
+	err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);
+	if (err < 0)
+		dev_err(&left->dev, "failed to set page address: %d\n", err);
+
+	err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2,
+					      mode->hdisplay - 1);
+	if (err < 0)
+		dev_err(&right->dev, "failed to set column address: %d\n", err);
+
+	err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);
+	if (err < 0)
+		dev_err(&right->dev, "failed to set page address: %d\n", err);
+
+	return 0;
+}
+
+static int sharp_panel_prepare(struct drm_panel *panel)
+{
+	struct sharp_panel *sharp = to_sharp_panel(panel);
+	u8 format = MIPI_DCS_PIXEL_FMT_24BIT;
+	int err;
+
+	if (sharp->prepared)
+		return 0;
+
+	err = regulator_enable(sharp->supply);
+	if (err < 0)
+		return err;
+
+	usleep_range(10000, 20000);
+
+	err = mipi_dsi_dcs_soft_reset(sharp->link1);
+	if (err < 0)
+		dev_err(panel->dev, "soft reset failed: %d\n", err);
+
+	msleep(120);
+
+	err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1);
+	if (err < 0)
+		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
+
+	/*
+	 * The MIPI DCS specification mandates this delay only between the
+	 * exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly
+	 * necessary here.
+	 */
+	/*
+	msleep(120);
+	*/
+
+	/* set left-right mode */
+	err = sharp_panel_write(sharp, 0x1000, 0x2a);
+	if (err < 0)
+		dev_err(panel->dev, "failed to set left-right mode: %d\n", err);
+
+	/* enable command mode */
+	err = sharp_panel_write(sharp, 0x1001, 0x01);
+	if (err < 0)
+		dev_err(panel->dev, "failed to enable command mode: %d\n", err);
+
+	err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format);
+	if (err < 0)
+		dev_err(panel->dev, "failed to set pixel format: %d\n", err);
+
+	/*
+	 * TODO: The device supports both left-right and even-odd split
+	 * configurations, but this driver currently supports only the left-
+	 * right split. To support a different mode a mechanism needs to be
+	 * put in place to communicate the configuration back to the DSI host
+	 * controller.
+	 */
+	err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2,
+					    sharp->mode);
+	if (err < 0)
+		dev_err(panel->dev, "failed to set up symmetrical split: %d\n",
+			err);
+
+	err = mipi_dsi_dcs_set_display_on(sharp->link1);
+	if (err < 0)
+		dev_err(panel->dev, "failed to set display on: %d\n", err);
+
+	sharp->prepared = true;
+
+	return err;
+}
+
+static int sharp_panel_enable(struct drm_panel *panel)
+{
+	struct sharp_panel *sharp = to_sharp_panel(panel);
+
+	if (sharp->enabled)
+		return 0;
+
+	if (sharp->backlight) {
+		sharp->backlight->props.power = FB_BLANK_UNBLANK;
+		backlight_update_status(sharp->backlight);
+	}
+
+	sharp->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+	.clock = 278000,
+	.hdisplay = 2560,
+	.hsync_start = 2560 + 128,
+	.hsync_end = 2560 + 128 + 64,
+	.htotal = 2560 + 128 + 64 + 64,
+	.vdisplay = 1600,
+	.vsync_start = 1600 + 4,
+	.vsync_end = 1600 + 4 + 8,
+	.vtotal = 1600 + 4 + 8 + 32,
+	.vrefresh = 60,
+};
+
+static int sharp_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			default_mode.hdisplay, default_mode.vdisplay,
+			default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.width_mm = 217;
+	panel->connector->display_info.height_mm = 136;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs sharp_panel_funcs = {
+	.disable = sharp_panel_disable,
+	.unprepare = sharp_panel_unprepare,
+	.prepare = sharp_panel_prepare,
+	.enable = sharp_panel_enable,
+	.get_modes = sharp_panel_get_modes,
+};
+
+static const struct of_device_id sharp_of_match[] = {
+	{ .compatible = "sharp,lq101r1sx01", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sharp_of_match);
+
+static int sharp_panel_add(struct sharp_panel *sharp)
+{
+	struct device_node *np;
+	int err;
+
+	sharp->mode = &default_mode;
+
+	sharp->supply = devm_regulator_get(&sharp->link1->dev, "power");
+	if (IS_ERR(sharp->supply))
+		return PTR_ERR(sharp->supply);
+
+	np = of_parse_phandle(sharp->link1->dev.of_node, "backlight", 0);
+	if (np) {
+		sharp->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!sharp->backlight)
+			return -EPROBE_DEFER;
+	}
+
+	drm_panel_init(&sharp->base);
+	sharp->base.funcs = &sharp_panel_funcs;
+	sharp->base.dev = &sharp->link1->dev;
+
+	err = drm_panel_add(&sharp->base);
+	if (err < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	if (sharp->backlight)
+		put_device(&sharp->backlight->dev);
+
+	return err;
+}
+
+static void sharp_panel_del(struct sharp_panel *sharp)
+{
+	if (sharp->base.dev)
+		drm_panel_remove(&sharp->base);
+
+	if (sharp->backlight)
+		put_device(&sharp->backlight->dev);
+}
+
+static int sharp_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct mipi_dsi_device *secondary = NULL;
+	struct sharp_panel *sharp;
+	struct device_node *np;
+	int err;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = 0;
+
+	/* Find DSI-LINK1 */
+	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
+	if (np) {
+		secondary = of_find_mipi_dsi_device_by_node(np);
+		of_node_put(np);
+
+		if (!secondary)
+			return -EPROBE_DEFER;
+	}
+
+	sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL);
+	if (!sharp)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, sharp);
+
+	/* register a panel for only the DSI-LINK1 interface */
+	if (secondary) {
+		sharp->link2 = secondary;
+		sharp->link1 = dsi;
+
+		err = sharp_panel_add(sharp);
+		if (err < 0)
+			return err;
+	}
+
+	err = mipi_dsi_attach(dsi);
+	if (err < 0) {
+		sharp_panel_del(sharp);
+		return err;
+	}
+
+	return 0;
+}
+
+static int sharp_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
+	int err;
+
+	/* only detach from host for the DSI-LINK2 interface */
+	if (!sharp->link2) {
+		mipi_dsi_detach(dsi);
+		return 0;
+	}
+
+	err = sharp_panel_disable(&sharp->base);
+	if (err < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
+
+	err = mipi_dsi_detach(dsi);
+	if (err < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
+
+	drm_panel_detach(&sharp->base);
+	drm_panel_remove(&sharp->base);
+
+	if (sharp->backlight)
+		put_device(&sharp->backlight->dev);
+
+	put_device(&sharp->link2->dev);
+
+	return 0;
+}
+
+static void sharp_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
+
+	sharp_panel_disable(&sharp->base);
+}
+
+static struct mipi_dsi_driver sharp_panel_driver = {
+	.driver = {
+		.name = "panel-sharp-lq101r1sx01",
+		.of_match_table = sharp_of_match,
+	},
+	.probe = sharp_panel_probe,
+	.remove = sharp_panel_remove,
+	.shutdown = sharp_panel_shutdown,
+};
+module_mipi_dsi_driver(sharp_panel_driver);
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.2

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (13 preceding siblings ...)
  2014-10-13 10:16 ` [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support Thierry Reding
@ 2014-10-13 12:29 ` Andrzej Hajda
  2014-10-14  9:00   ` Thierry Reding
  2014-10-14  8:00 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Andrzej Hajda
  15 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-13 12:29 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

Hi Thierry,

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@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().

As we discussed before I do not see much symmetry here - dcs read is
a kind of write command with non empty response.
The new function simplifies calls in case write has no arguments,
and it complicates it in other cases.
But the same simplification could be done by just providing inline function,
for example:
static inline ... mipi_dsi_dcs_write_cmd0(dsi, cmd)
{
    return mipi_dsi_dcs_write(dsi, &cmd, 1);
}

This way we would have simplified calls for commands without arguments
but without complication in other cases.

Anyway as I was not able to not convince you before, I guess I will not
convince you now.
I just recalled my arguments just to show my concerns to other developers.

On the other side as you left the old API I can live with it :)

Below some additional comments.

>
> 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@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);

It is just nitpick, but why did you replaced ops variable
by multiple dsi->host->ops expressions? I though it is a good
practice to avoid evaluating the same expression multiple times.

> +}
> +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;
> +
> +		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;
> +	}
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;
> +	msg.tx_len = size;
> +	msg.tx_buf = tx;
> +
> +	switch (len) {
> +	case 0:
> +		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
> +		break;
> +	case 1:
> +		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
> +		break;
>  	default:
>  		msg.type = MIPI_DSI_DCS_LONG_WRITE;
>  		break;
> @@ -234,23 +321,27 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
>  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
>  		msg.flags = MIPI_DSI_MSG_USE_LPM;
>  
> -	return ops->transfer(dsi->host, &msg);
> +	err = dsi->host->ops->transfer(dsi->host, &msg);

I think it would be better to replace the code above (starting from
memset) by:
    err = mipi_dsi_dcs_write_buffer(dsi, tx, size)

> +
> +	if (len > 0)
> +		kfree(tx);
> +
> +	return err;
>  }
>  EXPORT_SYMBOL(mipi_dsi_dcs_write);
>  
>  /**
> - * mipi_dsi_dcs_read - send DCS read request command
> - * @dsi: DSI device
> - * @cmd: DCS read command
> - * @data: pointer to read buffer
> - * @len: length of @data
> + * mipi_dsi_dcs_read() - send DCS read request command
> + * @dsi: DSI peripheral device
> + * @cmd: DCS command
> + * @data: buffer in which to receive data
> + * @len: size of receive buffer
>   *
> - * Function returns number of read bytes or error code.
> + * Return: The number of bytes read or a negative error code on failure.
>   */
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len)
>  {
> -	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
>  	struct mipi_dsi_msg msg = {
>  		.channel = dsi->channel,
>  		.type = MIPI_DSI_DCS_READ,
> @@ -260,13 +351,13 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  		.rx_len = len
>  	};
>  
> -	if (!ops || !ops->transfer)
> +	if (!dsi->host->ops || !dsi->host->ops->transfer)
>  		return -ENOSYS;
>  
>  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
>  		msg.flags = MIPI_DSI_MSG_USE_LPM;
>  
> -	return ops->transfer(dsi->host, &msg);
> +	return dsi->host->ops->transfer(dsi->host, &msg);

Again, why multiple evaluations of dsi->host->ops vs single one?

Regards
Andrzej

>  }
>  EXPORT_SYMBOL(mipi_dsi_dcs_read);
>  
> diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> index b5217fe37f02..0f85a7c37687 100644
> --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
> +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> @@ -141,7 +141,7 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
>  	if (ctx->error < 0)
>  		return;
>  
> -	ret = mipi_dsi_dcs_write(dsi, data, len);
> +	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
>  	if (ret < 0) {
>  		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len,
>  			data);
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 8569dc5a1026..ccc3869e137b 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -132,8 +132,10 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
>  
>  int mipi_dsi_attach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_detach(struct mipi_dsi_device *dsi);
> -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);
> +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
> +			   const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len);
>  

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg
  2014-10-13 10:16 ` [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg Thierry Reding
@ 2014-10-13 13:41   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-13 13:41 UTC (permalink / raw)
  To: Thierry Reding, dri-devel; +Cc: linux-samsung-soc

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> struct mipi_dsi_msg is a read-only structure, drivers should never need
> to modify it. Make this explicit by making all references to the struct
> const.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Acked-by: Andrzej Hajda <a.hajda@samsung.com>

--
Regards
Andrzej

> ---
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +-
>  include/drm/drm_mipi_dsi.h              | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 24741d8758e8..c5f3c76bfac3 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1236,7 +1236,7 @@ static bool exynos_dsi_is_short_dsi_type(u8 type)
>  }
>  
>  static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
> -				       struct mipi_dsi_msg *msg)
> +				        const struct mipi_dsi_msg *msg)
>  {
>  	struct exynos_dsi *dsi = host_to_dsi(host);
>  	struct exynos_dsi_transfer xfer;
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index ccc3869e137b..836cc2b677d0 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -56,7 +56,7 @@ struct mipi_dsi_host_ops {
>  	int (*detach)(struct mipi_dsi_host *host,
>  		      struct mipi_dsi_device *dsi);
>  	ssize_t (*transfer)(struct mipi_dsi_host *host,
> -			    struct mipi_dsi_msg *msg);
> +			    const struct mipi_dsi_msg *msg);
>  };
>  
>  /**
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  2014-10-13 10:16 ` [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper Thierry Reding
@ 2014-10-13 14:10   ` Andrzej Hajda
  2014-10-14  9:05     ` Thierry Reding
  2014-10-13 14:23   ` Andrzej Hajda
  1 sibling, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-13 14:10 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: YoungJun Cho <yj44.cho@samsung.com>
> 
> This function can be used to set the maximum return packet size for a
> MIPI DSI peripheral.
> 
> Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
> [treding: endianess, kerneldoc, return value]
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 30 ++++++++++++++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |  2 ++
>  2 files changed, 32 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 1702ffd07986..27fc6dac5e4a 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -198,6 +198,36 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi)
>  }
>  EXPORT_SYMBOL(mipi_dsi_detach);
>  
> +/*
> + * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
> + *    the payload in a long packet transmitted from the peripheral back to the
> + *    host processor
> + * @dsi: DSI peripheral device
> + * @value: the maximum size of the payload
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> +					    u16 value)
> +{
> +	u8 tx[2] = { value & 0xff, value >> 8 };
> +	struct mipi_dsi_msg msg;
> +	ssize_t err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;
> +	msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
> +	msg.tx_len = sizeof(tx);
> +	msg.tx_buf = tx;


An alternative (without tx variable) could be:

	__cpu_to_le16s(&value);
	msg.tx_len = sizeof(value);
	msg.tx_buf = &value;

But it is just pico-optimization.


> +
> +	err = dsi->host->ops->transfer(dsi->host, &msg);

I guess the sequence:
	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
		msg.flags = MIPI_DSI_MSG_USE_LPM;

should be before this callback.

The same should be in all other helpers calling ops->transfer,
so I guess it may be good to move it all to separate function, for
example sth like:
... dsi_transfer(dsi, msg)
{
	if (!ops || !ops->transfer)
		return -NOSYS;

	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
		msg.flags = MIPI_DSI_MSG_USE_LPM;


	return ops->transfer(dsi->host, msg);
}

Regards
Andrzej

> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
> +
>  /**
>   * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
>   * @dsi: DSI peripheral device
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 836cc2b677d0..ef50b5d0de57 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -132,6 +132,8 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
>  
>  int mipi_dsi_attach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_detach(struct mipi_dsi_device *dsi);
> +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> +					    u16 value);
>  ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>  				  const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function
  2014-10-13 10:16 ` [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function Thierry Reding
@ 2014-10-13 14:13   ` Andrzej Hajda
  2014-10-14  9:46     ` Thierry Reding
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-13 14:13 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Use the newly introduced mipi_dsi_set_maximum_return_packet_size()
> function to replace an open-coded version.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/panel/panel-s6e8aa0.c | 16 ++--------------
>  1 file changed, 2 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> index 0f85a7c37687..c31e2953f290 100644
> --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
> +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> @@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx)
>  }
>  
>  static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx,
> -						   int size)
> +						   u16 size)


I guess this whole function should be removed and direct call to
mipi_dsi_set_maximum_return_packet_size should be used.

Regards
Andrzej


>  {
>  	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
> -	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
> -	u8 buf[] = {size, 0};
> -	struct mipi_dsi_msg msg = {
> -		.channel = dsi->channel,
> -		.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
> -		.tx_len = sizeof(buf),
> -		.tx_buf = buf
> -	};
>  	int ret;
>  
>  	if (ctx->error < 0)
>  		return;
>  
> -	if (!ops || !ops->transfer)
> -		ret = -EIO;
> -	else
> -		ret = ops->transfer(dsi->host, &msg);
> -
> +	ret = mipi_dsi_set_maximum_return_packet_size(dsi, size);
>  	if (ret < 0) {
>  		dev_err(ctx->dev,
>  			"error %d setting maximum return packet size to %d\n",
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  2014-10-13 10:16 ` [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper Thierry Reding
  2014-10-13 14:10   ` Andrzej Hajda
@ 2014-10-13 14:23   ` Andrzej Hajda
  2014-10-14  9:31     ` Thierry Reding
  1 sibling, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-13 14:23 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> +					    u16 value)
> +{
> +	u8 tx[2] = { value & 0xff, value >> 8 };
> +	struct mipi_dsi_msg msg;
> +	ssize_t err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;
> +	msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
> +	msg.tx_len = sizeof(tx);
> +	msg.tx_buf = tx;

One more thing, why do not use initializer:
struct mipi_dsi_msg msg = {
	.channel = dsi->channel,
	.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
	.tx_len = sizeof(tx),
	.tx_buf = tx
};


Regards
Andrzej

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
                   ` (14 preceding siblings ...)
  2014-10-13 12:29 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical Andrzej Hajda
@ 2014-10-14  8:00 ` Andrzej Hajda
  2014-10-14  9:44   ` Thierry Reding
  15 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14  8:00 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@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@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.

> +
> +		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. And of course changing API would require also
changing current users of the API.
But in the first place it would be good to know why do you want to
change the API? What are benefits of this solution?


Regards
Andrzej


> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;
> +	msg.tx_len = size;
> +	msg.tx_buf = tx;
> +
> +	switch (len) {
> +	case 0:
> +		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
> +		break;
> +	case 1:
> +		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
> +		break;
>  	default:
>  		msg.type = MIPI_DSI_DCS_LONG_WRITE;
>  		break;
> @@ -234,23 +321,27 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
>  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
>  		msg.flags = MIPI_DSI_MSG_USE_LPM;
>  
> -	return ops->transfer(dsi->host, &msg);
> +	err = dsi->host->ops->transfer(dsi->host, &msg);
> +
> +	if (len > 0)
> +		kfree(tx);
> +
> +	return err;
>  }
>  EXPORT_SYMBOL(mipi_dsi_dcs_write);
>  
>  /**
> - * mipi_dsi_dcs_read - send DCS read request command
> - * @dsi: DSI device
> - * @cmd: DCS read command
> - * @data: pointer to read buffer
> - * @len: length of @data
> + * mipi_dsi_dcs_read() - send DCS read request command
> + * @dsi: DSI peripheral device
> + * @cmd: DCS command
> + * @data: buffer in which to receive data
> + * @len: size of receive buffer
>   *
> - * Function returns number of read bytes or error code.
> + * Return: The number of bytes read or a negative error code on failure.
>   */
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len)
>  {
> -	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
>  	struct mipi_dsi_msg msg = {
>  		.channel = dsi->channel,
>  		.type = MIPI_DSI_DCS_READ,
> @@ -260,13 +351,13 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  		.rx_len = len
>  	};
>  
> -	if (!ops || !ops->transfer)
> +	if (!dsi->host->ops || !dsi->host->ops->transfer)
>  		return -ENOSYS;
>  
>  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
>  		msg.flags = MIPI_DSI_MSG_USE_LPM;
>  
> -	return ops->transfer(dsi->host, &msg);
> +	return dsi->host->ops->transfer(dsi->host, &msg);
>  }
>  EXPORT_SYMBOL(mipi_dsi_dcs_read);
>  
> diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> index b5217fe37f02..0f85a7c37687 100644
> --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
> +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> @@ -141,7 +141,7 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
>  	if (ctx->error < 0)
>  		return;
>  
> -	ret = mipi_dsi_dcs_write(dsi, data, len);
> +	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
>  	if (ret < 0) {
>  		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len,
>  			data);
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 8569dc5a1026..ccc3869e137b 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -132,8 +132,10 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
>  
>  int mipi_dsi_attach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_detach(struct mipi_dsi_device *dsi);
> -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);
> +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
> +			   const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len);
>  
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/15] drm/dsi: Implement generic read and write commands
  2014-10-13 10:16 ` [PATCH 05/15] drm/dsi: Implement generic read and write commands Thierry Reding
@ 2014-10-14  8:59   ` Andrzej Hajda
  2014-10-14 10:03     ` Thierry Reding
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14  8:59 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Implement generic read and write commands. Selection of the proper data
> type for packets is done automatically based on the number of parameters
> or payload length.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 115 +++++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |   6 +++
>  2 files changed, 121 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 27fc6dac5e4a..7cd69273dbad 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -229,6 +229,121 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
>  EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
>  
>  /**
> + * mipi_dsi_generic_write() - transmit data using a generic write packet
> + * @dsi: DSI peripheral device
> + * @payload: buffer containing the payload
> + * @size: size of payload buffer
> + *
> + * This function will automatically choose the right data type depending on
> + * the payload length.
> + *
> + * Return: The number of bytes transmitted on success or a negative error code
> + * on failure.
> + */
> +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
> +			       size_t size)
> +{
> +	struct mipi_dsi_msg msg;
> +	ssize_t err;
> +	u8 *tx;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;

Again, maybe initializer would be better.

> +	msg.flags = MIPI_DSI_MSG_USE_LPM | MIPI_DSI_MSG_REQ_ACK;


You should not hardcode flags here, these flags have nothing to do with
dsi generic write. But there is general problem of encoding flags in
helpers. Possible solutions I see:
1. Translate DSI flags to msg flags as in case of MIPI_DSI_MODE_LPM ->
MIPI_DSI_MSG_USE_LPM.
2. Copy dsi.mode_flags to msg.flags, or to dedicated field of msg.
3. Call transfer callback with dsi pointer instead of host, this
way it will have access to mode_flags.


> +
> +	if (size > 2) {
> +		tx = kmalloc(2 + size, GFP_KERNEL);
> +		if (!tx)
> +			return -ENOMEM;
> +
> +		tx[0] = (size >> 0) & 0xff;
> +		tx[1] = (size >> 8) & 0xff;
> +
> +		memcpy(tx + 2, payload, size);
> +
> +		msg.tx_len = 2 + size;
> +		msg.tx_buf = tx;
> +	} else {
> +		msg.tx_buf = payload;
> +		msg.tx_len = size;
> +	}

Another API breakage, commented already in response to 1st patch.


> +
> +	switch (size) {
> +	case 0:
> +		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
> +		break;
> +
> +	case 1:
> +		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
> +		break;
> +
> +	case 2:
> +		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
> +		break;
> +
> +	default:
> +		msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
> +		break;
> +	}
> +
> +	err = dsi->host->ops->transfer(dsi->host, &msg);
> +
> +	if (size > 2)
> +		kfree(tx);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(mipi_dsi_generic_write);
> +
> +/**
> + * mipi_dsi_generic_read() - receive data using a generic read packet
> + * @dsi: DSI peripheral device
> + * @params: buffer containing the request parameters
> + * @num_params: number of request parameters
> + * @data: buffer in which to return the received data
> + * @size: size of receive buffer
> + *
> + * This function will automatically choose the right data type depending on
> + * the number of parameters passed in.
> + *
> + * Return: The number of bytes successfully read or a negative error code on
> + * failure.
> + */
> +ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
> +			      size_t num_params, void *data, size_t size)
> +{
> +	struct mipi_dsi_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.channel = dsi->channel;
> +	msg.flags = MIPI_DSI_MSG_USE_LPM;
> +	msg.tx_len = num_params;
> +	msg.tx_buf = params;
> +	msg.rx_len = size;
> +	msg.rx_buf = data;
> +
> +	switch (num_params) {
> +	case 0:
> +		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
> +		break;
> +
> +	case 1:
> +		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
> +		break;
> +
> +	case 2:
> +		msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return dsi->host->ops->transfer(dsi->host, &msg);
> +}
> +EXPORT_SYMBOL(mipi_dsi_generic_read);
> +
> +/**
>   * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
>   * @dsi: DSI peripheral device
>   * @data: buffer containing data to be transmitted
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index ef50b5d0de57..92ca66306702 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -134,6 +134,12 @@ int mipi_dsi_attach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_detach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
>  					    u16 value);
> +
> +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
> +			       size_t size);
> +ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
> +			      size_t num_params, void *data, size_t size);
> +
>  ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>  				  const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
> 

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical
  2014-10-13 12:29 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical Andrzej Hajda
@ 2014-10-14  9:00   ` Thierry Reding
  2014-10-21 11:10     ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-14  9:00 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 10623 bytes --]

On Mon, Oct 13, 2014 at 02:29:15PM +0200, Andrzej Hajda wrote:
> Hi Thierry,
> 
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > From: Thierry Reding <treding@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().
> 
> As we discussed before I do not see much symmetry here - dcs read is
> a kind of write command with non empty response.

The symmetry is in the API. DCS is a command specification, therefore
the command is the primary means of identification. Hiding the command
inside the payload buffer obfuscates in my opinion. Using an explicit
command parameter makes it immediately obvious from the function call
what command is being sent without having to look up the static buffer
where the command byte is embedded.

This is really the same as for I2C for example. You can use the very
low-level i2c_transfer() and it will allow you to perform any type of
transfer. That's the equivalent of the DSI host's .transfer() function.
On top of that you get helpers like i2c_smbus_write_byte_data() and
i2c_smbus_read_byte_data(), which both take a command byte. For writes
that command byte will likely be sent in the same transfer as the data,
but the important aspect here is that you get a high-level API because
it makes it easier or more intuitive to perform the task at hand.

> The new function simplifies calls in case write has no arguments,
> and it complicates it in other cases.

I don't see what is complicated here. The code itself may be slightly
more complicated, but I think the advantage of having a consistent API
outweighs that.

> But the same simplification could be done by just providing inline function,
> for example:
> static inline ... mipi_dsi_dcs_write_cmd0(dsi, cmd)
> {
>     return mipi_dsi_dcs_write(dsi, &cmd, 1);
> }

You get exactly the same using mipi_dsi_dcs_write(dsi, cmd, NULL, 0)
with the implementation proposed here. Again I think that's a pretty
natural representation of what's happening: "send command <cmd> with
no payload".

The above simplification works only for no parameters, whereas what I
proposed works with parameters, too:

	mipi_dsi_dcs_write(dsi, cmd, value, sizeof(value));

I essentially allows you to construct DCS commands straight from the
specification by filling an array with the parameters as given and
passing it as payload for the command.

> This way we would have simplified calls for commands without arguments
> but without complication in other cases.

Really, I don't see what's complicated about it. Yes, the implementation
is somewhat more complex and requires an allocation. But there are fast
paths in the kernel to deal with those, so I wouldn't expect any of the
calls to take significantly longer than if this was written into a
statically allocated buffer.

> > 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@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);
> 
> It is just nitpick, but why did you replaced ops variable
> by multiple dsi->host->ops expressions? I though it is a good
> practice to avoid evaluating the same expression multiple times.

I thought I was making this consistent with other functions, but
checking again I did exactly the opposite =). As for the evaluation I
don't see the point. Any compiler worth its salt will notice that we're
using the same object and optimize this, whether we store it in a local
variable or not.

Also I find it slightly confusing that we have a local variable for the
ops, but then need to do dsi->host to get a reference to the host.

Anyway, I'll remove that change to restore consistency with other
functions.

> > +}
> > +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;
> > +
> > +		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;
> > +	}
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.channel = dsi->channel;
> > +	msg.tx_len = size;
> > +	msg.tx_buf = tx;
> > +
> > +	switch (len) {
> > +	case 0:
> > +		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
> > +		break;
> > +	case 1:
> > +		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
> > +		break;
> >  	default:
> >  		msg.type = MIPI_DSI_DCS_LONG_WRITE;
> >  		break;
> > @@ -234,23 +321,27 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
> >  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
> >  		msg.flags = MIPI_DSI_MSG_USE_LPM;
> >  
> > -	return ops->transfer(dsi->host, &msg);
> > +	err = dsi->host->ops->transfer(dsi->host, &msg);
> 
> I think it would be better to replace the code above (starting from
> memset) by:
>     err = mipi_dsi_dcs_write_buffer(dsi, tx, size)

Good point, will do.

> 
> > +
> > +	if (len > 0)
> > +		kfree(tx);
> > +
> > +	return err;
> >  }
> >  EXPORT_SYMBOL(mipi_dsi_dcs_write);
> >  
> >  /**
> > - * mipi_dsi_dcs_read - send DCS read request command
> > - * @dsi: DSI device
> > - * @cmd: DCS read command
> > - * @data: pointer to read buffer
> > - * @len: length of @data
> > + * mipi_dsi_dcs_read() - send DCS read request command
> > + * @dsi: DSI peripheral device
> > + * @cmd: DCS command
> > + * @data: buffer in which to receive data
> > + * @len: size of receive buffer
> >   *
> > - * Function returns number of read bytes or error code.
> > + * Return: The number of bytes read or a negative error code on failure.
> >   */
> >  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
> >  			  size_t len)
> >  {
> > -	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
> >  	struct mipi_dsi_msg msg = {
> >  		.channel = dsi->channel,
> >  		.type = MIPI_DSI_DCS_READ,
> > @@ -260,13 +351,13 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
> >  		.rx_len = len
> >  	};
> >  
> > -	if (!ops || !ops->transfer)
> > +	if (!dsi->host->ops || !dsi->host->ops->transfer)
> >  		return -ENOSYS;
> >  
> >  	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
> >  		msg.flags = MIPI_DSI_MSG_USE_LPM;
> >  
> > -	return ops->transfer(dsi->host, &msg);
> > +	return dsi->host->ops->transfer(dsi->host, &msg);
> 
> Again, why multiple evaluations of dsi->host->ops vs single one?

I've undone that change for consistency.

Thanks,
Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  2014-10-13 14:10   ` Andrzej Hajda
@ 2014-10-14  9:05     ` Thierry Reding
  0 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14  9:05 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2762 bytes --]

On Mon, Oct 13, 2014 at 04:10:31PM +0200, Andrzej Hajda wrote:
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > From: YoungJun Cho <yj44.cho@samsung.com>
> > 
> > This function can be used to set the maximum return packet size for a
> > MIPI DSI peripheral.
> > 
> > Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
> > [treding: endianess, kerneldoc, return value]
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/gpu/drm/drm_mipi_dsi.c | 30 ++++++++++++++++++++++++++++++
> >  include/drm/drm_mipi_dsi.h     |  2 ++
> >  2 files changed, 32 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> > index 1702ffd07986..27fc6dac5e4a 100644
> > --- a/drivers/gpu/drm/drm_mipi_dsi.c
> > +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> > @@ -198,6 +198,36 @@ int mipi_dsi_detach(struct mipi_dsi_device *dsi)
> >  }
> >  EXPORT_SYMBOL(mipi_dsi_detach);
> >  
> > +/*
> > + * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
> > + *    the payload in a long packet transmitted from the peripheral back to the
> > + *    host processor
> > + * @dsi: DSI peripheral device
> > + * @value: the maximum size of the payload
> > + *
> > + * Return: 0 on success or a negative error code on failure.
> > + */
> > +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > +					    u16 value)
> > +{
> > +	u8 tx[2] = { value & 0xff, value >> 8 };
> > +	struct mipi_dsi_msg msg;
> > +	ssize_t err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.channel = dsi->channel;
> > +	msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
> > +	msg.tx_len = sizeof(tx);
> > +	msg.tx_buf = tx;
> 
> 
> An alternative (without tx variable) could be:
> 
> 	__cpu_to_le16s(&value);
> 	msg.tx_len = sizeof(value);
> 	msg.tx_buf = &value;
> 
> But it is just pico-optimization.
> 
> 
> > +
> > +	err = dsi->host->ops->transfer(dsi->host, &msg);
> 
> I guess the sequence:
> 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
> 		msg.flags = MIPI_DSI_MSG_USE_LPM;
> 
> should be before this callback.
> 
> The same should be in all other helpers calling ops->transfer,
> so I guess it may be good to move it all to separate function, for
> example sth like:
> ... dsi_transfer(dsi, msg)
> {
> 	if (!ops || !ops->transfer)
> 		return -NOSYS;
> 
> 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
> 		msg.flags = MIPI_DSI_MSG_USE_LPM;
> 
> 
> 	return ops->transfer(dsi->host, msg);
> }

Good idea. It'd be slightly better if we could check for valid ops
earlier, but I don't think it's really worth it. I'll add a helper that
performs these common checks and rebase the other patches on top of it.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper
  2014-10-13 14:23   ` Andrzej Hajda
@ 2014-10-14  9:31     ` Thierry Reding
  0 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14  9:31 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 780 bytes --]

On Mon, Oct 13, 2014 at 04:23:43PM +0200, Andrzej Hajda wrote:
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > +					    u16 value)
> > +{
> > +	u8 tx[2] = { value & 0xff, value >> 8 };
> > +	struct mipi_dsi_msg msg;
> > +	ssize_t err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.channel = dsi->channel;
> > +	msg.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
> > +	msg.tx_len = sizeof(tx);
> > +	msg.tx_buf = tx;
> 
> One more thing, why do not use initializer:
> struct mipi_dsi_msg msg = {
> 	.channel = dsi->channel,
> 	.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
> 	.tx_len = sizeof(tx),
> 	.tx_buf = tx
> };

Personal preference, that's all.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14  8:00 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Andrzej Hajda
@ 2014-10-14  9:44   ` Thierry Reding
  2014-10-14 10:38     ` Andrzej Hajda
  2014-10-14 10:59     ` Andrzej Hajda
  0 siblings, 2 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14  9:44 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 6096 bytes --]

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@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@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.

> And of course changing API would require also changing current users of
> the API.

There's a single user of this function and this patch switches it over
to the compatibility function mipi_dsi_dcs_write_buffer().

> But in the first place it would be good to know why do you want to
> change the API? What are benefits of this solution?

I've already explained this elsewhere.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function
  2014-10-13 14:13   ` Andrzej Hajda
@ 2014-10-14  9:46     ` Thierry Reding
  0 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14  9:46 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1406 bytes --]

On Mon, Oct 13, 2014 at 04:13:16PM +0200, Andrzej Hajda wrote:
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > Use the newly introduced mipi_dsi_set_maximum_return_packet_size()
> > function to replace an open-coded version.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/gpu/drm/panel/panel-s6e8aa0.c | 16 ++--------------
> >  1 file changed, 2 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> > index 0f85a7c37687..c31e2953f290 100644
> > --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
> > +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
> > @@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx)
> >  }
> >  
> >  static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx,
> > -						   int size)
> > +						   u16 size)
> 
> 
> I guess this whole function should be removed and direct call to
> mipi_dsi_set_maximum_return_packet_size should be used.

There's additional error handling logic in this function which would
have needed extra untangling. Since I don't have the hardware to test
this change I kept it to a minimum possible to reduce the risk of
accidentally breaking anything.

Feel free to send a follow-on patch cleaning this up as you see fit.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/15] drm/dsi: Implement generic read and write commands
  2014-10-14  8:59   ` Andrzej Hajda
@ 2014-10-14 10:03     ` Thierry Reding
  2014-10-15 13:32       ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 10:03 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 4296 bytes --]

On Tue, Oct 14, 2014 at 10:59:26AM +0200, Andrzej Hajda wrote:
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > Implement generic read and write commands. Selection of the proper data
> > type for packets is done automatically based on the number of parameters
> > or payload length.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/gpu/drm/drm_mipi_dsi.c | 115 +++++++++++++++++++++++++++++++++++++++++
> >  include/drm/drm_mipi_dsi.h     |   6 +++
> >  2 files changed, 121 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> > index 27fc6dac5e4a..7cd69273dbad 100644
> > --- a/drivers/gpu/drm/drm_mipi_dsi.c
> > +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> > @@ -229,6 +229,121 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> >  EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
> >  
> >  /**
> > + * mipi_dsi_generic_write() - transmit data using a generic write packet
> > + * @dsi: DSI peripheral device
> > + * @payload: buffer containing the payload
> > + * @size: size of payload buffer
> > + *
> > + * This function will automatically choose the right data type depending on
> > + * the payload length.
> > + *
> > + * Return: The number of bytes transmitted on success or a negative error code
> > + * on failure.
> > + */
> > +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
> > +			       size_t size)
> > +{
> > +	struct mipi_dsi_msg msg;
> > +	ssize_t err;
> > +	u8 *tx;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.channel = dsi->channel;
> 
> Again, maybe initializer would be better.

Why?

> > +	msg.flags = MIPI_DSI_MSG_USE_LPM | MIPI_DSI_MSG_REQ_ACK;
> 
> 
> You should not hardcode flags here, these flags have nothing to do with
> dsi generic write.

Agreed, these seem to be left-over from debugging that I overlooked when
squashing together various patches.

> But there is general problem of encoding flags in
> helpers. Possible solutions I see:
> 1. Translate DSI flags to msg flags as in case of MIPI_DSI_MODE_LPM ->
> MIPI_DSI_MSG_USE_LPM.

That's how it was originally intended. The DSI device's flags would be
used to derive message flags, yet wouldn't be an exact copy because not
all flags are relevant to message transfers.

There's a bit of an inconsistency here, though. MIPI_DSI_MODE_LPM isn't
very clearly defined. It says:

	/* transmit data in low power */
	#define MIPI_DSI_MODE_LPM	BIT(11)

Whereas:

	/* use Low Power Mode to transmit message */
	#define MIPI_DSI_MSG_USE_LPM	BIT(1)

Currently we assume that data == message in the above. However
MIPI_DSI_MODE_LPM could also mean that video data is supposed to be
transmitted in low-power mode.

I vaguely remember discussing this with you and Inki (?) before in the
context of continuous vs. non-continuous clocks, but there didn't seem
to be any final outcome on that discussion.

According to the specification, version 1.02.00, section 5.2, "The
peripheral shall be capable of receiving any transmission in Low Power
or High Speed Mode." That would indicate that MIPI_DSI_MSG_USE_LPM is
completely unnecessary, unless a device isn't compliant with the spec.

> 2. Copy dsi.mode_flags to msg.flags, or to dedicated field of msg.
> 3. Call transfer callback with dsi pointer instead of host, this
> way it will have access to mode_flags.

The intention was to keep .transfer() agnostic of the device. The idea
was that the DSI core would take care of these flags as necessary and
host implementations wouldn't have to worry about them.

> > +
> > +	if (size > 2) {
> > +		tx = kmalloc(2 + size, GFP_KERNEL);
> > +		if (!tx)
> > +			return -ENOMEM;
> > +
> > +		tx[0] = (size >> 0) & 0xff;
> > +		tx[1] = (size >> 8) & 0xff;
> > +
> > +		memcpy(tx + 2, payload, size);
> > +
> > +		msg.tx_len = 2 + size;
> > +		msg.tx_buf = tx;
> > +	} else {
> > +		msg.tx_buf = payload;
> > +		msg.tx_len = size;
> > +	}
> 
> Another API breakage, commented already in response to 1st patch.

Can you elaborate? How can this be API breakage if it's a new API being
introduced?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14  9:44   ` Thierry Reding
@ 2014-10-14 10:38     ` Andrzej Hajda
  2014-10-14 10:57       ` Thierry Reding
  2014-10-14 10:59     ` Andrzej Hajda
  1 sibling, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14 10:38 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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?
It has nothing to do with helpers symmetry, this is serious API change.

>
>> And of course changing API would require also changing current users of
>> the API.
> There's a single user of this function and this patch switches it over
> to the compatibility function mipi_dsi_dcs_write_buffer().

Mostly panels are users of these functions and these functions uses
transfer callback internally. If we allow different semantic for
transfer msg
we will end up with panels cooperating only with specific dsi hosts.
I do not think it is good direction.

>
>> But in the first place it would be good to know why do you want to
>> change the API? What are benefits of this solution?
> I've already explained this elsewhere.

Where? I remember only one discussion where you claimed this is better
solution
for you [1], but without explanation.

Do you have patches/repo for tegra with transfer callback implemented
with this semantic?
Maybe looking at the code will be helpful.

[1]: http://permalink.gmane.org/gmane.comp.video.dri.devel/110934

Regards
Andrzej

>
> Thierry

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 10:38     ` Andrzej Hajda
@ 2014-10-14 10:57       ` Thierry Reding
  2014-10-14 11:13         ` Thierry Reding
  2014-10-14 11:25         ` Andrzej Hajda
  0 siblings, 2 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 10:57 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 8591 bytes --]

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@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@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?

> It has nothing to do with helpers symmetry, this is serious API change.

No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
different one, mipi_dsi_dcs_write_buffer() and converts the one call
site where the function is used.

Then it introduces a new function that behaves the same but uses a
different signature that takes the DCS command byte as an explicit
parameter instead of embedding the DCS command byte into the "payload"
buffer.

I don't understand why you think we're changing anything fundamental
here.

> >> And of course changing API would require also changing current users of
> >> the API.
> > There's a single user of this function and this patch switches it over
> > to the compatibility function mipi_dsi_dcs_write_buffer().
> 
> Mostly panels are users of these functions and these functions uses
> transfer callback internally. If we allow different semantic for
> transfer msg
> we will end up with panels cooperating only with specific dsi hosts.
> I do not think it is good direction.

That's not at all the intention. Both functions are supposed to keep
working the same way. mipi_dsi_dcs_write_buffer() becomes what was
previously called mipi_dsi_dcs_write() and mipi_dsi_dcs_write() is a
new helper that concatenates a command byte with payload and passes
it into mipi_dsi_dcs_write_buffer().

So if you find that mipi_dsi_dcs_write_buffer() now does the wrong thing
and breaks the s6e8aa0 panel, please point out what exactly is going
wrong so that I can fix it.

> >> But in the first place it would be good to know why do you want to
> >> change the API? What are benefits of this solution?
> > I've already explained this elsewhere.
> 
> Where? I remember only one discussion where you claimed this is better
> solution
> for you [1], but without explanation.

I explained this in an earlier reply to you in this thread.

> Do you have patches/repo for tegra with transfer callback implemented
> with this semantic?
> Maybe looking at the code will be helpful.

I posted these patches for review to dri-devel yesterday, so they should
be available there. If you prefer patchwork, see:

	https://patchwork.kernel.org/patch/5075161/

And look for tegra_dsi_host_transfer().

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14  9:44   ` Thierry Reding
  2014-10-14 10:38     ` Andrzej Hajda
@ 2014-10-14 10:59     ` Andrzej Hajda
  1 sibling, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14 10:59 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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).

In such case you can put size calculation outside of the main if. Maybe
even you can
get rid of size variable and calculate msg.tx_len directly.

Regards
Andrzej

>
>>> +
>>> +		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.
>
>> And of course changing API would require also changing current users of
>> the API.
> There's a single user of this function and this patch switches it over
> to the compatibility function mipi_dsi_dcs_write_buffer().
>
>> But in the first place it would be good to know why do you want to
>> change the API? What are benefits of this solution?
> I've already explained this elsewhere.
>
> Thierry

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 10:57       ` Thierry Reding
@ 2014-10-14 11:13         ` Thierry Reding
  2014-10-14 11:47           ` Thierry Reding
  2014-10-14 11:25         ` Andrzej Hajda
  1 sibling, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 11:13 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1743 bytes --]

On Tue, Oct 14, 2014 at 12:57:31PM +0200, Thierry Reding wrote:
> On Tue, Oct 14, 2014 at 12:38:15PM +0200, Andrzej Hajda wrote:
[...]
> > It has nothing to do with helpers symmetry, this is serious API change.
> 
> No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
> different one, mipi_dsi_dcs_write_buffer() and converts the one call
> site where the function is used.
> 
> Then it introduces a new function that behaves the same but uses a
> different signature that takes the DCS command byte as an explicit
> parameter instead of embedding the DCS command byte into the "payload"
> buffer.
> 
> I don't understand why you think we're changing anything fundamental
> here.

I think I understand now why you think this is a problem. It seems like
the Exynos driver was implemented in a way that it injects the length of
the payload manually, whereas mipi_dsi_dcs_write() is written in a way
to insert the length into the payload already.

Letting host implementations take care of this is just going to lead to
duplicating the same code everywhere. I think we should let the DSI core
figure out what type of packet it is supposed to generate and then build
the complete message using that data. That way what the DSI host gets is
a raw buffer that it just needs to write into the right registers.

Specifically, if we don't do this in the DSI core, then *every* DSI host
driver needs to do something similar to the

	if (exynos_dsi_is_short_dsi_type(msg->type)) {
		...
	}

block in their implementation of the .transfer() function. And none of
that is really driver specific. It's defined by the DSI specification
and therefore has no place in the individual drivers.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 10:57       ` Thierry Reding
  2014-10-14 11:13         ` Thierry Reding
@ 2014-10-14 11:25         ` Andrzej Hajda
  2014-10-14 11:29           ` Thierry Reding
  1 sibling, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14 11:25 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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.

>
>> It has nothing to do with helpers symmetry, this is serious API change.
> No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
> different one, mipi_dsi_dcs_write_buffer() and converts the one call
> site where the function is used.
>
> Then it introduces a new function that behaves the same but uses a
> different signature that takes the DCS command byte as an explicit
> parameter instead of embedding the DCS command byte into the "payload"
> buffer.
>
> I don't understand why you think we're changing anything fundamental
> here.
>
>>>> And of course changing API would require also changing current users of
>>>> the API.
>>> There's a single user of this function and this patch switches it over
>>> to the compatibility function mipi_dsi_dcs_write_buffer().
>> Mostly panels are users of these functions and these functions uses
>> transfer callback internally. If we allow different semantic for
>> transfer msg
>> we will end up with panels cooperating only with specific dsi hosts.
>> I do not think it is good direction.
> That's not at all the intention. Both functions are supposed to keep
> working the same way. mipi_dsi_dcs_write_buffer() becomes what was
> previously called mipi_dsi_dcs_write() and mipi_dsi_dcs_write() is a
> new helper that concatenates a command byte with payload and passes
> it into mipi_dsi_dcs_write_buffer().
>
> So if you find that mipi_dsi_dcs_write_buffer() now does the wrong thing
> and breaks the s6e8aa0 panel, please point out what exactly is going
> wrong so that I can fix it.

The only wrong thing is that s6e8aa0 cannot work with tegra host
and lq101r1sx01 cannot work with Exynos. Not big deal at the moment
but clearly bad design. I guess there is non-zero chance that we will have
a panel which should cooperate with both dsi hosts.


>
>>>> But in the first place it would be good to know why do you want to
>>>> change the API? What are benefits of this solution?
>>> I've already explained this elsewhere.
>> Where? I remember only one discussion where you claimed this is better
>> solution
>> for you [1], but without explanation.
> I explained this in an earlier reply to you in this thread.
>
>> Do you have patches/repo for tegra with transfer callback implemented
>> with this semantic?
>> Maybe looking at the code will be helpful.
> I posted these patches for review to dri-devel yesterday, so they should
> be available there. If you prefer patchwork, see:
>
> 	https://patchwork.kernel.org/patch/5075161/
>
> And look for tegra_dsi_host_transfer().

Thanks I will look at it.

Regards
Andrzej


>
> Thierry

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 11:25         ` Andrzej Hajda
@ 2014-10-14 11:29           ` Thierry Reding
  2014-10-14 13:53             ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 11:29 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 9327 bytes --]

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@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@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. =)

I'm glad we noticed the disconnect this early, where it's still pretty
easy to fix.

> >> It has nothing to do with helpers symmetry, this is serious API change.
> > No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
> > different one, mipi_dsi_dcs_write_buffer() and converts the one call
> > site where the function is used.
> >
> > Then it introduces a new function that behaves the same but uses a
> > different signature that takes the DCS command byte as an explicit
> > parameter instead of embedding the DCS command byte into the "payload"
> > buffer.
> >
> > I don't understand why you think we're changing anything fundamental
> > here.
> >
> >>>> And of course changing API would require also changing current users of
> >>>> the API.
> >>> There's a single user of this function and this patch switches it over
> >>> to the compatibility function mipi_dsi_dcs_write_buffer().
> >> Mostly panels are users of these functions and these functions uses
> >> transfer callback internally. If we allow different semantic for
> >> transfer msg
> >> we will end up with panels cooperating only with specific dsi hosts.
> >> I do not think it is good direction.
> > That's not at all the intention. Both functions are supposed to keep
> > working the same way. mipi_dsi_dcs_write_buffer() becomes what was
> > previously called mipi_dsi_dcs_write() and mipi_dsi_dcs_write() is a
> > new helper that concatenates a command byte with payload and passes
> > it into mipi_dsi_dcs_write_buffer().
> >
> > So if you find that mipi_dsi_dcs_write_buffer() now does the wrong thing
> > and breaks the s6e8aa0 panel, please point out what exactly is going
> > wrong so that I can fix it.
> 
> The only wrong thing is that s6e8aa0 cannot work with tegra host
> and lq101r1sx01 cannot work with Exynos. Not big deal at the moment
> but clearly bad design. I guess there is non-zero chance that we will have
> a panel which should cooperate with both dsi hosts.

Agreed. The set of bytes transferred to the DSI host should be
rigorously defined to make sure they all end up sending the same
commands to the panels.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 11:13         ` Thierry Reding
@ 2014-10-14 11:47           ` Thierry Reding
  0 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 11:47 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2391 bytes --]

On Tue, Oct 14, 2014 at 01:13:38PM +0200, Thierry Reding wrote:
> On Tue, Oct 14, 2014 at 12:57:31PM +0200, Thierry Reding wrote:
> > On Tue, Oct 14, 2014 at 12:38:15PM +0200, Andrzej Hajda wrote:
> [...]
> > > It has nothing to do with helpers symmetry, this is serious API change.
> > 
> > No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
> > different one, mipi_dsi_dcs_write_buffer() and converts the one call
> > site where the function is used.
> > 
> > Then it introduces a new function that behaves the same but uses a
> > different signature that takes the DCS command byte as an explicit
> > parameter instead of embedding the DCS command byte into the "payload"
> > buffer.
> > 
> > I don't understand why you think we're changing anything fundamental
> > here.
> 
> I think I understand now why you think this is a problem. It seems like
> the Exynos driver was implemented in a way that it injects the length of
> the payload manually, whereas mipi_dsi_dcs_write() is written in a way
> to insert the length into the payload already.
> 
> Letting host implementations take care of this is just going to lead to
> duplicating the same code everywhere. I think we should let the DSI core
> figure out what type of packet it is supposed to generate and then build
> the complete message using that data. That way what the DSI host gets is
> a raw buffer that it just needs to write into the right registers.
> 
> Specifically, if we don't do this in the DSI core, then *every* DSI host
> driver needs to do something similar to the
> 
> 	if (exynos_dsi_is_short_dsi_type(msg->type)) {
> 		...
> 	}
> 
> block in their implementation of the .transfer() function. And none of
> that is really driver specific. It's defined by the DSI specification
> and therefore has no place in the individual drivers.

It should be pretty trivial to adapt the Exynos implementation to deal
with the DSI core providing the payload this way. I'm thinking something
like:

	xfer.data[0] = msg->tx_buf[0];

	if (msg->tx_len > 1)
		xfer.data[1] = msg->tx_buf[1];
	else
		xfer.data[1] = 0;

	xfer.tx_len = 0;

	if (msg->tx_len > 2) {
		xfer.tx_payload = &msg->tx_buf[2];
		xfer.tx_len = msg->tx_len - 2;
	}

And it can probably be even more reduced if the now unneeded .data or
.tx_payload fields are removed.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 11:29           ` Thierry Reding
@ 2014-10-14 13:53             ` Andrzej Hajda
  2014-10-14 14:16               ` Thierry Reding
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-14 13:53 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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

Regards
Andrzej

>
> I'm glad we noticed the disconnect this early, where it's still pretty
> easy to fix.
>
>>>> It has nothing to do with helpers symmetry, this is serious API change.
>>> No, it's not. It replaces an existing API, mipi_dsi_dcs_write() with a
>>> different one, mipi_dsi_dcs_write_buffer() and converts the one call
>>> site where the function is used.
>>>
>>> Then it introduces a new function that behaves the same but uses a
>>> different signature that takes the DCS command byte as an explicit
>>> parameter instead of embedding the DCS command byte into the "payload"
>>> buffer.
>>>
>>> I don't understand why you think we're changing anything fundamental
>>> here.
>>>
>>>>>> And of course changing API would require also changing current users of
>>>>>> the API.
>>>>> There's a single user of this function and this patch switches it over
>>>>> to the compatibility function mipi_dsi_dcs_write_buffer().
>>>> Mostly panels are users of these functions and these functions uses
>>>> transfer callback internally. If we allow different semantic for
>>>> transfer msg
>>>> we will end up with panels cooperating only with specific dsi hosts.
>>>> I do not think it is good direction.
>>> That's not at all the intention. Both functions are supposed to keep
>>> working the same way. mipi_dsi_dcs_write_buffer() becomes what was
>>> previously called mipi_dsi_dcs_write() and mipi_dsi_dcs_write() is a
>>> new helper that concatenates a command byte with payload and passes
>>> it into mipi_dsi_dcs_write_buffer().
>>>
>>> So if you find that mipi_dsi_dcs_write_buffer() now does the wrong thing
>>> and breaks the s6e8aa0 panel, please point out what exactly is going
>>> wrong so that I can fix it.
>> The only wrong thing is that s6e8aa0 cannot work with tegra host
>> and lq101r1sx01 cannot work with Exynos. Not big deal at the moment
>> but clearly bad design. I guess there is non-zero chance that we will have
>> a panel which should cooperate with both dsi hosts.
> Agreed. The set of bytes transferred to the DSI host should be
> rigorously defined to make sure they all end up sending the same
> commands to the panels.
>
> Thierry

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 13:53             ` Andrzej Hajda
@ 2014-10-14 14:16               ` Thierry Reding
  2014-10-15 11:01                 ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-14 14:16 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 9540 bytes --]

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@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@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?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-14 14:16               ` Thierry Reding
@ 2014-10-15 11:01                 ` Andrzej Hajda
  2014-10-15 11:11                   ` Thierry Reding
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-15 11:01 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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.


Regards
Andrzej

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-15 11:01                 ` Andrzej Hajda
@ 2014-10-15 11:11                   ` Thierry Reding
  2014-10-15 11:22                     ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Thierry Reding @ 2014-10-15 11:11 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 13103 bytes --]

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@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@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? It seems like it
would fit your hardware better than the current approach.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical
  2014-10-15 11:11                   ` Thierry Reding
@ 2014-10-15 11:22                     ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-15 11:22 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

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@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@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

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/15] drm/dsi: Implement generic read and write commands
  2014-10-14 10:03     ` Thierry Reding
@ 2014-10-15 13:32       ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-15 13:32 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

On 10/14/2014 12:03 PM, Thierry Reding wrote:
> On Tue, Oct 14, 2014 at 10:59:26AM +0200, Andrzej Hajda wrote:
>> On 10/13/2014 12:16 PM, Thierry Reding wrote:
>>> From: Thierry Reding <treding@nvidia.com>
>>>
>>> Implement generic read and write commands. Selection of the proper data
>>> type for packets is done automatically based on the number of parameters
>>> or payload length.
>>>
>>> Signed-off-by: Thierry Reding <treding@nvidia.com>
>>> ---
>>>  drivers/gpu/drm/drm_mipi_dsi.c | 115 +++++++++++++++++++++++++++++++++++++++++
>>>  include/drm/drm_mipi_dsi.h     |   6 +++
>>>  2 files changed, 121 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
>>> index 27fc6dac5e4a..7cd69273dbad 100644
>>> --- a/drivers/gpu/drm/drm_mipi_dsi.c
>>> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
>>> @@ -229,6 +229,121 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
>>>  EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
>>>  
>>>  /**
>>> + * mipi_dsi_generic_write() - transmit data using a generic write packet
>>> + * @dsi: DSI peripheral device
>>> + * @payload: buffer containing the payload
>>> + * @size: size of payload buffer
>>> + *
>>> + * This function will automatically choose the right data type depending on
>>> + * the payload length.
>>> + *
>>> + * Return: The number of bytes transmitted on success or a negative error code
>>> + * on failure.
>>> + */
>>> +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
>>> +			       size_t size)
>>> +{
>>> +	struct mipi_dsi_msg msg;
>>> +	ssize_t err;
>>> +	u8 *tx;
>>> +
>>> +	memset(&msg, 0, sizeof(msg));
>>> +	msg.channel = dsi->channel;
>> Again, maybe initializer would be better.
> Why?
>
>>> +	msg.flags = MIPI_DSI_MSG_USE_LPM | MIPI_DSI_MSG_REQ_ACK;
>>
>> You should not hardcode flags here, these flags have nothing to do with
>> dsi generic write.
> Agreed, these seem to be left-over from debugging that I overlooked when
> squashing together various patches.
>
>> But there is general problem of encoding flags in
>> helpers. Possible solutions I see:
>> 1. Translate DSI flags to msg flags as in case of MIPI_DSI_MODE_LPM ->
>> MIPI_DSI_MSG_USE_LPM.
> That's how it was originally intended. The DSI device's flags would be
> used to derive message flags, yet wouldn't be an exact copy because not
> all flags are relevant to message transfers.

OK

>
> There's a bit of an inconsistency here, though. MIPI_DSI_MODE_LPM isn't
> very clearly defined. It says:
>
> 	/* transmit data in low power */
> 	#define MIPI_DSI_MODE_LPM	BIT(11)
>
> Whereas:
>
> 	/* use Low Power Mode to transmit message */
> 	#define MIPI_DSI_MSG_USE_LPM	BIT(1)
>
> Currently we assume that data == message in the above. However
> MIPI_DSI_MODE_LPM could also mean that video data is supposed to be
> transmitted in low-power mode.
>
> I vaguely remember discussing this with you and Inki (?) before in the
> context of continuous vs. non-continuous clocks, but there didn't seem
> to be any final outcome on that discussion.

According to specs "Video
information should only be transmitted using High Speed Mode"[1],
but there is still command mode. Anyway it would be good to clarify
that it is only for messages.

[1]: DSI spec 1.2a chapter 4.2.2 Video Mode Operation

>
> According to the specification, version 1.02.00, section 5.2, "The
> peripheral shall be capable of receiving any transmission in Low Power
> or High Speed Mode." That would indicate that MIPI_DSI_MSG_USE_LPM is
> completely unnecessary, unless a device isn't compliant with the spec.

Toshiba bridge TC358764 can be controlled only using LPM commands.
IIRC the panel Inki was working on also requires initialization using
only LPM.
Also your patch "Always use LPM" suggests that you also have
non-compliant devices.

Regards
Andrzej

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands
  2014-10-13 10:16 ` [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands Thierry Reding
@ 2014-10-16  7:00   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-16  7:00 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Many peripherals require DCS commands to be sent in low power mode and
> will fail to correctly process them in high speed mode. Section 5.2 of
> the MIPI DSI specification also mandates that on bidirectional lanes,
> data shall be transmitted in low power mode only.

I guess you are referring to phrase:
"For bidirectional Lanes, data shall be transmitted in the
peripheral-to-processor, or reverse,
direction using Low-Power (LP) Mode only".

This phrase is not clear but I guess the part "peripheral-to-processor,
or reverse, direction"
should be read as "peripheral-to-processor (ie. reverse) direction".
Otherwise we would end up
with insane restriction.

>  At worst this change
> will make transmission of DCS commands slower than optimal on some DSI
> peripherals, but it should enable DCS commands to be successfully
> transmitted to any DSI peripheral.

I can imagine necessity of sending DCS/MCS commands in blank periods of
video mode. In such case
speed matters and in corner cases it will not be even possible to
transmit message in LP mode.

>
> If transmission in low power mode turns out to be too slow at some point
> in the future, one possible solution would be to explicitly mark devices
> that support high speed transmission of DCS commands.

We have MIPI_DSI_MODE_LPM flag in dsi.mode_flags it should be enough,
DCS helper should just check it.

Regards
Andrzej

>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index d4d3cf752be7..a0b9c7ea77a7 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -370,6 +370,7 @@ ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>  {
>  	struct mipi_dsi_msg msg = {
>  		.channel = dsi->channel,
> +		.flags = MIPI_DSI_MSG_USE_LPM,
>  		.tx_buf = data,
>  		.tx_len = len
>  	};
> @@ -457,6 +458,7 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
>  	}
>  
>  	memset(&msg, 0, sizeof(msg));
> +	msg.flags = MIPI_DSI_MSG_USE_LPM;
>  	msg.channel = dsi->channel;
>  	msg.tx_len = size;
>  	msg.tx_buf = tx;
> @@ -500,6 +502,7 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  	struct mipi_dsi_msg msg = {
>  		.channel = dsi->channel,
>  		.type = MIPI_DSI_DCS_READ,
> +		.flags = MIPI_DSI_MSG_USE_LPM,
>  		.tx_buf = &cmd,
>  		.tx_len = 1,
>  		.rx_buf = data,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle
  2014-10-13 10:16 ` [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle Thierry Reding
@ 2014-10-16  7:03   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-16  7:03 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Add a function, of_find_mipi_dsi_device_by_node(), that can be used to
> resolve a phandle to a MIPI DSI device.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Acked-by: Andrzej Hajda <a.hajda@samsung.com>
--
Regards
Andrzej

> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 23 +++++++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |  1 +
>  2 files changed, 24 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index a0b9c7ea77a7..0067f914c1ea 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -67,6 +67,29 @@ static struct bus_type mipi_dsi_bus_type = {
>  	.pm = &mipi_dsi_device_pm_ops,
>  };
>  
> +static int of_device_match(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +/**
> + * of_find_mipi_dsi_device_by_node() - find the MIPI DSI device matching a
> + *    device tree node
> + * @np: device tree node
> + *
> + * Return: A pointer to the MIPI DSI device corresponding to @np or NULL if no
> + *    such device exists (or has not been registered yet).
> + */
> +struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np)
> +{
> +	struct device *dev;
> +
> +	dev = bus_find_device(&mipi_dsi_bus_type, NULL, np, of_device_match);
> +
> +	return dev ? to_mipi_dsi_device(dev) : NULL;
> +}
> +EXPORT_SYMBOL(of_find_mipi_dsi_device_by_node);
> +
>  static void mipi_dsi_dev_release(struct device *dev)
>  {
>  	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 17ef2fbb09a5..8f025cc5ce99 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -142,6 +142,7 @@ static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
>  	return container_of(dev, struct mipi_dsi_device, dev);
>  }
>  
> +struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np);
>  int mipi_dsi_attach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_detach(struct mipi_dsi_device *dsi);
>  int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support
  2014-10-13 10:16 ` [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support Thierry Reding
@ 2014-10-20 15:56   ` Andrzej Hajda
  2014-10-31 14:28     ` Thierry Reding
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-20 15:56 UTC (permalink / raw)
  To: Thierry Reding, dri-devel
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, Rob Herring,
	Kumar Gala

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> This panel requires dual-channel mode. The device accepts command-mode
> data on 8 lanes and will therefore need a dual-channel DSI controller.
> The two interfaces that make up this device need to be instantiated in
> the controllers that gang up to provide the dual-channel DSI host.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  .../bindings/panel/sharp,lq101r1sx01.txt           |  46 +++
>  drivers/gpu/drm/panel/Kconfig                      |  13 +
>  drivers/gpu/drm/panel/Makefile                     |   1 +
>  drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c    | 426 +++++++++++++++++++++
>  4 files changed, 486 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
>  create mode 100644 drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
>
> diff --git a/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
> new file mode 100644
> index 000000000000..4ab4380ddac8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
> @@ -0,0 +1,46 @@
> +Sharp Microelectronics 10.1" WQXGA TFT LCD panel
> +
> +This panel requires a dual-channel DSI host to operate. It supports two modes:
> +- left-right: each channel drives the left or right half of the screen
> +- even-odd: each channel drives the even or odd lines of the screen
> +
> +Each of the DSI channels controls a separate DSI peripheral. The peripheral
> +driven by the first link (DSI-LINK1), left or even, is considered the primary
> +peripheral and controls the device. The 'link2' property contains a phandle
> +to the peripheral driven by the second link (DSI-LINK2, right or odd).
> +
> +Note that in video mode the DSI-LINK1 interface always provides the left/even
> +pixels and DSI-LINK2 always provides the right/odd pixels. In command mode it
> +is possible to program either link to drive the left/even or right/odd pixels
> +but for the sake of consistency this binding assumes that the same assignment
> +is chosen as for video mode.
> +
> +Required properties:
> +- compatible: should be "sharp,lq101r1sx01"
> +- link2: phandle to the DSI peripheral on the secondary link. Note that the
> +  presence of this property marks the containing node as DSI-LINK1.
> +- power-supply: phandle of the regulator that provides the supply voltage
> +
> +Optional properties:
> +- backlight: phandle of the backlight device attached to the panel
> +
> +Example:
> +
> +	dsi@54300000 {
> +		panel: panel@0 {
> +			compatible = "sharp,lq101r1sx01";
> +			reg = <0>;
> +
> +			link2 = <&secondary>;
> +
> +			power-supply = <...>;
> +			backlight = <...>;
> +		};
> +	};
> +
> +	dsi@54400000 {
> +		secondary: panel@0 {
> +			compatible = "sharp,lq101r1sx01";
> +			reg = <0>;
> +		};
> +	};

The example does not follow rules above, 2nd node does not contain
required properties.
Maybe it should be clearly stated that power-supply, link2 and backlight
properties can
be present only in LINK1 node; LINK2 node should contain nothing more
than compatible and reg.

I guess if it wouldn't be better if different compatibles could be used
to distinguish LINK1 and LINK2 nodes,
this way you can provide different sets of required/optional properties
for both nodes. As a bonus you can
have different probe/remove/shutdown callback per link.

> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index bee9f72b3a93..024e98ef8e4d 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -27,4 +27,17 @@ config DRM_PANEL_S6E8AA0
>  	select DRM_MIPI_DSI
>  	select VIDEOMODE_HELPERS
>  
> +config DRM_PANEL_SHARP_LQ101R1SX01
> +	tristate "Sharp LQ101R1SX01 panel"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	help
> +	  Say Y here if you want to enable support for Sharp LQ101R1SX01
> +	  TFT-LCD modules. The panel has a 2560x1600 resolution and uses
> +	  24 bit RGB per pixel. It provides a dual MIPI DSI interface to
> +	  the host and has a built-in LED backlight.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called panel-sharp-lq101r1sx01.
> +
>  endmenu
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 8b929212fad7..4b2a0430804b 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -1,3 +1,4 @@
>  obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
>  obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o
>  obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o
> +obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
> new file mode 100644
> index 000000000000..09356697d22f
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
> @@ -0,0 +1,426 @@
> +/*
> + * Copyright (C) 2014 NVIDIA Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +
> +#include <video/mipi_display.h>
> +
> +#include <linux/host1x.h>
> +
> +struct sharp_panel {
> +	struct drm_panel base;
> +	/* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */
> +	struct mipi_dsi_device *link1;
> +	struct mipi_dsi_device *link2;
> +
> +	struct backlight_device *backlight;
> +	struct regulator *supply;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	const struct drm_display_mode *mode;
> +};
> +
> +static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
> +{
> +	return container_of(panel, struct sharp_panel, base);
> +}
> +
> +static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
> +{
> +	u8 payload[3] = { offset >> 8, offset & 0xff, value };
> +	struct mipi_dsi_device *dsi = sharp->link1;
> +	ssize_t err;
> +
> +	err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
> +	if (err < 0)
> +		dev_err(&dsi->dev, "failed to write %02x to %04x: %d\n",
> +			value, offset, err);
> +
> +	err = mipi_dsi_dcs_nop(dsi);
> +	if (err < 0)
> +		dev_err(&dsi->dev, "failed to send DCS nop: %d\n", err);
> +

If mipi_dsi_generic_write fails and mipi_dsi_dcs_nop succeeds function
return success, is it OK?

> +	usleep_range(10, 20);
> +
> +	return err;
> +}
> +
> +static __maybe_unused int sharp_panel_read(struct sharp_panel *sharp,
> +					   u16 offset, u8 *value)
> +{
> +	ssize_t err;
> +
> +	cpu_to_be16s(&offset);
> +
> +	err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset),
> +				    value, sizeof(*value));
> +	if (err < 0)
> +		dev_err(&sharp->link1->dev, "failed to read from %04x: %d\n",
> +			offset, err);
> +
> +	return err;
> +}
> +
> +static int sharp_panel_disable(struct drm_panel *panel)
> +{
> +	struct sharp_panel *sharp = to_sharp_panel(panel);
> +
> +	if (!sharp->enabled)
> +		return 0;

Shouldn't we assume disable callback can be called only on enabled panel?

> +
> +	if (sharp->backlight) {
> +		sharp->backlight->props.power = FB_BLANK_POWERDOWN;
> +		backlight_update_status(sharp->backlight);
> +	}
> +
> +	sharp->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int sharp_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct sharp_panel *sharp = to_sharp_panel(panel);
> +	int err;
> +
> +	if (!sharp->prepared)
> +		return 0;

Shouldn't we assume unprepare callback can be called only on enabled panel?

> +
> +	err = mipi_dsi_dcs_set_display_off(sharp->link1);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to set display off: %d\n", err);
> +
> +	err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
> +
> +	msleep(120);
> +
> +	regulator_disable(sharp->supply);
> +
> +	sharp->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int sharp_setup_symmetrical_split(struct mipi_dsi_device *left,
> +					 struct mipi_dsi_device *right,
> +					 const struct drm_display_mode *mode)
> +{
> +	int err;
> +
> +	err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);
> +	if (err < 0)
> +		dev_err(&left->dev, "failed to set column address: %d\n", err);
> +
> +	err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);
> +	if (err < 0)
> +		dev_err(&left->dev, "failed to set page address: %d\n", err);
> +
> +	err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2,
> +					      mode->hdisplay - 1);
> +	if (err < 0)
> +		dev_err(&right->dev, "failed to set column address: %d\n", err);
> +
> +	err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);
> +	if (err < 0)
> +		dev_err(&right->dev, "failed to set page address: %d\n", err);
> +
> +	return 0;

It always returns 0, maybe it could be void?

> +}
> +
> +static int sharp_panel_prepare(struct drm_panel *panel)
> +{
> +	struct sharp_panel *sharp = to_sharp_panel(panel);
> +	u8 format = MIPI_DCS_PIXEL_FMT_24BIT;
> +	int err;
> +
> +	if (sharp->prepared)
> +		return 0;
> +
> +	err = regulator_enable(sharp->supply);
> +	if (err < 0)
> +		return err;
> +
> +	usleep_range(10000, 20000);
> +
> +	err = mipi_dsi_dcs_soft_reset(sharp->link1);
> +	if (err < 0)
> +		dev_err(panel->dev, "soft reset failed: %d\n", err);
> +
> +	msleep(120);
> +
> +	err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
> +
> +	/*
> +	 * The MIPI DCS specification mandates this delay only between the
> +	 * exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly
> +	 * necessary here.
> +	 */
> +	/*
> +	msleep(120);
> +	*/
> +
> +	/* set left-right mode */
> +	err = sharp_panel_write(sharp, 0x1000, 0x2a);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to set left-right mode: %d\n", err);
> +
> +	/* enable command mode */
> +	err = sharp_panel_write(sharp, 0x1001, 0x01);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to enable command mode: %d\n", err);
> +
> +	err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to set pixel format: %d\n", err);
> +
> +	/*
> +	 * TODO: The device supports both left-right and even-odd split
> +	 * configurations, but this driver currently supports only the left-
> +	 * right split. To support a different mode a mechanism needs to be
> +	 * put in place to communicate the configuration back to the DSI host
> +	 * controller.
> +	 */
> +	err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2,
> +					    sharp->mode);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to set up symmetrical split: %d\n",
> +			err);
> +
> +	err = mipi_dsi_dcs_set_display_on(sharp->link1);
> +	if (err < 0)
> +		dev_err(panel->dev, "failed to set display on: %d\n", err);
> +
> +	sharp->prepared = true;
> +
> +	return err;

You should return 0 here, or if you want to signal error to caller you
should unwind
changes, I guess regulator_disable should be enough.

> +}
> +
> +static int sharp_panel_enable(struct drm_panel *panel)
> +{
> +	struct sharp_panel *sharp = to_sharp_panel(panel);
> +
> +	if (sharp->enabled)
> +		return 0;
> +
> +	if (sharp->backlight) {
> +		sharp->backlight->props.power = FB_BLANK_UNBLANK;
> +		backlight_update_status(sharp->backlight);
> +	}
> +
> +	sharp->enabled = true;
> +
> +	return 0;
> +}
> +
> +static const struct drm_display_mode default_mode = {
> +	.clock = 278000,
> +	.hdisplay = 2560,
> +	.hsync_start = 2560 + 128,
> +	.hsync_end = 2560 + 128 + 64,
> +	.htotal = 2560 + 128 + 64 + 64,
> +	.vdisplay = 1600,
> +	.vsync_start = 1600 + 4,
> +	.vsync_end = 1600 + 4 + 8,
> +	.vtotal = 1600 + 4 + 8 + 32,
> +	.vrefresh = 60,
> +};
> +
> +static int sharp_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct drm_display_mode *mode;
> +
> +	mode = drm_mode_duplicate(panel->drm, &default_mode);
> +	if (!mode) {
> +		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
> +			default_mode.hdisplay, default_mode.vdisplay,
> +			default_mode.vrefresh);
> +		return -ENOMEM;
> +	}
> +
> +	drm_mode_set_name(mode);
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	panel->connector->display_info.width_mm = 217;
> +	panel->connector->display_info.height_mm = 136;
> +
> +	return 1;
> +}
> +
> +static const struct drm_panel_funcs sharp_panel_funcs = {
> +	.disable = sharp_panel_disable,
> +	.unprepare = sharp_panel_unprepare,
> +	.prepare = sharp_panel_prepare,
> +	.enable = sharp_panel_enable,
> +	.get_modes = sharp_panel_get_modes,
> +};
> +
> +static const struct of_device_id sharp_of_match[] = {
> +	{ .compatible = "sharp,lq101r1sx01", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, sharp_of_match);
> +
> +static int sharp_panel_add(struct sharp_panel *sharp)
> +{
> +	struct device_node *np;
> +	int err;
> +
> +	sharp->mode = &default_mode;
> +
> +	sharp->supply = devm_regulator_get(&sharp->link1->dev, "power");
> +	if (IS_ERR(sharp->supply))
> +		return PTR_ERR(sharp->supply);
> +
> +	np = of_parse_phandle(sharp->link1->dev.of_node, "backlight", 0);
> +	if (np) {
> +		sharp->backlight = of_find_backlight_by_node(np);
> +		of_node_put(np);
> +
> +		if (!sharp->backlight)
> +			return -EPROBE_DEFER;
> +	}
> +
> +	drm_panel_init(&sharp->base);
> +	sharp->base.funcs = &sharp_panel_funcs;
> +	sharp->base.dev = &sharp->link1->dev;
> +
> +	err = drm_panel_add(&sharp->base);
> +	if (err < 0)
> +		goto put_backlight;
> +
> +	return 0;
> +
> +put_backlight:
> +	if (sharp->backlight)
> +		put_device(&sharp->backlight->dev);
> +
> +	return err;
> +}
> +
> +static void sharp_panel_del(struct sharp_panel *sharp)
> +{
> +	if (sharp->base.dev)
> +		drm_panel_remove(&sharp->base);
> +
> +	if (sharp->backlight)
> +		put_device(&sharp->backlight->dev);
> +}
> +
> +static int sharp_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct mipi_dsi_device *secondary = NULL;
> +	struct sharp_panel *sharp;
> +	struct device_node *np;
> +	int err;
> +
> +	dsi->lanes = 4;
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags = 0;
> +
> +	/* Find DSI-LINK1 */
> +	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
> +	if (np) {
> +		secondary = of_find_mipi_dsi_device_by_node(np);
> +		of_node_put(np);
> +
> +		if (!secondary)
> +			return -EPROBE_DEFER;
> +	}
> +
> +	sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL);
> +	if (!sharp)
> +		return -ENOMEM;

Do we really need this for LINK2 device? You can just check if
mipi_dsi_get_drvdata
is not null to distinguish them, I suppose.

> +
> +	mipi_dsi_set_drvdata(dsi, sharp);
> +
> +	/* register a panel for only the DSI-LINK1 interface */
> +	if (secondary) {
> +		sharp->link2 = secondary;
> +		sharp->link1 = dsi;
> +
> +		err = sharp_panel_add(sharp);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	err = mipi_dsi_attach(dsi);
> +	if (err < 0) {
> +		sharp_panel_del(sharp);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sharp_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
> +	int err;
> +
> +	/* only detach from host for the DSI-LINK2 interface */
> +	if (!sharp->link2) {
> +		mipi_dsi_detach(dsi);

There is no locking mechanism here, so it is possible that
everything can crash if someone unbind driver from LINK2 and then try to
enable the panel.

> +		return 0;
> +	}
> +
> +	err = sharp_panel_disable(&sharp->base);
> +	if (err < 0)
> +		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);

IMHO calling mipi_dsi_detach below should cause connector to call panel
disable and unprepare so the call above seems to me unnecessary.

> +
> +	err = mipi_dsi_detach(dsi);
> +	if (err < 0)
> +		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
> +
> +	drm_panel_detach(&sharp->base);

drm_panel_attach is called from tegra_dsi_host_attach,
wouldn't be more 'symmetrical' to call drm_panel_detach from
tegra_dsi_host_detach :)

Regards
Andrzej

> +	drm_panel_remove(&sharp->base);
> +
> +	if (sharp->backlight)
> +		put_device(&sharp->backlight->dev);
> +
> +	put_device(&sharp->link2->dev);
> +
> +	return 0;
> +}
> +
> +static void sharp_panel_shutdown(struct mipi_dsi_device *dsi)
> +{
> +	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
> +
> +	sharp_panel_disable(&sharp->base);
> +}
> +
> +static struct mipi_dsi_driver sharp_panel_driver = {
> +	.driver = {
> +		.name = "panel-sharp-lq101r1sx01",
> +		.of_match_table = sharp_of_match,
> +	},
> +	.probe = sharp_panel_probe,
> +	.remove = sharp_panel_remove,
> +	.shutdown = sharp_panel_shutdown,
> +};
> +module_mipi_dsi_driver(sharp_panel_driver);
> +
> +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
> +MODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 06/15] drm/dsi: Implement some standard DCS commands
  2014-10-13 10:16 ` [PATCH 06/15] drm/dsi: Implement some standard DCS commands Thierry Reding
@ 2014-10-21  8:36   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21  8:36 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: YoungJun Cho <yj44.cho@samsung.com>
>
> Add helpers for the {enter,exit}_sleep_mode, set_display_{on,off} and
> set_tear_{on,off} DCS commands.
>
> Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
> [treding: kerneldoc and other minor cleanup]
> Signed-off-by: Thierry Reding <treding@nvidia.com>

The code below clearly shows that returning number of written bytes
by mipi_dsi_dcs_write is useless and causes only code bloat.
If mipi_dsi_dcs_write would return only error these function could be
written as static inlines in header file, for example:

static inline int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
{
    return mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
}

Regards
Andrzej

> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 118 +++++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |  19 +++++++
>  2 files changed, 137 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 7cd69273dbad..8bb38db861ec 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -506,6 +506,124 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  }
>  EXPORT_SYMBOL(mipi_dsi_dcs_read);
>  
> +/**
> + * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
> + *    display module except interface communication
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
> +
> +/**
> + * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
> + *    module
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
> +
> +/**
> + * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
> + *    display device
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
> +
> +/**
> + * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
> + *    display device
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure
> + */
> +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
> +
> +/**
> + * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
> + *    output signal on the TE signal line
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure
> + */
> +int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
> +
> +/**
> + * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
> + *    output signal on the TE signal line.
> + * @dsi: DSI peripheral device
> + * @mode: the Tearing Effect Output Line mode
> + *
> + * Return: 0 on success or a negative error code on failure
> + */
> +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
> +			     enum mipi_dsi_dcs_tear_mode mode)
> +{
> +	u8 value = mode;
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
> +				 sizeof(value));
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
> +
>  static int mipi_dsi_drv_probe(struct device *dev)
>  {
>  	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 92ca66306702..bde663725a58 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -140,12 +140,31 @@ ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
>  ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
>  			      size_t num_params, void *data, size_t size);
>  
> +/**
> + * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
> + * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
> + *    information only
> + * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
> + *    V-Blanking and H-Blanking information
> + */
> +enum mipi_dsi_dcs_tear_mode {
> +	MIPI_DSI_DCS_TEAR_MODE_VBLANK,
> +	MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
> +};
> +
>  ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>  				  const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
>  			   const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len);
> +int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
> +			     enum mipi_dsi_dcs_tear_mode mode);
>  
>  /**
>   * struct mipi_dsi_driver - DSI driver

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 07/15] drm/dsi: Add to DocBook documentation
  2014-10-13 10:16 ` [PATCH 07/15] drm/dsi: Add to DocBook documentation Thierry Reding
@ 2014-10-21  9:26   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21  9:26 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Integrate the MIPI DSI helpers into DocBook and clean up various
> kerneldoc warnings. Also add a brief DOC section and clarify some
> aspects of the mipi_dsi_host struct's .transfer() operation.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
Thanks

Acked-by: Andrzej Hajda <a.hajda@samsung.com>
--
Regards
Andrzej
> ---
>  Documentation/DocBook/drm.tmpl |  6 ++++++
>  drivers/gpu/drm/drm_mipi_dsi.c | 18 ++++++++++++++++--
>  include/drm/drm_mipi_dsi.h     | 16 ++++++++++++++--
>  3 files changed, 36 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
> index be35bc328b77..da733c28c92f 100644
> --- a/Documentation/DocBook/drm.tmpl
> +++ b/Documentation/DocBook/drm.tmpl
> @@ -2343,6 +2343,12 @@ void intel_crt_init(struct drm_device *dev)
>  !Edrivers/gpu/drm/drm_dp_mst_topology.c
>      </sect2>
>      <sect2>
> +      <title>MIPI DSI Helper Functions Reference</title>
> +!Pdrivers/gpu/drm/drm_mipi_dsi.c dsi helpers
> +!Iinclude/drm/drm_mipi_dsi.h
> +!Edrivers/gpu/drm/drm_mipi_dsi.c
> +    </sect2>
> +    <sect2>
>        <title>EDID Helper Functions Reference</title>
>  !Edrivers/gpu/drm/drm_edid.c
>      </sect2>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 8bb38db861ec..6749b88a6c72 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -35,6 +35,16 @@
>  
>  #include <video/mipi_display.h>
>  
> +/**
> + * DOC: dsi helpers
> + *
> + * These functions contain some common logic and helpers to deal with MIPI DSI
> + * peripherals.
> + *
> + * Helpers are provided for a number of standard MIPI DSI command as well as a
> + * subset of the MIPI DCS command set.
> + */
> +
>  static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv)
>  {
>  	return of_driver_match_device(dev, drv);
> @@ -649,8 +659,10 @@ static void mipi_dsi_drv_shutdown(struct device *dev)
>  }
>  
>  /**
> - * mipi_dsi_driver_register - register a driver for DSI devices
> + * mipi_dsi_driver_register() - register a driver for DSI devices
>   * @drv: DSI driver structure
> + *
> + * Return: 0 on success or a negative error code on failure.
>   */
>  int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
>  {
> @@ -667,8 +679,10 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
>  EXPORT_SYMBOL(mipi_dsi_driver_register);
>  
>  /**
> - * mipi_dsi_driver_unregister - unregister a driver for DSI devices
> + * mipi_dsi_driver_unregister() - unregister a driver for DSI devices
>   * @drv: DSI driver structure
> + *
> + * Return: 0 on success or a negative error code on failure.
>   */
>  void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
>  {
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index bde663725a58..8693711eb623 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -26,6 +26,7 @@ struct mipi_dsi_device;
>   * struct mipi_dsi_msg - read/write DSI buffer
>   * @channel: virtual channel id
>   * @type: payload data type
> + * @flags: flags controlling this message transmission
>   * @tx_len: length of @tx_buf
>   * @tx_buf: data to be written
>   * @rx_len: length of @rx_buf
> @@ -47,8 +48,19 @@ struct mipi_dsi_msg {
>   * struct mipi_dsi_host_ops - DSI bus operations
>   * @attach: attach DSI device to DSI host
>   * @detach: detach DSI device from DSI host
> - * @transfer: send and/or receive DSI packet, return number of received bytes,
> - * 	      or error
> + * @transfer: transmit a DSI packet
> + *
> + * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
> + * structures. This structure contains information about the type of packet
> + * being transmitted as well as the transmit and receive buffers. When an
> + * error is encountered during transmission, this function will return a
> + * negative error code. On success it shall return the number of bytes
> + * transmitted for write packets or the number of bytes received for read
> + * packets.
> + *
> + * Note that typically DSI packet transmission is atomic, so the .transfer()
> + * function will seldomly return anything other than the number of bytes
> + * contained in the transmit buffer on success.
>   */
>  struct mipi_dsi_host_ops {
>  	int (*attach)(struct mipi_dsi_host *host,

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command
  2014-10-13 10:16 ` [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command Thierry Reding
@ 2014-10-21  9:48   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21  9:48 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Provide a small convenience wrapper that transmits a DCS get_power_mode
> command. A set of bitmasks for the mode bits is also provided.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
Besides one nitpick below.

Acked-by: Andrzej Hajda <a.hajda@samsung.com>

---
 drivers/gpu/drm/drm_mipi_dsi.c | 21 +++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  7 +++++++
 2 files changed, 28 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 60bd68b520ae..4e016a6a9fc3 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -553,6 +553,27 @@ int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
 EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
 
 /**
+ * mipi_dsi_dcs_get_power_mode() - query the display module's current power
+ *    mode
+ * @dsi: DSI peripheral device
+ * @mode: return location for the current power mode
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
+{
+	ssize_t err;
+
+	err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
+				sizeof(*mode));
+	if (err < 0)
+		return err;
+
+	return 0;


Maybe better would be:
if (err == 1)
    return 0;

return err < 0 ? err : -ENODATA;

Or sth similar.

I wonder also if it would not be better to make the function inline ?

> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
> +
> +/**
>   * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
>   *    display module except interface communication
>   * @dsi: DSI peripheral device
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 2afbd5853f78..b659b99ac890 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -164,6 +164,12 @@ enum mipi_dsi_dcs_tear_mode {
>  	MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
>  };
>  
> +#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
> +#define MIPI_DSI_DCS_POWER_MODE_NORMAL  (1 << 3)
> +#define MIPI_DSI_DCS_POWER_MODE_SLEEP   (1 << 4)
> +#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
> +#define MIPI_DSI_DCS_POWER_MODE_IDLE    (1 << 6)
> +
>  ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
>  				  const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
> @@ -172,6 +178,7 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len);
>  int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
> +int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
>  int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH 08/15] drm/dsi: Implement DCS nop command
  2014-10-13 10:16 ` [PATCH 08/15] drm/dsi: Implement DCS nop command Thierry Reding
@ 2014-10-21  9:53   ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21  9:53 UTC (permalink / raw)
  To: Thierry Reding, dri-devel

On 10/13/2014 12:16 PM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Provide a small convenience wrapper that transmits a DCS nop command.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>

For this and patches 09, 11, 12 the same comments apply as for 06/15.
Beside this they look OK to me.


Regards
Andrzej

> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 18 ++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |  1 +
>  2 files changed, 19 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 6749b88a6c72..c88dcda2f4ac 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -517,6 +517,24 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  EXPORT_SYMBOL(mipi_dsi_dcs_read);
>  
>  /**
> + * mipi_dsi_dcs_nop() - send DCS nop packet
> + * @dsi: DSI peripheral device
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
> +{
> +	ssize_t err;
> +
> +	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dsi_dcs_nop);
> +
> +/**
>   * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
>   *    display module except interface communication
>   * @dsi: DSI peripheral device
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 8693711eb623..be16c8e769bd 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -170,6 +170,7 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
>  			   const void *data, size_t len);
>  ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
>  			  size_t len);
> +int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
>  int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical
  2014-10-14  9:00   ` Thierry Reding
@ 2014-10-21 11:10     ` Andrzej Hajda
  2014-10-21 11:19       ` Andrzej Hajda
  0 siblings, 1 reply; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21 11:10 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

On 10/14/2014 11:00 AM, Thierry Reding wrote:
> On Mon, Oct 13, 2014 at 02:29:15PM +0200, Andrzej Hajda wrote:
>> Hi Thierry,
>>
>> On 10/13/2014 12:16 PM, Thierry Reding wrote:
>>> From: Thierry Reding <treding@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().
>> As we discussed before I do not see much symmetry here - dcs read is
>> a kind of write command with non empty response.
> The symmetry is in the API. DCS is a command specification, therefore
> the command is the primary means of identification. Hiding the command
> inside the payload buffer obfuscates in my opinion. Using an explicit
> command parameter makes it immediately obvious from the function call
> what command is being sent without having to look up the static buffer
> where the command byte is embedded.

1. According to specs command is an integral part of the payload,
separating it from the payload is unnatural.
2. It is rather difficult to obfuscate command using static buffers, you can
  use variadic macros as in panel_s6e8aa0.c, for example:
    mipi_dsi_dcs(dsi, MIPI_DCS_SET_PIXEL_FORMAT, 13);
  it is the nicest way IMHO - you do not need to pass to the call extra
arguments,
  neither number of parameters, neither pointers, you just pass command
  and its parameters.

  But if you do not like variadic macros you can still use static buffers:
    u8 dcs_set_pixel_format[] = {MIPI_DCS_SET_PIXEL_FORMAT, 13};
    ...
    mipi_dsi_dcs_write(dsi, dcs_set_pixel_format,
ARRAY_SIZE(dcs_set_pixel_format));

  As you see you should pass buffer name, which should clearly indicate
  what is its content, again no obfuscation.

Again, if it does not convince you I am fine with this patch. It leaves
the possibility
to use 'old' API, so it is OK to me.

Regards
Andrzej

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical
  2014-10-21 11:10     ` Andrzej Hajda
@ 2014-10-21 11:19       ` Andrzej Hajda
  0 siblings, 0 replies; 51+ messages in thread
From: Andrzej Hajda @ 2014-10-21 11:19 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

On 10/21/2014 01:10 PM, Andrzej Hajda wrote:
> On 10/14/2014 11:00 AM, Thierry Reding wrote:
>> On Mon, Oct 13, 2014 at 02:29:15PM +0200, Andrzej Hajda wrote:
>>> Hi Thierry,
>>>
>>> On 10/13/2014 12:16 PM, Thierry Reding wrote:
>>>> From: Thierry Reding <treding@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().
>>> As we discussed before I do not see much symmetry here - dcs read is
>>> a kind of write command with non empty response.
>> The symmetry is in the API. DCS is a command specification, therefore
>> the command is the primary means of identification. Hiding the command
>> inside the payload buffer obfuscates in my opinion. Using an explicit
>> command parameter makes it immediately obvious from the function call
>> what command is being sent without having to look up the static buffer
>> where the command byte is embedded.
> 
> 1. According to specs command is an integral part of the payload,
> separating it from the payload is unnatural.
> 2. It is rather difficult to obfuscate command using static buffers, you can
>   use variadic macros as in panel_s6e8aa0.c, for example:
>     mipi_dsi_dcs(dsi, MIPI_DCS_SET_PIXEL_FORMAT, 13);
>   it is the nicest way IMHO - you do not need to pass to the call extra
> arguments,
>   neither number of parameters, neither pointers, you just pass command
>   and its parameters.
> 
>   But if you do not like variadic macros you can still use static buffers:
>     u8 dcs_set_pixel_format[] = {MIPI_DCS_SET_PIXEL_FORMAT, 13};
>     ...
>     mipi_dsi_dcs_write(dsi, dcs_set_pixel_format,
> ARRAY_SIZE(dcs_set_pixel_format));
> 
>   As you see you should pass buffer name, which should clearly indicate
>   what is its content, again no obfuscation.
> 
> Again, if it does not convince you I am fine with this patch. It leaves
> the possibility
> to use 'old' API, so it is OK to me.

To be more precise, I am fine with introduction of 'symmetrical' API,
there other issues regarding this patch which were discussed elsewhere.

Regards
Andrzej

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support
  2014-10-20 15:56   ` Andrzej Hajda
@ 2014-10-31 14:28     ` Thierry Reding
  0 siblings, 0 replies; 51+ messages in thread
From: Thierry Reding @ 2014-10-31 14:28 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, dri-devel,
	Rob Herring, Kumar Gala


[-- Attachment #1.1: Type: text/plain, Size: 11233 bytes --]

On Mon, Oct 20, 2014 at 05:56:57PM +0200, Andrzej Hajda wrote:
> On 10/13/2014 12:16 PM, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > This panel requires dual-channel mode. The device accepts command-mode
> > data on 8 lanes and will therefore need a dual-channel DSI controller.
> > The two interfaces that make up this device need to be instantiated in
> > the controllers that gang up to provide the dual-channel DSI host.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  .../bindings/panel/sharp,lq101r1sx01.txt           |  46 +++
> >  drivers/gpu/drm/panel/Kconfig                      |  13 +
> >  drivers/gpu/drm/panel/Makefile                     |   1 +
> >  drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c    | 426 +++++++++++++++++++++
> >  4 files changed, 486 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
> >  create mode 100644 drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
> >
> > diff --git a/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
> > new file mode 100644
> > index 000000000000..4ab4380ddac8
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/panel/sharp,lq101r1sx01.txt
> > @@ -0,0 +1,46 @@
> > +Sharp Microelectronics 10.1" WQXGA TFT LCD panel
> > +
> > +This panel requires a dual-channel DSI host to operate. It supports two modes:
> > +- left-right: each channel drives the left or right half of the screen
> > +- even-odd: each channel drives the even or odd lines of the screen
> > +
> > +Each of the DSI channels controls a separate DSI peripheral. The peripheral
> > +driven by the first link (DSI-LINK1), left or even, is considered the primary
> > +peripheral and controls the device. The 'link2' property contains a phandle
> > +to the peripheral driven by the second link (DSI-LINK2, right or odd).
> > +
> > +Note that in video mode the DSI-LINK1 interface always provides the left/even
> > +pixels and DSI-LINK2 always provides the right/odd pixels. In command mode it
> > +is possible to program either link to drive the left/even or right/odd pixels
> > +but for the sake of consistency this binding assumes that the same assignment
> > +is chosen as for video mode.
> > +
> > +Required properties:
> > +- compatible: should be "sharp,lq101r1sx01"
> > +- link2: phandle to the DSI peripheral on the secondary link. Note that the
> > +  presence of this property marks the containing node as DSI-LINK1.
> > +- power-supply: phandle of the regulator that provides the supply voltage
> > +
> > +Optional properties:
> > +- backlight: phandle of the backlight device attached to the panel
> > +
> > +Example:
> > +
> > +	dsi@54300000 {
> > +		panel: panel@0 {
> > +			compatible = "sharp,lq101r1sx01";
> > +			reg = <0>;
> > +
> > +			link2 = <&secondary>;
> > +
> > +			power-supply = <...>;
> > +			backlight = <...>;
> > +		};
> > +	};
> > +
> > +	dsi@54400000 {
> > +		secondary: panel@0 {
> > +			compatible = "sharp,lq101r1sx01";
> > +			reg = <0>;
> > +		};
> > +	};
> 
> The example does not follow rules above, 2nd node does not contain
> required properties.
> Maybe it should be clearly stated that power-supply, link2 and backlight
> properties can
> be present only in LINK1 node; LINK2 node should contain nothing more
> than compatible and reg.

I've updated the binding documentation to clarify this.

> I guess if it wouldn't be better if different compatibles could be used
> to distinguish LINK1 and LINK2 nodes,
> this way you can provide different sets of required/optional properties
> for both nodes. As a bonus you can
> have different probe/remove/shutdown callback per link.

I think having separate compatibles isn't entirely accurate. They're
both really the same device. There's already the link2 property that
distinguishes both halves.

Having separate probe/remove/shutdown isn't something I consider a
bonus. It would mean that we need to register a second driver. That is,
the device will no longer be driven by a single driver, but rather two
drivers that then need to talk to one another again. And it's not like
there's a lot to do for DSI-LINK2 in the first place, as you point out
yourself.

I'd rather stick with just a single driver and handle the distinction
via the link2 property, which we need to get access to the second
peripheral anyway.

> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
[...]
> > +static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value)
> > +{
> > +	u8 payload[3] = { offset >> 8, offset & 0xff, value };
> > +	struct mipi_dsi_device *dsi = sharp->link1;
> > +	ssize_t err;
> > +
> > +	err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
> > +	if (err < 0)
> > +		dev_err(&dsi->dev, "failed to write %02x to %04x: %d\n",
> > +			value, offset, err);
> > +
> > +	err = mipi_dsi_dcs_nop(dsi);
> > +	if (err < 0)
> > +		dev_err(&dsi->dev, "failed to send DCS nop: %d\n", err);
> > +
> 
> If mipi_dsi_generic_write fails and mipi_dsi_dcs_nop succeeds function
> return success, is it OK?

No, this should always return an error if it fails. If these writes
don't succeed the panel won't be usable anyway. Also returning an error
will actually allow us to decide what to do about the failure in the
caller, where more context exists to make a proper decision.

I've fixed this to propagate the error from mipi_dsi_generic_write() and
mipi_dsi_dcs_nop().

> > +	usleep_range(10, 20);
> > +
> > +	return err;
> > +}
> > +
> > +static __maybe_unused int sharp_panel_read(struct sharp_panel *sharp,
> > +					   u16 offset, u8 *value)
> > +{
> > +	ssize_t err;
> > +
> > +	cpu_to_be16s(&offset);
> > +
> > +	err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset),
> > +				    value, sizeof(*value));
> > +	if (err < 0)
> > +		dev_err(&sharp->link1->dev, "failed to read from %04x: %d\n",
> > +			offset, err);
> > +
> > +	return err;
> > +}
> > +
> > +static int sharp_panel_disable(struct drm_panel *panel)
> > +{
> > +	struct sharp_panel *sharp = to_sharp_panel(panel);
> > +
> > +	if (!sharp->enabled)
> > +		return 0;
> 
> Shouldn't we assume disable callback can be called only on enabled panel?

That's not something the core does for us currently. We might want to
change that at some point, but until then we can simply do what all
other panels do.

> > +
> > +	if (sharp->backlight) {
> > +		sharp->backlight->props.power = FB_BLANK_POWERDOWN;
> > +		backlight_update_status(sharp->backlight);
> > +	}
> > +
> > +	sharp->enabled = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static int sharp_panel_unprepare(struct drm_panel *panel)
> > +{
> > +	struct sharp_panel *sharp = to_sharp_panel(panel);
> > +	int err;
> > +
> > +	if (!sharp->prepared)
> > +		return 0;
> 
> Shouldn't we assume unprepare callback can be called only on enabled panel?

See above.

> > +static int sharp_setup_symmetrical_split(struct mipi_dsi_device *left,
> > +					 struct mipi_dsi_device *right,
> > +					 const struct drm_display_mode *mode)
> > +{
> > +	int err;
> > +
> > +	err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);
> > +	if (err < 0)
> > +		dev_err(&left->dev, "failed to set column address: %d\n", err);
> > +
> > +	err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);
> > +	if (err < 0)
> > +		dev_err(&left->dev, "failed to set page address: %d\n", err);
> > +
> > +	err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2,
> > +					      mode->hdisplay - 1);
> > +	if (err < 0)
> > +		dev_err(&right->dev, "failed to set column address: %d\n", err);
> > +
> > +	err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);
> > +	if (err < 0)
> > +		dev_err(&right->dev, "failed to set page address: %d\n", err);
> > +
> > +	return 0;
> 
> It always returns 0, maybe it could be void?

I think this should also propagate all errors.

> > +static int sharp_panel_prepare(struct drm_panel *panel)
> > +{
[...]
> > +	sharp->prepared = true;
> > +
> > +	return err;
> 
> You should return 0 here, or if you want to signal error to caller you
> should unwind
> changes, I guess regulator_disable should be enough.

Done.

> > +static int sharp_panel_probe(struct mipi_dsi_device *dsi)
> > +{
> > +	struct mipi_dsi_device *secondary = NULL;
> > +	struct sharp_panel *sharp;
> > +	struct device_node *np;
> > +	int err;
> > +
> > +	dsi->lanes = 4;
> > +	dsi->format = MIPI_DSI_FMT_RGB888;
> > +	dsi->mode_flags = 0;
> > +
> > +	/* Find DSI-LINK1 */
> > +	np = of_parse_phandle(dsi->dev.of_node, "link2", 0);
> > +	if (np) {
> > +		secondary = of_find_mipi_dsi_device_by_node(np);
> > +		of_node_put(np);
> > +
> > +		if (!secondary)
> > +			return -EPROBE_DEFER;
> > +	}
> > +
> > +	sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL);
> > +	if (!sharp)
> > +		return -ENOMEM;
> 
> Do we really need this for LINK2 device? You can just check if
> mipi_dsi_get_drvdata
> is not null to distinguish them, I suppose.

Done.

> > +static int sharp_panel_remove(struct mipi_dsi_device *dsi)
> > +{
> > +	struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
> > +	int err;
> > +
> > +	/* only detach from host for the DSI-LINK2 interface */
> > +	if (!sharp->link2) {
> > +		mipi_dsi_detach(dsi);
> 
> There is no locking mechanism here, so it is possible that
> everything can crash if someone unbind driver from LINK2 and then try to
> enable the panel.

I don't think so. Since we're not doing anything with the DSI-LINK2
device anymore it's irrelevant whether it is bound to the driver or
not.

> > +		return 0;
> > +	}
> > +
> > +	err = sharp_panel_disable(&sharp->base);
> > +	if (err < 0)
> > +		dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
> 
> IMHO calling mipi_dsi_detach below should cause connector to call panel
> disable and unprepare so the call above seems to me unnecessary.

I don't think the connector has any business doing anything with the
panel on mipi_dsi_detach(). I suppose we could implement something like
that as part of drm_panel_detach(), but that's not the case today, so
this simply follows what every other panel has done so far.

> > +
> > +	err = mipi_dsi_detach(dsi);
> > +	if (err < 0)
> > +		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
> > +
> > +	drm_panel_detach(&sharp->base);
> 
> drm_panel_attach is called from tegra_dsi_host_attach,
> wouldn't be more 'symmetrical' to call drm_panel_detach from
> tegra_dsi_host_detach :)

No, it's not called from tegra_dsi_host_attach(), it's called as part of
the DSI output initialization at DRM load time.

drm_panel_detach() really needs to be called from two places: when the
panel driver is unloaded and when the connector is unloaded. It seems
like this is another area where we may have to put more thought into how
to handle it more uniformly across drivers.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2014-10-31 14:28 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-13 10:16 [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Thierry Reding
2014-10-13 10:16 ` [PATCH 02/15] drm/dsi: Constify mipi_dsi_msg Thierry Reding
2014-10-13 13:41   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 03/15] drm/dsi: Add mipi_dsi_set_maximum_return_packet_size() helper Thierry Reding
2014-10-13 14:10   ` Andrzej Hajda
2014-10-14  9:05     ` Thierry Reding
2014-10-13 14:23   ` Andrzej Hajda
2014-10-14  9:31     ` Thierry Reding
2014-10-13 10:16 ` [PATCH 04/15] drm/panel: s6e8aa0: Use standard MIPI DSI function Thierry Reding
2014-10-13 14:13   ` Andrzej Hajda
2014-10-14  9:46     ` Thierry Reding
2014-10-13 10:16 ` [PATCH 05/15] drm/dsi: Implement generic read and write commands Thierry Reding
2014-10-14  8:59   ` Andrzej Hajda
2014-10-14 10:03     ` Thierry Reding
2014-10-15 13:32       ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 06/15] drm/dsi: Implement some standard DCS commands Thierry Reding
2014-10-21  8:36   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 07/15] drm/dsi: Add to DocBook documentation Thierry Reding
2014-10-21  9:26   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 08/15] drm/dsi: Implement DCS nop command Thierry Reding
2014-10-21  9:53   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 09/15] drm/dsi: Implement DCS soft_reset command Thierry Reding
2014-10-13 10:16 ` [PATCH 10/15] drm/dsi: Implement DCS get_power_mode command Thierry Reding
2014-10-21  9:48   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 11/15] drm/dsi: Implement DCS {get,set}_pixel_format commands Thierry Reding
2014-10-13 10:16 ` [PATCH 12/15] drm/dsi: Implement DCS set_{column, page}_address commands Thierry Reding
2014-10-13 10:16 ` [PATCH 13/15] drm/dsi: Always use low-power mode for DCS commands Thierry Reding
2014-10-16  7:00   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 14/15] drm/dsi: Resolve MIPI DSI device from phandle Thierry Reding
2014-10-16  7:03   ` Andrzej Hajda
2014-10-13 10:16 ` [PATCH 15/15] drm/panel: Add Sharp LQ101R1SX01 support Thierry Reding
2014-10-20 15:56   ` Andrzej Hajda
2014-10-31 14:28     ` Thierry Reding
2014-10-13 12:29 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read,write}() symmetrical Andrzej Hajda
2014-10-14  9:00   ` Thierry Reding
2014-10-21 11:10     ` Andrzej Hajda
2014-10-21 11:19       ` Andrzej Hajda
2014-10-14  8:00 ` [PATCH v2 01/15] drm/dsi: Make mipi_dsi_dcs_{read, write}() symmetrical Andrzej Hajda
2014-10-14  9:44   ` Thierry Reding
2014-10-14 10:38     ` Andrzej Hajda
2014-10-14 10:57       ` Thierry Reding
2014-10-14 11:13         ` Thierry Reding
2014-10-14 11:47           ` Thierry Reding
2014-10-14 11:25         ` Andrzej Hajda
2014-10-14 11:29           ` Thierry Reding
2014-10-14 13:53             ` Andrzej Hajda
2014-10-14 14:16               ` Thierry Reding
2014-10-15 11:01                 ` Andrzej Hajda
2014-10-15 11:11                   ` Thierry Reding
2014-10-15 11:22                     ` Andrzej Hajda
2014-10-14 10:59     ` Andrzej Hajda

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.