* [PATCH v5 4/9] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1480414245-14034-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../bindings/iio/multiplexer/iio-mux.txt | 51 ++++++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
diff --git a/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
new file mode 100644
index 000000000000..403912631dcf
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
@@ -0,0 +1,51 @@
+IIO multiplexer bindings
+
+If a multiplexer is used to select which hardware signal is fed to
+e.g. an ADC channel, these bindings describe that situation.
+
+Required properties:
+- compatible : "iio-mux"
+- io-channels : Channel node of the parent channel that has multiplexed
+ input.
+- io-channel-names : Should be "parent".
+- #address-cells = <1>;
+- #size-cells = <0>;
+- mux-controls : Mux controller node to use for operating the mux
+
+Required properties for iio-mux child nodes:
+- reg : The multiplexer state as described in ../misc/mux-controller.txt
+
+For each iio-mux child, an iio channel will be created whose number will
+match the mux controller state.
+
+Example:
+ mux: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mux-controls = <&mux>;
+
+ sync@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ system-regulator@2 {
+ reg = <2>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index dc7498682752..77045ae15865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6234,6 +6234,12 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.c
+IIO MULTIPLEXER
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de>
--
2.1.4
^ permalink raw reply related
* [PATCH v5 3/9] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1480414245-14034-1-git-send-email-peda@axentia.se>
Extend the inkern api with functions for reading and writing ext_info
of iio channels.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
drivers/iio/inkern.c | 60 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++
2 files changed, 97 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b0f4630a163f..4848b8129e6c 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -863,3 +863,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+ unsigned int i = 0;
+
+ if (!chan->channel->ext_info)
+ return i;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
+ ++i;
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
+
+static const struct iio_chan_spec_ext_info *iio_lookup_ext_info(
+ const struct iio_channel *chan,
+ const char *attr)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (!chan->channel->ext_info)
+ return NULL;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
+ if (!strcmp(attr, ext_info->name))
+ return ext_info;
+ }
+
+ return NULL;
+}
+
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->read(chan->indio_dev, ext_info->private,
+ chan->channel, buf);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
+
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ ext_info = iio_lookup_ext_info(chan, attr);
+ if (!ext_info)
+ return -EINVAL;
+
+ return ext_info->write(chan->indio_dev, ext_info->private,
+ chan->channel, buf, len);
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 47eeec3218b5..5e347a9805fd 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -312,4 +312,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale);
+/**
+ * iio_get_channel_ext_info_count() - get number of ext_info attributes
+ * connected to the channel.
+ * @chan: The channel being queried
+ *
+ * Returns the number of ext_info attributes
+ */
+unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan);
+
+/**
+ * iio_read_channel_ext_info() - read ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: Where to store the attribute value. Assumed to hold
+ * at least PAGE_SIZE bytes.
+ *
+ * Returns the number of bytes written to buf (perhaps w/o zero termination;
+ * it need not even be a string), or an error code.
+ */
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf);
+
+/**
+ * iio_write_channel_ext_info() - write ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: The new attribute value. Strings needs to be zero-
+ * terminated, but the terminator should not be included
+ * in the below len.
+ * @len: The size of the new attribute value.
+ *
+ * Returns the number of accepted bytes, which should be the same as len.
+ * An error code can also be returned.
+ */
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len);
+
#endif
--
2.1.4
^ permalink raw reply related
* [PATCH v5 2/9] misc: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480414245-14034-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Add a new minimalistic subsystem that handles multiplexer controllers.
When multiplexers are used in various places in the kernel, and the
same multiplexer controller can be used for several independent things,
there should be one place to implement support for said multiplexer
controller.
A single multiplexer controller can also be used to control several
parallel multiplexers, that are in turn used by different subsystems
in the kernel, leading to a need to coordinate multiplexer accesses.
The multiplexer subsystem handles this coordination.
This new mux controller subsystem initially comes with a single backend
driver that controls gpio based multiplexers. Even though not needed by
this initial driver, the mux controller subsystem is prepared to handle
chips with multiple (independent) mux controllers.
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 2 +
drivers/misc/Kconfig | 30 ++++
drivers/misc/Makefile | 2 +
drivers/misc/mux-core.c | 311 ++++++++++++++++++++++++++++++++++
drivers/misc/mux-gpio.c | 138 +++++++++++++++
include/linux/mux.h | 197 +++++++++++++++++++++
7 files changed, 685 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index ca9d1eb46bc0..d64ede85b61b 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -330,7 +330,11 @@ MEM
devm_kzalloc()
MFD
- devm_mfd_add_devices()
+ devm_mfd_add_devices()
+
+MUX
+ devm_mux_control_get()
+ devm_mux_control_put()
PER-CPU MEM
devm_alloc_percpu()
diff --git a/MAINTAINERS b/MAINTAINERS
index 3d4d0efc2b64..dc7498682752 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8407,6 +8407,8 @@ MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
S: Maintained
F: Documentation/devicetree/bindings/misc/mux-*
+F: include/linux/mux.h
+F: drivers/misc/mux-*
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv-Jdbf3xiKgS8@public.gmane.org>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971baf11fa..2ce675e410c5 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,36 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
+menuconfig MULTIPLEXER
+ bool "Multiplexer subsystem"
+ help
+ Multiplexer controller subsystem. Multiplexers are used in a
+ variety of settings, and this subsystem abstracts their use
+ so that the rest of the kernel sees a common interface. When
+ multiple parallel multiplexers are controlled by one single
+ multiplexer controller, this subsystem also coordinates the
+ multiplexer accesses.
+
+ If unsure, say no.
+
+if MULTIPLEXER
+
+config MUX_GPIO
+ tristate "GPIO-controlled Multiplexer"
+ depends on OF && GPIOLIB
+ help
+ GPIO-controlled Multiplexer controller.
+
+ The driver builds a single multiplexer controller using a number
+ of gpio pins. For N pins, there will be 2^N possible multiplexer
+ states. The GPIO pins can be connected (by the hardware) to several
+ multiplexers, which in that case will be operated in parallel.
+
+ To compile this driver as a module, choose M here: the module will
+ be called mux-gpio.
+
+endif
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 31983366090a..0befa2bba762 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_MULTIPLEXER) += mux-core.o
+obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
new file mode 100644
index 000000000000..cccaa7261a6e
--- /dev/null
+++ b/drivers/misc/mux-core.c
@@ -0,0 +1,311 @@
+/*
+ * Multiplexer subsystem
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "mux-core: " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+static struct class mux_class = {
+ .name = "mux",
+ .owner = THIS_MODULE,
+};
+
+static int __init mux_init(void)
+{
+ return class_register(&mux_class);
+}
+
+static DEFINE_IDA(mux_ida);
+
+static void mux_chip_release(struct device *dev)
+{
+ struct mux_chip *mux_chip = to_mux_chip(dev);
+
+ ida_simple_remove(&mux_ida, mux_chip->id);
+ kfree(mux_chip);
+}
+
+static struct device_type mux_type = {
+ .name = "mux-chip",
+ .release = mux_chip_release,
+};
+
+struct mux_chip *mux_chip_alloc(struct device *dev,
+ unsigned int controllers, size_t sizeof_priv)
+{
+ struct mux_chip *mux_chip;
+ int i;
+
+ if (!dev || !controllers)
+ return NULL;
+
+ mux_chip = kzalloc(sizeof(*mux_chip) +
+ controllers * sizeof(*mux_chip->mux) +
+ sizeof_priv, GFP_KERNEL);
+ if (!mux_chip)
+ return NULL;
+
+ mux_chip->mux = (struct mux_control *)(mux_chip + 1);
+ mux_chip->dev.class = &mux_class;
+ mux_chip->dev.type = &mux_type;
+ mux_chip->dev.parent = dev;
+ mux_chip->dev.of_node = dev->of_node;
+ dev_set_drvdata(&mux_chip->dev, mux_chip);
+
+ mux_chip->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
+ if (mux_chip->id < 0) {
+ pr_err("muxchipX failed to get a device id\n");
+ kfree(mux_chip);
+ return NULL;
+ }
+ dev_set_name(&mux_chip->dev, "muxchip%d", mux_chip->id);
+
+ mux_chip->controllers = controllers;
+ for (i = 0; i < controllers; ++i) {
+ struct mux_control *mux = &mux_chip->mux[i];
+
+ mux->chip = mux_chip;
+ init_rwsem(&mux->lock);
+ mux->cached_state = -1;
+ mux->idle_state = -1;
+ }
+
+ device_initialize(&mux_chip->dev);
+
+ return mux_chip;
+}
+EXPORT_SYMBOL_GPL(mux_chip_alloc);
+
+static int mux_control_set(struct mux_control *mux, int state)
+{
+ int ret = mux->chip->ops->set(mux, state);
+
+ mux->cached_state = ret < 0 ? -1 : state;
+
+ return ret;
+}
+
+int mux_chip_register(struct mux_chip *mux_chip)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < mux_chip->controllers; ++i) {
+ struct mux_control *mux = &mux_chip->mux[i];
+
+ if (mux->idle_state == mux->cached_state)
+ continue;
+
+ ret = mux_control_set(mux, mux->idle_state);
+ if (ret < 0)
+ return ret;
+ }
+
+ return device_add(&mux_chip->dev);
+}
+EXPORT_SYMBOL_GPL(mux_chip_register);
+
+void mux_chip_unregister(struct mux_chip *mux_chip)
+{
+ device_del(&mux_chip->dev);
+}
+EXPORT_SYMBOL_GPL(mux_chip_unregister);
+
+void mux_chip_free(struct mux_chip *mux_chip)
+{
+ if (!mux_chip)
+ return;
+ put_device(&mux_chip->dev);
+}
+EXPORT_SYMBOL_GPL(mux_chip_free);
+
+int mux_control_select(struct mux_control *mux, int state)
+{
+ int ret;
+
+ if (down_read_trylock(&mux->lock)) {
+ if (mux->cached_state == state)
+ return 0;
+
+ /* Sigh, the mux needs updating... */
+ up_read(&mux->lock);
+ }
+
+ /* ...or it's just contended. */
+ down_write(&mux->lock);
+
+ if (mux->cached_state == state) {
+ /*
+ * Hmmm, someone else changed the mux to my liking.
+ * That makes me wonder how long I waited for nothing?
+ */
+ downgrade_write(&mux->lock);
+ return 0;
+ }
+
+ ret = mux_control_set(mux, state);
+ if (ret < 0) {
+ if (mux->idle_state != -1)
+ mux_control_set(mux, mux->idle_state);
+
+ up_write(&mux->lock);
+ return ret;
+ }
+
+ downgrade_write(&mux->lock);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mux_control_select);
+
+int mux_control_deselect(struct mux_control *mux)
+{
+ int ret = 0;
+
+ if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
+ ret = mux_control_set(mux, mux->idle_state);
+
+ up_read(&mux->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mux_control_deselect);
+
+static int of_dev_node_match(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
+
+static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = class_find_device(&mux_class, NULL, np, of_dev_node_match);
+
+ return dev ? to_mux_chip(dev) : NULL;
+}
+
+struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
+{
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args args;
+ struct mux_chip *mux_chip;
+ unsigned int controller;
+ int index = 0;
+ int ret;
+
+ if (mux_name) {
+ index = of_property_match_string(np, "mux-control-names",
+ mux_name);
+ if (index < 0)
+ return ERR_PTR(index);
+ }
+
+ ret = of_parse_phandle_with_args(np,
+ "mux-controls", "#mux-control-cells",
+ index, &args);
+ if (ret) {
+ dev_err(dev, "%s: failed to get mux-control %s(%i)\n",
+ np->full_name, mux_name ?: "", index);
+ return ERR_PTR(ret);
+ }
+
+ mux_chip = of_find_mux_chip_by_node(args.np);
+ of_node_put(args.np);
+ if (!mux_chip)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ if (args.args_count > 1 ||
+ (!args.args_count && (mux_chip->controllers > 1))) {
+ dev_err(dev, "%s: wrong #mux-control-cells for %s\n",
+ np->full_name, args.np->full_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ controller = 0;
+ if (args.args_count)
+ controller = args.args[0];
+
+ if (controller >= mux_chip->controllers)
+ return ERR_PTR(-EINVAL);
+
+ get_device(&mux_chip->dev);
+ return &mux_chip->mux[controller];
+}
+EXPORT_SYMBOL_GPL(mux_control_get);
+
+void mux_control_put(struct mux_control *mux)
+{
+ put_device(&mux->chip->dev);
+}
+EXPORT_SYMBOL_GPL(mux_control_put);
+
+static void devm_mux_control_release(struct device *dev, void *res)
+{
+ struct mux_control *mux = *(struct mux_control **)res;
+
+ mux_control_put(mux);
+}
+
+struct mux_control *devm_mux_control_get(struct device *dev,
+ const char *mux_name)
+{
+ struct mux_control **ptr, *mux;
+
+ ptr = devres_alloc(devm_mux_control_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ mux = mux_control_get(dev, mux_name);
+ if (IS_ERR(mux)) {
+ devres_free(ptr);
+ return mux;
+ }
+
+ *ptr = mux;
+ devres_add(dev, ptr);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(devm_mux_control_get);
+
+static int devm_mux_control_match(struct device *dev, void *res, void *data)
+{
+ struct mux_control **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+void devm_mux_control_put(struct device *dev, struct mux_control *mux)
+{
+ WARN_ON(devres_release(dev, devm_mux_control_release,
+ devm_mux_control_match, mux));
+}
+EXPORT_SYMBOL_GPL(devm_mux_control_put);
+
+subsys_initcall(mux_init);
+
+MODULE_DESCRIPTION("Multiplexer subsystem");
+MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
new file mode 100644
index 000000000000..b50d6b871895
--- /dev/null
+++ b/drivers/misc/mux-gpio.c
@@ -0,0 +1,138 @@
+/*
+ * GPIO-controlled multiplexer driver
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct mux_gpio {
+ struct gpio_descs *gpios;
+ int *val;
+};
+
+static int mux_gpio_set(struct mux_control *mux, int state)
+{
+ struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
+ int i;
+
+ for (i = 0; i < mux_gpio->gpios->ndescs; i++)
+ mux_gpio->val[i] = (state >> i) & 1;
+
+ gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
+ mux_gpio->gpios->desc,
+ mux_gpio->val);
+
+ return 0;
+}
+
+static const struct mux_control_ops mux_gpio_ops = {
+ .set = mux_gpio_set,
+};
+
+static const struct of_device_id mux_gpio_dt_ids[] = {
+ { .compatible = "mux-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
+
+static int mux_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct mux_chip *mux_chip;
+ struct mux_gpio *mux_gpio;
+ int pins;
+ u32 idle_state;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ pins = gpiod_count(dev, "mux");
+ if (pins < 0)
+ return pins;
+
+ mux_chip = mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
+ pins * sizeof(*mux_gpio->val));
+ if (!mux_chip)
+ return -ENOMEM;
+
+ mux_gpio = mux_chip_priv(mux_chip);
+ mux_gpio->val = (int *)(mux_gpio + 1);
+ mux_chip->ops = &mux_gpio_ops;
+
+ platform_set_drvdata(pdev, mux_chip);
+
+ mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
+ if (IS_ERR(mux_gpio->gpios)) {
+ ret = PTR_ERR(mux_gpio->gpios);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to get gpios\n");
+ goto free_mux_chip;
+ }
+ WARN_ON(pins != mux_gpio->gpios->ndescs);
+ mux_chip->mux->states = 1 << pins;
+
+ ret = of_property_read_u32(np, "idle-state", &idle_state);
+ if (ret >= 0) {
+ if (idle_state >= mux_chip->mux->states) {
+ dev_err(dev, "invalid idle-state %u\n", idle_state);
+ ret = -EINVAL;
+ goto free_mux_chip;
+ }
+
+ mux_chip->mux->idle_state = idle_state;
+ }
+
+ ret = mux_chip_register(mux_chip);
+ if (ret < 0) {
+ dev_err(dev, "failed to register mux-chip\n");
+ goto free_mux_chip;
+ }
+
+ dev_info(dev, "%u-way mux-controller registered\n",
+ mux_chip->mux->states);
+
+ return 0;
+
+free_mux_chip:
+ mux_chip_free(mux_chip);
+ return ret;
+}
+
+static int mux_gpio_remove(struct platform_device *pdev)
+{
+ struct mux_chip *mux_chip = to_mux_chip(&pdev->dev);
+
+ mux_chip_unregister(mux_chip);
+ mux_chip_free(mux_chip);
+
+ return 0;
+}
+
+static struct platform_driver mux_gpio_driver = {
+ .driver = {
+ .name = "mux-gpio",
+ .of_match_table = of_match_ptr(mux_gpio_dt_ids),
+ },
+ .probe = mux_gpio_probe,
+ .remove = mux_gpio_remove,
+};
+module_platform_driver(mux_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
+MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mux.h b/include/linux/mux.h
new file mode 100644
index 000000000000..d52a651660ff
--- /dev/null
+++ b/include/linux/mux.h
@@ -0,0 +1,197 @@
+/*
+ * mux.h - definitions for the multiplexer interface
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_MUX_H
+#define _LINUX_MUX_H
+
+#include <linux/device.h>
+#include <linux/rwsem.h>
+
+struct mux_chip;
+struct mux_control;
+struct platform_device;
+
+struct mux_control_ops {
+ int (*set)(struct mux_control *mux, int state);
+};
+
+/**
+ * struct mux_control - Represents a mux controller.
+ * @lock: Protects the mux controller state.
+ * @chip: The mux chip that is handling this mux controller.
+ * @states: The number of mux controller states.
+ * @cached_state: The current mux controller state, or -1 if none.
+ * @idle_state: The mux controller state to use when inactive, or -1
+ * for none.
+ */
+struct mux_control {
+ struct rw_semaphore lock; /* protects the state of the mux */
+
+ struct mux_chip *chip;
+
+ unsigned int states;
+ int cached_state;
+ int idle_state;
+};
+
+/**
+ * struct mux_chip - Represents a chip holding mux controllers.
+ * @controllers: Number of mux controllers handled by the chip.
+ * @mux: Array of mux controllers that is handled.
+ * @dev: Device structure.
+ * @id: Used to identify the device internally.
+ * @ops: Mux controller operations.
+ */
+struct mux_chip {
+ unsigned int controllers;
+ struct mux_control *mux;
+ struct device dev;
+ int id;
+
+ const struct mux_control_ops *ops;
+};
+
+#define to_mux_chip(x) container_of((x), struct mux_chip, dev)
+
+/**
+ * mux_chip_priv() - Get the extra memory reserved by mux_chip_alloc().
+ * @mux_chip: The mux-chip to get the private memory from.
+ *
+ * Return: Pointer to the private memory reserved by the allocator.
+ */
+static inline void *mux_chip_priv(struct mux_chip *mux_chip)
+{
+ return mux_chip + 1;
+}
+
+/**
+ * mux_chip_alloc() - Allocate a mux-chip.
+ * @dev: The parent device implementing the mux interface.
+ * @controllers: The number of mux controllers to allocate for this chip.
+ * @sizeof_priv: Size of extra memory area for private use by the caller.
+ *
+ * Return: A pointer to the new mux-chip, NULL on failure.
+ */
+struct mux_chip *mux_chip_alloc(struct device *dev,
+ unsigned int controllers, size_t sizeof_priv);
+
+/**
+ * mux_chip_register() - Register a mux-chip, thus readying the controllers
+ * for use.
+ * @mux_chip: The mux-chip to register.
+ *
+ * Do not retry registration of the same mux-chip on failure. You should
+ * instead put it away with mux_chip_free() and allocate a new one, if you
+ * for some reason would like to retry registration.
+ *
+ * Return: Zero on success or a negative errno on error.
+ */
+int mux_chip_register(struct mux_chip *mux_chip);
+
+/**
+ * mux_chip_unregister() - Take the mux-chip off-line.
+ * @mux_chip: The mux-chip to unregister.
+ *
+ * mux_chip_unregister() reverses the effects of mux_chip_register().
+ * But not completely, you should not try to call mux_chip_register()
+ * on a mux-chip that has been registered before.
+ */
+void mux_chip_unregister(struct mux_chip *mux_chip);
+
+/**
+ * mux_chip_free() - Free the mux-chip for good.
+ * @mux_chip: The mux-chip to free.
+ *
+ * mux_chip_free() reverses the effects of mux_chip_alloc().
+ */
+void mux_chip_free(struct mux_chip *mux_chip);
+
+/**
+ * mux_control_select() - Select the given multiplexer state.
+ * @mux: The mux-control to request a change of state from.
+ * @state: The new requested state.
+ *
+ * Make sure to call mux_control_deselect() when the operation is complete and
+ * the mux-control is free for others to use, but do not call
+ * mux_control_deselect() if mux_control_select() fails.
+ *
+ * Return: 0 if the requested state was already active, or 1 it the
+ * mux-control state was changed to the requested state. Or a negavive
+ * errno on error.
+ *
+ * Note that the difference in return value of zero or one is of
+ * questionable value; especially if the mux-control has several independent
+ * consumers, which is something the consumers should perhaps not be making
+ * assumptions about.
+ */
+int mux_control_select(struct mux_control *mux, int state);
+
+/**
+ * mux_control_deselect() - Deselect the previously selected multiplexer state.
+ * @mux: The mux-control to deselect.
+ *
+ * Return: 0 on success and a negative errno on error. An error can only
+ * occur if the mux has an idle state. Note that even if an error occurs, the
+ * mux-control is unlocked for others to access.
+ */
+int mux_control_deselect(struct mux_control *mux);
+
+/**
+ * mux_control_get_index() - Get the index of the given mux controller
+ * @mux: The mux-control to the the index for.
+ *
+ * Return: The index of the mux controller within the mux chip the mux
+ * controller is a part of.
+ */
+static inline unsigned int mux_control_get_index(struct mux_control *mux)
+{
+ return mux - mux->chip->mux;
+}
+
+/**
+ * mux_control_get() - Get the mux-control for a device.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
+
+/**
+ * mux_control_put() - Put away the mux-control for good.
+ * @mux: The mux-control to put away.
+ *
+ * mux_control_put() reverses the effects of mux_control_get().
+ */
+void mux_control_put(struct mux_control *mux);
+
+/**
+ * devm_mux_control_get() - Get the mux-control for a device, with resource
+ * management.
+ * @dev: The device that needs a mux-control.
+ * @mux_name: The name identifying the mux-control.
+ *
+ * Return: Pointer to the mux-control, or an ERR_PTR with a negative errno.
+ */
+struct mux_control *devm_mux_control_get(struct device *dev,
+ const char *mux_name);
+
+/**
+ * devm_mux_control_put() - Resource-managed version mux_control_put().
+ * @dev: The device that originally got the mux-control.
+ * @mux: The mux-control to put away.
+ *
+ * Note that you do not normally need to call this function.
+ */
+void devm_mux_control_put(struct device *dev, struct mux_control *mux);
+
+#endif /* _LINUX_MUX_H */
--
2.1.4
--
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 related
* [PATCH v5 1/9] dt-bindings: document devicetree bindings for mux-controllers and mux-gpio
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1480414245-14034-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/misc/mux-controller.txt | 153 +++++++++++++++++++++
.../devicetree/bindings/misc/mux-gpio.txt | 85 ++++++++++++
MAINTAINERS | 5 +
3 files changed, 243 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
diff --git a/Documentation/devicetree/bindings/misc/mux-controller.txt b/Documentation/devicetree/bindings/misc/mux-controller.txt
new file mode 100644
index 000000000000..7957b8bd1278
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-controller.txt
@@ -0,0 +1,153 @@
+Common multiplexer controller bindings
+======================================
+
+A multiplexer (or mux) controller will have one, or several, consumer devices
+that uses the mux controller. Thus, a mux controller can possibly control
+several parallel multiplexers, presumably there will be at least one
+multiplexer needed by each consumer..
+
+A mux controller provides a number of states to its consumers, and the state
+space is a simple zero-based enumeration. I.e. 0-1 for a 2-way multiplexer,
+0-7 for an 8-way multiplexer, etc.
+
+
+Consumers
+---------
+
+Mux controller consumers should specify a list of mux controllers that they
+want to use with a property containing a 'mux-ctrl-list':
+
+ mux-ctrl-list ::= <single-mux-ctrl> [mux-ctrl-list]
+ single-mux-ctrl ::= <mux-ctrl-phandle> [mux-ctrl-specifier]
+ mux-ctrl-phandle : phandle to mux controller node
+ mux-ctrl-specifier : array of #mux-control-cells specifying the
+ given mux controller (controller specific)
+
+Mux controller properties should be named "mux-controls". The exact meaning of
+each mux controller property must be documented in the device tree binding for
+each consumer. An optional property "mux-control-names" may contain a list of
+strings to label each of the mux controllers listed in the "mux-controls"
+property.
+
+Drivers for devices that use more than a single mux controller can use the
+"mux-control-names" property to map the name of the mux controller requested by
+the mux_control_get() call to an index into the list given by the
+"mux-controls" property.
+
+mux-ctrl-specifier typically encodes the chip-relative mux controller number.
+If the mux controller chip only provides a single mux controller, the
+mux-ctrl-specifier can typically be left out.
+
+Example:
+
+ /* One consumer of a 2-way mux controller (one GPIO-line) */
+ mux: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+ mux-controls = <&mux>;
+ mux-control-names = "adc";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+ };
+
+Note that in the example above, specifying the "mux-control-names" is redundant
+because there is only one mux controller in the list.
+
+ /*
+ * Two consumers (one for an ADC line and one for an i2c bus) of
+ * parallel 4-way multiplexers controlled by the same two GPIO-lines.
+ */
+ mux: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ out@2 {
+ reg = <2>;
+ };
+
+ sync-2@3 {
+ reg = <3>;
+ };
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux-simple,mux-locked";
+ i2c-parent = <&i2c1>;
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ /* ... */
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ /* ... */
+ };
+ };
+ };
+
+
+Mux controller nodes
+--------------------
+
+Mux controller nodes must specify the number of cells used for the
+specifier using the '#mux-control-cells' property.
+
+An example mux controller might look like this:
+
+ mux: adg792a@50 {
+ compatible = "adi,adg792a";
+ reg = <0x50>;
+ #mux-control-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/misc/mux-gpio.txt b/Documentation/devicetree/bindings/misc/mux-gpio.txt
new file mode 100644
index 000000000000..6736f8215593
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-gpio.txt
@@ -0,0 +1,85 @@
+GPIO-based multiplexer controller bindings
+
+Define what GPIO pins are used to control a multiplexer. Or several
+multiplexers, if the same pins control more than one multiplexer.
+
+Required properties:
+- compatible : "mux-gpio"
+- mux-gpios : list of gpios used to control the multiplexer, least
+ significant bit first.
+- #mux-control-cells : <0>
+* Standard mux-controller bindings as decribed in mux-controller.txt
+
+Optional properties:
+- idle-state : if present, the state the mux will have when idle.
+
+The multiplexer state is defined as the number represented by the
+multiplexer GPIO pins, where the first pin is the least significant
+bit. An active pin is a binary 1, an inactive pin is a binary 0.
+
+Example:
+
+ mux: mux-controller {
+ compatible = "mux-gpio";
+ #mux-control-cells = <0>;
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ adc-mux {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ out@2 {
+ reg = <2>;
+ };
+
+ sync-2@3 {
+ reg = <3>;
+ };
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux-simple,mux-locked";
+ i2c-parent = <&i2c1>;
+
+ mux-controls = <&mux>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ /* ... */
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ /* ... */
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index d8eb3843dbd4..3d4d0efc2b64 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8403,6 +8403,11 @@ S: Orphan
F: drivers/mmc/host/mmc_spi.c
F: include/linux/spi/mmc_spi.h
+MULTIPLEXER SUBSYSTEM
+M: Peter Rosin <peda@axentia.se>
+S: Maintained
+F: Documentation/devicetree/bindings/misc/mux-*
+
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
S: Maintained
--
2.1.4
^ permalink raw reply related
* [PATCH v5 0/9] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2016-11-29 10:10 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA
Hi!
v4 -> v5 changes
- remove support for fancier dt layouts and go back to the phandle
approach from v2 and before, killing the horrible non-working
refcounting crap from v4 and avoiding a bunch of life-time issues
in v3.
- introduce the concept of a mux-chip, that can hold one or more
mux-controllers (inspired by the pwm subsystem).
- add dt #mux-control-cells property needed to get to the desired
mux controller if a mux chip provides more than one.
- take away the option to build the mux-core as a module.
- if the mux controller has an idle state, make sure the mux controller
is set up in the idle state initially (when it should be idle).
- do not use a variable length array on the stack in mux_gpio_set to
temporarily store the gpio state, preallocate space instead.
- fix resource leak on one failure path in mux_gpio_probe.
- driver for Analog Devices ADG792A/G, literally the first mux chip
I found on the Internet with an i2c interface (that was not a
dedicated i2c multiplexer like PCA9547) which I used to verify
that the abstractions in the mux core are up to the task. Untested,
just proof of concept that at least looks pretty and compiles...
- various touch-ups.
v3 -> v4 changes
- rebased onto next-20161122 (depends on recent _available iio changes).
- added support for having the mux-controller in a child node of a
mux-consumer if it is a sole consumer, to hopefully even further satisfy
the complaint from Rob (and later Lars-Peter) about dt complexity.
- the above came at the cost of some rather horrible refcounting code,
please review and suggest how it should be done...
- changed to register a device class instead of a bus.
- pass in the parent device into mux_control_alloc and require less
work from mux-control drivers.
- changed device names from mux:control%d to mux%d
- move kernel-doc from mux-core.c to mux.h (and add some bits).
- give the gpio driver a chance to update all mux pins at once.
- factor out iio ext_info lookup into new helper function. /Lars-Peter
- use an unsigned type for the iio ext_info count. /Lars-Peter
- unified "brag strings" in the file headers.
v2 -> v3 changes
- have the mux-controller in the parent node of any mux-controller consumer,
to hopefully satisfy complaint from Rob about dt complexity.
- improve commit message of the mux subsystem commit, making it more
general, as requested by Jonathan.
- remove priv member from struct mux_control and calculate it on the
fly. /Jonathan
- make the function comments in mux-core.c kernel doc. /Jonathan
- add devm_mux_control_* to Documentation/driver.model/devres.txt. /Jonathan
- add common dt bindings for mux-controllers, refer to them from the
mux-gpio bindings. /Rob
- clarify how the gpio pins map to the mux state. /Rob
- separate CONFIG_ variables for the mux core and the mux gpio driver.
- improve Kconfig help texts.
- make CONFIG_MUX_GPIO depend on CONFIG_GPIOLIB.
- keep track of the number of mux states in the mux core.
- since the iio channel number is used as mux state, it was possible
to drop the state member from the mux_child struct.
- cleanup dt bindings for i2c-mux-simple, it had some of copy-paste
problems from ots origin (i2c-mux-gpio).
- select the mux control subsystem in config for the i2c-mux-simple driver.
- add entries to MAINTAINERS and my sign-off, I'm now satisfied and know
nothing in this to be ashamed of.
v1 -> v2 changes
- fixup export of mux_control_put reported by kbuild
- drop devicetree iio-ext-info property as noted by Lars-Peter,
and replace the functionality by exposing all ext_info
attributes of the parent channel for each of the muxed
channels. A cache on top of that and each muxed channel
gets its own view of the ext_info of the parent channel.
- implement idle-state for muxes
- clear out the cache on failure in order to force a mux
update on the following use
- cleanup the probe of i2c-mux-simple driver
- fix a bug in the i2c-mux-simple driver, where failure in
the selection of the mux caused a deadlock when the mux
was later unconditionally deselected.
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
One thing that I would like to do, but don't see a solution
for, is to move the mux control code that is present in
various drivers in drivers/i2c/muxes to this new minimalistic
muxing subsystem, thus converting all present i2c muxes (but
perhaps not gates and arbitrators) to be i2c-mux-simple muxes.
I'm using an rwsem to lock a mux, but that isn't really a
perfect fit. Is there a better locking primitive that I don't
know about that fits better? I had a mutex at one point, but
that didn't allow any concurrent accesses at all. At least
the rwsem allows concurrent access as long as all users
agree on the mux state, but I suspect that the rwsem will
degrade to the mutex situation pretty quickly if there is
any contention.
Also, the "mux" name feels a bit ambitious, there are many muxes
in the world, and this tiny bit of code is probably not good
enough to be a nice fit for all...
Cheers,
Peter
Peter Rosin (9):
dt-bindings: document devicetree bindings for mux-controllers and
mux-gpio
misc: minimal mux subsystem and gpio-based mux controller
iio: inkern: api for manipulating ext_info of iio channels
dt-bindings: iio: iio-mux: document iio-mux bindings
iio: multiplexer: new iio category and iio-mux driver
dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
i2c: i2c-mux-simple: new driver
dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G
mux
misc: mux-adg792a: add mux controller driver for ADG792A/G
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 81 ++++
.../bindings/iio/multiplexer/iio-mux.txt | 51 +++
.../devicetree/bindings/misc/mux-adg792a.txt | 82 ++++
.../devicetree/bindings/misc/mux-controller.txt | 153 +++++++
.../devicetree/bindings/misc/mux-gpio.txt | 85 ++++
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 14 +
drivers/i2c/muxes/Kconfig | 13 +
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 179 ++++++++
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/inkern.c | 60 +++
drivers/iio/multiplexer/Kconfig | 18 +
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 457 +++++++++++++++++++++
drivers/misc/Kconfig | 42 ++
drivers/misc/Makefile | 3 +
drivers/misc/mux-adg792a.c | 154 +++++++
drivers/misc/mux-core.c | 311 ++++++++++++++
drivers/misc/mux-gpio.c | 138 +++++++
include/linux/iio/consumer.h | 37 ++
include/linux/mux.h | 197 +++++++++
23 files changed, 2089 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-adg792a.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
create mode 100644 drivers/misc/mux-adg792a.c
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
--
2.1.4
--
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 v7 5/8] clk: sunxi-ng: define the PLL DE clock
From: Jean-Francois Moine @ 2016-11-29 10:10 UTC (permalink / raw)
To: Dave Airlie, Maxime Ripard, Rob Herring
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <cover.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
include/dt-bindings/clock/sun8i-h3-ccu.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/dt-bindings/clock/sun8i-h3-ccu.h b/include/dt-bindings/clock/sun8i-h3-ccu.h
index efb7ba2..7af57b7 100644
--- a/include/dt-bindings/clock/sun8i-h3-ccu.h
+++ b/include/dt-bindings/clock/sun8i-h3-ccu.h
@@ -44,6 +44,7 @@
#define _DT_BINDINGS_CLK_SUN8I_H3_H_
#define CLK_CPUX 14
+#define CLK_PLL_DE 13
#define CLK_BUS_CE 20
#define CLK_BUS_DMA 21
--
2.10.2
^ permalink raw reply related
* Re: [PATCH v2 07/13] net: ethernet: ti: cpts: rework initialization/deinitialization
From: Richard Cochran @ 2016-11-29 10:07 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N,
Sekhar Nori, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Murali Karicheri, Wingman Kwok
In-Reply-To: <20161128230337.6731-8-grygorii.strashko-l0cyMroinI0@public.gmane.org>
On Mon, Nov 28, 2016 at 05:03:31PM -0600, Grygorii Strashko wrote:
> +int cpts_register(struct cpts *cpts)
> {
> int err, i;
>
> - cpts->info = cpts_info;
> - spin_lock_init(&cpts->lock);
> -
> - cpts->cc.read = cpts_systim_read;
> - cpts->cc.mask = CLOCKSOURCE_MASK(32);
> - cpts->cc_mult = mult;
> - cpts->cc.mult = mult;
> - cpts->cc.shift = shift;
> -
> INIT_LIST_HEAD(&cpts->events);
> INIT_LIST_HEAD(&cpts->pool);
> for (i = 0; i < CPTS_MAX_EVENTS; i++)
> list_add(&cpts->pool_data[i].list, &cpts->pool);
>
> - cpts_clk_init(dev, cpts);
> + clk_enable(cpts->refclk);
> +
> cpts_write32(cpts, CPTS_EN, control);
> cpts_write32(cpts, TS_PEND_EN, int_enable);
>
> + cpts->cc.mult = cpts->cc_mult;
It is not clear why you set cc.mult in a different place than
cc.shift. That isn't logical, but maybe later patches make it
clear...
> timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real()));
>
> - INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check);
> -
> - cpts->clock = ptp_clock_register(&cpts->info, dev);
> + cpts->clock = ptp_clock_register(&cpts->info, cpts->dev);
> if (IS_ERR(cpts->clock)) {
> err = PTR_ERR(cpts->clock);
> cpts->clock = NULL;
> @@ -392,26 +364,74 @@ int cpts_register(struct device *dev, struct cpts *cpts,
> return 0;
>
> err_ptp:
> - if (cpts->refclk)
> - cpts_clk_release(cpts);
> + clk_disable(cpts->refclk);
> return err;
> }
> EXPORT_SYMBOL_GPL(cpts_register);
>
> void cpts_unregister(struct cpts *cpts)
> {
> - if (cpts->clock) {
> - ptp_clock_unregister(cpts->clock);
> - cancel_delayed_work_sync(&cpts->overflow_work);
> - }
> + if (WARN_ON(!cpts->clock))
> + return;
> +
> + cancel_delayed_work_sync(&cpts->overflow_work);
> +
> + ptp_clock_unregister(cpts->clock);
> + cpts->clock = NULL;
>
> cpts_write32(cpts, 0, int_enable);
> cpts_write32(cpts, 0, control);
>
> - if (cpts->refclk)
> - cpts_clk_release(cpts);
> + clk_disable(cpts->refclk);
> }
> EXPORT_SYMBOL_GPL(cpts_unregister);
>
> +struct cpts *cpts_create(struct device *dev, void __iomem *regs,
> + u32 mult, u32 shift)
> +{
> + struct cpts *cpts;
> +
> + if (!regs || !dev)
> + return ERR_PTR(-EINVAL);
There is no need for this test, as the caller will always pass valid
pointers. (This isn't a user space library!)
Thanks,
Richard
--
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
* Re: [PATCH v2 06/13] net: ethernet: ti: cpts: disable cpts when unregistered
From: Richard Cochran @ 2016-11-29 9:49 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N,
Sekhar Nori, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Murali Karicheri, Wingman Kwok
In-Reply-To: <20161128230337.6731-7-grygorii.strashko-l0cyMroinI0@public.gmane.org>
On Mon, Nov 28, 2016 at 05:03:30PM -0600, Grygorii Strashko wrote:
> The cpts now is left enabled after unregistration.
> Hence, disable it in cpts_unregister().
>
> Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
Acked-by: Richard Cochran <richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
--
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
* Re: [PATCH v2 05/13] net: ethernet: ti: cpts: fix registration order
From: Richard Cochran @ 2016-11-29 9:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N,
Sekhar Nori, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Murali Karicheri, Wingman Kwok
In-Reply-To: <20161128230337.6731-6-grygorii.strashko-l0cyMroinI0@public.gmane.org>
On Mon, Nov 28, 2016 at 05:03:29PM -0600, Grygorii Strashko wrote:
> The ptp clock registered before spinlock, which is protecting it, and
> before timecounter and cyclecounter initialization in cpts_register().
>
> So, ensure that ptp clock is registered the last, after everything
> else is done.
>
> Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
Acked-by: Richard Cochran <richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
--
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
* Re: [PATCH v2 04/13] net: ethernet: ti: cpts: fix unbalanced clk api usage in cpts_register/unregister
From: Richard Cochran @ 2016-11-29 9:48 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N,
Sekhar Nori, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Murali Karicheri, Wingman Kwok
In-Reply-To: <20161128230337.6731-5-grygorii.strashko-l0cyMroinI0@public.gmane.org>
On Mon, Nov 28, 2016 at 05:03:28PM -0600, Grygorii Strashko wrote:
> There are two issues with TI CPTS code which are reproducible when TI
> CPSW ethX device passes few up/down iterations:
> - cpts refclk prepare counter continuously incremented after each
> up/down iteration;
> - devm_clk_get(dev, "cpts") is called many times.
>
> Hence, fix these issues by using clk_disable_unprepare() in
> cpts_clk_release() and skipping devm_clk_get() if cpts refclk has been
> acquired already.
>
> Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
Acked-by: Richard Cochran <richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
--
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
* Re: [PATCH v2 6/7] IIO: add STM32 IIO timer driver
From: Benjamin Gaignard @ 2016-11-29 9:46 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Lee Jones, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
Linux Kernel Mailing List, Thierry Reding,
linux-pwm-u79uwXL29TY76Z2rM5mHXA, knaack.h-Mmb7MZpHnFY,
Lars-Peter Clausen, Peter Meerwald-Stadler,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Fabrice Gasnier, Gerald Baeza, Arnaud Pouliquen, Linus Walleij,
Linaro Kernel Mailman List, Benjamin Gaignard
In-Reply-To: <3e2bce3d-c607-d397-487f-2439a0ba7b0b-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2016-11-27 16:42 GMT+01:00 Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
> I delved into the datasheet after trying to figure this out, so I think
> I now sort of understand your intent, but please do answer the questions
> inline.
>
> On 24/11/16 15:14, Benjamin Gaignard wrote:
>> Timers IPs can be used to generate triggers for other IPs like
>> DAC, ADC or other timers.
>> Each trigger may result of timer internals signals like counter enable,
>> reset or edge, this configuration could be done through "master_mode"
>> device attribute.
>>
>> A timer device could be triggered by other timers, we use the trigger
>> name and is_stm32_iio_timer_trigger() function to distinguish them
>> and configure IP input switch.
> The presence of an IIO device in here was a suprise.. What is it actually for?
IIO device is needed to be able to valid the input triggers, which
aren't the same than
those generated by the device.
Since I use triggers name to distinguish them I have introduced
is_stm32_iio_timer_trigger()
function to be sure that triggers are coming for a valid hardware and
not from a fake one
using the same name.
>
> I think this needs some examples of usage to make it clear what the aim is.
In the hardware block there is switch in input to select which trigger
will drive the IP.
For example that allow to start multiple pwm exactly that the same
time or to start/stop
it on master edges.
>
> I was basically expecting to see a driver instantiating one iio trigger
> per timer that can act as a trigger. Those would each have sampling frequency
> controls and basica enable / disable.
An hardware device could have up to 5 triggers: timX_trgo, timX_ch1, timX_ch2,
timX_ch3, timX_ch4.
Until now I have try to simplify the problem and just use timX_trgo trigger.
I have added a "sampling_frequency" attribute on the trigger to
control the frequence
and I use trigger set_state function to enable disable it.
>
> I'm seeing something much more complex here so additional explanation is
> needed.
>>
>> Timer may also decide on which event (edge, level) they could
>> be activated by a trigger, this configuration is done by writing in
>> "slave_mode" device attribute.
> Really? Sounds like magic numbers in sysfs which is never a good idea.
> Please document those attributes / or break them up into elements that
> don't require magic numbers.
I would like to use strings here, it is possible to use IIO_CONST_ATTR
to describe them ?
>>
>> Since triggers could also be used by DAC or ADC their names are defined
>> in include/dt-bindings/iio/timer/st,stm32-iio-timer.h so those IPs will be able
>> to configure themselves in valid_trigger function
>>
>> Trigger have a "sampling_frequency" attribute which allow to configure
>> timer sampling frequency without using pwm interface
>>
>> version 2:
>> - keep only one compatible
> Hmm. I'm not sure I like this as such. We are actually dealing with lots
> of instances of a hardware block with only a small amount of shared
> infrastrcuture (which is classic mfd teritory). So to my mind we
> should have a separate device for each.
Registers mapping and offset are the same, from triggers point of view
only the configuration of the input switch is different.
>
>> - use st,input-triggers-names and st,output-triggers-names
>> to know which triggers are accepted and/or create by the device
> I'm not following why we have this cascade setup?
>
> These are triggers, not devices in the IIO context. We need some detailed
> description of why you have it setup like this. This would include the
> ABI with examples of how you are using it.
I had put example of usage on the cover letter, I will duplicate them
in this commit
message.
>
> Basically I don't currently understand what you are doing :(
>
>
> Thanks,
>
> Jonathan
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
>> ---
>> drivers/iio/Kconfig | 2 +-
>> drivers/iio/Makefile | 1 +
>> drivers/iio/timer/Kconfig | 15 +
>> drivers/iio/timer/Makefile | 1 +
>> drivers/iio/timer/stm32-iio-timer.c | 448 +++++++++++++++++++++
>> drivers/iio/trigger/Kconfig | 1 -
>> include/dt-bindings/iio/timer/st,stm32-iio-timer.h | 23 ++
>> include/linux/iio/timer/stm32-iio-timers.h | 16 +
>> 8 files changed, 505 insertions(+), 2 deletions(-)
>> create mode 100644 drivers/iio/timer/Kconfig
>> create mode 100644 drivers/iio/timer/Makefile
>> create mode 100644 drivers/iio/timer/stm32-iio-timer.c
>> create mode 100644 include/dt-bindings/iio/timer/st,stm32-iio-timer.h
>> create mode 100644 include/linux/iio/timer/stm32-iio-timers.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 6743b18..2de2a80 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -90,5 +90,5 @@ source "drivers/iio/potentiometer/Kconfig"
>> source "drivers/iio/pressure/Kconfig"
>> source "drivers/iio/proximity/Kconfig"
>> source "drivers/iio/temperature/Kconfig"
>> -
>> +source "drivers/iio/timer/Kconfig"
>> endif # IIO
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 87e4c43..b797c08 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -32,4 +32,5 @@ obj-y += potentiometer/
>> obj-y += pressure/
>> obj-y += proximity/
>> obj-y += temperature/
>> +obj-y += timer/
>> obj-y += trigger/
>> diff --git a/drivers/iio/timer/Kconfig b/drivers/iio/timer/Kconfig
>> new file mode 100644
>> index 0000000..7a73bc6
>> --- /dev/null
>> +++ b/drivers/iio/timer/Kconfig
>> @@ -0,0 +1,15 @@
>> +#
>> +# Timers drivers
>> +
>> +menu "Timers"
>> +
>> +config IIO_STM32_TIMER
>> + tristate "stm32 iio timer"
>> + depends on ARCH_STM32
>> + depends on OF
>> + select IIO_TRIGGERED_EVENT
>> + select MFD_STM32_GP_TIMER
>> + help
>> + Select this option to enable stm32 timers hardware IPs
>> +
>> +endmenu
>> diff --git a/drivers/iio/timer/Makefile b/drivers/iio/timer/Makefile
>> new file mode 100644
>> index 0000000..a360c9f
>> --- /dev/null
>> +++ b/drivers/iio/timer/Makefile
>> @@ -0,0 +1 @@
>> +obj-$(CONFIG_IIO_STM32_TIMER) += stm32-iio-timer.o
>> diff --git a/drivers/iio/timer/stm32-iio-timer.c b/drivers/iio/timer/stm32-iio-timer.c
>> new file mode 100644
>> index 0000000..35f2687
>> --- /dev/null
>> +++ b/drivers/iio/timer/stm32-iio-timer.c
>> @@ -0,0 +1,448 @@
>> +/*
>> + * stm32-iio-timer.c
>> + *
>> + * Copyright (C) STMicroelectronics 2016
>> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org> for STMicroelectronics.
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/timer/stm32-iio-timers.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/triggered_event.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/stm32-gptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +#define DRIVER_NAME "stm32-iio-timer"
>> +
>> +struct stm32_iio_timer_dev {
>> + struct device *dev;
>> + struct regmap *regmap;
>> + struct clk *clk;
>> + int irq;
>> + bool own_timer;
>> + unsigned int sampling_frequency;
>> + struct iio_trigger *active_trigger;
>> +};
>> +
>> +static ssize_t _store_frequency(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t len)
>> +{
>> + struct iio_trigger *trig = to_iio_trigger(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
>> + unsigned int freq;
>> + int ret;
>> +
>> + ret = kstrtouint(buf, 10, &freq);
>> + if (ret)
>> + return ret;
>> +
>> + stm32->sampling_frequency = freq;
>> +
>> + return len;
>> +}
>> +
>> +static ssize_t _read_frequency(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct iio_trigger *trig = to_iio_trigger(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
>> + unsigned long long freq = stm32->sampling_frequency;
>> + u32 psc, arr, cr1;
>> +
>> + regmap_read(stm32->regmap, TIM_CR1, &cr1);
>> + regmap_read(stm32->regmap, TIM_PSC, &psc);
>> + regmap_read(stm32->regmap, TIM_ARR, &arr);
>> +
>> + if (psc && arr && (cr1 & TIM_CR1_CEN)) {
>> + freq = (unsigned long long)clk_get_rate(stm32->clk);
>> + do_div(freq, psc);
>> + do_div(freq, arr);
>> + }
>> +
>> + return sprintf(buf, "%d\n", (unsigned int)freq);
>> +}
>> +
>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>> + _read_frequency,
>> + _store_frequency);
>> +
>> +static struct attribute *stm32_trigger_attrs[] = {
>> + &iio_dev_attr_sampling_frequency.dev_attr.attr,
>> + NULL,
>> +};
>> +
>> +static const struct attribute_group stm32_trigger_attr_group = {
>> + .attrs = stm32_trigger_attrs,
>> +};
>> +
>> +static const struct attribute_group *stm32_trigger_attr_groups[] = {
>> + &stm32_trigger_attr_group,
>> + NULL,
>> +};
>> +
>> +static
>> +ssize_t _show_master_mode(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
>> + u32 cr2;
>> +
>> + regmap_read(stm32->regmap, TIM_CR2, &cr2);
>> +
>> + return snprintf(buf, PAGE_SIZE, "%d\n", (cr2 >> 4) & 0x7);
>> +}
>> +
>> +static
>> +ssize_t _store_master_mode(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t len)
>> +{
>> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
>> + u8 mode;
>> + int ret;
>> +
>> + ret = kstrtou8(buf, 10, &mode);
>> + if (ret)
>> + return ret;
>> +
>> + if (mode > 0x7)
>> + return -EINVAL;
>> +
>> + regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, mode << 4);
>> +
>> + return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(master_mode, S_IRUGO | S_IWUSR,
>> + _show_master_mode,
>> + _store_master_mode,
>> + 0);
>> +
>> +static
>> +ssize_t _show_slave_mode(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
>> + u32 smcr;
>> +
>> + regmap_read(stm32->regmap, TIM_SMCR, &smcr);
>> +
>> + return snprintf(buf, PAGE_SIZE, "%d\n", smcr & 0x3);
>> +}
>> +
>> +static
>> +ssize_t _store_slave_mode(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t len)
>> +{
>> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev);
>> + u8 mode;
>> + int ret;
>> +
>> + ret = kstrtou8(buf, 10, &mode);
>> + if (ret)
>> + return ret;
>> +
>> + if (mode > 0x7)
>> + return -EINVAL;
> How is something called slave mode going to be fed a number between 0 and 7?
> Rule of thumb is no magic numbers in sysfs and right now this is looking
> rather cryptic to say the least!
I would like to use strings here, it is possible to use IIO_CONST_ATTR
to describe them ?
In documentation slave modes are describe that this:
000: Slave mode disabled - if CEN = ‘1’ then the prescaler is clocked
directly by the internal clock.
001: Encoder mode 1 - Counter counts up/down on TI2FP1 edge depending
on TI1FP2 level.
010: Encoder mode 2 - Counter counts up/down on TI1FP2 edge depending
on TI2FP1 level.
011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2
edges depending on the level of the other input.
100: Reset Mode - Rising edge of the selected trigger input (TRGI)
reinitializes the counter and generates an update of the registers.
101: Gated Mode - The counter clock is enabled when the trigger input
(TRGI) is high.
The counter stops (but is not reset) as soon as the trigger becomes low.
Both start and stop of the counter are controlled.
110: Trigger Mode - The counter starts at a rising edge of the trigger
TRGI (but it is notreset).
Only the start of the counter is controlled.
111: External Clock Mode 1 - Rising edges of the selected trigger
(TRGI) clock the counter.
>> +
>> + regmap_update_bits(stm32->regmap, TIM_SMCR, TIM_SMCR_SMS, mode);
>> +
>> + return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(slave_mode, S_IRUGO | S_IWUSR,
> There is an iritating move (in terms of noise it's generating) to use values
> directly instead fo these defines. Still if you don't fix it here I'll only
> get a patch 'fixing' it soon after...
I will fix at in version 3
>
>> + _show_slave_mode,
>> + _store_slave_mode,
>> + 0);
>> +
>> +static struct attribute *stm32_timer_attrs[] = {
>> + &iio_dev_attr_master_mode.dev_attr.attr,
>> + &iio_dev_attr_slave_mode.dev_attr.attr,
> New ABI so must be documented under Documentation/ABI/testing/sysfs-bus-iio-*
OK
>> + NULL,
>> +};
>> +
>> +static const struct attribute_group stm32_timer_attr_group = {
>> + .attrs = stm32_timer_attrs,
>> +};
>> +
>> +static int stm32_timer_start(struct stm32_iio_timer_dev *stm32)
>> +{
>> + unsigned long long prd, div;
>> + int prescaler = 0;
>> + u32 max_arr = 0xFFFF, cr1;
>> +
>> + if (stm32->sampling_frequency == 0)
>> + return 0;
>> +
>> + /* Period and prescaler values depends of clock rate */
>> + div = (unsigned long long)clk_get_rate(stm32->clk);
>> +
>> + do_div(div, stm32->sampling_frequency);
>> +
>> + prd = div;
>> +
>> + while (div > max_arr) {
>> + prescaler++;
>> + div = prd;
>> + do_div(div, (prescaler + 1));
>> + }
>> + prd = div;
>> +
>> + if (prescaler > MAX_TIM_PSC) {
>> + dev_err(stm32->dev, "prescaler exceeds the maximum value\n");
>> + return -EINVAL;
>> + }
>> +
>> + /* Check that we own the timer */
>> + regmap_read(stm32->regmap, TIM_CR1, &cr1);
>> + if ((cr1 & TIM_CR1_CEN) && !stm32->own_timer)
>> + return -EBUSY;
>> +
>> + if (!stm32->own_timer) {
>> + stm32->own_timer = true;
>> + clk_enable(stm32->clk);
>> + }
>> +
>> + regmap_write(stm32->regmap, TIM_PSC, prescaler);
>> + regmap_write(stm32->regmap, TIM_ARR, prd - 1);
>> + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
>> +
>> + /* Force master mode to update mode */
>> + regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, 0x20);
>> +
>> + /* Make sure that registers are updated */
>> + regmap_update_bits(stm32->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
>> +
>> + /* Enable interrupt */
>> + regmap_write(stm32->regmap, TIM_SR, 0);
>> + regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, TIM_DIER_UIE);
>> +
>> + /* Enable controller */
>> + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_timer_stop(struct stm32_iio_timer_dev *stm32)
>> +{
>> + if (!stm32->own_timer)
>> + return 0;
>> +
>> + /* Stop timer */
>> + regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, 0);
>> + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, 0);
>> + regmap_write(stm32->regmap, TIM_PSC, 0);
>> + regmap_write(stm32->regmap, TIM_ARR, 0);
>> +
>> + clk_disable(stm32->clk);
>> +
>> + stm32->own_timer = false;
>> + stm32->active_trigger = NULL;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_set_trigger_state(struct iio_trigger *trig, bool state)
>> +{
>> + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig);
>> +
>> + stm32->active_trigger = trig;
>> +
>> + if (state)
>> + return stm32_timer_start(stm32);
>> + else
>> + return stm32_timer_stop(stm32);
>> +}
>> +
>> +static irqreturn_t stm32_timer_irq_handler(int irq, void *private)
>> +{
>> + struct stm32_iio_timer_dev *stm32 = private;
>> + u32 sr;
>> +
>> + regmap_read(stm32->regmap, TIM_SR, &sr);
>> + regmap_write(stm32->regmap, TIM_SR, 0);
>> +
>> + if ((sr & TIM_SR_UIF) && stm32->active_trigger)
>> + iio_trigger_poll(stm32->active_trigger);
> This is acting like a trigger cascading off another trigger?
Not only a trigger but ADC or DAC too.
>
> Normally this interrupt handler would be directly associated with the
> trigger hardware - in this case the timer.
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static const struct iio_trigger_ops timer_trigger_ops = {
>> + .owner = THIS_MODULE,
>> + .set_trigger_state = stm32_set_trigger_state,
>> +};
>> +
>> +static int stm32_setup_iio_triggers(struct stm32_iio_timer_dev *stm32)
>> +{
>> + int ret;
>> + struct property *p;
>> + const char *cur = NULL;
>> +
>> + p = of_find_property(stm32->dev->of_node,
>> + "st,output-triggers-names", NULL);
>> +
>> + while ((cur = of_prop_next_string(p, cur)) != NULL) {
>> + struct iio_trigger *trig;
>> +
>> + trig = devm_iio_trigger_alloc(stm32->dev, "%s", cur);
>> + if (!trig)
>> + return -ENOMEM;
>> +
>> + trig->dev.parent = stm32->dev->parent;
>> + trig->ops = &timer_trigger_ops;
>> + trig->dev.groups = stm32_trigger_attr_groups;
>> + iio_trigger_set_drvdata(trig, stm32);
>> +
>> + ret = devm_iio_trigger_register(stm32->dev, trig);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * is_stm32_iio_timer_trigger
>> + * @trig: trigger to be checked
>> + *
>> + * return true if the trigger is a valid stm32 iio timer trigger
>> + * either return false
>> + */
>> +bool is_stm32_iio_timer_trigger(struct iio_trigger *trig)
>> +{
>> + return (trig->ops == &timer_trigger_ops);
>> +}
>> +EXPORT_SYMBOL(is_stm32_iio_timer_trigger);
>> +
>> +static int stm32_validate_trigger(struct iio_dev *indio_dev,
>> + struct iio_trigger *trig)
>> +{
>> + struct stm32_iio_timer_dev *dev = iio_priv(indio_dev);
>> + int ret;
>> +
>> + if (!is_stm32_iio_timer_trigger(trig))
>> + return -EINVAL;
>> +
>> + ret = of_property_match_string(dev->dev->of_node,
>> + "st,input-triggers-names",
>> + trig->name);
>> +
>> + if (ret < 0)
>> + return ret;
>> +
>> + regmap_update_bits(dev->regmap, TIM_SMCR, TIM_SMCR_TS, ret << 4);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info stm32_trigger_info = {
>> + .driver_module = THIS_MODULE,
>> + .validate_trigger = stm32_validate_trigger,
>> + .attrs = &stm32_timer_attr_group,
>> +};
>> +
>> +static struct stm32_iio_timer_dev *stm32_setup_iio_device(struct device *dev)
>> +{
>> + struct iio_dev *indio_dev;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(dev, sizeof(struct stm32_iio_timer_dev));
>> + if (!indio_dev)
>> + return NULL;
> This is 'unusual'. Why does a trigger driver need an iio_dev at all?
Trigger doesn't need it but for configuring the input switch when
validating the triggers I need a device
>> +
>> + indio_dev->name = dev_name(dev);
>> + indio_dev->dev.parent = dev;
>> + indio_dev->info = &stm32_trigger_info;
>> + indio_dev->modes = INDIO_EVENT_TRIGGERED;
>> + indio_dev->num_channels = 0;
>> + indio_dev->dev.of_node = dev->of_node;
>> +
>> + ret = iio_triggered_event_setup(indio_dev,
>> + NULL,
>> + stm32_timer_irq_handler);
> So the iio_dev exists to provide the ability to fire this interrupt from
> another trigger? Why do you want to do this?
I need interrupt because I use set_trigger_state() to enable/disable
the sampling frequency.
As far I understand and test set_trigger_state() is only called when
indio_dev->modes = INDIO_EVENT_TRIGGERED
and iio_triggered_event_setup have been called to create register an
irq handler.
I just need irq declaration for this last condition, I don't need the
irq to fire in kernel to drive other hardware block.
If I could use set_trigger_state() without calling using
iio_triggered_event_setup() I should remove all
irq code from the driver.
One possible solution would be to add calls to set_trigger_state() in
iio_trigger_write_current() function
at the same level than iio_trigger_detach_poll_func() or
iio_trigger_attach_poll_func() calls:
if (indio_dev->modes = INDIO_DIRECT_MODE && oldtrig->ops->set_trigger_state)
oldtrig->ops->set_trigger_state(oldtrig, false);
if (indio_dev->modes = INDIO_DIRECT_MODE &&
indio_dev->trig->ops->set_trigger_state)
indio_dev->trig->ops->set_trigger_state(indio_dev->trig, true);
I'm to new in IIO framework to understand if that it possible or not
>> + if (ret)
>> + return NULL;
>> +
>> + ret = devm_iio_device_register(dev, indio_dev);
>> + if (ret) {
>> + iio_triggered_event_cleanup(indio_dev);
>> + return NULL;
>> + }
>> +
>> + return iio_priv(indio_dev);
>> +}
>> +
>> +static int stm32_iio_timer_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct stm32_iio_timer_dev *stm32;
>> + struct stm32_gptimer_dev *mfd = dev_get_drvdata(pdev->dev.parent);
>> + int ret;
>> +
>> + stm32 = stm32_setup_iio_device(dev);
>> + if (!stm32)
>> + return -ENOMEM;
>> +
>> + stm32->dev = dev;
>> + stm32->regmap = mfd->regmap;
>> + stm32->clk = mfd->clk;
>> +
>> + stm32->irq = platform_get_irq(pdev, 0);
>> + if (stm32->irq < 0)
>> + return -EINVAL;
>> +
>> + ret = devm_request_irq(stm32->dev, stm32->irq,
>> + stm32_timer_irq_handler, IRQF_SHARED,
>> + "iiotimer_event", stm32);
>> + if (ret)
>> + return ret;
>> +
>> + ret = stm32_setup_iio_triggers(stm32);
>> + if (ret)
>> + return ret;
>> +
>> + platform_set_drvdata(pdev, stm32);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_iio_timer_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_iio_timer_dev *stm32 = platform_get_drvdata(pdev);
>> +
>> + iio_triggered_event_cleanup((struct iio_dev *)stm32);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_trig_of_match[] = {
>> + {
>> + .compatible = "st,stm32-iio-timer",
>> + },
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
>> +
>> +static struct platform_driver stm32_iio_timer_driver = {
>> + .probe = stm32_iio_timer_probe,
>> + .remove = stm32_iio_timer_remove,
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = stm32_trig_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_iio_timer_driver);
>> +
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 iio timer driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
>> index 809b2e7..f2af4fe 100644
>> --- a/drivers/iio/trigger/Kconfig
>> +++ b/drivers/iio/trigger/Kconfig
>> @@ -46,5 +46,4 @@ config IIO_SYSFS_TRIGGER
>>
>> To compile this driver as a module, choose M here: the
>> module will be called iio-trig-sysfs.
>> -
> Clear this out...
>> endmenu
>> diff --git a/include/dt-bindings/iio/timer/st,stm32-iio-timer.h b/include/dt-bindings/iio/timer/st,stm32-iio-timer.h
>> new file mode 100644
>> index 0000000..d39bf16
>> --- /dev/null
>> +++ b/include/dt-bindings/iio/timer/st,stm32-iio-timer.h
>> @@ -0,0 +1,23 @@
>> +/*
>> + * st,stm32-iio-timer.h
>> + *
>> + * Copyright (C) STMicroelectronics 2016
>> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org> for STMicroelectronics.
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#ifndef _DT_BINDINGS_IIO_TIMER_H_
>> +#define _DT_BINDINGS_IIO_TIMER_H_
>> +
>> +#define TIM1_TRGO "tim1_trgo"
>> +#define TIM2_TRGO "tim2_trgo"
>> +#define TIM3_TRGO "tim3_trgo"
>> +#define TIM4_TRGO "tim4_trgo"
>> +#define TIM5_TRGO "tim5_trgo"
>> +#define TIM6_TRGO "tim6_trgo"
>> +#define TIM7_TRGO "tim7_trgo"
>> +#define TIM8_TRGO "tim8_trgo"
>> +#define TIM9_TRGO "tim9_trgo"
>> +#define TIM12_TRGO "tim12_trgo"
>> +
>> +#endif
>> diff --git a/include/linux/iio/timer/stm32-iio-timers.h b/include/linux/iio/timer/stm32-iio-timers.h
>> new file mode 100644
>> index 0000000..5d1b86c
>> --- /dev/null
>> +++ b/include/linux/iio/timer/stm32-iio-timers.h
>> @@ -0,0 +1,16 @@
>> +/*
>> + * stm32-iio-timers.h
>> + *
>> + * Copyright (C) STMicroelectronics 2016
>> + * Author: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org> for STMicroelectronics.
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#ifndef _STM32_IIO_TIMERS_H_
>> +#define _STM32_IIO_TIMERS_H_
>> +
>> +#include <dt-bindings/iio/timer/st,stm32-iio-timer.h>
>> +
>> +bool is_stm32_iio_timer_trigger(struct iio_trigger *trig);
>> +
>> +#endif
>>
>
--
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
* Re: [PATCH v3] iommu/ipmmu-vmsa: Add r8a7796 DT binding
From: Geert Uytterhoeven @ 2016-11-29 9:46 UTC (permalink / raw)
To: Magnus Damm
Cc: devicetree@vger.kernel.org, Mark Rutland, Laurent Pinchart,
Geert Uytterhoeven, Joerg Roedel, linux-kernel@vger.kernel.org,
Linux-Renesas, iommu, Rob Herring, Simon Horman
In-Reply-To: <20161123144054.11919.22936.sendpatchset@little-apple>
On Wed, Nov 23, 2016 at 3:40 PM, Magnus Damm <magnus.damm@gmail.com> wrote:
> From: Magnus Damm <damm+renesas@opensource.se>
>
> Update the IPMMU DT binding documentation to include the r8a7796 compat
> string for R-Car M3-W.
>
> Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Acked-by: Rob Herring <robh@kernel.org>
> Acked-by: Simon Horman <horms+renesas@verge.net.au>
Acked-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v2 01/13] net: ethernet: ti: cpts: switch to readl/writel_relaxed()
From: Richard Cochran @ 2016-11-29 9:38 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev, Mugunthan V N, Sekhar Nori, linux-kernel,
linux-omap, Rob Herring, devicetree, Murali Karicheri,
Wingman Kwok
In-Reply-To: <20161128230337.6731-2-grygorii.strashko@ti.com>
On Mon, Nov 28, 2016 at 05:03:25PM -0600, Grygorii Strashko wrote:
> Switch to readl/writel_relaxed() APIs, because this is recommended
> API and the CPTS IP is reused on Keystone 2 SoCs
> where LE/BE modes are supported.
>
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 02/13] net: ethernet: ti: allow cpts to be built separately
From: Richard Cochran @ 2016-11-29 9:37 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev, Mugunthan V N, Sekhar Nori, linux-kernel,
linux-omap, Rob Herring, devicetree, Murali Karicheri,
Wingman Kwok
In-Reply-To: <20161128230337.6731-3-grygorii.strashko@ti.com>
On Mon, Nov 28, 2016 at 05:03:26PM -0600, Grygorii Strashko wrote:
> diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
> +EXPORT_SYMBOL_GPL(cpts_unregister);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("TI CPTS ALE driver");
ALE?
Also, MODULE_AUTHOR (that's me) is missing.
Thanks,
Richard
^ permalink raw reply
* Re: [PATCH v5 8/8] iommu/rockchip: Enable Rockchip IOMMU on ARM64
From: Heiko Stübner @ 2016-11-29 9:22 UTC (permalink / raw)
To: Brian Norris
Cc: Shunqian Zheng, joro-zLv9SwRftAIdnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, mark.yao-TNX95d0MmH7DzftRWevZcw,
airlied-cv59FeDIM0c, tfiga-hpIqsD4AKlfQT0dZR+AlfA,
xxm-TNX95d0MmH7DzftRWevZcw, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Tomasz Figa,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20161129004225.GA109697-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Am Montag, 28. November 2016, 16:42:27 schrieb Brian Norris:
> Hi,
>
> On Fri, Jun 24, 2016 at 10:13:33AM +0800, Shunqian Zheng wrote:
> > From: Simon Xue <xxm-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> >
> > This patch makes it possible to compile the rockchip-iommu driver on
> > ARM64, so that it can be used with 64-bit SoCs equipped with this type
> > of IOMMU.
> >
> > Signed-off-by: Simon Xue <xxm-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > Signed-off-by: Tomasz Figa <tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > ---
> >
> > drivers/iommu/Kconfig | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> > index ad08603..5572621 100644
> > --- a/drivers/iommu/Kconfig
> > +++ b/drivers/iommu/Kconfig
> > @@ -218,7 +218,7 @@ config OMAP_IOMMU_DEBUG
> >
> > config ROCKCHIP_IOMMU
> >
> > bool "Rockchip IOMMU Support"
> >
> > - depends on ARM
> > + depends on ARM || ARM64
> >
> > depends on ARCH_ROCKCHIP || COMPILE_TEST
> > select IOMMU_API
> > select ARM_DMA_USE_IOMMU
>
> Whatever happened with the rest of this series? Some of the IOMMU bits
> made it, but the DRM fixes never did, and so this didn't get applied.
> This leaves the whole DRM stack unusable on ARM64 Rockchip systems.
>
> The patch context has changed a bit on patch 7 (and maybe 6?), so
> somebody will need to refresh those on the latest upstream.
I've tried forward-porting these when going through Caesars VOP dts patches,
but was somewhat unsucessful in my small attempt at the time - see [0].
Heiko
[0] https://lkml.org/lkml/2016/11/14/519
--
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
* Re: Question regarding clocks in the DW-HDMI DT bindings
From: Laurent Pinchart @ 2016-11-29 9:18 UTC (permalink / raw)
To: Michael Turquette
Cc: Andy Yan, Vladimir Zapolskiy, Fabio Estevam, DRI mailing list,
Linux-DT, nickey.yang-TNX95d0MmH7DzftRWevZcw, Stephen Boyd
In-Reply-To: <CAEG3pNCTH5YUjO8m-jd1rfRRKRus7c39W4rYK56c6NdfQsOcyA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Mike,
On Monday 28 Nov 2016 22:27:01 Michael Turquette wrote:
> On Mon, Nov 28, 2016 at 10:04 PM, Laurent Pinchart wrote:
> > On Monday 28 Nov 2016 13:56:11 Michael Turquette wrote:
> >> On Fri, Nov 25, 2016 at 7:22 AM, Laurent Pinchart wrote:
> >>> On Friday 25 Nov 2016 10:56:53 Andy Yan wrote:
> >>>> On 2016年11月25日 07:26, Laurent Pinchart wrote:
> >>>>> On Friday 25 Nov 2016 00:16:00 Vladimir Zapolskiy wrote:
> >>>>>> On 11/25/2016 12:07 AM, Fabio Estevam wrote:
> >>>>>>> On Thu, Nov 24, 2016 at 7:16 PM, Laurent Pinchart wrote:
> >>>>>>>> Hi Andy,
> >>>>>>>>
> >>>>>>>> As the author of the DW-HDMI DT bindings this question is
> >>>>>>>> addressed to you, but information from anyone is more than welcome.
> >>>>>>>>
> >>>>>>>> The DT bindings specify two clocks named "iahb" and "isfr" but
> >>>>>>>> don't describe them. While I assume that the "isfr" clock
> >>>>>>>> corresponds to the "isfrclk" input signal of the DW HDMI, there is
> >>>>>>>> no "iahb" clock described in the IP core datasheet.
> >>>>>>>
> >>>>>>> i.MX6Q has a DW-HDMI IP block.
> >>>>>>>
> >>>>>>> The names in the devicetree binding matches the ones listed at the
> >>>>>>> i.MX6Q Reference Manual - Table 33-1. HDMI Clocks
> >>>>>>
> >>>>>> correct, for your convenience the table is copied below:
> >>>>>>
> >>>>>> Clock name | Clock Root | Description
> >>>>>> -----------+--------------------+-----------------------------------
> >>>>>> iahbclk | ahb_clk_root | Bus clock
> >>>>>> icecclk | ckil_sync_clk_root | CEC low-frequency clock (32kHZ)
> >>>>>> ihclk | ahb_clk_root | Module clock
> >>>>>> isfrclk | video_27m_clk_root | Internal SFR clock (video clock
> >>>>>> 27MHz)
> >>>>>>
> >>>>>> Here AHB stands for ARM Advanced High-performance Bus.
> >>>>>
> >>>>> That's what I suspected. I believe the "iahb" name is wrong, as the
> >>>>> DW HDMI TX IP core clearly documents the bus clock as being called
> >>>>> "iapbclk". We could rename that in the DT bindings (with
> >>>>> compatibility code in the driver to keep supporting the old name) but
> >>>>> it might not be worth it. The bindings should however document that
> >>>>> the "iahb" clock is the IP core's "iapbclk" bus clock.
> >>>>
> >>>> I got the clock name from I.MX6Q TRM, I also checked the name again
> >>>> with Rockchip IC design team now, hope to get some new information
> >>>> soon.
> >>>
> >>> Thank you. While at it, could you ask them which version of the DW HDMI
> >>> IP used in the SoC ?
> >>>
> >>>>> Another question I have about the bus clock (CC'ing the devicetree
> >>>>> mailing list as well as the clock maintainers) is whether it should
> >>>>> be made optional. The clock is obviously mandatory from a hardware
> >>>>> point of view (given that APB is a synchronous bus and thus requires a
> >>>>> clock), but in some SoCs (specifically for the Renesas SoCs) that
> >>>>> clock is always on and can't be controlled. We already omit bus clocks
> >>>>> in DT for most IP cores when the clock can never be controlled (and we
> >>>>> also omit a bunch of other clocks that we don't even know exist), so
> >>>>> it could make sense to make the clock optional. Otherwise there would
> >>>>> be runtime overhead trying to handle a clock that can't be controlled.
> >>>>
> >>>> If this is the case on Renesas SOCs, we can consider make the clock as
> >>>> optional. Or move all the clock operations to platform specific
> >>>> code(dw_hdmi-rockchip.c/dw_hdmi-imx.c)?
> >>>
> >>> I'd prefer keeping the code generic, otherwise we'd end up with
> >>> platform-specific code that would perform the same operations on most
> >>> platforms. I'll submit a patch soon to make the clock optional, we can
> >>> discuss it then.
> >>
> >> Yes, let's keep the code generic. Absence of a "standard' clock is OK
> >> and we should accept the small overhead incurred in providing a
> >> solution that works for everyone. This prevents hardware-specific
> >> hacks in the driver.
> >>
> >> Related: we really should model bus clocks whenever possible. I've
> >> seen other attempts to merge functional/logic and bus clocks into a
> >> single entity (e.g. a single struct clk_hw/clk_core that turns both
> >> clocks on and off) and this defeats some fine-grained power management
> >> scenarios that the hardware designers had in mind when creating
> >> separate controls for the clocks.
> >
> > Sure, but that wasn't really the question :-) When the bus clock is
> > separately controllable then I agree it should be modelled separately in
> > DT. In my case the bus clock is always on, and I'm thus wondering whether
> > it would be better to make it optional in DT to reduce the runtime
> > overhead incurred by trying to control something that can't be
> > controlled.
>
> I thought I answered this, but maybe not directly enough :-)
>
> We should make the clock mandatory in DT if the physical line must be
> there. This is regardless of whether a given chip/IP variant has
> control over that clock; so long as the physical clock line always
> exists then it is not really "optional".
>
> In the case where there is an absence of the physical clock line, then
> making it optional in DT makes sense.
>
> As an aside, we did discuss the fact that the vast majority of clocks
> are not modeled in DT, and I'm not saying that we transcribe the RTL
> into DT. I'm just saying that if there is a debate over whether or not
> to make a clock optional in DT, when it is always physically there,
> then don't make it optional. Whether or not the control is exposed on
> a particular chip is less important.
>
> Anyways, this is more DT ridiculousness and I won't block either
> method getting merged. I'm just picking my favorite color to paint the
> bikeshed.
Thanks for sharing your opinion. I'll keep the clock mandatory and specify it
in DT.
> >>>>>> By the way while we're discussing DW HDMI bindings specific to iMX,
> >>>>>> I would recommend to remove utterly hackish and iMX only "gpr"
> >>>>>> property from the example in bindings/display/bridge/dw_hdmi.txt
--
Regards,
Laurent Pinchart
--
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
* Re: [PATCH] arm64: dts: Add symlinks for cros-ec-keyboard and cros-ec-sbs
From: Heiko Stübner @ 2016-11-29 9:16 UTC (permalink / raw)
To: Brian Norris
Cc: Douglas Anderson, olof-nZhT3qVonbNeoWH0uzbU5w, Arnd Bergmann,
robh-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161128235132.GA71295-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Am Montag, 28. November 2016, 15:51:35 schrieb Brian Norris:
> Hi Olof, Arnd,
>
> On Thu, May 12, 2016 at 03:02:10PM -0700, Doug Anderson wrote:
> > We'd like to be able to use the cros-ec-keyboard.dtsi and
> > cros-ec-sbs.dtsi snippets for arm64 devices. Currently those files live
> > in the arm/boot/dts directory.
> >
> > Let's follow the convention set by commit 8ee57b8182c4 ("ARM64: dts:
> > vexpress: Use a symlink to vexpress-v2m-rs1.dtsi from arch=arm") and use
> > a symlink. Note that in this case we put the files in a new
> > "include/common" directory since these snippets may need to be
> > referenced by dts files in many different subdirectories.
> >
> > Signed-off-by: Douglas Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > ---
> > Note that, as of right now, there are no users of this. However, given
> > development happening it is almost 100% certain that users will arrive
> > soon. If we need to wait for the first user before landing this we can
> > leave this on the back burner. If it's OK to have no users (yet), let's
> > land.
>
> This may not fit your definition of "soon", but I'm looking to start
> using these files in arch/arm64/. I'll probably carry along this patch
> and resend when I'm ready, but it'd be just as well if you'd merge it
> now (or express a preference for a different directory structure).
Till now I was expecting to just pick up this patch as well once the first
(rk3399-)users arrived. But of course it could also be picked up separately.
> > arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi | 1 +
> > arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi | 1 +
> > 2 files changed, 2 insertions(+)
> > create mode 120000
> > arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> > create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> >
> > \ No newline at end of file
> >
> > diff --git a/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> > b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi new file mode
> > 120000
> > index 000000000000..1c1889f0a791
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> > @@ -0,0 +1 @@
> > +../../../../../arm/boot/dts/cros-ec-keyboard.dtsi
> > \ No newline at end of file
> > diff --git a/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> > b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi new file mode
> > 120000
> > index 000000000000..3d7ae9c88bcd
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> > @@ -0,0 +1 @@
> > +../../../../../arm/boot/dts/cros-ec-sbs.dtsi
>
> FWIW:
>
> Reviewed-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
I'm also running with this on my rk3399-gru and it sucessfully enables the
cros-ec keyboard :-) ,so if anyone picks it up before me they can add
Reviewed-by: Heiko Stueber <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
Tested-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
--
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
* Re: [PATCH v3 1/2] dt-bindings: drm/bridge: adv7511: Add regulator bindings
From: Laurent Pinchart @ 2016-11-29 9:11 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-arm-msm, devicetree, Mark Brown, dri-devel
In-Reply-To: <b779c014-fc20-1fbc-dea9-cb90dbde5b71@codeaurora.org>
Hi Archit,
(CC'ing Mark Brown)
On Tuesday 29 Nov 2016 13:41:33 Archit Taneja wrote:
> On 11/29/2016 12:03 PM, Laurent Pinchart wrote:
> > On Tuesday 29 Nov 2016 11:37:41 Archit Taneja wrote:
> >> Add the regulator supply properties needed by ADV7511 and ADV7533.
> >>
> >> The regulators are specified as optional properties since there can
> >> be boards which have a fixed supply directly routed to the pins, and
> >> these may not be modelled as regulator supplies.
> >
> > That's why we have support for dummy supplies in the kernel, isn't it ?
> > Isn't it better to make the supplies mandatory in the bindings (and
> > obviously handling them as optional in the driver for
> > backward-compatibility) ?
>
> I'm a bit unclear on this.
>
> I thought we couldn't add mandatory properties once the device is already
> present in DT for one or more platforms.
You can, as long as you treat them as optional in the driver to retain
backward compatibility. The DT bindings should document the properties
expected from a new platform (older versions of the bindings will always be
available in the git history).
> Say, if we do make it mandatory for future additions, we would need to have
> DT property for the supplies for the new platforms. If the regulators on
> these boards are fixed supplies, they would be need to be modeled
> using "regulator-fixed", possibly without any input supply. Is that
> what you're suggesting?
That's the idea, yes. Clock maintainers have a similar opinion regarding the
clock bindings, where a clock that is not optional at the hardware level
should be specified in DT even if it's always present.
Mark, any opinion ?
> > Apart from that,
> >
> > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> >> Cc: devicetree@vger.kernel.org
> >> Acked-by: Rob Herring <robh@kernel.org>
> >> Signed-off-by: Archit Taneja <architt@codeaurora.org>
> >> ---
> >> v3:
> >> - Revert back to having a common avdd-supply property for the 1.8V
> >> supplies
> >>
> >> Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt | 9 ++++
> >> 1 file changed, 9 insertions(+)
> >>
> >> diff --git
> >> a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
> >> b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt index
> >> 6532a59..13d53bc 100644
> >> --- a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
> >> +++ b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
> >>
> >> @@ -56,6 +56,15 @@ Optional properties:
> >> - adi,disable-timing-generator: Only for ADV7533. Disables the internal
> >>
> >> timing generator. The chip will rely on the sync signals in the DSI data
> >> lanes, rather than generate its own timings for HDMI output.
> >> +- avdd-supply: A common 1.8V supply that powers up the AVDD, DVDD and
> >> PVDD
> >> + pins. On ADV7511, it also feeds to the BGVDD pin. On ADV7533, it also
> >> powers
> >> + up the A2VDD pin.
> >> +- v3p3-supply: A 3.3V supply that powers up the pin called DVDD_3V on
> >> + ADV7511 and V3P3 on ADV7533.
> >> +
> >> +ADV7533 specific supplies:
> >> +- v1p2-supply: A supply that powers up the V1P2 pin on the chip. It can
> >> be
> >> + either 1.2V or 1.8V.
> >>
> >> Required nodes:
--
Regards,
Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v2 11/13] clocksource: export the clocks_calc_mult_shift to use by timestamp code
From: Thomas Gleixner @ 2016-11-29 9:08 UTC (permalink / raw)
To: Grygorii Strashko
Cc: David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N,
Richard Cochran, Sekhar Nori, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA, Murali Karicheri, Wingman Kwok,
John Stultz
In-Reply-To: <20161128230337.6731-12-grygorii.strashko-l0cyMroinI0@public.gmane.org>
On Mon, 28 Nov 2016, Grygorii Strashko wrote:
> From: Murali Karicheri <m-karicheri2-l0cyMroinI0@public.gmane.org>
>
> The CPSW CPTS driver is capable of doing timestamping on tx/rx packets and
> requires to know mult and shift factors for timestamp conversion from raw
> value to nanoseconds (ptp clock). Now these mult and shift factors are
> calculated manually and provided through DT, which makes very hard to
> support of a lot number of platforms, especially if CPTS refclk is not the
> same for some kind of boards and depends on efuse settings (Keystone 2
> platforms). Hence, export clocks_calc_mult_shift() to allow drivers like
> CPSW CPTS (and other ptp drivesr) to benefit from automaitc calculation of
> mult and shift factors.
>
> Cc: John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
> Signed-off-by: Murali Karicheri <m-karicheri2-l0cyMroinI0@public.gmane.org>
> Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
> ---
> kernel/time/clocksource.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
> index 7e4fad7..150242c 100644
> --- a/kernel/time/clocksource.c
> +++ b/kernel/time/clocksource.c
> @@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
> *mult = tmp;
> *shift = sft;
> }
> +EXPORT_SYMBOL_GPL(clocks_calc_mult_shift);
>
> /*[Clocksource internal variables]---------
> * curr_clocksource:
> --
> 2.10.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 v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
From: Jean-Francois Moine @ 2016-11-29 9:08 UTC (permalink / raw)
To: Dave Airlie, Maxime Ripard, Rob Herring
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <cover.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
.../devicetree/bindings/display/sunxi/hdmi.txt | 56 ++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..1e107cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,56 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - compatible : should be one of
+ "allwinner,sun8i-a83t-hdmi"
+ "allwinner,sun8i-h3-hdmi"
+ - reg: base address and size of the I/O memory
+ - clocks : phandles to the HDMI clocks as described in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+ "bus" : bus gate
+ "clock" : streaming clock
+ "ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : when 2 phandles, must be
+ "hdmi0" and "hdmi1"
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+ as defined in Documentation/devicetree/bindings/graph.txt.
+ port@0 is video and port@1 is audio.
+
+Example:
+
+ hdmi: hdmi@01ee0000 {
+ compatible = "allwinner,sun8i-a83t-hdmi";
+ reg = <0x01ee0000 0x20000>;
+ clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+ <&ccu CLK_HDMI_DDC>;
+ clock-names = "bus", "clock", "ddc-clock";
+ resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+ reset-names = "hdmi0", "hdmi1";
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_pins_a>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { /* video */
+ reg = <0>;
+ hdmi_tcon1: endpoint {
+ remote-endpoint = <&tcon1_hdmi>;
+ };
+ };
+ port@1 { /* audio */
+ reg = <1>;
+ hdmi_i2s2: endpoint {
+ remote-endpoint = <&i2s2_hdmi>;
+ };
+ };
+ };
--
2.10.2
^ permalink raw reply related
* [PATCH v3 05/13] drm: bridge: Add LVDS encoder DT bindings
From: Laurent Pinchart @ 2016-11-29 9:04 UTC (permalink / raw)
To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Archit Taneja,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480410283-28698-1-git-send-email-laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
The DT bindings support parallel to LVDS encoders that don't require any
configuration, similarly to the dumb VGA DAC DT bindings.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
.../bindings/display/bridge/lvds-transmitter.txt | 64 ++++++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
new file mode 100644
index 000000000000..fd39ad34c383
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
@@ -0,0 +1,64 @@
+Parallel to LVDS Encoder
+------------------------
+
+This binding supports the parallel to LVDS encoders that don't require any
+configuration.
+
+LVDS is a physical layer specification defined in ANSI/TIA/EIA-644-A. Multiple
+incompatible data link layers have been used over time to transmit image data
+to LVDS panels. This binding targets devices compatible with the following
+specifications only.
+
+[JEIDA] "Digital Interface Standards for Monitor", JEIDA-59-1999, February
+1999 (Version 1.0), Japan Electronic Industry Development Association (JEIDA)
+[LDI] "Open LVDS Display Interface", May 1999 (Version 0.95), National
+Semiconductor
+[VESA] "VESA Notebook Panel Standard", October 2007 (Version 1.0), Video
+Electronics Standards Association (VESA)
+
+Those devices have been marketed under the FPD-Link and FlatLink brand names
+among others.
+
+
+Required properties:
+
+- compatible: Must be "lvds-encoder"
+
+Required nodes:
+
+This device has two video ports. Their connections are modeled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for parallel input
+- Video port 1 for LVDS output
+
+
+Example
+-------
+
+lvds-encoder {
+ compatible = "lvds-encoder";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ lvds_enc_in: endpoint {
+ remote-endpoint = <&display_out_rgb>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ lvds_enc_out: endpoint {
+ remote-endpoint = <&lvds_panel_in>;
+ };
+ };
+ };
+};
--
Regards,
Laurent Pinchart
--
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 related
* Re: [PATCH v2 1/7] MFD: add bindings for stm32 general purpose timer driver
From: Benjamin Gaignard @ 2016-11-29 8:48 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Lee Jones, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
Linux Kernel Mailing List, Thierry Reding,
linux-pwm-u79uwXL29TY76Z2rM5mHXA, knaack.h-Mmb7MZpHnFY,
Lars-Peter Clausen, Peter Meerwald-Stadler,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Fabrice Gasnier, Gerald Baeza, Arnaud Pouliquen, Linus Walleij,
Linaro Kernel Mailman List, Benjamin Gaignard
In-Reply-To: <b78a21f7-38a1-5a40-b96e-d1c9156aee68-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2016-11-27 16:41 GMT+01:00 Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
> On 27/11/16 14:10, Jonathan Cameron wrote:
>> On 24/11/16 15:14, Benjamin Gaignard wrote:
>>> Add bindings information for stm32 general purpose timer
>>>
>>> version 2:
>>> - rename stm32-mfd-timer to stm32-gptimer
>>> - only keep one compatible string
>>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
>>> ---
>>> .../bindings/mfd/stm32-general-purpose-timer.txt | 43 ++++++++++++++++++++++
>>> 1 file changed, 43 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>> new file mode 100644
>>> index 0000000..2f10e67
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>> @@ -0,0 +1,43 @@
>>> +STM32 general purpose timer driver
>>> +
>>> +Required parameters:
>>> +- compatible: must be "st,stm32-gptimer"
>>> +
>>> +- reg: Physical base address and length of the controller's
>>> + registers.
>>> +- clock-names: Set to "clk_int".
>>> +- clocks: Phandle to the clock used by the timer module.
>>> + For Clk properties, please refer to ../clock/clock-bindings.txt
>>> +
>>> +Optional parameters:
>>> +- resets: Phandle to the parent reset controller.
>>> + See ..reset/st,stm32-rcc.txt
>>> +
>>> +Optional subnodes:
>>> +- pwm: See ../pwm/pwm-stm32.txt
>>> +- iiotimer: See ../iio/timer/stm32-iio-timer.txt
>> Naming issue here. Can't mention IIO as that's a linux subsystem and all
>> bindings must be independent of OS.
>>
>> Perhaps adc-trigger-timer?
>>> +
>>> +Example:
>>> + gptimer1: gptimer1@40010000 {
>>> + compatible = "st,stm32-gptimer";
>>> + reg = <0x40010000 0x400>;
>>> + clocks = <&rcc 0 160>;
>>> + clock-names = "clk_int";
>>> +
>>> + pwm1@0 {
>>> + compatible = "st,stm32-pwm";
>>> + st,pwm-num-chan = <4>;
>>> + st,breakinput;
>>> + st,complementary;
>>> + };
>>> +
>>> + iiotimer1@0 {
>>> + compatible = "st,stm32-iio-timer";
>> Again, avoid the use of iio in here (same issue you had with mfd in the previous
>> version).
>>> + interrupts = <27>;
>>> + st,input-triggers-names = TIM5_TRGO,
>> Docs for these should be introduced before they are used in an example.
>> Same for the PWM ones above. Expand the detail fo the example as you add
>> the other elements.
>
> I've just dived into the datasheet for these timers.
> http://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf
>
I really appreciate that you do this effort, thanks.
>
> I think you need a binding that describes the capabilities of each of the timers
> explicitly. Down to the level of whether there is a repetition counter or not.
> Each should exists as a separate entity in device tree.
>
> They then have an existence as timers separate to the description of what they
> are used for.
>
> Here the only way we are saying they exist is by their use which doesn't feel
> right to me.
>
> So I think you need to move back to what you had in the first place. The key
> thing is that ever timer needs describing fully. They are different enough
> that for example the datasheet doesn't even try to describe them in one section.
> (it has 4 separate chapters covering different sets of these hardware blocks).
> The naming isn't really based on index, we are talking different hardware
> that the datasheet authors have decided not to give different names to!
Even if the hardware are named differently in the documentation they
all share the
same registers definitions and mapping but configurations are
different for each device.
>
> If they'd called them
> advanced timers
> generic timers
> basic timers
> really basic timers meant for driving the DAC (6 and 7)
>
> We'd all have been quite happy with different compatible strings giving away
> what they can do.
4 compatible strings will not be enough to describe devices
configuration, for example
in the documentation general purpose timers could have a 16 or 32 bit
counter, for 1 to 4
pwm channels and triggers (accepted or generated by the device) are
different for each device.
DAC could be drive by timers 2, 4, 5, 6, 7 and 8.
ADC could be driver by 32 triggers
> What you have here is far too specific to what you are trying to do with them
> right now.
>
> These things are separately capable of timing capture (which is I guess where
> the IIO device later comes in).
>
> So my expectation is that we end up potentially instantiating:
>
> 1) An MFD to handle the shared elements of the timers.
> 2) Up to 12ish timers each with separate existence as a device in the driver model
> and in device tree.
> (nasty corner cases here are using timers as perscalers for other timers - I'd be
> tempted to leave that for now)
> Note that each of these devices has a different register set I think? Any shared
> bits are handled via the mfd above (if we even need that MFD which I'm starting
> to doubt looking at the datasheet).
>
pwm and trigger share the same registers but not the same bits.
With regmap write functions I don't have sharing problems.
> 3) Up to N pwms again with there own existence in the device model. These don't
> do much other than wrap the timer and stick it in output mode.
> 4) Up to N iio triggers - this is basically using the timer as a periodic interrupt
> (though without the interrupt having visibility to the kernel) which fires off
> sampling on associated ADCs.
> 5) Up to N iio capture devices for all channels that support timing capture.
> Note there is also hardware encoder capture support in these which should be
> correctly handled as well. This comes back to an ancient discussion on the
> TI ecap units which have similar capabilities (driver never went anywhere but
> I think that was because the author left TI).
>
> Certainly for the IIO devices these should no be bound up into one instance
> as you have done here.
>
> Anyhow, I fear that right now this discussion is missing the key ingredient
> that the hardware is horrendously variable in it's capabilities and really
> is 4-5 different types of hardware that just happen to share a few bits of
> their offsets in their register maps.
Hardware really share the same registers mapping that why I have wrote
one only driver
per framework. Only the configurations are different
>
> So after all that I'm almost more confused than I was at the start!
>
> Jonathan
>
>
>>> + TIM2_TRGO,
>>> + TIM4_TRGO,
>>> + TIM3_TRGO;
>>> + st,output-triggers-names = TIM1_TRGO;
>>> + };
>>> + };
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>
--
Benjamin Gaignard
Graphic Study Group
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH] PM / Domains: Fix compatible for domain idle state
From: Ulf Hansson @ 2016-11-29 8:47 UTC (permalink / raw)
To: Rob Herring
Cc: Lina Iyer, Kevin Hilman, Rafael J. Wysocki,
linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Andy Gross, Stephen Boyd,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Brendan Jackman, Lorenzo Pieralisi, Sudeep Holla, Juri Lelli,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20161110195832.4nz7lxlmshaemcbb@rob-hp-laptop>
On 10 November 2016 at 20:58, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Mon, Nov 07, 2016 at 12:14:28PM +0100, Ulf Hansson wrote:
>> On 3 November 2016 at 22:54, Lina Iyer <lina.iyer-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> > Re-using idle state definition provided by arm,idle-state for domain
>> > idle states creates a lot of confusion and limits further evolution of
>> > the domain idle definition. To keep things clear and simple, define a
>> > idle states for domain using a new compatible "domain-idle-state".
>> >
>> > Fix existing PM domains code to look for the newly defined compatible.
>> >
>> > Cc: <devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
>> > Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> > Signed-off-by: Lina Iyer <lina.iyer-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> > ---
>> > .../bindings/power/domain-idle-state.txt | 33 ++++++++++++++++++++++
>> > .../devicetree/bindings/power/power_domain.txt | 8 +++---
>> > drivers/base/power/domain.c | 2 +-
>> > 3 files changed, 38 insertions(+), 5 deletions(-)
>> > create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>> >
>> > diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > new file mode 100644
>> > index 0000000..eefc7ed
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > @@ -0,0 +1,33 @@
>> > +PM Domain Idle State Node:
>> > +
>> > +A domain idle state node represents the state parameters that will be used to
>> > +select the state when there are no active components in the domain.
>> > +
>> > +The state node has the following parameters -
>> > +
>> > +- compatible:
>> > + Usage: Required
>> > + Value type: <string>
>> > + Definition: Must be "domain-idle-state".
>> > +
>> > +- entry-latency-us
>> > + Usage: Required
>> > + Value type: <prop-encoded-array>
>> > + Definition: u32 value representing worst case latency in
>> > + microseconds required to enter the idle state.
>> > + The exit-latency-us duration may be guaranteed
>> > + only after entry-latency-us has passed.
>>
>> As we anyway are going to change this, why not use an u64 and have the
>> value in ns instead of us?
>
> I can't imagine that you would need more resolution or range. For times
> less than 1us, s/w and register access times are going to dominate the
> time.
>
> Unless there is a real need, I'd keep alignment with the existing
> binding.
Rob, are you fine with this? I thought it would be great to get this
in for 4.10 rc1.
Kind regards
Uffe
--
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 v7 3/8] drm: sun8i: add HDMI video support to A83T and H3
From: Jean-Francois Moine @ 2016-11-29 8:39 UTC (permalink / raw)
To: Dave Airlie, Maxime Ripard, Rob Herring
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <cover.1480414715.git.moinejf-GANU6spQydw@public.gmane.org>
Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
---
drivers/gpu/drm/sun8i/Kconfig | 7 +
drivers/gpu/drm/sun8i/Makefile | 2 +
drivers/gpu/drm/sun8i/de2_hdmi.c | 440 +++++++++++++++++++
drivers/gpu/drm/sun8i/de2_hdmi.h | 51 +++
drivers/gpu/drm/sun8i/de2_hdmi_io.c | 843 ++++++++++++++++++++++++++++++++++++
5 files changed, 1343 insertions(+)
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_DE2
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+ tristate "Support for DE2 HDMI"
+ depends on DRM_SUN8I_DE2
+ help
+ Choose this option if you use want HDMI on DE2.
+ If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
#
sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9ff6132
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,440 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-hdmi",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-hdmi",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, encoder)
+
+/* --- encoder functions --- */
+
+static int de2_hdmi_set_clock(struct de2_hdmi_priv *priv,
+ int rate)
+{
+ struct clk *parent_clk;
+ u32 parent_rate;
+ int ret;
+
+ /* determine and set the best rate for the parent clock (pll-video) */
+ if ((270000 * 2) % rate == 0)
+ parent_rate = 270000000;
+ else if (297000 % rate == 0)
+ parent_rate = 297000000;
+ else
+ return -EINVAL; /* unsupported clock */
+
+ parent_clk = clk_get_parent(priv->clk);
+
+ ret = clk_set_rate(parent_clk, parent_rate);
+ if (ret) {
+ dev_err(priv->dev, "set parent rate failed %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(priv->clk, rate * 1000);
+ if (ret)
+ dev_err(priv->dev, "set rate failed %d\n", ret);
+
+ return ret;
+}
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ priv->cea_mode = drm_match_cea_mode(mode);
+
+ DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+ if (de2_hdmi_set_clock(priv, mode->clock) < 0)
+ return;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_mode_set(priv, mode);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+ .mode_set = de2_hdmi_encoder_mode_set,
+ .enable = de2_hdmi_encoder_enable,
+ .disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int cea_mode = drm_match_cea_mode(mode);
+
+ return hdmi_io_mode_valid(cea_mode) < 0 ? MODE_NOMODE : MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_get_hpd(priv);
+ mutex_unlock(&priv->mutex);
+
+ return ret ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+ unsigned int blk, size_t length)
+{
+ struct de2_hdmi_priv *priv = data;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_ddc_read(priv, blk / 2, (blk & 1) ? 128 : 0,
+ length, buf);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+/* values duplicated from edid_cea_modes[] */
+static const struct drm_display_mode lmodes_tb[] = {
+ /* 2 - 720x480@60Hz */
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+ 798, 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+ /* 4 - 1280x720@60Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 16 - 1920x1080@60Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+};
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ struct drm_display_mode *mode;
+ const struct drm_display_mode *lmode;
+ struct edid *edid;
+ int n;
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (!edid) {
+ dev_warn(priv->dev, "failed to read EDID\n");
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ if (connector->cmdline_mode.xres == 1920 &&
+ connector->cmdline_mode.yres == 1080)
+ lmode = &lmodes_tb[2];
+ else if (connector->cmdline_mode.xres == 1280 &&
+ connector->cmdline_mode.yres == 720)
+ lmode = &lmodes_tb[1];
+ else
+ lmode = &lmodes_tb[0];
+
+ mode = drm_mode_duplicate(connector->dev, lmode);
+ if (!mode)
+ return 0;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+ connector->eld[0] ? "HDMI" : "DVI", n);
+
+ return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+ .get_modes = de2_hdmi_connector_get_modes,
+ .mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = de2_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+ clk_disable_unprepare(priv->clk_ddc);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ reset_control_assert(priv->reset1);
+ reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = data;
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /* if no CRTC, delay */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ /* HDMI init */
+ ret = reset_control_deassert(priv->reset0);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(priv->reset1);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(priv->clk_ddc);
+ if (ret)
+ goto err;
+
+ de2_hdmi_set_clock(priv, 147500); /* set a valid clock rate */
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_init(priv);
+ mutex_unlock(&priv->mutex);
+
+ /* encoder init */
+ ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ goto err;
+
+ drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+ /* connector init */
+ ret = drm_connector_init(drm, connector,
+ &de2_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ drm_connector_helper_add(connector,
+ &de2_hdmi_connector_helper_funcs);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+err_connector:
+ drm_encoder_cleanup(encoder);
+err:
+ dev_err(dev, "err %d\n", ret);
+ return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->connector.dev)
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+ .bind = de2_hdmi_bind,
+ .unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct de2_hdmi_priv *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->dev = dev;
+
+ mutex_init(&priv->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -ENXIO;
+ }
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ dev_err(dev, "failed to map registers err %d\n", ret);
+ return ret;
+ }
+
+ priv->gate = devm_clk_get(dev, "bus");
+ if (IS_ERR(priv->gate)) {
+ ret = PTR_ERR(priv->gate);
+ dev_err(dev, "gate clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "hdmi clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+ if (IS_ERR(priv->clk_ddc)) {
+ ret = PTR_ERR(priv->clk_ddc);
+ dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+ if (IS_ERR(priv->reset0)) {
+ ret = PTR_ERR(priv->reset0);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+ if (IS_ERR(priv->reset1)) {
+ ret = PTR_ERR(priv->reset1);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+ &pdev->dev)->data;
+
+ de2_hdmi_audio_register(dev);
+
+ return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+ de2_hdmi_audio_unregister(&pdev->dev);
+ component_del(&pdev->dev, &de2_hdmi_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+ .probe = de2_hdmi_probe,
+ .remove = de2_hdmi_remove,
+ .driver = {
+ .name = "sun8i-de2-hdmi",
+ .of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+ },
+};
+
+/* create the video HDMI driver and the sound card driver */
+static int __init de2_hdmi_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&de2_hdmi_driver);
+
+ return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+ platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..6711a76
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-François Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+ struct device *dev;
+ void __iomem *mmio;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct clk *clk;
+ struct clk *clk_ddc;
+ struct clk *gate;
+ struct reset_control *reset0;
+ struct reset_control *reset1;
+
+ struct mutex mutex;
+ u8 soc_type;
+ u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char offset,
+ int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..b746a52
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,843 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ * https://linux-sunxi.org/DWC_HDMI_Controller
+ * https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+
+static int hdmi_mode = 1;
+MODULE_PARM_DESC(de2_hdmi_mode, "Force HDMI mode.\n"
+"When set, if the display device is connected by HDMI, switch to this mode.\n"
+"When unset, stay in DVI mode (useful when screen overscan).\n");
+module_param_named(de2_hdmi_mode, hdmi_mode, int, 0644);
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG 0x10010
+#define HDMI_PHY_CTRL_REG 0x10020
+#define HDMI_PHY_24_REG 0x10024
+#define HDMI_PHY_28_REG 0x10028
+#define HDMI_PHY_PLL_REG 0x1002c
+#define HDMI_PHY_CLK_REG 0x10030
+#define HDMI_PHY_34_REG 0x10034
+#define HDMI_PHY_STATUS_REG 0x10038
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define HDMI_FC_INVIDCONF_HDMI_MODE 0x08
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH0 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define HDMI_PHY_CONF0_PDZ BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define HDMI_AUD_CONF0_SW_RESET 0x80
+#define HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define HDMI_AUD_INPUTCLKFS_64FS 0x04
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+
+#define VIC_720x480_60 2
+#define VIC_1280x720_60 4
+#define VIC_1920x1080i_60 5
+#define VIC_720x480i_60 6
+#define VIC_1920x1080_60 16
+#define VIC_720x576_50 17
+#define VIC_1280x720_50 19
+#define VIC_1920x1080i_50 20
+#define VIC_720x576i_50 21
+#define VIC_1920x1080_50 31
+#define VIC_1920x1080_24 32
+#define VIC_1920x1080_25 33
+#define VIC_1920x1080_30 34
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readl_relaxed(priv->mmio + addr);
+}
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+ priv->mmio + addr);
+}
+
+/* read on/off functions */
+static inline void hdmi_read_on(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_read_off(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+ u8 clkdis = priv->soc_type == SOC_H3 ?
+ ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+ hdmi_read_on(priv);
+
+ /* software reset */
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0x00);
+ udelay(2);
+
+ /* mask all interrupts */
+ hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+ hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+ hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+ hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+ hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+ hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+ hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+ hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+ hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+ hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+ hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+ HDMI_A_HDCPCFG1_SWRESET);
+ hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+ hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+ hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+ hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+ hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+ hdmi_inner_init(priv);
+
+ hdmi_writeb(priv, 0x10000, 0x01);
+ hdmi_writeb(priv, 0x10001, 0x00);
+ hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, 0x10003, 0x00);
+ hdmi_writeb(priv, 0x10007, 0xa0);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ,
+ HDMI_MC_PHYRSTZ_DEASSERT);
+ udelay(1);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+ hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+ hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+ hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+ hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+ int to_cnt;
+ u32 tmp;
+
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+ udelay(10);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+ usleep_range(40, 50);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+ usleep_range(100, 120);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+ to_cnt = 10;
+ while (1) {
+ if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+ break;
+ usleep_range(200, 250);
+ if (--to_cnt == 0) {
+ dev_err(priv->dev, "hdmi phy init timeout\n");
+ break;
+ }
+ }
+
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+ hdmi_inner_init(priv);
+}
+
+static int get_divider(int rate)
+{
+ if (rate <= 27000)
+ return 11;
+ if (rate <= 74250)
+ return 4;
+ if (rate <= 148500)
+ return 2;
+ return 1;
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+ int addr, u8 valh, u8 vall)
+{
+ hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+ hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+ hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ usleep_range(2000, 2500);
+}
+
+static void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080 */
+ hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+/* case 11: * 480P/576P */
+ default:
+ hdmi_i2cm_write(priv, 0x06, 0x01,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0xe3 : 0xe0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ }
+ hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ u32 tmp;
+
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+ /* or 0x30dc5fc0 ? */
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(200);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ if (tmp < 0x3d)
+ tmp += 2;
+ else
+ tmp = 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ msleep(100);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080 */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ default:
+/* case 11: * 480P/576P */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ }
+}
+
+/* HDMI functions */
+
+/* hardware init */
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_phy_init_h3(priv);
+ else
+ hdmi_phy_init_a83t(priv);
+
+ /* disable hdcp */
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+/* check if the resolution is supported */
+int hdmi_io_mode_valid(int cea_mode)
+{
+ switch (cea_mode) {
+ case VIC_720x480_60:
+ case VIC_1280x720_60:
+ case VIC_1920x1080i_60:
+ case VIC_720x480i_60:
+ case VIC_1920x1080_60:
+ case VIC_720x576_50:
+ case VIC_1280x720_50:
+ case VIC_1920x1080i_50:
+ case VIC_720x576i_50:
+ case VIC_1920x1080_50:
+ case VIC_1920x1080_24:
+ case VIC_1920x1080_25:
+ case VIC_1920x1080_30:
+ return 1;
+ }
+ return -1;
+}
+
+/* output init */
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ int avi_d2; /* AVI InfoFrame Data Byte 2 */
+ int h_blank, h_sync_w, h_front_p;
+ int invidconf;
+
+ /* colorimetry and aspect ratio */
+ switch (priv->cea_mode) {
+ case 0:
+ return; /* bad mode */
+ case VIC_720x480_60:
+ case VIC_720x480i_60:
+ case VIC_720x576_50:
+ case VIC_720x576i_50:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+ (HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+ break;
+ default:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+ (HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+ break;
+ }
+
+ h_blank = mode->htotal - mode->hdisplay;
+ h_sync_w = mode->hsync_end - mode->hsync_start;
+ h_front_p = mode->hsync_start - mode->hdisplay;
+
+ invidconf = 0;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ invidconf |= 0x01;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ invidconf |= 0x20;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ invidconf |= 0x40;
+
+ if (priv->soc_type == SOC_H3) {
+ hdmi_phy_set_h3(priv, mode);
+ hdmi_inner_init(priv);
+ } else {
+ hdmi_phy_init_a83t(priv);
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+ hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+ invidconf |
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+ hdmi_writeb(priv, 0x10001, invidconf < 0x60 ? 0x03 : 0x00);
+ hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, mode->hdisplay >> 8);
+ hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+ mode->vsync_end - mode->vsync_start);
+ hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, mode->vdisplay >> 8);
+ hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, h_blank >> 8);
+ hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+ mode->vsync_start - mode->vdisplay);
+ hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, h_front_p >> 8);
+ hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, h_sync_w >> 8);
+ hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, mode->hdisplay);
+ hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, h_blank);
+ hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, h_front_p);
+ hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0, h_sync_w);
+ hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, mode->vdisplay);
+ hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+ mode->vtotal - mode->vdisplay);
+ hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+ hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+ hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+ hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+ hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+ hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+ hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+ hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x41 : 0x40);
+ hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+ hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+ hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+ hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+ hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+ hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+ hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+ hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+ hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+ hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+ hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+ if (priv->connector.eld[0]) { /* if audio/HDMI */
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+ hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+ hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+ HDMI_IEEE_OUI >> 8);
+ hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+ HDMI_IEEE_OUI & 0xff);
+ hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+ hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+ hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+ hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+ hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+ hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+ hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+ hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+ /* switch to HDMI mode */
+ if (hdmi_mode) {
+ hdmi_read_on(priv);
+ hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF,
+ HDMI_FC_INVIDCONF_HDMI_MODE);
+ hdmi_read_off(priv);
+ }
+
+ /* AVI */
+ hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+ hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+ hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+ hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+ }
+
+ hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ if (priv->soc_type != SOC_H3)
+ hdmi_phy_set_a83t(priv, mode);
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+ if (!priv->cea_mode)
+ return;
+pr_info("*jfm* hdmi video on\n");
+ if (priv->soc_type == SOC_H3)
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+ if (!priv->cea_mode)
+ return;
+pr_info("*jfm* hdmi video off\n");
+ if (priv->soc_type == SOC_H3)
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char off,
+ int nbyte, char *pbuf)
+{
+ unsigned int to_cnt;
+ u8 reg;
+ int ret = 0;
+
+ hdmi_read_on(priv);
+ hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+ to_cnt = 50;
+ while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+ udelay(10);
+ if (--to_cnt == 0) { /* wait for 500us for timeout */
+ dev_err(priv->dev, "hdmi ddc reset timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+ hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+ hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+ while (nbyte > 0) {
+ hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+ hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+ hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+ hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+ hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+ HDMI_I2CM_OPERATION_DDC_READ);
+
+ to_cnt = 200; /* timeout 100ms */
+ while (1) {
+ reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+ if (reg & 0x02) {
+ *pbuf++ = hdmi_readb(priv,
+ R_7e03_HDMI_I2CM_DATAI);
+ break;
+ }
+ if (reg & 0x01) {
+ dev_err(priv->dev, "hdmi ddc read error\n");
+ ret = -1;
+ break;
+ }
+ if (--to_cnt == 0) {
+ if (!ret) {
+ dev_err(priv->dev,
+ "hdmi ddc read timeout\n");
+ ret = -1;
+ }
+ break;
+ }
+ usleep_range(500, 800);
+ }
+ if (ret)
+ break;
+ nbyte--;
+ off++;
+ }
+ hdmi_read_off(priv);
+
+ return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+ int ret;
+
+ hdmi_read_on(priv);
+
+ if (priv->soc_type == SOC_H3)
+ ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+ else
+ ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+ hdmi_read_off(priv);
+
+ return ret != 0;
+}
--
2.10.2
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply related
* Re: [PATCH v2 01/13] devicetree/bindings: display: Document common panel properties
From: Laurent Pinchart @ 2016-11-29 8:27 UTC (permalink / raw)
To: dri-devel
Cc: Rob Herring, linux-renesas-soc, Tomi Valkeinen, Laurent Pinchart,
devicetree
In-Reply-To: <2307840.2mk6E40XHl@avalon>
Hi Rob,
On Tuesday 22 Nov 2016 11:36:55 Laurent Pinchart wrote:
> On Monday 21 Nov 2016 10:48:15 Rob Herring wrote:
> > On Sat, Nov 19, 2016 at 05:28:01AM +0200, Laurent Pinchart wrote:
> >> Document properties common to several display panels in a central
> >> location that can be referenced by the panel device tree bindings.
> >
> > Looks good. Just one comment...
> >
> > [...]
> >
> >> +Connectivity
> >> +------------
> >> +
> >> +- ports: Panels receive video data through one or multiple connections.
> >> While
> >> + the nature of those connections is specific to the panel type, the
> >> + connectivity is expressed in a standard fashion using ports as
> >> specified in
> >> + the device graph bindings defined in
> >> + Documentation/devicetree/bindings/graph.txt.
> >
> > We allow panels to either use graph binding or be a child of the display
> > controller.
>
> I knew that some display controllers use a phandle to the panel (see the
> fsl,panel and nvidia,panel properties), but I didn't know we had panels as
> children of display controller nodes. I don't think we should allow that for
> anything but DSI panels, as the DT hierarchy is based on control buses. Are
> you sure we have other panels instantiated through that mechanism ?
Ping ?
Please note that this file documents properties common to multiple panel DT
bindings, but in no way makes it mandatory to use the OF graph bindings for
panels. The decision is left to individual bindings.
> > Using the graph is preferred, but in the simple cases just a child node is
> > sufficient. This should be described here or somewhere in this doc.
--
Regards,
Laurent Pinchart
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox