Compare commits

..

No commits in common. "combined-stable" and "nfc-ce" have entirely different histories.

14 changed files with 293 additions and 3415 deletions

View file

@ -27,34 +27,6 @@
serial1 = &uart11;
};
/*
* The sensor I2C bus sits on TLMM eGPIO pads that have no AP QUP
* function; on the stock OS it is driven by an island QUP of the
* Snapdragon Sensor Core (ADSP). From the AP the pads are only
* usable as software GPIOs, so bit-bang the bus.
* Devices on the bus: QMC6308 magnetometer @ 0x2c, SPL07
* barometer @ 0x76, STK3BCx ALS/proximity @ 0x48.
*/
i2c-sensors {
compatible = "i2c-gpio";
sda-gpios = <&tlmm 153 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&tlmm 154 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
pinctrl-0 = <&sensor_i2c_default>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
magnetometer@2c {
compatible = "qstcorp,qmc6308";
reg = <0x2c>;
vdd-supply = <&vreg_l10b>;
mount-matrix = "0", "-1", "0",
"1", "0", "0",
"0", "0", "1";
};
};
gpio-keys {
compatible = "gpio-keys";
@ -237,34 +209,14 @@
sound {
compatible = "qcom,milos-sndcard", "qcom,sm8450-sndcard";
model = "Fairphone (Gen. 6)";
audio-routing = "AMIC1", "MIC BIAS1",
"AMIC2", "MIC BIAS2",
"AMIC3", "MIC BIAS3",
"TX SWR_INPUT0", "ADC1_OUTPUT",
"TX SWR_INPUT1", "ADC2_OUTPUT",
"TX SWR_INPUT2", "ADC3_OUTPUT";
// audio-routing = ...
pinctrl-0 = <&lpi_i2s2_active>;
pinctrl-1 = <&lpi_i2s2_sleep>;
pinctrl-names = "default",
"sleep";
wcd-capture-dai-link {
link-name = "WCD Capture";
codec {
sound-dai = <&wcd9378 1>, <&swr2 0>, <&lpass_txmacro 0>;
};
cpu {
sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>;
};
platform {
sound-dai = <&q6apm>;
};
};
/*
i2s-dai-link {
link-name = "Senary MI2S Playback";
@ -280,29 +232,7 @@
sound-dai = <&q6apm>;
};
};
};
wcd9378: audio-codec {
compatible = "qcom,wcd9378-codec";
pinctrl-0 = <&wcd_reset_n_active>;
pinctrl-names = "default";
reset-gpios = <&tlmm 162 GPIO_ACTIVE_LOW>;
vdd-buck-supply = <&vreg_l8b>;
vdd-rxtx-supply = <&vreg_l7b>;
vdd-io-supply = <&vreg_l7b>;
vdd-mic-bias-supply = <&vreg_bob>;
qcom,micbias1-microvolt = <1800000>;
qcom,micbias2-microvolt = <1800000>;
qcom,micbias3-microvolt = <1800000>;
qcom,rx-device = <&wcd9378_rx>;
qcom,tx-device = <&wcd9378_tx>;
#sound-dai-cells = <1>;
*/
};
thermal-zones {
@ -1175,36 +1105,6 @@
};
};
&swr1 {
status = "okay";
/* WCD9378 RX */
wcd9378_rx: codec@0,4 {
compatible = "sdw20217011000";
reg = <0 4>;
qcom,rx-port-mapping = <1 2 3 4 5>;
};
};
&swr2 {
status = "okay";
/* WCD9378 TX */
wcd9378_tx: codec@0,3 {
compatible = "sdw20217011000";
reg = <0 3>;
/*
* WCD9378 TX port 1 (ADC1) <=> SWR2 port 1 (SWRM_TX1)
* WCD9378 TX port 2 (ADC2) <=> SWR2 port 1
* WCD9378 TX port 3 (ADC3) <=> SWR2 port 1
* WCD9378 TX port 4 (DMIC0,1, MBHC) <=> SWR2 port 2
* WCD9378 TX port 5 (DMIC2..5) <=> SWR2 port 3
*/
qcom,tx-port-mapping = <1 1 1 2 3>;
};
};
&tlmm {
gpio-reserved-ranges = <8 4>, /* Fingerprint SPI */
<13 1>, /* NC */
@ -1217,14 +1117,6 @@
bias-disable;
};
sensor_i2c_default: sensor-i2c-default-state {
/* SDA, SCL; external pull-ups to vreg_l10b */
pins = "gpio153", "gpio154";
function = "gpio";
drive-strength = <2>;
bias-disable;
};
ts_active: ts-irq-active-state {
pins = "gpio19";
function = "gpio";
@ -1365,13 +1257,6 @@
drive-strength = <2>;
bias-pull-down;
};
wcd_reset_n_active: wcd-reset-n-active-state {
pins = "gpio162";
function = "gpio";
drive-strength = <16>;
output-high;
};
};
&uart5 {

View file

@ -198,17 +198,6 @@ config INFINEON_TLV493D
To compile this driver as a module, choose M here: the module
will be called tlv493d.
config QMC6308
tristate "QST QMC6308 3-Axis Magnetic Sensor"
depends on I2C
select REGMAP_I2C
help
Say Y here to add support for the QST QMC6308 3-Axis
Magnetic Sensor.
To compile this driver as a module, choose M here: the
module will be called qmc6308.
config SENSORS_HMC5843
tristate
select IIO_BUFFER

View file

@ -26,8 +26,6 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o
obj-$(CONFIG_QMC6308) += qmc6308.o
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o

View file

@ -1,590 +0,0 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Support for QST QMC6308 3-Axis Magnetic Sensor on I2C bus.
*
* Copyright (C) 2026 Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
*
* Datasheet available at
* <https://qstcorp.com/upload/pdf/202202/13-52-15%20QMC6308%20Datasheet%20Rev.%20F(1).pdf>
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/time.h>
#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/iio/iio.h>
#define QMC6308_REG_ID 0x00
#define QMC6308_REG_X_LSB 0x01
#define QMC6308_REG_STATUS 0x09
#define QMC6308_REG_CTRL1 0x0A
#define QMC6308_REG_CTRL2 0x0B
#define QMC6308_REG_CTRL3 0x0D
#define QMC6308_REG_CTRL4 0x29
#define QMC6308_CHIP_ID 0x80
/* Control register 1 */
#define QMC6308_MODE_MASK GENMASK(1, 0)
#define QMC6308_ODR_MASK GENMASK(3, 2)
#define QMC6308_OSR1_MASK GENMASK(5, 4)
#define QMC6308_OSR2_MASK GENMASK(7, 6)
#define QMC6308_MODE_SUSPEND 0x00
#define QMC6308_MODE_NORMAL 0x01
#define QMC6308_ODR_10HZ 0x00
#define QMC6308_ODR_50HZ 0x01
#define QMC6308_ODR_100HZ 0x02
#define QMC6308_ODR_200HZ 0x03
#define QMC6308_OSR1_8 0x00
#define QMC6308_OSR1_4 0x01
#define QMC6308_OSR1_2 0x02
#define QMC6308_OSR1_1 0x03
/* Control register 2 */
#define QMC6308_SET_RESET_MASK GENMASK(1, 0)
#define QMC6308_RNG_MASK GENMASK(3, 2)
#define QMC6308_SELF_TEST BIT(6)
#define QMC6308_SOFT_RST BIT(7)
#define QMC6308_SET_RESET_ON 0x00
#define QMC6308_RNG_30G 0x00
#define QMC6308_RNG_12G 0x01
#define QMC6308_RNG_8G 0x02
#define QMC6308_RNG_2G 0x03
/* Status register */
#define QMC6308_STATUS_DRDY BIT(0)
#define QMC6308_STATUS_OVFL BIT(1)
/*
* Power-on completion time (datasheet Table 7), also used as a
* conservative bound after soft reset, for which the datasheet
* gives no figure.
*/
#define QMC6308_POR_US 250
#define QMC6308_AUTOSUSPEND_DELAY_MS 500
struct qmc6308_data {
struct regmap *regmap;
/*
* Protect data->range/odr/osr.
* Protect poll and read during measurement (reading the status
* register clears DRDY).
*/
struct mutex mutex;
struct iio_mount_matrix orientation;
u8 range;
u8 odr;
u8 osr;
};
enum qmc6308_axis {
QMC6308_AXIS_X,
QMC6308_AXIS_Y,
QMC6308_AXIS_Z,
};
static const int qmc6308_odr_avail[] = {
[QMC6308_ODR_10HZ] = 10,
[QMC6308_ODR_50HZ] = 50,
[QMC6308_ODR_100HZ] = 100,
[QMC6308_ODR_200HZ] = 200,
};
static const int qmc6308_osr1_avail[] = {
[QMC6308_OSR1_8] = 8,
[QMC6308_OSR1_4] = 4,
[QMC6308_OSR1_2] = 2,
[QMC6308_OSR1_1] = 1,
};
/*
* Sensitivity is 1000/2500/3750/15000 LSB/Gauss for the
* +-30/12/8/2 Gauss ranges respectively.
*/
static const int qmc6308_scales[][2] = {
[QMC6308_RNG_30G] = { 0, 1000000 },
[QMC6308_RNG_12G] = { 0, 400000 },
[QMC6308_RNG_8G] = { 0, 266667 },
[QMC6308_RNG_2G] = { 0, 66667 },
};
static int qmc6308_set_mode(struct qmc6308_data *data, unsigned int mode)
{
return regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
QMC6308_MODE_MASK,
FIELD_PREP(QMC6308_MODE_MASK, mode));
}
static int qmc6308_take_measurement(struct iio_dev *indio_dev, int index,
int *val)
{
struct qmc6308_data *data = iio_priv(indio_dev);
struct regmap *map = data->regmap;
struct device *dev = regmap_get_device(map);
unsigned int status;
__le16 buf[3];
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret) {
/* EACCES means a read raced runtime PM disable on suspend */
if (ret != -EACCES)
dev_err(dev, "Failed to power on (%d)\n", ret);
return ret;
}
scoped_guard(mutex, &data->mutex) {
/* 50ms headroom over the slowest ODR (10Hz) */
ret = regmap_read_poll_timeout(map, QMC6308_REG_STATUS,
status,
(status & QMC6308_STATUS_DRDY),
2 * USEC_PER_MSEC,
150 * USEC_PER_MSEC);
if (ret)
goto out_rpm_put;
ret = regmap_bulk_read(map, QMC6308_REG_X_LSB, buf,
sizeof(buf));
if (ret)
goto out_rpm_put;
if (status & QMC6308_STATUS_OVFL)
ret = -ERANGE;
}
out_rpm_put:
pm_runtime_put_autosuspend(dev);
if (ret)
return ret;
*val = (s16)le16_to_cpu(buf[index]);
return 0;
}
static int qmc6308_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct qmc6308_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = qmc6308_take_measurement(indio_dev, chan->address, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE: {
guard(mutex)(&data->mutex);
*val = qmc6308_scales[data->range][0];
*val2 = qmc6308_scales[data->range][1];
return IIO_VAL_INT_PLUS_NANO;
}
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&data->mutex);
*val = qmc6308_odr_avail[data->odr];
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
guard(mutex)(&data->mutex);
*val = qmc6308_osr1_avail[data->osr];
return IIO_VAL_INT;
}
default:
return -EINVAL;
}
}
static int qmc6308_write_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long mask)
{
struct qmc6308_data *data = iio_priv(indio_dev);
unsigned int status;
unsigned int i;
int ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE: {
if (val != 0)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(qmc6308_scales); i++) {
if (val2 == qmc6308_scales[i][1])
break;
}
if (i == ARRAY_SIZE(qmc6308_scales))
return -EINVAL;
guard(mutex)(&data->mutex);
ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL2,
QMC6308_RNG_MASK,
FIELD_PREP(QMC6308_RNG_MASK, i));
if (ret)
return ret;
data->range = i;
/*
* The data registers still hold (and DRDY still
* advertises) a sample converted at the previous range;
* discard it so that the next read returns data matching
* the new scale.
*/
return regmap_read(data->regmap, QMC6308_REG_STATUS,
&status);
}
case IIO_CHAN_INFO_SAMP_FREQ: {
for (i = 0; i < ARRAY_SIZE(qmc6308_odr_avail); i++) {
if (val == qmc6308_odr_avail[i])
break;
}
if (i == ARRAY_SIZE(qmc6308_odr_avail))
return -EINVAL;
guard(mutex)(&data->mutex);
ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
QMC6308_ODR_MASK,
FIELD_PREP(QMC6308_ODR_MASK, i));
if (ret)
return ret;
data->odr = i;
return 0;
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
for (i = 0; i < ARRAY_SIZE(qmc6308_osr1_avail); i++) {
if (val == qmc6308_osr1_avail[i])
break;
}
if (i == ARRAY_SIZE(qmc6308_osr1_avail))
return -EINVAL;
guard(mutex)(&data->mutex);
ret = regmap_update_bits(data->regmap, QMC6308_REG_CTRL1,
QMC6308_OSR1_MASK,
FIELD_PREP(QMC6308_OSR1_MASK, i));
if (ret)
return ret;
data->osr = i;
return 0;
}
default:
return -EINVAL;
}
}
static int qmc6308_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = qmc6308_odr_avail;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(qmc6308_odr_avail);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = qmc6308_osr1_avail;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(qmc6308_osr1_avail);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)qmc6308_scales;
*type = IIO_VAL_INT_PLUS_NANO;
*length = ARRAY_SIZE(qmc6308_scales) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int qmc6308_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
default:
return IIO_VAL_INT;
}
}
static const struct iio_mount_matrix *
qmc6308_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct qmc6308_data *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_chan_spec_ext_info qmc6308_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qmc6308_get_mount_matrix),
{ }
};
static const struct iio_info qmc6308_info = {
.read_raw = qmc6308_read_raw,
.write_raw = qmc6308_write_raw,
.read_avail = qmc6308_read_avail,
.write_raw_get_fmt = qmc6308_write_raw_get_fmt,
};
static int qmc6308_init(struct qmc6308_data *data)
{
struct regmap *map = data->regmap;
unsigned int reg;
int ret;
ret = regmap_read(map, QMC6308_REG_ID, &reg);
if (ret)
return ret;
/* Allow unknown IDs so that fallback compatibles work */
if (reg != QMC6308_CHIP_ID)
dev_warn(regmap_get_device(map),
"Unknown chip id: 0x%02x, continuing\n", reg);
/* The SOFT_RST bit is not auto-cleared and must be written back 0 */
ret = regmap_write(map, QMC6308_REG_CTRL2, QMC6308_SOFT_RST);
if (ret)
return ret;
fsleep(QMC6308_POR_US);
data->range = QMC6308_RNG_30G;
ret = regmap_write(map, QMC6308_REG_CTRL2,
FIELD_PREP(QMC6308_SET_RESET_MASK,
QMC6308_SET_RESET_ON) |
FIELD_PREP(QMC6308_RNG_MASK, data->range));
if (ret)
return ret;
data->odr = QMC6308_ODR_50HZ;
data->osr = QMC6308_OSR1_8;
return regmap_write(map, QMC6308_REG_CTRL1,
FIELD_PREP(QMC6308_MODE_MASK,
QMC6308_MODE_NORMAL) |
FIELD_PREP(QMC6308_ODR_MASK, data->odr) |
FIELD_PREP(QMC6308_OSR1_MASK, data->osr));
}
static void qmc6308_power_down_action(void *priv)
{
struct qmc6308_data *data = priv;
if (!pm_runtime_status_suspended(regmap_get_device(data->regmap)))
qmc6308_set_mode(data, QMC6308_MODE_SUSPEND);
}
static bool qmc6308_volatile_reg(struct device *dev, unsigned int reg)
{
return reg >= QMC6308_REG_X_LSB && reg <= QMC6308_REG_STATUS;
}
static bool qmc6308_writable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case QMC6308_REG_CTRL1:
case QMC6308_REG_CTRL2:
case QMC6308_REG_CTRL3:
case QMC6308_REG_CTRL4:
return true;
default:
return false;
}
}
static const struct regmap_config qmc6308_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = QMC6308_REG_CTRL4,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = qmc6308_volatile_reg,
.writeable_reg = qmc6308_writable_reg,
};
#define QMC6308_CHANNEL(_axis) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
.address = QMC6308_AXIS_##_axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.ext_info = qmc6308_ext_info, \
}
static const struct iio_chan_spec qmc6308_channels[] = {
QMC6308_CHANNEL(X),
QMC6308_CHANNEL(Y),
QMC6308_CHANNEL(Z),
};
static int qmc6308_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct qmc6308_data *data;
struct iio_dev *indio_dev;
struct regmap *map;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
i2c_set_clientdata(client, indio_dev);
map = devm_regmap_init_i2c(client, &qmc6308_regmap_config);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map),
"regmap initialization failed\n");
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable VDD regulator\n");
fsleep(QMC6308_POR_US);
data = iio_priv(indio_dev);
data->regmap = map;
ret = devm_mutex_init(dev, &data->mutex);
if (ret)
return ret;
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return dev_err_probe(dev, ret,
"Failed to read mount matrix\n");
indio_dev->name = "qmc6308";
indio_dev->info = &qmc6308_info;
indio_dev->channels = qmc6308_channels;
indio_dev->num_channels = ARRAY_SIZE(qmc6308_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = qmc6308_init(data);
if (ret)
return dev_err_probe(dev, ret, "qmc6308 init failed\n");
pm_runtime_set_active(dev);
ret = devm_add_action_or_reset(dev, qmc6308_power_down_action, data);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, QMC6308_AUTOSUSPEND_DELAY_MS);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_put_autosuspend(dev);
return devm_iio_device_register(dev, indio_dev);
}
static int qmc6308_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct qmc6308_data *data = iio_priv(indio_dev);
return qmc6308_set_mode(data, QMC6308_MODE_SUSPEND);
}
static int qmc6308_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct qmc6308_data *data = iio_priv(indio_dev);
unsigned int status;
int ret;
ret = qmc6308_set_mode(data, QMC6308_MODE_NORMAL);
if (ret)
return ret;
/*
* DRDY may still be set for a sample converted before the last
* suspend; clear it so the next read waits for fresh data.
*/
return regmap_read(data->regmap, QMC6308_REG_STATUS, &status);
}
static DEFINE_RUNTIME_DEV_PM_OPS(qmc6308_pm_ops, qmc6308_runtime_suspend,
qmc6308_runtime_resume, NULL);
static const struct of_device_id qmc6308_match[] = {
{ .compatible = "qstcorp,qmc6308" },
{ }
};
MODULE_DEVICE_TABLE(of, qmc6308_match);
static const struct i2c_device_id qmc6308_id[] = {
{ .name = "qmc6308" },
{ }
};
MODULE_DEVICE_TABLE(i2c, qmc6308_id);
static struct i2c_driver qmc6308_driver = {
.driver = {
.name = "qmc6308",
.of_match_table = qmc6308_match,
.pm = pm_ptr(&qmc6308_pm_ops),
},
.id_table = qmc6308_id,
.probe = qmc6308_probe,
};
module_i2c_driver(qmc6308_driver);
MODULE_DESCRIPTION("QST QMC6308 3-Axis Magnetic Sensor driver");
MODULE_AUTHOR("Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>");
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -976,20 +976,6 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus,
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
int ret, i, len;
if (msg->page) {
ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page1,
msg->dev_num,
SDW_SCP_ADDRPAGE1);
if (ret)
return SDW_CMD_IGNORED;
ret = qcom_swrm_cmd_fifo_wr_cmd(ctrl, msg->addr_page2,
msg->dev_num,
SDW_SCP_ADDRPAGE2);
if (ret)
return SDW_CMD_IGNORED;
}
if (msg->flags == SDW_MSG_FLAG_READ) {
for (i = 0; i < msg->len;) {
len = min(msg->len - i, QCOM_SWRM_MAX_RD_LEN);

View file

@ -301,7 +301,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_WCD9335
imply SND_SOC_WCD934X
imply SND_SOC_WCD937X_SDW
imply SND_SOC_WCD9378_SDW
imply SND_SOC_WCD938X_SDW
imply SND_SOC_WCD939X_SDW
imply SND_SOC_LPASS_MACRO_COMMON
@ -2432,24 +2431,6 @@ config SND_SOC_WCD937X_SDW
via soundwire.
To compile this codec driver say Y or m.
config SND_SOC_WCD9378
depends on SND_SOC_WCD9378_SDW
tristate
depends on SOUNDWIRE || !SOUNDWIRE
select SND_SOC_WCD_COMMON
config SND_SOC_WCD9378_SDW
tristate "WCD9378 Codec - SDW"
select SND_SOC_WCD9378
depends on SOUNDWIRE
select REGMAP_SOUNDWIRE
help
The WCD9378 is an audio codec IC connected to the host over
SoundWire, found on Qualcomm SM7635 boards such as the
Fairphone 6. It has three ADCs for analog microphones, mic
bias supplies, headphone/earpiece outputs and MBHC.
To compile this codec driver say Y or m.
config SND_SOC_WCD938X
depends on SND_SOC_WCD938X_SDW
tristate

View file

@ -356,8 +356,6 @@ snd-soc-wcd9335-y := wcd9335.o
snd-soc-wcd934x-y := wcd934x.o
snd-soc-wcd937x-y := wcd937x.o
snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
snd-soc-wcd9378-y := wcd9378.o
snd-soc-wcd9378-sdw-y := wcd9378-sdw.o
snd-soc-wcd938x-y := wcd938x.o
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
snd-soc-wcd939x-y := wcd939x.o
@ -798,11 +796,6 @@ ifdef CONFIG_SND_SOC_WCD937X_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o
endif
obj-$(CONFIG_SND_SOC_WCD9378) += snd-soc-wcd9378.o
ifdef CONFIG_SND_SOC_WCD9378_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD9378) += snd-soc-wcd9378-sdw.o
endif
obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o
ifdef CONFIG_SND_SOC_WCD938X_SDW
# avoid link failure by forcing sdw code built-in when needed

View file

@ -10,12 +10,9 @@
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "aw88261.h"
#include "aw88395/aw88395_data_type.h"
#include "aw88395/aw88395_device.h"
@ -30,10 +27,64 @@ static const struct regmap_config aw88261_remap_config = {
static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
{
unsigned int volume = min(value, (unsigned int)AW88261_MUTE_VOL);
struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
unsigned int real_value, volume;
unsigned int reg_value;
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL2_REG,
~AW88261_VOL_MASK, DB_TO_REG_VAL(volume));
volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
real_value = DB_TO_REG_VAL(volume);
regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
}
static void aw88261_dev_fade_in(struct aw_device *aw_dev)
{
struct aw_volume_desc *desc = &aw_dev->volume_desc;
int fade_in_vol = desc->ctl_volume;
int fade_step = aw_dev->fade_step;
int i;
if (fade_step == 0 || aw_dev->fade_in_time == 0) {
aw88261_dev_set_volume(aw_dev, fade_in_vol);
return;
}
for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
aw88261_dev_set_volume(aw_dev, i);
usleep_range(aw_dev->fade_in_time,
aw_dev->fade_in_time + 10);
}
if (i != fade_in_vol)
aw88261_dev_set_volume(aw_dev, fade_in_vol);
}
static void aw88261_dev_fade_out(struct aw_device *aw_dev)
{
struct aw_volume_desc *desc = &aw_dev->volume_desc;
int fade_step = aw_dev->fade_step;
int i;
if (fade_step == 0 || aw_dev->fade_out_time == 0) {
aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
return;
}
for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
aw88261_dev_set_volume(aw_dev, i);
usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
}
if (i != AW88261_MUTE_VOL) {
aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
}
}
static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
@ -69,13 +120,13 @@ static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
{
if (is_mute) {
aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
aw88261_dev_fade_out(aw_dev);
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
} else {
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
aw88261_dev_set_volume(aw_dev, aw_dev->volume_desc.ctl_volume);
aw88261_dev_fade_in(aw_dev);
}
}
@ -100,21 +151,21 @@ static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
if (ret)
return ret;
if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
dev_dbg(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
return -EINVAL;
}
return ret;
}
static int aw88261_dev_check_pll(struct aw_device *aw_dev)
static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
{
int ret, i;
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
ret = aw88261_dev_get_iis_status(aw_dev);
if (ret) {
dev_dbg(aw_dev->dev, "mode1 iis signal check error");
dev_err(aw_dev->dev, "mode1 iis signal check error");
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
return ret;
@ -124,74 +175,71 @@ static int aw88261_dev_check_pll(struct aw_device *aw_dev)
return -EPERM;
}
static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
{
unsigned int reg_val;
int ret, i;
ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
if (ret)
return ret;
reg_val &= (~AW88261_CCO_MUX_MASK);
if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
return -EPERM;
}
/* change mode2 */
ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
if (ret)
return ret;
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
ret = aw88261_dev_get_iis_status(aw_dev);
if (ret) {
dev_err(aw_dev->dev, "mode2 iis signal check error");
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
break;
}
}
/* change mode1 */
ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
if (ret == 0) {
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
ret = aw88261_dev_check_mode1_pll(aw_dev);
if (ret) {
dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
break;
}
}
}
return ret;
}
static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
{
struct aw_device *aw_dev = aw88261->aw_pa;
int ret;
/* Configure TDM slots (I2S is represented as no slots) */
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
~AW88261_SLOT_NUM_MASK, aw88261->slot_num_value);
if (ret)
return ret;
ret = aw88261_dev_check_mode1_pll(aw_dev);
if (ret) {
dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
ret = aw88261_dev_check_mode2_pll(aw_dev);
if (ret) {
dev_err(aw_dev->dev, "mode2 check iis failed");
return ret;
}
}
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
~AW88261_I2S_TX_SLOTVLD_MASK,
aw88261->tx_slotvld_mask);
if (ret)
return ret;
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
~AW88261_I2S_RXL_SLOTVLD_MASK,
aw88261->rxl_slotvld_mask);
if (ret)
return ret;
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
~AW88261_I2S_RXR_SLOTVLD_MASK,
aw88261->rxr_slotvld_mask);
if (ret)
return ret;
/* PLL divider must be used for 8/16/32 kHz modes */
ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value);
if (ret)
return ret;
/* The word clock (WCK) defines the beginning of a frame */
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
~AW88261_I2SSR_MASK, aw88261->sr_value);
if (ret)
return ret;
/* The bit clock (BCK) defines the length of a frame */
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
~AW88261_I2SBCK_MASK,
(aw88261->tdm_bck_value != AW88261_TDM_BCK_UNSET)
? aw88261->tdm_bck_value : aw88261->bck_value);
if (ret)
return ret;
/* The logical frame size is the width of data for 1 slot */
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
~AW88261_I2SFS_MASK, aw88261->fs_value);
if (ret)
return ret;
/* The I2S interface mode (Philips standard, LSB/MSB justified) */
ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
~AW88261_I2SMD_MASK, aw88261->md_value);
if (ret)
return ret;
/* The polarity of the bit clock (BCK) */
ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
~AW88261_BCKINV_MASK, aw88261->bck_inv_value);
if (ret)
return ret;
return aw88261_dev_check_pll(aw_dev);
return ret;
}
static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
@ -206,10 +254,10 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
return ret;
check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
& AW88261_BIT_PLL_CHECK;
if (check_val != AW88261_BIT_PLL_CHECK) {
dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
reg_val, AW88261_BIT_PLL_CHECK);
& AW88261_BIT_SYSST_CHECK;
if (check_val != AW88261_BIT_SYSST_CHECK) {
dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
reg_val, AW88261_BIT_SYSST_CHECK);
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
return 0;
@ -236,22 +284,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261)
if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
/* set FORCE_PWM */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
/* set BOOST_OS_WIDTH */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
/* set BURST_LOOPR */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
/* set RSQN_DLY */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
/* set BURST_SSMODE */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
/* set BST_BURST */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
} else {
dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
}
@ -502,7 +550,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
int ret;
if (aw_dev->status == AW88261_DEV_PW_ON) {
dev_dbg(aw_dev->dev, "already power on");
dev_info(aw_dev->dev, "already power on");
return 0;
}
@ -510,9 +558,9 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
aw88261_dev_pwd(aw_dev, false);
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
ret = aw88261_dev_configure_syspll(aw88261);
ret = aw88261_dev_check_syspll(aw_dev);
if (ret) {
dev_dbg(aw_dev->dev, "pll check failed");
dev_err(aw_dev->dev, "pll check failed cannot start");
goto pll_check_fail;
}
@ -523,7 +571,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
/* check i2s status */
ret = aw88261_dev_check_sysst(aw_dev);
if (ret) {
dev_dbg(aw_dev->dev, "sysst check failed");
dev_err(aw_dev->dev, "sysst check failed");
goto sysst_check_fail;
}
@ -624,25 +672,31 @@ static void aw88261_start_pa(struct aw88261 *aw88261)
for (i = 0; i < AW88261_START_RETRIES; i++) {
ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
if (ret) {
dev_dbg(aw88261->aw_pa->dev,
"aw88261_reg_update failed, cnt:%d, ret:%d\n", i, ret);
dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
continue;
}
ret = aw88261_dev_start(aw88261);
if (ret) {
dev_dbg(aw88261->aw_pa->dev,
"aw88261_dev_start failed, cnt:%d, ret:%d\n", i, ret);
dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
continue;
} else {
dev_dbg(aw88261->aw_pa->dev, "start success\n");
dev_info(aw88261->aw_pa->dev, "start success\n");
break;
}
}
if (ret != 0)
dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret);
}
static void aw88261_start(struct aw88261 *aw88261)
static void aw88261_startup_work(struct work_struct *work)
{
struct aw88261 *aw88261 =
container_of(work, struct aw88261, start_work.work);
mutex_lock(&aw88261->lock);
aw88261_start_pa(aw88261);
mutex_unlock(&aw88261->lock);
}
static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
{
if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
return;
@ -650,226 +704,14 @@ static void aw88261_start(struct aw88261 *aw88261)
if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
return;
aw88261_start_pa(aw88261);
if (sync_start == AW88261_SYNC_START)
aw88261_start_pa(aw88261);
else
queue_delayed_work(system_dfl_wq,
&aw88261->start_work,
AW88261_START_WORK_DELAY_MS);
}
static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
break;
case SND_SOC_DAIFMT_IB_NF:
aw88261->bck_inv_value = AW88261_BCKINV_INVERTED_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported invert mode 0x%x\n",
fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_DSP_A:
aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
break;
case SND_SOC_DAIFMT_MSB:
case SND_SOC_DAIFMT_DSP_B:
aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE;
break;
case SND_SOC_DAIFMT_LSB:
aw88261->md_value = AW88261_I2SMD_LSB_JUSTIFIED_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported DAI format 0x%x\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
return 0;
}
static int aw88261_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return 0;
aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
switch (params_rate(params)) {
case 8000:
aw88261->sr_value = AW88261_I2SSR_8KHZ_VALUE;
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
break;
case 11025:
aw88261->sr_value = AW88261_I2SSR_11P025KHZ_VALUE;
break;
case 12000:
aw88261->sr_value = AW88261_I2SSR_12KHZ_VALUE;
break;
case 16000:
aw88261->sr_value = AW88261_I2SSR_16KHZ_VALUE;
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
break;
case 22050:
aw88261->sr_value = AW88261_I2SSR_22P05KHZ_VALUE;
break;
case 24000:
aw88261->sr_value = AW88261_I2SSR_24KHZ_VALUE;
break;
case 32000:
aw88261->sr_value = AW88261_I2SSR_32KHZ_VALUE;
aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
break;
case 44100:
aw88261->sr_value = AW88261_I2SSR_44P1KHZ_VALUE;
break;
case 48000:
aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
break;
case 96000:
aw88261->sr_value = AW88261_I2SSR_96KHZ_VALUE;
break;
case 192000:
aw88261->sr_value = AW88261_I2SSR_192KHZ_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
switch (params_width(params)) {
case 16:
aw88261->fs_value = AW88261_I2SFS_16_BITS_VALUE;
break;
case 20:
aw88261->fs_value = AW88261_I2SFS_20_BITS_VALUE;
break;
case 24:
aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
break;
case 32:
aw88261->fs_value = AW88261_I2SFS_32_BITS_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported bit width %d\n",
params_width(params));
return -EINVAL;
}
switch (params_physical_width(params)) {
case 16:
aw88261->bck_value = AW88261_I2SBCK_32FS_VALUE;
break;
case 24:
aw88261->bck_value = AW88261_I2SBCK_48FS_VALUE;
break;
case 32:
aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported physical bit width %d\n",
params_physical_width(params));
return -EINVAL;
}
return 0;
}
static int aw88261_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
int chan;
switch (slots) {
case 0:
/* Just reset everything TDM related to I2S values */
aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
aw88261->tx_slotvld_mask = 0 << AW88261_I2S_TX_SLOTVLD_START_BIT;
aw88261->rxl_slotvld_mask = 0 << AW88261_I2S_RXL_SLOTVLD_START_BIT;
aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
return 0;
case 1:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM1S_VALUE;
break;
case 2:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM2S_VALUE;
break;
case 4:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM4S_VALUE;
break;
case 6:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM6S_VALUE;
break;
case 8:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM8S_VALUE;
break;
case 16:
aw88261->slot_num_value = AW88261_SLOT_NUM_TDM16S_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported slot count %d\n", slots);
return -EINVAL;
}
switch (slot_width) {
case 16:
aw88261->tdm_bck_value = AW88261_I2SBCK_32FS_VALUE;
break;
case 20:
case 24:
aw88261->tdm_bck_value = AW88261_I2SBCK_48FS_VALUE;
break;
case 32:
aw88261->tdm_bck_value = AW88261_I2SBCK_64FS_VALUE;
break;
default:
dev_err(aw88261->aw_pa->dev, "unsupported slot width %d\n",
slot_width);
return -EINVAL;
}
if (tx_mask != 0) {
if ((chan = __ffs(tx_mask)) > 16)
return -EINVAL;
aw88261->tx_slotvld_mask = chan << AW88261_I2S_TX_SLOTVLD_START_BIT;
}
if (rx_mask != 0) {
if ((chan = __ffs(rx_mask)) > 16)
return -EINVAL;
aw88261->rxl_slotvld_mask = chan << AW88261_I2S_RXL_SLOTVLD_START_BIT;
}
if ((rx_mask & ~BIT(chan)) != 0) {
if ((chan = __ffs(rx_mask & ~BIT(chan))) > 16)
return -EINVAL;
aw88261->rxr_slotvld_mask = chan << AW88261_I2S_RXR_SLOTVLD_START_BIT;
}
return 0;
}
static const struct snd_soc_dai_ops aw88261_dai_ops = {
.set_fmt = aw88261_set_fmt,
.hw_params = aw88261_hw_params,
.set_tdm_slot = aw88261_set_tdm_slot,
};
static struct snd_soc_dai_driver aw88261_dai[] = {
{
.name = "aw88261-aif",
@ -888,10 +730,78 @@ static struct snd_soc_dai_driver aw88261_dai[] = {
.rates = AW88261_RATES,
.formats = AW88261_FORMATS,
},
.ops = &aw88261_dai_ops,
},
};
static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
struct aw_device *aw_dev = aw88261->aw_pa;
ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
return 0;
}
static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct aw_device *aw_dev = aw88261->aw_pa;
int time;
time = ucontrol->value.integer.value[0];
if (time < mc->min || time > mc->max)
return -EINVAL;
if (time != aw_dev->fade_in_time) {
aw_dev->fade_in_time = time;
return 1;
}
return 0;
}
static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
struct aw_device *aw_dev = aw88261->aw_pa;
ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
return 0;
}
static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct aw_device *aw_dev = aw88261->aw_pa;
int time;
time = ucontrol->value.integer.value[0];
if (time < mc->min || time > mc->max)
return -EINVAL;
if (time != aw_dev->fade_out_time) {
aw_dev->fade_out_time = time;
return 1;
}
return 0;
}
static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
{
/* check the index whether is valid */
@ -970,7 +880,7 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
if (aw88261->aw_pa->status) {
aw88261_dev_stop(aw88261->aw_pa);
aw88261_start(aw88261);
aw88261_start(aw88261, AW88261_SYNC_START);
}
mutex_unlock(&aw88261->lock);
@ -985,8 +895,7 @@ static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
ucontrol->value.integer.value[0] =
(AW88261_MUTE_VOL - vol_desc->ctl_volume) / 2;
ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
return 0;
}
@ -999,13 +908,13 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int value = ucontrol->value.integer.value[0];
int value;
value = ucontrol->value.integer.value[0];
if (value < mc->min || value > mc->max)
return -EINVAL;
value = AW88261_MUTE_VOL - (value * 2);
if (vol_desc->ctl_volume != value) {
vol_desc->ctl_volume = value;
aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
@ -1016,18 +925,48 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
return 0;
}
/*
* The field contains 4 bits in units of 6dB + 6 bits in units of 0.125dB
* which is too precise for TLV (!) so we have to multiply the scale by 2.
*
* The range is clamped at -90dB to prevent overflowing the 4-bit part.
*/
static const DECLARE_TLV_DB_SCALE(volume_tlv, -9000, 25, 0);
static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
return 0;
}
static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int value;
value = ucontrol->value.integer.value[0];
if (value < mc->min || value > mc->max)
return -EINVAL;
if (aw88261->aw_pa->fade_step != value) {
aw88261->aw_pa->fade_step = value;
return 1;
}
return 0;
}
static const struct snd_kcontrol_new aw88261_controls[] = {
SOC_SINGLE_EXT_TLV("PCM Playback Volume", AW88261_SYSCTRL2_REG,
6, AW88261_CTL_MAX_VOL, 1,
aw88261_volume_get, aw88261_volume_set, volume_tlv),
SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
aw88261_volume_set),
SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
aw88261_get_fade_step, aw88261_set_fade_step),
SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
aw88261_get_fade_in_time, aw88261_set_fade_in_time),
SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
aw88261_get_fade_out_time, aw88261_set_fade_out_time),
AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
aw88261_profile_get, aw88261_profile_set),
};
@ -1041,7 +980,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
mutex_lock(&aw88261->lock);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
aw88261_start(aw88261);
aw88261_start(aw88261, AW88261_ASYNC_START);
break;
case SND_SOC_DAPM_POST_PMD:
aw88261_dev_stop(aw88261->aw_pa);
@ -1118,6 +1057,8 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg
if (ret)
return ret;
aw_dev->fade_in_time = AW88261_500_US;
aw_dev->fade_out_time = AW88261_500_US;
aw_dev->prof_cur = AW88261_INIT_PROFILE;
aw_dev->prof_index = AW88261_INIT_PROFILE;
@ -1153,7 +1094,6 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg
static int aw88261_request_firmware_file(struct aw88261 *aw88261)
{
const struct firmware *cont = NULL;
struct aw_container *aw_cfg;
const char *fw_name;
int ret;
@ -1171,17 +1111,15 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261)
dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
fw_name, cont ? cont->size : 0);
aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL);
if (!aw_cfg) {
aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
if (!aw88261->aw_cfg) {
release_firmware(cont);
return -ENOMEM;
}
aw_cfg->len = (int)cont->size;
memcpy(aw_cfg->data, cont->data, cont->size);
aw88261->aw_cfg->len = (int)cont->size;
memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
release_firmware(cont);
aw88261->aw_cfg = aw_cfg;
ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
if (ret) {
dev_err(aw88261->aw_pa->dev, "load [%s] failed !", fw_name);
@ -1204,6 +1142,8 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
int ret;
INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
ret = aw88261_request_firmware_file(aw88261);
if (ret)
return dev_err_probe(aw88261->aw_pa->dev, ret,
@ -1227,8 +1167,16 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
return ret;
}
static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
{
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
cancel_delayed_work_sync(&aw88261->start_work);
}
static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
.probe = aw88261_codec_probe,
.remove = aw88261_codec_remove,
};
static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
@ -1260,7 +1208,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
return ret;
}
if (chip_id != AW88261_CHIP_ID) {
dev_err(&i2c->dev, "unsupported device id = %x", chip_id);
dev_err(&i2c->dev, "unsupported device");
return -ENXIO;
}
@ -1281,7 +1229,8 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
aw_dev->channel = 0;
aw_dev->fw_status = AW88261_DEV_FW_FAILED;
aw_dev->volume_desc.ctl_volume = AW88261_CTL_DEFAULT_VOL;
aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
aw88261_parse_channel_dt(aw88261);
@ -1300,17 +1249,6 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
if (!aw88261)
return -ENOMEM;
/* set defaults */
aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
mutex_init(&aw88261->lock);
i2c_set_clientdata(i2c, aw88261);
@ -1336,7 +1274,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88261_i2c_id[] = {
{ .name = "aw88261" },
{ "aw88261" },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);

