All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH RFC v3 2/2] block/qapi: reduce the execution time of qmp_query_blockstats
From: Dou Liyang @ 2017-01-04  6:58 UTC (permalink / raw)
  To: stefanha, kwolf, armbru, mreitz, eblake, famz, danpb
  Cc: qemu-devel, izumi.taku, caoj.fnst, fanc.fnst, Dou Liyang
In-Reply-To: <1483513091-30661-1-git-send-email-douly.fnst@cn.fujitsu.com>

In order to reduce the execution time, this patch optimize
the qmp_query_blockstats():
Remove the next_query_bds function.
Remove the bdrv_query_stats function.
Remove some judgement sentence.

The original qmp_query_blockstats calls next_query_bds to get
the next objects in each loops. In the next_query_bds, it checks
the query_nodes and blk. It also call bdrv_query_stats to get
the stats, In the bdrv_query_stats, it checks blk and bs each
times. This waste more times, which may stall the main loop a
bit. And if the disk is too many and donot use the dataplane
feature, this may affect the performance in main loop thread.

This patch removes that two functions, and makes the structure
clearly.

Signed-off-by: Dou Liyang <douly.fnst@cn.fujitsu.com>
---
 block/qapi.c | 72 +++++++++++++++++++++++-------------------------------------
 1 file changed, 28 insertions(+), 44 deletions(-)

diff --git a/block/qapi.c b/block/qapi.c
index bc622cd..6e1e8bd 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -456,23 +456,6 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
     return s;
 }
 
-static BlockStats *bdrv_query_stats(BlockBackend *blk,
-                                    const BlockDriverState *bs,
-                                    bool query_backing)
-{
-    BlockStats *s;
-
-    s = bdrv_query_bds_stats(bs, query_backing);
-
-    if (blk) {
-        s->has_device = true;
-        s->device = g_strdup(blk_name(blk));
-        bdrv_query_blk_stats(s->stats, blk);
-    }
-
-    return s;
-}
-
 BlockInfoList *qmp_query_block(Error **errp)
 {
     BlockInfoList *head = NULL, **p_next = &head;
@@ -496,42 +479,43 @@ BlockInfoList *qmp_query_block(Error **errp)
     return head;
 }
 
-static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs,
-                           bool query_nodes)
-{
-    if (query_nodes) {
-        *bs = bdrv_next_node(*bs);
-        return !!*bs;
-    }
-
-    *blk = blk_next(*blk);
-    *bs = *blk ? blk_bs(*blk) : NULL;
-
-    return !!*blk;
-}
-
 BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
                                      bool query_nodes,
                                      Error **errp)
 {
     BlockStatsList *head = NULL, **p_next = &head;
-    BlockBackend *blk = NULL;
-    BlockDriverState *bs = NULL;
+    BlockBackend *blk;
+    BlockDriverState *bs;
 
     /* Just to be safe if query_nodes is not always initialized */
-    query_nodes = has_query_nodes && query_nodes;
-
-    while (next_query_bds(&blk, &bs, query_nodes)) {
-        BlockStatsList *info = g_malloc0(sizeof(*info));
-        AioContext *ctx = blk ? blk_get_aio_context(blk)
-                              : bdrv_get_aio_context(bs);
+    if (has_query_nodes && query_nodes) {
+        for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) {
+            BlockStatsList *info = g_malloc0(sizeof(*info));
+            AioContext *ctx = bdrv_get_aio_context(bs);
 
-        aio_context_acquire(ctx);
-        info->value = bdrv_query_stats(blk, bs, !query_nodes);
-        aio_context_release(ctx);
+            aio_context_acquire(ctx);
+            info->value = bdrv_query_bds_stats(bs, false);
+            aio_context_release(ctx);
 
-        *p_next = info;
-        p_next = &info->next;
+            *p_next = info;
+            p_next = &info->next;
+        }
+    } else {
+        for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+            BlockStatsList *info = g_malloc0(sizeof(*info));
+            AioContext *ctx = blk_get_aio_context(blk);
+
+            aio_context_acquire(ctx);
+            BlockStats *s = bdrv_query_bds_stats(blk_bs(blk), true);
+            s->has_device = true;
+            s->device = g_strdup(blk_name(blk));
+            bdrv_query_blk_stats(s->stats, blk);
+            aio_context_release(ctx);
+
+            info->value = s;
+            *p_next = info;
+            p_next = &info->next;
+        }
     }
 
     return head;
-- 
2.5.5

^ permalink raw reply related

* [Qemu-devel] [PATCH RFC v3 1/2] block/qapi: reduce the coupling between the bdrv_query_stats and bdrv_query_bds_stats
From: Dou Liyang @ 2017-01-04  6:58 UTC (permalink / raw)
  To: stefanha, kwolf, armbru, mreitz, eblake, famz, danpb
  Cc: qemu-devel, izumi.taku, caoj.fnst, fanc.fnst, Dou Liyang
In-Reply-To: <1483513091-30661-1-git-send-email-douly.fnst@cn.fujitsu.com>

the bdrv_query_stats and bdrv_query_bds_stats functions need to call
each other, that increases the coupling. it also makes the program
complicated and makes some unnecessary judgements.

remove the call from bdrv_query_bds_stats to bdrv_query_stats, just
take some recursion to make it clearly.

avoid judging whether the blk is NULL during querying the bds stats.
it is unnecessary.

Signed-off-by: Dou Liyang <douly.fnst@cn.fujitsu.com>
---
 block/qapi.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/block/qapi.c b/block/qapi.c
index a62e862..bc622cd 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -357,10 +357,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
     qapi_free_BlockInfo(info);
 }
 
-static BlockStats *bdrv_query_stats(BlockBackend *blk,
-                                    const BlockDriverState *bs,
-                                    bool query_backing);
-
 static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
 {
     BlockAcctStats *stats = blk_get_stats(blk);
@@ -428,9 +424,18 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
     }
 }
 
-static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
+static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
                                  bool query_backing)
 {
+    BlockStats *s = NULL;
+
+    s = g_malloc0(sizeof(*s));
+    s->stats = g_malloc0(sizeof(*s->stats));
+
+    if (!bs) {
+        return s;
+    }
+
     if (bdrv_get_node_name(bs)[0]) {
         s->has_node_name = true;
         s->node_name = g_strdup(bdrv_get_node_name(bs));
@@ -440,14 +445,15 @@ static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
 
     if (bs->file) {
         s->has_parent = true;
-        s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing);
+        s->parent = bdrv_query_bds_stats(bs->file->bs, query_backing);
     }
 
     if (query_backing && bs->backing) {
         s->has_backing = true;
-        s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing);
+        s->backing = bdrv_query_bds_stats(bs->backing->bs, query_backing);
     }
 
+    return s;
 }
 
 static BlockStats *bdrv_query_stats(BlockBackend *blk,
@@ -456,17 +462,13 @@ static BlockStats *bdrv_query_stats(BlockBackend *blk,
 {
     BlockStats *s;
 
-    s = g_malloc0(sizeof(*s));
-    s->stats = g_malloc0(sizeof(*s->stats));
+    s = bdrv_query_bds_stats(bs, query_backing);
 
     if (blk) {
         s->has_device = true;
         s->device = g_strdup(blk_name(blk));
         bdrv_query_blk_stats(s->stats, blk);
     }
-    if (bs) {
-        bdrv_query_bds_stats(s, bs, query_backing);
-    }
 
     return s;
 }
-- 
2.5.5

^ permalink raw reply related

* [Qemu-devel] [PATCH RFC v3 0/2] block/qapi: refactor and optimize the qmp_query_blockstats()
From: Dou Liyang @ 2017-01-04  6:58 UTC (permalink / raw)
  To: stefanha, kwolf, armbru, mreitz, eblake, famz, danpb
  Cc: qemu-devel, izumi.taku, caoj.fnst, fanc.fnst, Dou Liyang

Change log v2 -> v3:
 1. Remove the unnecessary code for the bdrv_next_node().
 2. Remove the change of the locking rules.
    Even if this change can improve the performance, but it may
    effect the consistency.

For the multi-disks guest, we can use the dataplane feature to
hold performance does not drop, if we execute some slow monitor
commands, such as "info blockstats". But, without this feature, 
How to reduce the decline in performance?

These patches aim to refactor the qmp_query_blockstats() and
improve the performance by reducing the running time of it.

There are the two jobs:

1 For the performance:

1.1 the time it takes(ns) in each time:
the disk numbers     | 10    | 500
-------------------------------------
before these patches | 19429 | 667722 
after these patches  | 18536 | 627945

1.2 the I/O performance is degraded(%) during the monitor:

the disk numbers     | 10    | 500
-------------------------------------
before these patches | 1.3   | 14.2
after these patches  | 1.0   | 11.3

used the dd command likes this to test: 
dd if=date_1.dat of=date_2.dat conv=fsync oflag=direct bs=1k count=100k.

2 refactor qmp_query_blockstats():

From:

+--------------+      +---------------------+
 | 1            |      | 4.                  |
 |next_query_bds|      |bdrv_query_bds_stats +---+
 |              |      |                     |   |
 +--------^-----+      +-------------^-------+   |
          |                          |           |
+---------+----------+      +--------+-------+   |
| 0.                 |      | 2.             |   |
|qmp_query_blockstats+------>bdrv_query_stats<----
|                    |      |                |
+--------------------+      +--------+-------+
                                     |
                       +-------------v-------+
                       | 3.                  |
                       |bdrv_query_blk_stats |
                       |                     |
                       +---------------------+

To:

                                    +--------------+
                                    |              |
                           +--------v-----------+  |
                       +--->  3.                |  |
+-------------------+  |   |bdrv_query_bds_stats+--+
| 1.                +--+   |                    |
|                   +      +--------------------+
|qmp_query_blockstats--+
|                   |  |
+-------------------+  |   +--------------------+
                       |   | 2.                 |
                       +--->                    |
                           |bdrv_query_blk_stats|
                           |                    |
                           +--------------------+

Dou Liyang (2):
  block/qapi: reduce the coupling between the bdrv_query_stats and
    bdrv_query_bds_stats
  block/qapi: reduce the execution time of qmp_query_blockstats

 block/qapi.c | 94 ++++++++++++++++++++++++++----------------------------------
 1 file changed, 40 insertions(+), 54 deletions(-)

-- 
2.5.5

^ permalink raw reply

* [PATCH v4 3/3] arm64: dts: exynos: Add support for S6E3HA2 panel device on TM2 board
From: Hoegeun Kwon @ 2017-01-04  6:58 UTC (permalink / raw)
  To: robh, thierry.reding, airlied, kgene, krzk, inki.dae
  Cc: devicetree, linux-samsung-soc, linux-kernel, dri-devel,
	jh80.chung, cw00.choi, Hyungwon Hwang, Hoegeun Kwon
In-Reply-To: <1483513115-3068-1-git-send-email-hoegeun.kwon@samsung.com>

From: Hyungwon Hwang <human.hwang@samsung.com>

This patch add the panel device tree node for S6E3HA2 display
controller to TM2 dts.

Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
---
 arch/arm64/boot/dts/exynos/exynos5433-tm2.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm64/boot/dts/exynos/exynos5433-tm2.dts b/arch/arm64/boot/dts/exynos/exynos5433-tm2.dts
index 5b9451d..b3ba1ac 100644
--- a/arch/arm64/boot/dts/exynos/exynos5433-tm2.dts
+++ b/arch/arm64/boot/dts/exynos/exynos5433-tm2.dts
@@ -304,11 +304,28 @@
 			reg = <1>;
 
 			dsi_out: endpoint {
+				remote-endpoint = <&panel_in>;
 				samsung,burst-clock-frequency = <512000000>;
 				samsung,esc-clock-frequency = <16000000>;
 			};
 		};
 	};
+
+	panel@0 {
+		compatible = "samsung,s6e3ha2";
+		reg = <0>;
+		vdd3-supply = <&ldo27_reg>;
+		vci-supply = <&ldo28_reg>;
+		reset-gpios = <&gpg0 0 GPIO_ACTIVE_LOW>;
+		enable-gpios = <&gpf1 5 GPIO_ACTIVE_HIGH>;
+		te-gpios = <&gpf1 3 GPIO_ACTIVE_HIGH>;
+
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&dsi_out>;
+			};
+		};
+	};
 };
 
 &hsi2c_0 {
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v4 2/3] drm/panel: Add support for S6E3HA2 panel driver on TM2 board
From: Hoegeun Kwon @ 2017-01-04  6:58 UTC (permalink / raw)
  To: robh, thierry.reding, airlied, kgene, krzk, inki.dae
  Cc: devicetree, linux-samsung-soc, Donghwa Lee, linux-kernel,
	dri-devel, jh80.chung, cw00.choi, Hyungwon Hwang, Hoegeun Kwon
In-Reply-To: <1483513115-3068-1-git-send-email-hoegeun.kwon@samsung.com>

This patch add support for MIPI-DSI based S6E3HA2 AMOLED panel
driver. This panel has 1440x2560 resolution in 5.7-inch physical
panel in the TM2 device.

Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
Signed-off-by: Hyungwon Hwang <human.hwang@samsung.com>
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
---
 .../bindings/display/panel/samsung,s6e3ha2.txt     |  40 ++
 drivers/gpu/drm/panel/Kconfig                      |   6 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c      | 741 +++++++++++++++++++++
 4 files changed, 788 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
 create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c

diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt b/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
new file mode 100644
index 0000000..6879f51
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
@@ -0,0 +1,40 @@
+Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel
+
+Required properties:
+  - compatible: "samsung,s6e3ha2"
+  - reg: the virtual channel number of a DSI peripheral
+  - vdd3-supply: I/O voltage supply
+  - vci-supply: voltage supply for analog circuits
+  - reset-gpios: a GPIO spec for the reset pin (active low)
+  - enable-gpios: a GPIO spec for the panel enable pin (active high)
+  - te-gpios: a GPIO spec for the tearing effect synchronization signal
+    gpio pin (active high)
+
+The device node can contain one 'port' child node with one child
+'endpoint' node, according to the bindings defined in [1]. This
+node should describe panel's video bus.
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+
+&dsi {
+	...
+
+	panel@0 {
+		compatible = "samsung,s6e3ha2";
+		reg = <0>;
+		vdd3-supply = <&ldo27_reg>;
+		vci-supply = <&ldo28_reg>;
+		reset-gpios = <&gpg0 0 GPIO_ACTIVE_LOW>;
+		enable-gpios = <&gpf1 5 GPIO_ACTIVE_HIGH>;
+		te-gpios = <&gpf1 3 GPIO_ACTIVE_HIGH>;
+
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&dsi_out>;
+			};
+		};
+	};
+};
+
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 62aba97..eea2902 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -52,6 +52,12 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
 	  WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
 	  Xperia Z2 tablets
 
+config DRM_PANEL_SAMSUNG_S6E3HA2
+	tristate "Samsung S6E3HA2 DSI video mode panel"
+	depends on OF
+	depends on  DRM_MIPI_DSI
+	select VIDEOMODE_HELPERS
+
 config DRM_PANEL_SAMSUNG_S6E8AA0
 	tristate "Samsung S6E8AA0 DSI video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a5c7ec0..1d483b0 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
