* [PATCH] video: hd44780: Add hd44780 lcd display driver
From: Lars Poeschel @ 2017-12-06 13:52 UTC (permalink / raw)
To: David Airlie, Rob Herring, Mark Rutland,
Bartlomiej Zolnierkiewicz, Manuel Schölling,
Greg Kroah-Hartman, Daniel Vetter, Stafford Horne,
Christophe Leroy, Randy Dunlap, Kate Stewart, Philippe Ombredanne,
Sean Paul, Thomas Gleixner, dri-devel, devicetree, linux-kernel,
linux-fbdev
Cc: Lars Poeschel
This adds a console driver for hd44780 based character lcd displays and
clones. The driver currently supports 20x4 character displays with
character ROMs A00 and A02.
The hardware wirings to the display have to be supplied to the kernel in
the devicetree. The binding doc has the necessary information.
There are also tons of these cheap displays sold with a serial
interface. Many of them use a simple pcf8574 gpio expanders. An example
for using that kind of display is also in the binding doc.
Signed-off-by: Lars Poeschel <poeschel@lemonage.de>
---
.../bindings/video/console/hd44780con.txt | 42 ++
drivers/video/console/Kconfig | 13 +
drivers/video/console/Makefile | 1 +
drivers/video/console/hd44780con.c | 676 +++++++++++++++++++++
4 files changed, 732 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/console/hd44780con.txt
create mode 100644 drivers/video/console/hd44780con.c
diff --git a/Documentation/devicetree/bindings/video/console/hd44780con.txt b/Documentation/devicetree/bindings/video/console/hd44780con.txt
new file mode 100644
index 000000000000..261301eb0c68
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/console/hd44780con.txt
@@ -0,0 +1,42 @@
+Console display driver for Hitachi HD44780 based displays and clones
+
+Required properties:
+- compatible : "hit,hd44780"
+- rs-gpios : GPIO reference for the register select line of the display
+- rw-gpios : GPIO reference for the r/w line of the display
+- e-gpios : GPIO reference for the enable line of the display
+- bl-gpios : GPIO reference for the backlight of the display
+- data-gpios : GPIO reference for the data lines of the display. Currently
+ only the 4 bit mode is supported by the driver. So you have
+ to connect the 4 data lines to DB4 - DB7 of the display.
+- charset-rom : Either "a00" or "a02". The datasheet mentions these two
+ charset roms to be available. Supply the one you have here.
+
+Example:
+
+hd44780con: hd44780@27 {
+ compatible = "hit,hd44780";
+ rs-gpios = <&gpiom27 0 GPIO_ACTIVE_HIGH>;
+ rw-gpios = <&gpiom27 1 GPIO_ACTIVE_HIGH>;
+ e-gpios = <&gpiom27 2 GPIO_ACTIVE_HIGH>;
+ bl-gpios = <&gpiom27 3 GPIO_ACTIVE_HIGH>;
+ data-gpios = <&gpiom27 4 GPIO_ACTIVE_HIGH>,
+ <&gpiom27 5 GPIO_ACTIVE_HIGH>,
+ <&gpiom27 6 GPIO_ACTIVE_HIGH>,
+ <&gpiom27 7 GPIO_ACTIVE_HIGH>;
+ charset-rom = "a00";
+};
+
+
+These hd44780 displays often come with some sort of serial interface. The
+cheap ones often only have a pcf8574 gpio expander from nxp on it. You
+connect to your controller via i2c and gpio expander drives the raw lines
+of your display. Such a display would need a devicetree entry for the
+pcf8574 that looks like this:
+
+gpiom27: gpio27@27 {
+ reg = <0x27>;
+ compatible = "nxp,pcf8574";
+ gpio-controller;
+ #gpio-cells = <2>;
+};
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 7f1f1fbcef9e..204e6ddf1417 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -161,5 +161,18 @@ config STI_CONSOLE
machines. Say Y here to build support for it into your kernel.
The alternative is to use your primary serial port as a console.
+config HD44780_CONSOLE
+ tristate "Hitachi HD44780 20x4 character display as console"
+ depends on GPIOLIB
+ default n
+ help
+ This is a driver that lets you use the cheap lcd 20x4 character
+ display with i2c serial interface using a pcf8574at chip as a
+ console output device. The display is a simple single color
+ character display. It is often referred to as HD44780. To use this
+ driver, you have to connect it to an I2C bus with the lcm1602
+ device. This device contains the pcf8574at chip.
+ This driver only supports the 20x4 character version of the
+ display at the moment.
endmenu
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index db07b784bd2c..1d3df4173280 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o
obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o
obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o
+obj-$(CONFIG_HD44780_CONSOLE) += hd44780con.o
obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
diff --git a/drivers/video/console/hd44780con.c b/drivers/video/console/hd44780con.c
new file mode 100644
index 000000000000..c5195410f3bc
--- /dev/null
+++ b/drivers/video/console/hd44780con.c
@@ -0,0 +1,676 @@
+/*
+ * console driver for hitachi hd44780 20x4 character displays
+ *
+ * This is a driver allowing you to use a HD44780 character lcd display
+ * as console output device. Currently only the 20x4 character version
+ * of the display is supported.
+ * The display is able to work in a 4 bit or a 8 bit mode. This driver
+ * currently only supports 4 bit mode.
+ *
+ * (C) 2017 by Lemonage Software GmbH
+ * Author: Lars Pöschel <poeschel@lemonage.de>
+ * All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/console_struct.h>
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#define HD44780_CMD_CLEAR_DISPLAY 0x01
+#define HD44780_CMD_RETURN_HOME 0x02
+#define HD44780_CMD_ENTRY_MODE_SET 0x04
+#define HD44780_CMD_ON_OFF_CONTROL 0x08
+#define HD44780_CMD_CUR_SHIFT_LEFT 0x10
+#define HD44780_CMD_CUR_SHIFT_RIGHT 0x14
+#define HD44780_CMD_FUNCTION_SET 0x20
+#define HD44780_CMD_DDRAM_ADDR 0x80
+
+#define HD44780_ENTRY_MODE_INC 0x02
+#define HD44780_DISPLAY_SHIFT 2
+#define HD44780_CUR_UL_SHIFT 1
+#define HD44780_CUR_BLINK_SHIFT 0
+
+#define HD44780_FIRST 8
+#define HD44780_LAST 9
+
+#define HD44780_MAX_ROWS 4
+#define HD44780_MAX_COLS 20
+
+struct hd44780_characteristics {
+ uint8_t num_rows;
+ uint8_t num_cols;
+ uint8_t row_to_ddram[];
+};
+
+struct hd44780_data {
+ struct gpio_desc *rs, *rw, *e, *bl;
+ struct gpio_descs *data;
+ uint8_t cur_row;
+ uint8_t cur_col;
+ unsigned int cur_blink:1;
+ unsigned int cur_ul:1;
+ unsigned int display:1;
+ struct hd44780_characteristics const *ch;
+ const uint8_t *cs;
+ unsigned short display_buf[HD44780_MAX_ROWS][HD44780_MAX_COLS];
+};
+
+static const struct hd44780_characteristics ch20x4 = {
+ .num_rows = 4,
+ .num_cols = 20,
+ .row_to_ddram = { 0, 0x40, 20, 0x40 + 20 }
+};
+
+static const uint8_t hd44780_a00_charset[] = {
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0x00 - 0x0f */
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0x7e, 0x7f, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0x10 - 0x1f */
+ /*→ ←*/
+ 0xa1, 0xa1, 0x7e, 0x7f, 0xa1, 0xa1, 0xa1, 0xa1,
+ /* → ←*/
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20 - 0x2f */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30 - 0x3f */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40 - 0x4f */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50 - 0x5f */
+ 0x58, 0x59, 0x5a, 0x5b, 0xa1, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60 - 0x6f */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70 - 0x7f */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0xa1, 0xa1,
+ 0xa1, 0xf5, 0xa1, 0xa1, 0xe1, 0xa1, 0xa1, 0xa1, /* 0x80 - 0x8f */
+ /* ü ä*/
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xef, 0xa1, 0xa1, 0xa1, /* 0x90 - 0x9f */
+ /* ö*/
+ 0xa1, 0xa1, 0xa1, 0xef, 0xa1, 0x5c, 0xa1, 0xa1,
+ /* ¢ ¥*/
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0xa0 - 0xaf */
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0xb0 - 0xbf */
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0xc0 - 0xcf */
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, /* 0xd0 - 0xdf */
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xe0, 0xe2, 0xa1, 0xf7, 0xf6, 0xe5, 0xe6, 0xa1, /* 0xe0 - 0xef */
+ /*α β π ∑ σ μ*/
+ 0xa1, 0xf2, 0xf4, 0xa1, 0xf3, 0xa1, 0xe3, 0xa1,
+ /* θ Ω ∞ ε*/
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xfd, /* 0xf0 - 0xff */
+ /* ÷*/
+ 0xa1, 0xa5, 0x2e, 0xe8, 0xa1, 0xa1, 0xff, 0x20
+ /*∘ ∙ . √*/
+};
+
+static const uint8_t hd44780_a02_charset[] = {
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, /* 0x00 - 0x0f */
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0x10, 0x11, 0xb7, 0xb7, 0x93, 0xa7, 0xb7, 0xb7, /* 0x10 - 0x1f */
+ /*⏵ ⏴ π §←*/
+ 0x18, 0x19, 0x1a, 0x1b, 0xb7, 0xb7, 0x1e, 0x1f,
+ /*↑ ↓ → ← ⏶ ⏷*/
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20 - 0x2f */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30 - 0x3f */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40 - 0x4f */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50 - 0x5f */
+ 0x58, 0x59, 0x5a, 0x5b, 0xa1, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60 - 0x6f */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70 - 0x7f */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 0x80 - 0x8f */
+ /*Ç ü é â ä à å ç*/
+ 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,
+ /*ê ë è ï î ì Ä Å*/
+ 0xc8, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 0x90 - 0x9f */
+ /*È æ Æ ô ö ò û ù*/
+ 0xff, 0xd6, 0xdc, 0xb7, 0xa3, 0xb7, 0xd7, 0xa8,
+ /*ÿ Ö Ü £ × ƒ*/
+ 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xb7, 0xb7 /* 0xa0 - 0xaf */
+ /*á í ó ú ñ Ñ*/
+ 0xbf, 0xae, 0xb7, 0xbd, 0xbc, 0xa1, 0xab, 0xbb,
+ /*¿ ® ½ ¼ ¡ « »*/
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xc1, 0xc2, 0xc0, /* 0xb0 - 0xbf */
+ /* Á Â À*/
+ 0xa9, 0xb7, 0xb7, 0xb7, 0xb7, 0xa2, 0xa5, 0xb7,
+ /*© ¢ ¥*/
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xe3, 0xc3, /* 0xc0 - 0xcf */
+ /* ã Ã*/
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xa4,
+ /* ¤*/
+ 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0xb7, 0xcd, 0xce, /* 0xd0 - 0xdf */
+ /*ð Ð Ê Ë È Í Î*/
+ 0xcf, 0xb7, 0xb7, 0xb7, 0xb7, 0xa6, 0xcc, 0xb7,
+ /*Ï ¦ Ì*/
+ 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xde, /* 0xe0 - 0xef */
+ /*Ó β Ô Ò õ Õ µ Ϸ*/
+ 0xfe, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xb7, 0xaf,
+ /*ϸ Ú Û Ù ý Ý ´*/
+ 0xb7, 0xb7, 0xb7, 0xbe, 0x93, 0xa7, 0xf7, 0xb7, /* 0xf0 - 0xff */
+ /* ¾ π ∮ ÷*/
+ 0xb0, 0xb7, 0xb7, 0xb9, 0xb3, 0xb2, 0xb7, 0x20
+ /*∘ . ¹ ³ ²*/
+};
+
+static struct hd44780_data hd44780;
+
+static void hd44780_fill_gpio_array(uint8_t value, int * const array)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (value & 0x01)
+ array[i] = 1;
+ else
+ array[i] = 0;
+
+ value >>= 1;
+ }
+}
+
+static void hd44780_write_4bit(uint8_t value)
+{
+ int array[4];
+
+ gpiod_set_value_cansleep(hd44780.rw, 0);
+ hd44780_fill_gpio_array(value, array);
+ gpiod_set_array_value_cansleep(4, hd44780.data->desc, array);
+ gpiod_set_value_cansleep(hd44780.e, 1);
+ udelay(30);
+ gpiod_set_value_cansleep(hd44780.e, 0);
+ udelay(120);
+}
+
+static void hd44780_write_4bit_instr(uint8_t value)
+{
+ gpiod_set_value_cansleep(hd44780.rs, 0);
+ hd44780_write_4bit(value);
+}
+
+static void hd44780_write_8bit_instr(uint8_t value)
+{
+ hd44780_write_4bit_instr(value >> 4);
+ hd44780_write_4bit_instr(value);
+}
+
+static void hd44780_write_4bit_data(uint8_t value)
+{
+ gpiod_set_value_cansleep(hd44780.rs, 1);
+ hd44780_write_4bit(value);
+}
+
+static void hd44780_write_8bit_data(uint8_t value)
+{
+ hd44780_write_4bit_data(value >> 4);
+ hd44780_write_4bit_data(value);
+}
+
+static void hd44780_clear_display(void)
+{
+ hd44780_write_8bit_instr(HD44780_CMD_CLEAR_DISPLAY);
+ hd44780.cur_row = 0;
+ hd44780.cur_col = 0;
+ memset(hd44780.display_buf, ' ', sizeof(hd44780.display_buf));
+}
+
+static void hd44780_reset_cursor(void)
+{
+ hd44780_write_8bit_instr(HD44780_CMD_RETURN_HOME);
+ hd44780.cur_row = 0;
+ hd44780.cur_col = 0;
+}
+
+static void hd44780_set_ddram_addr(int row, int col)
+{
+ pr_debug("%s row:%i col:%i\n", __func__, row, col);
+ if ((row < 0) || (col < 0))
+ return;
+
+ if ((row >= hd44780.ch->num_rows) || (col >= hd44780.ch->num_cols))
+ return;
+
+ hd44780_write_8bit_instr(HD44780_CMD_DDRAM_ADDR |
+ (hd44780.ch->row_to_ddram[row] + col));
+}
+
+static void hd44780_set_cursor(int row, int col)
+{
+ pr_debug("%s row:%i, col:%i\n", __func__, row, col);
+ if ((hd44780.cur_row == row) && (hd44780.cur_col == col)) {
+ pr_debug("not setting cursor");
+ return;
+ }
+
+ hd44780_set_ddram_addr(row, col);
+ hd44780.cur_row = row;
+ hd44780.cur_col = col;
+}
+
+static void hd44780_display_on(void)
+{
+ if (!hd44780.display) {
+ hd44780.display = 1;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static void hd44780_display_off(void)
+{
+ if (hd44780.display) {
+ hd44780.display = 0;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static void hd44780_cursor_ul_on(void)
+{
+ if (!hd44780.cur_ul) {
+ hd44780.cur_ul = 1;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static void hd44780_cursor_ul_off(void)
+{
+ if (hd44780.cur_ul) {
+ hd44780.cur_ul = 0;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static void hd44780_cursor_blink_on(void)
+{
+ if (!hd44780.cur_blink) {
+ hd44780.cur_blink = 1;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static void hd44780_cursor_blink_off(void)
+{
+ if (hd44780.cur_blink) {
+ hd44780.cur_blink = 0;
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL |
+ hd44780.display << HD44780_DISPLAY_SHIFT |
+ hd44780.cur_ul << HD44780_CUR_UL_SHIFT |
+ hd44780.cur_blink << HD44780_CUR_BLINK_SHIFT);
+ }
+}
+
+static const char *hd44780_startup(void)
+{
+ return "hd44780 console";
+}
+
+/*
+ * init is set if console is currently allocated during init
+ */
+static void hd44780_init(struct vc_data *con, int init)
+{
+ hd44780.ch = &ch20x4;
+ /* initialisation sequence - set display to 4 bit mode */
+ hd44780_write_4bit_instr(0x03);
+ usleep_range(5000, 20000);
+ hd44780_write_4bit_instr(0x03);
+ usleep_range(100, 1000);
+ hd44780_write_4bit_instr(0x03);
+ hd44780_write_4bit_instr(0x02);
+ /* we are in 4 bit mode now, function set */
+ hd44780_write_8bit_instr(HD44780_CMD_FUNCTION_SET | 0x08);
+ /* display off, cursor off, blinking off */
+ hd44780_write_8bit_instr(HD44780_CMD_ON_OFF_CONTROL);
+ hd44780.display = 0;
+ hd44780.cur_ul = 0;
+ hd44780.cur_blink = 0;
+ /* display clear */
+ hd44780_clear_display();
+ /* entry mode set */
+ hd44780_write_8bit_instr(HD44780_CMD_ENTRY_MODE_SET |
+ HD44780_ENTRY_MODE_INC);
+
+ /* turn backlight on */
+ gpiod_set_value_cansleep(hd44780.bl, 1);
+ hd44780_display_on();
+ hd44780_cursor_ul_on();
+ hd44780_reset_cursor();
+
+ con->vc_can_do_color = 0;
+ con->vc_hi_font_mask = 0;
+
+ if (init) {
+ con->vc_rows = hd44780.ch->num_rows;
+ con->vc_cols = hd44780.ch->num_cols;
+ } else
+ vc_resize(con, hd44780.ch->num_cols, hd44780.ch->num_rows);
+}
+
+static void hd44780_deinit(struct vc_data *con)
+{
+ hd44780_display_off();
+ hd44780_cursor_ul_off();
+ hd44780_cursor_blink_off();
+ gpiod_set_value_cansleep(hd44780.bl, 0);
+}
+
+static void hd44780_increase_cursor(void)
+{
+ hd44780.cur_col++;
+ if (hd44780.cur_col > hd44780.ch->num_cols) {
+ hd44780.cur_col = 0;
+ hd44780.cur_row++;
+ if (hd44780.cur_row > hd44780.ch->num_rows)
+ hd44780.cur_row = 0;
+ }
+}
+
+static void hd44780_putc(struct vc_data *con, int data, int row, int col)
+{
+ pr_debug("%s data:0x%x, row:%i, col:%i\n", __func__, data, row, col);
+ hd44780_set_cursor(row, col);
+ hd44780_write_8bit_data(hd44780.cs[data & 0xff]);
+ hd44780.display_buf[row][col] = data;
+ hd44780_increase_cursor();
+}
+
+static void hd44780_putcs(struct vc_data *con, const unsigned short *buf,
+ int len, int row, int col)
+{
+ int i;
+
+ pr_debug("%s len:%i, row:%i, col:%i\n", __func__, len, row, col);
+ hd44780_set_cursor(row, col);
+ for (i = 0; i < len; i++) {
+ hd44780_write_8bit_data(hd44780.cs[buf[i] & 0xff]);
+ hd44780.display_buf[row][col + i] = buf[i];
+ hd44780_increase_cursor();
+ }
+}
+
+static void hd44780_clear(struct vc_data *con, int s_row, int s_col,
+ int height, int width)
+{
+ unsigned short buf[width];
+ uint8_t i;
+
+ pr_debug("%s\n", __func__);
+ if (width <= 0 || height <= 0)
+ return;
+
+ /* if the whole display is to clear, we have a single command */
+ if (s_col == 0 && s_row == 0 &&
+ height >= con->vc_rows - 1 && width >= con->vc_cols - 1) {
+ hd44780_clear_display();
+ return;
+ }
+
+ memset(buf, ' ', width);
+ for (i = s_col; i <= height; i++)
+ hd44780_putcs(con, buf, width, s_row, i);
+}
+
+static void hd44780_cursor(struct vc_data *con, int mode)
+{
+ pr_debug("%s ", __func__);
+ switch (mode) {
+ case CM_ERASE:
+ pr_debug("CM_ERASE\n");
+ hd44780_cursor_blink_off();
+ hd44780_cursor_ul_off();
+ break;
+ case CM_MOVE:
+ pr_debug("CM_MOVE ");
+ case CM_DRAW:
+ if (mode == CM_DRAW)
+ pr_debug("CM_DRAW ");
+
+ hd44780_set_cursor(con->vc_y, con->vc_x);
+ switch (con->vc_cursor_type & CUR_HWMASK) {
+ case CUR_UNDERLINE:
+ pr_debug("CUR_UNDERLINE\n");
+ hd44780_cursor_ul_on();
+ hd44780_cursor_blink_off();
+ break;
+ case CUR_NONE:
+ pr_debug("CUR_NONE\n");
+ hd44780_cursor_blink_off();
+ hd44780_cursor_ul_off();
+ break;
+ default:
+ pr_debug("default\n");
+ hd44780_cursor_blink_on();
+ hd44780_cursor_ul_off();
+ break;
+ }
+ break;
+ }
+}
+
+static bool hd44780_scroll(struct vc_data *con, unsigned int top,
+ unsigned int bot, enum con_scroll dir, unsigned int lines)
+{
+ uint8_t i;
+
+ pr_debug("%s top:%i bot:%i dir:%i lines:%i\n", __func__,
+ top, bot, dir, lines);
+
+ if (lines >= hd44780.ch->num_rows)
+ memset(hd44780.display_buf, ' ', sizeof(hd44780.display_buf));
+ else
+ switch (dir) {
+ case SM_UP:
+ memmove(&hd44780.display_buf[0][0],
+ &hd44780.display_buf[lines][0],
+ sizeof(hd44780.display_buf[0][0]) *
+ hd44780.ch->num_cols *
+ (hd44780.ch->num_rows - lines));
+ memset(&hd44780.display_buf[
+ hd44780.ch->num_rows - lines][0],
+ ' ',
+ sizeof(hd44780.display_buf[0][0]) *
+ hd44780.ch->num_cols * lines);
+ break;
+ case SM_DOWN:
+ memmove(&hd44780.display_buf[lines][0],
+ &hd44780.display_buf[
+ hd44780.ch->num_rows - lines][0],
+ sizeof(hd44780.display_buf[0][0]) *
+ hd44780.ch->num_cols *
+ (hd44780.ch->num_rows - lines));
+ memset(&hd44780.display_buf[0][0], ' ',
+ sizeof(hd44780.display_buf[0][0]) *
+ hd44780.ch->num_rows * lines);
+ break;
+ }
+
+ for (i = 0; i < hd44780.ch->num_rows; i++)
+ hd44780_putcs(con,
+ (const unsigned short *)&hd44780.display_buf[i][0],
+ hd44780.ch->num_cols, i, 0);
+
+ return true;
+}
+
+static int hd44780_switch(struct vc_data *con)
+{
+ return 1;
+}
+
+static int hd44780_blank(struct vc_data *con, int blank, int mode_switch)
+{
+ switch (blank) {
+ case 0: /* unblank */
+ hd44780_display_on();
+ hd44780_cursor_ul_on();
+ hd44780_cursor_blink_on();
+ break;
+ case 1: /* normal blanking */
+ hd44780_display_off();
+ hd44780_cursor_ul_off();
+ hd44780_cursor_blink_off();
+ break;
+ }
+ return 0;
+}
+
+static const struct consw hd44780_con = {
+ .owner = THIS_MODULE,
+ .con_startup = hd44780_startup,
+ .con_init = hd44780_init,
+ .con_deinit = hd44780_deinit,
+ .con_clear = hd44780_clear,
+ .con_putc = hd44780_putc,
+ .con_putcs = hd44780_putcs,
+ .con_cursor = hd44780_cursor,
+ .con_scroll = hd44780_scroll,
+ .con_switch = hd44780_switch,
+ .con_blank = hd44780_blank,
+};
+
+static const struct of_device_id hd44780_of_match[] = {
+ { .compatible = "hit,hd44780", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hd44780_of_match);
+
+static int hd44780_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dn = dev->of_node;
+ int err;
+ const char *cs;
+
+ hd44780.rs = gpiod_get(dev, "rs", GPIOD_OUT_LOW);
+ if (IS_ERR(hd44780.rs)) {
+ err = PTR_ERR(hd44780.rs);
+ goto fail_rs;
+ }
+
+ hd44780.rw = gpiod_get(dev, "rw", GPIOD_OUT_LOW);
+ if (IS_ERR(hd44780.rw)) {
+ err = PTR_ERR(hd44780.rw);
+ goto fail_rw;
+ }
+
+ hd44780.e = gpiod_get(dev, "e", GPIOD_OUT_LOW);
+ if (IS_ERR(hd44780.e)) {
+ err = PTR_ERR(hd44780.e);
+ goto fail_e;
+ }
+
+ hd44780.bl = gpiod_get(dev, "bl", GPIOD_OUT_HIGH);
+ if (IS_ERR(hd44780.bl)) {
+ err = PTR_ERR(hd44780.bl);
+ goto fail_bl;
+ }
+
+ hd44780.data = gpiod_get_array(dev, "data", GPIOD_OUT_LOW);
+ if (IS_ERR(hd44780.data)) {
+ err = PTR_ERR(hd44780.data);
+ goto fail_data;
+ }
+
+ if (hd44780.data->ndescs != 4) {
+ dev_err(dev, "can only work with 4 data lines (4 bit mode)");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ err = of_property_read_string(dn, "charset-rom", &cs);
+ if (err < 0) {
+ dev_err(dev,
+ "please specify charset-rom for the hd44780 lcd display");
+ goto fail;
+ }
+
+ if (!strcasecmp(cs, "a00")) {
+ hd44780.cs = hd44780_a00_charset;
+ } else if (!strcasecmp(cs, "a02")) {
+ hd44780.cs = hd44780_a02_charset;
+ } else {
+ dev_err(dev,
+ "unknown charset specified for hd44780 lcd display");
+ goto fail;
+ }
+
+ console_lock();
+ do_take_over_console(&hd44780_con, HD44780_FIRST, HD44780_LAST, 1);
+ console_unlock();
+
+ return 0;
+fail:
+ gpiod_put_array(hd44780.data);
+fail_data:
+ gpiod_put(hd44780.bl);
+fail_bl:
+ gpiod_put(hd44780.e);
+fail_e:
+ gpiod_put(hd44780.rw);
+fail_rw:
+ gpiod_put(hd44780.rs);
+fail_rs:
+ return err;
+}
+
+static int hd44780_remove(struct platform_device *pdev)
+{
+ /* unregister from console subsystem */
+ do_unregister_con_driver(&hd44780_con);
+
+ gpiod_put(hd44780.rs);
+ gpiod_put(hd44780.rw);
+ gpiod_put(hd44780.e);
+ gpiod_put(hd44780.bl);
+ gpiod_put_array(hd44780.data);
+ return 0;
+}
+
+static struct platform_driver hd44780_device_driver = {
+ .probe = hd44780_probe,
+ .remove = hd44780_remove,
+ .driver = {
+ .name = "hd44780con",
+ .of_match_table = hd44780_of_match,
+ }
+};
+
+module_platform_driver(hd44780_device_driver);
+
+MODULE_DESCRIPTION("hd44780 character display console driver using gpio");
+MODULE_AUTHOR("Lars Pöschel");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hd44780con");
--
2.15.0
^ permalink raw reply related
* Re: [PATCH v3 2/4] ARM: dts: at91: sama5d2: added dma property for ADC device
From: Eugen Hristev @ 2017-12-06 13:18 UTC (permalink / raw)
To: Alexandre Belloni
Cc: nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
linux-iio-u79uwXL29TY76Z2rM5mHXA, lars-Qo5EllUWu/uELgA04lAiVw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
ludovic.desroches-UWL1GkI3JZL3oGB3hsPCZA,
jic23-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20171129210507.GQ21126-m++hUPXGwpdeoWH0uzbU5w@public.gmane.org>
On 29.11.2017 23:05, Alexandre Belloni wrote:
> Hi,
>
> On 15/11/2017 at 14:56:46 +0200, Eugen Hristev wrote:
>> Added DMA property for ADC device
>>
>> Signed-off-by: Eugen Hristev <eugen.hristev-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org>
>> ---
>> arch/arm/boot/dts/sama5d2.dtsi | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>
> This didn't apply cleanly, please check
> https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git/commit/?h=at91-dt&id=0ae80c039ef2407c08842ddde892af0c9128711b
Hi,
I tested on your tree and looked over the file, and it's all good.
Thanks.
>
>> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
>> index 38d2216..ca8bf13 100644
>> --- a/arch/arm/boot/dts/sama5d2.dtsi
>> +++ b/arch/arm/boot/dts/sama5d2.dtsi
>> @@ -1430,6 +1430,8 @@
>> atmel,min-sample-rate-hz = <200000>;
>> atmel,max-sample-rate-hz = <20000000>;
>> atmel,startup-time-ms = <4>;
>> + dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>;
>> + dma-names = "rx";
>> status = "disabled";
>> };
>>
>> --
>> 2.7.4
>>
>
^ permalink raw reply
* Applied "ASoC: pcm186x: Add initial PCM1862/63/64/65 universal ADC driver" to the asoc tree
From: Mark Brown @ 2017-12-06 13:08 UTC (permalink / raw)
To: Andreas Dannenberg
Cc: Mark Rutland, devicetree, alsa-devel, linux-kernel, Liam Girdwood,
Rob Herring, Andrew F . Davis, Mark Brown, Michael Stecklein
In-Reply-To: <20171205205256.14592-2-afd@ti.com>
The patch
ASoC: pcm186x: Add initial PCM1862/63/64/65 universal ADC driver
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 993a3450712b2a723689b6b6b1a7fe6fe053708e Mon Sep 17 00:00:00 2001
From: Andreas Dannenberg <dannenberg@ti.com>
Date: Tue, 5 Dec 2017 14:52:56 -0600
Subject: [PATCH] ASoC: pcm186x: Add initial PCM1862/63/64/65 universal ADC
driver
This is an initial version of the PCM186x codec driver supporting both
2-channel and 4-channel device variants. Not all device features are
supported yet such as master/slave mode PLL configuration for which the
codec driver currently relies on the PCM186x built-in clock
auto-detection feature or the connection of digital microphones.
However here is what's here and what should work:
- Support for SPI and I2C low-level interfaces
- Regmap support and basic register definitions
- Input Mixer and Mux selection
- I2C, LJ, and TDM DAI format support
Signed-off-by: Andreas Dannenberg <dannenberg@ti.com>
Signed-off-by: Michael Stecklein <m-stecklein@ti.com>
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/Kconfig | 17 +
sound/soc/codecs/Makefile | 6 +
sound/soc/codecs/pcm186x-i2c.c | 69 ++++
sound/soc/codecs/pcm186x-spi.c | 69 ++++
sound/soc/codecs/pcm186x.c | 719 +++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/pcm186x.h | 220 +++++++++++++
6 files changed, 1100 insertions(+)
create mode 100644 sound/soc/codecs/pcm186x-i2c.c
create mode 100644 sound/soc/codecs/pcm186x-spi.c
create mode 100644 sound/soc/codecs/pcm186x.c
create mode 100644 sound/soc/codecs/pcm186x.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a42ddbc93f3d..dda8c01170b3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -109,6 +109,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM179X_I2C if I2C
select SND_SOC_PCM179X_SPI if SPI_MASTER
+ select SND_SOC_PCM186X_I2C if I2C
+ select SND_SOC_PCM186X_SPI if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER
@@ -661,6 +663,21 @@ config SND_SOC_PCM179X_SPI
Enable support for Texas Instruments PCM179x CODEC.
Select this if your PCM179x is connected via an SPI bus.
+config SND_SOC_PCM186X
+ tristate
+
+config SND_SOC_PCM186X_I2C
+ tristate "Texas Instruments PCM186x CODECs - I2C"
+ depends on I2C
+ select SND_SOC_PCM186X
+ select REGMAP_I2C
+
+config SND_SOC_PCM186X_SPI
+ tristate "Texas Instruments PCM186x CODECs - SPI"
+ depends on SPI_MASTER
+ select SND_SOC_PCM186X
+ select REGMAP_SPI
+
config SND_SOC_PCM3008
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0001069ce2a7..146e48a60098 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -105,6 +105,9 @@ snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm179x-codec-objs := pcm179x.o
snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
snd-soc-pcm179x-spi-objs := pcm179x-spi.o
+snd-soc-pcm186x-objs := pcm186x.o
+snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o
+snd-soc-pcm186x-spi-objs := pcm186x-spi.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
@@ -345,6 +348,9 @@ obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o
+obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o
+obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
new file mode 100644
index 000000000000..543621232d60
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - I2C
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static int pcm186x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
+ int irq = i2c->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&i2c->dev, type, irq, regmap);
+}
+
+static int pcm186x_i2c_remove(struct i2c_client *i2c)
+{
+ pcm186x_remove(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id pcm186x_i2c_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
+
+static struct i2c_driver pcm186x_i2c_driver = {
+ .probe = pcm186x_i2c_probe,
+ .remove = pcm186x_i2c_remove,
+ .id_table = pcm186x_i2c_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_i2c_driver(pcm186x_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c
new file mode 100644
index 000000000000..2366f8e4d4d4
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-spi.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC - SPI
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "pcm186x.h"
+
+static const struct of_device_id pcm186x_of_match[] = {
+ { .compatible = "ti,pcm1862", .data = (void *)PCM1862 },
+ { .compatible = "ti,pcm1863", .data = (void *)PCM1863 },
+ { .compatible = "ti,pcm1864", .data = (void *)PCM1864 },
+ { .compatible = "ti,pcm1865", .data = (void *)PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm186x_of_match);
+
+static int pcm186x_spi_probe(struct spi_device *spi)
+{
+ const enum pcm186x_type type =
+ (enum pcm186x_type)spi_get_device_id(spi)->driver_data;
+ int irq = spi->irq;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &pcm186x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return pcm186x_probe(&spi->dev, type, irq, regmap);
+}
+
+static int pcm186x_spi_remove(struct spi_device *spi)
+{
+ pcm186x_remove(&spi->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id pcm186x_spi_id[] = {
+ { "pcm1862", PCM1862 },
+ { "pcm1863", PCM1863 },
+ { "pcm1864", PCM1864 },
+ { "pcm1865", PCM1865 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, pcm186x_spi_id);
+
+static struct spi_driver pcm186x_spi_driver = {
+ .probe = pcm186x_spi_probe,
+ .remove = pcm186x_spi_remove,
+ .id_table = pcm186x_spi_id,
+ .driver = {
+ .name = "pcm186x",
+ .of_match_table = pcm186x_of_match,
+ },
+};
+module_spi_driver(pcm186x_spi_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
new file mode 100644
index 000000000000..f7aa56e20169
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "pcm186x.h"
+
+static const char * const pcm186x_supply_names[] = {
+ "avdd", /* Analog power supply. Connect to 3.3-V supply. */
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */
+};
+#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names)
+
+struct pcm186x_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int tdm_offset;
+ bool is_tdm_mode;
+ bool is_master_mode;
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 4000, 50);
+
+static const struct snd_kcontrol_new pcm1863_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+static const struct snd_kcontrol_new pcm1865_snd_controls[] = {
+ SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L,
+ PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+ SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L,
+ PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0,
+ pcm186x_pga_tlv),
+};
+
+const unsigned int pcm186x_adc_input_channel_sel_value[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x20, 0x30
+};
+
+static const char * const pcm186x_adcl_input_channel_sel_text[] = {
+ "No Select",
+ "VINL1[SE]", /* Default for ADC1L */
+ "VINL2[SE]", /* Default for ADC2L */
+ "VINL2[SE] + VINL1[SE]",
+ "VINL3[SE]",
+ "VINL3[SE] + VINL1[SE]",
+ "VINL3[SE] + VINL2[SE]",
+ "VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE]",
+ "VINL4[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL2[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL1[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE]",
+ "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]",
+ "{VIN1P, VIN1M}[DIFF]",
+ "{VIN4P, VIN4M}[DIFF]",
+ "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]"
+};
+
+static const char * const pcm186x_adcr_input_channel_sel_text[] = {
+ "No Select",
+ "VINR1[SE]", /* Default for ADC1R */
+ "VINR2[SE]", /* Default for ADC2R */
+ "VINR2[SE] + VINR1[SE]",
+ "VINR3[SE]",
+ "VINR3[SE] + VINR1[SE]",
+ "VINR3[SE] + VINR2[SE]",
+ "VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE]",
+ "VINR4[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR2[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR1[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE]",
+ "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]",
+ "{VIN2P, VIN2M}[DIFF]",
+ "{VIN3P, VIN3M}[DIFF]",
+ "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]"
+};
+
+static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+ pcm186x_adcl_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0,
+ PCM186X_ADC_INPUT_SEL_MASK,
+ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+ pcm186x_adcr_input_channel_sel_text,
+ pcm186x_adc_input_channel_sel_value),
+};
+
+static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = {
+ SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]),
+ SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]),
+ SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]),
+ SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]),
+};
+
+static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("VINL1"),
+ SND_SOC_DAPM_INPUT("VINR1"),
+ SND_SOC_DAPM_INPUT("VINL2"),
+ SND_SOC_DAPM_INPUT("VINR2"),
+ SND_SOC_DAPM_INPUT("VINL3"),
+ SND_SOC_DAPM_INPUT("VINR3"),
+ SND_SOC_DAPM_INPUT("VINL4"),
+ SND_SOC_DAPM_INPUT("VINR4"),
+
+ SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[0]),
+ SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[1]),
+ SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[2]),
+ SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &pcm186x_adc_mux_controls[3]),
+
+ /*
+ * Put the codec into SLEEP mode when not in use, allowing the
+ * Energysense mechanism to operate.
+ */
+ SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 0),
+ SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 0),
+};
+
+static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = {
+ { "ADC Left Capture Source", NULL, "VINL1" },
+ { "ADC Left Capture Source", NULL, "VINR1" },
+ { "ADC Left Capture Source", NULL, "VINL2" },
+ { "ADC Left Capture Source", NULL, "VINR2" },
+ { "ADC Left Capture Source", NULL, "VINL3" },
+ { "ADC Left Capture Source", NULL, "VINR3" },
+ { "ADC Left Capture Source", NULL, "VINL4" },
+ { "ADC Left Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Left Capture Source" },
+
+ { "ADC Right Capture Source", NULL, "VINL1" },
+ { "ADC Right Capture Source", NULL, "VINR1" },
+ { "ADC Right Capture Source", NULL, "VINL2" },
+ { "ADC Right Capture Source", NULL, "VINR2" },
+ { "ADC Right Capture Source", NULL, "VINL3" },
+ { "ADC Right Capture Source", NULL, "VINR3" },
+ { "ADC Right Capture Source", NULL, "VINL4" },
+ { "ADC Right Capture Source", NULL, "VINR4" },
+
+ { "ADC", NULL, "ADC Right Capture Source" },
+};
+
+static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = {
+ { "ADC1 Left Capture Source", NULL, "VINL1" },
+ { "ADC1 Left Capture Source", NULL, "VINR1" },
+ { "ADC1 Left Capture Source", NULL, "VINL2" },
+ { "ADC1 Left Capture Source", NULL, "VINR2" },
+ { "ADC1 Left Capture Source", NULL, "VINL3" },
+ { "ADC1 Left Capture Source", NULL, "VINR3" },
+ { "ADC1 Left Capture Source", NULL, "VINL4" },
+ { "ADC1 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Left Capture Source" },
+
+ { "ADC1 Right Capture Source", NULL, "VINL1" },
+ { "ADC1 Right Capture Source", NULL, "VINR1" },
+ { "ADC1 Right Capture Source", NULL, "VINL2" },
+ { "ADC1 Right Capture Source", NULL, "VINR2" },
+ { "ADC1 Right Capture Source", NULL, "VINL3" },
+ { "ADC1 Right Capture Source", NULL, "VINR3" },
+ { "ADC1 Right Capture Source", NULL, "VINL4" },
+ { "ADC1 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC1", NULL, "ADC1 Right Capture Source" },
+
+ { "ADC2 Left Capture Source", NULL, "VINL1" },
+ { "ADC2 Left Capture Source", NULL, "VINR1" },
+ { "ADC2 Left Capture Source", NULL, "VINL2" },
+ { "ADC2 Left Capture Source", NULL, "VINR2" },
+ { "ADC2 Left Capture Source", NULL, "VINL3" },
+ { "ADC2 Left Capture Source", NULL, "VINR3" },
+ { "ADC2 Left Capture Source", NULL, "VINL4" },
+ { "ADC2 Left Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Left Capture Source" },
+
+ { "ADC2 Right Capture Source", NULL, "VINL1" },
+ { "ADC2 Right Capture Source", NULL, "VINR1" },
+ { "ADC2 Right Capture Source", NULL, "VINL2" },
+ { "ADC2 Right Capture Source", NULL, "VINR2" },
+ { "ADC2 Right Capture Source", NULL, "VINL3" },
+ { "ADC2 Right Capture Source", NULL, "VINR3" },
+ { "ADC2 Right Capture Source", NULL, "VINL4" },
+ { "ADC2 Right Capture Source", NULL, "VINR4" },
+
+ { "ADC2", NULL, "ADC2 Right Capture Source" },
+};
+
+static int pcm186x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int rate = params_rate(params);
+ unsigned int format = params_format(params);
+ unsigned int width = params_width(params);
+ unsigned int channels = params_channels(params);
+ unsigned int div_lrck;
+ unsigned int div_bck;
+ u8 tdm_tx_sel = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(codec->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
+ __func__, rate, format, width, channels);
+
+ switch (width) {
+ case 16:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_16 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 20:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_20 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 24:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_24 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ case 32:
+ pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 <<
+ PCM186X_PCM_CFG_RX_WLEN_SHIFT |
+ PCM186X_PCM_CFG_TX_WLEN_32 <<
+ PCM186X_PCM_CFG_TX_WLEN_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_RX_WLEN_MASK |
+ PCM186X_PCM_CFG_TX_WLEN_MASK,
+ pcm_cfg);
+
+ div_lrck = width * channels;
+
+ if (priv->is_tdm_mode) {
+ /* Select TDM transmission data */
+ switch (channels) {
+ case 2:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH;
+ break;
+ case 4:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH;
+ break;
+ case 6:
+ tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, PCM186X_TDM_TX_SEL,
+ PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel);
+
+ /* In DSP/TDM mode, the LRCLK divider must be 256 */
+ div_lrck = 256;
+
+ /* Configure 1/256 duty cycle for LRCK */
+ snd_soc_update_bits(codec, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE,
+ PCM186X_PCM_CFG_TDM_LRCK_MODE);
+ }
+
+ /* Only configure clock dividers in master mode. */
+ if (priv->is_master_mode) {
+ div_bck = priv->sysclk / (div_lrck * rate);
+
+ dev_dbg(codec->dev,
+ "%s() master_clk=%u div_bck=%u div_lrck=%u\n",
+ __func__, priv->sysclk, div_bck, div_lrck);
+
+ snd_soc_write(codec, PCM186X_BCK_DIV, div_bck - 1);
+ snd_soc_write(codec, PCM186X_LRK_DIV, div_lrck - 1);
+ }
+
+ return 0;
+}
+
+static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ u8 clk_ctrl = 0;
+ u8 pcm_cfg = 0;
+
+ dev_dbg(codec->dev, "%s() format=0x%x\n", __func__, format);
+
+ /* set master/slave audio interface */
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (!priv->sysclk) {
+ dev_err(codec->dev, "operating in master mode requires sysclock to be configured\n");
+ return -EINVAL;
+ }
+ clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE;
+ priv->is_master_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ priv->is_master_mode = false;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* set interface polarity */
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(codec->dev, "Inverted DAI clocks not supported\n");
+ return -EINVAL;
+ }
+
+ /* set interface format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ priv->tdm_offset += 1;
+ /* Fall through... DSP_A uses the same basic config as DSP_B
+ * except we need to shift the TDM output by one BCK cycle
+ */
+ case SND_SOC_DAIFMT_DSP_B:
+ priv->is_tdm_mode = true;
+ pcm_cfg = PCM186X_PCM_CFG_FMT_TDM;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, PCM186X_CLK_CTRL,
+ PCM186X_CLK_CTRL_MST_MODE, clk_ctrl);
+
+ snd_soc_write(codec, PCM186X_TDM_TX_OFFSET, priv->tdm_offset);
+
+ snd_soc_update_bits(codec, PCM186X_PCM_CFG,
+ PCM186X_PCM_CFG_FMT_MASK, pcm_cfg);
+
+ return 0;
+}
+
+static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int first_slot, last_slot, tdm_offset;
+
+ dev_dbg(codec->dev,
+ "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (!tx_mask) {
+ dev_err(codec->dev, "tdm tx mask must not be 0\n");
+ return -EINVAL;
+ }
+
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(tx_mask);
+
+ if (last_slot - first_slot != hweight32(tx_mask) - 1) {
+ dev_err(codec->dev, "tdm tx mask must be contiguous\n");
+ return -EINVAL;
+ }
+
+ tdm_offset = first_slot * slot_width;
+
+ if (tdm_offset > 255) {
+ dev_err(codec->dev, "tdm tx slot selection out of bounds\n");
+ return -EINVAL;
+ }
+
+ priv->tdm_offset = tdm_offset;
+
+ return 0;
+}
+
+static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ priv->sysclk = freq;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops pcm186x_dai_ops = {
+ .set_sysclk = pcm186x_set_dai_sysclk,
+ .set_tdm_slot = pcm186x_set_tdm_slot,
+ .set_fmt = pcm186x_set_fmt,
+ .hw_params = pcm186x_hw_params,
+};
+
+static struct snd_soc_dai_driver pcm1863_dai = {
+ .name = "pcm1863-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static struct snd_soc_dai_driver pcm1865_dai = {
+ .name = "pcm1865-aif",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = PCM186X_RATES,
+ .formats = PCM186X_FORMATS,
+ },
+ .ops = &pcm186x_dai_ops,
+};
+
+static int pcm186x_power_on(struct snd_soc_codec *codec)
+{
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(priv->regmap, false);
+ ret = regcache_sync(priv->regmap);
+ if (ret) {
+ dev_err(codec->dev, "Failed to restore cache\n");
+ regcache_cache_only(priv->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ return ret;
+ }
+
+ snd_soc_update_bits(codec, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, 0);
+
+ return 0;
+}
+
+static int pcm186x_power_off(struct snd_soc_codec *codec)
+{
+ struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ snd_soc_update_bits(codec, PCM186X_POWER_CTRL,
+ PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN);
+
+ regcache_cache_only(priv->regmap, true);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int pcm186x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
+ snd_soc_codec_get_bias_level(codec), level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ pcm186x_power_on(codec);
+ break;
+ case SND_SOC_BIAS_OFF:
+ pcm186x_power_off(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1863 = {
+ .set_bias_level = pcm186x_set_bias_level,
+
+ .component_driver = {
+ .controls = pcm1863_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1863_snd_controls),
+ .dapm_widgets = pcm1863_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets),
+ .dapm_routes = pcm1863_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes),
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1865 = {
+ .set_bias_level = pcm186x_set_bias_level,
+ .suspend_bias_off = true,
+
+ .component_driver = {
+ .controls = pcm1865_snd_controls,
+ .num_controls = ARRAY_SIZE(pcm1865_snd_controls),
+ .dapm_widgets = pcm1865_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets),
+ .dapm_routes = pcm1865_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes),
+ },
+};
+
+static bool pcm186x_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PCM186X_PAGE:
+ case PCM186X_DEVICE_STATUS:
+ case PCM186X_FSAMPLE_STATUS:
+ case PCM186X_DIV_STATUS:
+ case PCM186X_CLK_STATUS:
+ case PCM186X_SUPPLY_STATUS:
+ case PCM186X_MMAP_STAT_CTRL:
+ case PCM186X_MMAP_ADDRESS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_range_cfg pcm186x_range = {
+ .name = "Pages",
+ .range_max = PCM186X_MAX_REGISTER,
+ .selector_reg = PCM186X_PAGE,
+ .selector_mask = 0xff,
+ .window_len = PCM186X_PAGE_LEN,
+};
+
+const struct regmap_config pcm186x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .volatile_reg = pcm186x_volatile,
+
+ .ranges = &pcm186x_range,
+ .num_ranges = 1,
+
+ .max_register = PCM186X_MAX_REGISTER,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm186x_regmap);
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap)
+{
+ struct pcm186x_priv *priv;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(priv->supplies); i++)
+ priv->supplies[i].supply = pcm186x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset device registers for a consistent power-on like state */
+ ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET);
+ if (ret) {
+ dev_err(dev, "failed to write device: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret) {
+ dev_err(dev, "failed disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ switch (type) {
+ case PCM1865:
+ case PCM1864:
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1865,
+ &pcm1865_dai, 1);
+ break;
+ case PCM1863:
+ case PCM1862:
+ default:
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1863,
+ &pcm1863_dai, 1);
+ }
+ if (ret) {
+ dev_err(dev, "failed to register CODEC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcm186x_probe);
+
+int pcm186x_remove(struct device *dev)
+{
+ snd_soc_unregister_codec(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pcm186x_remove);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h
new file mode 100644
index 000000000000..b630111bb3c4
--- /dev/null
+++ b/sound/soc/codecs/pcm186x.h
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments PCM186x Universal Audio ADC
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com
+ * Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef _PCM186X_H_
+#define _PCM186X_H_
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+enum pcm186x_type {
+ PCM1862,
+ PCM1863,
+ PCM1864,
+ PCM1865,
+};
+
+#define PCM186X_RATES SNDRV_PCM_RATE_8000_192000
+#define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM186X_PAGE_LEN 0x0100
+#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n)
+
+/* The page selection register address is the same on all pages */
+#define PCM186X_PAGE 0
+
+/* Register Definitions - Page 0 */
+#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1)
+#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2)
+#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3)
+#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4)
+#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5)
+#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6)
+#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7)
+#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8)
+#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9)
+#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10)
+#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11)
+#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12)
+#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13)
+#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14)
+#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15)
+#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16)
+#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17)
+#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18)
+#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19)
+#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20)
+#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21)
+#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22)
+#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23)
+#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24)
+#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25)
+#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26)
+#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27)
+#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32)
+#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33)
+#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34)
+#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35)
+#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37)
+#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38)
+#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39)
+#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40)
+#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41)
+#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42)
+#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43)
+#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44)
+#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45)
+#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48)
+#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49)
+#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50)
+#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52)
+#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53)
+#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54)
+#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64)
+#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65)
+#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66)
+#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67)
+#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68)
+#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69)
+#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70)
+#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71)
+#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72)
+#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73)
+#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74)
+#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75)
+#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76)
+#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77)
+#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78)
+#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79)
+#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80)
+#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81)
+#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82)
+#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83)
+#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84)
+#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85)
+#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86)
+#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87)
+#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88)
+#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89)
+#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90)
+#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96)
+#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97)
+#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98)
+#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112)
+#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113)
+#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114)
+#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115)
+#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116)
+#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117)
+#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120)
+
+/* Register Definitions - Page 1 */
+#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1)
+#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2)
+#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4)
+#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5)
+#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6)
+#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7)
+#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8)
+#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9)
+#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10)
+#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11)
+
+/* Register Definitions - Page 3 */
+#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18)
+#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21)
+
+/* Register Definitions - Page 253 */
+#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20)
+
+#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL
+
+/* PCM186X_PAGE */
+#define PCM186X_RESET 0xff
+
+/* PCM186X_ADCX_INPUT_SEL_X */
+#define PCM186X_ADC_INPUT_SEL_POL BIT(7)
+#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0)
+
+/* PCM186X_PCM_CFG */
+#define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6)
+#define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6
+#define PCM186X_PCM_CFG_RX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_RX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_RX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_RX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4)
+#define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2)
+#define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2
+#define PCM186X_PCM_CFG_TX_WLEN_32 0x00
+#define PCM186X_PCM_CFG_TX_WLEN_24 0x01
+#define PCM186X_PCM_CFG_TX_WLEN_20 0x02
+#define PCM186X_PCM_CFG_TX_WLEN_16 0x03
+#define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0)
+#define PCM186X_PCM_CFG_FMT_SHIFT 0
+#define PCM186X_PCM_CFG_FMT_I2S 0x00
+#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01
+#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02
+#define PCM186X_PCM_CFG_FMT_TDM 0x03
+
+/* PCM186X_TDM_TX_SEL */
+#define PCM186X_TDM_TX_SEL_2CH 0x00
+#define PCM186X_TDM_TX_SEL_4CH 0x01
+#define PCM186X_TDM_TX_SEL_6CH 0x02
+#define PCM186X_TDM_TX_SEL_MASK 0x03
+
+/* PCM186X_CLK_CTRL */
+#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7)
+#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6)
+#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5)
+#define PCM186X_CLK_CTRL_MST_MODE BIT(4)
+#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3)
+#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2)
+#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1)
+#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0)
+
+/* PCM186X_PLL_CTRL */
+#define PCM186X_PLL_CTRL_LOCK BIT(4)
+#define PCM186X_PLL_CTRL_REF_SEL BIT(1)
+#define PCM186X_PLL_CTRL_EN BIT(0)
+
+/* PCM186X_POWER_CTRL */
+#define PCM186X_PWR_CTRL_PWRDN BIT(2)
+#define PCM186X_PWR_CTRL_SLEEP BIT(1)
+#define PCM186X_PWR_CTRL_STBY BIT(0)
+
+/* PCM186X_CLK_STATUS */
+#define PCM186X_CLK_STATUS_LRCKHLT BIT(6)
+#define PCM186X_CLK_STATUS_BCKHLT BIT(5)
+#define PCM186X_CLK_STATUS_SCKHLT BIT(4)
+#define PCM186X_CLK_STATUS_LRCKERR BIT(2)
+#define PCM186X_CLK_STATUS_BCKERR BIT(1)
+#define PCM186X_CLK_STATUS_SCKERR BIT(0)
+
+/* PCM186X_SUPPLY_STATUS */
+#define PCM186X_SUPPLY_STATUS_DVDD BIT(2)
+#define PCM186X_SUPPLY_STATUS_AVDD BIT(1)
+#define PCM186X_SUPPLY_STATUS_LDO BIT(0)
+
+/* PCM186X_MMAP_STAT_CTRL */
+#define PCM186X_MMAP_STAT_DONE BIT(4)
+#define PCM186X_MMAP_STAT_BUSY BIT(2)
+#define PCM186X_MMAP_STAT_R_REQ BIT(1)
+#define PCM186X_MMAP_STAT_W_REQ BIT(0)
+
+extern const struct regmap_config pcm186x_regmap;
+
+int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq,
+ struct regmap *regmap);
+int pcm186x_remove(struct device *dev);
+
+#endif /* _PCM186X_H_ */
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: pcm186x: Add PCM186x binding documentation" to the asoc tree
From: Mark Brown @ 2017-12-06 13:07 UTC (permalink / raw)
Cc: Mark Rutland, Rob Herring, alsa-devel, devicetree, Liam Girdwood,
Rob Herring, linux-kernel, Mark Brown, Andrew F . Davis
In-Reply-To: <20171129185015.5304-1-afd@ti.com>
The patch
ASoC: pcm186x: Add PCM186x binding documentation
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From eb2a8168b9fd69f66199d9d7e86d23fecfab4e33 Mon Sep 17 00:00:00 2001
From: "Andrew F. Davis" <afd@ti.com>
Date: Tue, 5 Dec 2017 14:52:55 -0600
Subject: [PATCH] ASoC: pcm186x: Add PCM186x binding documentation
Add the dt-binding documentation for the TI PCM186x 2ch and 4ch Audio
ADCs With Universal Front End.
Signed-off-by: Andrew F. Davis <afd@ti.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
.../devicetree/bindings/sound/pcm186x.txt | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/pcm186x.txt
diff --git a/Documentation/devicetree/bindings/sound/pcm186x.txt b/Documentation/devicetree/bindings/sound/pcm186x.txt
new file mode 100644
index 000000000000..1087f4855980
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/pcm186x.txt
@@ -0,0 +1,42 @@
+Texas Instruments PCM186x Universal Audio ADC
+
+These devices support both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+ - compatible : "ti,pcm1862",
+ "ti,pcm1863",
+ "ti,pcm1864",
+ "ti,pcm1865"
+
+ - reg : The I2C address of the device for I2C, the chip select
+ number for SPI.
+
+ - avdd-supply: Analog core power supply (3.3v)
+ - dvdd-supply: Digital core power supply
+ - iovdd-supply: Digital IO power supply
+ See regulator/regulator.txt for more information
+
+CODEC input pins:
+ * VINL1
+ * VINR1
+ * VINL2
+ * VINR2
+ * VINL3
+ * VINR3
+ * VINL4
+ * VINR4
+
+The pins can be used in referring sound node's audio-routing property.
+
+Example:
+
+ pcm186x: audio-codec@4a {
+ compatible = "ti,pcm1865";
+ reg = <0x4a>;
+
+ avdd-supply = <®_3v3_analog>;
+ dvdd-supply = <®_3v3>;
+ iovdd-supply = <®_1v8>;
+ };
--
2.15.0
^ permalink raw reply related
* Re: [PATCH 5/8] ASoC: uniphier: add support for UniPhier AIO driver
From: Mark Brown @ 2017-12-06 12:58 UTC (permalink / raw)
To: Katsuhiro Suzuki
Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA,
Yamada, Masahiro/山田 真弘,
Masami Hiramatsu, Jassi Brar,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <004801d36e57$ea1940d0$be4bc270$@socionext.com>
[-- Attachment #1: Type: text/plain, Size: 1799 bytes --]
On Wed, Dec 06, 2017 at 03:03:18PM +0900, Katsuhiro Suzuki wrote:
> > I'd expect this code to be structured more like a library - have a
> > driver that handles the specific IPs then have it call into a shared
> > block of code that does the generic bits. Though in this case the
> > device specific bit looks like a couple of tiny data tables so I'm not
> > sure it's worth making it conditional or separate at all.
> Sorry... I agree your opinion, but I can't imagine the detail.
> I think my driver has structure as follows (ex. startup):
> DAI: uniphier_aio_startup()@aio-core.c
> Lib: uniphier_aio_init()@aio-regctrl.c
> SoC specific: uniphier_aio_ld11_spec@aio-ld11.c
> Am I wrong? Would you mean split the functions in aio-regctl.[ch] to other
> kernel module? I wonder if you could tell me the example from existing
> drivers. I'll try to fix my driver like as it.
One example is how all the drivers that use the generic dmaengine code
instantiate their DMA drivers, or how all the drivers for CODECs that
have both I2C and SPIi control interfaces instantiate - given that the
device specific code here seems to be mostly data tables that's probably
the closest thing.
> > At least. I do think we need to get to the bottom of how flexible the
> > hardware is first though.
> Yes, indeed. This hardware is more flexible and complex, but now I (and our
> company) don't use it. Of course, I don't want to hide some features of this
> hardware from ALSA people. I should try to upstream all features in the future,
> I think.
My main concern here is to make sure that when you decide you need to
use the more complex hardware that this can be done without too much
pain to existing machines (and that they can benefit from as much of the
enhanced functionality as is possible).
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Applied "ASoC: tas6424: add bindings for TAS6424" to the asoc tree
From: Mark Brown @ 2017-12-06 12:58 UTC (permalink / raw)
To: Michael Stecklein
Cc: Mark Rutland, devicetree, alsa-devel, linux-kernel, Liam Girdwood,
Rob Herring, Andrew F . Davis, Mark Brown
In-Reply-To: <20171205155412.20137-2-afd@ti.com>
The patch
ASoC: tas6424: add bindings for TAS6424
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 2e662342962863bb6044ad581d7cc03795da4e9d Mon Sep 17 00:00:00 2001
From: Michael Stecklein <m-stecklein@ti.com>
Date: Tue, 5 Dec 2017 09:54:11 -0600
Subject: [PATCH] ASoC: tas6424: add bindings for TAS6424
Add the bindings for the TAS6424 digital amplifier.
Signed-off-by: Michael Stecklein <m-stecklein@ti.com>
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
.../devicetree/bindings/sound/ti,tas6424.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas6424.txt
diff --git a/Documentation/devicetree/bindings/sound/ti,tas6424.txt b/Documentation/devicetree/bindings/sound/ti,tas6424.txt
new file mode 100644
index 000000000000..1c4ada0eef4e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tas6424.txt
@@ -0,0 +1,20 @@
+Texas Instruments TAS6424 Quad-Channel Audio amplifier
+
+The TAS6424 serial control bus communicates through I2C protocols.
+
+Required properties:
+ - compatible: "ti,tas6424" - TAS6424
+ - reg: I2C slave address
+ - sound-dai-cells: must be equal to 0
+
+Example:
+
+tas6424: tas6424@6a {
+ compatible = "ti,tas6424";
+ reg = <0x6a>;
+
+ #sound-dai-cells = <0>;
+};
+
+For more product information please see the link below:
+http://www.ti.com/product/TAS6424-Q1
--
2.15.0
^ permalink raw reply related
* Applied "ASoC: tas6424: Add support for TAS6424 digital amplifier" to the asoc tree
From: Mark Brown @ 2017-12-06 12:58 UTC (permalink / raw)
To: Andreas Dannenberg
Cc: Mark Rutland, devicetree, alsa-devel, linux-kernel, Liam Girdwood,
Rob Herring, Andrew F . Davis, Mark Brown, Michael Stecklein
In-Reply-To: <20171205155412.20137-3-afd@ti.com>
The patch
ASoC: tas6424: Add support for TAS6424 digital amplifier
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 157b68babe3281222e08c9c58456ca22544f06bc Mon Sep 17 00:00:00 2001
From: Andreas Dannenberg <dannenberg@ti.com>
Date: Tue, 5 Dec 2017 09:54:12 -0600
Subject: [PATCH] ASoC: tas6424: Add support for TAS6424 digital amplifier
The Texas Instruments TAS6424 device is a high-efficiency quad-channel
Class-D audio power amplifier. Its digital time division multiplexed
(TDM) interface enables up to 2 devices to share the same bus,
supporting a total of eight channels from one audio serial port.
Signed-off-by: Andreas Dannenberg <dannenberg@ti.com>
Signed-off-by: Michael Stecklein <m-stecklein@ti.com>
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/codecs/Kconfig | 8 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas6424.c | 707 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tas6424.h | 144 +++++++++
4 files changed, 861 insertions(+)
create mode 100644 sound/soc/codecs/tas6424.c
create mode 100644 sound/soc/codecs/tas6424.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a42ddbc93f3d..6c2e0d5426f7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -148,6 +148,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TAS5086 if I2C
select SND_SOC_TAS571X if I2C
select SND_SOC_TAS5720 if I2C
+ select SND_SOC_TAS6424 if I2C
select SND_SOC_TFA9879 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -883,6 +884,13 @@ config SND_SOC_TAS5720
Enable support for Texas Instruments TAS5720L/M high-efficiency mono
Class-D audio power amplifiers.
+config SND_SOC_TAS6424
+ tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS6424 high-efficiency
+ digital input quad-channel Class-D audio power amplifiers.
+
config SND_SOC_TFA9879
tristate "NXP Semiconductors TFA9879 amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0001069ce2a7..154abd758c30 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -156,6 +156,7 @@ snd-soc-sti-sas-objs := sti-sas.o
snd-soc-tas5086-objs := tas5086.o
snd-soc-tas571x-objs := tas571x.o
snd-soc-tas5720-objs := tas5720.o
+snd-soc-tas6424-objs := tas6424.o
snd-soc-tfa9879-objs := tfa9879.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -395,6 +396,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
+obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
new file mode 100644
index 000000000000..49b87f6e85bf
--- /dev/null
+++ b/sound/soc/codecs/tas6424.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas6424.h"
+
+/* Define how often to check (and clear) the fault status register (in ms) */
+#define TAS6424_FAULT_CHECK_INTERVAL 200
+
+static const char * const tas6424_supply_names[] = {
+ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
+ "vbat", /* Supply used for higher voltage analog circuits. */
+ "pvdd", /* Class-D amp output FETs supply. */
+};
+#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names)
+
+struct tas6424_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES];
+ struct delayed_work fault_check_work;
+ unsigned int last_fault1;
+ unsigned int last_fault2;
+ unsigned int last_warn;
+};
+
+/*
+ * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
+ * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
+ * as per device datasheet.
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new tas6424_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume",
+ TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume",
+ TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume",
+ TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume",
+ TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv),
+};
+
+static int tas6424_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s() event=0x%0x\n", __func__, event);
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Observe codec shutdown-to-active time */
+ msleep(12);
+
+ /* Turn on TAS6424 periodic fault checking/handling */
+ tas6424->last_fault1 = 0;
+ tas6424->last_fault2 = 0;
+ tas6424->last_warn = 0;
+ schedule_delayed_work(&tas6424->fault_check_work,
+ msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
+ } else if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable TAS6424 periodic fault checking/handling */
+ cancel_delayed_work_sync(&tas6424->fault_check_work);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas6424_audio_map[] = {
+ { "DAC", NULL, "DAC IN" },
+ { "OUT", NULL, "DAC" },
+};
+
+static int tas6424_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int rate = params_rate(params);
+ unsigned int width = params_width(params);
+ u8 sap_ctrl = 0;
+
+ dev_dbg(codec->dev, "%s() rate=%u width=%u\n", __func__, rate, width);
+
+ switch (rate) {
+ case 44100:
+ sap_ctrl |= TAS6424_SAP_RATE_44100;
+ break;
+ case 48000:
+ sap_ctrl |= TAS6424_SAP_RATE_48000;
+ break;
+ case 96000:
+ sap_ctrl |= TAS6424_SAP_RATE_96000;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sample rate: %u\n", rate);
+ return -EINVAL;
+ }
+
+ switch (width) {
+ case 16:
+ sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16;
+ break;
+ case 24:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sample width: %u\n", width);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TAS6424_SAP_CTRL,
+ TAS6424_SAP_RATE_MASK |
+ TAS6424_SAP_TDM_SLOT_SZ_16,
+ sap_ctrl);
+
+ return 0;
+}
+
+static int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 serial_format = 0;
+
+ dev_dbg(codec->dev, "%s() fmt=0x%0x\n", __func__, fmt);
+
+ /* clock masters */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* signal polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI clock signal polarity\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ serial_format |= TAS6424_SAP_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ serial_format |= TAS6424_SAP_DSP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /*
+ * We can use the fact that the TAS6424 does not care about the
+ * LRCLK duty cycle during TDM to receive DSP_B formatted data
+ * in LEFTJ mode (no delaying of the 1st data bit).
+ */
+ serial_format |= TAS6424_SAP_LEFTJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serial_format |= TAS6424_SAP_LEFTJ;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TAS6424_SAP_CTRL,
+ TAS6424_SAP_FMT_MASK, serial_format);
+
+ return 0;
+}
+
+static int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int first_slot, last_slot;
+ bool sap_tdm_slot_last;
+
+ dev_dbg(codec->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__,
+ tx_mask, rx_mask);
+
+ if (!tx_mask || !rx_mask)
+ return 0; /* nothing needed to disable TDM mode */
+
+ /*
+ * Determine the first slot and last slot that is being requested so
+ * we'll be able to more easily enforce certain constraints as the
+ * TAS6424's TDM interface is not fully configurable.
+ */
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(rx_mask);
+
+ if (last_slot - first_slot != 4) {
+ dev_err(codec->dev, "tdm mask must cover 4 contiguous slots\n");
+ return -EINVAL;
+ }
+
+ switch (first_slot) {
+ case 0:
+ sap_tdm_slot_last = false;
+ break;
+ case 4:
+ sap_tdm_slot_last = true;
+ break;
+ default:
+ dev_err(codec->dev, "tdm mask must start at slot 0 or 4\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST,
+ sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0);
+
+ return 0;
+}
+
+static int tas6424_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val;
+
+ dev_dbg(codec->dev, "%s() mute=%d\n", __func__, mute);
+
+ if (mute)
+ val = TAS6424_ALL_STATE_MUTE;
+ else
+ val = TAS6424_ALL_STATE_PLAY;
+
+ snd_soc_write(codec, TAS6424_CH_STATE_CTRL, val);
+
+ return 0;
+}
+
+static int tas6424_power_off(struct snd_soc_codec *codec)
+{
+ struct tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ snd_soc_write(codec, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ);
+
+ regcache_cache_only(tas6424->regmap, true);
+ regcache_mark_dirty(tas6424->regmap);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas6424_power_on(struct snd_soc_codec *codec)
+{
+ struct tas6424_data *tas6424 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(tas6424->regmap, false);
+
+ ret = regcache_sync(tas6424->regmap);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to sync regcache: %d\n", ret);
+ return ret;
+ }
+
+ snd_soc_write(codec, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_MUTE);
+
+ /* any time we come out of HIZ, the output channels automatically run DC
+ * load diagnostics, wait here until this completes
+ */
+ msleep(230);
+
+ return 0;
+}
+
+static int tas6424_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ dev_dbg(codec->dev, "%s() level=%d\n", __func__, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ tas6424_power_on(codec);
+ break;
+ case SND_SOC_BIAS_OFF:
+ tas6424_power_off(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tas6424 = {
+ .set_bias_level = tas6424_set_bias_level,
+ .idle_bias_off = true,
+
+ .component_driver = {
+ .controls = tas6424_snd_controls,
+ .num_controls = ARRAY_SIZE(tas6424_snd_controls),
+ .dapm_widgets = tas6424_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets),
+ .dapm_routes = tas6424_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map),
+ },
+};
+
+static struct snd_soc_dai_ops tas6424_speaker_dai_ops = {
+ .hw_params = tas6424_hw_params,
+ .set_fmt = tas6424_set_dai_fmt,
+ .set_tdm_slot = tas6424_set_dai_tdm_slot,
+ .digital_mute = tas6424_mute,
+};
+
+static struct snd_soc_dai_driver tas6424_dai[] = {
+ {
+ .name = "tas6424-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS6424_RATES,
+ .formats = TAS6424_FORMATS,
+ },
+ .ops = &tas6424_speaker_dai_ops,
+ },
+};
+
+static void tas6424_fault_check_work(struct work_struct *work)
+{
+ struct tas6424_data *tas6424 = container_of(work, struct tas6424_data,
+ fault_check_work.work);
+ struct device *dev = tas6424->dev;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, ®);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FAULT1 register: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * Ignore any clock faults as there is no clean way to check for them.
+ * We would need to start checking for those faults *after* the SAIF
+ * stream has been setup, and stop checking *before* the stream is
+ * stopped to avoid any false-positives. However there are no
+ * appropriate hooks to monitor these events.
+ */
+ reg &= TAS6424_FAULT_PVDD_OV |
+ TAS6424_FAULT_VBAT_OV |
+ TAS6424_FAULT_PVDD_UV |
+ TAS6424_FAULT_VBAT_UV;
+
+ if (reg)
+ goto check_global_fault2_reg;
+
+ /*
+ * Only flag errors once for a given occurrence. This is needed as
+ * the TAS6424 will take time clearing the fault condition internally
+ * during which we don't want to bombard the system with the same
+ * error message over and over.
+ */
+ if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV))
+ dev_crit(dev, "experienced a PVDD overvoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV))
+ dev_crit(dev, "experienced a VBAT overvoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV))
+ dev_crit(dev, "experienced a PVDD undervoltage fault\n");
+
+ if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV))
+ dev_crit(dev, "experienced a VBAT undervoltage fault\n");
+
+ /* Store current fault1 value so we can detect any changes next time */
+ tas6424->last_fault1 = reg;
+
+check_global_fault2_reg:
+ ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, ®);
+ if (ret < 0) {
+ dev_err(dev, "failed to read FAULT2 register: %d\n", ret);
+ goto out;
+ }
+
+ reg &= TAS6424_FAULT_OTSD |
+ TAS6424_FAULT_OTSD_CH1 |
+ TAS6424_FAULT_OTSD_CH2 |
+ TAS6424_FAULT_OTSD_CH3 |
+ TAS6424_FAULT_OTSD_CH4;
+
+ if (!reg)
+ goto check_warn_reg;
+
+ if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD))
+ dev_crit(dev, "experienced a global overtemp shutdown\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1))
+ dev_crit(dev, "experienced an overtemp shutdown on CH1\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2))
+ dev_crit(dev, "experienced an overtemp shutdown on CH2\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3))
+ dev_crit(dev, "experienced an overtemp shutdown on CH3\n");
+
+ if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4))
+ dev_crit(dev, "experienced an overtemp shutdown on CH4\n");
+
+ /* Store current fault2 value so we can detect any changes next time */
+ tas6424->last_fault2 = reg;
+
+check_warn_reg:
+ ret = regmap_read(tas6424->regmap, TAS6424_WARN, ®);
+ if (ret < 0) {
+ dev_err(dev, "failed to read WARN register: %d\n", ret);
+ goto out;
+ }
+
+ reg &= TAS6424_WARN_VDD_UV |
+ TAS6424_WARN_VDD_POR |
+ TAS6424_WARN_VDD_OTW |
+ TAS6424_WARN_VDD_OTW_CH1 |
+ TAS6424_WARN_VDD_OTW_CH2 |
+ TAS6424_WARN_VDD_OTW_CH3 |
+ TAS6424_WARN_VDD_OTW_CH4;
+
+ if (!reg)
+ goto out;
+
+ if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV))
+ dev_warn(dev, "experienced a VDD under voltage condition\n");
+
+ if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR))
+ dev_warn(dev, "experienced a VDD POR condition\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW))
+ dev_warn(dev, "experienced a global overtemp warning\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1))
+ dev_warn(dev, "experienced an overtemp warning on CH1\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2))
+ dev_warn(dev, "experienced an overtemp warning on CH2\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3))
+ dev_warn(dev, "experienced an overtemp warning on CH3\n");
+
+ if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4))
+ dev_warn(dev, "experienced an overtemp warning on CH4\n");
+
+ /* Store current warn value so we can detect any changes next time */
+ tas6424->last_warn = reg;
+
+ /* Clear any faults by toggling the CLEAR_FAULT control bit */
+ ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
+ TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT);
+ if (ret < 0)
+ dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
+
+ ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
+ TAS6424_CLEAR_FAULT, 0);
+ if (ret < 0)
+ dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret);
+
+out:
+ /* Schedule the next fault check at the specified interval */
+ schedule_delayed_work(&tas6424->fault_check_work,
+ msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL));
+}
+
+static const struct reg_default tas6424_reg_defaults[] = {
+ { TAS6424_MODE_CTRL, 0x00 },
+ { TAS6424_MISC_CTRL1, 0x32 },
+ { TAS6424_MISC_CTRL2, 0x62 },
+ { TAS6424_SAP_CTRL, 0x04 },
+ { TAS6424_CH_STATE_CTRL, 0x55 },
+ { TAS6424_CH1_VOL_CTRL, 0xcf },
+ { TAS6424_CH2_VOL_CTRL, 0xcf },
+ { TAS6424_CH3_VOL_CTRL, 0xcf },
+ { TAS6424_CH4_VOL_CTRL, 0xcf },
+ { TAS6424_DC_DIAG_CTRL1, 0x00 },
+ { TAS6424_DC_DIAG_CTRL2, 0x11 },
+ { TAS6424_DC_DIAG_CTRL3, 0x11 },
+ { TAS6424_PIN_CTRL, 0xff },
+ { TAS6424_AC_DIAG_CTRL1, 0x00 },
+ { TAS6424_MISC_CTRL3, 0x00 },
+ { TAS6424_CLIP_CTRL, 0x01 },
+ { TAS6424_CLIP_WINDOW, 0x14 },
+ { TAS6424_CLIP_WARN, 0x00 },
+ { TAS6424_CBC_STAT, 0x00 },
+ { TAS6424_MISC_CTRL4, 0x40 },
+};
+
+static bool tas6424_is_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS6424_MODE_CTRL:
+ case TAS6424_MISC_CTRL1:
+ case TAS6424_MISC_CTRL2:
+ case TAS6424_SAP_CTRL:
+ case TAS6424_CH_STATE_CTRL:
+ case TAS6424_CH1_VOL_CTRL:
+ case TAS6424_CH2_VOL_CTRL:
+ case TAS6424_CH3_VOL_CTRL:
+ case TAS6424_CH4_VOL_CTRL:
+ case TAS6424_DC_DIAG_CTRL1:
+ case TAS6424_DC_DIAG_CTRL2:
+ case TAS6424_DC_DIAG_CTRL3:
+ case TAS6424_PIN_CTRL:
+ case TAS6424_AC_DIAG_CTRL1:
+ case TAS6424_MISC_CTRL3:
+ case TAS6424_CLIP_CTRL:
+ case TAS6424_CLIP_WINDOW:
+ case TAS6424_CLIP_WARN:
+ case TAS6424_CBC_STAT:
+ case TAS6424_MISC_CTRL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS6424_DC_LOAD_DIAG_REP12:
+ case TAS6424_DC_LOAD_DIAG_REP34:
+ case TAS6424_DC_LOAD_DIAG_REPLO:
+ case TAS6424_CHANNEL_STATE:
+ case TAS6424_CHANNEL_FAULT:
+ case TAS6424_GLOB_FAULT1:
+ case TAS6424_GLOB_FAULT2:
+ case TAS6424_WARN:
+ case TAS6424_AC_LOAD_DIAG_REP1:
+ case TAS6424_AC_LOAD_DIAG_REP2:
+ case TAS6424_AC_LOAD_DIAG_REP3:
+ case TAS6424_AC_LOAD_DIAG_REP4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas6424_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .writeable_reg = tas6424_is_writable_reg,
+ .volatile_reg = tas6424_is_volatile_reg,
+
+ .max_register = TAS6424_MAX,
+ .reg_defaults = tas6424_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas6424_of_ids[] = {
+ { .compatible = "ti,tas6424", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tas6424_of_ids);
+#endif
+
+static int tas6424_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct tas6424_data *tas6424;
+ int ret;
+ int i;
+
+ tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL);
+ if (!tas6424)
+ return -ENOMEM;
+ dev_set_drvdata(dev, tas6424);
+
+ tas6424->dev = dev;
+
+ tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config);
+ if (IS_ERR(tas6424->regmap)) {
+ ret = PTR_ERR(tas6424->regmap);
+ dev_err(dev, "unable to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++)
+ tas6424->supplies[i].supply = tas6424_supply_names[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret) {
+ dev_err(dev, "unable to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret) {
+ dev_err(dev, "unable to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset device to establish well-defined startup state */
+ ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL,
+ TAS6424_RESET, TAS6424_RESET);
+ if (ret) {
+ dev_err(dev, "unable to reset device: %d\n", ret);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work);
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_tas6424,
+ tas6424_dai, ARRAY_SIZE(tas6424_dai));
+ if (ret < 0) {
+ dev_err(dev, "unable to register codec: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas6424_i2c_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tas6424_data *tas6424 = dev_get_drvdata(dev);
+ int ret;
+
+ snd_soc_unregister_codec(dev);
+
+ cancel_delayed_work_sync(&tas6424->fault_check_work);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies),
+ tas6424->supplies);
+ if (ret < 0) {
+ dev_err(dev, "unable to disable supplies: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id tas6424_i2c_ids[] = {
+ { "tas6424", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids);
+
+static struct i2c_driver tas6424_i2c_driver = {
+ .driver = {
+ .name = "tas6424",
+ .of_match_table = of_match_ptr(tas6424_of_ids),
+ },
+ .probe = tas6424_i2c_probe,
+ .remove = tas6424_i2c_remove,
+ .id_table = tas6424_i2c_ids,
+};
+module_i2c_driver(tas6424_i2c_driver);
+
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TAS6424 Audio amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h
new file mode 100644
index 000000000000..430588328a06
--- /dev/null
+++ b/sound/soc/codecs/tas6424.h
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef __TAS6424_H__
+#define __TAS6424_H__
+
+#define TAS6424_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define TAS6424_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* Register Address Map */
+#define TAS6424_MODE_CTRL 0x00
+#define TAS6424_MISC_CTRL1 0x01
+#define TAS6424_MISC_CTRL2 0x02
+#define TAS6424_SAP_CTRL 0x03
+#define TAS6424_CH_STATE_CTRL 0x04
+#define TAS6424_CH1_VOL_CTRL 0x05
+#define TAS6424_CH2_VOL_CTRL 0x06
+#define TAS6424_CH3_VOL_CTRL 0x07
+#define TAS6424_CH4_VOL_CTRL 0x08
+#define TAS6424_DC_DIAG_CTRL1 0x09
+#define TAS6424_DC_DIAG_CTRL2 0x0a
+#define TAS6424_DC_DIAG_CTRL3 0x0b
+#define TAS6424_DC_LOAD_DIAG_REP12 0x0c
+#define TAS6424_DC_LOAD_DIAG_REP34 0x0d
+#define TAS6424_DC_LOAD_DIAG_REPLO 0x0e
+#define TAS6424_CHANNEL_STATE 0x0f
+#define TAS6424_CHANNEL_FAULT 0x10
+#define TAS6424_GLOB_FAULT1 0x11
+#define TAS6424_GLOB_FAULT2 0x12
+#define TAS6424_WARN 0x13
+#define TAS6424_PIN_CTRL 0x14
+#define TAS6424_AC_DIAG_CTRL1 0x15
+#define TAS6424_AC_DIAG_CTRL2 0x16
+#define TAS6424_AC_LOAD_DIAG_REP1 0x17
+#define TAS6424_AC_LOAD_DIAG_REP2 0x18
+#define TAS6424_AC_LOAD_DIAG_REP3 0x19
+#define TAS6424_AC_LOAD_DIAG_REP4 0x1a
+#define TAS6424_MISC_CTRL3 0x21
+#define TAS6424_CLIP_CTRL 0x22
+#define TAS6424_CLIP_WINDOW 0x23
+#define TAS6424_CLIP_WARN 0x24
+#define TAS6424_CBC_STAT 0x25
+#define TAS6424_MISC_CTRL4 0x26
+#define TAS6424_MAX TAS6424_MISC_CTRL4
+
+/* TAS6424_MODE_CTRL_REG */
+#define TAS6424_RESET BIT(7)
+
+/* TAS6424_SAP_CTRL_REG */
+#define TAS6424_SAP_RATE_MASK GENMASK(7, 6)
+#define TAS6424_SAP_RATE_44100 (0x00 << 6)
+#define TAS6424_SAP_RATE_48000 (0x01 << 6)
+#define TAS6424_SAP_RATE_96000 (0x02 << 6)
+#define TAS6424_SAP_TDM_SLOT_LAST BIT(5)
+#define TAS6424_SAP_TDM_SLOT_SZ_16 BIT(4)
+#define TAS6424_SAP_TDM_SLOT_SWAP BIT(3)
+#define TAS6424_SAP_FMT_MASK GENMASK(2, 0)
+#define TAS6424_SAP_RIGHTJ_24 (0x00 << 0)
+#define TAS6424_SAP_RIGHTJ_20 (0x01 << 0)
+#define TAS6424_SAP_RIGHTJ_18 (0x02 << 0)
+#define TAS6424_SAP_RIGHTJ_16 (0x03 << 0)
+#define TAS6424_SAP_I2S (0x04 << 0)
+#define TAS6424_SAP_LEFTJ (0x05 << 0)
+#define TAS6424_SAP_DSP (0x06 << 0)
+
+/* TAS6424_CH_STATE_CTRL_REG */
+#define TAS6424_CH1_STATE_MASK GENMASK(7, 6)
+#define TAS6424_CH1_STATE_PLAY (0x00 << 6)
+#define TAS6424_CH1_STATE_HIZ (0x01 << 6)
+#define TAS6424_CH1_STATE_MUTE (0x02 << 6)
+#define TAS6424_CH1_STATE_DIAG (0x03 << 6)
+#define TAS6424_CH2_STATE_MASK GENMASK(5, 4)
+#define TAS6424_CH2_STATE_PLAY (0x00 << 4)
+#define TAS6424_CH2_STATE_HIZ (0x01 << 4)
+#define TAS6424_CH2_STATE_MUTE (0x02 << 4)
+#define TAS6424_CH2_STATE_DIAG (0x03 << 4)
+#define TAS6424_CH3_STATE_MASK GENMASK(3, 2)
+#define TAS6424_CH3_STATE_PLAY (0x00 << 2)
+#define TAS6424_CH3_STATE_HIZ (0x01 << 2)
+#define TAS6424_CH3_STATE_MUTE (0x02 << 2)
+#define TAS6424_CH3_STATE_DIAG (0x03 << 2)
+#define TAS6424_CH4_STATE_MASK GENMASK(1, 0)
+#define TAS6424_CH4_STATE_PLAY (0x00 << 0)
+#define TAS6424_CH4_STATE_HIZ (0x01 << 0)
+#define TAS6424_CH4_STATE_MUTE (0x02 << 0)
+#define TAS6424_CH4_STATE_DIAG (0x03 << 0)
+#define TAS6424_ALL_STATE_PLAY (TAS6424_CH1_STATE_PLAY | \
+ TAS6424_CH2_STATE_PLAY | \
+ TAS6424_CH3_STATE_PLAY | \
+ TAS6424_CH4_STATE_PLAY)
+#define TAS6424_ALL_STATE_HIZ (TAS6424_CH1_STATE_HIZ | \
+ TAS6424_CH2_STATE_HIZ | \
+ TAS6424_CH3_STATE_HIZ | \
+ TAS6424_CH4_STATE_HIZ)
+#define TAS6424_ALL_STATE_MUTE (TAS6424_CH1_STATE_MUTE | \
+ TAS6424_CH2_STATE_MUTE | \
+ TAS6424_CH3_STATE_MUTE | \
+ TAS6424_CH4_STATE_MUTE)
+#define TAS6424_ALL_STATE_DIAG (TAS6424_CH1_STATE_DIAG | \
+ TAS6424_CH2_STATE_DIAG | \
+ TAS6424_CH3_STATE_DIAG | \
+ TAS6424_CH4_STATE_DIAG)
+
+/* TAS6424_GLOB_FAULT1_REG */
+#define TAS6424_FAULT_CLOCK BIT(4)
+#define TAS6424_FAULT_PVDD_OV BIT(3)
+#define TAS6424_FAULT_VBAT_OV BIT(2)
+#define TAS6424_FAULT_PVDD_UV BIT(1)
+#define TAS6424_FAULT_VBAT_UV BIT(0)
+
+/* TAS6424_GLOB_FAULT2_REG */
+#define TAS6424_FAULT_OTSD BIT(4)
+#define TAS6424_FAULT_OTSD_CH1 BIT(3)
+#define TAS6424_FAULT_OTSD_CH2 BIT(2)
+#define TAS6424_FAULT_OTSD_CH3 BIT(1)
+#define TAS6424_FAULT_OTSD_CH4 BIT(0)
+
+/* TAS6424_WARN_REG */
+#define TAS6424_WARN_VDD_UV BIT(6)
+#define TAS6424_WARN_VDD_POR BIT(5)
+#define TAS6424_WARN_VDD_OTW BIT(4)
+#define TAS6424_WARN_VDD_OTW_CH1 BIT(3)
+#define TAS6424_WARN_VDD_OTW_CH2 BIT(2)
+#define TAS6424_WARN_VDD_OTW_CH3 BIT(1)
+#define TAS6424_WARN_VDD_OTW_CH4 BIT(0)
+
+/* TAS6424_MISC_CTRL3_REG */
+#define TAS6424_CLEAR_FAULT BIT(7)
+#define TAS6424_PBTL_CH_SEL BIT(6)
+#define TAS6424_MASK_CBC_WARN BIT(5)
+#define TAS6424_MASK_VDD_UV BIT(4)
+#define TAS6424_OTSD_AUTO_RECOVERY BIT(3)
+
+#endif /* __TAS6424_H__ */
--
2.15.0
^ permalink raw reply related
* Re: [PATCH v2 08/19] ASoC: tlv320aic31xx: Switch GPIO handling to use gpiod_* API
From: Mark Brown @ 2017-12-06 12:46 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, linux-kernel, Rob Herring, Benoît Cousson
In-Reply-To: <17fcc926-9ee8-1229-ea3d-f38347cb9a23@ti.com>
[-- Attachment #1.1: Type: text/plain, Size: 683 bytes --]
On Tue, Dec 05, 2017 at 03:23:49PM -0600, Andrew F. Davis wrote:
> On 12/04/2017 10:47 AM, Andrew F. Davis wrote:
> > Kbuild bot seems mad a this one, looks like I need to include
> > linux/gpio/consumer.h, will fix for v3.
> Looks like you already have this in your -next branch, how do you want
> this fix, I can send a delta patch with the added include, a new v3
> version that you can replace the patch in-tree with, or if it's easier
> for you manually fix in-tree?
As the patch applied mail says:
| If any updates are required or you are submitting further changes they
| should be sent as incremental updates against current git, existing
| patches will not be replaced.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH v2 09/19] ASoC: tlv320aic31xx: Remove platform data
From: Mark Brown @ 2017-12-06 12:45 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Mark Rutland, devicetree, alsa-devel, Tony Lindgren,
Liam Girdwood, linux-kernel, Rob Herring, Benoît Cousson
In-Reply-To: <4fee76ef-e518-69b6-3bcc-f65b0a31cacc@ti.com>
[-- Attachment #1.1: Type: text/plain, Size: 797 bytes --]
On Tue, Dec 05, 2017 at 03:20:19PM -0600, Andrew F. Davis wrote:
> On 12/01/2017 07:26 AM, Mark Brown wrote:
> > The advantage being...? Not all architectures use DT or ACPI so it's
> > not clear that this is a step forwards in itself.
> Simplifies the code in several places, and you don't need to use DT or
> ACPI, it probes just fine anyway you normally add an I2C device.
> All we are dropping here is the platform_data way of specifying mic-bias
> voltage, which if you are wanting to do that in an out-of-tree board
> file, then I'm sure you can locally modify this driver to use your
> wanted voltage setting by default.
Then if you want to upstream the driver you'll have to add the platform
data support again. Like I say not all architectures have anything
other than board files.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* [PATCH v2 7/7] dt-bindings: tda998x: add the calibration gpio
From: Russell King @ 2017-12-06 12:35 UTC (permalink / raw)
To: David Airlie, Hans Verkuil
Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Rob Herring,
Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171206123452.GA13127-l+eeeJia6m9URfEZ8mYm6t73F7V6hmMc@public.gmane.org>
Add the optional calibration gpio for integrated TDA9950 CEC support.
This GPIO corresponds with the interrupt from the TDA998x, as the
calibration requires driving the interrupt pin low.
Signed-off-by: Russell King <rmk+kernel-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
---
Documentation/devicetree/bindings/display/bridge/tda998x.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/tda998x.txt b/Documentation/devicetree/bindings/display/bridge/tda998x.txt
index 24cc2466185a..1a4eaca40d94 100644
--- a/Documentation/devicetree/bindings/display/bridge/tda998x.txt
+++ b/Documentation/devicetree/bindings/display/bridge/tda998x.txt
@@ -27,6 +27,9 @@ Required properties;
in question is used. The implementation allows one or two DAIs. If two
DAIs are defined, they must be of different type.
+ - nxp,calib-gpios: calibration GPIO, which must correspond with the
+ gpio used for the TDA998x interrupt pin.
+
[1] Documentation/sound/alsa/soc/DAI.txt
[2] include/dt-bindings/display/tda998x.h
--
2.7.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
* Re: [PATCH v4 1/2] dt-bindings: sound: add bindings for TAS6424
From: Mark Brown @ 2017-12-06 12:24 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Mark Rutland, devicetree, alsa-devel, Liam Girdwood, linux-kernel,
Rob Herring
In-Reply-To: <20171205155412.20137-2-afd@ti.com>
[-- Attachment #1.1: Type: text/plain, Size: 303 bytes --]
On Tue, Dec 05, 2017 at 09:54:11AM -0600, Andrew F. Davis wrote:
> From: Michael Stecklein <m-stecklein@ti.com>
>
> Add the bindings for the TAS6424 digital amplifier.
Please use subject lines matching the style for the subsystem. This
makes it easier for people to identify relevant patches.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH 5/6] cpufreq: Add DVFS support for Armada 37xx
From: Gregory CLEMENT @ 2017-12-06 12:24 UTC (permalink / raw)
To: Viresh Kumar
Cc: Rafael J. Wysocki, linux-pm, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Rob Herring, devicetree, Thomas Petazzoni,
linux-arm-kernel, Antoine Tenart, Miquèl Raynal,
Nadav Haklai, Victor Gu, Marcin Wojtas, Wilson Ding, Hua Jing,
Neta Zur Hershkovits, Evan Wang
In-Reply-To: <20171205055414.yghqdxg2but34n4i@vireshk-mac-ubuntu>
Hi Viresh,
On mar., déc. 05 2017, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 01-12-17, 12:25, Gregory CLEMENT wrote:
>> This patch adds DVFS support for the Armada 37xx SoCs
>>
>> There are up to four CPU frequency loads for Armada 37xx controlled by
>> the hardware.
>>
>> This driver associates the CPU load level to a frequency, then the
>> hardware will switch while selecting a load level.
>>
>> The hardware also can associate a voltage for each level (AVS support)
>> but it is not yet supported
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/cpufreq/Kconfig.arm | 7 +
>> drivers/cpufreq/Makefile | 1 +
>> drivers/cpufreq/armada-37xx-cpufreq.c | 241 ++++++++++++++++++++++++++++++++++
>> 3 files changed, 249 insertions(+)
>> create mode 100644 drivers/cpufreq/armada-37xx-cpufreq.c
>>
>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>> index 17625115c67f..3018ff0d068f 100644
>> --- a/drivers/cpufreq/Kconfig.arm
>> +++ b/drivers/cpufreq/Kconfig.arm
>> @@ -19,6 +19,13 @@ config ACPI_CPPC_CPUFREQ
>>
>> If in doubt, say N.
>>
>> +config ARM_ARMADA_37XX_CPUFREQ
>> + tristate "Armada 37xx CPUFreq support"
>> + depends on ARCH_MVEBU
>> + help
>> + This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
>> + The Armada 37xx PMU supports 4 frequency and VDD levels.
>> +
>> # big LITTLE core layer and glue drivers
>> config ARM_BIG_LITTLE_CPUFREQ
>> tristate "Generic ARM big LITTLE CPUfreq driver"
>> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
>> index d762e76887e7..e07715ce8844 100644
>> --- a/drivers/cpufreq/Makefile
>> +++ b/drivers/cpufreq/Makefile
>> @@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
>> # LITTLE drivers, so that it is probed last.
>> obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
>>
>> +obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
>> obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
>> obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
>> obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
>> diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
>> new file mode 100644
>> index 000000000000..40c9a744cc6e
>> --- /dev/null
>> +++ b/drivers/cpufreq/armada-37xx-cpufreq.c
>> @@ -0,0 +1,241 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * CPU frequency scaling support for Armada 37xx platform.
>> + *
>> + * Copyright (C) 2017 Marvell
>> + *
>> + * Gregory CLEMENT <gregory.clement@free-electrons.com>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/cpu.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/err.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_opp.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +/* Power management in North Bridge register set */
>> +#define ARMADA_37XX_NB_L0L1 0x18
>> +#define ARMADA_37XX_NB_L2L3 0x1C
>> +#define ARMADA_37XX_NB_TBG_DIV_OFF 13
>> +#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
>> +#define ARMADA_37XX_NB_CLK_SEL_OFF 11
>> +#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
>> +#define ARMADA_37XX_NB_CLK_SEL_TBG 0x1
>> +#define ARMADA_37XX_NB_TBG_SEL_OFF 9
>> +#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
>> +#define ARMADA_37XX_NB_VDD_SEL_OFF 6
>> +#define ARMADA_37XX_NB_VDD_SEL_MASK 0x3
>> +#define ARMADA_37XX_NB_CONFIG_SHIFT 16
>
> Looks like you have added tabs after #define as well? Perhaps a space
> is good enough there.
OK
>
>> +#define ARMADA_37XX_NB_DYN_MOD 0x24
>> +#define ARMADA_37XX_NB_CLK_SEL_EN BIT(26)
>> +#define ARMADA_37XX_NB_TBG_EN BIT(28)
>> +#define ARMADA_37XX_NB_DIV_EN BIT(29)
>> +#define ARMADA_37XX_NB_VDD_EN BIT(30)
>> +#define ARMADA_37XX_NB_DFS_EN BIT(31)
>> +#define ARMADA_37XX_NB_CPU_LOAD 0x30
>> +#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
>> +#define ARMADA_37XX_DVFS_LOAD_0 0
>> +#define ARMADA_37XX_DVFS_LOAD_1 1
>> +#define ARMADA_37XX_DVFS_LOAD_2 2
>> +#define ARMADA_37XX_DVFS_LOAD_3 3
>> +
>> +/*
>> + * On Armada 37xx the Power management manages 4 level of CPU load,
>> + * each level can be associated with a CPU clock source, a CPU
>> + * divider, a VDD level, etc...
>> + */
>> +#define LOAD_LEVEL_NR 4
>> +
>> +struct armada_37xx_dvfs {
>> + u32 cpu_freq_max;
>> + u8 divider[LOAD_LEVEL_NR];
>> +};
>> +
>> +static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
>> + {.cpu_freq_max = 1200*1000*1000, .divider = {1, 2, 4, 6} },
>> + {.cpu_freq_max = 1000*1000*1000, .divider = {1, 2, 4, 5} },
>> + {.cpu_freq_max = 800*1000*1000, .divider = {1, 2, 3, 4} },
>> + {.cpu_freq_max = 600*1000*1000, .divider = {2, 4, 5, 6} },
>> +};
>> +
>> +static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(armada_37xx_dvfs); i++) {
>> + if (freq == armada_37xx_dvfs[i].cpu_freq_max)
>> + return &armada_37xx_dvfs[i];
>> + }
>> +
>> + pr_err("Unsupported CPU frequency %d MHz\n", freq/1000000);
>> + return NULL;
>> +}
>> +
>> +/*
>> + * Setup the four level managed by the hardware. Once the four level
>> + * will be configured then the DVFS will be enabled.
>> + */
>> +static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
>> + struct clk *clk, u8 *divider)
>> +{
>> + int load_level;
>> + struct clk *parent;
>> +
>> + for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) {
>> + unsigned int reg, mask, val, offset = 0;
>> +
>> + if (load_level <= ARMADA_37XX_DVFS_LOAD_1)
>> + reg = ARMADA_37XX_NB_L0L1;
>> + else
>> + reg = ARMADA_37XX_NB_L2L3;
>> +
>> + if (load_level == ARMADA_37XX_DVFS_LOAD_0 ||
>> + load_level == ARMADA_37XX_DVFS_LOAD_2)
>> + offset += ARMADA_37XX_NB_CONFIG_SHIFT;
>> +
>> + /* Set cpu clock source, for all the level we use TBG */
>> + val = ARMADA_37XX_NB_CLK_SEL_TBG << ARMADA_37XX_NB_CLK_SEL_OFF;
>> + mask = (ARMADA_37XX_NB_CLK_SEL_MASK
>> + << ARMADA_37XX_NB_CLK_SEL_OFF);
>> +
>> + /*
>> + * Set cpu divider based on the pre-computed array in
>> + * order to have balanced step.
>> + */
>> + val |= divider[load_level] << ARMADA_37XX_NB_TBG_DIV_OFF;
>> + mask |= (ARMADA_37XX_NB_TBG_DIV_MASK
>> + << ARMADA_37XX_NB_TBG_DIV_OFF);
>> +
>> + /* Set VDD divider which is actually the load level. */
>> + val |= load_level << ARMADA_37XX_NB_VDD_SEL_OFF;
>> + mask |= (ARMADA_37XX_NB_VDD_SEL_MASK
>> + << ARMADA_37XX_NB_VDD_SEL_OFF);
>> +
>> + val <<= offset;
>> + mask <<= offset;
>> +
>> + regmap_update_bits(base, reg, mask, val);
>> + }
>> +
>> + /*
>> + * Set cpu clock source, for all the level we keep the same
>> + * clock source that the one already configured. For this one
>> + * we need to use the clock framework
>> + */
>> + parent = clk_get_parent(clk);
>> + clk_set_parent(clk, parent);
>> +}
>> +
>> +static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
>> +{
>> + unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
>> + mask = ARMADA_37XX_NB_DFS_EN;
>> +
>> + regmap_update_bits(base, reg, mask, 0);
>> +}
>> +
>> +static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
>> +{
>> + unsigned int val, reg = ARMADA_37XX_NB_CPU_LOAD,
>> + mask = ARMADA_37XX_NB_CPU_LOAD_MASK;
>> +
>> + /* Start with the highest load (0) */
>> + val = ARMADA_37XX_DVFS_LOAD_0;
>> + regmap_update_bits(base, reg, mask, val);
>> +
>> + /* Now enable DVFS for the CPUs */
>> + reg = ARMADA_37XX_NB_DYN_MOD;
>> + mask = ARMADA_37XX_NB_CLK_SEL_EN | ARMADA_37XX_NB_TBG_EN |
>> + ARMADA_37XX_NB_DIV_EN | ARMADA_37XX_NB_VDD_EN |
>> + ARMADA_37XX_NB_DFS_EN;
>> +
>> + regmap_update_bits(base, reg, mask, mask);
>> +}
>> +
>> +static int __init armada37xx_cpufreq_driver_init(void)
>> +{
>> + struct armada_37xx_dvfs *dvfs;
>> + struct platform_device *pdev;
>> + unsigned int cur_frequency;
>> + struct regmap *nb_pm_base;
>> + struct device *cpu_dev;
>> + int load_level, ret;
>> + struct clk *clk;
>> +
>> + nb_pm_base =
>> + syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm");
>> +
>> + if (IS_ERR(nb_pm_base))
>> + return -ENODEV;
>> +
>> + /* Before doing any configuration on the DVFS first, disable it */
>> + armada37xx_cpufreq_disable_dvfs(nb_pm_base);
>> +
>> + /*
>> + * On CPU 0 register the operating points supported (which are
>> + * the nominal CPU frequency and full integer divisions of
>> + * it).
>> + */
>> + cpu_dev = get_cpu_device(0);
>> + if (!cpu_dev) {
>> + dev_err(cpu_dev, "Cannot get CPU\n");
>> + return -ENODEV;
>> + }
>> +
>> + clk = clk_get(cpu_dev, 0);
>> + if (IS_ERR(clk)) {
>> + dev_err(cpu_dev, "Cannot get clock for CPU0\n");
>> + return PTR_ERR(clk);
>> + }
>> +
>> + /* Get nominal (current) CPU frequency */
>> + cur_frequency = clk_get_rate(clk);
>> + if (!cur_frequency) {
>> + dev_err(cpu_dev, "Failed to get clock rate for CPU\n");
>> + return -EINVAL;
>> + }
>> +
>> + dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
>> + if (!dvfs)
>> + return -EINVAL;
>> +
>> + armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
>> +
>> + /*
>> + * In case of a failure of dev_pm_opp_add(), we don't bother
>> + * with cleaning up the registered OPP (there's no function to
>> + * do so),
>
> What do you mean by the comment within ()? We do have
> dev_pm_opp_remove() helper.
Actually it was a copy and paste from an other driver, I didn't check it
this was still true.
So the original comment was added in July 2014 and the
dev_pm_opp_remove() helpers was added few months later
in Nov 2014.
I will fix it and also in the other driver mvebu-cpufreq.c.
Thanks,
Gregory
>
>> and simply cancel the registration of the cpufreq
>> + * device.
>> + */
>> + for (load_level = ARMADA_37XX_DVFS_LOAD_0; load_level < LOAD_LEVEL_NR;
>> + load_level++) {
>> + unsigned long freq = dvfs->divider[load_level];
>> +
>> + ret = dev_pm_opp_add(cpu_dev, freq, 0);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + /* Now that everything is setup, enable the DVFS at hardware level */
>> + armada37xx_cpufreq_enable_dvfs(nb_pm_base);
>> +
>> + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
>> +
>> + return PTR_ERR_OR_ZERO(pdev);
>> +}
>> +/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
>> +late_initcall(armada37xx_cpufreq_driver_init);
>> +
>> +MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
>> +MODULE_DESCRIPTION("Armada 37xx cpufreq driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.15.0
>
> --
> viresh
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v2] ARM: dts: r8a7745: Add APMU node and second CPU core
From: Geert Uytterhoeven @ 2017-12-06 12:12 UTC (permalink / raw)
To: Fabrizio Castro
Cc: Simon Horman, Rob Herring, Mark Rutland, Magnus Damm,
Russell King, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Linux-Renesas,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Chris Paterson
In-Reply-To: <1512561929-16540-1-git-send-email-fabrizio.castro-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
On Wed, Dec 6, 2017 at 1:05 PM, Fabrizio Castro
<fabrizio.castro-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> wrote:
>
> Add DT node for the Advanced Power Management Unit (APMU), add the
> second CPU core, and use "renesas,apmu" as "enable-method".
>
> Signed-off-by: Fabrizio Castro <fabrizio.castro-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
> Signed-off-by: Chris Paterson <chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> Reviewed-by: Biju Das <biju.das-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
> v2:
> - rebased against renesas-devel-20171205-v4.15-rc2
Seems my
Reviewed-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
was not added.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.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
--
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 3/6] cpufreq: sort the drivers in ARM part
From: Gregory CLEMENT @ 2017-12-06 12:09 UTC (permalink / raw)
To: Viresh Kumar
Cc: Rafael J. Wysocki, linux-pm, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Rob Herring, devicetree, Thomas Petazzoni,
linux-arm-kernel, Antoine Tenart, Miquèl Raynal,
Nadav Haklai, Victor Gu, Marcin Wojtas, Wilson Ding, Hua Jing,
Neta Zur Hershkovits, Evan Wang
In-Reply-To: <20171204091850.iamq5uslv3hv6bez@vireshk-mac-ubuntu>
Hi Viresh,
On lun., déc. 04 2017, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 01-12-17, 12:25, Gregory CLEMENT wrote:
>> Keep the driver files alphabetically sorted.
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>> drivers/cpufreq/Makefile | 8 ++++----
>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
>> index 812f9e0d01a3..d762e76887e7 100644
>> --- a/drivers/cpufreq/Makefile
>> +++ b/drivers/cpufreq/Makefile
>> @@ -53,22 +53,24 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
>> obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
>>
>> obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
>> +obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
>
> Shouldn't we add them in ascending order of the whole config name and not just
> CPPC_CPUFREQ ?
Here it is the object name that are sorted not the CONFIG_ name. So
cppc_cpufreq comes after brcmstb-avs-cpufreq.
Thanks,
Gregory
>
> --
> viresh
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH v2] ARM: dts: r8a7745: Add APMU node and second CPU core
From: Fabrizio Castro @ 2017-12-06 12:05 UTC (permalink / raw)
To: Simon Horman
Cc: Fabrizio Castro, Rob Herring, Mark Rutland, Magnus Damm,
Russell King, devicetree, linux-renesas-soc, linux-arm-kernel,
Chris Paterson, Geert Uytterhoeven
Add DT node for the Advanced Power Management Unit (APMU), add the
second CPU core, and use "renesas,apmu" as "enable-method".
Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
Signed-off-by: Chris Paterson <chris.paterson2@renesas.com>
Reviewed-by: Biju Das <biju.das@bp.renesas.com>
---
Dear All,
I am reposting this patch now that its dependecy ("ARM: shmobile: rcar-gen2:
Make sure CNTVOFF is initialized on CA7/15") is part of v4.15-rc1, similarly
to patch "ARM: dts: r8a7794: Add SMP support".
v2:
- rebased against renesas-devel-20171205-v4.15-rc2
Thanks
arch/arm/boot/dts/r8a7745.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7745.dtsi b/arch/arm/boot/dts/r8a7745.dtsi
index de13e15..0fa7861 100644
--- a/arch/arm/boot/dts/r8a7745.dtsi
+++ b/arch/arm/boot/dts/r8a7745.dtsi
@@ -38,6 +38,7 @@
cpus {
#address-cells = <1>;
#size-cells = <0>;
+ enable-method = "renesas,apmu";
cpu0: cpu@0 {
device_type = "cpu";
@@ -49,6 +50,15 @@
next-level-cache = <&L2_CA7>;
};
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <1>;
+ clock-frequency = <1000000000>;
+ power-domains = <&sysc R8A7745_PD_CA7_CPU1>;
+ next-level-cache = <&L2_CA7>;
+ };
+
L2_CA7: cache-controller-0 {
compatible = "cache";
cache-unified;
@@ -65,6 +75,12 @@
#size-cells = <2>;
ranges;
+ apmu@e6151000 {
+ compatible = "renesas,r8a7745-apmu", "renesas,apmu";
+ reg = <0 0xe6151000 0 0x188>;
+ cpus = <&cpu0 &cpu1>;
+ };
+
gic: interrupt-controller@f1001000 {
compatible = "arm,gic-400";
#interrupt-cells = <3>;
--
2.7.4
^ permalink raw reply related
* RE: [PATCH 1/8] ARM: dts: r8a7745: Add APMU node and second CPU core
From: Fabrizio Castro @ 2017-12-06 11:59 UTC (permalink / raw)
To: Geert Uytterhoeven, Simon Horman
Cc: Chris Paterson, Rob Herring, Mark Rutland, Magnus Damm,
Russell King, devicetree@vger.kernel.org, Linux-Renesas,
linux-arm-kernel@lists.infradead.org, Geert Uytterhoeven,
Fabrizio Castro, Biju Das
In-Reply-To: <TY1PR06MB089533C44E4B42D647C2CC25C0320@TY1PR06MB0895.apcprd06.prod.outlook.com>
Hello Geert, Simon,
the problems I am seeing are not related to APMU/SMP, I'll send a v2 shortly.
Thanks,
Fab
>
> Hello Geert, Simon,
>
> >
> > As the dependency is in v4.15-rc1, it can be applied now.
> >
>
> we thought exactly the same thing, therefore we rebased and we re-ran our tests, to discover that something broke in between
> versions.
> I'll be in touch once I have more information, but it's a no go for now.
>
> Thanks,
> Fab
>
>
> [https://www2.renesas.eu/media/email/unicef_2017.jpg]
>
> This Christmas, instead of sending out cards, Renesas Electronics Europe have decided to support Unicef with a donation. For further
> details click here<https://www.unicef.org/> to find out about the valuable work they do, helping children all over the world.
> We would like to take this opportunity to wish you a Merry Christmas and a prosperous New Year.
>
>
>
> Renesas Electronics Europe Ltd, Dukes Meadow, Millboard Road, Bourne End, Buckinghamshire, SL8 5FH, UK. Registered in England &
> Wales under Registered No. 04586709.
[https://www2.renesas.eu/media/email/unicef_2017.jpg]
This Christmas, instead of sending out cards, Renesas Electronics Europe have decided to support Unicef with a donation. For further details click here<https://www.unicef.org/> to find out about the valuable work they do, helping children all over the world.
We would like to take this opportunity to wish you a Merry Christmas and a prosperous New Year.
Renesas Electronics Europe Ltd, Dukes Meadow, Millboard Road, Bourne End, Buckinghamshire, SL8 5FH, UK. Registered in England & Wales under Registered No. 04586709.
^ permalink raw reply
* Re: [PATCH 0/2] of: dynamic: restrict overlay by targets
From: Frank Rowand @ 2017-12-06 11:58 UTC (permalink / raw)
To: Alan Tull
Cc: Moritz Fischer, Rob Herring, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel,
linux-fpga-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CANk1AXQ4HV9YAO_p1wrY66nnWY1n2WnR0JoCbAKU6RfrWqf9sQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 12/05/17 12:07, Alan Tull wrote:
> On Mon, Dec 4, 2017 at 7:14 PM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> Hi Alan,
>>
>> In the RFC thread "of: Add whitelist", I did not understand the use case and
>> asked you some questions (30 Nov 2017 07:46:36 -0500), that you seem to have
>> overlooked (or my mail server failed to deliver your answer to me). Can you
>> please answer that question so I can better understand this patch set is
>> needed for.
>
> Hi Frank,
>
> Sorry I missed those, I've replied to the original questions now.
Thanks! I have now replied to several comments in that thread, hoping to keep
the conversation in one thread instead of split across two threads.
-Frank
>
> Alan
>
>>
>> Thanks,
>>
>> Frank
>>
>>
>> On 12/04/17 14:13, Alan Tull wrote:
>>> Restrict which nodes are valid targets for a DT overlay.
>>>
>>> Add a flag bit to struct device_node allowing nodes to be marked as
>>> valid target for overlays.
>>>
>>> A driver that is always intended to handle DT overlays can
>>> enable overlays by calling a function for its DT node.
>>>
>>> For individual nodes that need to be opened up for a specific use,
>>> adding the property "overlay-allowed" enables overlays targeting
>>> that node. I'll need to document the DT property, not sure where
>>> specifically. New file bindings/overlay.txt?
>>>
>>> This patchset differs from the RFC:
>>> * Added a flag bit and got rid of the whitelist
>>> * Renamed the functions that enable a node
>>> * Added a DT property
>>>
>>> Alan Tull (2):
>>> of: overlay: add flag enabling overlays and enable fpga-region
>>> overlays
>>> of: dynamic: add overlay-allowed DT property
>>>
>>> drivers/fpga/of-fpga-region.c | 4 ++++
>>> drivers/of/base.c | 4 ++--
>>> drivers/of/dynamic.c | 3 +++
>>> drivers/of/fdt.c | 3 +++
>>> drivers/of/of_private.h | 2 ++
>>> drivers/of/overlay.c | 26 ++++++++++++++++++++++++++
>>> include/linux/of.h | 19 +++++++++++++++++++
>>> 7 files changed, 59 insertions(+), 2 deletions(-)
>>>
>>
>
--
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: [RFC 0/2] of: Add whitelist
From: Frank Rowand @ 2017-12-06 11:56 UTC (permalink / raw)
To: Alan Tull
Cc: Rob Herring, Pantelis Antoniou, Moritz Fischer,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-fpga-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CANk1AXQ0L4Uzu_KV9fKeVZZFOPQhQ6uq6y+32=bVXRz+iEngLw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 12/05/17 11:33, Alan Tull wrote:
> On Thu, Nov 30, 2017 at 6:46 AM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On 11/29/17 11:11, Alan Tull wrote:
>>> On Wed, Nov 29, 2017 at 7:31 AM, Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>>>> On Wed, Nov 29, 2017 at 3:20 AM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>>>> On 11/27/17 15:58, Alan Tull wrote:
>>>>>> Here's a proposal for a whitelist to lock down the dynamic device tree.
>>>>>>
>>>>>> For an overlay to be accepted, all of its targets are required to be
>>>>>> on a target node whitelist.
>>>>>>
>>>>>> Currently the only way I have to get on the whitelist is calling a
>>>>>> function to add a node. That works for fpga regions, but I think
>>>>>> other uses will need a way of having adding specific nodes from the
>>>>>> base device tree, such as by adding a property like 'allow-overlay;'
>>>>>> or 'allow-overlay = "okay";' If that is acceptable, I could use some
>>>>>> advice on where that particular code should go.
>>>>>>
>>>>>> Alan
>>>>>>
>>>>>> Alan Tull (2):
>>>>>> of: overlay: add whitelist
>>>>>> fpga: of region: add of-fpga-region to whitelist
>>>>>>
>>>>>> drivers/fpga/of-fpga-region.c | 9 ++++++
>>>>>> drivers/of/overlay.c | 73 +++++++++++++++++++++++++++++++++++++++++++
>>>>>> include/linux/of.h | 12 +++++++
>>>>>> 3 files changed, 94 insertions(+)
>>>>>>
>>>>>
>>>>> The plan was to use connectors to restrict where an overlay could be applied.
>>>>> I would prefer not to have multiple methods for accomplishing the same thing
>>>>> unless there is a compelling reason to do so.
>>>>
>>>> Connector nodes need a mechanism to enable themselves, too. I don't
>>>> think connector nodes are going to solve every usecase.
>>>>
>>>> Rob
>>>
>>> The two methods I'm suggesting are intended to handle different cases.
>>> There will exist some drivers that by their nature will want every
>>> instance to be enabled for overlays, such as fpga regions. The other
>>> case is where drivers could support overlays but that's not the
>>> widespread use for them. So no need to enable every instance of that
>>> driver for overlays.
>>
>> I understand what the paragraph, to this point, means. But I had to
>> read it several times to understand it because the way the concept is
>> phrased clashed with my mental model.
>
> Hi Frank,
>
> I see where my explanation is confusing things. I was talking about
> two methods for marking a node as being a valid target for an overlay
> (use a function or add a DT property). I'll drop the idea of using a
> DT property to enable a node for overlays and only focus on my
> proposal of a function to enable nodes.
>
>>
>> The device node is not an instance of a driver, which is why I was
>> getting confused. (Yes, I do understand that the paragraph is talking
>> about multiple device nodes that are bound to the same driver, but
>> my mental model is tied to the device node, not to the driver.)
>>
>> If each of the device nodes in question is a connector, then each of
>> the nodes will bind to a connector driver, based on the value of the
>> compatible property. (This is of course a theoretical assumption on
>> my part since the connectors are not yet implemented.)
>>
>> If the connector node is an fpga, or an fpga region (I may be getting
>> my terminology wrong here - please correct as needed) then an fpga
>> overlay could be applied to the node.
>
> We're still pre-connector currently, but yes I want to mark FPGA
> regions as being valid targets. Then I can use Pantelis' configfs
> interface to apply overlays while leaving the rest of the DT locked
> down. That's the FPGA use of this patch in the pre-connector era of
> things.
>
>>
>> If I understand what you are saying, there will be some fpga connector
>> nodes for which the usage at a given moment might be programmed to
>> function in a manner that will not be described by an overlay, but
>> at a different moment in time may be programmed in a way that needs
>> to be described by an overlay. So there may be some times that it
>> is valid to apply an overlay to the connector node and times that
>> it is not valid to apply an overlay to the connector node.
>
> I think connectors would likely always be valid targets (but I could
> be wrong) and other nodes would not be valid targets. The DT needs a
> way to mark some nodes as valid targets, currently it doesn't have a
> way of doing that. Every connector driver's probe could use this code
> to mark itself as a valid target.
>
>>
>> Is my understanding correct, or am I still confused?
>
> Hope that helps, sorry for the muddled explanation earlier.
No need to be sorry, I always value what you have to say, and usually
become more educated from reading what you write.
We still seem to be talking at cross purposes. It seems that the model
that you are describing is driver centric. My model is node centric.
Once we figure out what the connector implementation and architecture
are, it might be the case that each connector node has a driver bound
to it, and that driver is able to tell the devicetree core code that
the node that it is bound to is a valid place to apply an overlay.
But I currently think that the core infrastructure code is what
should recognize that a connector node is a valid place to apply
an overlay. It _might_ even be the case the the connector
architecture does not result in a driver being bound to the
connector node.
I would really prefer to get the connector architecture (and
maybe also the implementation) before deciding how to handle
the question of how to determine what nodes overlays can be
appplied to.
>
> Alan
>
>>
>> -Frank
>>
>>> In that case the DT property provides some
>>> granularity, only enabling overlays for specific instances of that
>>> driver, leaving the rest of the DT locked down.>
>>> If we only want one method, I would choose having the DT property only
>>> and not exporting the functions. Users would have to add the property
>>> for every FPGA region but that's not really painful. This would have
>>> the benefit of still keeping the DT locked down unless someone
>>> specifically wanted to enable some regions for overlays for their
>>> particular use.
>>>
>>> Alan
>>>
>>
>
--
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
* [RESEND PATCH 2/4] dt-bindings: display: amlogic,meson-dw-hdmi: Add optional HDMI 5V regulator
From: Neil Armstrong @ 2017-12-06 11:54 UTC (permalink / raw)
To: airlied
Cc: Neil Armstrong, dri-devel, linux-amlogic, linux-arm-kernel,
linux-kernel, devicetree
In-Reply-To: <1512561268-29806-1-git-send-email-narmstrong@baylibre.com>
On reference boards and derivatives, the HDMI Logic is powered by an external
5V regulator.
This regulator was set by the Vendor U-Boot, add optional support for it.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt b/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt
index 7f040ed..bf4a180 100644
--- a/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt
+++ b/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt
@@ -48,6 +48,10 @@ Required properties:
Documentation/devicetree/bindings/reset/reset.txt,
the reset-names should be "hdmitx_apb", "hdmitx", "hdmitx_phy"
+Optional properties:
+- hdmi-supply: Optional phandle to an external 5V regulator to power the HDMI
+ logic, as described in the file ../regulator/regulator.txt
+
Required nodes:
The connections to the HDMI ports are modeled using the OF graph
--
2.7.4
^ permalink raw reply related
* [RESEND PATCH 1/4] dt-bindings: display: amlogic,meson-vpu: Add optional power domain property
From: Neil Armstrong @ 2017-12-06 11:54 UTC (permalink / raw)
To: airlied
Cc: Neil Armstrong, dri-devel, linux-amlogic, linux-arm-kernel,
linux-kernel, devicetree
In-Reply-To: <1512561268-29806-1-git-send-email-narmstrong@baylibre.com>
The Video Processing Unit power domain was setup by the Vendor U-Boot,
add support for an optional Power Domain phandle to setup it from the kernel.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
index 00f74ba..057b813 100644
--- a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
+++ b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
@@ -64,6 +64,10 @@ Required properties:
- reg-names: should contain the names of the previous memory regions
- interrupts: should contain the VENC Vsync interrupt number
+Optional properties:
+- power-domains: Optional phandle to associated power domain as described in
+ the file ../power/power_domain.txt
+
Required nodes:
The connections to the VPU output video ports are modeled using the OF graph
--
2.7.4
^ permalink raw reply related
* Re: [PATCH 2/6] cpufreq: ARM: sort the Kconfig menu
From: Gregory CLEMENT @ 2017-12-06 11:52 UTC (permalink / raw)
To: Viresh Kumar
Cc: Thomas Petazzoni, Andrew Lunn, Jason Cooper, linux-pm,
Antoine Tenart, Rafael J. Wysocki, Evan Wang, Nadav Haklai,
devicetree, Rob Herring, Neta Zur Hershkovits, Miquèl Raynal,
Victor Gu, Hua Jing, Marcin Wojtas, Wilson Ding, linux-arm-kernel,
Sebastian Hesselbarth
In-Reply-To: <20171204084124.mchpmw3kta5tddgh@vireshk-mac-ubuntu>
Hi Viresh,
On lun., déc. 04 2017, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 01-12-17, 12:25, Gregory CLEMENT wrote:
>> +config ARM_VEXPRESS_SPC_CPUFREQ
>> + tristate "Versatile Express SPC based CPUfreq driver"
>> + depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC
>> + help
>> + This add the CPUfreq driver support for Versatile Express
>> + big.LITTLE platforms using SPC for power management.
>> +
>> +config ARM_SCPI_CPUFREQ
>
> The order of above two must be reversed ?
Right, I will fix it.
Thanks,
Gregory
>
> --
> viresh
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/6] dt-bindings: marvell: Add documentation for the North Bridge PM on Armada 37xx
From: Gregory CLEMENT @ 2017-12-06 11:51 UTC (permalink / raw)
To: Rob Herring
Cc: Rafael J. Wysocki, Viresh Kumar, linux-pm-u79uwXL29TY76Z2rM5mHXA,
Jason Cooper, Andrew Lunn, Sebastian Hesselbarth,
devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Antoine Tenart,
Miquèl Raynal, Nadav Haklai, Victor Gu, Marcin Wojtas,
Wilson Ding, Hua Jing, Neta Zur Hershkovits, Evan Wang
In-Reply-To: <20171204214706.4sbjgxme3ftmf3l3@rob-hp-laptop>
Hi Rob,
On lun., déc. 04 2017, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Dec 01, 2017 at 12:25:03PM +0100, Gregory CLEMENT wrote:
>> Extend the documentation of the Armada 37xx SoC with the the North
>> Bridge Power Management component.
>>
>> Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> ---
>> .../devicetree/bindings/arm/marvell/armada-37xx.txt | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt b/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
>> index 51336e5fc761..7ad9830d9177 100644
>> --- a/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
>> +++ b/Documentation/devicetree/bindings/arm/marvell/armada-37xx.txt
>> @@ -14,3 +14,22 @@ following property before the previous one:
>> Example:
>>
>> compatible = "marvell,armada-3720-db", "marvell,armada3720", "marvell,armada3710";
>> +
>> +
>> +Power management
>> +----------------
>> +
>> +For power management (particularly DVFS and AVS), the North Bridge
>> +Power Management component is needed:
>> +
>> +Required properties:
>> +- compatible : should contain "marvell,armada-3700-nb-pm", "syscon";
>> +- reg : the register start and length for the North Bridge
>> + Power Management
>> +
>> +Example:
>> +
>> +nb_pm: nb_pm@14000 {
>
> Don't use underscore in node or property names. "syscon" is a better
> choice here.
OK I will change this.
Thanks,
Gregory
>
>> + compatible = "marvell,armada-3700-nb-pm", "syscon";
>> + reg = <0x14000 0x60>;
>> +}
>> --
>> 2.15.0
>>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
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: [RFC 0/2] of: Add whitelist
From: Frank Rowand @ 2017-12-06 11:47 UTC (permalink / raw)
To: Alan Tull
Cc: Rob Herring, Pantelis Antoniou, Moritz Fischer,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-fpga
In-Reply-To: <CANk1AXSAdRT8WWg+xqCWGNYwO=4ke-74ETs+HNObcswEBmKz4g@mail.gmail.com>
On 12/05/17 11:55, Alan Tull wrote:
> On Thu, Nov 30, 2017 at 6:18 AM, Frank Rowand <frowand.list@gmail.com> wrote:
>> On 11/29/17 08:31, Rob Herring wrote:
>>> On Wed, Nov 29, 2017 at 3:20 AM, Frank Rowand <frowand.list@gmail.com> wrote:
>>>> On 11/27/17 15:58, Alan Tull wrote:
>>>>> Here's a proposal for a whitelist to lock down the dynamic device tree.
>>>>>
>>>>> For an overlay to be accepted, all of its targets are required to be
>>>>> on a target node whitelist.
>>>>>
>>>>> Currently the only way I have to get on the whitelist is calling a
>>>>> function to add a node. That works for fpga regions, but I think
>>>>> other uses will need a way of having adding specific nodes from the
>>>>> base device tree, such as by adding a property like 'allow-overlay;'
>>>>> or 'allow-overlay = "okay";' If that is acceptable, I could use some
>>>>> advice on where that particular code should go.
>>>>>
>>>>> Alan
>>>>>
>>>>> Alan Tull (2):
>>>>> of: overlay: add whitelist
>>>>> fpga: of region: add of-fpga-region to whitelist
>>>>>
>>>>> drivers/fpga/of-fpga-region.c | 9 ++++++
>>>>> drivers/of/overlay.c | 73 +++++++++++++++++++++++++++++++++++++++++++
>>>>> include/linux/of.h | 12 +++++++
>>>>> 3 files changed, 94 insertions(+)
>>>>>
>>>>
>>>> The plan was to use connectors to restrict where an overlay could be applied.
>>>> I would prefer not to have multiple methods for accomplishing the same thing
>>>> unless there is a compelling reason to do so.
>>>
>>> Connector nodes need a mechanism to enable themselves, too. I don't
>>> think connector nodes are going to solve every usecase.
>>>
>>> Rob
>>>
>>
>> The overlay code related to connectors does not exist yet, so my comment
>> is going to be theoretical.
>>
>> I would expect the overlay code to check that the target of the overlay
>> fragment is a connector node, so there is no need to explicitly "enable"
>> applying an overlay to a connector node.
>
> This will depend on how connectors are implemented. My proposal in v1
> is that device nodes can have a flag bit. If its not set, then an
> overlay that contains fragments that target that node can't be
> applied. There's probably other ways a connector node could be marked
> as different from other nodes, but a flag bit seems simple. The
> advantage to this scheme is that it gives me something I can use while
> connectors don't exist yet and it will still will be useful later for
> the implementation of connectors (giving connector drivers a way of
> marking their device nodes as valid targets).
I think it is premature to add this code to the kernel when we don't
have an agreed upon architecture for what we are trying to achieve.
>
>>
>> -Frank
>
^ permalink raw reply
* Re: [RFC 0/2] of: Add whitelist
From: Frank Rowand @ 2017-12-06 11:44 UTC (permalink / raw)
To: Rob Herring
Cc: Alan Tull, Rob Herring, Pantelis Antoniou, Moritz Fischer,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Linux Kernel Mailing List, linux-fpga-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CABGGisyiG3mV9s+ksgNFVgoOk=dJrFn4PGj6Ny-O7=o9kmpRTw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 11/30/17 09:39, Rob Herring wrote:
> On Wed, Nov 29, 2017 at 4:47 PM, Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On 11/29/17 04:20, Frank Rowand wrote:
>>> On 11/27/17 15:58, Alan Tull wrote:
>>>> Here's a proposal for a whitelist to lock down the dynamic device tree.
>>>>
>>>> For an overlay to be accepted, all of its targets are required to be
>>>> on a target node whitelist.
>>>>
>>>> Currently the only way I have to get on the whitelist is calling a
>>>> function to add a node. That works for fpga regions, but I think
>>>> other uses will need a way of having adding specific nodes from the
>>>> base device tree, such as by adding a property like 'allow-overlay;'
>>>> or 'allow-overlay = "okay";' If that is acceptable, I could use some
>>>> advice on where that particular code should go.
>>>>
>>>> Alan
>>>>
>>>> Alan Tull (2):
>>>> of: overlay: add whitelist
>>>> fpga: of region: add of-fpga-region to whitelist
>>>>
>>>> drivers/fpga/of-fpga-region.c | 9 ++++++
>>>> drivers/of/overlay.c | 73 +++++++++++++++++++++++++++++++++++++++++++
>>>> include/linux/of.h | 12 +++++++
>>>> 3 files changed, 94 insertions(+)
>>>>
>>>
>>> The plan was to use connectors to restrict where an overlay could be applied.
>>> I would prefer not to have multiple methods for accomplishing the same thing
>>> unless there is a compelling reason to do so.
>>
>> Going back one level in my thinking, I don't think that having a driver mark
>> a node as a location where an overlay fragment can be applied is serving a
>> useful purpose. Any driver, including any driver loaded as a module,
>> could mark a node as ok. I don't see how this is providing any meaningful
>> restriction on where an overlay fragment can be applied.
>
> It serves to separate the setting of which nodes overlays can be
> applied to and the mechanism to apply them (checking permissions). The
> former can't be centralized
My expectation is that determining which nodes overlays can be applied
to can and _should_ be centralized, at least to begin with. If we
loosen the restrictions on valid overlay application nodes then we
_might_ find that we have to provide additional non-centralized permission
granting.
I think that the core devicetree code is the place (for initial implementation)
that determining which nodes an overlay can be applied to. My expectation
is that it will be implicitly obvious to the core devicetree code which
nodes are connector nodes. Given that there have been several different
proposals for connector implementation, my expectation may be completely
wrong. So I am sure I will revisit my expectations the actual
implementation of connectors arrives.
Since the architecture and implementation of connectors is still so
uncertain, I think it is premature to accept the changes proposed in
the patch set, and the next patch set that has been proposed in
response to the conversation in this thread.
> and the latter can be. For example,
> something in the kernel enables overlays on a node or nodes, then the
> overlay is applied with configfs interface and no board specific code
> involved.
I agree that the permission checking should not need to involve board
specific code.
> My concern is not whether any kernel component can enable applying of
> overlays, but userspace. If it is a kernel component, then it is
> explicit. And an OOT kernel module doesn't count because there's no
> ABI guarantee there.
>
> I agree that this patch series alone is not all that useful with only
> in kernel users. It is only really interesting when we have a
> userspace interface. However, an implementation with a flag bit is so
> little code, I'm fine taking it now and not having to update all in
> kernel users when adding a userspace interface.
I think the concept of an API called by a driver, instead of the
devicetree core code determining which nodes an overlay can be
applied to is premature, since there is no direct need for it,
and given that it is little code it can easily be added when it
is needed, and we better understand how it will be used.
>
> Rob
>
--
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] ARM: dts: colibri/apalis: use correct compatible for RTC
From: Fabio Estevam @ 2017-12-06 11:41 UTC (permalink / raw)
To: Stefan Agner
Cc: Shawn Guo, Sascha Hauer, Fabio Estevam,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Sanchayan Maity, Marcel Ziswiler
In-Reply-To: <20171206102929.4755-1-stefan-XLVq0VzYD2Y@public.gmane.org>
On Wed, Dec 6, 2017 at 8:29 AM, Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org> wrote:
> All Toradex Carrier Boards use a st,m41t0 compatible RTC. Compared
> to a st,m41t00 this RTC has also an oscillator fail bit which allows
> to detect when the RTC lost track of time.
>
> Cc: Sanchayan Maity <maitysanchayan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Marcel Ziswiler <marcel.ziswiler-2KBjVHiyJgBBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org>
Reviewed-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@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
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