View file

@ -116,19 +116,6 @@
#define AW88261_VCALK_SHIFT (0)
#define AW88261_VCALKL_SHIFT (0)
#define AW88261_BCKINV_START_BIT (4)
#define AW88261_BCKINV_BITS_LEN (1)
#define AW88261_BCKINV_MASK \
(~(((1<<AW88261_BCKINV_BITS_LEN)-1) << AW88261_BCKINV_START_BIT))
#define AW88261_BCKINV_NOT_INVERT (0)
#define AW88261_BCKINV_NOT_INVERT_VALUE \
(AW88261_BCKINV_NOT_INVERT << AW88261_BCKINV_START_BIT)
#define AW88261_BCKINV_INVERTED (1)
#define AW88261_BCKINV_INVERTED_VALUE \
(AW88261_BCKINV_INVERTED << AW88261_BCKINV_START_BIT)
#define AW88261_AMPPD_START_BIT (1)
#define AW88261_AMPPD_BITS_LEN (1)
#define AW88261_AMPPD_MASK \
@ -194,6 +181,12 @@
AW88261_OTHS_OT_VALUE | \
AW88261_PLLS_LOCKED_VALUE))
#define AW88261_BIT_SYSST_CHECK \
(AW88261_BSTS_FINISHED_VALUE | \
AW88261_SWS_SWITCHING_VALUE | \
AW88261_CLKS_STABLE_VALUE | \
AW88261_PLLS_LOCKED_VALUE)
#define AW88261_ULS_HMUTE_START_BIT (14)
#define AW88261_ULS_HMUTE_BITS_LEN (1)
#define AW88261_ULS_HMUTE_MASK \
@ -256,8 +249,7 @@
#define AW88261_VOL_MASK \
(~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
#define AW88261_CTL_MAX_VOL (AW88261_MUTE_VOL / 2)
#define AW88261_CTL_DEFAULT_VOL (AW88261_CTL_MAX_VOL / 2)
#define AW88261_VOL_DEFAULT_VALUE (0)
#define AW88261_I2STXEN_START_BIT (6)
#define AW88261_I2STXEN_BITS_LEN (1)
@ -272,148 +264,7 @@
#define AW88261_I2STXEN_ENABLE_VALUE \
(AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
#define AW88261_I2SMD_START_BIT (8)
#define AW88261_I2SMD_BITS_LEN (2)
#define AW88261_I2SMD_MASK \
(~(((1<<AW88261_I2SMD_BITS_LEN)-1) << AW88261_I2SMD_START_BIT))
#define AW88261_I2SMD_PHILIPS_STANDARD (0)
#define AW88261_I2SMD_PHILIPS_STANDARD_VALUE \
(AW88261_I2SMD_PHILIPS_STANDARD << AW88261_I2SMD_START_BIT)
#define AW88261_I2SMD_MSB_JUSTIFIED (1)
#define AW88261_I2SMD_MSB_JUSTIFIED_VALUE \
(AW88261_I2SMD_MSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
#define AW88261_I2SMD_LSB_JUSTIFIED (2)
#define AW88261_I2SMD_LSB_JUSTIFIED_VALUE \
(AW88261_I2SMD_LSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
#define AW88261_I2SFS_START_BIT (6)
#define AW88261_I2SFS_BITS_LEN (2)
#define AW88261_I2SFS_MASK \
(~(((1<<AW88261_I2SFS_BITS_LEN)-1)<<AW88261_I2SFS_START_BIT))
#define AW88261_I2SFS_16_BITS (0)
#define AW88261_I2SFS_16_BITS_VALUE \
(AW88261_I2SFS_16_BITS << AW88261_I2SFS_START_BIT)
#define AW88261_I2SFS_20_BITS (1)
#define AW88261_I2SFS_20_BITS_VALUE \
(AW88261_I2SFS_20_BITS << AW88261_I2SFS_START_BIT)
#define AW88261_I2SFS_24_BITS (2)
#define AW88261_I2SFS_24_BITS_VALUE \
(AW88261_I2SFS_24_BITS << AW88261_I2SFS_START_BIT)
#define AW88261_I2SFS_32_BITS (3)
#define AW88261_I2SFS_32_BITS_VALUE \
(AW88261_I2SFS_32_BITS << AW88261_I2SFS_START_BIT)
#define AW88261_I2SBCK_START_BIT (4)
#define AW88261_I2SBCK_BITS_LEN (2)
#define AW88261_I2SBCK_MASK \
(~(((1<<AW88261_I2SBCK_BITS_LEN)-1) << AW88261_I2SBCK_START_BIT))
#define AW88261_I2SBCK_32FS (0)
#define AW88261_I2SBCK_32FS_VALUE \
(AW88261_I2SBCK_32FS << AW88261_I2SBCK_START_BIT)
#define AW88261_I2SBCK_48FS (1)
#define AW88261_I2SBCK_48FS_VALUE \
(AW88261_I2SBCK_48FS << AW88261_I2SBCK_START_BIT)
#define AW88261_I2SBCK_64FS (2)
#define AW88261_I2SBCK_64FS_VALUE \
(AW88261_I2SBCK_64FS << AW88261_I2SBCK_START_BIT)
#define AW88261_TDM_BCK_UNSET UINT_MAX
#define AW88261_I2SSR_START_BIT (0)
#define AW88261_I2SSR_BITS_LEN (4)
#define AW88261_I2SSR_MASK \
(~(((1<<AW88261_I2SSR_BITS_LEN)-1) << AW88261_I2SSR_START_BIT))
#define AW88261_I2SSR_8KHZ (0)
#define AW88261_I2SSR_8KHZ_VALUE \
(AW88261_I2SSR_8KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_11P025KHZ (1)
#define AW88261_I2SSR_11P025KHZ_VALUE \
(AW88261_I2SSR_11P025KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_12KHZ (2)
#define AW88261_I2SSR_12KHZ_VALUE \
(AW88261_I2SSR_12KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_16KHZ (3)
#define AW88261_I2SSR_16KHZ_VALUE \
(AW88261_I2SSR_16KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_22P05KHZ (4)
#define AW88261_I2SSR_22P05KHZ_VALUE \
(AW88261_I2SSR_22P05KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_24KHZ (5)
#define AW88261_I2SSR_24KHZ_VALUE \
(AW88261_I2SSR_24KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_32KHZ (6)
#define AW88261_I2SSR_32KHZ_VALUE \
(AW88261_I2SSR_32KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_44P1KHZ (7)
#define AW88261_I2SSR_44P1KHZ_VALUE \
(AW88261_I2SSR_44P1KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_48KHZ (8)
#define AW88261_I2SSR_48KHZ_VALUE \
(AW88261_I2SSR_48KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_96KHZ (9)
#define AW88261_I2SSR_96KHZ_VALUE \
(AW88261_I2SSR_96KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_I2SSR_192KHZ (10)
#define AW88261_I2SSR_192KHZ_VALUE \
(AW88261_I2SSR_192KHZ << AW88261_I2SSR_START_BIT)
#define AW88261_SLOT_NUM_START_BIT (12)
#define AW88261_SLOT_NUM_BITS_LEN (3)
#define AW88261_SLOT_NUM_MASK \
(~(((1<<AW88261_SLOT_NUM_BITS_LEN)-1) << AW88261_SLOT_NUM_START_BIT))
#define AW88261_SLOT_NUM_I2S_MODE (0)
#define AW88261_SLOT_NUM_I2S_MODE_VALUE \
(AW88261_SLOT_NUM_I2S_MODE << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM1S (1)
#define AW88261_SLOT_NUM_TDM1S_VALUE \
(AW88261_SLOT_NUM_TDM1S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM2S (2)
#define AW88261_SLOT_NUM_TDM2S_VALUE \
(AW88261_SLOT_NUM_TDM2S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM4S (3)
#define AW88261_SLOT_NUM_TDM4S_VALUE \
(AW88261_SLOT_NUM_TDM4S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM6S (4)
#define AW88261_SLOT_NUM_TDM6S_VALUE \
(AW88261_SLOT_NUM_TDM6S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM8S (5)
#define AW88261_SLOT_NUM_TDM8S_VALUE \
(AW88261_SLOT_NUM_TDM8S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_SLOT_NUM_TDM16S (6)
#define AW88261_SLOT_NUM_TDM16S_VALUE \
(AW88261_SLOT_NUM_TDM16S << AW88261_SLOT_NUM_START_BIT)
#define AW88261_I2S_TX_SLOTVLD_START_BIT (8)
#define AW88261_I2S_TX_SLOTVLD_BITS_LEN (4)
#define AW88261_I2S_TX_SLOTVLD_MASK \
(~(((1<<AW88261_I2S_TX_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_TX_SLOTVLD_START_BIT))
#define AW88261_I2S_RXR_SLOTVLD_START_BIT (4)
#define AW88261_I2S_RXR_SLOTVLD_BITS_LEN (4)
#define AW88261_I2S_RXR_SLOTVLD_MASK \
(~(((1<<AW88261_I2S_RXR_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXR_SLOTVLD_START_BIT))
#define AW88261_I2S_RXL_SLOTVLD_START_BIT (0)
#define AW88261_I2S_RXL_SLOTVLD_BITS_LEN (4)
#define AW88261_I2S_RXL_SLOTVLD_MASK \
(~(((1<<AW88261_I2S_RXL_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXL_SLOTVLD_START_BIT))
#define AW88261_CCO_MUX_START_BIT (6)
#define AW88261_CCO_MUX_START_BIT (14)
#define AW88261_CCO_MUX_BITS_LEN (1)
#define AW88261_CCO_MUX_MASK \
(~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
@ -519,20 +370,21 @@
#define AW88261_START_RETRIES (5)
#define AW88261_START_WORK_DELAY_MS (0)
/* NOTE: 192000 has a reg value donwstream but not listed in datasheet */
#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
SNDRV_PCM_RATE_12000 | \
SNDRV_PCM_RATE_24000 | \
SNDRV_PCM_RATE_96000)
#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define FADE_TIME_MAX 100000
#define FADE_TIME_MIN 0
#define AW88261_DEV_DEFAULT_CH (0)
#define AW88261_ACF_FILE "aw88261_acf.bin"
#define AW88261_DEV_SYSST_CHECK_MAX (10)
#define AW88261_SOFT_RESET_VALUE (0x55aa)
#define AW88261_REG_TO_DB (0x3f)
#define AW88261_VOL_START_MASK (0xfc00)
#define AW88261_INIT_PROFILE (0)
#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \
@ -551,6 +403,11 @@
.put = profile_set, \
}
enum {
AW88261_SYNC_START = 0,
AW88261_ASYNC_START,
};
enum aw88261_id {
AW88261_CHIP_ID = 0x2113,
};
@ -585,6 +442,7 @@ struct aw88261 {
struct aw_device *aw_pa;
struct mutex lock;
struct gpio_desc *reset_gpio;
struct delayed_work start_work;
struct regmap *regmap;
struct aw_container *aw_cfg;
@ -593,19 +451,6 @@ struct aw88261 {
unsigned int mute_st;
unsigned int amppd_st;
unsigned int sr_value;
unsigned int cco_mux_value;
unsigned int fs_value;
unsigned int bck_value;
unsigned int bck_inv_value;
unsigned int tdm_bck_value;
unsigned int md_value;
unsigned int slot_num_value;
unsigned int tx_slotvld_mask;
unsigned int rxl_slotvld_mask;
unsigned int rxr_slotvld_mask;
bool phase_sync;
};

View file

@ -1,495 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2026, Jorijn van der Graaf
*
* SoundWire slave driver for the Qualcomm WCD9378 audio codec.
*
* The codec presents two SoundWire slaves (RX and TX, mfg 0x0217 part
* 0x0110); the SDCA control space is a 32-bit paged register map accessed
* through the TX slave.
*/
#include <linux/component.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/pcm_params.h>
#include "wcd-common.h"
#include "wcd9378.h"
#define WCD9378_SDW_CH(id, pn, cmask, mmask) \
[id] = { \
.port_num = pn, \
.ch_mask = cmask, \
.master_ch_mask = mmask, \
}
/*
* Each ADC sits alone on its own TX device port (channel 1); by default
* they land on channels 1/2/3 of the same master port (SWRM_TX1 on the
* FP6). DMIC/MBHC masks per the downstream qcom,tx_swr_ch_map.
*/
static struct wcd_sdw_ch_info wcd9378_sdw_tx_ch_info[] = {
WCD9378_SDW_CH(WCD9378_ADC1, WCD9378_ADC_1_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_ADC2, WCD9378_ADC_2_PORT, BIT(0), BIT(1)),
WCD9378_SDW_CH(WCD9378_ADC3, WCD9378_ADC_3_PORT, BIT(0), BIT(2)),
WCD9378_SDW_CH(WCD9378_DMIC0, WCD9378_DMIC_0_1_MBHC_PORT, BIT(2), BIT(0)),
WCD9378_SDW_CH(WCD9378_DMIC1, WCD9378_DMIC_0_1_MBHC_PORT, BIT(3), BIT(1)),
WCD9378_SDW_CH(WCD9378_MBHC, WCD9378_DMIC_0_1_MBHC_PORT, BIT(2), BIT(2)),
WCD9378_SDW_CH(WCD9378_DMIC2, WCD9378_DMIC_2_5_PORT, BIT(0), BIT(2)),
WCD9378_SDW_CH(WCD9378_DMIC3, WCD9378_DMIC_2_5_PORT, BIT(1), BIT(3)),
WCD9378_SDW_CH(WCD9378_DMIC4, WCD9378_DMIC_2_5_PORT, BIT(2), BIT(0)),
WCD9378_SDW_CH(WCD9378_DMIC5, WCD9378_DMIC_2_5_PORT, BIT(3), BIT(1)),
};
static struct wcd_sdw_ch_info wcd9378_sdw_rx_ch_info[] = {
WCD9378_SDW_CH(WCD9378_HPH_L, WCD9378_HPH_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_HPH_R, WCD9378_HPH_PORT, BIT(1), BIT(1)),
WCD9378_SDW_CH(WCD9378_CLSH, WCD9378_CLSH_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_COMP_L, WCD9378_COMP_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_COMP_R, WCD9378_COMP_PORT, BIT(1), BIT(1)),
WCD9378_SDW_CH(WCD9378_LO, WCD9378_LO_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_DSD_L, WCD9378_DSD_PORT, BIT(0), BIT(0)),
WCD9378_SDW_CH(WCD9378_DSD_R, WCD9378_DSD_PORT, BIT(1), BIT(1)),
};
static struct sdw_dpn_prop wcd9378_dpn_prop[WCD9378_MAX_SWR_PORTS] = {
{
.num = 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 8,
.simple_ch_prep_sm = true,
}, {
.num = 2,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 4,
.simple_ch_prep_sm = true,
}, {
.num = 3,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 4,
.simple_ch_prep_sm = true,
}, {
.num = 4,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 4,
.simple_ch_prep_sm = true,
}, {
.num = 5,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 4,
.simple_ch_prep_sm = true,
}
};
int wcd9378_sdw_hw_params(struct wcd9378_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sdw_port_config port_config[WCD9378_MAX_SWR_PORTS];
unsigned long ch_mask;
int i, j;
wcd->sconfig.ch_count = 1;
wcd->active_ports = 0;
for (i = 0; i < WCD9378_MAX_SWR_PORTS; i++) {
ch_mask = wcd->port_config[i].ch_mask;
if (!ch_mask)
continue;
for_each_set_bit(j, &ch_mask, 4)
wcd->sconfig.ch_count++;
port_config[wcd->active_ports] = wcd->port_config[i];
wcd->active_ports++;
}
wcd->sconfig.bps = 1;
wcd->sconfig.frame_rate = params_rate(params);
wcd->sconfig.direction = wcd->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
wcd->sconfig.type = SDW_STREAM_PCM;
return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig,
&port_config[0], wcd->active_ports,
wcd->sruntime);
}
EXPORT_SYMBOL_GPL(wcd9378_sdw_hw_params);
/*
* Tell the codec the bus clock: base 19.2 MHz plus a scale (div) per bank.
* The downstream driver writes these raw SCP registers on every capture
* start; here the bus_config callback covers bank switches.
*/
static int wcd9378_bus_config(struct sdw_slave *slave,
struct sdw_bus_params *params)
{
u8 scale;
switch (params->curr_dr_freq) {
case 4800000:
scale = WCD9378_SWRS_CLK_SCALE_DIV4;
break;
case 9600000:
default:
scale = WCD9378_SWRS_CLK_SCALE_DIV2;
break;
}
sdw_write(slave, WCD9378_SWRS_SCP_HOST_CLK_DIV2_CTL(params->next_bank),
0x01);
sdw_write(slave, WCD9378_SWRS_SCP_BASE_CLK,
WCD9378_SWRS_BASE_CLK_19P2MHZ);
sdw_write(slave, WCD9378_SWRS_SCP_BUSCLK_SCALE_BANK0, scale);
sdw_write(slave, WCD9378_SWRS_SCP_BUSCLK_SCALE_BANK1, scale);
return 0;
}
static const struct sdw_slave_ops wcd9378_slave_ops = {
.update_status = wcd_update_status,
.bus_config = wcd9378_bus_config,
};
static const struct reg_default wcd9378_defaults[] = {
{ WCD9378_ANA_BIAS, 0x00 },
{ WCD9378_ANA_TX_CH1, 0x20 },
{ WCD9378_ANA_TX_CH2, 0x00 },
{ WCD9378_ANA_TX_CH3, 0x20 },
{ WCD9378_ANA_TX_CH3_HPF, 0x00 },
{ WCD9378_ANA_MICB2_RAMP, 0x00 },
{ WCD9378_BIAS_VBG_FINE_ADJ, 0x55 },
{ WCD9378_MBHC_CTL_SPARE_1, 0x02 },
{ WCD9378_MICB1_TEST_CTL_2, 0x00 },
{ WCD9378_MICB2_TEST_CTL_2, 0x00 },
{ WCD9378_MICB3_TEST_CTL_2, 0x80 },
{ WCD9378_TX_COM_TXFE_DIV_CTL, 0x22 },
{ WCD9378_SLEEP_CTL, 0x16 },
{ WCD9378_TX_NEW_CH12_MUX, 0x11 },
{ WCD9378_TX_NEW_CH34_MUX, 0x23 },
{ WCD9378_TOP_CLK_CFG, 0x00 },
{ WCD9378_CDC_ANA_TX_CLK_CTL, 0x0e },
{ WCD9378_CDC_AMIC_CTL, 0x07 },
{ WCD9378_PDM_WD_CTL0, 0x0f },
{ WCD9378_PDM_WD_CTL1, 0x0f },
{ WCD9378_PLATFORM_CTL, 0x01 },
{ WCD9378_SYS_USAGE_CTRL, 0x00 },
{ WCD9378_HPH_UP_T0, 0x02 },
{ WCD9378_HPH_UP_T9, 0x02 },
{ WCD9378_HPH_DN_T0, 0x05 },
{ WCD9378_MICB_REMAP_TABLE_VAL_3, 0x00 },
{ WCD9378_MICB_REMAP_TABLE_VAL_4, 0x00 },
{ WCD9378_MICB_REMAP_TABLE_VAL_5, 0x00 },
{ WCD9378_SM0_MB_SEL, 0x00 },
{ WCD9378_SM1_MB_SEL, 0x00 },
{ WCD9378_SM2_MB_SEL, 0x00 },
{ WCD9378_MB_PULLUP_EN, 0x00 },
{ WCD9378_SMP_AMP_FUNC_ACT, 0x00 },
{ WCD9378_CMT_GRP_MASK, 0x00 },
{ WCD9378_SMP_JACK_IT31_MICB, 0x00 },
{ WCD9378_SMP_JACK_IT31_USAGE, 0x03 },
{ WCD9378_SMP_JACK_PDE34_REQ_PS, 0x03 },
{ WCD9378_SMP_JACK_FUNC_ACT, 0x00 },
{ WCD9378_SMP_MIC_IT11_MICB(0), 0x00 },
{ WCD9378_SMP_MIC_IT11_USAGE(0), 0x03 },
{ WCD9378_SMP_MIC_PDE11_REQ_PS(0), 0x03 },
{ WCD9378_SMP_MIC_FUNC_ACT(0), 0x00 },
{ WCD9378_SMP_MIC_IT11_MICB(1), 0x00 },
{ WCD9378_SMP_MIC_IT11_USAGE(1), 0x03 },
{ WCD9378_SMP_MIC_PDE11_REQ_PS(1), 0x03 },
{ WCD9378_SMP_MIC_FUNC_ACT(1), 0x00 },
{ WCD9378_SMP_MIC_IT11_MICB(2), 0x00 },
{ WCD9378_SMP_MIC_IT11_USAGE(2), 0x03 },
{ WCD9378_SMP_MIC_PDE11_REQ_PS(2), 0x03 },
{ WCD9378_SMP_MIC_FUNC_ACT(2), 0x00 },
};
static bool wcd9378_rdwr_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WCD9378_ANA_BIAS:
case WCD9378_ANA_TX_CH1:
case WCD9378_ANA_TX_CH2:
case WCD9378_ANA_TX_CH3:
case WCD9378_ANA_TX_CH3_HPF:
case WCD9378_ANA_MICB2_RAMP:
case WCD9378_BIAS_VBG_FINE_ADJ:
case WCD9378_MBHC_CTL_SPARE_1:
case WCD9378_MICB1_TEST_CTL_2:
case WCD9378_MICB2_TEST_CTL_2:
case WCD9378_MICB3_TEST_CTL_2:
case WCD9378_TX_COM_TXFE_DIV_CTL:
case WCD9378_SLEEP_CTL:
case WCD9378_TX_NEW_CH12_MUX:
case WCD9378_TX_NEW_CH34_MUX:
case WCD9378_HPH_RDAC_GAIN_CTL:
case WCD9378_HPH_RDAC_HD2_CTL_L:
case WCD9378_HPH_RDAC_HD2_CTL_R:
case WCD9378_TOP_CLK_CFG:
case WCD9378_CDC_ANA_TX_CLK_CTL:
case WCD9378_CDC_AMIC_CTL:
case WCD9378_PDM_WD_CTL0:
case WCD9378_PDM_WD_CTL1:
case WCD9378_PLATFORM_CTL:
case WCD9378_SYS_USAGE_CTRL:
case WCD9378_HPH_UP_T0:
case WCD9378_HPH_UP_T9:
case WCD9378_HPH_DN_T0:
case WCD9378_MICB_REMAP_TABLE_VAL_3:
case WCD9378_MICB_REMAP_TABLE_VAL_4:
case WCD9378_MICB_REMAP_TABLE_VAL_5:
case WCD9378_SM0_MB_SEL:
case WCD9378_SM1_MB_SEL:
case WCD9378_SM2_MB_SEL:
case WCD9378_MB_PULLUP_EN:
case WCD9378_SMP_AMP_FUNC_STAT:
case WCD9378_SMP_AMP_FUNC_ACT:
case WCD9378_CMT_GRP_MASK:
case WCD9378_SMP_JACK_IT31_MICB:
case WCD9378_SMP_JACK_IT31_USAGE:
case WCD9378_SMP_JACK_PDE34_REQ_PS:
case WCD9378_SMP_JACK_FUNC_STAT:
case WCD9378_SMP_JACK_FUNC_ACT:
case WCD9378_SMP_MIC_IT11_MICB(0):
case WCD9378_SMP_MIC_IT11_USAGE(0):
case WCD9378_SMP_MIC_PDE11_REQ_PS(0):
case WCD9378_SMP_MIC_FUNC_STAT(0):
case WCD9378_SMP_MIC_FUNC_ACT(0):
case WCD9378_SMP_MIC_IT11_MICB(1):
case WCD9378_SMP_MIC_IT11_USAGE(1):
case WCD9378_SMP_MIC_PDE11_REQ_PS(1):
case WCD9378_SMP_MIC_FUNC_STAT(1):
case WCD9378_SMP_MIC_FUNC_ACT(1):
case WCD9378_SMP_MIC_IT11_MICB(2):
case WCD9378_SMP_MIC_IT11_USAGE(2):
case WCD9378_SMP_MIC_PDE11_REQ_PS(2):
case WCD9378_SMP_MIC_FUNC_STAT(2):
case WCD9378_SMP_MIC_FUNC_ACT(2):
return true;
}
return false;
}
static bool wcd9378_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WCD9378_FUNC_EXT_ID_0:
case WCD9378_FUNC_EXT_ID_1:
case WCD9378_FUNC_EXT_VER:
case WCD9378_FUNC_STAT:
case WCD9378_DEV_MANU_ID_0:
case WCD9378_DEV_MANU_ID_1:
case WCD9378_DEV_PART_ID_0:
case WCD9378_DEV_PART_ID_1:
case WCD9378_DEV_VER:
case WCD9378_EFUSE_REG_16:
case WCD9378_EFUSE_REG_29:
case WCD9378_SEQ_TX0_STAT:
case WCD9378_SEQ_TX1_STAT:
case WCD9378_SEQ_TX2_STAT:
case WCD9378_SMP_AMP_FUNC_STAT:
case WCD9378_SMP_JACK_FUNC_STAT:
case WCD9378_SMP_JACK_PDE34_ACT_PS:
case WCD9378_SMP_MIC_FUNC_STAT(0):
case WCD9378_SMP_MIC_FUNC_STAT(1):
case WCD9378_SMP_MIC_FUNC_STAT(2):
case WCD9378_SMP_MIC_OT10_USAGE(0):
case WCD9378_SMP_MIC_PDE11_ACT_PS(0):
case WCD9378_SMP_MIC_OT10_USAGE(1):
case WCD9378_SMP_MIC_PDE11_ACT_PS(1):
case WCD9378_SMP_MIC_OT10_USAGE(2):
case WCD9378_SMP_MIC_PDE11_ACT_PS(2):
return true;
}
return false;
}
static bool wcd9378_readable_register(struct device *dev, unsigned int reg)
{
if (wcd9378_volatile_register(dev, reg))
return true;
return wcd9378_rdwr_register(dev, reg);
}
static const struct regmap_config wcd9378_regmap_config = {
.name = "wcd9378_csr",
.reg_bits = 32,
.val_bits = 8,
.cache_type = REGCACHE_MAPLE,
.reg_defaults = wcd9378_defaults,
.num_reg_defaults = ARRAY_SIZE(wcd9378_defaults),
.max_register = WCD9378_MAX_REGISTER,
.readable_reg = wcd9378_readable_register,
.writeable_reg = wcd9378_rdwr_register,
.volatile_reg = wcd9378_volatile_register,
};
static int wcd9378_sdw_probe(struct sdw_slave *pdev,
const struct sdw_device_id *id)
{
struct device *dev = &pdev->dev;
struct wcd9378_sdw_priv *wcd;
u8 master_ch_mask[WCD9378_MAX_SWR_CH_IDS];
int master_ch_mask_size = 0;
int ret, i;
wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
if (!wcd)
return -ENOMEM;
/* Port map index starts at 0, however the data ports start at index 1 */
if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
wcd->is_tx = true;
ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
&pdev->m_port_map[1],
WCD9378_MAX_TX_SWR_PORTS);
} else {
ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
&pdev->m_port_map[1],
WCD9378_MAX_SWR_PORTS);
}
if (ret < 0)
dev_info(dev, "Error getting static port mapping for %s (%d)\n",
wcd->is_tx ? "TX" : "RX", ret);
wcd->sdev = pdev;
dev_set_drvdata(dev, wcd);
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
pdev->prop.lane_control_support = true;
pdev->prop.simple_clk_stop_capable = true;
/* The SDCA control space sits above the 16-bit address range */
pdev->prop.paging_support = true;
memset(master_ch_mask, 0, WCD9378_MAX_SWR_CH_IDS);
if (wcd->is_tx) {
master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
"qcom,tx-channel-mapping");
master_ch_mask_size = min_t(int, master_ch_mask_size,
ARRAY_SIZE(wcd9378_sdw_tx_ch_info));
if (master_ch_mask_size > 0)
ret = of_property_read_u8_array(dev->of_node,
"qcom,tx-channel-mapping",
master_ch_mask,
master_ch_mask_size);
} else {
master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
"qcom,rx-channel-mapping");
master_ch_mask_size = min_t(int, master_ch_mask_size,
ARRAY_SIZE(wcd9378_sdw_rx_ch_info));
if (master_ch_mask_size > 0)
ret = of_property_read_u8_array(dev->of_node,
"qcom,rx-channel-mapping",
master_ch_mask,
master_ch_mask_size);
}
if (wcd->is_tx) {
pdev->prop.source_ports = GENMASK(WCD9378_MAX_TX_SWR_PORTS, 1);
pdev->prop.src_dpn_prop = wcd9378_dpn_prop;
wcd->ch_info = &wcd9378_sdw_tx_ch_info[0];
for (i = 0; i < master_ch_mask_size; i++)
wcd->ch_info[i].master_ch_mask = WCD9378_SWRM_CH_MASK(master_ch_mask[i]);
pdev->prop.wake_capable = true;
wcd->regmap = devm_regmap_init_sdw(pdev, &wcd9378_regmap_config);
if (IS_ERR(wcd->regmap))
return dev_err_probe(dev, PTR_ERR(wcd->regmap),
"Regmap init failed\n");
/* Start in cache-only until device is enumerated */
regcache_cache_only(wcd->regmap, true);
} else {
pdev->prop.sink_ports = GENMASK(WCD9378_MAX_SWR_PORTS, 1);
pdev->prop.sink_dpn_prop = wcd9378_dpn_prop;
wcd->ch_info = &wcd9378_sdw_rx_ch_info[0];
for (i = 0; i < master_ch_mask_size; i++)
wcd->ch_info[i].master_ch_mask = WCD9378_SWRM_CH_MASK(master_ch_mask[i]);
}
ret = component_add(dev, &wcd_sdw_component_ops);
if (ret)
return ret;
/* Set suspended until aggregate device is bind */
pm_runtime_set_suspended(dev);
return 0;
}
static void wcd9378_sdw_remove(struct sdw_slave *pdev)
{
struct device *dev = &pdev->dev;
component_del(dev, &wcd_sdw_component_ops);
}
static const struct sdw_device_id wcd9378_sdw_id[] = {
SDW_SLAVE_ENTRY(0x0217, 0x0110, 0),
{ },
};
MODULE_DEVICE_TABLE(sdw, wcd9378_sdw_id);
static int wcd9378_sdw_runtime_suspend(struct device *dev)
{
struct wcd9378_sdw_priv *wcd = dev_get_drvdata(dev);
if (wcd->regmap) {
regcache_cache_only(wcd->regmap, true);
regcache_mark_dirty(wcd->regmap);
}
return 0;
}
static int wcd9378_sdw_runtime_resume(struct device *dev)
{
struct wcd9378_sdw_priv *wcd = dev_get_drvdata(dev);
if (wcd->regmap) {
regcache_cache_only(wcd->regmap, false);
regcache_sync(wcd->regmap);
}
return 0;
}
static const struct dev_pm_ops wcd9378_sdw_pm_ops = {
RUNTIME_PM_OPS(wcd9378_sdw_runtime_suspend, wcd9378_sdw_runtime_resume, NULL)
};
static struct sdw_driver wcd9378_sdw_driver = {
.probe = wcd9378_sdw_probe,
.remove = wcd9378_sdw_remove,
.ops = &wcd9378_slave_ops,
.id_table = wcd9378_sdw_id,
.driver = {
.name = "wcd9378-sdw",
.pm = pm_ptr(&wcd9378_sdw_pm_ops),
}
};
module_sdw_driver(wcd9378_sdw_driver);
MODULE_DESCRIPTION("WCD9378 SDW codec driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -1,246 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2026, Jorijn van der Graaf
*
* Register map for the Qualcomm WCD9378 audio codec.
*
* The codec exposes its control registers in the SoundWire SDCA control
* address space (bit 30 set, SDCA function number in bits 25:22), accessed
* through the TX SoundWire slave. The analog core registers (function 0,
* implementation-defined region at +0x180000) are layout-compatible with
* the WCD937x family; on top of that the chip adds SDCA-style functions
* (SmartMIC0/1/2, SmartJACK, SmartAMP) whose sequencers drive the analog
* power-up autonomously.
*/
#ifndef __WCD9378_H__
#define __WCD9378_H__
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/soc.h>
/* SDCA function 0 (extension unit): device identity */
#define WCD9378_FUNC_EXT_ID_0 0x40000048
#define WCD9378_FUNC_EXT_ID_1 0x40000049
#define WCD9378_FUNC_EXT_VER 0x40000050
#define WCD9378_FUNC_STAT 0x40080000
#define WCD9378_DEV_MANU_ID_0 0x40100060
#define WCD9378_DEV_MANU_ID_1 0x40100061
#define WCD9378_DEV_PART_ID_0 0x40100068
#define WCD9378_DEV_PART_ID_1 0x40100069
#define WCD9378_DEV_VER 0x40100070
/* Analog core (WCD937x-compatible layout), function 0 + 0x180000 */
#define WCD9378_ANA_PAGE 0x40180000
#define WCD9378_ANA_BIAS 0x40180001
#define WCD9378_ANA_BIAS_ANALOG_BIAS_EN BIT(7)
#define WCD9378_ANA_BIAS_PRECHRG_EN BIT(6)
#define WCD9378_ANA_RX_SUPPLIES 0x40180008
#define WCD9378_ANA_TX_CH1 0x4018000e
#define WCD9378_ANA_TX_CH2 0x4018000f
#define WCD9378_ANA_TX_CH2_HPF1_INIT BIT(6)
#define WCD9378_ANA_TX_CH2_HPF2_INIT BIT(5)
#define WCD9378_ANA_TX_CH3 0x40180010
#define WCD9378_ANA_TX_CH3_HPF 0x40180011
#define WCD9378_ANA_TX_CH3_HPF3_INIT BIT(6)
#define WCD9378_ANA_TX_GAIN_MASK GENMASK(4, 0)
#define WCD9378_ANA_MICB1 0x40180022
#define WCD9378_ANA_MICB2 0x40180023
#define WCD9378_ANA_MICB2_RAMP 0x40180024
#define WCD9378_ANA_MICB2_RAMP_SHIFT_CTL_MASK GENMASK(4, 2)
#define WCD9378_ANA_MICB2_RAMP_EN BIT(7)
#define WCD9378_ANA_MICB3 0x40180025
#define WCD9378_BIAS_VBG_FINE_ADJ 0x40180029
#define WCD9378_MBHC_CTL_SPARE_1 0x40180058
#define WCD9378_MICB1_TEST_CTL_2 0x4018006c
#define WCD9378_MICB2_TEST_CTL_2 0x4018006f
#define WCD9378_MICB3_TEST_CTL_2 0x40180072
#define WCD9378_TX_COM_TXFE_DIV_CTL 0x4018007b
#define WCD9378_TX_COM_TXFE_DIV_SEQ_BYPASS BIT(7)
#define WCD9378_SLEEP_CTL 0x40180103
#define WCD9378_SLEEP_CTL_BG_CTL_MASK GENMASK(3, 1)
#define WCD9378_SLEEP_CTL_BG_EN BIT(7)
#define WCD9378_SLEEP_CTL_LDOL_BG_SEL BIT(6)
#define WCD9378_TX_NEW_CH12_MUX 0x4018012e
#define WCD9378_TX_NEW_CH12_MUX_CH1_SEL_MASK GENMASK(2, 0)
#define WCD9378_TX_NEW_CH12_MUX_CH2_SEL_MASK GENMASK(5, 3)
#define WCD9378_TX_NEW_CH34_MUX 0x4018012f
#define WCD9378_TX_NEW_CH34_MUX_CH3_SEL_MASK GENMASK(2, 0)
#define WCD9378_HPH_RDAC_GAIN_CTL 0x40180132
#define WCD9378_HPH_RDAC_HD2_CTL_L 0x40180133
#define WCD9378_HPH_RDAC_HD2_CTL_R 0x40180136
/* Digital page */
#define WCD9378_TOP_CLK_CFG 0x40180407
#define WCD9378_CDC_ANA_TX_CLK_CTL 0x40180417
#define WCD9378_CDC_ANA_TXSCBIAS_CLK_EN BIT(0)
#define WCD9378_CDC_AMIC_CTL 0x4018045a
#define WCD9378_PDM_WD_CTL0 0x40180465
#define WCD9378_PDM_WD_CTL1 0x40180466
#define WCD9378_EFUSE_REG_16 0x401804c0
#define WCD9378_EFUSE_REG_29 0x401804cd
#define WCD9378_PLATFORM_CTL 0x401804f0
/* Sequencer block (SEQR) */
#define WCD9378_SYS_USAGE_CTRL 0x40180501
#define WCD9378_SYS_USAGE_CTRL_MASK GENMASK(3, 0)
#define WCD9378_HPH_UP_T0 0x40180510
#define WCD9378_HPH_UP_T9 0x40180519
#define WCD9378_HPH_DN_T0 0x4018051b
#define WCD9378_SEQ_TX0_STAT 0x40180592
#define WCD9378_SEQ_TX1_STAT 0x40180593
#define WCD9378_SEQ_TX2_STAT 0x40180594
#define WCD9378_MICB_REMAP_TABLE_VAL_3 0x401805a3
#define WCD9378_MICB_REMAP_TABLE_VAL_4 0x401805a4
#define WCD9378_MICB_REMAP_TABLE_VAL_5 0x401805a5
#define WCD9378_SM0_MB_SEL 0x401805b0
#define WCD9378_SM1_MB_SEL 0x401805b1
#define WCD9378_SM2_MB_SEL 0x401805b2
#define WCD9378_SM_MB_SEL_MASK GENMASK(1, 0)
#define WCD9378_MB_PULLUP_EN 0x401805b3
/* SmartAMP SDCA function */
#define WCD9378_SMP_AMP_FUNC_STAT 0x40880000
#define WCD9378_SMP_AMP_FUNC_ACT 0x40880008
/* SmartJACK SDCA function (hosts ADC2 when fed from AMIC2) */
#define WCD9378_CMT_GRP_MASK 0x40c00008
#define WCD9378_SMP_JACK_IT31_MICB 0x40c00798
#define WCD9378_SMP_JACK_IT31_USAGE 0x40c007a0
#define WCD9378_SMP_JACK_PDE34_REQ_PS 0x40c00808
#define WCD9378_SMP_JACK_FUNC_STAT 0x40c80000
#define WCD9378_SMP_JACK_FUNC_ACT 0x40c80008
#define WCD9378_SMP_JACK_PDE34_ACT_PS 0x40c80800
/* SmartMIC0/1/2 SDCA functions (ADC1/ADC2/ADC3 sequencers) */
#define WCD9378_SMP_MIC_BASE(n) (0x41000000 + (n) * 0x400000)
#define WCD9378_SMP_MIC_IT11_MICB(n) (WCD9378_SMP_MIC_BASE(n) + 0x98)
#define WCD9378_SMP_MIC_IT11_USAGE(n) (WCD9378_SMP_MIC_BASE(n) + 0xa0)
#define WCD9378_SMP_MIC_PDE11_REQ_PS(n) (WCD9378_SMP_MIC_BASE(n) + 0x108)
#define WCD9378_SMP_MIC_OT10_USAGE(n) (WCD9378_SMP_MIC_BASE(n) + 0x3a0)
#define WCD9378_SMP_MIC_FUNC_STAT(n) (WCD9378_SMP_MIC_BASE(n) + 0x80000)
#define WCD9378_SMP_MIC_FUNC_ACT(n) (WCD9378_SMP_MIC_BASE(n) + 0x80008)
#define WCD9378_SMP_MIC_PDE11_ACT_PS(n) (WCD9378_SMP_MIC_BASE(n) + 0x80100)
#define WCD9378_MAX_REGISTER 0x41900070
/*
* Raw (16-bit, non-paged) Qualcomm slave SCP registers, written with
* sdw_write() directly. Bus clock indication towards the codec and
* SDCA interrupt type configuration.
*/
#define WCD9378_SWRS_SCP_BASE_CLK 0x4d
#define WCD9378_SWRS_SCP_BUSCLK_SCALE_BANK0 0x62
#define WCD9378_SWRS_SCP_BUSCLK_SCALE_BANK1 0x72
#define WCD9378_SWRS_SCP_SDCA_INTRTYPE_1 0xf4
#define WCD9378_SWRS_SCP_SDCA_INTRTYPE_2 0xf8
#define WCD9378_SWRS_SCP_SDCA_INTRTYPE_3 0xfc
#define WCD9378_SWRS_SCP_HOST_CLK_DIV2_CTL(m) (0xe0 + 0x10 * (m))
#define WCD9378_SWRS_BASE_CLK_19P2MHZ 0x01
#define WCD9378_SWRS_CLK_SCALE_DIV2 0x02 /* 9.6 MHz */
#define WCD9378_SWRS_CLK_SCALE_DIV4 0x03 /* 4.8 MHz */
/* ITxx_USAGE ADC mode values */
#define WCD9378_ADC_USAGE_HIFI 0x01
#define WCD9378_ADC_USAGE_LO_HIF 0x02
#define WCD9378_ADC_USAGE_NORMAL 0x03
#define WCD9378_ADC_USAGE_LP 0x05
#define WCD9378_ADC_USAGE_OFF 0x00
/* ITxx_MICB usage values */
#define WCD9378_MICB_USAGE_OFF 0x00
#define WCD9378_MICB_USAGE_PULL_DOWN 0x01
#define WCD9378_MICB_USAGE_1P2V 0x02
#define WCD9378_MICB_USAGE_1P8V_OR_PULLUP 0x03
#define WCD9378_MICB_USAGE_2P5V 0x04
#define WCD9378_MICB_USAGE_2P75V 0x05
#define WCD9378_MICB_USAGE_2P2V 0xf0
#define WCD9378_MICB_USAGE_2P7V 0xf1
#define WCD9378_MICB_USAGE_2P8V 0xf2
#define WCD9378_MICB_USAGE_REMAP_TABLE_3 0xf3
#define WCD9378_MICB_USAGE_REMAP_TABLE_4 0xf4
#define WCD9378_MICB_USAGE_REMAP_TABLE_5 0xf5
/* PDExx_REQ_PS power states */
#define WCD9378_PDE_PS0_ON 0x00
#define WCD9378_PDE_PS3_OFF 0x03
#define WCD9378_MAX_MICBIAS 3
#define WCD9378_MAX_SWR_CH_IDS 15
#define WCD9378_SWRM_CH_MASK(ch_idx) BIT((ch_idx) - 1)
enum wcd9378_tx_sdw_ports {
WCD9378_ADC_1_PORT = 1,
WCD9378_ADC_2_PORT,
WCD9378_ADC_3_PORT,
WCD9378_DMIC_0_1_MBHC_PORT,
WCD9378_DMIC_2_5_PORT,
WCD9378_MAX_TX_SWR_PORTS = WCD9378_DMIC_2_5_PORT,
};
enum wcd9378_rx_sdw_ports {
WCD9378_HPH_PORT = 1,
WCD9378_CLSH_PORT,
WCD9378_COMP_PORT,
WCD9378_LO_PORT,
WCD9378_DSD_PORT,
WCD9378_MAX_SWR_PORTS = WCD9378_DSD_PORT,
};
enum wcd9378_tx_sdw_channels {
WCD9378_ADC1,
WCD9378_ADC2,
WCD9378_ADC3,
WCD9378_DMIC0,
WCD9378_DMIC1,
WCD9378_MBHC,
WCD9378_DMIC2,
WCD9378_DMIC3,
WCD9378_DMIC4,
WCD9378_DMIC5,
};
enum wcd9378_rx_sdw_channels {
WCD9378_HPH_L,
WCD9378_HPH_R,
WCD9378_CLSH,
WCD9378_COMP_L,
WCD9378_COMP_R,
WCD9378_LO,
WCD9378_DSD_L,
WCD9378_DSD_R,
};
struct wcd9378_priv;
struct wcd9378_sdw_priv {
struct sdw_slave *sdev;
struct sdw_stream_config sconfig;
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WCD9378_MAX_SWR_PORTS];
struct wcd_sdw_ch_info *ch_info;
bool port_enable[WCD9378_MAX_SWR_CH_IDS];
unsigned int master_channel_map[SDW_MAX_PORTS];
int active_ports;
bool is_tx;
struct wcd9378_priv *wcd9378;
struct regmap *regmap;
};
#if IS_ENABLED(CONFIG_SND_SOC_WCD9378_SDW)
int wcd9378_sdw_hw_params(struct wcd9378_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
#else
static inline int wcd9378_sdw_hw_params(struct wcd9378_sdw_priv *wcd,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return -EOPNOTSUPP;
}
#endif
#endif /* __WCD9378_H__ */

View file

@ -224,21 +224,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s
dev_err(dai->dev, "Failed to prepare Graph %d\n", rc);
goto err;
}
/*
* Start the port already at prepare, like q6afe does: this starts
* the interface clocks before the DAPM power-up sequence runs, so
* codecs that need a live BCLK at power-up (e.g. aw88261) can
* start synchronously. The trigger callback keeps its start as a
* no-op fallback via is_port_started.
*/
rc = q6apm_graph_start(dai_data->graph[dai->id]);
if (rc < 0) {
dev_err(dai->dev, "Failed to start APM port %d\n", dai->id);
goto err;
}
dai_data->is_port_started[dai->id] = true;
return 0;
err:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {

View file

@ -34,7 +34,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
switch (cpu_dai->id) {
case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX:
case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
case SENARY_MI2S_RX...SENARY_MI2S_TX:
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
break;
case WSA_CODEC_DMA_RX_0: