[PATCH 6/7] pinctrl: ds90ux9xx: add TI DS90Ux9xx pinmux and GPIO controller driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Vladimir Zapolskiy <vladimir_zapolskiy@xxxxxxxxxx>

The change adds an MFD cell driver for managing pin functions on
TI DS90Ux9xx de-/serializers.

Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@xxxxxxxxxx>
---
 drivers/pinctrl/Kconfig             |  11 +
 drivers/pinctrl/Makefile            |   1 +
 drivers/pinctrl/pinctrl-ds90ux9xx.c | 970 ++++++++++++++++++++++++++++
 3 files changed, 982 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-ds90ux9xx.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 978b2ed4d014..9350263cac4b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -123,6 +123,17 @@ config PINCTRL_DIGICOLOR
 	select PINMUX
 	select GENERIC_PINCONF
 
+config PINCTRL_DS90UX9XX
+	tristate "TI DS90Ux9xx pin multiplexer and GPIO driver"
+	depends on MFD_DS90UX9XX
+	default MFD_DS90UX9XX
+	select GPIOLIB
+	select PINMUX
+	select GENERIC_PINCONF
+	help
+	  Select this option to enable pinmux and GPIO bridge/controller
+	  driver for the TI DS90Ux9xx de-/serializer chip family.
+
 config PINCTRL_LANTIQ
 	bool
 	depends on LANTIQ
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8e127bd8610f..34fc2dbfb9c1 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_AT91PIO4)	+= pinctrl-at91-pio4.o
 obj-$(CONFIG_PINCTRL_AMD)	+= pinctrl-amd.o
 obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o
 obj-$(CONFIG_PINCTRL_DIGICOLOR)	+= pinctrl-digicolor.o
+obj-$(CONFIG_PINCTRL_DS90UX9XX)	+= pinctrl-ds90ux9xx.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_GEMINI)	+= pinctrl-gemini.o
 obj-$(CONFIG_PINCTRL_MAX77620)	+= pinctrl-max77620.o
diff --git a/drivers/pinctrl/pinctrl-ds90ux9xx.c b/drivers/pinctrl/pinctrl-ds90ux9xx.c
new file mode 100644
index 000000000000..7fdb5c15743a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ds90ux9xx.c
@@ -0,0 +1,970 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Pinmux and GPIO controller driver for TI DS90Ux9xx De-/Serializer hardware
+ *
+ * Copyright (c) 2017-2018 Mentor Graphics Inc.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/mfd/ds90ux9xx.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+
+#include "pinctrl-utils.h"
+
+#define SER_REG_PIN_CTRL		0x12
+#define PIN_CTRL_RGB18			BIT(2)
+#define PIN_CTRL_I2S_DATA_ISLAND	BIT(1)
+#define PIN_CTRL_I2S_CHANNEL_B		(BIT(0) | BIT(3))
+
+#define SER_REG_I2S_SURROUND		0x1A
+#define PIN_CTRL_I2S_SURR_BIT		BIT(0)
+
+#define DES_REG_INDIRECT_PASS		0x16
+
+#define OUTPUT_HIGH			BIT(3)
+#define REMOTE_CONTROL			BIT(2)
+#define DIR_INPUT			BIT(1)
+#define ENABLE_GPIO			BIT(0)
+
+#define GPIO_AS_INPUT			(ENABLE_GPIO | DIR_INPUT)
+#define GPIO_AS_OUTPUT			ENABLE_GPIO
+#define GPIO_OUTPUT_HIGH		(GPIO_AS_OUTPUT | OUTPUT_HIGH)
+#define GPIO_OUTPUT_LOW			GPIO_AS_OUTPUT
+#define GPIO_OUTPUT_REMOTE		(GPIO_AS_OUTPUT | REMOTE_CONTROL)
+
+#define DS90UX9XX_MAX_GROUP_PINS	6
+
+struct ds90ux9xx_gpio {
+	const u8 cfg_reg;
+	const u8 cfg_mask;
+	const u8 stat_reg;
+	const u8 stat_bit;
+	const bool input;
+};
+
+#define DS90UX9XX_PIN_GPIO_SIMPLE(_cfg, _high, _input)		\
+	{							\
+		.cfg_reg	= _cfg,				\
+		.cfg_mask	= _high ? 0xf0 : 0x0f,		\
+		.stat_reg	= 0x0,				\
+		.stat_bit	= 0x0,				\
+		.input		= _input ? true : false,	\
+	}
+
+#define DS90UX9XX_PIN_GPIO(_cfg, _high, _stat, _bit)		\
+	{							\
+		.cfg_reg	= _cfg,				\
+		.cfg_mask	= _high ? 0xf0 : 0x0f,		\
+		.stat_reg	= _stat,			\
+		.stat_bit	= BIT(_bit),			\
+		.input		= true,				\
+	}
+
+enum ds90ux9xx_function {
+	DS90UX9XX_FUNC_NONE,
+	DS90UX9XX_FUNC_GPIO,
+	DS90UX9XX_FUNC_REMOTE,
+	DS90UX9XX_FUNC_PASS,
+	DS90UX9XX_FUNC_I2S_1,
+	DS90UX9XX_FUNC_I2S_2,
+	DS90UX9XX_FUNC_I2S_3,
+	DS90UX9XX_FUNC_I2S_4,
+	DS90UX9XX_FUNC_I2S_M,
+	DS90UX9XX_FUNC_PARALLEL,
+};
+
+struct ds90ux9xx_pin {
+	const unsigned int id;
+	const char *name;
+	const u32 func_mask;
+	const u8 pass_bit;
+	const struct ds90ux9xx_gpio gpio;
+};
+
+#define TO_BIT(_f)	(DS90UX9XX_FUNC_##_f ? BIT(DS90UX9XX_FUNC_##_f) : 0)
+#define DS90UX9XX_GPIO(_pin, _name, _f1, _f2, _f3)			\
+	[_pin] = {							\
+		.id = _pin,						\
+		.name = _name,						\
+		.func_mask = TO_BIT(GPIO) | TO_BIT(_f2) | TO_BIT(_f3),	\
+		.gpio = DS90UX9XX_PIN_##_f1,				\
+	}
+
+#define DS90UX940_GPIO(_pin, _name, _f1, _f2, _f3, _pass)		\
+	[_pin] = {							\
+		.id = _pin,						\
+		.name = _name,						\
+		.func_mask = TO_BIT(GPIO) | TO_BIT(PASS) |		\
+			     TO_BIT(_f2) | TO_BIT(_f3),			\
+		.pass_bit = BIT(_pass),					\
+		.gpio = DS90UX9XX_PIN_##_f1,				\
+	}
+
+struct ds90ux9xx_pinctrl_data {
+	const char *name;
+	const struct ds90ux9xx_pin *pins;
+	const struct pinctrl_pin_desc *pins_desc;
+	const unsigned int npins;
+	const enum ds90ux9xx_function *functions;
+	const unsigned int nfunctions;
+};
+
+static const struct pinctrl_pin_desc ds90ux925_926_pins_desc[] = {
+	PINCTRL_PIN(0, "gpio0"),	/* DS90Ux925 pin 25, DS90Ux926 pin 41 */
+	PINCTRL_PIN(1, "gpio1"),	/* DS90Ux925 pin 26, DS90Ux926 pin 40 */
+	PINCTRL_PIN(2, "gpio2"),	/* DS90Ux925 pin 35, DS90Ux926 pin 28 */
+	PINCTRL_PIN(3, "gpio3"),	/* DS90Ux925 pin 36, DS90Ux926 pin 27 */
+	PINCTRL_PIN(4, "gpio4"),	/* DS90Ux925 pin 43, DS90Ux926 pin 19 */
+	PINCTRL_PIN(5, "gpio5"),	/* DS90Ux925 pin 44, DS90Ux926 pin 18 */
+	PINCTRL_PIN(6, "gpio6"),	/* DS90Ux925 pin 11, DS90Ux926 pin 45 */
+	PINCTRL_PIN(7, "gpio7"),	/* DS90Ux925 pin 12, DS90Ux926 pin 30 */
+	PINCTRL_PIN(8, "gpio8"),	/* DS90Ux925 pin 13, DS90Ux926 pin  1 */
+};
+
+static const struct pinctrl_pin_desc ds90ux927_928_pins_desc[] = {
+	PINCTRL_PIN(0, "gpio0"),	/* DS90Ux927 pin 39, DS90Ux928 pin 14 */
+	PINCTRL_PIN(1, "gpio1"),	/* DS90Ux927 pin 40, DS90Ux928 pin 13 */
+	PINCTRL_PIN(2, "gpio2"),	/* DS90Ux927 pin  5, DS90Ux928 pin 37 */
+	PINCTRL_PIN(3, "gpio3"),	/* DS90Ux927 pin  6, DS90Ux928 pin 36 */
+	PINCTRL_PIN(4, "gpio5"),	/* DS90Ux927 pin  4, DS90Ux928 pin  3 */
+	PINCTRL_PIN(5, "gpio6"),	/* DS90Ux927 pin  3, DS90Ux928 pin  7 */
+	PINCTRL_PIN(6, "gpio7"),	/* DS90Ux927 pin  2, DS90Ux928 pin 10 */
+	PINCTRL_PIN(7, "gpio8"),	/* DS90Ux927 pin  1, DS90Ux928 pin  8 */
+};
+
+static const struct pinctrl_pin_desc ds90ux940_pins_desc[] = {
+	PINCTRL_PIN(0, "gpio0"),	/* DS90Ux940 pin  7 */
+	PINCTRL_PIN(1, "gpio1"),	/* DS90Ux940 pin  8 */
+	PINCTRL_PIN(2, "gpio2"),	/* DS90Ux940 pin 10 */
+	PINCTRL_PIN(3, "gpio3"),	/* DS90Ux940 pin  9 */
+	PINCTRL_PIN(4, "gpio5"),	/* DS90Ux940 pin 11 */
+	PINCTRL_PIN(5, "gpio6"),	/* DS90Ux940 pin 12 */
+	PINCTRL_PIN(6, "gpio7"),	/* DS90Ux940 pin 14 */
+	PINCTRL_PIN(7, "gpio8"),	/* DS90Ux940 pin 13 */
+	PINCTRL_PIN(8, "gpio9"),	/* DS90Ux940 pin 15 */
+};
+
+static const struct ds90ux9xx_pin ds90ux925_pins[] = {
+	DS90UX9XX_GPIO(0, "gpio0", GPIO_SIMPLE(0x0d, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(1, "gpio1", GPIO_SIMPLE(0x0e, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(2, "gpio2", GPIO_SIMPLE(0x0e, 1, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(3, "gpio3", GPIO_SIMPLE(0x0f, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(4, "gpio4", GPIO_SIMPLE(0x0f, 1, 0),   NONE, PARALLEL),
+	DS90UX9XX_GPIO(5, "gpio5", GPIO_SIMPLE(0x10, 0, 0),  I2S_2, PARALLEL),
+	DS90UX9XX_GPIO(6, "gpio6", GPIO_SIMPLE(0x10, 1, 0),  I2S_1,     NONE),
+	DS90UX9XX_GPIO(7, "gpio7", GPIO_SIMPLE(0x11, 0, 0),  I2S_1,     NONE),
+	DS90UX9XX_GPIO(8, "gpio8", GPIO_SIMPLE(0x11, 1, 0),  I2S_1,     NONE),
+};
+
+static const struct ds90ux9xx_pin ds90ux926_pins[] = {
+	DS90UX9XX_GPIO(0, "gpio0", GPIO_SIMPLE(0x1d, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(1, "gpio1", GPIO_SIMPLE(0x1e, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(2, "gpio2", GPIO_SIMPLE(0x1e, 1, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(3, "gpio3", GPIO_SIMPLE(0x1f, 0, 1), REMOTE, PARALLEL),
+	DS90UX9XX_GPIO(4, "gpio4", GPIO_SIMPLE(0x1f, 1, 0),   NONE, PARALLEL),
+	DS90UX9XX_GPIO(5, "gpio5", GPIO_SIMPLE(0x20, 0, 0),  I2S_2, PARALLEL),
+	DS90UX9XX_GPIO(6, "gpio6", GPIO_SIMPLE(0x20, 1, 0),  I2S_1,     NONE),
+	DS90UX9XX_GPIO(7, "gpio7", GPIO_SIMPLE(0x21, 0, 0),  I2S_1,     NONE),
+	DS90UX9XX_GPIO(8, "gpio8", GPIO_SIMPLE(0x21, 1, 0),  I2S_1,     NONE),
+};
+
+static const struct ds90ux9xx_pin ds90ux927_pins[] = {
+	DS90UX9XX_GPIO(0, "gpio0", GPIO(0x0d, 0, 0x1c, 0), REMOTE,  NONE),
+	DS90UX9XX_GPIO(1, "gpio1", GPIO(0x0e, 0, 0x1c, 1), REMOTE,  NONE),
+	DS90UX9XX_GPIO(2, "gpio2", GPIO(0x0e, 1, 0x1c, 2), REMOTE, I2S_3),
+	DS90UX9XX_GPIO(3, "gpio3", GPIO(0x0f, 0, 0x1c, 3), REMOTE, I2S_4),
+	DS90UX9XX_GPIO(4, "gpio5", GPIO(0x10, 0, 0x1c, 5),   NONE, I2S_2),
+	DS90UX9XX_GPIO(5, "gpio6", GPIO(0x10, 1, 0x1c, 6),   NONE, I2S_1),
+	DS90UX9XX_GPIO(6, "gpio7", GPIO(0x11, 0, 0x1c, 7),   NONE, I2S_1),
+	DS90UX9XX_GPIO(7, "gpio8", GPIO(0x11, 1, 0x1d, 0),   NONE, I2S_1),
+};
+
+static const struct ds90ux9xx_pin ds90ux928_pins[] = {
+	DS90UX9XX_GPIO(0, "gpio0", GPIO(0x1d, 0, 0x6e, 0), REMOTE, I2S_M),
+	DS90UX9XX_GPIO(1, "gpio1", GPIO(0x1e, 0, 0x6e, 1), REMOTE, I2S_M),
+	DS90UX9XX_GPIO(2, "gpio2", GPIO(0x1e, 1, 0x6e, 2), REMOTE, I2S_3),
+	DS90UX9XX_GPIO(3, "gpio3", GPIO(0x1f, 0, 0x6e, 3), REMOTE, I2S_4),
+	DS90UX9XX_GPIO(4, "gpio5", GPIO(0x20, 0, 0x6e, 5),   NONE, I2S_2),
+	DS90UX9XX_GPIO(5, "gpio6", GPIO(0x20, 1, 0x6e, 6),   NONE, I2S_1),
+	DS90UX9XX_GPIO(6, "gpio7", GPIO(0x21, 0, 0x6e, 7),   NONE, I2S_1),
+	DS90UX9XX_GPIO(7, "gpio8", GPIO(0x21, 1, 0x6f, 0),   NONE, I2S_1),
+};
+
+static const struct ds90ux9xx_pin ds90ux940_pins[] = {
+	DS90UX940_GPIO(0, "gpio0", GPIO(0x1d, 0, 0x6e, 0), REMOTE, I2S_M, 1),
+	DS90UX9XX_GPIO(1, "gpio1", GPIO(0x1e, 0, 0x6e, 1), REMOTE, I2S_M),
+	DS90UX9XX_GPIO(2, "gpio2", GPIO(0x1e, 1, 0x6e, 2), REMOTE, I2S_3),
+	DS90UX940_GPIO(3, "gpio3", GPIO(0x1f, 0, 0x6e, 3), REMOTE, I2S_4, 2),
+	DS90UX9XX_GPIO(4, "gpio5", GPIO(0x20, 0, 0x6e, 5),   NONE, I2S_2),
+	DS90UX9XX_GPIO(5, "gpio6", GPIO(0x20, 1, 0x6e, 6),   NONE, I2S_1),
+	DS90UX9XX_GPIO(6, "gpio7", GPIO(0x21, 0, 0x6e, 7),   NONE, I2S_1),
+	DS90UX9XX_GPIO(7, "gpio8", GPIO(0x21, 1, 0x6f, 0),   NONE, I2S_1),
+	DS90UX9XX_GPIO(8, "gpio9", GPIO_SIMPLE(0x1a, 0, 1),  NONE, I2S_M),
+};
+
+static const enum ds90ux9xx_function ds90ux925_926_pin_functions[] = {
+	DS90UX9XX_FUNC_GPIO,
+	DS90UX9XX_FUNC_REMOTE,
+	DS90UX9XX_FUNC_I2S_1,
+	DS90UX9XX_FUNC_I2S_2,
+	DS90UX9XX_FUNC_PARALLEL,
+};
+
+static const enum ds90ux9xx_function ds90ux927_pin_functions[] = {
+	DS90UX9XX_FUNC_GPIO,
+	DS90UX9XX_FUNC_REMOTE,
+	DS90UX9XX_FUNC_I2S_1,
+	DS90UX9XX_FUNC_I2S_2,
+	DS90UX9XX_FUNC_I2S_3,
+	DS90UX9XX_FUNC_I2S_4,
+};
+
+static const enum ds90ux9xx_function ds90ux928_pin_functions[] = {
+	DS90UX9XX_FUNC_GPIO,
+	DS90UX9XX_FUNC_REMOTE,
+	DS90UX9XX_FUNC_I2S_1,
+	DS90UX9XX_FUNC_I2S_2,
+	DS90UX9XX_FUNC_I2S_3,
+	DS90UX9XX_FUNC_I2S_4,
+	DS90UX9XX_FUNC_I2S_M,
+};
+
+static const enum ds90ux9xx_function ds90ux940_pin_functions[] = {
+	DS90UX9XX_FUNC_GPIO,
+	DS90UX9XX_FUNC_REMOTE,
+	DS90UX9XX_FUNC_PASS,
+	DS90UX9XX_FUNC_I2S_1,
+	DS90UX9XX_FUNC_I2S_2,
+	DS90UX9XX_FUNC_I2S_3,
+	DS90UX9XX_FUNC_I2S_4,
+	DS90UX9XX_FUNC_I2S_M,
+};
+
+static const struct ds90ux9xx_pinctrl_data ds90ux925_pinctrl = {
+	.name = "ds90ux925",
+	.pins_desc = ds90ux925_926_pins_desc,
+	.pins = ds90ux925_pins,
+	.npins = ARRAY_SIZE(ds90ux925_pins),
+	.functions = ds90ux925_926_pin_functions,
+	.nfunctions = ARRAY_SIZE(ds90ux925_926_pin_functions),
+};
+
+static const struct ds90ux9xx_pinctrl_data ds90ux926_pinctrl = {
+	.name = "ds90ux926",
+	.pins_desc = ds90ux925_926_pins_desc,
+	.pins = ds90ux926_pins,
+	.npins = ARRAY_SIZE(ds90ux926_pins),
+	.functions = ds90ux925_926_pin_functions,
+	.nfunctions = ARRAY_SIZE(ds90ux925_926_pin_functions),
+};
+
+static const struct ds90ux9xx_pinctrl_data ds90ux927_pinctrl = {
+	.name = "ds90ux927",
+	.pins_desc = ds90ux927_928_pins_desc,
+	.pins = ds90ux927_pins,
+	.npins = ARRAY_SIZE(ds90ux927_pins),
+	.functions = ds90ux927_pin_functions,
+	.nfunctions = ARRAY_SIZE(ds90ux927_pin_functions),
+};
+
+static const struct ds90ux9xx_pinctrl_data ds90ux928_pinctrl = {
+	.name = "ds90ux928",
+	.pins_desc = ds90ux927_928_pins_desc,
+	.pins = ds90ux928_pins,
+	.npins = ARRAY_SIZE(ds90ux928_pins),
+	.functions = ds90ux928_pin_functions,
+	.nfunctions = ARRAY_SIZE(ds90ux928_pin_functions),
+};
+
+static const struct ds90ux9xx_pinctrl_data ds90ux940_pinctrl = {
+	.name = "ds90ux940",
+	.pins_desc = ds90ux940_pins_desc,
+	.pins = ds90ux940_pins,
+	.npins = ARRAY_SIZE(ds90ux940_pins),
+	.functions = ds90ux940_pin_functions,
+	.nfunctions = ARRAY_SIZE(ds90ux940_pin_functions),
+};
+
+struct ds90ux9xx_pin_function {
+	enum ds90ux9xx_function id;
+	const char **groups;
+	unsigned int ngroups;
+};
+
+struct ds90ux9xx_pin_group {
+	const char *name;
+	unsigned int pins[DS90UX9XX_MAX_GROUP_PINS];
+	unsigned int npins;
+};
+
+struct ds90ux9xx_pinctrl {
+	struct device *dev;
+	struct regmap *regmap;
+
+	struct pinctrl_dev *pctrl;
+	struct pinctrl_desc desc;
+
+	struct ds90ux9xx_pin_function *functions;
+	unsigned int nfunctions;
+
+	struct ds90ux9xx_pin_group *groups;
+	unsigned int ngroups;
+
+	const struct ds90ux9xx_pin *pins;
+	unsigned int npins;
+
+	struct gpio_chip gpiochip;
+	unsigned int ngpios;
+};
+
+static const char *const ds90ux9xx_func_names[] = {
+	[DS90UX9XX_FUNC_NONE]		= "none",
+	[DS90UX9XX_FUNC_GPIO]		= "gpio",
+	[DS90UX9XX_FUNC_REMOTE]		= "gpio-remote",
+	[DS90UX9XX_FUNC_PASS]		= "pass",
+	[DS90UX9XX_FUNC_I2S_1]		= "i2s-1",
+	[DS90UX9XX_FUNC_I2S_2]		= "i2s-2",
+	[DS90UX9XX_FUNC_I2S_3]		= "i2s-3",
+	[DS90UX9XX_FUNC_I2S_4]		= "i2s-4",
+	[DS90UX9XX_FUNC_I2S_M]		= "i2s-m",
+	[DS90UX9XX_FUNC_PARALLEL]	= "parallel",
+};
+
+static bool ds90ux9xx_func_in_group(u32 func_mask, enum ds90ux9xx_function id)
+{
+	u32 mask = BIT(id);
+
+	if (id == DS90UX9XX_FUNC_I2S_4) {
+		mask |= BIT(DS90UX9XX_FUNC_I2S_3);
+		id = DS90UX9XX_FUNC_I2S_3;
+	}
+
+	if (id == DS90UX9XX_FUNC_I2S_3) {
+		mask |= BIT(DS90UX9XX_FUNC_I2S_2);
+		id = DS90UX9XX_FUNC_I2S_2;
+	}
+
+	if (id == DS90UX9XX_FUNC_I2S_2)
+		mask |= BIT(DS90UX9XX_FUNC_I2S_1);
+
+	return func_mask & mask;
+}
+
+static bool ds90ux9xx_pin_function(enum ds90ux9xx_function id)
+{
+	if (id == DS90UX9XX_FUNC_GPIO || id == DS90UX9XX_FUNC_REMOTE ||
+	    id == DS90UX9XX_FUNC_PASS)
+		return true;
+
+	return false;
+}
+
+static int ds90ux9xx_populate_groups(struct ds90ux9xx_pinctrl *pctrl,
+				     const struct ds90ux9xx_pinctrl_data *cfg)
+{
+	struct ds90ux9xx_pin_function *func;
+	struct ds90ux9xx_pin_group *group;
+	enum ds90ux9xx_function func_id;
+	unsigned int i, j, n;
+
+	pctrl->pins = cfg->pins;
+	pctrl->npins = cfg->npins;
+
+	/* Assert that only GPIO pins are muxed, it may be changed in future */
+	for (j = 0; j < pctrl->npins; j++)
+		if (!(pctrl->pins[j].func_mask & BIT(DS90UX9XX_FUNC_GPIO)))
+			return -EINVAL;
+
+	pctrl->ngpios = pctrl->npins;
+
+	pctrl->nfunctions = cfg->nfunctions;
+	pctrl->functions = devm_kcalloc(pctrl->dev, pctrl->nfunctions,
+					sizeof(*pctrl->functions), GFP_KERNEL);
+	if (!pctrl->functions)
+		return -ENOMEM;
+
+	for (i = 0; i < pctrl->nfunctions; i++) {
+		func = &pctrl->functions[i];
+		func->id = cfg->functions[i];
+
+		/*
+		 * Number of pin groups is a sum of pins and pin group functions
+		 */
+		if (ds90ux9xx_pin_function(func->id)) {
+			for (j = 0; j < pctrl->npins; j++) {
+				if (func->id == DS90UX9XX_FUNC_GPIO)
+					pctrl->ngroups++;
+
+				if (pctrl->pins[j].func_mask & BIT(func->id))
+					func->ngroups++;
+			}
+		} else {
+			pctrl->ngroups++;
+			func->ngroups = 1;
+		}
+
+		func->groups = devm_kcalloc(pctrl->dev, func->ngroups,
+					    sizeof(*func->groups), GFP_KERNEL);
+		if (!func->groups)
+			return -ENOMEM;
+
+		if (ds90ux9xx_pin_function(func->id)) {
+			n = 0;
+			for (j = 0; j < pctrl->npins; j++)
+				if (pctrl->pins[j].func_mask & BIT(func->id))
+					func->groups[n++] = pctrl->pins[j].name;
+		} else {
+			/* Group name matches function name */
+			func->groups[0] = ds90ux9xx_func_names[func->id];
+		}
+	}
+
+	pctrl->groups = devm_kcalloc(pctrl->dev, pctrl->ngroups,
+				     sizeof(*pctrl->groups), GFP_KERNEL);
+	if (!pctrl->groups)
+		return -ENOMEM;
+
+	/* Firstly scan for GPIOs as individual pin groups */
+	group = pctrl->groups;
+	for (i = 0; i < pctrl->npins; i++) {
+		group->name = pctrl->pins[i].name;
+		group->pins[0] = pctrl->pins[i].id;
+		group->npins = 1;
+		group++;
+	}
+
+	/* Now scan for the rest of pin groups, which has own functions */
+	for (i = 0; i < pctrl->nfunctions; i++) {
+		func_id = pctrl->functions[i].id;
+
+		/* Those pin groups were accounted above as pin functions */
+		if (ds90ux9xx_pin_function(func_id))
+			continue;
+
+		group->name = ds90ux9xx_func_names[func_id];
+		for (j = 0; j < pctrl->npins; j++) {
+			if (ds90ux9xx_func_in_group(pctrl->pins[j].func_mask,
+						    func_id)) {
+				group->pins[group->npins] = pctrl->pins[j].id;
+				group->npins++;
+			}
+		}
+
+		group++;
+	}
+
+	return 0;
+}
+
+static int ds90ux9xx_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctrl->ngroups;
+}
+
+static const char *ds90ux9xx_get_group_name(struct pinctrl_dev *pctldev,
+					    unsigned int group)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctrl->groups[group].name;
+}
+
+static int ds90ux9xx_get_group_pins(struct pinctrl_dev *pctldev,
+				    unsigned int group,
+				    const unsigned int **pins,
+				    unsigned int *num_pins)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	*pins = pctrl->groups[group].pins;
+	*num_pins = pctrl->groups[group].npins;
+
+	return 0;
+}
+
+static const struct pinctrl_ops ds90ux9xx_pinctrl_ops = {
+	.get_groups_count	= ds90ux9xx_get_groups_count,
+	.get_group_name		= ds90ux9xx_get_group_name,
+	.get_group_pins		= ds90ux9xx_get_group_pins,
+	.dt_node_to_map		= pinconf_generic_dt_node_to_map_all,
+	.dt_free_map		= pinctrl_utils_free_map,
+};
+
+static int ds90ux9xx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctrl->nfunctions;
+}
+
+static const char *ds90ux9xx_get_func_name(struct pinctrl_dev *pctldev,
+					   unsigned int function)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return ds90ux9xx_func_names[pctrl->functions[function].id];
+}
+
+static int ds90ux9xx_get_func_groups(struct pinctrl_dev *pctldev,
+				     unsigned int function,
+				     const char * const **groups,
+				     unsigned int * const num_groups)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = pctrl->functions[function].groups;
+	*num_groups = pctrl->functions[function].ngroups;
+
+	return 0;
+}
+
+static int ds90ux9xx_gpio_read(struct ds90ux9xx_pinctrl *pctrl,
+			       unsigned int offset, u8 *value)
+{
+	const struct ds90ux9xx_gpio *gpio;
+	unsigned int val;
+	int ret;
+
+	gpio = &pctrl->pins[offset].gpio;
+
+	ret = regmap_read(pctrl->regmap, gpio->cfg_reg, &val);
+	if (ret) {
+		dev_err(pctrl->dev, "Failed to read register %#x, gpio %d\n",
+			gpio->cfg_reg, offset);
+		return ret;
+	}
+
+	*value = val & gpio->cfg_mask;
+	if (gpio->cfg_mask == 0xf0)
+		*value >>= 4;
+
+	return 0;
+}
+
+static int ds90ux9xx_gpio_read_stat(struct ds90ux9xx_pinctrl *pctrl,
+				    unsigned int offset, u8 *value)
+{
+	const struct ds90ux9xx_gpio *gpio;
+	unsigned int val;
+	int ret;
+
+	gpio = &pctrl->pins[offset].gpio;
+
+	if (!gpio->stat_bit)
+		return -EINVAL;
+
+	ret = regmap_read(pctrl->regmap, gpio->stat_reg, &val);
+	if (ret) {
+		dev_err(pctrl->dev, "Failed to read register %#x, gpio %d\n",
+			gpio->stat_reg, offset);
+		return ret;
+	}
+
+	*value = val & gpio->stat_bit;
+
+	return 0;
+}
+
+static int ds90ux9xx_gpio_write(struct ds90ux9xx_pinctrl *pctrl,
+				unsigned int offset, u8 value)
+{
+	const struct ds90ux9xx_gpio *gpio;
+	int ret;
+
+	gpio = &pctrl->pins[offset].gpio;
+
+	if (value & DIR_INPUT && !gpio->input)
+		return -EINVAL;
+
+	if (gpio->cfg_mask == 0xf0)
+		value <<= 4;
+
+	ret = regmap_update_bits(pctrl->regmap, gpio->cfg_reg,
+				 gpio->cfg_mask, value);
+	if (ret)
+		dev_err(pctrl->dev, "Failed to modify register %#x, gpio %d\n",
+			gpio->cfg_reg, offset);
+
+	return ret;
+}
+
+static int ds90ux940_set_pass(struct ds90ux9xx_pinctrl *pctrl,
+			      unsigned int pin, bool enable)
+{
+	u8 bit = pctrl->pins[pin].pass_bit;
+
+	return ds90ux9xx_update_bits_indirect(pctrl->dev->parent,
+			DES_REG_INDIRECT_PASS, bit, enable ? bit : 0x0);
+}
+
+static int ds90ux9xx_pinctrl_group_disable(struct ds90ux9xx_pinctrl *pctrl,
+					   enum ds90ux9xx_function function,
+					   unsigned int pin)
+{
+	int ret;
+
+	switch (function) {
+	case DS90UX9XX_FUNC_GPIO:
+	case DS90UX9XX_FUNC_REMOTE:
+		return ds90ux9xx_gpio_write(pctrl, pin, 0x0);
+	case DS90UX9XX_FUNC_PASS:
+		return ds90ux940_set_pass(pctrl, pin, false);
+	default:
+		break;
+	}
+
+	if (!ds90ux9xx_is_serializer(pctrl->dev->parent))
+		return 0;
+
+	switch (function) {
+	case DS90UX9XX_FUNC_PARALLEL:
+		return regmap_update_bits(pctrl->regmap, SER_REG_PIN_CTRL,
+					  PIN_CTRL_RGB18, PIN_CTRL_RGB18);
+	case DS90UX9XX_FUNC_I2S_4:
+	case DS90UX9XX_FUNC_I2S_3:
+		ret = regmap_update_bits(pctrl->regmap, SER_REG_I2S_SURROUND,
+					 PIN_CTRL_I2S_SURR_BIT, 0x0);
+		if (ret)
+			return ret;
+		/* Fall through */
+	case DS90UX9XX_FUNC_I2S_2:
+		return regmap_update_bits(pctrl->regmap, SER_REG_PIN_CTRL,
+					  PIN_CTRL_I2S_CHANNEL_B, 0x0);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ds90ux9xx_pinctrl_group_enable(struct ds90ux9xx_pinctrl *pctrl,
+					  enum ds90ux9xx_function function,
+					  struct ds90ux9xx_pin_group *group)
+{
+	unsigned int pin = group->pins[0];
+	int ret;
+
+	switch (function) {
+	case DS90UX9XX_FUNC_GPIO:
+		/* Not all GPIOs can be set to input, fallback to output low */
+		ret = ds90ux9xx_gpio_write(pctrl, pin, GPIO_AS_INPUT);
+		if (ret < 0)
+			ret = ds90ux9xx_gpio_write(pctrl, pin, GPIO_OUTPUT_LOW);
+		return ret;
+	case DS90UX9XX_FUNC_REMOTE:
+		return ds90ux9xx_gpio_write(pctrl, pin, GPIO_OUTPUT_REMOTE);
+	case DS90UX9XX_FUNC_PASS:
+		return ds90ux940_set_pass(pctrl, pin, true);
+	default:
+		break;
+	}
+
+	if (!ds90ux9xx_is_serializer(pctrl->dev->parent))
+		return 0;
+
+	switch (function) {
+	case DS90UX9XX_FUNC_PARALLEL:
+		return regmap_update_bits(pctrl->regmap, SER_REG_PIN_CTRL,
+					  PIN_CTRL_RGB18, 0x0);
+	case DS90UX9XX_FUNC_I2S_4:
+	case DS90UX9XX_FUNC_I2S_3:
+		ret = regmap_update_bits(pctrl->regmap, SER_REG_I2S_SURROUND,
+					 PIN_CTRL_I2S_SURR_BIT,
+					 PIN_CTRL_I2S_SURR_BIT);
+		if (ret)
+			return ret;
+		/* Fall through */
+	case DS90UX9XX_FUNC_I2S_2:
+		return regmap_update_bits(pctrl->regmap, SER_REG_PIN_CTRL,
+			PIN_CTRL_I2S_CHANNEL_B | PIN_CTRL_I2S_DATA_ISLAND,
+					  PIN_CTRL_I2S_CHANNEL_B);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ds90ux9xx_pinctrl_func_enable(struct ds90ux9xx_pinctrl *pctrl,
+					 enum ds90ux9xx_function function,
+					 struct ds90ux9xx_pin_group *group)
+{
+	enum ds90ux9xx_function func;
+	unsigned int i, pin;
+	u32 func_mask;
+	int ret;
+
+	/* Disable probably enabled pin functions with pin resource conflicts */
+	for (i = 0; i < group->npins; i++) {
+		pin = group->pins[i];
+
+		func_mask = pctrl->pins[pin].func_mask & ~BIT(function);
+
+		/* Abandon remote GPIOs which are covered by GPIO function */
+		if (function == DS90UX9XX_FUNC_REMOTE)
+			func_mask &= ~BIT(DS90UX9XX_FUNC_GPIO);
+		else
+			func_mask &= ~BIT(DS90UX9XX_FUNC_REMOTE);
+
+		/* Zero to three possible conflicting pin functions */
+		while (func_mask) {
+			func = __ffs(func_mask);
+			func_mask &= ~BIT(func);
+			ret = ds90ux9xx_pinctrl_group_disable(pctrl, func, pin);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ds90ux9xx_pinctrl_group_enable(pctrl, function, group);
+}
+
+static int ds90ux9xx_set_mux(struct pinctrl_dev *pctldev,
+			     unsigned int function, unsigned int group)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+	enum ds90ux9xx_function func = pctrl->functions[function].id;
+	struct ds90ux9xx_pin_group *grp = &pctrl->groups[group];
+
+	return ds90ux9xx_pinctrl_func_enable(pctrl, func, grp);
+}
+
+static int ds90ux9xx_gpio_request_enable(struct pinctrl_dev *pctldev,
+					 struct pinctrl_gpio_range *range,
+					 unsigned int offset)
+{
+	struct ds90ux9xx_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return ds90ux9xx_pinctrl_func_enable(pctrl, DS90UX9XX_FUNC_GPIO,
+					     &pctrl->groups[offset]);
+}
+
+static const struct pinmux_ops ds90ux9xx_pinmux_ops = {
+	.get_functions_count	= ds90ux9xx_get_funcs_count,
+	.get_function_name	= ds90ux9xx_get_func_name,
+	.get_function_groups	= ds90ux9xx_get_func_groups,
+	.set_mux		= ds90ux9xx_set_mux,
+	.gpio_request_enable	= ds90ux9xx_gpio_request_enable,
+	.strict = true,
+};
+
+static const struct pinctrl_desc ds90ux9xx_pinctrl_desc = {
+	.pctlops	= &ds90ux9xx_pinctrl_ops,
+	.pmxops		= &ds90ux9xx_pinmux_ops,
+};
+
+static int ds90ux9xx_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct ds90ux9xx_pinctrl *pctrl = gpiochip_get_data(chip);
+	int ret;
+	u8 val;
+
+	ret = ds90ux9xx_gpio_read(pctrl, offset, &val);
+	if (ret)
+		return ret;
+
+	if (!(val & DIR_INPUT))
+		return !!(val & OUTPUT_HIGH);
+
+	ret = ds90ux9xx_gpio_read_stat(pctrl, offset, &val);
+	if (ret)
+		return ret;
+
+	return !!val;
+}
+
+static void ds90ux9xx_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			       int value)
+{
+	struct ds90ux9xx_pinctrl *pctrl = gpiochip_get_data(chip);
+	u8 val = value ? GPIO_OUTPUT_HIGH : GPIO_OUTPUT_LOW;
+
+	ds90ux9xx_gpio_write(pctrl, offset, val);
+}
+
+static int ds90ux9xx_gpio_get_direction(struct gpio_chip *chip,
+					unsigned int offset)
+{
+	struct ds90ux9xx_pinctrl *pctrl = gpiochip_get_data(chip);
+	int ret;
+	u8 val;
+
+	ret = ds90ux9xx_gpio_read(pctrl, offset, &val);
+	if (ret)
+		return ret;
+
+	return !!(val & DIR_INPUT);
+}
+
+static int ds90ux9xx_gpio_direction_input(struct gpio_chip *chip,
+					  unsigned int offset)
+{
+	struct ds90ux9xx_pinctrl *pctrl = gpiochip_get_data(chip);
+
+	return ds90ux9xx_gpio_write(pctrl, offset, GPIO_AS_INPUT);
+}
+
+static int ds90ux9xx_gpio_direction_output(struct gpio_chip *chip,
+					   unsigned int offset, int value)
+{
+	struct ds90ux9xx_pinctrl *pctrl = gpiochip_get_data(chip);
+	u8 val = value ? GPIO_OUTPUT_HIGH : GPIO_OUTPUT_LOW;
+
+	return ds90ux9xx_gpio_write(pctrl, offset, val);
+}
+
+static const struct gpio_chip ds90ux9xx_gpio_chip = {
+	.owner			= THIS_MODULE,
+	.get			= ds90ux9xx_gpio_get,
+	.set			= ds90ux9xx_gpio_set,
+	.get_direction		= ds90ux9xx_gpio_get_direction,
+	.direction_input	= ds90ux9xx_gpio_direction_input,
+	.direction_output	= ds90ux9xx_gpio_direction_output,
+	.base			= -1,
+	.can_sleep		= 1,
+	.of_gpio_n_cells	= 2,
+	.of_xlate		= of_gpio_simple_xlate,
+};
+
+static int ds90ux9xx_parse_dt_properties(struct ds90ux9xx_pinctrl *pctrl)
+{
+	struct device_node *np = pctrl->dev->of_node;
+	unsigned int val;
+
+	if (!ds90ux9xx_is_serializer(pctrl->dev->parent))
+		return 0;
+
+	/*
+	 * Optionally set "Video Color Depth Mode" to RGB18 mode, it may be
+	 * needed if DS90Ux927 serializer is paired with DS90Ux926 deserializer
+	 * or if there is no pins conflicting with the "parallel" pin group
+	 * to disable RGB24 mode implicitly.
+	 */
+	if (of_property_read_bool(np, "ti,video-depth-18bit"))
+		val = PIN_CTRL_RGB18;
+	else
+		val = 0;
+
+	return regmap_update_bits(pctrl->regmap, SER_REG_PIN_CTRL,
+				  PIN_CTRL_RGB18, val);
+}
+
+static void ds90ux9xx_get_data(struct ds90ux9xx_pinctrl *pctrl,
+			       const struct ds90ux9xx_pinctrl_data **pctrl_data)
+{
+	enum ds90ux9xx_device_id id = ds90ux9xx_get_ic_type(pctrl->dev->parent);
+
+	switch (id) {
+	case TI_DS90UB925:
+	case TI_DS90UH925:
+		*pctrl_data = &ds90ux925_pinctrl;
+		break;
+	case TI_DS90UB927:
+	case TI_DS90UH927:
+		*pctrl_data = &ds90ux927_pinctrl;
+		break;
+	case TI_DS90UB926:
+	case TI_DS90UH926:
+		*pctrl_data = &ds90ux926_pinctrl;
+		break;
+	case TI_DS90UB928:
+	case TI_DS90UH928:
+		*pctrl_data = &ds90ux928_pinctrl;
+		break;
+	case TI_DS90UB940:
+	case TI_DS90UH940:
+		*pctrl_data = &ds90ux940_pinctrl;
+		break;
+	default:
+		dev_err(pctrl->dev, "Unsupported hardware id %d\n", id);
+	}
+}
+
+static int ds90ux9xx_pinctrl_probe(struct platform_device *pdev)
+{
+	const struct ds90ux9xx_pinctrl_data *pctrl_data;
+	struct ds90ux9xx_pinctrl *pctrl;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
+	if (!pctrl)
+		return -ENOMEM;
+
+	pctrl->dev = dev;
+	pctrl->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!pctrl->regmap)
+		return -ENODEV;
+
+	pctrl_data = of_device_get_match_data(dev);
+	if (!pctrl_data)
+		ds90ux9xx_get_data(pctrl, &pctrl_data);
+
+	if (!pctrl_data)
+		return -ENODEV;
+
+	ret = ds90ux9xx_populate_groups(pctrl, pctrl_data);
+	if (ret)
+		return ret;
+
+	ret = ds90ux9xx_parse_dt_properties(pctrl);
+	if (ret)
+		return ret;
+
+	pctrl->desc = ds90ux9xx_pinctrl_desc;
+	pctrl->desc.name = pctrl_data->name;
+	pctrl->desc.pins = pctrl_data->pins_desc;
+	pctrl->desc.npins = pctrl_data->npins;
+
+	pctrl->pctrl = devm_pinctrl_register(dev, &pctrl->desc, pctrl);
+	if (IS_ERR(pctrl->pctrl))
+		return PTR_ERR(pctrl->pctrl);
+
+	platform_set_drvdata(pdev, pctrl);
+
+	pctrl->gpiochip = ds90ux9xx_gpio_chip;
+	pctrl->gpiochip.parent = dev;
+	pctrl->gpiochip.label = pctrl_data->name;
+	pctrl->gpiochip.ngpio = pctrl->ngpios;
+
+	if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
+		pctrl->gpiochip.request = gpiochip_generic_request;
+		pctrl->gpiochip.free = gpiochip_generic_free;
+	}
+
+	return devm_gpiochip_add_data(dev, &pctrl->gpiochip, pctrl);
+}
+
+static const struct of_device_id ds90ux9xx_pinctrl_dt_ids[] = {
+	{ .compatible = "ti,ds90ux9xx-pinctrl", },
+	{ .compatible = "ti,ds90ux925-pinctrl", .data = &ds90ux925_pinctrl, },
+	{ .compatible = "ti,ds90ux926-pinctrl", .data = &ds90ux926_pinctrl, },
+	{ .compatible = "ti,ds90ux927-pinctrl", .data = &ds90ux927_pinctrl, },
+	{ .compatible = "ti,ds90ux928-pinctrl", .data = &ds90ux928_pinctrl, },
+	{ .compatible = "ti,ds90ux940-pinctrl", .data = &ds90ux940_pinctrl, },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ds90ux9xx_pinctrl_dt_ids);
+
+static struct platform_driver ds90ux9xx_pinctrl_driver = {
+	.probe = ds90ux9xx_pinctrl_probe,
+	.driver = {
+		.name = "ds90ux9xx-pinctrl",
+		.of_match_table = ds90ux9xx_pinctrl_dt_ids,
+	},
+};
+module_platform_driver(ds90ux9xx_pinctrl_driver);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@xxxxxxxxxx>");
+MODULE_DESCRIPTION("TI DS90Ux9xx pinmux and GPIO controller driver");
+MODULE_LICENSE("GPL");
-- 
2.17.1




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux