aboutsummaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen3-adas/recipes-kernel/linux
diff options
context:
space:
mode:
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux')
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch315
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0062-IIO-lsm9ds0-add-IMU-driver.patch972
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch479
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/h3ulcb.cfg22
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/m3ulcb.cfg22
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend5
6 files changed, 1789 insertions, 26 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch
new file mode 100644
index 0000000..0a6e504
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch
@@ -0,0 +1,315 @@
+From 7e4907c9f824c1b52b4c2cf8be91d62c75839e39 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 24 Nov 2016 15:50:25 +0300
+Subject: [PATCH] ASoC: R-Car: add tdm16 support, enable tdm for ssi78
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/sh/rcar/adg.c | 13 ++++------
+ sound/soc/sh/rcar/core.c | 19 ++++++++++----
+ sound/soc/sh/rcar/gen.c | 4 +++
+ sound/soc/sh/rcar/rsnd.h | 3 +++
+ sound/soc/sh/rcar/ssi.c | 66 +++++++++++++++++++++++++++++++++++++++++-------
+ sound/soc/sh/rcar/ssiu.c | 11 +++++---
+ 6 files changed, 91 insertions(+), 25 deletions(-)
+
+diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
+index 85a33ac..a73b45c 100644
+--- a/sound/soc/sh/rcar/adg.c
++++ b/sound/soc/sh/rcar/adg.c
+@@ -46,15 +46,12 @@ struct rsnd_adg {
+ #define adg_mode_flags(adg) (adg->flags)
+
+ #define for_each_rsnd_clk(pos, adg, i) \
+- for (i = 0; \
+- (i < CLKMAX) && \
+- ((pos) = adg->clk[i]); \
+- i++)
++ for (i = 0; i < CLKMAX; i++) \
++ if (((pos) = adg->clk[i])) \
++
+ #define for_each_rsnd_clkout(pos, adg, i) \
+- for (i = 0; \
+- (i < CLKOUTMAX) && \
+- ((pos) = adg->clkout[i]); \
+- i++)
++ for (i = 0; i < CLKOUTMAX; i++) \
++ if (((pos) = adg->clkout[i]))
+ #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+
+ static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
+index 448072c..75e992f 100644
+--- a/sound/soc/sh/rcar/core.c
++++ b/sound/soc/sh/rcar/core.c
+@@ -277,7 +277,10 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
+
+ int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
+ {
+- return rsnd_runtime_channel_for_ssi(io) >= 6;
++ if (rsnd_runtime_channel_for_ssi(io) < 6)
++ return 0;
++ else
++ return rsnd_runtime_channel_for_ssi(io);
+ }
+
+ /*
+@@ -322,8 +325,10 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
+ target = cmd ? cmd : ssiu;
+ }
+
+- mask <<= runtime->channels * 4;
+- val = val & mask;
++ if (runtime->channels < 8) {
++ mask <<= runtime->channels * 4;
++ val = val & mask;
++ }
+
+ switch (runtime->sample_bits) {
+ case 16:
+@@ -680,6 +685,10 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ switch (slots) {
+ case 6:
+ /* TDM Extend Mode */
++ case 8:
++ /* TDM Mode */
++ case 16:
++ /* TDM16 Mode */
+ rsnd_set_slot(rdai, slots, 1);
+ break;
+ default:
+@@ -775,7 +784,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
+ drv->playback.rates = RSND_RATES;
+ drv->playback.formats = RSND_FMTS;
+ drv->playback.channels_min = 2;
+- drv->playback.channels_max = 6;
++ drv->playback.channels_max = 16;
+ drv->playback.stream_name = rdai->playback.name;
+
+ snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
+@@ -783,7 +792,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
+ drv->capture.rates = RSND_RATES;
+ drv->capture.formats = RSND_FMTS;
+ drv->capture.channels_min = 2;
+- drv->capture.channels_max = 6;
++ drv->capture.channels_max = 16;
+ drv->capture.stream_name = rdai->capture.name;
+
+ rdai->playback.rdai = rdai;
+diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
+index e785fe94..fce2a7b 100644
+--- a/sound/soc/sh/rcar/gen.c
++++ b/sound/soc/sh/rcar/gen.c
+@@ -334,6 +334,9 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
+ RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
+ RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
+ RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
++ RSND_GEN_M_REG(SSIFMR, 0x24, 0x40),
++ RSND_GEN_M_REG(SSIFSR, 0x28, 0x40),
++ RSND_GEN_M_REG(SSICRE, 0x30, 0x40),
+ };
+ int ret_ssiu;
+ int ret_scu;
+@@ -407,6 +410,7 @@ int rsnd_gen_probe(struct rsnd_priv *priv)
+ ret = rsnd_gen1_probe(priv);
+ else if (rsnd_is_gen2(priv))
+ ret = rsnd_gen2_probe(priv);
++ /* TODO: add gen3 */
+
+ if (ret < 0)
+ dev_err(dev, "unknown generation R-Car sound device\n");
+diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
+index bf9596b..8ccd9d0 100644
+--- a/sound/soc/sh/rcar/rsnd.h
++++ b/sound/soc/sh/rcar/rsnd.h
+@@ -166,6 +166,9 @@ enum rsnd_reg {
+ RSND_REG_SSITDR,
+ RSND_REG_SSIRDR,
+ RSND_REG_SSIWSR,
++ RSND_REG_SSIFMR,
++ RSND_REG_SSIFSR,
++ RSND_REG_SSICRE,
+
+ RSND_REG_MAX,
+ };
+diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
+index 630cb6b..62abb58 100644
+--- a/sound/soc/sh/rcar/ssi.c
++++ b/sound/soc/sh/rcar/ssi.c
+@@ -35,7 +35,13 @@
+ #define DWL_24 (5 << 19) /* Data Word Length */
+ #define DWL_32 (6 << 19) /* Data Word Length */
+
+-#define SWL_32 (3 << 16) /* R/W System Word Length */
++#define SWL_16 (1 << 16) /* R/W System Word Length 16 bit */
++#define SWL_24 (2 << 16) /* R/W System Word Length 24 bit */
++#define SWL_32 (3 << 16) /* R/W System Word Length 32 bit */
++#define SWL_48 (4 << 16) /* R/W System Word Length 48 bit */
++#define SWL_64 (5 << 16) /* R/W System Word Length 64 bit */
++#define SWL_128 (6 << 16) /* R/W System Word Length 128 bit */
++#define SWL_256 (7 << 16) /* R/W System Word Length 256 bit */
+ #define SCKD (1 << 15) /* Serial Bit Clock Direction */
+ #define SWSD (1 << 14) /* Serial WS Direction */
+ #define SCKP (1 << 13) /* Serial Bit Clock Polarity */
+@@ -61,6 +67,11 @@
+ #define CONT (1 << 8) /* WS Continue Function */
+ #define WS_MODE (1 << 0) /* WS Mode */
+
++/*
++ * SSICRE
++ */
++#define CHNL_16 (1 << 0) /* Channels */
++
+ #define SSI_NAME "ssi"
+
+ struct rsnd_ssi {
+@@ -71,6 +82,7 @@ struct rsnd_ssi {
+ u32 cr_own;
+ u32 cr_clk;
+ u32 cr_mode;
++ u32 cre_own;
+ u32 wsr;
+ int chan;
+ int rate;
+@@ -224,8 +236,12 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
+
+ /*
+ * Find best clock, and try to start ADG
++ *
++ * Start with j = 1 because:
++ * "CKDV = 000 is invalid when WS_MODE = 1 or CONT = 1 in the WS
++ * Mode Register."
+ */
+- for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
++ for (j = 1; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
+ * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+@@ -239,15 +255,23 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
+ /*
+ * this driver is assuming that
+ * system word is 32bit x chan
+- * see rsnd_ssi_init()
++ *
++ * Expect:
++ * TDM 16ch mode where SWL should be 16 bit
+ */
+- main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
++ if (chan != 16)
++ main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
++ else
++ main_rate = rate * 16 * chan * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+ if (0 == ret) {
+- ssi->cr_clk = FORCE | SWL_32 |
++ ssi->cr_clk = FORCE |
+ SCKD | SWSD | CKDV(j);
+- ssi->wsr = CONT;
++ if (chan != 16)
++ ssi->cr_clk |= SWL_32;
++ else
++ ssi->cr_clk |= SWL_16;
+
+ ssi->rate = rate;
+
+@@ -288,10 +312,13 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
+ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io)
+ {
++ struct rsnd_priv *priv = rsnd_io_to_priv(io);
++ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 cr_own;
++ u32 cre_own;
+ u32 cr_mode;
+ u32 wsr;
+ int is_tdm;
+@@ -301,12 +328,24 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
+ /*
+ * always use 32bit system word.
+ * see also rsnd_ssi_master_clk_enable()
++ * NOPE!
+ */
+- cr_own = FORCE | SWL_32 | PDTA;
++ cr_own = FORCE | PDTA;
++ cre_own = 0;
++
++ /*
++ * TDM16 mode can handle only 16bit data
++ */
++ if (rsnd_runtime_channel_for_ssi(io) != 16)
++ cr_own |= SWL_32;
++ else
++ cr_own |= SWL_16;
++
++ cre_own = 0;
+
+ if (rdai->bit_clk_inv)
+ cr_own |= SCKP;
+- if (rdai->frm_clk_inv ^ is_tdm)
++ if (rdai->frm_clk_inv ^ (!!is_tdm))
+ cr_own |= SWSP;
+ if (rdai->data_alignment)
+ cr_own |= SDTA;
+@@ -337,12 +376,20 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
+ * rsnd_ssiu_init_gen2()
+ */
+ wsr = ssi->wsr;
+- if (is_tdm) {
++ if (is_tdm == 8) {
+ wsr |= WS_MODE;
+ cr_own |= CHNL_8;
++ } else if (is_tdm == 16) {
++ wsr |= WS_MODE;
++ cre_own |= CHNL_16;
++ } else if (is_tdm) {
++ dev_err(dev, "%s[%d] invalid tdm channels %d\n",
++ rsnd_mod_name(mod),
++ rsnd_mod_id(mod), is_tdm);
+ }
+
+ ssi->cr_own = cr_own;
++ ssi->cre_own = cre_own;
+ ssi->cr_mode = cr_mode;
+ ssi->wsr = wsr;
+ }
+@@ -352,6 +399,7 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ rsnd_mod_write(mod, SSIWSR, ssi->wsr);
++ rsnd_mod_write(mod, SSICRE, ssi->cre_own);
+ rsnd_mod_write(mod, SSICR, ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_mode); /* without EN */
+diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
+index 3f95d6b..19f5f5e 100644
+--- a/sound/soc/sh/rcar/ssiu.c
++++ b/sound/soc/sh/rcar/ssiu.c
+@@ -61,13 +61,18 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
+ case 4:
+ shift = 16;
+ break;
++ case 8:
++ /* ignore? */
++ break;
+ default:
+ return -EINVAL;
+ }
+
+- mask1 |= 0x3 << shift;
+- val1 = rsnd_rdai_is_clk_master(rdai) ?
+- 0x2 << shift : 0x1 << shift;
++ if (shift >= 0) {
++ mask1 |= 0x3 << shift;
++ val1 = rsnd_rdai_is_clk_master(rdai) ?
++ 0x2 << shift : 0x1 << shift;
++ }
+
+ } else if (multi_ssi_slaves) {
+
+--
+1.9.1
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0062-IIO-lsm9ds0-add-IMU-driver.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0062-IIO-lsm9ds0-add-IMU-driver.patch
new file mode 100644
index 0000000..d3a329f
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0062-IIO-lsm9ds0-add-IMU-driver.patch
@@ -0,0 +1,972 @@
+From 4631208dd9557e0183acba14dec79318f9cabdc3 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 7 Jun 2017 13:35:52 +0300
+Subject: [PATCH] IIO: lsm9ds0: add IMU driver
+
+Taken from:
+https://github.com/mpod/kernel-playground
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/iio/imu/Kconfig | 11 +
+ drivers/iio/imu/Makefile | 2 +
+ drivers/iio/imu/lsm9ds0.c | 912 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 925 insertions(+)
+ create mode 100644 drivers/iio/imu/lsm9ds0.c
+
+diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
+index 1f1ad41ef881..063c09b8fc53 100644
+--- a/drivers/iio/imu/Kconfig
++++ b/drivers/iio/imu/Kconfig
+@@ -38,6 +38,17 @@ config KMX61
+ To compile this driver as module, choose M here: the module will
+ be called kmx61.
+
++config LSM9DS0
++ tristate "ST LSM9DS0 9-axis IMU"
++ depends on I2C
++ select IIO_BUFFER
++ select IIO_TRIGGERED_BUFFER
++ help
++ Say Y here if you want to build a driver for ST LSM9DS0
++ system-in-package featuring a 3D digital linear acceleration
++ sensor, a 3D digital angular rate sensor, and a 3D digital magnetic
++ sensor.
++
+ source "drivers/iio/imu/inv_mpu6050/Kconfig"
+
+ endmenu
+diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
+index c71bcd30dc38..4de076d0766e 100644
+--- a/drivers/iio/imu/Makefile
++++ b/drivers/iio/imu/Makefile
+@@ -13,6 +13,8 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
+ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
+ obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
+
++obj-$(CONFIG_LSM9DS0) += lsm9ds0.o
++
+ obj-y += bmi160/
+ obj-y += inv_mpu6050/
+
+diff --git a/drivers/iio/imu/lsm9ds0.c b/drivers/iio/imu/lsm9ds0.c
+new file mode 100644
+index 000000000000..15e2671daef9
+--- /dev/null
++++ b/drivers/iio/imu/lsm9ds0.c
+@@ -0,0 +1,912 @@
++/*
++ * lsm9ds0_gyro.c
++ *
++ * Copyright (C) 2016 Matija Podravec <matija_podravec@fastmail.fm>
++ *
++ * 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 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ *
++ * Driver for ST LSM9DS0 gyroscope, accelerometer, and magnetometer sensor.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/bitops.h>
++#include <linux/iio/iio.h>
++#include <linux/iio/sysfs.h>
++#include <linux/iio/trigger_consumer.h>
++#include <linux/iio/kfifo_buf.h>
++
++#define LSM9DS0_WHO_AM_I_REG (0x0F)
++#define LSM9DS0_CTRL_REG1_G_REG (0x20)
++#define LSM9DS0_CTRL_REG2_G_REG (0x21)
++#define LSM9DS0_CTRL_REG3_G_REG (0x22)
++#define LSM9DS0_CTRL_REG4_G_REG (0x23)
++#define LSM9DS0_CTRL_REG5_G_REG (0x24)
++#define LSM9DS0_REFERENCE_G_REG (0x25)
++#define LSM9DS0_STATUS_REG_G_REG (0x27)
++#define LSM9DS0_OUT_X_L_G_REG (0x28)
++#define LSM9DS0_OUT_X_H_G_REG (0x29)
++#define LSM9DS0_OUT_Y_L_G_REG (0x2A)
++#define LSM9DS0_OUT_Y_H_G_REG (0x2B)
++#define LSM9DS0_OUT_Z_L_G_REG (0x2C)
++#define LSM9DS0_OUT_Z_H_G_REG (0x2D)
++#define LSM9DS0_FIFO_CTRL_REG_G_REG (0x2E)
++#define LSM9DS0_FIFO_SRC_REG_G_REG (0x2F)
++#define LSM9DS0_INT1_CFG_G_REG (0x30)
++#define LSM9DS0_INT1_SRC_G_REG (0x31)
++#define LSM9DS0_INT1_TSH_XH_G_REG (0x32)
++#define LSM9DS0_INT1_TSH_XL_G_REG (0x33)
++#define LSM9DS0_INT1_TSH_YH_G_REG (0x34)
++#define LSM9DS0_INT1_TSH_YL_G_REG (0x35)
++#define LSM9DS0_INT1_TSH_ZH_G_REG (0x36)
++#define LSM9DS0_INT1_TSH_ZL_G_REG (0x37)
++#define LSM9DS0_INT1_DURATION_G_REG (0x38)
++#define LSM9DS0_OUT_TEMP_L_XM_REG (0x05)
++#define LSM9DS0_OUT_TEMP_H_XM_REG (0x06)
++#define LSM9DS0_STATUS_REG_M_REG (0x07)
++#define LSM9DS0_OUT_X_L_M_REG (0x08)
++#define LSM9DS0_OUT_X_H_M_REG (0x09)
++#define LSM9DS0_OUT_Y_L_M_REG (0x0A)
++#define LSM9DS0_OUT_Y_H_M_REG (0x0B)
++#define LSM9DS0_OUT_Z_L_M_REG (0x0C)
++#define LSM9DS0_OUT_Z_H_M_REG (0x0D)
++#define LSM9DS0_INT_CTRL_REG_M_REG (0x12)
++#define LSM9DS0_INT_SRC_REG_M_REG (0x13)
++#define LSM9DS0_INT_THS_L_M_REG (0x14)
++#define LSM9DS0_INT_THS_H_M_REG (0x15)
++#define LSM9DS0_OFFSET_X_L_M_REG (0x16)
++#define LSM9DS0_OFFSET_X_H_M_REG (0x17)
++#define LSM9DS0_OFFSET_Y_L_M_REG (0x18)
++#define LSM9DS0_OFFSET_Y_H_M_REG (0x19)
++#define LSM9DS0_OFFSET_Z_L_M_REG (0x1A)
++#define LSM9DS0_OFFSET_Z_H_M_REG (0x1B)
++#define LSM9DS0_REFERENCE_X_REG (0x1C)
++#define LSM9DS0_REFERENCE_Y_REG (0x1D)
++#define LSM9DS0_REFERENCE_Z_REG (0x1E)
++#define LSM9DS0_CTRL_REG0_XM_REG (0x1F)
++#define LSM9DS0_CTRL_REG1_XM_REG (0x20)
++#define LSM9DS0_CTRL_REG2_XM_REG (0x21)
++#define LSM9DS0_CTRL_REG3_XM_REG (0x22)
++#define LSM9DS0_CTRL_REG4_XM_REG (0x23)
++#define LSM9DS0_CTRL_REG5_XM_REG (0x24)
++#define LSM9DS0_CTRL_REG6_XM_REG (0x25)
++#define LSM9DS0_CTRL_REG7_XM_REG (0x26)
++#define LSM9DS0_STATUS_REG_A_REG (0x27)
++#define LSM9DS0_OUT_X_L_A_REG (0x28)
++#define LSM9DS0_OUT_X_H_A_REG (0x29)
++#define LSM9DS0_OUT_Y_L_A_REG (0x2A)
++#define LSM9DS0_OUT_Y_H_A_REG (0x2B)
++#define LSM9DS0_OUT_Z_L_A_REG (0x2C)
++#define LSM9DS0_OUT_Z_H_A_REG (0x2D)
++#define LSM9DS0_FIFO_CTRL_REG_REG (0x2E)
++#define LSM9DS0_FIFO_SRC_REG_REG (0x2F)
++#define LSM9DS0_INT_GEN_1_REG_REG (0x30)
++#define LSM9DS0_INT_GEN_1_SRC_REG (0x31)
++#define LSM9DS0_INT_GEN_1_THS_REG (0x32)
++#define LSM9DS0_INT_GEN_1_DURATION_REG (0x33)
++#define LSM9DS0_INT_GEN_2_REG_REG (0x34)
++#define LSM9DS0_INT_GEN_2_SRC_REG (0x35)
++#define LSM9DS0_INT_GEN_2_THS_REG (0x36)
++#define LSM9DS0_INT_GEN_2_DURATION_REG (0x37)
++#define LSM9DS0_CLICK_CFG_REG (0x38)
++#define LSM9DS0_CLICK_SRC_REG (0x39)
++#define LSM9DS0_CLICK_THS_REG (0x3A)
++#define LSM9DS0_TIME_LIMIT_REG (0x3B)
++#define LSM9DS0_TIME_LATENCY_REG (0x3C)
++#define LSM9DS0_TIME_WINDOW_REG (0x3D)
++#define LSM9DS0_ACT_THS_REG (0x3E)
++#define LSM9DS0_ACT_DUR_REG (0x3F)
++
++#define LSM9DS0_GYRO_ODR_95HZ_VAL (0x00 << 6)
++#define LSM9DS0_GYRO_ODR_190HZ_VAL (0x01 << 6)
++#define LSM9DS0_GYRO_ODR_380HZ_VAL (0x02 << 6)
++#define LSM9DS0_GYRO_ODR_760HZ_VAL (0x03 << 6)
++
++#define LSM9DS0_ACCEL_POWER_DOWN (0x00 << 4)
++#define LSM9DS0_ACCEL_ODR_3_125HZ_VAL (0x01 << 4)
++#define LSM9DS0_ACCEL_ODR_6_25HZ_VAL (0x02 << 4)
++#define LSM9DS0_ACCEL_ODR_12_5HZ_VAL (0x03 << 4)
++#define LSM9DS0_ACCEL_ODR_25HZ_VAL (0x04 << 4)
++#define LSM9DS0_ACCEL_ODR_50HZ_VAL (0x05 << 4)
++#define LSM9DS0_ACCEL_ODR_100HZ_VAL (0x06 << 4)
++#define LSM9DS0_ACCEL_ODR_200HZ_VAL (0x07 << 4)
++#define LSM9DS0_ACCEL_ODR_400HZ_VAL (0x08 << 4)
++#define LSM9DS0_ACCEL_ODR_800HZ_VAL (0x09 << 4)
++#define LSM9DS0_ACCEL_ODR_1600HZ_VAL (0x0A << 4)
++
++#define LSM9DS0_ACCEL_FS_MASK (0x03 << 3)
++#define LSM9DS0_ACCEL_FS_2G_VAL (0x00 << 3)
++#define LSM9DS0_ACCEL_FS_4G_VAL (0x01 << 3)
++#define LSM9DS0_ACCEL_FS_6G_VAL (0x02 << 3)
++#define LSM9DS0_ACCEL_FS_8G_VAL (0x03 << 3)
++#define LSM9DS0_ACCEL_FS_16G_VAL (0x04 << 3)
++#define LSM9DS0_ACCEL_FS_2G_GAIN 61 /* ug/LSB */
++#define LSM9DS0_ACCEL_FS_4G_GAIN 122 /* ug/LSB */
++#define LSM9DS0_ACCEL_FS_6G_GAIN 183 /* ug/LSB */
++#define LSM9DS0_ACCEL_FS_8G_GAIN 244 /* ug/LSB */
++#define LSM9DS0_ACCEL_FS_16G_GAIN 732 /* ug/LSB */
++
++#define LSM9DS0_MAGN_ODR_3_125HZ_VAL (0x00 << 2)
++#define LSM9DS0_MAGN_ODR_6_25HZ_VAL (0x01 << 2)
++#define LSM9DS0_MAGN_ODR_12_5HZ_VAL (0x02 << 2)
++#define LSM9DS0_MAGN_ODR_25HZ_VAL (0x03 << 2)
++#define LSM9DS0_MAGN_ODR_50HZ_VAL (0x04 << 2)
++#define LSM9DS0_MAGN_ODR_100HZ_VAL (0x05 << 2)
++
++#define LSM9DS0_MAGN_FS_MASK (0x03 << 5)
++#define LSM9DS0_MAGN_FS_2GAUSS_VAL (0x00 << 5)
++#define LSM9DS0_MAGN_FS_4GAUSS_VAL (0x01 << 5)
++#define LSM9DS0_MAGN_FS_8GAUSS_VAL (0x02 << 5)
++#define LSM9DS0_MAGN_FS_12GAUSS_VAL (0x03 << 5)
++#define LSM9DS0_MAGN_FS_2GAUSS_GAIN 80 /* ugauss/LSB */
++#define LSM9DS0_MAGN_FS_4GAUSS_GAIN 160 /* ugauss/LSB */
++#define LSM9DS0_MAGN_FS_8GAUSS_GAIN 320 /* ugauss/LSB */
++#define LSM9DS0_MAGN_FS_12GAUSS_GAIN 480 /* ugauss/LSB */
++
++#define LSM9DS0_GYRO_FS_MASK (0x03 << 4)
++#define LSM9DS0_GYRO_FS_245DPS_VAL (0x00 << 4)
++#define LSM9DS0_GYRO_FS_500DPS_VAL (0x01 << 4)
++#define LSM9DS0_GYRO_FS_2000DPS_VAL (0x02 << 4)
++#define LSM9DS0_GYRO_FS_245DPS_GAIN 8750 /* udps/LSB */
++#define LSM9DS0_GYRO_FS_500DPS_GAIN 17500 /* udps/LSB */
++#define LSM9DS0_GYRO_FS_2000DPS_GAIN 70000 /* udps/LSB */
++
++#define LSM9DS0_GYRO_X_EN BIT(1)
++#define LSM9DS0_GYRO_Y_EN BIT(0)
++#define LSM9DS0_GYRO_Z_EN BIT(2)
++#define LSM9DS0_GYRO_POWER_DOWN (0x00 << 3)
++#define LSM9DS0_GYRO_NORMAL_MODE BIT(3)
++#define LSM9DS0_ACCEL_X_EN BIT(0)
++#define LSM9DS0_ACCEL_Y_EN BIT(1)
++#define LSM9DS0_ACCEL_Z_EN BIT(2)
++#define LSM9DS0_TEMP_EN BIT(7)
++#define LSM9DS0_MAGN_LOW_RES_VAL (0x00 << 5)
++#define LSM9DS0_MAGN_HIGH_RES_VAL (0x03 << 5)
++#define LSM9DS0_MAGN_POWER_DOWN (0x02)
++#define LSM9DS0_MAGN_CONT_CONV_MODE (0x00)
++#define LSM9DS0_MAGN_SINGLE_CONV_MODE (0x01)
++
++#define LSM9DS0_GYRO_ID 0xD4
++#define LSM9DS0_ACCEL_MAGN_ID 0x49
++
++enum { SCAN_INDEX_X, SCAN_INDEX_Y, SCAN_INDEX_Z };
++enum {
++ SCAN_INDEX_ACCEL_X, SCAN_INDEX_ACCEL_Y, SCAN_INDEX_ACCEL_Z,
++ SCAN_INDEX_MAGN_X, SCAN_INDEX_MAGN_Y, SCAN_INDEX_MAGN_Z
++};
++enum { GYRO, ACCEL_MAGN };
++
++struct lsm9ds0_data {
++ struct i2c_client *client;
++ struct mutex lock;
++ int sensor_type;
++ int gyro_scale;
++ int accel_scale;
++ int magn_scale;
++};
++
++struct sensor_fs_avl {
++ unsigned int num;
++ u8 value;
++ unsigned int gain;
++};
++
++static const struct sensor_fs_avl lsm9ds0_gyro_fs_avl[3] = {
++ {245, LSM9DS0_GYRO_FS_245DPS_VAL, LSM9DS0_GYRO_FS_245DPS_GAIN},
++ {500, LSM9DS0_GYRO_FS_500DPS_VAL, LSM9DS0_GYRO_FS_500DPS_GAIN},
++ {2000, LSM9DS0_GYRO_FS_2000DPS_VAL, LSM9DS0_GYRO_FS_2000DPS_GAIN},
++};
++
++static const struct sensor_fs_avl lsm9ds0_accel_fs_avl[5] = {
++ {2, LSM9DS0_ACCEL_FS_2G_VAL, LSM9DS0_ACCEL_FS_2G_GAIN},
++ {4, LSM9DS0_ACCEL_FS_4G_VAL, LSM9DS0_ACCEL_FS_4G_GAIN},
++ {6, LSM9DS0_ACCEL_FS_6G_VAL, LSM9DS0_ACCEL_FS_6G_GAIN},
++ {8, LSM9DS0_ACCEL_FS_8G_VAL, LSM9DS0_ACCEL_FS_8G_GAIN},
++ {16, LSM9DS0_ACCEL_FS_16G_VAL, LSM9DS0_ACCEL_FS_16G_GAIN},
++};
++
++static const struct sensor_fs_avl lsm9ds0_magn_fs_avl[4] = {
++ {2, LSM9DS0_MAGN_FS_2GAUSS_VAL, LSM9DS0_MAGN_FS_2GAUSS_GAIN},
++ {4, LSM9DS0_MAGN_FS_4GAUSS_VAL, LSM9DS0_MAGN_FS_4GAUSS_GAIN},
++ {8, LSM9DS0_MAGN_FS_8GAUSS_VAL, LSM9DS0_MAGN_FS_8GAUSS_GAIN},
++ {12, LSM9DS0_MAGN_FS_12GAUSS_VAL, LSM9DS0_MAGN_FS_12GAUSS_GAIN},
++};
++
++static ssize_t lsm9ds0_show_scale_avail(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ //struct iio_dev *indio_dev = dev_to_iio_dev(dev);
++ //struct lsm9ds0_data *data = iio_priv(indio_dev);
++ size_t len = 0;
++ int n;
++ const struct sensor_fs_avl (*avl)[];
++
++ if (strcmp(attr->attr.name, "in_gyro_scale_available") == 0) {
++ avl = &lsm9ds0_gyro_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_gyro_fs_avl);
++ } else if (strcmp(attr->attr.name, "in_accel_scale_available") == 0) {
++ avl = &lsm9ds0_accel_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_accel_fs_avl);
++ } else if (strcmp(attr->attr.name, "in_magn_scale_available") == 0) {
++ avl = &lsm9ds0_magn_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_magn_fs_avl);
++ } else {
++ return -EINVAL;
++ }
++
++ while (n-- > 0)
++ len += scnprintf(buf + len, PAGE_SIZE - len,
++ "0.%06u ", (*avl)[n].gain);
++ buf[len - 1] = '\n';
++
++ return len;
++}
++
++static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
++ lsm9ds0_show_scale_avail, NULL, 0);
++static IIO_DEVICE_ATTR(in_magn_scale_available, S_IRUGO,
++ lsm9ds0_show_scale_avail, NULL, 0);
++static IIO_DEVICE_ATTR(in_gyro_scale_available, S_IRUGO,
++ lsm9ds0_show_scale_avail, NULL, 0);
++
++static struct attribute *lsm9ds0_gyro_attributes[] = {
++ &iio_dev_attr_in_gyro_scale_available.dev_attr.attr,
++ NULL
++};
++
++static struct attribute *lsm9ds0_accel_magn_attributes[] = {
++ &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
++ &iio_dev_attr_in_magn_scale_available.dev_attr.attr,
++ NULL
++};
++
++static const struct attribute_group lsm9ds0_gyro_group = {
++ .attrs = lsm9ds0_gyro_attributes,
++};
++
++static const struct attribute_group lsm9ds0_accel_magn_group = {
++ .attrs = lsm9ds0_accel_magn_attributes,
++};
++
++static const struct iio_buffer_setup_ops lsm9ds0_buffer_setup_ops = {
++ .postenable = &iio_triggered_buffer_postenable,
++ .predisable = &iio_triggered_buffer_predisable,
++};
++
++static const struct iio_chan_spec lsm9ds0_gyro_channels[] = {
++ {
++ .type = IIO_ANGL_VEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_X,
++ .scan_index = SCAN_INDEX_X,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_ANGL_VEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Y,
++ .scan_index = SCAN_INDEX_Y,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_ANGL_VEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Z,
++ .scan_index = SCAN_INDEX_Z,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ },
++ IIO_CHAN_SOFT_TIMESTAMP(3),
++};
++
++static const struct iio_chan_spec lsm9ds0_accel_magn_channels[] = {
++ {
++ .type = IIO_ACCEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_X,
++ .scan_index = SCAN_INDEX_ACCEL_X,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_ACCEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Y,
++ .scan_index = SCAN_INDEX_ACCEL_Y,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_ACCEL,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Z,
++ .scan_index = SCAN_INDEX_ACCEL_Z,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_MAGN,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_X,
++ .scan_index = SCAN_INDEX_MAGN_X,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_MAGN,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Y,
++ .scan_index = SCAN_INDEX_MAGN_Y,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ }, {
++ .type = IIO_MAGN,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
++ .modified = 1,
++ .channel2 = IIO_MOD_Z,
++ .scan_index = SCAN_INDEX_MAGN_Z,
++ .scan_type = {
++ .sign = 's',
++ .realbits = 16,
++ .storagebits = 16,
++ .shift = 0,
++ .endianness = IIO_LE,
++ },
++ },
++ IIO_CHAN_SOFT_TIMESTAMP(6),
++};
++
++static int lsm9ds0_read_measurements(struct i2c_client *client,
++ u8 reg_address, s16 *x, s16 *y, s16 *z)
++{
++ int ret;
++ u8 buf[6] = {0};
++
++ buf[0] = 0x80 | reg_address;
++ ret = i2c_master_send(client, buf, 1);
++ if (ret < 0)
++ return ret;
++
++ ret = i2c_master_recv(client, buf, 6);
++ if (ret < 0)
++ return ret;
++
++ *x = (buf[1] << 8) | buf[0];
++ *y = (buf[3] << 8) | buf[2];
++ *z = (buf[5] << 8) | buf[4];
++ return ret;
++}
++
++static int lsm9ds0_read_raw(struct iio_dev *iio_dev,
++ struct iio_chan_spec const *channel,
++ int *val, int *val2, long mask)
++{
++ struct lsm9ds0_data *data = iio_priv(iio_dev);
++ int err = 0;
++ s16 x = 0, y = 0, z = 0;
++ int scale = 0;
++
++ switch (mask) {
++ case IIO_CHAN_INFO_RAW:
++ mutex_lock(&data->lock);
++ switch (channel->type) {
++ case IIO_ANGL_VEL:
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_G_REG, &x, &y, &z);
++ scale = data->gyro_scale;
++ break;
++ case IIO_ACCEL:
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_A_REG, &x, &y, &z);
++ scale = data->accel_scale;
++ break;
++ case IIO_MAGN:
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_M_REG, &x, &y, &z);
++ scale = data->magn_scale;
++ break;
++ default:
++ return -EINVAL;
++ }
++ mutex_unlock(&data->lock);
++ if (err < 0)
++ goto read_error;
++
++ switch (channel->channel2) {
++ case IIO_MOD_X:
++ *val = x;
++ break;
++ case IIO_MOD_Y:
++ *val = y;
++ break;
++ case IIO_MOD_Z:
++ *val = z;
++ break;
++ }
++ return IIO_VAL_INT;
++ case IIO_CHAN_INFO_SCALE:
++ *val = 0;
++ switch (channel->type) {
++ case IIO_ANGL_VEL:
++ *val2 = data->gyro_scale;
++ break;
++ case IIO_ACCEL:
++ *val2 = data->accel_scale;
++ break;
++ case IIO_MAGN:
++ *val2 = data->magn_scale;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return IIO_VAL_INT_PLUS_MICRO;
++ default:
++ return -EINVAL;
++ }
++
++read_error:
++ return err;
++}
++
++static int lsm9ds0_write_config(struct i2c_client *client,
++ u8 reg_address, u8 mask, u8 value)
++{
++ u8 reg;
++ s32 ret;
++ ret = i2c_smbus_read_byte_data(client, reg_address);
++ if (ret < 0)
++ return -EINVAL;
++
++ reg = (u8)ret;
++ reg &= ~mask;
++ reg |= value;
++
++ ret = i2c_smbus_write_byte_data(client, reg_address, reg);
++
++ return ret;
++}
++
++static int lsm9ds0_write_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *channel,
++ int val, int val2, long mask)
++{
++ struct lsm9ds0_data *data = iio_priv(indio_dev);
++ struct i2c_client *client = data->client;
++ const struct sensor_fs_avl (*avl)[];
++ int n, i, ret;
++ u8 reg_address, reg_mask, new_value;
++ int *scale_in_data;
++
++ mutex_lock(&data->lock);
++ switch (mask) {
++ case IIO_CHAN_INFO_SCALE:
++ dev_info(&client->dev, "Vals %d %d\n", val, val2);
++ switch (channel->type) {
++ case IIO_ANGL_VEL:
++ avl = &lsm9ds0_gyro_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_gyro_fs_avl);
++ reg_address = LSM9DS0_CTRL_REG4_G_REG;
++ reg_mask = LSM9DS0_GYRO_FS_MASK;
++ scale_in_data = &(data->gyro_scale);
++ break;
++ case IIO_ACCEL:
++ avl = &lsm9ds0_accel_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_accel_fs_avl);
++ reg_address = LSM9DS0_CTRL_REG2_XM_REG;
++ reg_mask = LSM9DS0_ACCEL_FS_MASK;
++ scale_in_data = &(data->accel_scale);
++ break;
++ case IIO_MAGN:
++ avl = &lsm9ds0_magn_fs_avl;
++ n = ARRAY_SIZE(lsm9ds0_magn_fs_avl);
++ reg_address = LSM9DS0_CTRL_REG6_XM_REG;
++ reg_mask = LSM9DS0_MAGN_FS_MASK;
++ scale_in_data = &(data->magn_scale);
++ break;
++ default:
++ ret = -EINVAL;
++ goto done;
++ }
++ ret = -EINVAL;
++ for (i = 0; i < n; i++) {
++ if ((*avl)[i].gain == val2) {
++ ret = 0;
++ new_value = (*avl)[i].value;
++ break;
++ }
++ }
++ if (ret < 0)
++ goto done;
++
++ ret = lsm9ds0_write_config(client, reg_address, reg_mask, new_value);
++ if (ret < 0)
++ goto done;
++
++ *scale_in_data = (*avl)[i].gain;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++done:
++ mutex_unlock(&data->lock);
++ return ret;
++}
++
++static irqreturn_t lsm9ds0_trigger_h(int irq, void *p)
++{
++ struct iio_poll_func *pf = p;
++ struct iio_dev *indio_dev = pf->indio_dev;
++ struct lsm9ds0_data *data = iio_priv(indio_dev);
++ u32 *buf_data;
++ int i, j;
++ s16 x1, y1, z1, x2, y2, z2;
++ int err;
++
++ buf_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
++ if (!buf_data)
++ goto done;
++
++ mutex_lock(&data->lock);
++ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) {
++
++ if (data->sensor_type == GYRO) {
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_G_REG, &x1, &y1, &z1);
++ if (err < 0)
++ goto free_buf;
++ } else if (data->sensor_type == ACCEL_MAGN) {
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_A_REG, &x1, &y1, &z1);
++ if (err < 0)
++ goto free_buf;
++ err = lsm9ds0_read_measurements(data->client,
++ LSM9DS0_OUT_X_L_M_REG, &x2, &y2, &z2);
++ if (err < 0)
++ goto free_buf;
++ } else
++ goto free_buf;
++
++ for (i = 0, j = 0;
++ i < bitmap_weight(indio_dev->active_scan_mask, indio_dev->masklength);
++ i++, j++) {
++ j = find_next_bit(indio_dev->active_scan_mask, indio_dev->masklength, j);
++
++ if (data->sensor_type == GYRO) {
++ switch (j) {
++ case SCAN_INDEX_X:
++ buf_data[i] = x1;
++ break;
++ case SCAN_INDEX_Y:
++ buf_data[i] = y1;
++ break;
++ case SCAN_INDEX_Z:
++ buf_data[i] = z1;
++ break;
++ default:
++ break;
++ }
++ } else {
++ switch (j) {
++ case SCAN_INDEX_ACCEL_X:
++ buf_data[i] = x1;
++ break;
++ case SCAN_INDEX_ACCEL_Y:
++ buf_data[i] = y1;
++ break;
++ case SCAN_INDEX_ACCEL_Z:
++ buf_data[i] = z1;
++ break;
++ case SCAN_INDEX_MAGN_X:
++ buf_data[i] = x2;
++ break;
++ case SCAN_INDEX_MAGN_Y:
++ buf_data[i] = y2;
++ break;
++ case SCAN_INDEX_MAGN_Z:
++ buf_data[i] = z2;
++ break;
++ default:
++ break;
++ }
++ }
++ }
++ }
++
++ iio_push_to_buffers_with_timestamp(indio_dev, buf_data, iio_get_time_ns(indio_dev));
++
++free_buf:
++ kfree(buf_data);
++ mutex_unlock(&data->lock);
++
++done:
++ iio_trigger_notify_done(indio_dev->trig);
++
++ return IRQ_HANDLED;
++}
++
++static const struct iio_info lsm9ds0_gyro_info = {
++ .attrs = &lsm9ds0_gyro_group,
++ .read_raw = lsm9ds0_read_raw,
++ .write_raw = lsm9ds0_write_raw,
++ .driver_module = THIS_MODULE,
++};
++
++static const struct iio_info lsm9ds0_accel_magn_info = {
++ .attrs = &lsm9ds0_accel_magn_group,
++ .read_raw = lsm9ds0_read_raw,
++ .write_raw = lsm9ds0_write_raw,
++ .driver_module = THIS_MODULE,
++};
++
++static int lsm9ds0_gyro_init(struct i2c_client *client)
++{
++ int ret;
++ struct iio_dev *indio_dev;
++ struct lsm9ds0_data *data;
++
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG1_G_REG,
++ LSM9DS0_GYRO_NORMAL_MODE | LSM9DS0_GYRO_X_EN |
++ LSM9DS0_GYRO_Y_EN | LSM9DS0_GYRO_Z_EN);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 5.\n");
++ return ret;
++ }
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG4_G_REG,
++ LSM9DS0_GYRO_FS_245DPS_VAL);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 4.\n");
++ return ret;
++ }
++
++ indio_dev = i2c_get_clientdata(client);
++ data = iio_priv(indio_dev);
++
++ data->gyro_scale = LSM9DS0_GYRO_FS_245DPS_GAIN;
++
++ return 0;
++}
++
++static int lsm9ds0_accel_magn_init(struct i2c_client *client)
++{
++ int ret;
++ struct iio_dev *indio_dev;
++ struct lsm9ds0_data *data;
++
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG1_XM_REG,
++ LSM9DS0_ACCEL_ODR_100HZ_VAL | LSM9DS0_ACCEL_X_EN |
++ LSM9DS0_ACCEL_Y_EN | LSM9DS0_ACCEL_Z_EN);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 1.\n");
++ return ret;
++ }
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG5_XM_REG,
++ LSM9DS0_TEMP_EN | LSM9DS0_MAGN_HIGH_RES_VAL | LSM9DS0_MAGN_ODR_50HZ_VAL);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 5.\n");
++ return ret;
++ }
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG7_XM_REG,
++ LSM9DS0_MAGN_CONT_CONV_MODE);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 7.\n");
++ return ret;
++ }
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG2_XM_REG,
++ LSM9DS0_ACCEL_FS_2G_VAL);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 2.\n");
++ return ret;
++ }
++ ret = i2c_smbus_write_byte_data(client, LSM9DS0_CTRL_REG6_XM_REG,
++ LSM9DS0_MAGN_FS_2GAUSS_VAL);
++ if (ret < 0) {
++ dev_err(&client->dev, "Failed to write control register 6.\n");
++ return ret;
++ }
++
++ indio_dev = i2c_get_clientdata(client);
++ data = iio_priv(indio_dev);
++
++ data->accel_scale = LSM9DS0_ACCEL_FS_2G_GAIN;
++ data->magn_scale = LSM9DS0_MAGN_FS_2GAUSS_GAIN;
++
++ return 0;
++}
++
++static int lsm9ds0_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct iio_dev *indio_dev;
++ struct lsm9ds0_data *data;
++ struct iio_buffer *buffer;
++ int sensor_type;
++ int ret;
++
++
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
++ ret = -ENODEV;
++ goto error_ret;
++ }
++
++ ret = i2c_smbus_read_byte_data(client, LSM9DS0_WHO_AM_I_REG);
++ if (ret < 0) {
++ ret = -EINVAL;
++ goto error_ret;
++ }
++ if (ret == LSM9DS0_GYRO_ID) {
++ dev_info(&client->dev, "Gyroscope found.\n");
++ sensor_type = GYRO;
++ } else if (ret == LSM9DS0_ACCEL_MAGN_ID) {
++ dev_info(&client->dev, "Accelerometer and magnetometer found.\n");
++ sensor_type = ACCEL_MAGN;
++ } else {
++ dev_err(&client->dev, "No LSM9DS0 sensor found.\n");
++ ret = -ENODEV;
++ goto error_ret;
++ }
++
++ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
++ if (!indio_dev) {
++ ret = -ENOMEM;
++ goto error_ret;
++ }
++
++ data = iio_priv(indio_dev);
++ mutex_init(&data->lock);
++ i2c_set_clientdata(client, indio_dev);
++ data->client = client;
++ data->sensor_type = sensor_type;
++
++ indio_dev->dev.parent = &client->dev;
++ indio_dev->name = dev_name(&client->dev);
++ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
++
++
++ if (sensor_type == GYRO) {
++ ret = lsm9ds0_gyro_init(client);
++ indio_dev->info = &lsm9ds0_gyro_info;
++ indio_dev->channels = lsm9ds0_gyro_channels;
++ indio_dev->num_channels = ARRAY_SIZE(lsm9ds0_gyro_channels);
++ } else {
++ ret = lsm9ds0_accel_magn_init(client);
++ indio_dev->info = &lsm9ds0_accel_magn_info;
++ indio_dev->channels = lsm9ds0_accel_magn_channels;
++ indio_dev->num_channels = ARRAY_SIZE(lsm9ds0_accel_magn_channels);
++ }
++ if (ret < 0)
++ goto error_free_device;
++
++ buffer = iio_kfifo_allocate();
++ if (!buffer) {
++ ret = -ENOMEM;
++ goto error_free_device;
++ }
++ iio_device_attach_buffer(indio_dev, buffer);
++ buffer->scan_timestamp = true;
++ indio_dev->setup_ops = &lsm9ds0_buffer_setup_ops;
++ indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
++ &lsm9ds0_trigger_h,
++ IRQF_ONESHOT,
++ indio_dev,
++ "lsm9ds0_consumer%d",
++ indio_dev->id);
++ if (!indio_dev->pollfunc) {
++ ret = -ENOMEM;
++ goto error_free_buffer;
++ }
++
++ ret = iio_device_register(indio_dev);
++ if (ret < 0)
++ goto error_unconfigure_buffer;
++
++ return 0;
++
++error_unconfigure_buffer:
++ iio_dealloc_pollfunc(indio_dev->pollfunc);
++error_free_buffer:
++ iio_kfifo_free(indio_dev->buffer);
++error_free_device:
++ iio_device_free(indio_dev);
++error_ret:
++ return ret;
++}
++
++static int lsm9ds0_remove(struct i2c_client *client)
++{
++ struct iio_dev *indio_dev = i2c_get_clientdata(client);
++ iio_device_unregister(indio_dev);
++ iio_device_free(indio_dev);
++ dev_info(&client->dev, "Driver removed.");
++ return 0;
++}
++
++static const struct i2c_device_id lsm9ds0_id[] = {
++ { "lsm9ds0_gyro", 0 },
++ { "lsm9ds0_accel_magn", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, lsm9ds0_id);
++
++static struct i2c_driver lsm9ds0_driver = {
++ .driver = {
++ .name = "lsm9ds0",
++ .owner = THIS_MODULE,
++ },
++ .probe = lsm9ds0_probe,
++ .remove = lsm9ds0_remove,
++ .id_table = lsm9ds0_id,
++};
++module_i2c_driver(lsm9ds0_driver);
++
++MODULE_AUTHOR("Matija Podravec <matija_podravec@fastmail.fm>");
++MODULE_DESCRIPTION("LSM9DS0 gyroscope, accelerometer, and magnetometer sensor");
++MODULE_LICENSE("GPL");
+--
+2.13.0
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch
new file mode 100644
index 0000000..46d5d84
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch
@@ -0,0 +1,479 @@
+From 963d9e84748bc43a8dbcd28019bd8ff0942b5934 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 13 Apr 2016 15:32:38 +0300
+Subject: [PATCH] ASoC: PCM3168A: add TDM modes, merge ADC and DAC
+
+Also disable 16 bit format and enable at start
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 320 ++++++++++++++++++++++++++++----------------
+ sound/soc/codecs/pcm3168a.h | 2 +-
+ 2 files changed, 205 insertions(+), 117 deletions(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 992a77edcd5d..8b9e4ff6b354 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -22,7 +22,7 @@
+
+ #include "pcm3168a.h"
+
+-#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
++#define PCM3168A_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | */\
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+@@ -33,7 +33,11 @@
+ #define PCM3168A_FMT_RIGHT_J_16 0x3
+ #define PCM3168A_FMT_DSP_A 0x4
+ #define PCM3168A_FMT_DSP_B 0x5
+-#define PCM3168A_FMT_DSP_MASK 0x4
++#define PCM3168A_FMT_I2S_TDM 0x6
++#define PCM3168A_FMT_LEFT_J_TDM 0x7
++/* High speed */
++#define PCM3168A_FMT_I2S_TDMHS 0x8
++#define PCM3168A_FMT_LEFT_J_TDMHS 0x9
+
+ #define PCM3168A_NUM_SUPPLIES 6
+ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+@@ -45,12 +49,18 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+ "VCCDA2"
+ };
+
++#define TDM_MODE_NONE 0
++#define TDM_MODE_NORM 1
++#define TDM_MODE_HS 2
++
+ struct pcm3168a_priv {
+ struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct clk *scki;
+- bool adc_master_mode;
+- bool dac_master_mode;
++ bool master_mode;
++ unsigned int tdm;
++ unsigned int slots;
++ unsigned int slot_width;
+ unsigned long sysclk;
+ unsigned int adc_fmt;
+ unsigned int dac_fmt;
+@@ -313,32 +323,43 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
+ return 0;
+ }
+
+-static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
++int format_table[3][6] = {
++ [TDM_MODE_NONE] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J,
++ [SND_SOC_DAIFMT_RIGHT_J] = PCM3168A_FMT_RIGHT_J,
++ [SND_SOC_DAIFMT_DSP_A] = PCM3168A_FMT_DSP_A,
++ [SND_SOC_DAIFMT_DSP_B] = PCM3168A_FMT_DSP_B},
++ [TDM_MODE_NORM] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDM,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDM,
++ [SND_SOC_DAIFMT_RIGHT_J] = -1,
++ [SND_SOC_DAIFMT_DSP_A] = -1,
++ [SND_SOC_DAIFMT_DSP_B] = -1},
++ [TDM_MODE_HS] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDMHS,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDMHS,
++ [SND_SOC_DAIFMT_RIGHT_J] = -1,
++ [SND_SOC_DAIFMT_DSP_A] = -1,
++ [SND_SOC_DAIFMT_DSP_B] = -1},
++};
++
++static int __pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format, bool dac)
+ {
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+- u32 fmt, reg, mask, shift;
++ u32 reg, mask, shift;
++ int fmt;
+ bool master_mode;
+
+- switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+- case SND_SOC_DAIFMT_LEFT_J:
+- fmt = PCM3168A_FMT_LEFT_J;
+- break;
+- case SND_SOC_DAIFMT_I2S:
+- fmt = PCM3168A_FMT_I2S;
+- break;
+- case SND_SOC_DAIFMT_RIGHT_J:
+- fmt = PCM3168A_FMT_RIGHT_J;
+- break;
+- case SND_SOC_DAIFMT_DSP_A:
+- fmt = PCM3168A_FMT_DSP_A;
+- break;
+- case SND_SOC_DAIFMT_DSP_B:
+- fmt = PCM3168A_FMT_DSP_B;
+- break;
+- default:
+- dev_err(codec->dev, "unsupported dai format\n");
++ fmt = format_table[pcm3168a->tdm][format & SND_SOC_DAIFMT_FORMAT_MASK];
++
++ if (fmt < 0) {
++ dev_err(codec->dev, "unsupported dai format of TDM mode\n");
+ return -EINVAL;
+ }
+
+@@ -354,6 +375,16 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ return -EINVAL;
+ }
+
++ if ((pcm3168a->tdm == TDM_MODE_HS) && (master_mode)) {
++ dev_err(codec->dev, "TDM high speed supported only in slave mode\n");
++ return -EINVAL;
++ }
++
++ if ((pcm3168a->tdm == TDM_MODE_HS) && (!dac)) {
++ dev_err(codec->dev, "TDM high speed not supported for ADC\n");
++ return -EINVAL;
++ }
++
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+@@ -365,31 +396,32 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ reg = PCM3168A_DAC_PWR_MST_FMT;
+ mask = PCM3168A_DAC_FMT_MASK;
+ shift = PCM3168A_DAC_FMT_SHIFT;
+- pcm3168a->dac_master_mode = master_mode;
+ pcm3168a->dac_fmt = fmt;
+ } else {
+ reg = PCM3168A_ADC_MST_FMT;
+ mask = PCM3168A_ADC_FMTAD_MASK;
+ shift = PCM3168A_ADC_FMTAD_SHIFT;
+- pcm3168a->adc_master_mode = master_mode;
+ pcm3168a->adc_fmt = fmt;
+ }
+
++ pcm3168a->master_mode = master_mode;
++
+ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+ return 0;
+ }
+
+-static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
++static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format)
+ {
+- return pcm3168a_set_dai_fmt(dai, format, true);
+-}
++ int ret;
+
+-static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
+- unsigned int format)
+-{
+- return pcm3168a_set_dai_fmt(dai, format, false);
++ /* dac */
++ ret = __pcm3168a_set_dai_fmt(dai, format, false);
++ if (ret)
++ return ret;
++ /* adc */
++ return __pcm3168a_set_dai_fmt(dai, format, true);
+ }
+
+ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+@@ -398,127 +430,170 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ {
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+- bool tx, master_mode;
++ int bits;
++ bool tx;
+ u32 val, mask, shift, reg;
+- unsigned int rate, fmt, ratio, max_ratio;
++ u32 sample_rate = 0; /* auto */
++ unsigned int rate, channels, fmt, ratio, max_ratio;
+ int i, min_frame_size;
+ snd_pcm_format_t format;
+
+ rate = params_rate(params);
+ format = params_format(params);
+-
+- ratio = pcm3168a->sysclk / rate;
++ channels = params_channels(params);
++ bits = params->msbits;
+
+ tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
++
+ if (tx) {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+- reg = PCM3168A_DAC_PWR_MST_FMT;
+- mask = PCM3168A_DAC_MSDA_MASK;
+- shift = PCM3168A_DAC_MSDA_SHIFT;
+- master_mode = pcm3168a->dac_master_mode;
+ fmt = pcm3168a->dac_fmt;
+ } else {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+- reg = PCM3168A_ADC_MST_FMT;
+- mask = PCM3168A_ADC_MSAD_MASK;
+- shift = PCM3168A_ADC_MSAD_SHIFT;
+- master_mode = pcm3168a->adc_master_mode;
+ fmt = pcm3168a->adc_fmt;
+ }
+
+- for (i = 0; i < max_ratio; i++) {
+- if (pcm3168a_scki_ratios[i] == ratio)
+- break;
+- }
+-
+- if (i == max_ratio) {
+- dev_err(codec->dev, "unsupported sysclk ratio\n");
+- return -EINVAL;
+- }
++ if (pcm3168a->master_mode) {
++ ratio = pcm3168a->sysclk / rate;
++ for (i = 0; i < max_ratio; i++)
++ if (pcm3168a_scki_ratios[i] == ratio)
++ break;
+
+- min_frame_size = params_width(params) * 2;
+- switch (min_frame_size) {
+- case 32:
+- if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
+- dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
++ if (i == max_ratio) {
++ dev_err(codec->dev, "unsupported sysclk ratio: %d\n", ratio);
+ return -EINVAL;
+ }
+- fmt = PCM3168A_FMT_RIGHT_J_16;
+- break;
+- case 48:
+- if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
+- dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+- return -EINVAL;
+- }
+- break;
+- case 64:
+- break;
+- default:
+- dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+- return -EINVAL;
++ val = i + 1;
++ } else {
++ /* slave mode */
++ val = 0;
+ }
+
+- if (master_mode)
+- val = ((i + 1) << shift);
+- else
++ if (pcm3168a->tdm == TDM_MODE_NONE) {
++ /* one stereo frame size */
++ min_frame_size = bits * 2;
++ switch (min_frame_size) {
++ case 32:
++ if (pcm3168a->master_mode ||
++ (fmt != PCM3168A_FMT_RIGHT_J)) {
++ dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
++ return -EINVAL;
++ }
++ fmt = PCM3168A_FMT_RIGHT_J_16;
++ break;
++ case 48:
++ if (pcm3168a->master_mode ||
++ (fmt == PCM3168A_FMT_DSP_A) ||
++ (fmt == PCM3168A_FMT_DSP_B)) {
++ dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
++ return -EINVAL;
++ }
++ break;
++ case 64:
++ break;
++ default:
++ dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
++ return -EINVAL;
++ }
++ }
++ if ((pcm3168a->tdm == TDM_MODE_NORM) ||
++ (pcm3168a->tdm == TDM_MODE_HS)) {
++ /* all channels over one or two line */
++ min_frame_size = bits * channels;
++
++ /* single rate */
++ sample_rate = 1;
++
++ /*
++ * 256fs for single line DIN0/DOUT0
++ * 128fs for two lines DIN01/DOU01
++ */
++ if ((min_frame_size != 256) &&
++ (min_frame_size != 128)) {
++ dev_err(codec->dev, "256/128-bit frames only supported in TDM formats\n");
++ return -EINVAL;
++ }
++ }
++
++ /* Setup ADC in master mode, couse it drives ADC */
++ if ((pcm3168a->master_mode) || (tx)) {
++ fmt = pcm3168a->dac_fmt;
++ reg = PCM3168A_DAC_PWR_MST_FMT;
++ mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK;
++ shift = PCM3168A_DAC_MSDA_SHIFT;
++ /* start DAC */
++ regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt);
++ }
++ /* Do we need also ADC? */
++ if (!tx) {
++ fmt = pcm3168a->adc_fmt;
++ reg = PCM3168A_ADC_MST_FMT;
++ mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK;
++ shift = PCM3168A_ADC_MSAD_SHIFT;
++ /* ADC slave mode only, driven by DAC or CPU DAI */
+ val = 0;
++ regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt);
++ }
+
+- regmap_update_bits(pcm3168a->regmap, reg, mask, val);
++ regmap_update_bits(pcm3168a->regmap, PCM3168A_RST_SMODE,
++ PCM3168A_DAC_SRDA_MASK,
++ sample_rate << PCM3168A_DAC_SRDA_SHIFT);
+
+- if (tx) {
+- mask = PCM3168A_DAC_FMT_MASK;
+- shift = PCM3168A_DAC_FMT_SHIFT;
+- } else {
+- mask = PCM3168A_ADC_FMTAD_MASK;
+- shift = PCM3168A_ADC_FMTAD_SHIFT;
+- }
++ return 0;
++}
+
+- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
++static int pcm3168a_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 pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
++
++ if ((slots != 8) && (slots != 4))
++ return -EINVAL;
++
++ if ((slot_width != 32) && (slot_width != 24))
++ return -EINVAL;
++
++ pcm3168a->slots = slots;
++ pcm3168a->slot_width = slot_width;
+
+ return 0;
+ }
+
+-static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+- .set_fmt = pcm3168a_set_dai_fmt_dac,
++static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
++ .set_fmt = pcm3168a_set_dai_fmt,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
++ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .hw_params = pcm3168a_hw_params,
+ .digital_mute = pcm3168a_digital_mute
+ };
+
+-static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+- .set_fmt = pcm3168a_set_dai_fmt_adc,
+- .set_sysclk = pcm3168a_set_dai_sysclk,
+- .hw_params = pcm3168a_hw_params
+-};
+-
+-static struct snd_soc_dai_driver pcm3168a_dais[] = {
+- {
+- .name = "pcm3168a-dac",
+- .playback = {
+- .stream_name = "Playback",
+- .channels_min = 1,
+- .channels_max = 8,
+- .rates = SNDRV_PCM_RATE_8000_192000,
+- .formats = PCM3168A_FORMATS
+- },
+- .ops = &pcm3168a_dac_dai_ops
++static struct snd_soc_dai_driver pcm3168a_dai = {
++ .name = "pcm3168a",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_192000,
++ .formats = PCM3168A_FORMATS
+ },
+- {
+- .name = "pcm3168a-adc",
+- .capture = {
+- .stream_name = "Capture",
+- .channels_min = 1,
+- .channels_max = 6,
+- .rates = SNDRV_PCM_RATE_8000_96000,
+- .formats = PCM3168A_FORMATS
+- },
+- .ops = &pcm3168a_adc_dai_ops
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_96000,
++ .formats = PCM3168A_FORMATS
+ },
++ .ops = &pcm3168a_dai_ops,
++ .symmetric_rates = 1,
+ };
+
+ static const struct reg_default pcm3168a_reg_default[] = {
+ { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
+- { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
++ { PCM3168A_DAC_PWR_MST_FMT, 0x80 },
+ { PCM3168A_DAC_OP_FLT, 0x00 },
+ { PCM3168A_DAC_INV, 0x00 },
+ { PCM3168A_DAC_MUTE, 0x00 },
+@@ -665,12 +740,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+ goto err_regulator;
+ }
+
++ /* get TDM mode */
++ if (dev->of_node) {
++ if (of_get_property(dev->of_node, "tdm", NULL))
++ pcm3168a->tdm = TDM_MODE_NORM;
++ else if (of_get_property(dev->of_node, "tdmhs", NULL))
++ pcm3168a->tdm = TDM_MODE_HS;
++ }
++
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+- ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
+- ARRAY_SIZE(pcm3168a_dais));
++ if (pcm3168a->tdm != TDM_MODE_NONE) {
++ pcm3168a_dai.playback.channels_min = 8;
++ pcm3168a_dai.capture.channels_min = 8;
++ }
++
++ ret = snd_soc_register_codec(dev, &pcm3168a_driver,
++ &pcm3168a_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ goto err_regulator;
+diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
+index 56c8332d82fb..658507f86c97 100644
+--- a/sound/soc/codecs/pcm3168a.h
++++ b/sound/soc/codecs/pcm3168a.h
+@@ -69,7 +69,7 @@ extern void pcm3168a_remove(struct device *dev);
+ #define PCM3168A_ADC_MSAD_SHIFT 4
+ #define PCM3168A_ADC_MSAD_MASK 0x70
+ #define PCM3168A_ADC_FMTAD_SHIFT 0
+-#define PCM3168A_ADC_FMTAD_MASK 0x7
++#define PCM3168A_ADC_FMTAD_MASK 0xf
+
+ #define PCM3168A_ADC_PWR_HPFB 0x52
+ #define PCM3168A_ADC_PSVAD_SHIFT 4
+--
+2.13.0
+
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/h3ulcb.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/h3ulcb.cfg
index 24ebe62..73008f1 100644
--- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/h3ulcb.cfg
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/h3ulcb.cfg
@@ -7,7 +7,7 @@ CONFIG_CAN_CALC_BITTIMING=y
CONFIG_CAN_RCAR=y
CONFIG_CAN_RCAR_CANFD=y
CONFIG_DUMMY=y
-CONFIG_EXTRA_FIRMWARE="r8a779x_usb3_v2.dlmem"
+CONFIG_EXTRA_FIRMWARE="r8a779x_usb3_v2.dlmem r8a779x_usb3_v3.dlmem"
CONFIG_EXTRA_FIRMWARE_DIR="firmware"
CONFIG_BLK_DEV_NVME=m
CONFIG_SATA_ACARD_AHCI=y
@@ -55,15 +55,11 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_PROPERTIES=y
CONFIG_HID_MULTITOUCH=y
CONFIG_IIO=y
-CONFIG_IIO_ST_ACCEL_3AXIS=y
-CONFIG_IIO_ST_ACCEL_I2C_3AXIS=y
-CONFIG_IIO_ST_ACCEL_SPI_3AXIS=y
-CONFIG_IIO_ST_SENSORS_I2C=y
-CONFIG_IIO_ST_SENSORS_SPI=y
-CONFIG_IIO_ST_SENSORS_CORE=y
-CONFIG_IIO_ST_GYRO_3AXIS=y
-CONFIG_IIO_ST_GYRO_I2C_3AXIS=y
-CONFIG_IIO_ST_GYRO_SPI_3AXIS=y
-CONFIG_IIO_ST_MAGN_3AXIS=y
-CONFIG_IIO_ST_MAGN_I2C_3AXIS=y
-CONFIG_IIO_ST_MAGN_SPI_3AXIS=y
+CONFIG_IIO_BUFFER=y
+CONFIG_IIO_BUFFER_CB=y
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_IIO_TRIGGERED_BUFFER=y
+CONFIG_IIO_TRIGGER=y
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+CONFIG_LSM9DS0=y
+CONFIG_DRM_I2C_ADV7511=y
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/m3ulcb.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/m3ulcb.cfg
index 24ebe62..73008f1 100644
--- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/m3ulcb.cfg
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/m3ulcb.cfg
@@ -7,7 +7,7 @@ CONFIG_CAN_CALC_BITTIMING=y
CONFIG_CAN_RCAR=y
CONFIG_CAN_RCAR_CANFD=y
CONFIG_DUMMY=y
-CONFIG_EXTRA_FIRMWARE="r8a779x_usb3_v2.dlmem"
+CONFIG_EXTRA_FIRMWARE="r8a779x_usb3_v2.dlmem r8a779x_usb3_v3.dlmem"
CONFIG_EXTRA_FIRMWARE_DIR="firmware"
CONFIG_BLK_DEV_NVME=m
CONFIG_SATA_ACARD_AHCI=y
@@ -55,15 +55,11 @@ CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_PROPERTIES=y
CONFIG_HID_MULTITOUCH=y
CONFIG_IIO=y
-CONFIG_IIO_ST_ACCEL_3AXIS=y
-CONFIG_IIO_ST_ACCEL_I2C_3AXIS=y
-CONFIG_IIO_ST_ACCEL_SPI_3AXIS=y
-CONFIG_IIO_ST_SENSORS_I2C=y
-CONFIG_IIO_ST_SENSORS_SPI=y
-CONFIG_IIO_ST_SENSORS_CORE=y
-CONFIG_IIO_ST_GYRO_3AXIS=y
-CONFIG_IIO_ST_GYRO_I2C_3AXIS=y
-CONFIG_IIO_ST_GYRO_SPI_3AXIS=y
-CONFIG_IIO_ST_MAGN_3AXIS=y
-CONFIG_IIO_ST_MAGN_I2C_3AXIS=y
-CONFIG_IIO_ST_MAGN_SPI_3AXIS=y
+CONFIG_IIO_BUFFER=y
+CONFIG_IIO_BUFFER_CB=y
+CONFIG_IIO_KFIFO_BUF=y
+CONFIG_IIO_TRIGGERED_BUFFER=y
+CONFIG_IIO_TRIGGER=y
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+CONFIG_LSM9DS0=y
+CONFIG_DRM_I2C_ADV7511=y
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend
index 12651b5..86ca21d 100644
--- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend
@@ -49,6 +49,9 @@ SRC_URI_append = " \
${@base_conditional("LVDSCAMERA_SECOND4_TYPE1", "1", " file://0051-arm64-dts-Gen3-view-boards-TYPE1-second-4-cameras.patch", "", d)} \
${@base_conditional("LVDSCAMERA_FIRST4_TYPE2", "1", " file://0052-arm64-dts-Gen3-view-boards-TYPE2-first-4-cameras.patch", "", d)} \
file://0060-media-i2c-Add-ov5647-sensor.patch \
+ ${@base_conditional("SOUND_MULTICHANNEL", "1", " file://0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch", "", d)} \
+ file://0062-IIO-lsm9ds0-add-IMU-driver.patch \
+ file://0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch \
"
SRC_URI_append_h3ulcb = " file://h3ulcb.cfg"
@@ -62,11 +65,13 @@ KERNEL_DEVICETREE_append_h3ulcb = " \
renesas/r8a7795-es1-h3ulcb-had-beta.dtb \
renesas/r8a7795-es1-h3ulcb-kf.dtb \
renesas/r8a7795-es1-h3ulcb-kf-v1.dtb \
+ renesas/r8a7795-es1-h3ulcb-vb.dtb \
renesas/r8a7795-h3ulcb-view.dtb \
renesas/r8a7795-h3ulcb-had-alfa.dtb \
renesas/r8a7795-h3ulcb-had-beta.dtb \
renesas/r8a7795-h3ulcb-kf.dtb \
renesas/r8a7795-h3ulcb-kf-v1.dtb \
+ renesas/r8a7795-h3ulcb-vb.dtb \
"
KERNEL_DEVICETREE_append_m3ulcb = " \