[PATCH v3 5/5] pinctrl: lochnagar: Add support for the Cirrus Logic Lochnagar

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

 



Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.

Lochnagar provides many pins which can generally be used for an
audio function such as an AIF or a PDM interface, but also as
GPIOs.

Signed-off-by: Charles Keepax <ckeepax@xxxxxxxxxxxxxxxxxxxxx>
---

Changes since v2:
 - Updated aif-master/aif-slave to use a vendor prefix
 - Correctly get and put of_node pointer in probe/remove

Thanks,
Charles

 drivers/pinctrl/cirrus/Kconfig             |   10 +
 drivers/pinctrl/cirrus/Makefile            |    2 +
 drivers/pinctrl/cirrus/pinctrl-lochnagar.c | 1167 ++++++++++++++++++++++++++++
 drivers/pinctrl/cirrus/pinctrl-lochnagar.h |  112 +++
 4 files changed, 1291 insertions(+)
 create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.c
 create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.h

diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig
index 27013e5949bc1..74af07e251741 100644
--- a/drivers/pinctrl/cirrus/Kconfig
+++ b/drivers/pinctrl/cirrus/Kconfig
@@ -1,3 +1,13 @@
+config PINCTRL_LOCHNAGAR
+	tristate "Cirrus Logic Lochnagar pinctrl driver"
+	depends on MFD_LOCHNAGAR
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+	help
+	  This driver supports configuring the GPIO and other pin configuration
+	  of the Cirrus Logic Lochnagar audio development board.
+
 # This is all selected by the Madera MFD driver Kconfig options
 config PINCTRL_MADERA
 	tristate
diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile
index 6e4938cde9e30..20baebf438f62 100644
--- a/drivers/pinctrl/cirrus/Makefile
+++ b/drivers/pinctrl/cirrus/Makefile
@@ -1,4 +1,6 @@
 # Cirrus Logic pinctrl drivers
+obj-$(CONFIG_PINCTRL_LOCHNAGAR)	+= pinctrl-lochnagar.o
+
 pinctrl-madera-objs		:= pinctrl-madera-core.o
 ifeq ($(CONFIG_PINCTRL_CS47L35),y)
 pinctrl-madera-objs		+= pinctrl-cs47l35.o
diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.c b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
new file mode 100644
index 0000000000000..d5dc8362cc48d
--- /dev/null
+++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
@@ -0,0 +1,1167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar pin and GPIO control
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ *                         Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <ckeepax@xxxxxxxxxxxxxxxxxxxxx>
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include <linux/mfd/lochnagar.h>
+
+#include "../pinctrl-utils.h"
+#include "pinctrl-lochnagar.h"
+
+#define LN_PIN_GPIO(REV, ID, NAME, REG, SHIFT, INVERT) \
+static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = { \
+	.name = NAME, .type = LN_PTYPE_GPIO, .reg = LOCHNAGAR##REV##_##REG, \
+	.shift = LOCHNAGAR##REV##_##SHIFT##_SHIFT, .invert = INVERT, \
+}
+
+#define LN_PIN_SAIF(REV, ID, NAME) \
+static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = \
+	{ .name = NAME, .type = LN_PTYPE_AIF, }
+
+#define LN_PIN_AIF(REV, ID) \
+	LN_PIN_SAIF(REV, ID##_BCLK,  LN_##ID##_STR"-bclk"); \
+	LN_PIN_SAIF(REV, ID##_LRCLK, LN_##ID##_STR"-lrclk"); \
+	LN_PIN_SAIF(REV, ID##_RXDAT, LN_##ID##_STR"-rxdat"); \
+	LN_PIN_SAIF(REV, ID##_TXDAT, LN_##ID##_STR"-txdat")
+
+#define LN1_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \
+	LN_PIN_GPIO(1, ID, NAME, REG, SHIFT, INVERT)
+
+#define LN1_PIN_MUX(ID, NAME) \
+static const struct lochnagar_pin lochnagar1_##ID##_pin = \
+	{ .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR1_##ID, }
+
+#define LN1_PIN_AIF(ID) LN_PIN_AIF(1, ID)
+
+#define LN2_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \
+	LN_PIN_GPIO(2, ID, NAME, REG, SHIFT, INVERT)
+
+#define LN2_PIN_MUX(ID, NAME) \
+static const struct lochnagar_pin lochnagar2_##ID##_pin = \
+	{ .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR2_GPIO_##ID, }
+
+#define LN2_PIN_AIF(ID) LN_PIN_AIF(2, ID)
+
+#define LN2_PIN_GAI(ID) \
+	LN2_PIN_MUX(ID##_BCLK,  LN_##ID##_STR"-bclk"); \
+	LN2_PIN_MUX(ID##_LRCLK, LN_##ID##_STR"-lrclk"); \
+	LN2_PIN_MUX(ID##_RXDAT, LN_##ID##_STR"-rxdat"); \
+	LN2_PIN_MUX(ID##_TXDAT, LN_##ID##_STR"-txdat")
+
+#define LN_PIN(REV, ID) [LOCHNAGAR##REV##_PIN_##ID] = { \
+	.number = LOCHNAGAR##REV##_PIN_##ID, \
+	.name = lochnagar##REV##_##ID##_pin.name, \
+	.drv_data = (void *)&lochnagar##REV##_##ID##_pin, \
+}
+
+#define LN1_PIN(ID) LN_PIN(1, ID)
+#define LN2_PIN(ID) LN_PIN(2, ID)
+
+#define LN_PINS(REV, ID) \
+	LN_PIN(REV, ID##_BCLK), LN_PIN(REV, ID##_LRCLK), \
+	LN_PIN(REV, ID##_RXDAT), LN_PIN(REV, ID##_TXDAT)
+
+#define LN1_PINS(ID) LN_PINS(1, ID)
+#define LN2_PINS(ID) LN_PINS(2, ID)
+
+enum lochnagar_pin_type {
+	LN_PTYPE_GPIO,
+	LN_PTYPE_MUX,
+	LN_PTYPE_AIF,
+	LN_PTYPE_COUNT,
+};
+
+struct lochnagar_pin {
+	const char name[20];
+
+	enum lochnagar_pin_type type;
+
+	unsigned int reg;
+	int shift;
+	bool invert;
+};
+
+LN1_PIN_GPIO(CDC_RESET,    "codec-reset",    RST,      CDC_RESET,    1);
+LN1_PIN_GPIO(DSP_RESET,    "dsp-reset",      RST,      DSP_RESET,    1);
+LN1_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", I2C_CTRL, CDC_CIF_MODE, 0);
+LN1_PIN_MUX(GF_GPIO2,      "gf-gpio2");
+LN1_PIN_MUX(GF_GPIO3,      "gf-gpio3");
+LN1_PIN_MUX(GF_GPIO7,      "gf-gpio7");
+LN1_PIN_MUX(LED1,          "led1");
+LN1_PIN_MUX(LED2,          "led2");
+LN1_PIN_AIF(CDC_AIF1);
+LN1_PIN_AIF(CDC_AIF2);
+LN1_PIN_AIF(CDC_AIF3);
+LN1_PIN_AIF(DSP_AIF1);
+LN1_PIN_AIF(DSP_AIF2);
+LN1_PIN_AIF(PSIA1);
+LN1_PIN_AIF(PSIA2);
+LN1_PIN_AIF(SPDIF_AIF);
+LN1_PIN_AIF(GF_AIF1);
+LN1_PIN_AIF(GF_AIF2);
+LN1_PIN_AIF(GF_AIF3);
+LN1_PIN_AIF(GF_AIF4);
+
+LN2_PIN_GPIO(CDC_RESET,    "codec-reset",    MINICARD_RESETS, CDC_RESET,     1);
+LN2_PIN_GPIO(DSP_RESET,    "dsp-reset",      MINICARD_RESETS, DSP_RESET,     1);
+LN2_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", COMMS_CTRL4,     CDC_CIF1MODE,  0);
+LN2_PIN_GPIO(CDC_LDOENA,   "codec-ldoena",   POWER_CTRL,      PWR_ENA,       0);
+LN2_PIN_GPIO(SPDIF_HWMODE, "spdif-hwmode",   SPDIF_CTRL,      SPDIF_HWMODE,  0);
+LN2_PIN_GPIO(SPDIF_RESET,  "spdif-reset",    SPDIF_CTRL,      SPDIF_RESET,   1);
+LN2_PIN_MUX(FPGA_GPIO1,    "fgpa-gpio1");
+LN2_PIN_MUX(FPGA_GPIO2,    "fgpa-gpio2");
+LN2_PIN_MUX(FPGA_GPIO3,    "fgpa-gpio3");
+LN2_PIN_MUX(FPGA_GPIO4,    "fgpa-gpio4");
+LN2_PIN_MUX(FPGA_GPIO5,    "fgpa-gpio5");
+LN2_PIN_MUX(FPGA_GPIO6,    "fgpa-gpio6");
+LN2_PIN_MUX(CDC_GPIO1,     "codec-gpio1");
+LN2_PIN_MUX(CDC_GPIO2,     "codec-gpio2");
+LN2_PIN_MUX(CDC_GPIO3,     "codec-gpio3");
+LN2_PIN_MUX(CDC_GPIO4,     "codec-gpio4");
+LN2_PIN_MUX(CDC_GPIO5,     "codec-gpio5");
+LN2_PIN_MUX(CDC_GPIO6,     "codec-gpio6");
+LN2_PIN_MUX(CDC_GPIO7,     "codec-gpio7");
+LN2_PIN_MUX(CDC_GPIO8,     "codec-gpio8");
+LN2_PIN_MUX(DSP_GPIO1,     "dsp-gpio1");
+LN2_PIN_MUX(DSP_GPIO2,     "dsp-gpio2");
+LN2_PIN_MUX(DSP_GPIO3,     "dsp-gpio3");
+LN2_PIN_MUX(DSP_GPIO4,     "dsp-gpio4");
+LN2_PIN_MUX(DSP_GPIO5,     "dsp-gpio5");
+LN2_PIN_MUX(DSP_GPIO6,     "dsp-gpio6");
+LN2_PIN_MUX(GF_GPIO2,      "gf-gpio2");
+LN2_PIN_MUX(GF_GPIO3,      "gf-gpio3");
+LN2_PIN_MUX(GF_GPIO7,      "gf-gpio7");
+LN2_PIN_MUX(DSP_UART1_RX,  "dsp-uart1-rx");
+LN2_PIN_MUX(DSP_UART1_TX,  "dsp-uart1-tx");
+LN2_PIN_MUX(DSP_UART2_RX,  "dsp-uart2-rx");
+LN2_PIN_MUX(DSP_UART2_TX,  "dsp-uart2-tx");
+LN2_PIN_MUX(GF_UART2_RX,   "gf-uart2-rx");
+LN2_PIN_MUX(GF_UART2_TX,   "gf-uart2-tx");
+LN2_PIN_MUX(USB_UART_RX,   "usb-uart-rx");
+LN2_PIN_MUX(CDC_PDMCLK1,   "codec-pdmclk1");
+LN2_PIN_MUX(CDC_PDMDAT1,   "codec-pdmdat1");
+LN2_PIN_MUX(CDC_PDMCLK2,   "codec-pdmclk2");
+LN2_PIN_MUX(CDC_PDMDAT2,   "codec-pdmdat2");
+LN2_PIN_MUX(CDC_DMICCLK1,  "codec-dmicclk1");
+LN2_PIN_MUX(CDC_DMICDAT1,  "codec-dmicdat1");
+LN2_PIN_MUX(CDC_DMICCLK2,  "codec-dmicclk2");
+LN2_PIN_MUX(CDC_DMICDAT2,  "codec-dmicdat2");
+LN2_PIN_MUX(CDC_DMICCLK3,  "codec-dmicclk3");
+LN2_PIN_MUX(CDC_DMICDAT3,  "codec-dmicdat3");
+LN2_PIN_MUX(CDC_DMICCLK4,  "codec-dmicclk4");
+LN2_PIN_MUX(CDC_DMICDAT4,  "codec-dmicdat4");
+LN2_PIN_MUX(DSP_DMICCLK1,  "dsp-dmicclk1");
+LN2_PIN_MUX(DSP_DMICDAT1,  "dsp-dmicdat1");
+LN2_PIN_MUX(DSP_DMICCLK2,  "dsp-dmicclk2");
+LN2_PIN_MUX(DSP_DMICDAT2,  "dsp-dmicdat2");
+LN2_PIN_MUX(I2C2_SCL,      "i2c2-scl");
+LN2_PIN_MUX(I2C2_SDA,      "i2c2-sda");
+LN2_PIN_MUX(I2C3_SCL,      "i2c3-scl");
+LN2_PIN_MUX(I2C3_SDA,      "i2c3-sda");
+LN2_PIN_MUX(I2C4_SCL,      "i2c4-scl");
+LN2_PIN_MUX(I2C4_SDA,      "i2c4-sda");
+LN2_PIN_MUX(DSP_STANDBY,   "dsp-standby");
+LN2_PIN_MUX(CDC_MCLK1,     "codec-mclk1");
+LN2_PIN_MUX(CDC_MCLK2,     "codec-mclk2");
+LN2_PIN_MUX(DSP_CLKIN,     "dsp-clkin");
+LN2_PIN_MUX(PSIA1_MCLK,    "psia1-mclk");
+LN2_PIN_MUX(PSIA2_MCLK,    "psia2-mclk");
+LN2_PIN_MUX(GF_GPIO1,      "gf-gpio1");
+LN2_PIN_MUX(GF_GPIO5,      "gf-gpio5");
+LN2_PIN_MUX(DSP_GPIO20,    "dsp-gpio20");
+LN2_PIN_GAI(CDC_AIF1);
+LN2_PIN_GAI(CDC_AIF2);
+LN2_PIN_GAI(CDC_AIF3);
+LN2_PIN_GAI(DSP_AIF1);
+LN2_PIN_GAI(DSP_AIF2);
+LN2_PIN_GAI(PSIA1);
+LN2_PIN_GAI(PSIA2);
+LN2_PIN_GAI(GF_AIF1);
+LN2_PIN_GAI(GF_AIF2);
+LN2_PIN_GAI(GF_AIF3);
+LN2_PIN_GAI(GF_AIF4);
+LN2_PIN_AIF(SPDIF_AIF);
+LN2_PIN_AIF(USB_AIF1);
+LN2_PIN_AIF(USB_AIF2);
+LN2_PIN_AIF(ADAT_AIF);
+LN2_PIN_AIF(SOUNDCARD_AIF);
+
+static const struct pinctrl_pin_desc lochnagar1_pins[] = {
+	LN1_PIN(CDC_RESET),      LN1_PIN(DSP_RESET),    LN1_PIN(CDC_CIF1MODE),
+	LN1_PIN(GF_GPIO2),       LN1_PIN(GF_GPIO3),     LN1_PIN(GF_GPIO7),
+	LN1_PIN(LED1),           LN1_PIN(LED2),
+	LN1_PINS(CDC_AIF1),      LN1_PINS(CDC_AIF2),    LN1_PINS(CDC_AIF3),
+	LN1_PINS(DSP_AIF1),      LN1_PINS(DSP_AIF2),
+	LN1_PINS(PSIA1),         LN1_PINS(PSIA2),
+	LN1_PINS(SPDIF_AIF),
+	LN1_PINS(GF_AIF1),       LN1_PINS(GF_AIF2),
+	LN1_PINS(GF_AIF3),       LN1_PINS(GF_AIF4),
+};
+
+static const struct pinctrl_pin_desc lochnagar2_pins[] = {
+	LN2_PIN(CDC_RESET),      LN2_PIN(DSP_RESET),    LN2_PIN(CDC_CIF1MODE),
+	LN2_PIN(CDC_LDOENA),
+	LN2_PIN(SPDIF_HWMODE),   LN2_PIN(SPDIF_RESET),
+	LN2_PIN(FPGA_GPIO1),     LN2_PIN(FPGA_GPIO2),   LN2_PIN(FPGA_GPIO3),
+	LN2_PIN(FPGA_GPIO4),     LN2_PIN(FPGA_GPIO5),   LN2_PIN(FPGA_GPIO6),
+	LN2_PIN(CDC_GPIO1),      LN2_PIN(CDC_GPIO2),    LN2_PIN(CDC_GPIO3),
+	LN2_PIN(CDC_GPIO4),      LN2_PIN(CDC_GPIO5),    LN2_PIN(CDC_GPIO6),
+	LN2_PIN(CDC_GPIO7),      LN2_PIN(CDC_GPIO8),
+	LN2_PIN(DSP_GPIO1),      LN2_PIN(DSP_GPIO2),    LN2_PIN(DSP_GPIO3),
+	LN2_PIN(DSP_GPIO4),      LN2_PIN(DSP_GPIO5),    LN2_PIN(DSP_GPIO6),
+	LN2_PIN(DSP_GPIO20),
+	LN2_PIN(GF_GPIO1),       LN2_PIN(GF_GPIO2),     LN2_PIN(GF_GPIO3),
+	LN2_PIN(GF_GPIO5),       LN2_PIN(GF_GPIO7),
+	LN2_PINS(CDC_AIF1),      LN2_PINS(CDC_AIF2),    LN2_PINS(CDC_AIF3),
+	LN2_PINS(DSP_AIF1),      LN2_PINS(DSP_AIF2),
+	LN2_PINS(PSIA1),         LN2_PINS(PSIA2),
+	LN2_PINS(GF_AIF1),       LN2_PINS(GF_AIF2),
+	LN2_PINS(GF_AIF3),       LN2_PINS(GF_AIF4),
+	LN2_PIN(DSP_UART1_RX),   LN2_PIN(DSP_UART1_TX),
+	LN2_PIN(DSP_UART2_RX),   LN2_PIN(DSP_UART2_TX),
+	LN2_PIN(GF_UART2_RX),    LN2_PIN(GF_UART2_TX),
+	LN2_PIN(USB_UART_RX),
+	LN2_PIN(CDC_PDMCLK1),    LN2_PIN(CDC_PDMDAT1),
+	LN2_PIN(CDC_PDMCLK2),    LN2_PIN(CDC_PDMDAT2),
+	LN2_PIN(CDC_DMICCLK1),   LN2_PIN(CDC_DMICDAT1),
+	LN2_PIN(CDC_DMICCLK2),   LN2_PIN(CDC_DMICDAT2),
+	LN2_PIN(CDC_DMICCLK3),   LN2_PIN(CDC_DMICDAT3),
+	LN2_PIN(CDC_DMICCLK4),   LN2_PIN(CDC_DMICDAT4),
+	LN2_PIN(DSP_DMICCLK1),   LN2_PIN(DSP_DMICDAT1),
+	LN2_PIN(DSP_DMICCLK2),   LN2_PIN(DSP_DMICDAT2),
+	LN2_PIN(I2C2_SCL),       LN2_PIN(I2C2_SDA),
+	LN2_PIN(I2C3_SCL),       LN2_PIN(I2C3_SDA),
+	LN2_PIN(I2C4_SCL),       LN2_PIN(I2C4_SDA),
+	LN2_PIN(DSP_STANDBY),
+	LN2_PIN(CDC_MCLK1),      LN2_PIN(CDC_MCLK2),
+	LN2_PIN(DSP_CLKIN),
+	LN2_PIN(PSIA1_MCLK),     LN2_PIN(PSIA2_MCLK),
+	LN2_PINS(SPDIF_AIF),
+	LN2_PINS(USB_AIF1),      LN2_PINS(USB_AIF2),
+	LN2_PINS(ADAT_AIF),
+	LN2_PINS(SOUNDCARD_AIF),
+};
+
+#define LN_AIF_PINS(REV, ID) \
+	LOCHNAGAR##REV##_PIN_##ID##_BCLK, \
+	LOCHNAGAR##REV##_PIN_##ID##_LRCLK, \
+	LOCHNAGAR##REV##_PIN_##ID##_TXDAT, \
+	LOCHNAGAR##REV##_PIN_##ID##_RXDAT,
+
+#define LN1_AIF(ID, CTRL) \
+static const struct lochnagar_aif lochnagar1_##ID##_aif = { \
+	.name = LN_##ID##_STR, \
+	.pins = { LN_AIF_PINS(1, ID) }, \
+	.src_reg = LOCHNAGAR1_##ID##_SEL, \
+	.src_mask = LOCHNAGAR1_SRC_MASK, \
+	.ctrl_reg = LOCHNAGAR1_##CTRL, \
+	.ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
+	.master_mask = LOCHNAGAR1_##ID##_LRCLK_DIR_MASK | \
+		       LOCHNAGAR1_##ID##_BCLK_DIR_MASK, \
+}
+
+#define LN2_AIF(ID) \
+static const struct lochnagar_aif lochnagar2_##ID##_aif = { \
+	.name = LN_##ID##_STR, \
+	.pins = { LN_AIF_PINS(2, ID) }, \
+	.src_reg = LOCHNAGAR2_##ID##_CTRL,  \
+	.src_mask = LOCHNAGAR2_AIF_SRC_MASK, \
+	.ctrl_reg = LOCHNAGAR2_##ID##_CTRL, \
+	.ena_mask = LOCHNAGAR2_AIF_ENA_MASK, \
+	.master_mask = LOCHNAGAR2_AIF_LRCLK_DIR_MASK | \
+		       LOCHNAGAR2_AIF_BCLK_DIR_MASK, \
+}
+
+struct lochnagar_aif {
+	const char name[16];
+
+	unsigned int pins[4];
+
+	unsigned int src_reg;
+	unsigned int src_mask;
+
+	unsigned int ctrl_reg;
+	unsigned int ena_mask;
+	unsigned int master_mask;
+};
+
+LN1_AIF(CDC_AIF1,      CDC_AIF_CTRL1);
+LN1_AIF(CDC_AIF2,      CDC_AIF_CTRL1);
+LN1_AIF(CDC_AIF3,      CDC_AIF_CTRL2);
+LN1_AIF(DSP_AIF1,      DSP_AIF);
+LN1_AIF(DSP_AIF2,      DSP_AIF);
+LN1_AIF(PSIA1,         PSIA_AIF);
+LN1_AIF(PSIA2,         PSIA_AIF);
+LN1_AIF(GF_AIF1,       GF_AIF1);
+LN1_AIF(GF_AIF2,       GF_AIF2);
+LN1_AIF(GF_AIF3,       GF_AIF1);
+LN1_AIF(GF_AIF4,       GF_AIF2);
+LN1_AIF(SPDIF_AIF,     EXT_AIF_CTRL);
+
+LN2_AIF(CDC_AIF1);
+LN2_AIF(CDC_AIF2);
+LN2_AIF(CDC_AIF3);
+LN2_AIF(DSP_AIF1);
+LN2_AIF(DSP_AIF2);
+LN2_AIF(PSIA1);
+LN2_AIF(PSIA2);
+LN2_AIF(GF_AIF1);
+LN2_AIF(GF_AIF2);
+LN2_AIF(GF_AIF3);
+LN2_AIF(GF_AIF4);
+LN2_AIF(SPDIF_AIF);
+LN2_AIF(USB_AIF1);
+LN2_AIF(USB_AIF2);
+LN2_AIF(ADAT_AIF);
+LN2_AIF(SOUNDCARD_AIF);
+
+#define LN2_OP_AIF	0x00
+#define LN2_OP_GPIO	0xFE
+
+#define LN_FUNC(NAME, TYPE, OP) \
+	{ .name = NAME, .type = LN_FTYPE_##TYPE, .op = OP }
+
+#define LN_FUNC_PIN(REV, ID, OP) \
+	LN_FUNC(lochnagar##REV##_##ID##_pin.name, PIN, OP)
+
+#define LN1_FUNC_PIN(ID, OP) LN_FUNC_PIN(1, ID, OP)
+#define LN2_FUNC_PIN(ID, OP) LN_FUNC_PIN(2, ID, OP)
+
+#define LN_FUNC_AIF(REV, ID, OP) \
+	LN_FUNC(lochnagar##REV##_##ID##_aif.name, AIF, OP)
+
+#define LN1_FUNC_AIF(ID, OP) LN_FUNC_AIF(1, ID, OP)
+#define LN2_FUNC_AIF(ID, OP) LN_FUNC_AIF(2, ID, OP)
+
+#define LN2_FUNC_GAI(ID, OP, BOP, LROP, RXOP, TXOP) \
+	LN2_FUNC_AIF(ID, OP), \
+	LN_FUNC(lochnagar2_##ID##_BCLK_pin.name, PIN, BOP), \
+	LN_FUNC(lochnagar2_##ID##_LRCLK_pin.name, PIN, LROP), \
+	LN_FUNC(lochnagar2_##ID##_RXDAT_pin.name, PIN, RXOP), \
+	LN_FUNC(lochnagar2_##ID##_TXDAT_pin.name, PIN, TXOP)
+
+enum lochnagar_func_type {
+	LN_FTYPE_PIN,
+	LN_FTYPE_AIF,
+	LN_FTYPE_COUNT,
+};
+
+struct lochnagar_func {
+	const char * const name;
+
+	enum lochnagar_func_type type;
+
+	int op;
+};
+
+static const struct lochnagar_func lochnagar1_funcs[] = {
+	LN_FUNC("dsp-gpio1",       PIN, 0x01),
+	LN_FUNC("dsp-gpio2",       PIN, 0x02),
+	LN_FUNC("dsp-gpio3",       PIN, 0x03),
+	LN_FUNC("codec-gpio1",     PIN, 0x04),
+	LN_FUNC("codec-gpio2",     PIN, 0x05),
+	LN_FUNC("codec-gpio3",     PIN, 0x06),
+	LN_FUNC("codec-gpio4",     PIN, 0x07),
+	LN_FUNC("codec-gpio5",     PIN, 0x08),
+	LN_FUNC("codec-gpio6",     PIN, 0x09),
+	LN_FUNC("codec-gpio7",     PIN, 0x0A),
+	LN_FUNC("codec-gpio8",     PIN, 0x0B),
+	LN1_FUNC_PIN(GF_GPIO2,          0x0C),
+	LN1_FUNC_PIN(GF_GPIO3,          0x0D),
+	LN1_FUNC_PIN(GF_GPIO7,          0x0E),
+
+	LN1_FUNC_AIF(SPDIF_AIF,         0x01),
+	LN1_FUNC_AIF(PSIA1,             0x02),
+	LN1_FUNC_AIF(PSIA2,             0x03),
+	LN1_FUNC_AIF(CDC_AIF1,          0x04),
+	LN1_FUNC_AIF(CDC_AIF2,          0x05),
+	LN1_FUNC_AIF(CDC_AIF3,          0x06),
+	LN1_FUNC_AIF(DSP_AIF1,          0x07),
+	LN1_FUNC_AIF(DSP_AIF2,          0x08),
+	LN1_FUNC_AIF(GF_AIF3,           0x09),
+	LN1_FUNC_AIF(GF_AIF4,           0x0A),
+	LN1_FUNC_AIF(GF_AIF1,           0x0B),
+	LN1_FUNC_AIF(GF_AIF2,           0x0C),
+};
+
+static const struct lochnagar_func lochnagar2_funcs[] = {
+	LN_FUNC("aif",             PIN, LN2_OP_AIF),
+	LN2_FUNC_PIN(FPGA_GPIO1,        0x01),
+	LN2_FUNC_PIN(FPGA_GPIO2,        0x02),
+	LN2_FUNC_PIN(FPGA_GPIO3,        0x03),
+	LN2_FUNC_PIN(FPGA_GPIO4,        0x04),
+	LN2_FUNC_PIN(FPGA_GPIO5,        0x05),
+	LN2_FUNC_PIN(FPGA_GPIO6,        0x06),
+	LN2_FUNC_PIN(CDC_GPIO1,         0x07),
+	LN2_FUNC_PIN(CDC_GPIO2,         0x08),
+	LN2_FUNC_PIN(CDC_GPIO3,         0x09),
+	LN2_FUNC_PIN(CDC_GPIO4,         0x0A),
+	LN2_FUNC_PIN(CDC_GPIO5,         0x0B),
+	LN2_FUNC_PIN(CDC_GPIO6,         0x0C),
+	LN2_FUNC_PIN(CDC_GPIO7,         0x0D),
+	LN2_FUNC_PIN(CDC_GPIO8,         0x0E),
+	LN2_FUNC_PIN(DSP_GPIO1,         0x0F),
+	LN2_FUNC_PIN(DSP_GPIO2,         0x10),
+	LN2_FUNC_PIN(DSP_GPIO3,         0x11),
+	LN2_FUNC_PIN(DSP_GPIO4,         0x12),
+	LN2_FUNC_PIN(DSP_GPIO5,         0x13),
+	LN2_FUNC_PIN(DSP_GPIO6,         0x14),
+	LN2_FUNC_PIN(GF_GPIO2,          0x15),
+	LN2_FUNC_PIN(GF_GPIO3,          0x16),
+	LN2_FUNC_PIN(GF_GPIO7,          0x17),
+	LN2_FUNC_PIN(GF_GPIO1,          0x18),
+	LN2_FUNC_PIN(GF_GPIO5,          0x19),
+	LN2_FUNC_PIN(DSP_GPIO20,        0x1A),
+	LN_FUNC("codec-clkout",    PIN, 0x20),
+	LN_FUNC("dsp-clkout",      PIN, 0x21),
+	LN_FUNC("pmic-32k",        PIN, 0x22),
+	LN_FUNC("spdif-clkout",    PIN, 0x23),
+	LN_FUNC("clk-12m288",      PIN, 0x24),
+	LN_FUNC("clk-11m2986",     PIN, 0x25),
+	LN_FUNC("clk-24m576",      PIN, 0x26),
+	LN_FUNC("clk-22m5792",     PIN, 0x27),
+	LN_FUNC("xmos-mclk",       PIN, 0x29),
+	LN_FUNC("gf-clkout1",      PIN, 0x2A),
+	LN_FUNC("gf-mclk1",        PIN, 0x2B),
+	LN_FUNC("gf-mclk3",        PIN, 0x2C),
+	LN_FUNC("gf-mclk2",        PIN, 0x2D),
+	LN_FUNC("gf-clkout2",      PIN, 0x2E),
+	LN2_FUNC_PIN(CDC_MCLK1,         0x2F),
+	LN2_FUNC_PIN(CDC_MCLK2,         0x30),
+	LN2_FUNC_PIN(DSP_CLKIN,         0x31),
+	LN2_FUNC_PIN(PSIA1_MCLK,        0x32),
+	LN2_FUNC_PIN(PSIA2_MCLK,        0x33),
+	LN_FUNC("spdif-mclk",      PIN, 0x34),
+	LN_FUNC("codec-irq",       PIN, 0x42),
+	LN2_FUNC_PIN(CDC_RESET,         0x43),
+	LN2_FUNC_PIN(DSP_RESET,         0x44),
+	LN_FUNC("dsp-irq",         PIN, 0x45),
+	LN2_FUNC_PIN(DSP_STANDBY,       0x46),
+	LN2_FUNC_PIN(CDC_PDMCLK1,       0x90),
+	LN2_FUNC_PIN(CDC_PDMDAT1,       0x91),
+	LN2_FUNC_PIN(CDC_PDMCLK2,       0x92),
+	LN2_FUNC_PIN(CDC_PDMDAT2,       0x93),
+	LN2_FUNC_PIN(CDC_DMICCLK1,      0xA0),
+	LN2_FUNC_PIN(CDC_DMICDAT1,      0xA1),
+	LN2_FUNC_PIN(CDC_DMICCLK2,      0xA2),
+	LN2_FUNC_PIN(CDC_DMICDAT2,      0xA3),
+	LN2_FUNC_PIN(CDC_DMICCLK3,      0xA4),
+	LN2_FUNC_PIN(CDC_DMICDAT3,      0xA5),
+	LN2_FUNC_PIN(CDC_DMICCLK4,      0xA6),
+	LN2_FUNC_PIN(CDC_DMICDAT4,      0xA7),
+	LN2_FUNC_PIN(DSP_DMICCLK1,      0xA8),
+	LN2_FUNC_PIN(DSP_DMICDAT1,      0xA9),
+	LN2_FUNC_PIN(DSP_DMICCLK2,      0xAA),
+	LN2_FUNC_PIN(DSP_DMICDAT2,      0xAB),
+	LN2_FUNC_PIN(DSP_UART1_RX,      0xC0),
+	LN2_FUNC_PIN(DSP_UART1_TX,      0xC1),
+	LN2_FUNC_PIN(DSP_UART2_RX,      0xC2),
+	LN2_FUNC_PIN(DSP_UART2_TX,      0xC3),
+	LN2_FUNC_PIN(GF_UART2_RX,       0xC4),
+	LN2_FUNC_PIN(GF_UART2_TX,       0xC5),
+	LN2_FUNC_PIN(USB_UART_RX,       0xC6),
+	LN_FUNC("usb-uart-tx",     PIN, 0xC7),
+	LN2_FUNC_PIN(I2C2_SCL,          0xE0),
+	LN2_FUNC_PIN(I2C2_SDA,          0xE1),
+	LN2_FUNC_PIN(I2C3_SCL,          0xE2),
+	LN2_FUNC_PIN(I2C3_SDA,          0xE3),
+	LN2_FUNC_PIN(I2C4_SCL,          0xE4),
+	LN2_FUNC_PIN(I2C4_SDA,          0xE5),
+	LN_FUNC("gpio",            PIN, LN2_OP_GPIO),
+
+	LN2_FUNC_AIF(SPDIF_AIF,         0x01),
+	LN2_FUNC_GAI(PSIA1,             0x02, 0x50, 0x51, 0x52, 0x53),
+	LN2_FUNC_GAI(PSIA2,             0x03, 0x54, 0x55, 0x56, 0x57),
+	LN2_FUNC_GAI(CDC_AIF1,          0x04, 0x59, 0x5B, 0x5A, 0x58),
+	LN2_FUNC_GAI(CDC_AIF2,          0x05, 0x5D, 0x5F, 0x5E, 0x5C),
+	LN2_FUNC_GAI(CDC_AIF3,          0x06, 0x61, 0x62, 0x63, 0x60),
+	LN2_FUNC_GAI(DSP_AIF1,          0x07, 0x65, 0x67, 0x66, 0x64),
+	LN2_FUNC_GAI(DSP_AIF2,          0x08, 0x69, 0x6B, 0x6A, 0x68),
+	LN2_FUNC_GAI(GF_AIF3,           0x09, 0x6D, 0x6F, 0x6C, 0x6E),
+	LN2_FUNC_GAI(GF_AIF4,           0x0A, 0x71, 0x73, 0x70, 0x72),
+	LN2_FUNC_GAI(GF_AIF1,           0x0B, 0x75, 0x77, 0x74, 0x76),
+	LN2_FUNC_GAI(GF_AIF2,           0x0C, 0x79, 0x7B, 0x78, 0x7A),
+	LN2_FUNC_AIF(USB_AIF1,          0x0D),
+	LN2_FUNC_AIF(USB_AIF2,          0x0E),
+	LN2_FUNC_AIF(ADAT_AIF,          0x0F),
+	LN2_FUNC_AIF(SOUNDCARD_AIF,     0x10),
+};
+
+#define LN_GROUP_PIN(REV, ID) { \
+	.name = lochnagar##REV##_##ID##_pin.name, \
+	.type = LN_FTYPE_PIN, \
+	.pins = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID].number, \
+	.npins = 1, \
+	.priv = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID], \
+}
+
+#define LN_GROUP_AIF(REV, ID) { \
+	.name = lochnagar##REV##_##ID##_aif.name, \
+	.type = LN_FTYPE_AIF, \
+	.pins = lochnagar##REV##_##ID##_aif.pins, \
+	.npins = ARRAY_SIZE(lochnagar##REV##_##ID##_aif.pins), \
+	.priv = &lochnagar##REV##_##ID##_aif, \
+}
+
+#define LN1_GROUP_PIN(ID) LN_GROUP_PIN(1, ID)
+#define LN2_GROUP_PIN(ID) LN_GROUP_PIN(2, ID)
+
+#define LN1_GROUP_AIF(ID) LN_GROUP_AIF(1, ID)
+#define LN2_GROUP_AIF(ID) LN_GROUP_AIF(2, ID)
+
+#define LN2_GROUP_GAI(ID) \
+	LN2_GROUP_AIF(ID), \
+	LN2_GROUP_PIN(ID##_BCLK), LN2_GROUP_PIN(ID##_LRCLK), \
+	LN2_GROUP_PIN(ID##_RXDAT), LN2_GROUP_PIN(ID##_TXDAT)
+
+struct lochnagar_group {
+	const char * const name;
+
+	enum lochnagar_func_type type;
+
+	const unsigned int *pins;
+	int npins;
+
+	const void *priv;
+};
+
+static const struct lochnagar_group lochnagar1_groups[] = {
+	LN1_GROUP_PIN(GF_GPIO2),       LN1_GROUP_PIN(GF_GPIO3),
+	LN1_GROUP_PIN(GF_GPIO7),
+	LN1_GROUP_PIN(LED1),           LN1_GROUP_PIN(LED2),
+	LN1_GROUP_AIF(CDC_AIF1),       LN1_GROUP_AIF(CDC_AIF2),
+	LN1_GROUP_AIF(CDC_AIF3),
+	LN1_GROUP_AIF(DSP_AIF1),       LN1_GROUP_AIF(DSP_AIF2),
+	LN1_GROUP_AIF(PSIA1),          LN1_GROUP_AIF(PSIA2),
+	LN1_GROUP_AIF(GF_AIF1),        LN1_GROUP_AIF(GF_AIF2),
+	LN1_GROUP_AIF(GF_AIF3),        LN1_GROUP_AIF(GF_AIF4),
+	LN1_GROUP_AIF(SPDIF_AIF),
+};
+
+static const struct lochnagar_group lochnagar2_groups[] = {
+	LN2_GROUP_PIN(FPGA_GPIO1),     LN2_GROUP_PIN(FPGA_GPIO2),
+	LN2_GROUP_PIN(FPGA_GPIO3),     LN2_GROUP_PIN(FPGA_GPIO4),
+	LN2_GROUP_PIN(FPGA_GPIO5),     LN2_GROUP_PIN(FPGA_GPIO6),
+	LN2_GROUP_PIN(CDC_GPIO1),      LN2_GROUP_PIN(CDC_GPIO2),
+	LN2_GROUP_PIN(CDC_GPIO3),      LN2_GROUP_PIN(CDC_GPIO4),
+	LN2_GROUP_PIN(CDC_GPIO5),      LN2_GROUP_PIN(CDC_GPIO6),
+	LN2_GROUP_PIN(CDC_GPIO7),      LN2_GROUP_PIN(CDC_GPIO8),
+	LN2_GROUP_PIN(DSP_GPIO1),      LN2_GROUP_PIN(DSP_GPIO2),
+	LN2_GROUP_PIN(DSP_GPIO3),      LN2_GROUP_PIN(DSP_GPIO4),
+	LN2_GROUP_PIN(DSP_GPIO5),      LN2_GROUP_PIN(DSP_GPIO6),
+	LN2_GROUP_PIN(DSP_GPIO20),
+	LN2_GROUP_PIN(GF_GPIO1),
+	LN2_GROUP_PIN(GF_GPIO2),       LN2_GROUP_PIN(GF_GPIO5),
+	LN2_GROUP_PIN(GF_GPIO3),       LN2_GROUP_PIN(GF_GPIO7),
+	LN2_GROUP_PIN(DSP_UART1_RX),   LN2_GROUP_PIN(DSP_UART1_TX),
+	LN2_GROUP_PIN(DSP_UART2_RX),   LN2_GROUP_PIN(DSP_UART2_TX),
+	LN2_GROUP_PIN(GF_UART2_RX),    LN2_GROUP_PIN(GF_UART2_TX),
+	LN2_GROUP_PIN(USB_UART_RX),
+	LN2_GROUP_PIN(CDC_PDMCLK1),    LN2_GROUP_PIN(CDC_PDMDAT1),
+	LN2_GROUP_PIN(CDC_PDMCLK2),    LN2_GROUP_PIN(CDC_PDMDAT2),
+	LN2_GROUP_PIN(CDC_DMICCLK1),   LN2_GROUP_PIN(CDC_DMICDAT1),
+	LN2_GROUP_PIN(CDC_DMICCLK2),   LN2_GROUP_PIN(CDC_DMICDAT2),
+	LN2_GROUP_PIN(CDC_DMICCLK3),   LN2_GROUP_PIN(CDC_DMICDAT3),
+	LN2_GROUP_PIN(CDC_DMICCLK4),   LN2_GROUP_PIN(CDC_DMICDAT4),
+	LN2_GROUP_PIN(DSP_DMICCLK1),   LN2_GROUP_PIN(DSP_DMICDAT1),
+	LN2_GROUP_PIN(DSP_DMICCLK2),   LN2_GROUP_PIN(DSP_DMICDAT2),
+	LN2_GROUP_PIN(I2C2_SCL),       LN2_GROUP_PIN(I2C2_SDA),
+	LN2_GROUP_PIN(I2C3_SCL),       LN2_GROUP_PIN(I2C3_SDA),
+	LN2_GROUP_PIN(I2C4_SCL),       LN2_GROUP_PIN(I2C4_SDA),
+	LN2_GROUP_PIN(DSP_STANDBY),
+	LN2_GROUP_PIN(CDC_MCLK1),      LN2_GROUP_PIN(CDC_MCLK2),
+	LN2_GROUP_PIN(DSP_CLKIN),
+	LN2_GROUP_PIN(PSIA1_MCLK),     LN2_GROUP_PIN(PSIA2_MCLK),
+	LN2_GROUP_GAI(CDC_AIF1),       LN2_GROUP_GAI(CDC_AIF2),
+	LN2_GROUP_GAI(CDC_AIF3),
+	LN2_GROUP_GAI(DSP_AIF1),       LN2_GROUP_GAI(DSP_AIF2),
+	LN2_GROUP_GAI(PSIA1),          LN2_GROUP_GAI(PSIA2),
+	LN2_GROUP_GAI(GF_AIF1),        LN2_GROUP_GAI(GF_AIF2),
+	LN2_GROUP_GAI(GF_AIF3),        LN2_GROUP_GAI(GF_AIF4),
+	LN2_GROUP_AIF(SPDIF_AIF),
+	LN2_GROUP_AIF(USB_AIF1),       LN2_GROUP_AIF(USB_AIF2),
+	LN2_GROUP_AIF(ADAT_AIF),
+	LN2_GROUP_AIF(SOUNDCARD_AIF),
+};
+
+struct lochnagar_func_groups {
+	const char **groups;
+	int ngroups;
+};
+
+struct lochnagar_pin_priv {
+	struct lochnagar *lochnagar;
+	struct device *dev;
+
+	const struct lochnagar_func *funcs;
+	int nfuncs;
+
+	const struct pinctrl_pin_desc *pins;
+	int npins;
+
+	const struct lochnagar_group *groups;
+	int ngroups;
+
+	struct lochnagar_func_groups func_groups[LN_FTYPE_COUNT];
+
+	struct gpio_chip gpio_chip;
+};
+
+static int lochnagar_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+	return priv->ngroups;
+}
+
+static const char *lochnagar_get_group_name(struct pinctrl_dev *pctldev,
+					    unsigned int group_idx)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+	return priv->groups[group_idx].name;
+}
+
+static int lochnagar_get_group_pins(struct pinctrl_dev *pctldev,
+				    unsigned int group_idx,
+				    const unsigned int **pins,
+				    unsigned int *num_pins)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+	*pins = priv->groups[group_idx].pins;
+	*num_pins = priv->groups[group_idx].npins;
+
+	return 0;
+}
+
+static const struct pinctrl_ops lochnagar_pin_group_ops = {
+	.get_groups_count = lochnagar_get_groups_count,
+	.get_group_name = lochnagar_get_group_name,
+	.get_group_pins = lochnagar_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinctrl_utils_free_map,
+};
+
+static int lochnagar_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+	return priv->nfuncs;
+}
+
+static const char *lochnagar_get_func_name(struct pinctrl_dev *pctldev,
+					   unsigned int func_idx)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+	return priv->funcs[func_idx].name;
+}
+
+static int lochnagar_get_func_groups(struct pinctrl_dev *pctldev,
+				     unsigned int func_idx,
+				     const char * const **groups,
+				     unsigned int * const num_groups)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+	int func_type;
+
+	func_type = priv->funcs[func_idx].type;
+
+	*groups = priv->func_groups[func_type].groups;
+	*num_groups = priv->func_groups[func_type].ngroups;
+
+	return 0;
+}
+
+static int lochnagar2_get_gpio_chan(struct lochnagar_pin_priv *priv,
+				    unsigned int op)
+{
+	struct regmap *regmap = priv->lochnagar->regmap;
+	unsigned int val;
+	int free = -1;
+	int i, ret;
+
+	for (i = 0; i < LN2_NUM_GPIO_CHANNELS; i++) {
+		ret = regmap_read(regmap, LOCHNAGAR2_GPIO_CHANNEL1 + i, &val);
+		if (ret)
+			return ret;
+
+		val &= LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK;
+
+		if (val == op)
+			return i + 1;
+
+		if (free < 0 && !val)
+			free = i;
+	}
+
+	if (free >= 0) {
+		ret = regmap_update_bits(regmap,
+					 LOCHNAGAR2_GPIO_CHANNEL1 + free,
+					 LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK, op);
+		if (ret)
+			return ret;
+
+		free++;
+
+		dev_dbg(priv->dev, "Set channel %d to 0x%x\n", free, op);
+
+		return free;
+	}
+
+	return -ENOSPC;
+}
+
+static int lochnagar_pin_set_mux(struct lochnagar_pin_priv *priv,
+				 const struct lochnagar_pin *pin,
+				 unsigned int op)
+{
+	int ret;
+
+	switch (priv->lochnagar->type) {
+	case LOCHNAGAR1:
+		break;
+	default:
+		ret = lochnagar2_get_gpio_chan(priv, op);
+		if (ret < 0) {
+			dev_err(priv->dev, "Failed to get channel for %s: %d\n",
+				pin->name, ret);
+			return ret;
+		}
+
+		op = ret;
+		break;
+	}
+
+	dev_dbg(priv->dev, "Set pin %s to 0x%x\n", pin->name, op);
+
+	ret = regmap_write(priv->lochnagar->regmap, pin->reg, op);
+	if (ret)
+		dev_err(priv->dev, "Failed to set %s mux: %d\n",
+			pin->name, ret);
+
+	return 0;
+}
+
+static int lochnagar_aif_set_mux(struct lochnagar_pin_priv *priv,
+				 const struct lochnagar_group *group,
+				 unsigned int op)
+{
+	struct regmap *regmap = priv->lochnagar->regmap;
+	const struct lochnagar_aif *aif = group->priv;
+	const struct lochnagar_pin *pin;
+	int i, ret;
+
+	ret = regmap_update_bits(regmap, aif->src_reg, aif->src_mask, op);
+	if (ret) {
+		dev_err(priv->dev, "Failed to set %s source: %d\n",
+			group->name, ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(regmap, aif->ctrl_reg,
+				 aif->ena_mask, aif->ena_mask);
+	if (ret) {
+		dev_err(priv->dev, "Failed to set %s enable: %d\n",
+			group->name, ret);
+		return ret;
+	}
+
+	for (i = 0; i < group->npins; i++) {
+		pin = priv->pins[group->pins[i]].drv_data;
+
+		if (pin->type != LN_PTYPE_MUX)
+			continue;
+
+		dev_dbg(priv->dev, "Set pin %s to AIF\n", pin->name);
+
+		ret = regmap_update_bits(regmap, pin->reg,
+					 LOCHNAGAR2_GPIO_SRC_MASK,
+					 LN2_OP_AIF);
+		if (ret) {
+			dev_err(priv->dev, "Failed to set %s to AIF: %d\n",
+				pin->name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int lochnagar_set_mux(struct pinctrl_dev *pctldev,
+			     unsigned int func_idx, unsigned int group_idx)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+	const struct lochnagar_func *func = &priv->funcs[func_idx];
+	const struct lochnagar_group *group = &priv->groups[group_idx];
+	const struct lochnagar_pin *pin;
+
+	switch (func->type) {
+	case LN_FTYPE_AIF:
+		dev_dbg(priv->dev, "Set group %s to %s\n",
+			group->name, func->name);
+
+		return lochnagar_aif_set_mux(priv, group, func->op);
+	case LN_FTYPE_PIN:
+		pin = priv->pins[*group->pins].drv_data;
+
+		dev_dbg(priv->dev, "Set pin %s to %s\n", pin->name, func->name);
+
+		return lochnagar_pin_set_mux(priv, pin, func->op);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int lochnagar_gpio_request(struct pinctrl_dev *pctldev,
+				  struct pinctrl_gpio_range *range,
+				  unsigned int offset)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+	struct lochnagar *lochnagar = priv->lochnagar;
+	const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
+	int ret;
+
+	dev_dbg(priv->dev, "Requesting GPIO %s\n", pin->name);
+
+	if (lochnagar->type == LOCHNAGAR1 || pin->type != LN_PTYPE_MUX)
+		return 0;
+
+	ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO);
+	if (ret < 0) {
+		dev_err(priv->dev, "Failed to get low channel: %d\n", ret);
+		return ret;
+	}
+
+	ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO | 0x1);
+	if (ret < 0) {
+		dev_err(priv->dev, "Failed to get high channel: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lochnagar_gpio_set_direction(struct pinctrl_dev *pctldev,
+					struct pinctrl_gpio_range *range,
+					unsigned int offset,
+					bool input)
+{
+	/* The GPIOs only support output */
+	if (input)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct pinmux_ops lochnagar_pin_mux_ops = {
+	.get_functions_count = lochnagar_get_funcs_count,
+	.get_function_name = lochnagar_get_func_name,
+	.get_function_groups = lochnagar_get_func_groups,
+	.set_mux = lochnagar_set_mux,
+
+	.gpio_request_enable = lochnagar_gpio_request,
+	.gpio_set_direction = lochnagar_gpio_set_direction,
+
+	.strict = true,
+};
+
+enum lochnagar_pin_config_param {
+	PIN_CONFIG_AIFMASTER = PIN_CONFIG_END + 1,
+	PIN_CONFIG_AIFSLAVE,
+};
+
+static int lochnagar_aif_set_master(struct lochnagar_pin_priv *priv,
+				    unsigned int group_idx, bool master)
+{
+	struct regmap *regmap = priv->lochnagar->regmap;
+	const struct lochnagar_group *group = &priv->groups[group_idx];
+	const struct lochnagar_aif *aif = group->priv;
+	unsigned int val = 0;
+	int ret;
+
+	if (group->type != LN_FTYPE_AIF)
+		return -EINVAL;
+
+	if (!master)
+		val = aif->master_mask;
+
+	dev_dbg(priv->dev, "Set AIF %s to %s\n",
+		group->name, master ? "master" : "slave");
+
+	ret = regmap_update_bits(regmap, aif->ctrl_reg, aif->master_mask, val);
+	if (ret) {
+		dev_err(priv->dev, "Failed to set %s mode: %d\n",
+			group->name, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lochnagar_conf_group_set(struct pinctrl_dev *pctldev,
+				    unsigned int group_idx,
+				    unsigned long *configs,
+				    unsigned int num_configs)
+{
+	struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+	int i, ret;
+
+	for (i = 0; i < num_configs; i++) {
+		unsigned int param = pinconf_to_config_param(*configs);
+
+		switch (param) {
+		case PIN_CONFIG_AIFMASTER:
+			ret = lochnagar_aif_set_master(priv, group_idx, true);
+			if (ret)
+				return ret;
+			break;
+		case PIN_CONFIG_AIFSLAVE:
+			ret = lochnagar_aif_set_master(priv, group_idx, false);
+			if (ret)
+				return ret;
+			break;
+		default:
+			return -ENOTSUPP;
+		}
+
+		configs++;
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops lochnagar_pin_conf_ops = {
+	.pin_config_group_set = lochnagar_conf_group_set,
+};
+
+static const struct pinconf_generic_params lochnagar_dt_params[] = {
+	{"cirrus,aif-master", PIN_CONFIG_AIFMASTER,  0},
+	{"cirrus,aif-slave",  PIN_CONFIG_AIFSLAVE,   0},
+};
+
+static const struct pin_config_item lochnagar_conf_items[] = {
+	PCONFDUMP(PIN_CONFIG_AIFMASTER,  "cirrus,aif-master", NULL, false),
+	PCONFDUMP(PIN_CONFIG_AIFSLAVE,   "cirrus,aif-slave",  NULL, false),
+};
+
+static const struct pinctrl_desc lochnagar_pin_desc = {
+	.name = "lochnagar-pinctrl",
+	.owner = THIS_MODULE,
+
+	.pctlops = &lochnagar_pin_group_ops,
+	.pmxops = &lochnagar_pin_mux_ops,
+	.confops = &lochnagar_pin_conf_ops,
+
+	.custom_params = lochnagar_dt_params,
+	.num_custom_params = ARRAY_SIZE(lochnagar_dt_params),
+	.custom_conf_items = lochnagar_conf_items,
+};
+
+static void lochnagar_gpio_set(struct gpio_chip *chip,
+			       unsigned int offset, int value)
+{
+	struct lochnagar_pin_priv *priv = gpiochip_get_data(chip);
+	struct lochnagar *lochnagar = priv->lochnagar;
+	const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
+	int ret;
+
+	value = !!value;
+
+	dev_dbg(priv->dev, "Set GPIO %s to %s\n",
+		pin->name, value ? "high" : "low");
+
+	switch (pin->type) {
+	case LN_PTYPE_MUX:
+		value |= LN2_OP_GPIO;
+
+		ret = lochnagar_pin_set_mux(priv, pin, value);
+		break;
+	case LN_PTYPE_GPIO:
+		if (pin->invert)
+			value = !value;
+
+		ret = regmap_update_bits(lochnagar->regmap, pin->reg,
+					 0x1 << pin->shift,
+					 value << pin->shift);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret < 0)
+		dev_err(chip->parent, "Failed to set %s value: %d\n",
+			pin->name, ret);
+}
+
+static int lochnagar_gpio_direction_out(struct gpio_chip *chip,
+					unsigned int offset, int value)
+{
+	lochnagar_gpio_set(chip, offset, value);
+
+	return pinctrl_gpio_direction_output(chip->base + offset);
+}
+
+static const struct gpio_chip template_chip = {
+	.label = "lochnagar-gpio",
+
+	.request = gpiochip_generic_request,
+	.free = gpiochip_generic_free,
+	.direction_output = lochnagar_gpio_direction_out,
+	.set = lochnagar_gpio_set,
+
+	.can_sleep = true,
+};
+
+static int lochnagar_fill_func_groups(struct lochnagar_pin_priv *priv)
+{
+	struct lochnagar_func_groups *funcs;
+	int i;
+
+	for (i = 0; i < priv->ngroups; i++)
+		priv->func_groups[priv->groups[i].type].ngroups++;
+
+	for (i = 0; i < LN_FTYPE_COUNT; i++) {
+		funcs = &priv->func_groups[i];
+
+		if (!funcs->ngroups)
+			continue;
+
+		funcs->groups = devm_kcalloc(priv->dev, funcs->ngroups,
+					     sizeof(*funcs->groups),
+					     GFP_KERNEL);
+		if (!funcs->groups)
+			return -ENOMEM;
+
+		funcs->ngroups = 0;
+	}
+
+	for (i = 0; i < priv->ngroups; i++) {
+		funcs = &priv->func_groups[priv->groups[i].type];
+
+		funcs->groups[funcs->ngroups++] = priv->groups[i].name;
+	}
+
+	return 0;
+}
+
+static int lochnagar_pin_probe(struct platform_device *pdev)
+{
+	struct lochnagar *lochnagar = dev_get_drvdata(pdev->dev.parent);
+	struct lochnagar_pin_priv *priv;
+	struct pinctrl_desc *desc;
+	struct pinctrl_dev *pctl;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	dev->of_node = of_node_get(lochnagar->dev->of_node);
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	priv->lochnagar = lochnagar;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	*desc = lochnagar_pin_desc;
+
+	priv->gpio_chip = template_chip;
+	priv->gpio_chip.parent = dev;
+	priv->gpio_chip.base = -1;
+#ifdef CONFIG_OF_GPIO
+	priv->gpio_chip.of_node = lochnagar->dev->of_node;
+#endif
+
+	switch (lochnagar->type) {
+	case LOCHNAGAR1:
+		priv->funcs = lochnagar1_funcs;
+		priv->nfuncs = ARRAY_SIZE(lochnagar1_funcs);
+		priv->pins = lochnagar1_pins;
+		priv->npins = ARRAY_SIZE(lochnagar1_pins);
+		priv->groups = lochnagar1_groups;
+		priv->ngroups = ARRAY_SIZE(lochnagar1_groups);
+
+		priv->gpio_chip.ngpio = LOCHNAGAR1_PIN_NUM_GPIOS;
+		break;
+	case LOCHNAGAR2:
+		priv->funcs = lochnagar2_funcs;
+		priv->nfuncs = ARRAY_SIZE(lochnagar2_funcs);
+		priv->pins = lochnagar2_pins;
+		priv->npins = ARRAY_SIZE(lochnagar2_pins);
+		priv->groups = lochnagar2_groups;
+		priv->ngroups = ARRAY_SIZE(lochnagar2_groups);
+
+		priv->gpio_chip.ngpio = LOCHNAGAR2_PIN_NUM_GPIOS;
+		break;
+	default:
+		dev_err(dev, "Unknown Lochnagar type: %d\n", lochnagar->type);
+		return -EINVAL;
+	}
+
+	ret = lochnagar_fill_func_groups(priv);
+	if (ret < 0)
+		return ret;
+
+	desc->pins = priv->pins;
+	desc->npins = priv->npins;
+
+	pctl = devm_pinctrl_register(dev, desc, priv);
+	if (IS_ERR(pctl)) {
+		ret = PTR_ERR(pctl);
+		dev_err(priv->dev, "Failed to register pinctrl: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register gpiochip: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int lochnagar_pin_remove(struct platform_device *pdev)
+{
+	of_node_put(pdev->dev.of_node);
+	pdev->dev.of_node = NULL;
+
+	return 0;
+}
+
+static struct platform_driver lochnagar_pin_driver = {
+	.driver = {
+		.name = "lochnagar-pinctrl",
+	},
+
+	.probe = lochnagar_pin_probe,
+	.remove = lochnagar_pin_remove,
+};
+module_platform_driver(lochnagar_pin_driver);
+
+MODULE_AUTHOR("Charles Keepax <ckeepax@xxxxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Pinctrl driver for Cirrus Logic Lochnagar Board");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-pinctrl");
diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.h b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h
new file mode 100644
index 0000000000000..325ce356a300a
--- /dev/null
+++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Defines for Lochnagar pinctrl
+ *
+ * Copyright (c) 2018 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <ckeepax@xxxxxxxxxxxxxxxxxxxxx>
+ */
+
+#include <dt-bindings/pinctrl/lochnagar.h>
+
+#ifndef PINCTRL_LOCHNAGAR_H
+#define PINCTRL_LOCHNAGAR_H
+
+#define LN2_NUM_GPIO_CHANNELS	16
+
+#define LN_CDC_AIF1_STR		"codec-aif1"
+#define LN_CDC_AIF2_STR		"codec-aif2"
+#define LN_CDC_AIF3_STR		"codec-aif3"
+#define LN_DSP_AIF1_STR		"dsp-aif1"
+#define LN_DSP_AIF2_STR		"dsp-aif2"
+#define LN_PSIA1_STR		"psia1"
+#define LN_PSIA2_STR		"psia2"
+#define LN_GF_AIF1_STR		"gf-aif1"
+#define LN_GF_AIF2_STR		"gf-aif2"
+#define LN_GF_AIF3_STR		"gf-aif3"
+#define LN_GF_AIF4_STR		"gf-aif4"
+#define LN_SPDIF_AIF_STR	"spdif-aif"
+#define LN_USB_AIF1_STR		"usb-aif1"
+#define LN_USB_AIF2_STR		"usb-aif2"
+#define LN_ADAT_AIF_STR		"adat-aif"
+#define LN_SOUNDCARD_AIF_STR	"soundcard-aif"
+
+enum {
+	LOCHNAGAR1_PIN_GF_GPIO2 = LOCHNAGAR1_PIN_NUM_GPIOS,
+	LOCHNAGAR1_PIN_GF_GPIO3,
+	LOCHNAGAR1_PIN_GF_GPIO7,
+	LOCHNAGAR1_PIN_LED1,
+	LOCHNAGAR1_PIN_LED2,
+	LOCHNAGAR1_PIN_CDC_AIF1_BCLK,
+	LOCHNAGAR1_PIN_CDC_AIF1_LRCLK,
+	LOCHNAGAR1_PIN_CDC_AIF1_RXDAT,
+	LOCHNAGAR1_PIN_CDC_AIF1_TXDAT,
+	LOCHNAGAR1_PIN_CDC_AIF2_BCLK,
+	LOCHNAGAR1_PIN_CDC_AIF2_LRCLK,
+	LOCHNAGAR1_PIN_CDC_AIF2_RXDAT,
+	LOCHNAGAR1_PIN_CDC_AIF2_TXDAT,
+	LOCHNAGAR1_PIN_CDC_AIF3_BCLK,
+	LOCHNAGAR1_PIN_CDC_AIF3_LRCLK,
+	LOCHNAGAR1_PIN_CDC_AIF3_RXDAT,
+	LOCHNAGAR1_PIN_CDC_AIF3_TXDAT,
+	LOCHNAGAR1_PIN_DSP_AIF1_BCLK,
+	LOCHNAGAR1_PIN_DSP_AIF1_LRCLK,
+	LOCHNAGAR1_PIN_DSP_AIF1_RXDAT,
+	LOCHNAGAR1_PIN_DSP_AIF1_TXDAT,
+	LOCHNAGAR1_PIN_DSP_AIF2_BCLK,
+	LOCHNAGAR1_PIN_DSP_AIF2_LRCLK,
+	LOCHNAGAR1_PIN_DSP_AIF2_RXDAT,
+	LOCHNAGAR1_PIN_DSP_AIF2_TXDAT,
+	LOCHNAGAR1_PIN_PSIA1_BCLK,
+	LOCHNAGAR1_PIN_PSIA1_LRCLK,
+	LOCHNAGAR1_PIN_PSIA1_RXDAT,
+	LOCHNAGAR1_PIN_PSIA1_TXDAT,
+	LOCHNAGAR1_PIN_PSIA2_BCLK,
+	LOCHNAGAR1_PIN_PSIA2_LRCLK,
+	LOCHNAGAR1_PIN_PSIA2_RXDAT,
+	LOCHNAGAR1_PIN_PSIA2_TXDAT,
+	LOCHNAGAR1_PIN_SPDIF_AIF_BCLK,
+	LOCHNAGAR1_PIN_SPDIF_AIF_LRCLK,
+	LOCHNAGAR1_PIN_SPDIF_AIF_RXDAT,
+	LOCHNAGAR1_PIN_SPDIF_AIF_TXDAT,
+	LOCHNAGAR1_PIN_GF_AIF3_BCLK,
+	LOCHNAGAR1_PIN_GF_AIF3_RXDAT,
+	LOCHNAGAR1_PIN_GF_AIF3_LRCLK,
+	LOCHNAGAR1_PIN_GF_AIF3_TXDAT,
+	LOCHNAGAR1_PIN_GF_AIF4_BCLK,
+	LOCHNAGAR1_PIN_GF_AIF4_RXDAT,
+	LOCHNAGAR1_PIN_GF_AIF4_LRCLK,
+	LOCHNAGAR1_PIN_GF_AIF4_TXDAT,
+	LOCHNAGAR1_PIN_GF_AIF1_BCLK,
+	LOCHNAGAR1_PIN_GF_AIF1_RXDAT,
+	LOCHNAGAR1_PIN_GF_AIF1_LRCLK,
+	LOCHNAGAR1_PIN_GF_AIF1_TXDAT,
+	LOCHNAGAR1_PIN_GF_AIF2_BCLK,
+	LOCHNAGAR1_PIN_GF_AIF2_RXDAT,
+	LOCHNAGAR1_PIN_GF_AIF2_LRCLK,
+	LOCHNAGAR1_PIN_GF_AIF2_TXDAT,
+
+	LOCHNAGAR2_PIN_SPDIF_AIF_BCLK = LOCHNAGAR2_PIN_NUM_GPIOS,
+	LOCHNAGAR2_PIN_SPDIF_AIF_LRCLK,
+	LOCHNAGAR2_PIN_SPDIF_AIF_RXDAT,
+	LOCHNAGAR2_PIN_SPDIF_AIF_TXDAT,
+	LOCHNAGAR2_PIN_USB_AIF1_BCLK,
+	LOCHNAGAR2_PIN_USB_AIF1_LRCLK,
+	LOCHNAGAR2_PIN_USB_AIF1_RXDAT,
+	LOCHNAGAR2_PIN_USB_AIF1_TXDAT,
+	LOCHNAGAR2_PIN_USB_AIF2_BCLK,
+	LOCHNAGAR2_PIN_USB_AIF2_LRCLK,
+	LOCHNAGAR2_PIN_USB_AIF2_RXDAT,
+	LOCHNAGAR2_PIN_USB_AIF2_TXDAT,
+	LOCHNAGAR2_PIN_ADAT_AIF_BCLK,
+	LOCHNAGAR2_PIN_ADAT_AIF_LRCLK,
+	LOCHNAGAR2_PIN_ADAT_AIF_RXDAT,
+	LOCHNAGAR2_PIN_ADAT_AIF_TXDAT,
+	LOCHNAGAR2_PIN_SOUNDCARD_AIF_BCLK,
+	LOCHNAGAR2_PIN_SOUNDCARD_AIF_LRCLK,
+	LOCHNAGAR2_PIN_SOUNDCARD_AIF_RXDAT,
+	LOCHNAGAR2_PIN_SOUNDCARD_AIF_TXDAT,
+};
+
+#endif
-- 
2.11.0




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux