dm: demo: Add a simple GPIO demonstration

Add a new 'demo light' command which uses GPIOs to control imaginary lights.
Each light is assigned a bit number in the overall value. This provides an
example driver for using the new GPIO API.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/demo/demo-shape.c b/drivers/demo/demo-shape.c
index 3fa9c59..d908736 100644
--- a/drivers/demo/demo-shape.c
+++ b/drivers/demo/demo-shape.c
@@ -11,6 +11,7 @@
 #include <malloc.h>
 #include <dm-demo.h>
 #include <asm/io.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -20,6 +21,8 @@
 
 struct shape_data {
 	int num_chars;	/* Number of non-space characters output so far */
+	struct gpio_desc gpio_desc[8];
+	int gpio_count;
 };
 
 /* Crazy little function to draw shapes on the console */
@@ -89,9 +92,52 @@
 	return 0;
 }
 
+static int set_light(struct udevice *dev, int light)
+{
+	struct shape_data *priv = dev_get_priv(dev);
+	struct gpio_desc *desc;
+	int ret;
+	int i;
+
+	desc = priv->gpio_desc;
+	for (i = 0; i < priv->gpio_count; i++, desc++) {
+		uint mask = 1 << i;
+
+		ret = dm_gpio_set_value(desc, light & mask);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int get_light(struct udevice *dev)
+{
+	struct shape_data *priv = dev_get_priv(dev);
+	struct gpio_desc *desc;
+	uint value = 0;
+	int ret;
+	int i;
+
+	desc = priv->gpio_desc;
+	for (i = 0; i < priv->gpio_count; i++, desc++) {
+		uint mask = 1 << i;
+
+		ret = dm_gpio_get_value(desc);
+		if (ret < 0)
+			return ret;
+		if (ret)
+			value |= mask;
+	}
+
+	return value;
+}
+
 static const struct demo_ops shape_ops = {
 	.hello = shape_hello,
 	.status = shape_status,
+	.get_light = get_light,
+	.set_light = set_light,
 };
 
 static int shape_ofdata_to_platdata(struct udevice *dev)
@@ -111,6 +157,29 @@
 	return 0;
 }
 
+static int dm_shape_probe(struct udevice *dev)
+{
+	struct shape_data *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = gpio_request_list_by_name(dev, "light-gpios", priv->gpio_desc,
+					ARRAY_SIZE(priv->gpio_desc),
+					GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+	if (ret < 0)
+		return ret;
+	priv->gpio_count = ret;
+	debug("%s: %d GPIOs\n", __func__, priv->gpio_count);
+
+	return 0;
+}
+
+static int dm_shape_remove(struct udevice *dev)
+{
+	struct shape_data *priv = dev_get_priv(dev);
+
+	return gpio_free_list(dev, priv->gpio_desc, priv->gpio_count);
+}
+
 static const struct udevice_id demo_shape_id[] = {
 	{ "demo-shape", 0 },
 	{ },
@@ -122,6 +191,8 @@
 	.id	= UCLASS_DEMO,
 	.ofdata_to_platdata = shape_ofdata_to_platdata,
 	.ops	= &shape_ops,
+	.probe = dm_shape_probe,
+	.remove = dm_shape_remove,
 	.priv_auto_alloc_size = sizeof(struct shape_data),
 	.platdata_auto_alloc_size = sizeof(struct dm_demo_pdata),
 };