new file mode 100644
index 0000000..8c5a1c2
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
@@ -0,0 +1,741 @@
+/*
+ * MIPI-DSI based s6e3ha2 AMOLED 5.7 inch panel driver.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Donghwa Lee <dh09.lee@samsung.com>
+ * Hyungwon Hwang <human.hwang@samsung.com>
+ * Hoegeun Kwon <hoegeun.kwon@samsung.com>
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define S6E3HA2_MIN_BRIGHTNESS		0
+#define S6E3HA2_MAX_BRIGHTNESS		100
+#define S6E3HA2_DEFAULT_BRIGHTNESS	80
+
+#define S6E3HA2_NUM_GAMMA_STEPS		46
+#define S6E3HA2_GAMMA_CMD_CNT		35
+#define S6E3HA2_VINT_STATUS_MAX		10
+
+static const u8 gamma_tbl[S6E3HA2_NUM_GAMMA_STEPS][S6E3HA2_GAMMA_CMD_CNT] = {
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x82, 0x83,
+	  0x85, 0x88, 0x8b, 0x8b, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8c,
+	  0x94, 0x84, 0xb1, 0xaf, 0x8e, 0xcf, 0xad, 0xc9, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x84, 0x84,
+	  0x85, 0x87, 0x8b, 0x8a, 0x84, 0x88, 0x82, 0x82, 0x89, 0x86, 0x8a,
+	  0x93, 0x84, 0xb0, 0xae, 0x8e, 0xc9, 0xa8, 0xc5, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x88, 0x81, 0x84, 0x8a, 0x88, 0x8a,
+	  0x91, 0x84, 0xb1, 0xae, 0x8b, 0xd5, 0xb2, 0xcc, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x8a, 0x8a, 0x84, 0x87, 0x81, 0x84, 0x8a, 0x87, 0x8a,
+	  0x91, 0x85, 0xae, 0xac, 0x8a, 0xc3, 0xa3, 0xc0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x85, 0x85,
+	  0x86, 0x85, 0x88, 0x89, 0x84, 0x89, 0x82, 0x84, 0x87, 0x85, 0x8b,
+	  0x91, 0x88, 0xad, 0xab, 0x8a, 0xb7, 0x9b, 0xb6, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x89, 0x8a, 0x84, 0x89, 0x83, 0x83, 0x86, 0x84, 0x8b,
+	  0x90, 0x84, 0xb0, 0xae, 0x8b, 0xce, 0xad, 0xc8, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x89,
+	  0x8f, 0x84, 0xac, 0xaa, 0x89, 0xb1, 0x98, 0xaf, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x88, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8c,
+	  0x91, 0x86, 0xac, 0xaa, 0x89, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x87, 0x89, 0x8a, 0x83, 0x87, 0x82, 0x85, 0x88, 0x87, 0x88,
+	  0x8b, 0x82, 0xad, 0xaa, 0x8a, 0xc2, 0xa5, 0xbd, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x89, 0x87, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x82, 0x85, 0x84, 0x8a,
+	  0x8e, 0x84, 0xae, 0xac, 0x89, 0xda, 0xb7, 0xd0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x88, 0x83, 0x80, 0x83, 0x82, 0x8b,
+	  0x8e, 0x85, 0xac, 0xaa, 0x89, 0xc8, 0xaa, 0xc1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x81, 0x85, 0x81, 0x84, 0x86, 0x84, 0x8c,
+	  0x8c, 0x84, 0xa9, 0xa8, 0x87, 0xa3, 0x92, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x84, 0x86, 0x83, 0x80, 0x83, 0x81, 0x8c,
+	  0x8d, 0x84, 0xaa, 0xaa, 0x89, 0xce, 0xaf, 0xc5, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x85, 0x86, 0x87, 0x89, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
+	  0x8c, 0x84, 0xa8, 0xa8, 0x88, 0xb5, 0x9f, 0xb0, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x83, 0x85, 0x85, 0x8c,
+	  0x8b, 0x84, 0xab, 0xa8, 0x86, 0xd4, 0xb4, 0xc9, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x86, 0x87, 0x88, 0x81, 0x83, 0x80, 0x84, 0x84, 0x85, 0x8b,
+	  0x8a, 0x83, 0xa6, 0xa5, 0x84, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x84, 0x84,
+	  0x86, 0x85, 0x86, 0x86, 0x82, 0x85, 0x81, 0x82, 0x83, 0x84, 0x8e,
+	  0x8b, 0x83, 0xa4, 0xa3, 0x8a, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8e,
+	  0x8b, 0x83, 0xa4, 0xa2, 0x86, 0xc1, 0xa9, 0xb7, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x82, 0x82, 0x84, 0x8d,
+	  0x89, 0x82, 0xa2, 0xa1, 0x84, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xb1, 0x88, 0x86, 0x87, 0x83, 0x83,
+	  0x85, 0x86, 0x87, 0x87, 0x82, 0x85, 0x81, 0x83, 0x83, 0x85, 0x8c,
+	  0x87, 0x7f, 0xa2, 0x9d, 0x88, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xb4, 0x87, 0x86, 0x86, 0x84, 0x83,
+	  0x86, 0x87, 0x87, 0x87, 0x80, 0x82, 0x7f, 0x86, 0x86, 0x88, 0x8a,
+	  0x84, 0x7e, 0x9d, 0x9c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xbd, 0x00, 0xc7, 0x00, 0xb7, 0x87, 0x85, 0x85, 0x84, 0x83,
+	  0x86, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x83, 0x84, 0x85, 0x8a,
+	  0x85, 0x7e, 0x9c, 0x9b, 0x85, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc0, 0x00, 0xca, 0x00, 0xbb, 0x87, 0x86, 0x85, 0x83, 0x83,
+	  0x85, 0x86, 0x86, 0x88, 0x81, 0x83, 0x80, 0x84, 0x85, 0x86, 0x89,
+	  0x83, 0x7d, 0x9c, 0x99, 0x87, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc4, 0x00, 0xcd, 0x00, 0xbe, 0x87, 0x86, 0x85, 0x83, 0x83,
+	  0x86, 0x85, 0x85, 0x87, 0x81, 0x82, 0x80, 0x82, 0x82, 0x83, 0x8a,
+	  0x85, 0x7f, 0x9f, 0x9b, 0x86, 0xb4, 0xa1, 0xac, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xc7, 0x00, 0xd0, 0x00, 0xc2, 0x87, 0x85, 0x85, 0x83, 0x82,
+	  0x85, 0x85, 0x85, 0x86, 0x82, 0x83, 0x80, 0x82, 0x82, 0x84, 0x87,
+	  0x86, 0x80, 0x9e, 0x9a, 0x87, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xca, 0x00, 0xd2, 0x00, 0xc5, 0x87, 0x85, 0x84, 0x82, 0x82,
+	  0x84, 0x85, 0x85, 0x86, 0x81, 0x82, 0x7f, 0x82, 0x82, 0x84, 0x88,
+	  0x86, 0x81, 0x9d, 0x98, 0x86, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xce, 0x00, 0xd6, 0x00, 0xca, 0x86, 0x85, 0x84, 0x83, 0x83,
+	  0x85, 0x84, 0x84, 0x85, 0x81, 0x82, 0x80, 0x81, 0x81, 0x82, 0x89,
+	  0x86, 0x81, 0x9c, 0x97, 0x86, 0xa7, 0x98, 0xa1, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd1, 0x00, 0xd9, 0x00, 0xce, 0x86, 0x84, 0x83, 0x83, 0x82,
+	  0x85, 0x85, 0x85, 0x86, 0x81, 0x83, 0x81, 0x82, 0x82, 0x83, 0x86,
+	  0x83, 0x7f, 0x99, 0x95, 0x86, 0xbb, 0xa4, 0xb3, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd4, 0x00, 0xdb, 0x00, 0xd1, 0x86, 0x85, 0x83, 0x83, 0x82,
+	  0x85, 0x84, 0x84, 0x85, 0x80, 0x83, 0x82, 0x80, 0x80, 0x81, 0x87,
+	  0x84, 0x81, 0x98, 0x93, 0x85, 0xae, 0x9c, 0xa8, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xd8, 0x00, 0xde, 0x00, 0xd6, 0x86, 0x84, 0x83, 0x81, 0x81,
+	  0x83, 0x85, 0x85, 0x85, 0x82, 0x83, 0x81, 0x81, 0x81, 0x83, 0x86,
+	  0x84, 0x80, 0x98, 0x91, 0x85, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xdc, 0x00, 0xe2, 0x00, 0xda, 0x85, 0x84, 0x83, 0x82, 0x82,
+	  0x84, 0x84, 0x84, 0x85, 0x81, 0x82, 0x82, 0x80, 0x80, 0x81, 0x83,
+	  0x82, 0x7f, 0x99, 0x93, 0x86, 0x94, 0x8b, 0x92, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xdf, 0x00, 0xe5, 0x00, 0xde, 0x85, 0x84, 0x82, 0x82, 0x82,
+	  0x84, 0x83, 0x83, 0x84, 0x81, 0x81, 0x80, 0x83, 0x82, 0x84, 0x82,
+	  0x81, 0x7f, 0x99, 0x92, 0x86, 0x7b, 0x7b, 0x7c, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x83, 0x83, 0x84, 0x80,
+	  0x81, 0x7c, 0x99, 0x92, 0x87, 0xa1, 0x93, 0x9d, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x85, 0x84, 0x83, 0x81, 0x81,
+	  0x82, 0x82, 0x82, 0x83, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
+	  0x82, 0x80, 0x91, 0x8d, 0x83, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x84, 0x80, 0x81, 0x80, 0x81, 0x80, 0x82, 0x83,
+	  0x81, 0x7f, 0x91, 0x8c, 0x82, 0x8d, 0x88, 0x8b, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x82,
+	  0x82, 0x7f, 0x94, 0x89, 0x84, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x80, 0x82, 0x83,
+	  0x82, 0x7f, 0x91, 0x85, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x83, 0x82, 0x84, 0x83,
+	  0x82, 0x7f, 0x90, 0x84, 0x81, 0x9a, 0x90, 0x96, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe4, 0x00, 0xe9, 0x00, 0xe3, 0x84, 0x83, 0x82, 0x80, 0x80,
+	  0x82, 0x83, 0x83, 0x83, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x81, 0x81,
+	  0x82, 0x83, 0x7e, 0x80, 0x7c, 0xa4, 0x97, 0x9f, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xe9, 0x00, 0xec, 0x00, 0xe8, 0x84, 0x83, 0x82, 0x81, 0x81,
+	  0x82, 0x82, 0x82, 0x83, 0x7f, 0x7f, 0x7f, 0x81, 0x80, 0x82, 0x83,
+	  0x83, 0x84, 0x79, 0x7c, 0x79, 0xb1, 0xa0, 0xaa, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xed, 0x00, 0xf0, 0x00, 0xec, 0x83, 0x83, 0x82, 0x80, 0x80,
+	  0x81, 0x82, 0x82, 0x82, 0x7f, 0x7f, 0x7e, 0x81, 0x81, 0x82, 0x80,
+	  0x81, 0x81, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xf1, 0x00, 0xf4, 0x00, 0xf1, 0x83, 0x82, 0x82, 0x80, 0x80,
+	  0x81, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x7d,
+	  0x7e, 0x7f, 0x84, 0x84, 0x83, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xf6, 0x00, 0xf7, 0x00, 0xf5, 0x82, 0x82, 0x81, 0x80, 0x80,
+	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x82,
+	  0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfa, 0x81, 0x81, 0x81, 0x80, 0x80,
+	  0x80, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 },
+	{ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+	  0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00,
+	  0x00, 0x00 }
+};
+
+unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
+	0x18, 0x19, 0x1a, 0x1b, 0x1c,
+	0x1d, 0x1e, 0x1f, 0x20, 0x21
+};
+
+struct s6e3ha2 {
+	struct device *dev;
+	struct drm_panel panel;
+	struct backlight_device *bl_dev;
+
+	struct regulator_bulk_data supplies[2];
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+
+	/* This field is tested by functions directly accessing DSI bus before
+	 * transfer, transfer is skipped if it is set. In case of transfer
+	 * failure or unexpected response the field is set to error value.
+	 * Such construct allows to eliminate many checks in higher level
+	 * functions.
+	 */
+	int error;
+};
+
+static int  s6e3ha2_clear_error(struct s6e3ha2 *ctx)
+{
+	int ret = ctx->error;
+
+	ctx->error = 0;
+	return ret;
+}
+
+static void s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+	ssize_t ret;
+
+	if (ctx->error < 0)
+		return;
+
+	ret = mipi_dsi_dcs_write_buffer(dsi, data, len);
+	if (ret < 0) {
+		dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n",
+						ret, (int)len, data);
+		ctx->error = ret;
+	}
+}
+
+#define s6e3ha2_dcs_write_seq_static(ctx, seq...) do {	\
+	static const u8 d[] = { seq };			\
+	s6e3ha2_dcs_write(ctx, d, ARRAY_SIZE(d));	\
+} while (0)
+
+static void s6e3ha2_test_key_on_f0(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a);
+}
+
+static void s6e3ha2_test_key_off_f0(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf0, 0xa5, 0xa5);
+}
+
+static void s6e3ha2_test_key_on_fc(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a);
+}
+
+static void s6e3ha2_test_key_off_fc(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5);
+}
+
+static void s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf9, 0x09);
+}
+
+static void s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
+	s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
+		0x40, 0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
+}
+
+static void s6e3ha2_aor_control(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb2, 0x03, 0x10);
+}
+
+static void s6e3ha2_caps_elvss_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb6, 0x9c, 0x0a);
+}
+
+static void s6e3ha2_acl_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0x55, 0x00);
+}
+
+static void s6e3ha2_acl_off_opr(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb5, 0x40);
+}
+
+static void s6e3ha2_test_global(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x07);
+}
+
+static void s6e3ha2_test(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb8, 0x19);
+}
+
+static void s6e3ha2_touch_hsync_on1(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx,
+			0xbd, 0x33, 0x11, 0x02, 0x16, 0x02, 0x16);
+}
+
+static void s6e3ha2_pentile_control(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x00, 0xd8, 0xd8);
+}
+
+static void s6e3ha2_poc_global(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb0, 0x20);
+}
+
+static void s6e3ha2_poc_setting(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x08);
+}
+
+static void s6e3ha2_pcd_set_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xcc, 0x40, 0x51);
+}
+
+static void s6e3ha2_err_fg_set(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xed, 0x44);
+}
+
+static void s6e3ha2_hbm_off(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0x53, 0x00);
+}
+
+static void s6e3ha2_te_start_setting(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xb9, 0x10, 0x09, 0xff, 0x00, 0x09);
+}
+
+static void s6e3ha2_gamma_update(struct s6e3ha2 *ctx)
+{
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x03);
+	ndelay(100); /* need for 100ns delay */
+	s6e3ha2_dcs_write_seq_static(ctx, 0xf7, 0x00);
+}
+
+static int s6e3ha2_get_brightness(struct backlight_device *bl_dev)
+{
+	return bl_dev->props.brightness;
+}
+
+static void s6e3ha2_set_vint(struct s6e3ha2 *ctx)
+{
+	struct backlight_device *bl_dev = ctx->bl_dev;
+	unsigned int brightness = bl_dev->props.brightness;
+	unsigned char data[] = { 0xf4, 0x8b,
+			vint_table[brightness * (S6E3HA2_VINT_STATUS_MAX - 1) /
+			S6E3HA2_MAX_BRIGHTNESS] };
+
+	s6e3ha2_dcs_write(ctx, data, 3);
+}
+
+static unsigned int s6e3ha2_get_brightness_index(unsigned int brightness)
+{
+	return (brightness * (S6E3HA2_NUM_GAMMA_STEPS - 1)) /
+		S6E3HA2_MAX_BRIGHTNESS;
+}
+
+static int s6e3ha2_update_gamma(struct s6e3ha2 *ctx, unsigned int brightness)
+{
+	struct backlight_device *bl_dev = ctx->bl_dev;
+	unsigned int index = s6e3ha2_get_brightness_index(brightness);
+	u8 data[S6E3HA2_GAMMA_CMD_CNT + 1] = { 0xca, };
+
+	memcpy(data + 1, gamma_tbl + index, S6E3HA2_GAMMA_CMD_CNT);
+	s6e3ha2_dcs_write(ctx, data, ARRAY_SIZE(data));
+
+	s6e3ha2_gamma_update(ctx);
+	bl_dev->props.brightness = brightness;
+
+	return 0;
+}
+
+static int s6e3ha2_set_brightness(struct backlight_device *bl_dev)
+{
+	struct s6e3ha2 *ctx = (struct s6e3ha2 *)bl_get_data(bl_dev);
+	unsigned int brightness = bl_dev->props.brightness;
+
+	if (brightness < S6E3HA2_MIN_BRIGHTNESS ||
+		brightness > bl_dev->props.max_brightness) {
+		dev_err(ctx->dev, "Invalid brightness: %u\n", brightness);
+		return -EINVAL;
+	}
+
+	if (bl_dev->props.power > FB_BLANK_NORMAL)
+		return -EPERM;
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_update_gamma(ctx, brightness);
+	s6e3ha2_aor_control(ctx);
+	s6e3ha2_set_vint(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+
+	return ctx->error;
+}
+
+static const struct backlight_ops s6e3ha2_bl_ops = {
+	.get_brightness = s6e3ha2_get_brightness,
+	.update_status = s6e3ha2_set_brightness,
+};
+
+static void s6e3ha2_panel_init(struct s6e3ha2 *ctx)
+{
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+	mipi_dsi_dcs_exit_sleep_mode(dsi);
+	usleep_range(5000, 6000);
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_single_dsi_set(ctx);
+	s6e3ha2_test_key_on_fc(ctx);
+	s6e3ha2_freq_calibration(ctx);
+	s6e3ha2_test_key_off_fc(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+}
+
+static int s6e3ha2_power_off(struct s6e3ha2 *ctx)
+{
+	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int s6e3ha2_disable(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+	mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ctx->error != 0)
+		goto err;
+
+	mipi_dsi_dcs_set_display_off(dsi);
+	if (ctx->error != 0)
+		goto err;
+
+	msleep(40);
+	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+	return 0;
+err:
+	return ctx->error;
+}
+
+static int s6e3ha2_unprepare(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	int ret;
+
+	ret = s6e3ha2_clear_error(ctx);
+	if (!ret)
+		ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+	return s6e3ha2_power_off(ctx);
+}
+
+static int s6e3ha2_power_on(struct s6e3ha2 *ctx)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+	if (ret < 0)
+		return ret;
+
+	msleep(120);
+
+	gpiod_set_value(ctx->enable_gpio, 0);
+	usleep_range(5000, 6000);
+	gpiod_set_value(ctx->enable_gpio, 1);
+
+	gpiod_set_value(ctx->reset_gpio, 1);
+	usleep_range(5000, 6000);
+	gpiod_set_value(ctx->reset_gpio, 0);
+	usleep_range(5000, 6000);
+
+	return 0;
+}
+static int s6e3ha2_prepare(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	int ret;
+
+	ret = s6e3ha2_power_on(ctx);
+	if (ret < 0)
+		return ret;
+
+	s6e3ha2_panel_init(ctx);
+
+	ret = s6e3ha2_clear_error(ctx);
+	if (ret < 0) {
+		s6e3ha2_power_off(ctx);
+		return ret;
+	}
+
+	ctx->bl_dev->props.power = FB_BLANK_NORMAL;
+
+	return 0;
+}
+
+static int s6e3ha2_enable(struct drm_panel *panel)
+{
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
+	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+
+	/* common setting */
+	mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+	s6e3ha2_test_key_on_f0(ctx);
+	s6e3ha2_test_key_on_fc(ctx);
+	s6e3ha2_touch_hsync_on1(ctx);
+	s6e3ha2_pentile_control(ctx);
+	s6e3ha2_poc_global(ctx);
+	s6e3ha2_poc_setting(ctx);
+	s6e3ha2_test_key_off_fc(ctx);
+
+	/* pcd setting off for TB */
+	s6e3ha2_pcd_set_off(ctx);
+	s6e3ha2_err_fg_set(ctx);
+	s6e3ha2_te_start_setting(ctx);
+
+	/* brightness setting */
+	s6e3ha2_set_brightness(ctx->bl_dev);
+	s6e3ha2_aor_control(ctx);
+	s6e3ha2_caps_elvss_set(ctx);
+	s6e3ha2_gamma_update(ctx);
+	s6e3ha2_acl_off(ctx);
+	s6e3ha2_acl_off_opr(ctx);
+	s6e3ha2_hbm_off(ctx);
+
+	/* elvss temp compensation */
+	s6e3ha2_test_global(ctx);
+	s6e3ha2_test(ctx);
+	s6e3ha2_test_key_off_f0(ctx);
+
+	mipi_dsi_dcs_set_display_on(dsi);
+	if (ctx->error != 0)
+		return ctx->error;
+
+	ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+	.clock = 14874,
+	.hdisplay = 1440,
+	.hsync_start = 1440 + 1,
+	.hsync_end = 1440 + 1 + 1,
+	.htotal = 1440 + 1 + 1 + 1,
+	.vdisplay = 2560,
+	.vsync_start = 2560 + 1,
+	.vsync_end = 2560 + 1 + 1,
+	.vtotal = 2560 + 1 + 1 + 15,
+	.vrefresh = 60,
+	.flags = 0,
+};
+
+static int s6e3ha2_get_modes(struct drm_panel *panel)
+{
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		DRM_ERROR("failed to create a new display mode\n");
+		DRM_ERROR("failed to add mode %ux%ux@%u\n",
+				default_mode.hdisplay, default_mode.vdisplay,
+				default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = 71;
+	connector->display_info.height_mm = 125;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs s6e3ha2_drm_funcs = {
+	.disable = s6e3ha2_disable,
+	.unprepare = s6e3ha2_unprepare,
+	.prepare = s6e3ha2_prepare,
+	.enable = s6e3ha2_enable,
+	.get_modes = s6e3ha2_get_modes,
+};
+
+static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct s6e3ha2 *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	ctx->dev = dev;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ctx->supplies[0].supply = "vdd3";
+	ctx->supplies[1].supply = "vci";
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+				      ctx->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->reset_gpio)) {
+		dev_err(dev, "cannot get reset-gpios %ld\n",
+			PTR_ERR(ctx->reset_gpio));
+		return PTR_ERR(ctx->reset_gpio);
+	}
+
+	ctx->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+	if (IS_ERR(ctx->enable_gpio)) {
+		dev_err(dev, "cannot get enable-gpios %ld\n",
+			PTR_ERR(ctx->enable_gpio));
+		return PTR_ERR(ctx->enable_gpio);
+	}
+
+	ctx->bl_dev = backlight_device_register("s6e3ha2", dev, ctx,
+						&s6e3ha2_bl_ops, NULL);
+	if (IS_ERR(ctx->bl_dev)) {
+		dev_err(dev, "failed to register backlight device\n");
+		return PTR_ERR(ctx->bl_dev);
+	}
+
+	ctx->bl_dev->props.max_brightness = S6E3HA2_MAX_BRIGHTNESS;
+	ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
+	ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
+
+	drm_panel_init(&ctx->panel);
+	ctx->panel.dev = dev;
+	ctx->panel.funcs = &s6e3ha2_drm_funcs;
+
+	ret = drm_panel_add(&ctx->panel);
+	if (ret < 0)
+		goto unregister_backlight;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0)
+		goto remove_panel;
+
+	return ret;
+
+remove_panel:
+	drm_panel_remove(&ctx->panel);
+
+unregister_backlight:
+	backlight_device_unregister(ctx->bl_dev);
+
+	return ret;
+}
+
+static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
+{
+	struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi);
+
+	mipi_dsi_detach(dsi);
+	drm_panel_remove(&ctx->panel);
+	backlight_device_unregister(ctx->bl_dev);
+
+	return 0;
+}
+
+static const struct of_device_id s6e3ha2_of_match[] = {
+	{ .compatible = "samsung,s6e3ha2" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);
+
+static struct mipi_dsi_driver s6e3ha2_driver = {
+	.probe = s6e3ha2_probe,
+	.remove = s6e3ha2_remove,
+	.driver = {
+		.name = "panel-samsung-s6e3ha2",
+		.of_match_table = s6e3ha2_of_match,
+	},
+};
+module_mipi_dsi_driver(s6e3ha2_driver);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Hyungwon Hwang <human.hwang@samsung.com>");
+MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e3ha2 AMOLED Panel Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v4 1/3] drm/exynos: mic: Add mode_set callback function
From: Hoegeun Kwon @ 2017-01-04  6:58 UTC (permalink / raw)
  To: robh, thierry.reding, airlied, kgene, krzk, inki.dae
  Cc: devicetree, linux-samsung-soc, linux-kernel, dri-devel,
	jh80.chung, cw00.choi, Hoegeun Kwon
In-Reply-To: <1483513115-3068-1-git-send-email-hoegeun.kwon@samsung.com>

Before applying the patch, used the of_get_videomode function to
parse the display-timings in the panel which is the child driver
of dsi in the devicetree. this is wrong. So removed the
of_get_videomode and fixed to get videomode struct through
mode_set callback function.

Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_mic.c | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c
index a0def0b..9a50ceb 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_mic.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c
@@ -286,13 +286,6 @@ static int parse_dt(struct exynos_mic *mic)
 			}
 			nodes[j++] = remote_node;
 
-			ret = of_get_videomode(remote_node,
-							&mic->vm, 0);
-			if (ret) {
-				DRM_ERROR("mic: failed to get videomode");
-				goto exit;
-			}
-
 			break;
 		default:
 			DRM_ERROR("mic: Unknown endpoint from MIC");
@@ -329,6 +322,27 @@ static void mic_post_disable(struct drm_bridge *bridge)
 	mutex_unlock(&mic_mutex);
 }
 
+static void mic_mode_set(struct drm_bridge *bridge,
+			struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode)
+{
+	struct exynos_mic *mic = bridge->driver_private;
+
+	mutex_lock(&mic_mutex);
+	if (mic->enabled)
+		goto already_enabled;
+
+	drm_display_mode_to_videomode(mode, &mic->vm);
+
+	if (!mic->i80_mode)
+		mic_set_porch_timing(mic);
+	mic_set_img_size(mic);
+	mic_set_output_timing(mic);
+
+already_enabled:
+	mutex_unlock(&mic_mutex);
+}
+
 static void mic_pre_enable(struct drm_bridge *bridge)
 {
 	struct exynos_mic *mic = bridge->driver_private;
@@ -355,10 +369,6 @@ static void mic_pre_enable(struct drm_bridge *bridge)
 		goto turn_off_clks;
 	}
 
-	if (!mic->i80_mode)
-		mic_set_porch_timing(mic);
-	mic_set_img_size(mic);
-	mic_set_output_timing(mic);
 	mic_set_reg_on(mic, 1);
 	mic->enabled = 1;
 	mutex_unlock(&mic_mutex);
@@ -377,6 +387,7 @@ static void mic_enable(struct drm_bridge *bridge) { }
 static const struct drm_bridge_funcs mic_bridge_funcs = {
 	.disable = mic_disable,
 	.post_disable = mic_post_disable,
+	.mode_set = mic_mode_set,
 	.pre_enable = mic_pre_enable,
 	.enable = mic_enable,
 };
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v4 0/3] Add support for the S6E3HA2 panel on TM2 board
From: Hoegeun Kwon @ 2017-01-04  6:58 UTC (permalink / raw)
  To: robh-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w, airlied-cv59FeDIM0c,
	kgene-DgEjT+Ai2ygdnm+yROfE0A, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	inki.dae-Sze3O3UU22JBDgjK7y7TUQ
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	a.hajda-Sze3O3UU22JBDgjK7y7TUQ, cw00.choi-Sze3O3UU22JBDgjK7y7TUQ,
	jh80.chung-Sze3O3UU22JBDgjK7y7TUQ, Hoegeun Kwon
In-Reply-To: <CGME20170104065858epcas5p2f6a70e125b1b6d7051d8fe3a28e626f5@epcas5p2.samsung.com>

Purpose of this patch is add support for S6E3HA2 AMOLED panel on
the TM2 board. The first patch adds support for S6E3HA2 panel
device tree document and driver, the second patch add support for
S6E3HA2 panel device tree.

Changes for V4:

- Removed display-timings in devicetree, the display-timings has
  been fixed to be provided by the device driver.
- Added the mode_set callback function into exynos_drm_mic,
  because the exynos_drm_mic driver can not parse a videomode
  struct by removing the display-timings from the devicetree.

Hoegeun Kwon (2):
  drm/exynos: mic: Add mode_set callback function
  drm/panel: Add support for S6E3HA2 panel driver on TM2 board

Hyungwon Hwang (1):
  arm64: dts: exynos: Add support for S6E3HA2 panel device on TM2 board

 .../bindings/display/panel/samsung,s6e3ha2.txt     |  40 ++
 arch/arm64/boot/dts/exynos/exynos5433-tm2.dts      |  17 +
 drivers/gpu/drm/exynos/exynos_drm_mic.c            |  33 +-
 drivers/gpu/drm/panel/Kconfig                      |   6 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c      | 741 +++++++++++++++++++++
 6 files changed, 827 insertions(+), 11 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
 create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c

-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 2/3] dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in vdma
From: Appana Durga Kedareswara Rao @ 2017-01-04  6:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a8f5c216-cac0-daf9-a880-3820ae9b5e13@synopsys.com>

Hi Jose Miguel Abreu,

	Thanks for the review...

> >> If so then there is no race condition, but the HW image that I have
> >> does not have this register enabled so I was getting this result
> >> (memory corruption because not all framebuffers had addresses set).
> > Thanks for the explanation.
> > Agree the issue that you mentioned won't come when
> > XILINX_DMA_REG_FRMSTORE
> > (C_ENABLE_DEBUG_INFO_5 and C_ENABLE_DEBUG_INFO_13) Register is
> enabled in the IP.
> > But this register won't get enabled with the default IP configuration
> (C_ENABLE_DEBUG_INFO_5 and C_ENABLE_DEBUG_INFO_13).
> >
> > When user is not enabled XILINX_DMA_REG_FRMSTORE in the h/w and
> submits frames less than h/w capable.
> > The solution that I am thinking is to throw an error in the driver
> > saying that either enable the num frame store feature in the IP or submit the
> frames up to h/w capable what do you think???
> 
> Sounds fine by me.

Thanks posted the v3 series please review when you have some time...

Regards,
Kedar.

> 
> Best regards,
> Jose Miguel Abreu
> 
> >
> > Regards,
> > Kedar.
> >
> >> Best regards,
> >> Jose Miguel Abreu
> >>
> >>> Regards,
> >>> Kedar.
> >>>
> >>>> Best regards,
> >>>> Jose Miguel Abreu
> >>>>

^ permalink raw reply

* Re: [PATCH v2 5/9] net/virtio: setup rxq interrupts
From: Tan, Jianfeng @ 2017-01-04  6:56 UTC (permalink / raw)
  To: Yuanhan Liu; +Cc: dev, stephen
In-Reply-To: <20161230062747.GI21789@yliu-dev.sh.intel.com>



On 12/30/2016 2:27 PM, Yuanhan Liu wrote:
> On Thu, Dec 29, 2016 at 07:30:39AM +0000, Jianfeng Tan wrote:
>> This patch mainly allocates structure to store queue/irq mapping,
>> and configure queue/irq mapping down through PCI ops. It also creates
>> eventfds for each Rx queue and tell the kernel about the eventfd/intr
>> binding.
>>
>> Mostly importantly, different from previous NICs (usually implements
>> these logic in dev_start()), virtio's interrupt settings should be
>> configured down to QEMU before sending DRIVER_OK notification.
> Isn't it obvious we have to have all driver stuff (including interrupt
> settings) configured properly before setting DRIVER_OK? :) That said,
> it's meanless to state the fact that virtio acts differently than other
> nics here on dev_start/stop.
>
>> Note: We only support 1:1 queue/irq mapping so far, which means, each
>> rx queue has one exclusive interrupt (corresponding to irqfd in the
>> qemu/kvm) to get notified when packets are available on that queue.
> That means you have to setup the "vectors=N" option has to set correctly
> in QEMU, otherwise it won't work?

Yes, actually, the correct value should be "vectors>=N+1", with N 
standing for the number of queue pairs. It's due to the hard coded 
mapping logic:
0 -> config irq
1 -> rxq0
2 -> rxq1
...

>   If so, you also have to doc it somewhere.

Agreed.

[...]
>> +
>> +	if (virtio_queues_bind_intr(dev) < 0) {
>> +		PMD_INIT_LOG(ERR, "Failed to bind queue/interrupt");
>> +		return -1;
> You have to free intr_handle->intr_vec, otherwise, memory leak occurs.

It's freed at dev_close(). Do you mean freeing and reallocating here? As 
nr_rx_queues is not a changeable value, I don't see the necessity here. 
I miss something?

Thanks,
Jianfeng

^ permalink raw reply

* Re: [PATCH 01/12] libmultipath: add wwid for "struct uevent" to record wwid of uevent
From: tang.junhui @ 2017-01-04  6:56 UTC (permalink / raw)
  To: Benjamin Marzinski
  Cc: tang.wenjun3, zhang.kai16, dm-devel, bart.vanassche, mwilck
In-Reply-To: <20170103220254.GD2732@octiron.msp.redhat.com>


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

Hello Ben,

> Right now, multipath users are allowed configure devices to set the wwid
> based on any udev environment variable (or even use a callout, although
> this is deprecated). With this patch, that breaks.
Does WWID obtained by different methods change? If it changes, we would 
better to modify code to keep it no change. 

> If the udev sets
> ID_SERIAL for a device, that is its wwid, right? 
Yes

> Do you know if rbd
> devices have ID_SERIAL set? 
WWID has different label in uevents for different devices, I only test for
SCSI devices. Now we do not support rbd divice for uevents merging, these 
device process as old way, it has no harm in logic. If we need to merge 
rbd uevents for these devices, we can add code to get wwid from uevents
and it can be supported easily. 


Regards
Tang Junhui





发件人:         "Benjamin Marzinski" <bmarzins@redhat.com>
收件人:         tang.junhui@zte.com.cn, 
抄送:   christophe.varoqui@opensvc.com, hare@suse.de, mwilck@suse.com, 
bart.vanassche@sandisk.com, dm-devel@redhat.com, zhang.kai16@zte.com.cn, 
tang.wenjun3@zte.com.cn
日期:   2017/01/04 06:03
主题:   Re: [PATCH 01/12] libmultipath: add wwid for "struct uevent" to 
record wwid of uevent



On Tue, Dec 27, 2016 at 04:03:18PM +0800, tang.junhui@zte.com.cn wrote:
> From: tang.junhui <tang.junhui@zte.com.cn>
> 
> Add "char *wwid" to point WWID of uevent. This member identifies
> the LUN ID which the path belongs to, and it is used for merging
> uevents. WWID possibly did not exist in uevent yet, so ->wwid
> would be NULL, those uevents would not be merged, but be proccessed
> as old way.

Right now, multipath users are allowed configure devices to set the wwid
based on any udev environment variable (or even use a callout, although
this is deprecated). With this patch, that breaks. If the udev sets
ID_SERIAL for a device, that is its wwid, right?  Do you know if rbd
devices have ID_SERIAL set? If so, this change will break them.  Even if
this change doesn't break any devices in their default configurations,
we would need to disallow changing how the wwid is set for this patch
to be safe.

-Ben 

> 
> Change-Id: Ie6b076363b3735dc7de10184b27fa799b499af0e
> Signed-off-by: tang.junhui <tang.junhui@zte.com.cn>
> ---
>  libmultipath/uevent.c | 2 ++
>  libmultipath/uevent.h | 1 +
>  2 files changed, 3 insertions(+)
> 
> diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
> index 7edcce1..ef1bafe 100644
> --- a/libmultipath/uevent.c
> +++ b/libmultipath/uevent.c
> @@ -424,6 +424,8 @@ struct uevent *uevent_from_udev_device(struct 
udev_device *dev)
>                                                uev->devpath = 
uev->envp[i] + 8;
>                                if (strcmp(name, "ACTION") == 0)
>                                                uev->action = 
uev->envp[i] + 7;
> +                              if (strcmp(name, "ID_SERIAL") == 0)
> +                                              uev->wwid = uev->envp[i] 
+ 10;
>                                i++;
>                                if (i == HOTPLUG_NUM_ENVP - 1)
>                                                break;
> diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h
> index 9d22dcd..7bfccef 100644
> --- a/libmultipath/uevent.h
> +++ b/libmultipath/uevent.h
> @@ -22,6 +22,7 @@ struct uevent {
>                char *devpath;
>                char *action;
>                char *kernel;
> +              char *wwid;
>                unsigned long seqnum;
>                char *envp[HOTPLUG_NUM_ENVP];
>  };
> -- 
> 2.8.1.windows.1
> 



[-- Attachment #1.2: Type: text/html, Size: 6642 bytes --]

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



^ permalink raw reply

* Re: [PATCH v4 0/5] road to reentrant real_path
From: Torsten Bögershausen @ 2017-01-04  6:56 UTC (permalink / raw)
  To: Jeff King, Brandon Williams
  Cc: git, sbeller, jacob.keller, gitster, ramsay, j6t, pclouds,
	larsxschneider
In-Reply-To: <20170104004825.3s27dsircdp5lqte@sigill.intra.peff.net>

On 04.01.17 01:48, Jeff King wrote:
> On Tue, Jan 03, 2017 at 11:09:18AM -0800, Brandon Williams wrote:
> 
>> Only change with v4 is in [1/5] renaming the #define MAXSYMLINKS back to
>> MAXDEPTH due to a naming conflict brought up by Lars Schneider.
> 
> Hmm. Isn't MAXSYMLINKS basically what you want here, though? It what's
> what all other similar functions will be using.
> 
> The only problem was that we were redefining the macro. So maybe:
> 
>   #ifndef MAXSYMLINKS
>   #define MAXSYMLINKS 5
>   #endif
> 
> would be a good solution?
Why 5  ? (looking at the  20..30 below)
And why 5 on one system and e.g. on my Mac OS
#define MAXSYMLINKS     32  

Would the same value value for all Git installations on all platforms make sense?
#define GITMAXSYMLINKS 20

> 
> It looks like the "usual" value is more like 20 or 30 on most systems,
> though.  We should probably also set errno to ELOOP when we hit the
> limit, which is what other symlink-resolving functions would do.
> 
> I'm surprised we didn't hit this on Linux, which has MAXSYMLINKS, too.
> We should be picking it up from <sys/param.h>.
> 
> -Peff
> 


^ permalink raw reply

* [Acked] [PATCH] checkpatch: Warn on embedded function names
From: Andy Whitcroft @ 2017-01-04  6:56 UTC (permalink / raw)
  To: Joe Perches; +Cc: Andrew Morton, linux-kernel
In-Reply-To: <ac9631fdbac5af3507c5bfe88ad9064f0ed764ec.1483500080.git.joe@perches.com>

On Tue, Jan 03, 2017 at 07:21:54PM -0800, Joe Perches wrote:
> Embedded function names are less appropriate to use when
> refactoring can cause function renaming.  Prefer the use
> of "%s", __func__ to embedded function names.
> 
> Signed-off-by: Joe Perches <joe@perches.com>
> ---
>  scripts/checkpatch.pl | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 982c52ca6473..4f53093a1b0b 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -2134,7 +2134,7 @@ sub process {
>  	my $in_header_lines = $file ? 0 : 1;
>  	my $in_commit_log = 0;		#Scanning lines before patch
>  	my $has_commit_log = 0;		#Encountered lines before patch
> -       my $commit_log_possible_stack_dump = 0;
> +	my $commit_log_possible_stack_dump = 0;
>  	my $commit_log_long_line = 0;
>  	my $commit_log_has_diff = 0;
>  	my $reported_maintainer_file = 0;
> @@ -2154,6 +2154,7 @@ sub process {
>  	my $realline = 0;
>  	my $realcnt = 0;
>  	my $here = '';
> +	my $context_function;		#undef'd unless there's a known function
>  	my $in_comment = 0;
>  	my $comment_edge = 0;
>  	my $first_line = 0;
> @@ -2192,7 +2193,8 @@ sub process {
>  			}
>  			#next;
>  		}
> -		if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
> +		if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
> +			my $context = $4;
>  			$realline=$1-1;
>  			if (defined $2) {
>  				$realcnt=$3+1;
> @@ -2201,6 +2203,12 @@ sub process {
>  			}
>  			$in_comment = 0;
>  
> +			if ($context =~ /\b(\w+)\s*\(/) {
> +				$context_function = $1;
> +			} else {
> +				undef $context_function;
> +			}
> +
>  			# Guestimate if this is a continuing comment.  Run
>  			# the context looking for a comment "edge".  If this
>  			# edge is a close comment then we must be in a comment
> @@ -5157,6 +5165,16 @@ sub process {
>  			     "break quoted strings at a space character\n" . $hereprev);
>  		}
>  
> +#check for an embedded function name in a string when the function is known
> +# as part of a diff.  This does not work for -f --file checking as it
> +#depends on patch context providing the function name
> +		if ($line =~ /^\+.*$String/ &&
> +		    defined($context_function) &&
> +		    get_quoted_string($line, $rawline) =~ /\b$context_function\b/) {
> +			WARN("EMBEDDED_FUNCTION_NAME",
> +			     "Prefer using \"%s\", __func__ to embedded function names\n" . $herecurr);
> +		}
> +
>  # check for spaces before a quoted newline
>  		if ($rawline =~ /^.*\".*\s\\n/) {
>  			if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
> -- 
> 2.10.0.rc2.1.g053435c
> 

Looks sane enough to me.

Acked-by: Andy Whitcroft <apw@canonical.com>

-apw

^ permalink raw reply

* RE: [PATCH v2] fio: add device dax engine
From: Elliott, Robert (Persistent Memory) @ 2017-01-04  6:55 UTC (permalink / raw)
  To: Dave Jiang, fio@vger.kernel.org
  Cc: ross.zwisler@linux.intel.com, Williams, Dan J
In-Reply-To: <DF4PR84MB016932384C62DB5ED4CB2F7DAB6E0@DF4PR84MB0169.NAMPRD84.PROD.OUTLOOK.COM>

> -----Original Message-----
> From: fio-owner@vger.kernel.org [mailto:fio-owner@vger.kernel.org] On
> Behalf Of Elliott, Robert (Persistent Memory)
> Sent: Tuesday, January 03, 2017 3:32 PM
> Subject: RE: [PATCH v2] fio: add device dax engine
...
> Perhaps pmemblk and dev-dax should be restructured to load the libpmem
> and libpmemblk libraries at startup time like the others.  I think that
> requires adding -lpmem and -lpmemblk to LIBS in the configure script,
> so they end up in config-host.mak like the others:
> 
> LIBS+=-ll -lnuma -lrt -laio -lz

I've posted a patch series that does this.  It works correctly when I
manually remove or restore /lib64/libpmem.so.


^ permalink raw reply

* [Buildroot] dhcpcd build fails
From: Prasant J @ 2017-01-04  6:55 UTC (permalink / raw)
  To: buildroot

Hi,



I'm trying to build my rootfs from buildroot for my Analog Devices board.
"dhcpcd" package fails to build. Below are the details:



>>> dhcpcd 6.6.7 Building
PATH="/WORK/build-repo/sc58x-dev/buildroot/output/host/bin:/WORK/build-repo/sc58x-dev/buildroot/output/host/sbin:/WORK/build-repo/sc58x-dev/buildroot/output/host/usr/bin:/WORK/build-repo/sc58x-dev/buildroot/output/host/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/analog/cces-linux-add-in/1.1.0/ARM/arm-linux-gnueabi/bin:/opt/analog/cces/2.3.0/ARM/arm-none-eabi/bin"
/usr/bin/make -j1 -C
/WORK/build-repo/sc58x-dev/buildroot/output/build/dhcpcd-6.6.7 all
make[1]: Entering directory
`/WORK/build-repo/sc58x-dev/buildroot/output/build/dhcpcd-6.6.7'
/WORK/build-repo/sc58x-dev/buildroot/output/host/usr/bin/arm-linux-gnueabi-gcc
 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os
-std=c99  -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
-D_FILE_OFFSET_BITS=64 -D_BSD_SOURCE -D_XOPEN_SOURCE=700
-D_DEFAULT_SOURCE -DINET -DINET6 -I./crypt -c dhcpcd.c -o dhcpcd.o
In file included from
/WORK/build-repo/sc58x-dev/buildroot/output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/include/sys/socket.h:38:0,
                 from dhcpcd.c:33:
ipv6.h:154:41: error: invalid application of ?sizeof? to incomplete
type ?struct in6_pktinfo?
  unsigned char sndbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
                                         ^
ipv6.h:146:38: error: invalid application of ?sizeof? to incomplete
type ?struct in6_pktinfo?
 #define IP6BUFLEN (CMSG_SPACE(sizeof(struct in6_pktinfo)) + \
                                      ^
ipv6.h:157:23: note: in expansion of macro ?IP6BUFLEN?
  unsigned char rcvbuf[IP6BUFLEN];
                       ^
make[1]: *** [dhcpcd.o] Error 1
make[1]: Leaving directory
`/WORK/build-repo/sc58x-dev/buildroot/output/build/dhcpcd-6.6.7'



struct in6_pktinfo is defined in linux/ipv6.h
(output/host/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/include/linux/ipv6.h)
and that seems to be included in ipv6.h if "-D__linux__ &&
-D__GLIBC__" are used. I tried including both in config.mk but the
error persists.



Any inputs on how to fix this?



Regards, Pj

^ permalink raw reply

* 答复: Re: [PATCH] lib/librte_vhost: fix memory leak
From: wang.yong19 @ 2017-01-04  6:55 UTC (permalink / raw)
  To: Yuanhan Liu; +Cc: dev
In-Reply-To: <20170104040210.GE21228@yliu-dev.sh.intel.com>

> Yuanhan Liu <yuanhan.liu@linux.intel.com> 
> 2017/01/04 12:02
> 
> to
> 
> Yong Wang <wang.yong19@zte.com.cn>, 
> 
> cc
> 
> dev@dpdk.org
> 
> subject
> 
> Re: [PATCH] lib/librte_vhost: fix memory leak
> 
> On Tue, Jan 03, 2017 at 10:57:55PM -0500, Yong Wang wrote:
> > In function vhost_new_device(), current code dose not free 'dev'
> > in "i == MAX_VHOST_DEVICE" condition statements. It will lead to a
> > memory leak.
> 
> Nice catch!
> 
> Here are few minor stuff you might need pay attention to for future
> contribution:
> 
> - a fix patch needs a fixline, like following
> 
>   Fixes: 45ca9c6f7bc6 ("vhost: get rid of linked list for devices")
> 
> - the prefix for vhost lib is "vhost: ". And FYI, for PMD drivers, it's
>   'net/PMD_NAME', say 'net/virtio'.
> 
> 
> For you convenience, I have fixed the two while applying. And thanks
> for the fix.
> 
> Applied to dpdk-next-virtio.
> 
>    --yliu

Thanks for your advice. 

^ permalink raw reply

* Re: [PATCH 7/7] misc: intel-ish-client: add intel ishtp clients driver
From: Xu, Even @ 2017-01-04  6:55 UTC (permalink / raw)
  To: jikos@kernel.org
  Cc: srinivas.pandruvada@linux.intel.com, linux-input@vger.kernel.org,
	benjamin.tissoires@redhat.com, linux-kernel@vger.kernel.org,
	gregkh@linuxfoundation.org, arnd@arndb.de, Shevchenko, Andriy
In-Reply-To: <alpine.LSU.2.20.1701031050510.28985@cbobk.fhfr.pm>

On 二, 2017-01-03 at 10:54 +0100, Jiri Kosina wrote:
> On Fri, 23 Dec 2016, Even Xu wrote:
> 
> [ ... snip ... ]
> > 
> > +static ssize_t ishtp_cl_write(struct file *file, const char __user
> > *ubuf,
> > +	size_t length, loff_t *offset)
> > +{
> > +	struct ishtp_cl_miscdev *ishtp_cl_misc = file-
> > >private_data;
> > +	struct ishtp_cl *cl;
> > +	void *write_buf;
> > +	struct ishtp_device *dev;
> > +	int ret;
> > +
> > +	/* Non-blocking semantics are not supported */
> > +	if (file->f_flags & O_NONBLOCK) {
> > +		ret = -EINVAL;
> > +		goto out_unlock;
> When taking the error path here you'd try to unlock 
> ishtp_cl_misc->cl_mutex before actually acquiring it.
> 

Thanks for your comments, Jiri, I update my patch below:
===============================================================

Intel ISHFW supports many different clients, in
hid/intel-ish-hid/ishtp bus driver, it creates following client devices:
HID client:
	interface of sensor configure and sensor event report.
SMHI client:
	interface of sensor calibration, ISHFW debug, ISHFW performance
	analysis and manufacture support.
Trace client:
	interface of ISHFW debug log output.
Trace configure client:
	interface of ISHFW debug log configuration, such as output port,
	log level, filter.
ISHFW loader client:
	interface of customized ISHFW loader.
HID client has been handle by hid/intel-ish-hid/intel-ishtp-hid client
driver, and rest of the clients export interface using miscellaneous
drivers. This interface is used by user space tools for debugging and
calibration of sensors.

Signed-off-by: Even Xu <even.xu@intel.com>
Reviewed-by: Andriy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
 drivers/misc/Kconfig                               |   1 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/intel-ish-client/Kconfig              |  15 +
 drivers/misc/intel-ish-client/Makefile             |   8 +
 .../misc/intel-ish-client/intel-ishtp-clients.c    | 882 +++++++++++++++++++++
 include/uapi/linux/intel-ishtp-clients.h           |  73 ++
 6 files changed, 980 insertions(+)
 create mode 100644 drivers/misc/intel-ish-client/Kconfig
 create mode 100644 drivers/misc/intel-ish-client/Makefile
 create mode 100644 drivers/misc/intel-ish-client/intel-ishtp-clients.c
 create mode 100644 include/uapi/linux/intel-ishtp-clients.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971ba..a89849f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -778,4 +778,5 @@ source "drivers/misc/mic/Kconfig"
 source "drivers/misc/genwqe/Kconfig"
 source "drivers/misc/echo/Kconfig"
 source "drivers/misc/cxl/Kconfig"
+source "drivers/misc/intel-ish-client/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3198336..c54015d 100644
--- a/drivers/misc/Makefile

+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_INTEL_ISH_CLIENT)	+= intel-ish-client/
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
diff --git a/drivers/misc/intel-ish-client/Kconfig b/drivers/misc/intel-ish-client/Kconfig
new file mode 100644
index 0000000..6fa9cc0
--- /dev/null
+++ b/drivers/misc/intel-ish-client/Kconfig
@@ -0,0 +1,15 @@
+menu "Intel ISH Client support"
+	depends on INTEL_ISH_HID
+
+config INTEL_ISH_CLIENT
+	tristate "Intel Integrated Sensor Hub Client"
+	help
+	  The Integrated Sensor Hub (ISH) supports many clients, Intel ISH client
+	  driver supports following clients:
+	  SMHI client: calibration & perfermance & menufacture tool interface
+	  trace configure client: ISHFW trace parameter configure interface
+	  trace tool client: ISHFW trace log output interface
+	  loader client: loading customized ISHFW interface
+
+	  Say Y here if you want to support Intel ISH clients. If unsure, say N.
+endmenu
diff --git a/drivers/misc/intel-ish-client/Makefile b/drivers/misc/intel-ish-client/Makefile
new file mode 100644
index 0000000..29a5461
--- /dev/null
+++ b/drivers/misc/intel-ish-client/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile - Intel ISH client driver
+# Copyright (c) 2014-2016, Intel Corporation.
+#
+#
+obj-$(CONFIG_INTEL_ISH_CLIENT) += intel-ishtp-clients.o
+
+ccflags-y += -I$(srctree)/drivers/hid/intel-ish-hid/ishtp
diff --git a/drivers/misc/intel-ish-client/intel-ishtp-clients.c b/drivers/misc/intel-ish-client/intel-ishtp-clients.c
new file mode 100644
index 0000000..4ca3ab8
--- /dev/null
+++ b/drivers/misc/intel-ish-client/intel-ishtp-clients.c
@@ -0,0 +1,882 @@
+/*
+ * ISHTP clients driver
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/capability.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/intel-ishtp-clients.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/uaccess.h>
+
+#include "ishtp-dev.h"
+#include "client.h"
+
+/*
+ * ISH client misc driver structure
+ */
+struct ishtp_cl_miscdev {
+	struct miscdevice cl_miscdev;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl;
+
+	/* Wait queue for waiting ISHFW event/message */
+	wait_queue_head_t read_wait;
+	/* Read buffer, will point to available ISHFW message */
+	struct ishtp_cl_rb *read_rb;
+
+	/*
+	 * cl member can be freed/changed by ISHFW reset and release() calling,
+	 * so must pay attention to protect cl while try to access it. This
+	 * mutex is used to protect cl member.
+	 */
+	struct mutex cl_mutex;
+
+	struct work_struct reset_work;
+};
+
+/* ISH client GUIDs */
+/* SMHI client UUID: bb579a2e-cc54-4450-b1d0-5e7520dcad25 */
+static const uuid_le ishtp_smhi_guid =
+			UUID_LE(0xbb579a2e, 0xcc54, 0x4450,
+				0xb1, 0xd0, 0x5e, 0x75, 0x20, 0xdc, 0xad, 0x25);
+
+/* Trace log client UUID: c1cc78b9-b693-4e54-9191-5169cb027c25 */
+static const uuid_le ishtp_trace_guid =
+			UUID_LE(0xc1cc78b9, 0xb693, 0x4e54,
+				0x91, 0x91, 0x51, 0x69, 0xcb, 0x02, 0x7c, 0x25);
+
+/* Trace config client UUID: 1f050626-d505-4e94-b189-535d7de19cf2 */
+static const uuid_le ishtp_traceconfig_guid =
+			UUID_LE(0x1f050626, 0xd505, 0x4e94,
+				0xb1, 0x89, 0x53, 0x5d, 0x7d, 0xe1, 0x9c, 0xf2);
+
+/* ISHFW loader client UUID: c804d06a-55bd-4ea7-aded-1e31228c76dc */
+static const uuid_le ishtp_loader_guid =
+			UUID_LE(0xc804d06a, 0x55bd, 0x4ea7,
+				0xad, 0xed, 0x1e, 0x31, 0x22, 0x8c, 0x76, 0xdc);
+
+static int ishtp_cl_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_device *dev;
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	int ret;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK)
+		return -EINVAL;
+
+	ishtp_cl_misc = container_of(misc,
+				struct ishtp_cl_miscdev, cl_miscdev);
+	if (!ishtp_cl_misc || !ishtp_cl_misc->cl_device)
+		return -ENODEV;
+
+	dev = ishtp_cl_misc->cl_device->ishtp_dev;
+	if (!dev)
+		return -ENODEV;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * Every client only supports one opened instance
+	 * at the sametime.
+	 */
+	if (ishtp_cl_misc->cl) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	cl = ishtp_cl_allocate(dev);
+	if (!cl) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	ret = ishtp_cl_link(cl, ISHTP_HOST_CLIENT_ID_ANY);
+	if (ret)
+		goto out_free;
+
+	ishtp_cl_misc->cl = cl;
+
+	file->private_data = ishtp_cl_misc;
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return nonseekable_open(inode, file);
+
+out_free:
+	kfree(cl);
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+	return ret;
+}
+
+#define WAIT_FOR_SEND_SLICE_MS		100
+#define WAIT_FOR_SEND_COUNT		10
+
+static int ishtp_cl_release(struct inode *inode, struct file *file)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+	struct ishtp_device *dev;
+	int try = WAIT_FOR_SEND_COUNT;
+	int ret;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	cl = ishtp_cl_misc->cl;
+	dev = cl->dev;
+
+	/*
+	 * May happen if device sent FW reset or was intentionally
+	 * halted by host SW. The client is then invalid.
+	 */
+	if ((dev->dev_state == ISHTP_DEV_ENABLED) &&
+			(cl->state == ISHTP_CL_CONNECTED)) {
+		/*
+		 * Check and wait 1s for message in tx_list to be sent.
+		 */
+		do {
+			if (!ishtp_cl_tx_empty(cl))
+				msleep_interruptible(WAIT_FOR_SEND_SLICE_MS);
+			else
+				break;
+		} while (--try);
+
+		cl->state = ISHTP_CL_DISCONNECTING;
+		ret = ishtp_cl_disconnect(cl);
+	}
+
+	ishtp_cl_unlink(cl);
+	ishtp_cl_flush_queues(cl);
+	/* Disband and free all Tx and Rx client-level rings */
+	ishtp_cl_free(cl);
+
+	ishtp_cl_misc->cl = NULL;
+
+	rb = ishtp_cl_misc->read_rb;
+	if (rb) {
+		ishtp_cl_io_rb_recycle(rb);
+		ishtp_cl_misc->read_rb = NULL;
+	}
+
+	file->private_data = NULL;
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret;
+}
+
+static ssize_t ishtp_cl_read(struct file *file, char __user *ubuf,
+			size_t length, loff_t *offset)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+	struct ishtp_device *dev;
+	int ret = 0;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK)
+		return -EINVAL;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	dev = cl->dev;
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (ishtp_cl_misc->read_rb)
+		goto get_rb;
+
+	rb = ishtp_cl_rx_get_rb(cl);
+	if (rb)
+		goto copy_buffer;
+
+	/*
+	 * Release mutex for other operation can be processed parallelly
+	 * during waiting.
+	 */
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	if (wait_event_interruptible(ishtp_cl_misc->read_wait,
+			ishtp_cl_misc->read_rb != NULL)) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Wake up not successful;"
+			"signal pending = %d signal = %08lX\n",
+			signal_pending(current),
+			current->pending.signal.sig[0]);
+		return -ERESTARTSYS;
+	}
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * waitqueue can be woken up in many cases, so must check
+	 * if dev and cl is still available.
+	 */
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	cl = ishtp_cl_misc->cl;
+	if (!cl) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (cl->state == ISHTP_CL_INITIALIZING ||
+		cl->state == ISHTP_CL_DISCONNECTED ||
+		cl->state == ISHTP_CL_DISCONNECTING) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+get_rb:
+	rb = ishtp_cl_misc->read_rb;
+	if (!rb) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+copy_buffer:
+	/* Now copy the data to user space */
+	if (!length || !ubuf || *offset > rb->buf_idx) {
+		ret = -EMSGSIZE;
+		goto out_unlock;
+	}
+
+	/*
+	 * length is being truncated, however buf_idx may
+	 * point beyond that.
+	 */
+	length = min_t(size_t, length, rb->buf_idx - *offset);
+
+	if (copy_to_user(ubuf, rb->buffer.data + *offset, length)) {
+		ret = -EFAULT;
+		goto out_unlock;
+	}
+
+	*offset += length;
+	if ((unsigned long)*offset < rb->buf_idx)
+		goto out_unlock;
+
+	ishtp_cl_io_rb_recycle(rb);
+	ishtp_cl_misc->read_rb = NULL;
+	*offset = 0;
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+	return ret < 0 ? ret : length;
+}
+
+static ssize_t ishtp_cl_write(struct file *file, const char __user *ubuf,
+	size_t length, loff_t *offset)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	void *write_buf;
+	struct ishtp_device *dev;
+	int ret;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK)
+		return -EINVAL;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	dev = cl->dev;
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (cl->state != ISHTP_CL_CONNECTED) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"host client = %d isn't connected to fw client = %d\n",
+			cl->host_client_id, cl->fw_client_id);
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (length <= 0 || length > cl->device->fw_client->props.max_msg_length) {
+		ret = -EMSGSIZE;
+		goto out_unlock;
+	}
+
+	write_buf = memdup_user(ubuf, length);
+	if (IS_ERR(write_buf)) {
+		ret = PTR_ERR(write_buf);
+		goto out_unlock;
+	}
+
+	ret = ishtp_cl_send(cl, write_buf, length);
+
+	kfree(write_buf);
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret < 0 ? ret : length;
+}
+
+static int ishtp_cl_ioctl_connect_client(struct file *file,
+	struct ishtp_connect_client_data *data)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_device *dev;
+	struct ishtp_client *client;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl = ishtp_cl_misc->cl;
+	struct ishtp_fw_client *fw_client;
+
+	if (!cl || !cl->dev)
+		return -ENODEV;
+
+	dev = cl->dev;
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED)
+		return -ENODEV;
+
+	if (cl->state != ISHTP_CL_INITIALIZING &&
+			cl->state != ISHTP_CL_DISCONNECTED)
+		return -EBUSY;
+
+	cl_device = ishtp_cl_misc->cl_device;
+
+	if (uuid_le_cmp(data->in_client_uuid,
+			cl_device->fw_client->props.protocol_name) != 0) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Required uuid don't match current client uuid\n");
+		return -EFAULT;
+	}
+
+	/* Find the fw client we're trying to connect to */
+	fw_client = ishtp_fw_cl_get_client(dev, &data->in_client_uuid);
+	if (!fw_client) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Don't find current client uuid\n");
+		return -ENOENT;
+	}
+
+	cl->fw_client_id = fw_client->client_id;
+	cl->state = ISHTP_CL_CONNECTING;
+
+	/* Prepare the output buffer */
+	client = &data->out_client_properties;
+	client->max_msg_length = fw_client->props.max_msg_length;
+	client->protocol_version = fw_client->props.protocol_version;
+
+	return ishtp_cl_connect(cl);
+}
+
+static long ishtp_cl_ioctl(struct file *file, unsigned int cmd,
+			unsigned long data)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_device *dev;
+	struct ishtp_connect_client_data *connect_data;
+	char fw_stat_buf[20];
+	unsigned int ring_size;
+	int ret = 0;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		mutex_unlock(&ishtp_cl_misc->cl_mutex);
+		return -ENODEV;
+	}
+
+	dev = cl->dev;
+
+	switch (cmd) {
+	case IOCTL_ISH_HW_RESET:
+		ret = ish_hw_reset(dev);
+		break;
+
+	case IOCTL_ISHTP_SET_RX_FIFO_SIZE:
+		ring_size = data;
+
+		if (ring_size > CL_MAX_RX_RING_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (cl->state != ISHTP_CL_INITIALIZING) {
+			ret = -EBUSY;
+			break;
+		}
+
+		cl->rx_ring_size = ring_size;
+		break;
+
+	case IOCTL_ISHTP_SET_TX_FIFO_SIZE:
+		ring_size = data;
+
+		if (ring_size > CL_MAX_TX_RING_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (cl->state != ISHTP_CL_INITIALIZING) {
+			ret = -EBUSY;
+			break;
+		}
+
+		cl->tx_ring_size = ring_size;
+		break;
+
+	case IOCTL_ISH_GET_FW_STATUS:
+		if (!data) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		snprintf(fw_stat_buf, sizeof(fw_stat_buf),
+			"%08X\n", dev->ops->get_fw_status(dev));
+
+		if (copy_to_user((char __user *)data, fw_stat_buf,
+				strlen(fw_stat_buf))) {
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = strlen(fw_stat_buf);
+		break;
+
+	case IOCTL_ISHTP_CONNECT_CLIENT:
+		if (dev->dev_state != ISHTP_DEV_ENABLED) {
+			ret = -ENODEV;
+			break;
+		}
+
+		connect_data = memdup_user((char __user *)data,
+					sizeof(struct ishtp_connect_client_data));
+		if (IS_ERR(connect_data)) {
+			ret = PTR_ERR(connect_data);
+			break;
+		}
+
+		ret = ishtp_cl_ioctl_connect_client(file, connect_data);
+		if (ret) {
+			kfree(connect_data);
+			break;
+		}
+
+		/* If all is ok, copying the data back to user. */
+		if (copy_to_user((char __user *)data, connect_data,
+				sizeof(struct ishtp_connect_client_data)))
+			ret = -EFAULT;
+
+		kfree(connect_data);
+
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret;
+}
+
+/*
+ * File operations structure will be used for ishtp client misc device.
+ */
+static const struct file_operations ishtp_cl_fops = {
+	.owner = THIS_MODULE,
+	.read = ishtp_cl_read,
+	.unlocked_ioctl = ishtp_cl_ioctl,
+	.open = ishtp_cl_open,
+	.release = ishtp_cl_release,
+	.write = ishtp_cl_write,
+	.llseek = no_llseek
+};
+
+/**
+ * ishtp_cl_event_cb() - ISHTP client driver event callback
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on related event recevied from ISHFW.
+ * It will remove event buffer exists on in_process list to related
+ * client device and wait up client driver to process.
+ */
+static void ishtp_cl_event_cb(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc)
+		return;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * If this waitqueue is active, cl_mutex is locked by read(), it's safe
+	 * to access ishtp_cl_misc and cl.
+	 */
+	if (waitqueue_active(&ishtp_cl_misc->read_wait)) {
+
+		/*
+		 * If already has read_rb, wake up waitqueue directly.
+		 */
+		if (ishtp_cl_misc->read_rb) {
+			mutex_unlock(&ishtp_cl_misc->cl_mutex);
+			wake_up_interruptible(&ishtp_cl_misc->read_wait);
+			return;
+		}
+
+		cl = ishtp_cl_misc->cl;
+
+		rb = ishtp_cl_rx_get_rb(cl);
+		if (rb)
+			ishtp_cl_misc->read_rb = rb;
+
+		wake_up_interruptible(&ishtp_cl_misc->read_wait);
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+}
+
+/**
+ * ishtp_cl_reset_handler() - ISHTP client driver reset work handler
+ * @work:		work struct
+ *
+ * This function gets called on reset workqueue scheduled when ISHFW
+ * reset happen. It will disconnect and remove current ishtp_cl, then
+ * create a new ishtp_cl and re-connect again.
+ */
+static void ishtp_cl_reset_handler(struct work_struct *work)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_device *dev;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl;
+	struct ishtp_fw_client *fw_client;
+	int ret = 0;
+
+	ishtp_cl_misc = container_of(work,
+			struct ishtp_cl_miscdev, reset_work);
+
+	dev = ishtp_cl_misc->cl_device->ishtp_dev;
+	if (!dev) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"This cl_device not link to ishtp_dev\n");
+		return;
+	}
+
+	cl_device = ishtp_cl_misc->cl_device;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	cl = ishtp_cl_misc->cl;
+	if (cl) {
+		ishtp_cl_flush_queues(cl);
+		ishtp_cl_free(cl);
+
+		cl = NULL;
+
+		cl = ishtp_cl_allocate(dev);
+		if (!cl) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Allocate ishtp_cl failed\n");
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+
+		if (dev->dev_state != ISHTP_DEV_ENABLED) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Ishtp dev isn't enabled\n");
+			ret = -ENODEV;
+			goto out_free;
+		}
+
+		ret = ishtp_cl_link(cl, ISHTP_HOST_CLIENT_ID_ANY);
+		if (ret) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Can not link to ishtp\n");
+			goto out_free;
+		}
+
+		fw_client = ishtp_fw_cl_get_client(dev,
+				&cl_device->fw_client->props.protocol_name);
+		if (!fw_client) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Don't find related fw client\n");
+			ret = -ENOENT;
+			goto out_free;
+		}
+
+		cl->fw_client_id = fw_client->client_id;
+		cl->state = ISHTP_CL_CONNECTING;
+
+		ret = ishtp_cl_connect(cl);
+		if (ret) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Connect to fw failed\n");
+			goto out_free;
+		}
+
+		ishtp_cl_misc->cl = cl;
+	}
+
+	/* After reset, must register event callback again */
+	ishtp_register_event_cb(cl_device, ishtp_cl_event_cb);
+
+out_free:
+	if (ret) {
+		ishtp_cl_free(cl);
+		ishtp_cl_misc->cl = NULL;
+
+		dev_err(&ishtp_cl_misc->cl_device->dev, "Reset failed\n");
+	}
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+}
+
+/**
+ * ishtp_cl_probe() - ISHTP client driver probe
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device create on ISHTP bus
+ *
+ * Return: 0 on success, non zero on error
+ */
+static int ishtp_cl_probe(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	int ret;
+
+	if (!cl_device)
+		return -ENODEV;
+
+	ishtp_cl_misc = kzalloc(sizeof(struct ishtp_cl_miscdev),
+				GFP_KERNEL);
+	if (!ishtp_cl_misc)
+		return -ENOMEM;
+
+	if (uuid_le_cmp(ishtp_smhi_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-smhi";
+	} else if (uuid_le_cmp(ishtp_trace_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-trace";
+	} else if (uuid_le_cmp(ishtp_traceconfig_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-tracec";
+	} else if (uuid_le_cmp(ishtp_loader_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-loader";
+	} else {
+		dev_err(&cl_device->dev, "Not supported client\n");
+		ret = -ENODEV;
+		goto release_mem;
+	}
+
+	ishtp_cl_misc->cl_miscdev.parent = &cl_device->dev;
+	ishtp_cl_misc->cl_miscdev.fops = &ishtp_cl_fops;
+	ishtp_cl_misc->cl_miscdev.minor = MISC_DYNAMIC_MINOR,
+
+	ret = misc_register(&ishtp_cl_misc->cl_miscdev);
+	if (ret) {
+		dev_err(&cl_device->dev, "misc device register failed\n");
+		goto release_mem;
+	}
+
+	ishtp_cl_misc->cl_device = cl_device;
+
+	init_waitqueue_head(&ishtp_cl_misc->read_wait);
+
+	ishtp_set_drvdata(cl_device, ishtp_cl_misc);
+
+	ishtp_get_device(cl_device);
+
+	mutex_init(&ishtp_cl_misc->cl_mutex);
+
+	INIT_WORK(&ishtp_cl_misc->reset_work, ishtp_cl_reset_handler);
+
+	/* Register event callback */
+	ishtp_register_event_cb(cl_device, ishtp_cl_event_cb);
+
+	return 0;
+
+release_mem:
+	kfree(ishtp_cl_misc);
+
+	return ret;
+}
+
+/**
+ * ishtp_cl_remove() - ISHTP client driver remove
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device remove on ISHTP bus
+ *
+ * Return: 0
+ */
+static int ishtp_cl_remove(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_cl *cl;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc)
+		return -ENODEV;
+
+	if (!ishtp_cl_misc->cl_miscdev.parent)
+		return -ENODEV;
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+	if (cl) {
+		cl->state = ISHTP_CL_DISCONNECTING;
+		ishtp_cl_disconnect(cl);
+		ishtp_cl_unlink(cl);
+		ishtp_cl_flush_queues(cl);
+		ishtp_cl_free(cl);
+		ishtp_cl_misc->cl = NULL;
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	mutex_destroy(&ishtp_cl_misc->cl_mutex);
+
+	misc_deregister(&ishtp_cl_misc->cl_miscdev);
+
+	ishtp_put_device(cl_device);
+
+	kfree(ishtp_cl_misc);
+
+	return 0;
+}
+
+/**
+ * ishtp_cl_reset() - ISHTP client driver reset
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device reset on ISHTP bus.
+ * If client is connected, needs to disconnect client and
+ * reconnect again.
+ *
+ * Return: 0
+ */
+static int ishtp_cl_reset(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc) {
+		dev_err(&cl_device->dev, "Client driver not ready yet\n");
+		return -ENODEV;
+	}
+
+	schedule_work(&ishtp_cl_misc->reset_work);
+
+	return 0;
+}
+
+static struct ishtp_cl_driver ishtp_cl_driver = {
+	.name = "ishtp-client",
+	.probe = ishtp_cl_probe,
+	.remove = ishtp_cl_remove,
+	.reset = ishtp_cl_reset,
+};
+
+static int __init ishtp_client_init(void)
+{
+	/* Register ISHTP client device driver with ISHTP Bus */
+	return ishtp_cl_driver_register(&ishtp_cl_driver);
+}
+
+static void __exit ishtp_client_exit(void)
+{
+	ishtp_cl_driver_unregister(&ishtp_cl_driver);
+}
+
+/* To make sure ISHTP bus driver loaded first */
+late_initcall(ishtp_client_init);
+module_exit(ishtp_client_exit);
+
+MODULE_DESCRIPTION("ISH ISHTP client driver");
+MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ishtp:*");
diff --git a/include/uapi/linux/intel-ishtp-clients.h b/include/uapi/linux/intel-ishtp-clients.h
new file mode 100644
index 0000000..792500a
--- /dev/null
+++ b/include/uapi/linux/intel-ishtp-clients.h
@@ -0,0 +1,73 @@
+/*
+ * Intel ISHTP Clients Interface Header
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _INTEL_ISHTP_CLIENTS_H
+#define _INTEL_ISHTP_CLIENTS_H
+
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+/*
+ * This IOCTL is used to associate the current file descriptor with a
+ * FW Client (given by UUID). This opens a communication channel
+ * between a host client and a FW client. From this point every read and write
+ * will communicate with the associated FW client.
+ * Only in close() (file_operation release()) the communication between
+ * the clients is disconnected
+ *
+ * The IOCTL argument is a struct with a union that contains
+ * the input parameter and the output parameter for this IOCTL.
+ *
+ * The input parameter is UUID of the FW Client.
+ * The output parameter is the properties of the FW client
+ * (FW protocol version and max message size).
+ *
+ */
+#define IOCTL_ISHTP_CONNECT_CLIENT	_IOWR('H', 0x81,	\
+				struct ishtp_connect_client_data)
+
+/* Configuration: set number of Rx/Tx buffers. Must be used before connection */
+#define IOCTL_ISHTP_SET_RX_FIFO_SIZE	_IOWR('H', 0x82, long)
+#define IOCTL_ISHTP_SET_TX_FIFO_SIZE	_IOWR('H', 0x83, long)
+
+/* Get FW status */
+#define IOCTL_ISH_GET_FW_STATUS	_IO('H', 0x84)
+
+#define IOCTL_ISH_HW_RESET	_IO('H', 0x85)
+
+/*
+ * Intel ISHTP client information struct
+ */
+struct ishtp_client {
+	__u32 max_msg_length;
+	__u8 protocol_version;
+	__u8 reserved[3];
+};
+
+/*
+ * IOCTL Connect client data structure
+ */
+struct ishtp_connect_client_data {
+	union {
+		uuid_le			in_client_uuid;
+		struct ishtp_client 	out_client_properties;
+	};
+};
+
+#endif /* _INTEL_ISHTP_CLIENTS_H */
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 3/3] dmaengine: xilinx_dma: Fix race condition in the driver for multiple descriptor scenario
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: robh+dt, mark.rutland, dan.j.williams, vinod.koul, michal.simek,
	soren.brinkmann, appanad, moritz.fischer, laurent.pinchart, luis,
	Jose.Abreu
  Cc: dmaengine, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

When driver is handling AXI DMA SoftIP
When user submits multiple descriptors back to back on the S2MM(recv)
side with the current driver flow the last buffer descriptor next bd
points to a invalid location resulting the invalid data or errors in the
DMA engine.

This patch fixes this issue by creating a BD Chain during
channel allocation itself and use those BD's.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> None.
Changes for v2:
---> None.

 drivers/dma/xilinx/xilinx_dma.c | 133 +++++++++++++++++++++++++---------------
 1 file changed, 83 insertions(+), 50 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 7cd8e08..822ccf00 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -163,6 +163,7 @@
 #define XILINX_DMA_BD_SOP		BIT(27)
 #define XILINX_DMA_BD_EOP		BIT(26)
 #define XILINX_DMA_COALESCE_MAX		255
+#define XILINX_DMA_NUM_DESCS		255
 #define XILINX_DMA_NUM_APP_WORDS	5
 
 /* Multi-Channel DMA Descriptor offsets*/
@@ -310,6 +311,7 @@ struct xilinx_dma_tx_descriptor {
  * @pending_list: Descriptors waiting
  * @active_list: Descriptors ready to submit
  * @done_list: Complete descriptors
+ * @free_seg_list: Free descriptors
  * @common: DMA common channel
  * @desc_pool: Descriptors pool
  * @dev: The dma device
@@ -331,7 +333,9 @@ struct xilinx_dma_tx_descriptor {
  * @desc_submitcount: Descriptor h/w submitted count
  * @residue: Residue for AXI DMA
  * @seg_v: Statically allocated segments base
+ * @seg_p: Physical allocated segments base
  * @cyclic_seg_v: Statically allocated segment base for cyclic transfers
+ * @cyclic_seg_p: Physical allocated segments base for cyclic dma
  * @start_transfer: Differentiate b/w DMA IP's transfer
  */
 struct xilinx_dma_chan {
@@ -342,6 +346,7 @@ struct xilinx_dma_chan {
 	struct list_head pending_list;
 	struct list_head active_list;
 	struct list_head done_list;
+	struct list_head free_seg_list;
 	struct dma_chan common;
 	struct dma_pool *desc_pool;
 	struct device *dev;
@@ -363,7 +368,9 @@ struct xilinx_dma_chan {
 	u32 desc_submitcount;
 	u32 residue;
 	struct xilinx_axidma_tx_segment *seg_v;
+	dma_addr_t seg_p;
 	struct xilinx_axidma_tx_segment *cyclic_seg_v;
+	dma_addr_t cyclic_seg_p;
 	void (*start_transfer)(struct xilinx_dma_chan *chan);
 	u16 tdest;
 };
@@ -569,17 +576,31 @@ static struct xilinx_axidma_tx_segment *
 xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_axidma_tx_segment *segment;
-	dma_addr_t phys;
-
-	segment = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &phys);
-	if (!segment)
-		return NULL;
+	unsigned long flags;
 
-	segment->phys = phys;
+	spin_lock_irqsave(&chan->lock, flags);
+	if (!list_empty(&chan->free_seg_list)) {
+		segment = list_first_entry(&chan->free_seg_list,
+					   struct xilinx_axidma_tx_segment,
+					   node);
+		list_del(&segment->node);
+	}
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 	return segment;
 }
 
+static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
+{
+	u32 next_desc = hw->next_desc;
+	u32 next_desc_msb = hw->next_desc_msb;
+
+	memset(hw, 0, sizeof(struct xilinx_axidma_desc_hw));
+
+	hw->next_desc = next_desc;
+	hw->next_desc_msb = next_desc_msb;
+}
+
 /**
  * xilinx_dma_free_tx_segment - Free transaction segment
  * @chan: Driver specific DMA channel
@@ -588,7 +609,9 @@ xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan,
 				struct xilinx_axidma_tx_segment *segment)
 {
-	dma_pool_free(chan->desc_pool, segment, segment->phys);
+	xilinx_dma_clean_hw_desc(&segment->hw);
+
+	list_add_tail(&segment->node, &chan->free_seg_list);
 }
 
 /**
@@ -713,16 +736,26 @@ static void xilinx_dma_free_descriptors(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	unsigned long flags;
 
 	dev_dbg(chan->dev, "Free all channel resources.\n");
 
 	xilinx_dma_free_descriptors(chan);
+
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		xilinx_dma_free_tx_segment(chan, chan->cyclic_seg_v);
-		xilinx_dma_free_tx_segment(chan, chan->seg_v);
+		spin_lock_irqsave(&chan->lock, flags);
+		INIT_LIST_HEAD(&chan->free_seg_list);
+		spin_unlock_irqrestore(&chan->lock, flags);
+
+		/* Free Memory that is allocated for cyclic DMA Mode */
+		dma_free_coherent(chan->dev, sizeof(*chan->cyclic_seg_v),
+				  chan->cyclic_seg_v, chan->cyclic_seg_p);
+	}
+
+	if (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA) {
+		dma_pool_destroy(chan->desc_pool);
+		chan->desc_pool = NULL;
 	}
-	dma_pool_destroy(chan->desc_pool);
-	chan->desc_pool = NULL;
 }
 
 /**
@@ -805,6 +838,7 @@ static void xilinx_dma_do_tasklet(unsigned long data)
 static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	int i;
 
 	/* Has this channel already been allocated? */
 	if (chan->desc_pool)
@@ -815,11 +849,30 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 	 * for meeting Xilinx VDMA specification requirement.
 	 */
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		chan->desc_pool = dma_pool_create("xilinx_dma_desc_pool",
-				   chan->dev,
-				   sizeof(struct xilinx_axidma_tx_segment),
-				   __alignof__(struct xilinx_axidma_tx_segment),
-				   0);
+		/* Allocate the buffer descriptors. */
+		chan->seg_v = dma_zalloc_coherent(chan->dev,
+						  sizeof(*chan->seg_v) *
+						  XILINX_DMA_NUM_DESCS,
+						  &chan->seg_p, GFP_KERNEL);
+		if (!chan->seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate channel %d descriptors\n",
+				chan->id);
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < XILINX_DMA_NUM_DESCS; i++) {
+			chan->seg_v[i].hw.next_desc =
+			lower_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].hw.next_desc_msb =
+			upper_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].phys = chan->seg_p +
+				sizeof(*chan->seg_v) * i;
+			list_add_tail(&chan->seg_v[i].node,
+				      &chan->free_seg_list);
+		}
 	} else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
 		chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool",
 				   chan->dev,
@@ -834,7 +887,8 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 				     0);
 	}
 
-	if (!chan->desc_pool) {
+	if (!chan->desc_pool &&
+	    (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA)) {
 		dev_err(chan->dev,
 			"unable to allocate channel %d descriptor pool\n",
 			chan->id);
@@ -843,22 +897,20 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
 		/*
-		 * For AXI DMA case after submitting a pending_list, keep
-		 * an extra segment allocated so that the "next descriptor"
-		 * pointer on the tail descriptor always points to a
-		 * valid descriptor, even when paused after reaching taildesc.
-		 * This way, it is possible to issue additional
-		 * transfers without halting and restarting the channel.
-		 */
-		chan->seg_v = xilinx_axidma_alloc_tx_segment(chan);
-
-		/*
 		 * For cyclic DMA mode we need to program the tail Descriptor
 		 * register with a value which is not a part of the BD chain
 		 * so allocating a desc segment during channel allocation for
 		 * programming tail descriptor.
 		 */
-		chan->cyclic_seg_v = xilinx_axidma_alloc_tx_segment(chan);
+		chan->cyclic_seg_v = dma_zalloc_coherent(chan->dev,
+					sizeof(*chan->cyclic_seg_v),
+					&chan->cyclic_seg_p, GFP_KERNEL);
+		if (!chan->cyclic_seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate desc segment for cyclic DMA\n");
+			return -ENOMEM;
+		}
+		chan->cyclic_seg_v->phys = chan->cyclic_seg_p;
 	}
 
 	dma_cookie_init(dchan);
@@ -1197,7 +1249,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_dma_tx_descriptor *head_desc, *tail_desc;
-	struct xilinx_axidma_tx_segment *tail_segment, *old_head, *new_head;
+	struct xilinx_axidma_tx_segment *tail_segment;
 	u32 reg;
 
 	if (chan->err)
@@ -1216,21 +1268,6 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_axidma_tx_segment, node);
 
-	if (chan->has_sg && !chan->xdev->mcdma) {
-		old_head = list_first_entry(&head_desc->segments,
-					struct xilinx_axidma_tx_segment, node);
-		new_head = chan->seg_v;
-		/* Copy Buffer Descriptor fields. */
-		new_head->hw = old_head->hw;
-
-		/* Swap and save new reserve */
-		list_replace_init(&old_head->node, &new_head->node);
-		chan->seg_v = old_head;
-
-		tail_segment->hw.next_desc = chan->seg_v->phys;
-		head_desc->async_tx.phys = new_head->phys;
-	}
-
 	reg = dma_ctrl_read(chan, XILINX_DMA_REG_DMACR);
 
 	if (chan->desc_pendingcount <= XILINX_DMA_COALESCE_MAX) {
@@ -1728,7 +1765,7 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
 	struct xilinx_dma_tx_descriptor *desc;
-	struct xilinx_axidma_tx_segment *segment = NULL, *prev = NULL;
+	struct xilinx_axidma_tx_segment *segment = NULL;
 	u32 *app_w = (u32 *)context;
 	struct scatterlist *sg;
 	size_t copy;
@@ -1779,10 +1816,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 					       XILINX_DMA_NUM_APP_WORDS);
 			}
 
-			if (prev)
-				prev->hw.next_desc = segment->phys;
-
-			prev = segment;
 			sg_used += copy;
 
 			/*
@@ -1796,7 +1829,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 	segment = list_first_entry(&desc->segments,
 				   struct xilinx_axidma_tx_segment, node);
 	desc->async_tx.phys = segment->phys;
-	prev->hw.next_desc = segment->phys;
 
 	/* For the last DMA_MEM_TO_DEV transfer, set EOP */
 	if (chan->direction == DMA_MEM_TO_DEV) {
@@ -2340,6 +2372,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	INIT_LIST_HEAD(&chan->pending_list);
 	INIT_LIST_HEAD(&chan->done_list);
 	INIT_LIST_HEAD(&chan->active_list);
+	INIT_LIST_HEAD(&chan->free_seg_list);
 
 	/* Retrieve the channel properties from the device tree */
 	has_dre = of_property_read_bool(node, "xlnx,include-dre");
-- 
2.1.2

^ permalink raw reply related

* [PATCH v3 3/3] dmaengine: xilinx_dma: Fix race condition in the driver for multiple descriptor scenario
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

When driver is handling AXI DMA SoftIP
When user submits multiple descriptors back to back on the S2MM(recv)
side with the current driver flow the last buffer descriptor next bd
points to a invalid location resulting the invalid data or errors in the
DMA engine.

This patch fixes this issue by creating a BD Chain during
channel allocation itself and use those BD's.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> None.
Changes for v2:
---> None.

 drivers/dma/xilinx/xilinx_dma.c | 133 +++++++++++++++++++++++++---------------
 1 file changed, 83 insertions(+), 50 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 7cd8e08..822ccf00 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -163,6 +163,7 @@
 #define XILINX_DMA_BD_SOP		BIT(27)
 #define XILINX_DMA_BD_EOP		BIT(26)
 #define XILINX_DMA_COALESCE_MAX		255
+#define XILINX_DMA_NUM_DESCS		255
 #define XILINX_DMA_NUM_APP_WORDS	5
 
 /* Multi-Channel DMA Descriptor offsets*/
@@ -310,6 +311,7 @@ struct xilinx_dma_tx_descriptor {
  * @pending_list: Descriptors waiting
  * @active_list: Descriptors ready to submit
  * @done_list: Complete descriptors
+ * @free_seg_list: Free descriptors
  * @common: DMA common channel
  * @desc_pool: Descriptors pool
  * @dev: The dma device
@@ -331,7 +333,9 @@ struct xilinx_dma_tx_descriptor {
  * @desc_submitcount: Descriptor h/w submitted count
  * @residue: Residue for AXI DMA
  * @seg_v: Statically allocated segments base
+ * @seg_p: Physical allocated segments base
  * @cyclic_seg_v: Statically allocated segment base for cyclic transfers
+ * @cyclic_seg_p: Physical allocated segments base for cyclic dma
  * @start_transfer: Differentiate b/w DMA IP's transfer
  */
 struct xilinx_dma_chan {
@@ -342,6 +346,7 @@ struct xilinx_dma_chan {
 	struct list_head pending_list;
 	struct list_head active_list;
 	struct list_head done_list;
+	struct list_head free_seg_list;
 	struct dma_chan common;
 	struct dma_pool *desc_pool;
 	struct device *dev;
@@ -363,7 +368,9 @@ struct xilinx_dma_chan {
 	u32 desc_submitcount;
 	u32 residue;
 	struct xilinx_axidma_tx_segment *seg_v;
+	dma_addr_t seg_p;
 	struct xilinx_axidma_tx_segment *cyclic_seg_v;
+	dma_addr_t cyclic_seg_p;
 	void (*start_transfer)(struct xilinx_dma_chan *chan);
 	u16 tdest;
 };
@@ -569,17 +576,31 @@ static struct xilinx_axidma_tx_segment *
 xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_axidma_tx_segment *segment;
-	dma_addr_t phys;
-
-	segment = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &phys);
-	if (!segment)
-		return NULL;
+	unsigned long flags;
 
-	segment->phys = phys;
+	spin_lock_irqsave(&chan->lock, flags);
+	if (!list_empty(&chan->free_seg_list)) {
+		segment = list_first_entry(&chan->free_seg_list,
+					   struct xilinx_axidma_tx_segment,
+					   node);
+		list_del(&segment->node);
+	}
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 	return segment;
 }
 
+static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
+{
+	u32 next_desc = hw->next_desc;
+	u32 next_desc_msb = hw->next_desc_msb;
+
+	memset(hw, 0, sizeof(struct xilinx_axidma_desc_hw));
+
+	hw->next_desc = next_desc;
+	hw->next_desc_msb = next_desc_msb;
+}
+
 /**
  * xilinx_dma_free_tx_segment - Free transaction segment
  * @chan: Driver specific DMA channel
@@ -588,7 +609,9 @@ xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan,
 				struct xilinx_axidma_tx_segment *segment)
 {
-	dma_pool_free(chan->desc_pool, segment, segment->phys);
+	xilinx_dma_clean_hw_desc(&segment->hw);
+
+	list_add_tail(&segment->node, &chan->free_seg_list);
 }
 
 /**
@@ -713,16 +736,26 @@ static void xilinx_dma_free_descriptors(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	unsigned long flags;
 
 	dev_dbg(chan->dev, "Free all channel resources.\n");
 
 	xilinx_dma_free_descriptors(chan);
+
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		xilinx_dma_free_tx_segment(chan, chan->cyclic_seg_v);
-		xilinx_dma_free_tx_segment(chan, chan->seg_v);
+		spin_lock_irqsave(&chan->lock, flags);
+		INIT_LIST_HEAD(&chan->free_seg_list);
+		spin_unlock_irqrestore(&chan->lock, flags);
+
+		/* Free Memory that is allocated for cyclic DMA Mode */
+		dma_free_coherent(chan->dev, sizeof(*chan->cyclic_seg_v),
+				  chan->cyclic_seg_v, chan->cyclic_seg_p);
+	}
+
+	if (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA) {
+		dma_pool_destroy(chan->desc_pool);
+		chan->desc_pool = NULL;
 	}
-	dma_pool_destroy(chan->desc_pool);
-	chan->desc_pool = NULL;
 }
 
 /**
@@ -805,6 +838,7 @@ static void xilinx_dma_do_tasklet(unsigned long data)
 static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	int i;
 
 	/* Has this channel already been allocated? */
 	if (chan->desc_pool)
@@ -815,11 +849,30 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 	 * for meeting Xilinx VDMA specification requirement.
 	 */
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		chan->desc_pool = dma_pool_create("xilinx_dma_desc_pool",
-				   chan->dev,
-				   sizeof(struct xilinx_axidma_tx_segment),
-				   __alignof__(struct xilinx_axidma_tx_segment),
-				   0);
+		/* Allocate the buffer descriptors. */
+		chan->seg_v = dma_zalloc_coherent(chan->dev,
+						  sizeof(*chan->seg_v) *
+						  XILINX_DMA_NUM_DESCS,
+						  &chan->seg_p, GFP_KERNEL);
+		if (!chan->seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate channel %d descriptors\n",
+				chan->id);
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < XILINX_DMA_NUM_DESCS; i++) {
+			chan->seg_v[i].hw.next_desc =
+			lower_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].hw.next_desc_msb =
+			upper_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].phys = chan->seg_p +
+				sizeof(*chan->seg_v) * i;
+			list_add_tail(&chan->seg_v[i].node,
+				      &chan->free_seg_list);
+		}
 	} else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
 		chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool",
 				   chan->dev,
@@ -834,7 +887,8 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 				     0);
 	}
 
-	if (!chan->desc_pool) {
+	if (!chan->desc_pool &&
+	    (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA)) {
 		dev_err(chan->dev,
 			"unable to allocate channel %d descriptor pool\n",
 			chan->id);
@@ -843,22 +897,20 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
 		/*
-		 * For AXI DMA case after submitting a pending_list, keep
-		 * an extra segment allocated so that the "next descriptor"
-		 * pointer on the tail descriptor always points to a
-		 * valid descriptor, even when paused after reaching taildesc.
-		 * This way, it is possible to issue additional
-		 * transfers without halting and restarting the channel.
-		 */
-		chan->seg_v = xilinx_axidma_alloc_tx_segment(chan);
-
-		/*
 		 * For cyclic DMA mode we need to program the tail Descriptor
 		 * register with a value which is not a part of the BD chain
 		 * so allocating a desc segment during channel allocation for
 		 * programming tail descriptor.
 		 */
-		chan->cyclic_seg_v = xilinx_axidma_alloc_tx_segment(chan);
+		chan->cyclic_seg_v = dma_zalloc_coherent(chan->dev,
+					sizeof(*chan->cyclic_seg_v),
+					&chan->cyclic_seg_p, GFP_KERNEL);
+		if (!chan->cyclic_seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate desc segment for cyclic DMA\n");
+			return -ENOMEM;
+		}
+		chan->cyclic_seg_v->phys = chan->cyclic_seg_p;
 	}
 
 	dma_cookie_init(dchan);
@@ -1197,7 +1249,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_dma_tx_descriptor *head_desc, *tail_desc;
-	struct xilinx_axidma_tx_segment *tail_segment, *old_head, *new_head;
+	struct xilinx_axidma_tx_segment *tail_segment;
 	u32 reg;
 
 	if (chan->err)
@@ -1216,21 +1268,6 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_axidma_tx_segment, node);
 
-	if (chan->has_sg && !chan->xdev->mcdma) {
-		old_head = list_first_entry(&head_desc->segments,
-					struct xilinx_axidma_tx_segment, node);
-		new_head = chan->seg_v;
-		/* Copy Buffer Descriptor fields. */
-		new_head->hw = old_head->hw;
-
-		/* Swap and save new reserve */
-		list_replace_init(&old_head->node, &new_head->node);
-		chan->seg_v = old_head;
-
-		tail_segment->hw.next_desc = chan->seg_v->phys;
-		head_desc->async_tx.phys = new_head->phys;
-	}
-
 	reg = dma_ctrl_read(chan, XILINX_DMA_REG_DMACR);
 
 	if (chan->desc_pendingcount <= XILINX_DMA_COALESCE_MAX) {
@@ -1728,7 +1765,7 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
 	struct xilinx_dma_tx_descriptor *desc;
-	struct xilinx_axidma_tx_segment *segment = NULL, *prev = NULL;
+	struct xilinx_axidma_tx_segment *segment = NULL;
 	u32 *app_w = (u32 *)context;
 	struct scatterlist *sg;
 	size_t copy;
@@ -1779,10 +1816,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 					       XILINX_DMA_NUM_APP_WORDS);
 			}
 
-			if (prev)
-				prev->hw.next_desc = segment->phys;
-
-			prev = segment;
 			sg_used += copy;
 
 			/*
@@ -1796,7 +1829,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 	segment = list_first_entry(&desc->segments,
 				   struct xilinx_axidma_tx_segment, node);
 	desc->async_tx.phys = segment->phys;
-	prev->hw.next_desc = segment->phys;
 
 	/* For the last DMA_MEM_TO_DEV transfer, set EOP */
 	if (chan->direction == DMA_MEM_TO_DEV) {
@@ -2340,6 +2372,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	INIT_LIST_HEAD(&chan->pending_list);
 	INIT_LIST_HEAD(&chan->done_list);
 	INIT_LIST_HEAD(&chan->active_list);
+	INIT_LIST_HEAD(&chan->free_seg_list);
 
 	/* Retrieve the channel properties from the device tree */
 	has_dre = of_property_read_bool(node, "xlnx,include-dre");
-- 
2.1.2

^ permalink raw reply related

* [PATCH v3 2/3] dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in vdma
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: robh+dt, mark.rutland, dan.j.williams, vinod.koul, michal.simek,
	soren.brinkmann, appanad, moritz.fischer, laurent.pinchart, luis,
	Jose.Abreu
  Cc: dmaengine, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

When VDMA is configured for more than one frame in the h/w
for example h/w is configured for n number of frames and user
Submits n number of frames and triggered the DMA using issue_pending API.
In the current driver flow we are submitting one frame at a time
but we should submit all the n number of frames at one time as the h/w
Is configured for n number of frames.

This patch fixes this issue.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> Added Checks for frame store configuration. If frame store
     Configuration is not present at the h/w level and user
     Submits less frames added debug prints in the driver as relevant.
Changes for v2:
---> Fixed race conditions in the driver as suggested by Jose Abreu
---> Fixed unnecessray if else checks in the vdma_start_transfer
     as suggested by Laurent Pinchart.

 .../devicetree/bindings/dma/xilinx/xilinx_dma.txt  |  2 +
 drivers/dma/xilinx/xilinx_dma.c                    | 78 +++++++++++++++-------
 2 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
index a2b8bfa..1f65e09 100644
--- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
+++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
@@ -66,6 +66,8 @@ Optional child node properties:
 Optional child node properties for VDMA:
 - xlnx,genlock-mode: Tells Genlock synchronization is
 	enabled/disabled in hardware.
+- xlnx,fstore-config: Tells Whether Frame Store Configuration is
+	enabled/disabled in hardware.
 Optional child node properties for AXI DMA:
 -dma-channels: Number of dma channels in child node.
 
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index be7eb41..7cd8e08 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -322,6 +322,7 @@ struct xilinx_dma_tx_descriptor {
  * @genlock: Support genlock mode
  * @err: Channel has errors
  * @idle: Check for channel idle
+ * @has_fstoreconfig: Check for frame store configuration
  * @tasklet: Cleanup work after irq
  * @config: Device configuration info
  * @flush_on_fsync: Flush on Frame sync
@@ -353,6 +354,7 @@ struct xilinx_dma_chan {
 	bool genlock;
 	bool err;
 	bool idle;
+	bool has_fstoreconfig;
 	struct tasklet_struct tasklet;
 	struct xilinx_vdma_config config;
 	bool flush_on_fsync;
@@ -990,6 +992,26 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (list_empty(&chan->pending_list))
 		return;
 
+	/*
+	 * Note: When VDMA is built with default h/w configuration
+	 * On the S2MM(recv) side user should submit frames upto
+	 * H/W configured. If users submits less than h/w configured
+	 * VDMA engine tries to write to a invalid location
+	 * Results undefined behaviour/memory corruption.
+	 *
+	 * If user would like to submit frames less than h/w capable
+	 * On S2MM side please enable debug info 13 at the h/w level
+	 * It will allows the frame buffers numbers to be modified at runtime.
+	 */
+	if (!chan->has_fstoreconfig && chan->direction == DMA_DEV_TO_MEM &&
+	    chan->desc_pendingcount < chan->num_frms) {
+		dev_dbg(chan->dev, "Frame Store Configuration is not enabled at the");
+		dev_dbg(chan->dev, " H/w level enable Debug info 13 at the h/w level");
+		dev_dbg(chan->dev, " OR Submit the frames upto h/w Capable\n\r");
+
+		return;
+	}
+
 	desc = list_first_entry(&chan->pending_list,
 				struct xilinx_dma_tx_descriptor, node);
 	tail_desc = list_last_entry(&chan->pending_list,
@@ -1052,25 +1074,38 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->has_sg) {
 		dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
 				tail_segment->phys);
+		list_splice_tail_init(&chan->pending_list, &chan->active_list);
+		chan->desc_pendingcount = 0;
 	} else {
 		struct xilinx_vdma_tx_segment *segment, *last = NULL;
-		int i = 0;
+		int i = 0, j = 0;
 
 		if (chan->desc_submitcount < chan->num_frms)
 			i = chan->desc_submitcount;
 
-		list_for_each_entry(segment, &desc->segments, node) {
-			if (chan->ext_addr)
-				vdma_desc_write_64(chan,
-					XILINX_VDMA_REG_START_ADDRESS_64(i++),
-					segment->hw.buf_addr,
-					segment->hw.buf_addr_msb);
-			else
-				vdma_desc_write(chan,
-					XILINX_VDMA_REG_START_ADDRESS(i++),
-					segment->hw.buf_addr);
-
-			last = segment;
+		for (j = 0; j < chan->num_frms; ) {
+			list_for_each_entry(segment, &desc->segments, node) {
+				if (chan->ext_addr)
+					vdma_desc_write_64(chan,
+					  XILINX_VDMA_REG_START_ADDRESS_64(i++),
+					  segment->hw.buf_addr,
+					  segment->hw.buf_addr_msb);
+				else
+					vdma_desc_write(chan,
+					    XILINX_VDMA_REG_START_ADDRESS(i++),
+					    segment->hw.buf_addr);
+
+				last = segment;
+			}
+			list_del(&desc->node);
+			list_add_tail(&desc->node, &chan->active_list);
+			j++;
+			if (list_empty(&chan->pending_list) ||
+			    (i == chan->num_frms))
+				break;
+			desc = list_first_entry(&chan->pending_list,
+						struct xilinx_dma_tx_descriptor,
+						node);
 		}
 
 		if (!last)
@@ -1081,20 +1116,14 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE,
 				last->hw.stride);
 		vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
-	}
 
-	chan->idle = false;
-	if (!chan->has_sg) {
-		list_del(&desc->node);
-		list_add_tail(&desc->node, &chan->active_list);
-		chan->desc_submitcount++;
-		chan->desc_pendingcount--;
+		chan->desc_submitcount += j;
+		chan->desc_pendingcount -= j;
 		if (chan->desc_submitcount == chan->num_frms)
 			chan->desc_submitcount = 0;
-	} else {
-		list_splice_tail_init(&chan->pending_list, &chan->active_list);
-		chan->desc_pendingcount = 0;
 	}
+
+	chan->idle = false;
 }
 
 /**
@@ -1342,6 +1371,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 
 	chan->err = false;
 	chan->idle = true;
+	chan->desc_submitcount = 0;
 
 	return err;
 }
@@ -2315,6 +2345,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	has_dre = of_property_read_bool(node, "xlnx,include-dre");
 
 	chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode");
+	chan->has_fstoreconfig = of_property_read_bool(node,
+						       "xlnx,fstore-config");
 
 	err = of_property_read_u32(node, "xlnx,datawidth", &value);
 	if (err) {
-- 
2.1.2

^ permalink raw reply related

* [PATCH v3 2/3] dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in vdma
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

When VDMA is configured for more than one frame in the h/w
for example h/w is configured for n number of frames and user
Submits n number of frames and triggered the DMA using issue_pending API.
In the current driver flow we are submitting one frame at a time
but we should submit all the n number of frames at one time as the h/w
Is configured for n number of frames.

This patch fixes this issue.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> Added Checks for frame store configuration. If frame store
     Configuration is not present at the h/w level and user
     Submits less frames added debug prints in the driver as relevant.
Changes for v2:
---> Fixed race conditions in the driver as suggested by Jose Abreu
---> Fixed unnecessray if else checks in the vdma_start_transfer
     as suggested by Laurent Pinchart.

 .../devicetree/bindings/dma/xilinx/xilinx_dma.txt  |  2 +
 drivers/dma/xilinx/xilinx_dma.c                    | 78 +++++++++++++++-------
 2 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
index a2b8bfa..1f65e09 100644
--- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
+++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
@@ -66,6 +66,8 @@ Optional child node properties:
 Optional child node properties for VDMA:
 - xlnx,genlock-mode: Tells Genlock synchronization is
 	enabled/disabled in hardware.
+- xlnx,fstore-config: Tells Whether Frame Store Configuration is
+	enabled/disabled in hardware.
 Optional child node properties for AXI DMA:
 -dma-channels: Number of dma channels in child node.
 
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index be7eb41..7cd8e08 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -322,6 +322,7 @@ struct xilinx_dma_tx_descriptor {
  * @genlock: Support genlock mode
  * @err: Channel has errors
  * @idle: Check for channel idle
+ * @has_fstoreconfig: Check for frame store configuration
  * @tasklet: Cleanup work after irq
  * @config: Device configuration info
  * @flush_on_fsync: Flush on Frame sync
@@ -353,6 +354,7 @@ struct xilinx_dma_chan {
 	bool genlock;
 	bool err;
 	bool idle;
+	bool has_fstoreconfig;
 	struct tasklet_struct tasklet;
 	struct xilinx_vdma_config config;
 	bool flush_on_fsync;
@@ -990,6 +992,26 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (list_empty(&chan->pending_list))
 		return;
 
+	/*
+	 * Note: When VDMA is built with default h/w configuration
+	 * On the S2MM(recv) side user should submit frames upto
+	 * H/W configured. If users submits less than h/w configured
+	 * VDMA engine tries to write to a invalid location
+	 * Results undefined behaviour/memory corruption.
+	 *
+	 * If user would like to submit frames less than h/w capable
+	 * On S2MM side please enable debug info 13 at the h/w level
+	 * It will allows the frame buffers numbers to be modified at runtime.
+	 */
+	if (!chan->has_fstoreconfig && chan->direction == DMA_DEV_TO_MEM &&
+	    chan->desc_pendingcount < chan->num_frms) {
+		dev_dbg(chan->dev, "Frame Store Configuration is not enabled at the");
+		dev_dbg(chan->dev, " H/w level enable Debug info 13 at the h/w level");
+		dev_dbg(chan->dev, " OR Submit the frames upto h/w Capable\n\r");
+
+		return;
+	}
+
 	desc = list_first_entry(&chan->pending_list,
 				struct xilinx_dma_tx_descriptor, node);
 	tail_desc = list_last_entry(&chan->pending_list,
@@ -1052,25 +1074,38 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->has_sg) {
 		dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
 				tail_segment->phys);
+		list_splice_tail_init(&chan->pending_list, &chan->active_list);
+		chan->desc_pendingcount = 0;
 	} else {
 		struct xilinx_vdma_tx_segment *segment, *last = NULL;
-		int i = 0;
+		int i = 0, j = 0;
 
 		if (chan->desc_submitcount < chan->num_frms)
 			i = chan->desc_submitcount;
 
-		list_for_each_entry(segment, &desc->segments, node) {
-			if (chan->ext_addr)
-				vdma_desc_write_64(chan,
-					XILINX_VDMA_REG_START_ADDRESS_64(i++),
-					segment->hw.buf_addr,
-					segment->hw.buf_addr_msb);
-			else
-				vdma_desc_write(chan,
-					XILINX_VDMA_REG_START_ADDRESS(i++),
-					segment->hw.buf_addr);
-
-			last = segment;
+		for (j = 0; j < chan->num_frms; ) {
+			list_for_each_entry(segment, &desc->segments, node) {
+				if (chan->ext_addr)
+					vdma_desc_write_64(chan,
+					  XILINX_VDMA_REG_START_ADDRESS_64(i++),
+					  segment->hw.buf_addr,
+					  segment->hw.buf_addr_msb);
+				else
+					vdma_desc_write(chan,
+					    XILINX_VDMA_REG_START_ADDRESS(i++),
+					    segment->hw.buf_addr);
+
+				last = segment;
+			}
+			list_del(&desc->node);
+			list_add_tail(&desc->node, &chan->active_list);
+			j++;
+			if (list_empty(&chan->pending_list) ||
+			    (i == chan->num_frms))
+				break;
+			desc = list_first_entry(&chan->pending_list,
+						struct xilinx_dma_tx_descriptor,
+						node);
 		}
 
 		if (!last)
@@ -1081,20 +1116,14 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE,
 				last->hw.stride);
 		vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
-	}
 
-	chan->idle = false;
-	if (!chan->has_sg) {
-		list_del(&desc->node);
-		list_add_tail(&desc->node, &chan->active_list);
-		chan->desc_submitcount++;
-		chan->desc_pendingcount--;
+		chan->desc_submitcount += j;
+		chan->desc_pendingcount -= j;
 		if (chan->desc_submitcount == chan->num_frms)
 			chan->desc_submitcount = 0;
-	} else {
-		list_splice_tail_init(&chan->pending_list, &chan->active_list);
-		chan->desc_pendingcount = 0;
 	}
+
+	chan->idle = false;
 }
 
 /**
@@ -1342,6 +1371,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 
 	chan->err = false;
 	chan->idle = true;
+	chan->desc_submitcount = 0;
 
 	return err;
 }
@@ -2315,6 +2345,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	has_dre = of_property_read_bool(node, "xlnx,include-dre");
 
 	chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode");
+	chan->has_fstoreconfig = of_property_read_bool(node,
+						       "xlnx,fstore-config");
 
 	err = of_property_read_u32(node, "xlnx,datawidth", &value);
 	if (err) {
-- 
2.1.2

^ permalink raw reply related

* Re: [Qemu-devel] [PATCH v2 3/4] migration: disallow migrate_add_blocker during migration
From: Ashijeet Acharya @ 2017-01-04  6:53 UTC (permalink / raw)
  To: Greg Kurz
  Cc: dgilbert@redhat.com, John Snow, amit.shah, Paolo Bonzini,
	Kevin Wolf, Markus Armbruster, quintela, Michael S. Tsirkin,
	marcandre.lureau, aneesh.kumar, Peter Maydell, QEMU Developers
In-Reply-To: <20161216113728.77671b1a@bahia.lan>

On Fri, Dec 16, 2016 at 4:07 PM, Greg Kurz <groug@kaod.org> wrote:
> On Fri, 16 Dec 2016 15:23:43 +0530
> Ashijeet Acharya <ashijeetacharya@gmail.com> wrote:
>
>> If a migration is already in progress and somebody attempts
>> to add a migration blocker, this should rightly fail.
>>
>> Add an errp parameter and a retcode return value to migrate_add_blocker.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>
>> ---
>>  block/qcow.c                  |  6 +++++-
>>  block/vdi.c                   |  6 +++++-
>>  block/vhdx.c                  | 16 ++++++++++------
>>  block/vmdk.c                  |  7 ++++++-
>>  block/vpc.c                   | 10 +++++++---
>>  block/vvfat.c                 | 20 ++++++++++++--------
>>  hw/9pfs/9p.c                  | 16 ++++++++++++----
>>  hw/display/virtio-gpu.c       | 29 ++++++++++++++++-------------
>>  hw/intc/arm_gic_kvm.c         | 16 ++++++++++------
>>  hw/intc/arm_gicv3_its_kvm.c   | 18 +++++++++++-------
>>  hw/intc/arm_gicv3_kvm.c       | 19 ++++++++++++-------
>>  hw/misc/ivshmem.c             | 11 +++++++----
>>  hw/scsi/vhost-scsi.c          | 25 +++++++++++++++++++------
>>  hw/virtio/vhost.c             |  8 +++++++-
>>  include/migration/migration.h |  6 +++++-
>>  migration/migration.c         | 35 +++++++++++++++++++++++++++++++++--
>>  stubs/migr-blocker.c          |  3 ++-
>>  target-i386/kvm.c             | 16 +++++++++++++---
>>  18 files changed, 192 insertions(+), 75 deletions(-)
>>
>> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
>> index faebd91..e72ef43 100644
>> --- a/hw/9pfs/9p.c
>> +++ b/hw/9pfs/9p.c
>> @@ -1013,20 +1013,28 @@ static void coroutine_fn v9fs_attach(void *opaque)
>>          goto out;
>>      }
>>      err += offset;
>> -    memcpy(&s->root_qid, &qid, sizeof(qid));
>> -    trace_v9fs_attach_return(pdu->tag, pdu->id,
>> -                             qid.type, qid.version, qid.path);
>> +
>>      /*
>>       * disable migration if we haven't done already.
>>       * attach could get called multiple times for the same export.
>>       */
>>      if (!s->migration_blocker) {
>> +        int ret;
>>          s->root_fid = fid;
>
> Since we may fail now, I guess ^^ should move...
>
>>          error_setg(&s->migration_blocker,
>>                     "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
>>                     s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
>> -        migrate_add_blocker(s->migration_blocker);
>> +        ret = migrate_add_blocker(s->migration_blocker, NULL);
>> +        if (ret < 0) {
>> +            err = ret;
>> +            clunk_fid(s, fid);
>
> Please don't rely on put_fid() to rollback what was done in this
> function. You need to do the following here:
> - error_free(s->migration_blocker)
> - s->migration_blocker = NULL

Okay, I was not aware of this. I have added these two now.

>
>> +            goto out;
>> +        }
>
>           s->root_fid = fid;
>
> ... here.

Right. I missed out on this one. I have moved it now.

Ashijeet
>
>>      }
>> +
>> +    memcpy(&s->root_qid, &qid, sizeof(qid));
>> +    trace_v9fs_attach_return(pdu->tag, pdu->id,
>> +                             qid.type, qid.version, qid.path);
>>  out:
>>      put_fid(pdu, fidp);
>>  out_nofid:

^ permalink raw reply

* [PATCH v3 1/3] dmaengine: xilinx_dma: Check for channel idle state before submitting dma descriptor
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: robh+dt, mark.rutland, dan.j.williams, vinod.koul, michal.simek,
	soren.brinkmann, appanad, moritz.fischer, laurent.pinchart, luis,
	Jose.Abreu
  Cc: dmaengine, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

Add channel idle state to ensure that dma descriptor is not
submitted when VDMA engine is in progress.

Reviewed-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> None.
Changes for v2:
---> Add idle check in the reset as suggested by Jose Abreu
---> Removed xilinx_dma_is_running/xilinx_dma_is_idle checks
    in the driver and used common idle checks across the driver
    as suggested by Laurent Pinchart.

 drivers/dma/xilinx/xilinx_dma.c | 56 +++++++++++++----------------------------
 1 file changed, 17 insertions(+), 39 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 8288fe4..be7eb41 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -321,6 +321,7 @@ struct xilinx_dma_tx_descriptor {
  * @cyclic: Check for cyclic transfers.
  * @genlock: Support genlock mode
  * @err: Channel has errors
+ * @idle: Check for channel idle
  * @tasklet: Cleanup work after irq
  * @config: Device configuration info
  * @flush_on_fsync: Flush on Frame sync
@@ -351,6 +352,7 @@ struct xilinx_dma_chan {
 	bool cyclic;
 	bool genlock;
 	bool err;
+	bool idle;
 	struct tasklet_struct tasklet;
 	struct xilinx_vdma_config config;
 	bool flush_on_fsync;
@@ -920,32 +922,6 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
 }
 
 /**
- * xilinx_dma_is_running - Check if DMA channel is running
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if running, '0' if not.
- */
-static bool xilinx_dma_is_running(struct xilinx_dma_chan *chan)
-{
-	return !(dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		 XILINX_DMA_DMASR_HALTED) &&
-		(dma_ctrl_read(chan, XILINX_DMA_REG_DMACR) &
-		 XILINX_DMA_DMACR_RUNSTOP);
-}
-
-/**
- * xilinx_dma_is_idle - Check if DMA channel is idle
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if idle, '0' if not.
- */
-static bool xilinx_dma_is_idle(struct xilinx_dma_chan *chan)
-{
-	return dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		XILINX_DMA_DMASR_IDLE;
-}
-
-/**
  * xilinx_dma_halt - Halt DMA channel
  * @chan: Driver specific DMA channel
  */
@@ -966,6 +942,7 @@ static void xilinx_dma_halt(struct xilinx_dma_chan *chan)
 			chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR));
 		chan->err = true;
 	}
+	chan->idle = true;
 }
 
 /**
@@ -1007,6 +984,9 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1018,13 +998,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_vdma_tx_segment, node);
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
-		return;
-	}
-
 	/*
 	 * If hardware is idle, then all descriptors on the running lists are
 	 * done, start new transfers
@@ -1110,6 +1083,7 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
 	}
 
+	chan->idle = false;
 	if (!chan->has_sg) {
 		list_del(&desc->node);
 		list_add_tail(&desc->node, &chan->active_list);
@@ -1136,6 +1110,9 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1181,6 +1158,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1196,15 +1174,11 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
-	if (list_empty(&chan->pending_list))
+	if (!chan->idle)
 		return;
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
+	if (list_empty(&chan->pending_list))
 		return;
-	}
 
 	head_desc = list_first_entry(&chan->pending_list,
 				     struct xilinx_dma_tx_descriptor, node);
@@ -1302,6 +1276,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1366,6 +1341,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 	}
 
 	chan->err = false;
+	chan->idle = true;
 
 	return err;
 }
@@ -1447,6 +1423,7 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
 	if (status & XILINX_DMA_DMASR_FRM_CNT_IRQ) {
 		spin_lock(&chan->lock);
 		xilinx_dma_complete_descriptor(chan);
+		chan->idle = true;
 		chan->start_transfer(chan);
 		spin_unlock(&chan->lock);
 	}
@@ -2327,6 +2304,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	chan->has_sg = xdev->has_sg;
 	chan->desc_pendingcount = 0x0;
 	chan->ext_addr = xdev->ext_addr;
+	chan->idle = true;
 
 	spin_lock_init(&chan->lock);
 	INIT_LIST_HEAD(&chan->pending_list);
-- 
2.1.2

^ permalink raw reply related

* [PATCH v3 1/3] dmaengine: xilinx_dma: Check for channel idle state before submitting dma descriptor
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483512847-25710-1-git-send-email-appanad@xilinx.com>

Add channel idle state to ensure that dma descriptor is not
submitted when VDMA engine is in progress.

Reviewed-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v3:
---> None.
Changes for v2:
---> Add idle check in the reset as suggested by Jose Abreu
---> Removed xilinx_dma_is_running/xilinx_dma_is_idle checks
    in the driver and used common idle checks across the driver
    as suggested by Laurent Pinchart.

 drivers/dma/xilinx/xilinx_dma.c | 56 +++++++++++++----------------------------
 1 file changed, 17 insertions(+), 39 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 8288fe4..be7eb41 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -321,6 +321,7 @@ struct xilinx_dma_tx_descriptor {
  * @cyclic: Check for cyclic transfers.
  * @genlock: Support genlock mode
  * @err: Channel has errors
+ * @idle: Check for channel idle
  * @tasklet: Cleanup work after irq
  * @config: Device configuration info
  * @flush_on_fsync: Flush on Frame sync
@@ -351,6 +352,7 @@ struct xilinx_dma_chan {
 	bool cyclic;
 	bool genlock;
 	bool err;
+	bool idle;
 	struct tasklet_struct tasklet;
 	struct xilinx_vdma_config config;
 	bool flush_on_fsync;
@@ -920,32 +922,6 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
 }
 
 /**
- * xilinx_dma_is_running - Check if DMA channel is running
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if running, '0' if not.
- */
-static bool xilinx_dma_is_running(struct xilinx_dma_chan *chan)
-{
-	return !(dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		 XILINX_DMA_DMASR_HALTED) &&
-		(dma_ctrl_read(chan, XILINX_DMA_REG_DMACR) &
-		 XILINX_DMA_DMACR_RUNSTOP);
-}
-
-/**
- * xilinx_dma_is_idle - Check if DMA channel is idle
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if idle, '0' if not.
- */
-static bool xilinx_dma_is_idle(struct xilinx_dma_chan *chan)
-{
-	return dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		XILINX_DMA_DMASR_IDLE;
-}
-
-/**
  * xilinx_dma_halt - Halt DMA channel
  * @chan: Driver specific DMA channel
  */
@@ -966,6 +942,7 @@ static void xilinx_dma_halt(struct xilinx_dma_chan *chan)
 			chan, dma_ctrl_read(chan, XILINX_DMA_REG_DMASR));
 		chan->err = true;
 	}
+	chan->idle = true;
 }
 
 /**
@@ -1007,6 +984,9 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1018,13 +998,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_vdma_tx_segment, node);
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
-		return;
-	}
-
 	/*
 	 * If hardware is idle, then all descriptors on the running lists are
 	 * done, start new transfers
@@ -1110,6 +1083,7 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
 	}
 
+	chan->idle = false;
 	if (!chan->has_sg) {
 		list_del(&desc->node);
 		list_add_tail(&desc->node, &chan->active_list);
@@ -1136,6 +1110,9 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1181,6 +1158,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1196,15 +1174,11 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
-	if (list_empty(&chan->pending_list))
+	if (!chan->idle)
 		return;
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
+	if (list_empty(&chan->pending_list))
 		return;
-	}
 
 	head_desc = list_first_entry(&chan->pending_list,
 				     struct xilinx_dma_tx_descriptor, node);
@@ -1302,6 +1276,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1366,6 +1341,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 	}
 
 	chan->err = false;
+	chan->idle = true;
 
 	return err;
 }
@@ -1447,6 +1423,7 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
 	if (status & XILINX_DMA_DMASR_FRM_CNT_IRQ) {
 		spin_lock(&chan->lock);
 		xilinx_dma_complete_descriptor(chan);
+		chan->idle = true;
 		chan->start_transfer(chan);
 		spin_unlock(&chan->lock);
 	}
@@ -2327,6 +2304,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	chan->has_sg = xdev->has_sg;
 	chan->desc_pendingcount = 0x0;
 	chan->ext_addr = xdev->ext_addr;
+	chan->idle = true;
 
 	spin_lock_init(&chan->lock);
 	INIT_LIST_HEAD(&chan->pending_list);
-- 
2.1.2

^ permalink raw reply related

* [PATCH v3 0/3] dmaengine: xilinx_dma: Bug fixes
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: robh+dt, mark.rutland, dan.j.williams, vinod.koul, michal.simek,
	soren.brinkmann, appanad, moritz.fischer, laurent.pinchart, luis,
	Jose.Abreu
  Cc: dmaengine, devicetree, linux-kernel, linux-arm-kernel

This patch series fixes below bugs in DMA and VDMA IP's
---> Do not start VDMA until frame buffer is processed by the h/w Fix 
---> bug in Multi frame sotres handling in VDMA Fix issues w.r.to multi 
---> frame descriptors submit with AXI DMA S2MM(recv) Side.

Kedareswara rao Appana (3):
  dmaengine: xilinx_dma: Check for channel idle state before submitting
    dma descriptor
  dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in
    vdma
  dmaengine: xilinx_dma: Fix race condition in the driver for multiple
    descriptor scenario

 .../devicetree/bindings/dma/xilinx/xilinx_dma.txt  |   2 +
 drivers/dma/xilinx/xilinx_dma.c                    | 265 ++++++++++++---------
 2 files changed, 156 insertions(+), 111 deletions(-)

-- 
2.1.2

^ permalink raw reply

* [PATCH v3 0/3] dmaengine: xilinx_dma: Bug fixes
From: Kedareswara rao Appana @ 2017-01-04  6:54 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series fixes below bugs in DMA and VDMA IP's
---> Do not start VDMA until frame buffer is processed by the h/w Fix 
---> bug in Multi frame sotres handling in VDMA Fix issues w.r.to multi 
---> frame descriptors submit with AXI DMA S2MM(recv) Side.

Kedareswara rao Appana (3):
  dmaengine: xilinx_dma: Check for channel idle state before submitting
    dma descriptor
  dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in
    vdma
  dmaengine: xilinx_dma: Fix race condition in the driver for multiple
    descriptor scenario

 .../devicetree/bindings/dma/xilinx/xilinx_dma.txt  |   2 +
 drivers/dma/xilinx/xilinx_dma.c                    | 265 ++++++++++++---------
 2 files changed, 156 insertions(+), 111 deletions(-)

-- 
2.1.2

^ permalink raw reply


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.