From cc630108ca97d394b7797d8a556ae391a1ead201 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 13 Jul 2017 07:34:14 +0300 Subject: Kingfisher: add multichannel audio, IMU driver - Fix multichannel in rcar sound (optional compilation) - add IMU driver - enable new sensor drivers - enalbe v3 usb3.0 firmware - deploy Videobox dtb files --- ...ar-add-tdm16-support-enable-tdm-for-ssi78.patch | 315 +++++++ .../0062-IIO-lsm9ds0-add-IMU-driver.patch | 972 +++++++++++++++++++++ ...-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch | 479 ++++++++++ .../recipes-kernel/linux/linux-renesas/h3ulcb.cfg | 22 +- .../recipes-kernel/linux/linux-renesas/m3ulcb.cfg | 22 +- .../linux/linux-renesas_4.9.bbappend | 5 + 6 files changed, 1789 insertions(+), 26 deletions(-) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0062-IIO-lsm9ds0-add-IMU-driver.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0063-ASoC-PCM3168A-add-TDM-modes-merge-ADC-and-DAC.patch (limited to 'meta-rcar-gen3-adas') 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 +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 +--- + 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 +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 +--- + 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 ++ * ++ * 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 . ++ * ++ * ++ * Driver for ST LSM9DS0 gyroscope, accelerometer, and magnetometer sensor. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 +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 +--- + 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 = " \ -- cgit 1.2.3-korg