diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml index 42d05334bd39..2fb95544db8b 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml @@ -21,47 +21,6 @@ properties: '#sound-dai-cells': const: 1 - '#address-cells': - const: 1 - - '#size-cells': - const: 0 - -# Digital Audio Interfaces -patternProperties: - '^dai@[0-9a-f]+$': - type: object - description: - Q6DSP Digital Audio Interfaces. - - properties: - reg: - maxItems: 1 - description: - Digital Audio Interface ID - - clocks: - minItems: 1 - items: - - description: MI2S master clock - - description: MI2S bit clock - - description: MI2S external bit clock - - clock-names: - minItems: 1 - items: - - const: mclk - - const: bclk - - const: eclk - - dependencies: - clocks: [clock-names] - - required: - - reg - - additionalProperties: false - required: - compatible - '#sound-dai-cells' @@ -70,22 +29,7 @@ unevaluatedProperties: false examples: - | - #include - dais { compatible = "qcom,q6apm-lpass-dais"; #sound-dai-cells = <1>; - #address-cells = <1>; - #size-cells = <0>; - - dai@16 { - reg = ; - clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 - LPASS_CLK_ATTRIBUTE_COUPLE_NO>, - <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT - LPASS_CLK_ATTRIBUTE_COUPLE_NO>, - <&q6prmcc LPASS_CLK_ID_PRI_MI2S_EBIT - LPASS_CLK_ATTRIBUTE_COUPLE_NO>; - clock-names = "mclk", "bclk", "eclk"; - }; }; diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts index 2e90892cbc1f..7bbcb280b05f 100644 --- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts +++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts @@ -27,6 +27,34 @@ 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"; @@ -1018,17 +1046,6 @@ status = "okay"; }; -&q6apmbedai { - #address-cells = <1>; - #size-cells = <0>; - - dai@147 { - reg = ; - clocks = <&q6prmcc LPASS_CLK_ID_SEN_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; - clock-names = "bclk"; - }; -}; - &qup_uart11_cts { /* * Configure a bias-bus-hold on CTS to lower power @@ -1200,6 +1217,14 @@ 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"; diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi index 5394f51bdd31..288e0e658629 100644 --- a/arch/arm64/boot/dts/qcom/milos.dtsi +++ b/arch/arm64/boot/dts/qcom/milos.dtsi @@ -548,14 +548,6 @@ no-map; }; - adsp_rpc_heap_mem: adsp-rpc-heap { - compatible = "shared-dma-pool"; - alloc-ranges = <0x0 0x0 0x0 0xffffffff>; - reusable; - alignment = <0x0 0x400000>; - size = <0x0 0xc00000>; - }; - pvm_fw_mem: pvm-fw-region@824a0000 { reg = <0x0 0x824a0000 0x0 0x100000>; no-map; @@ -1406,9 +1398,6 @@ qcom,glink-channels = "fastrpcglink-apps-dsp"; label = "adsp"; qcom,non-secure-domain; - memory-region = <&adsp_rpc_heap_mem>; - qcom,vmids = ; #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index fb313e591e85..7cd4d751e1d2 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -198,6 +198,17 @@ 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 diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 5bd227f8c120..59ea5cf17287 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -26,6 +26,8 @@ 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 diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c new file mode 100644 index 000000000000..ceb4b98402bb --- /dev/null +++ b/drivers/iio/magnetometer/qmc6308.c @@ -0,0 +1,590 @@ +// 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 + * + * Datasheet available at + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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, ®); + 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 "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index aae337572be7..db823177e636 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -520,54 +520,6 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) qrtr_node_assign(node, le32_to_cpu(pkt->server.node)); } - /* The DSPs are star-connected through this node: forward packets - * destined to another node onto that node's endpoint (e.g. the - * modem's voice stack talking to the ADSP's audio service). Only - * DATA and RESUME_TX transit; control packets keep going to the - * local ns, which does its own mesh-wide redistribution. RESUME_TX - * additionally releases this hop's flow-control token on the - * arrival link: per-hop counters advance in lockstep with the - * end-to-end ones since every packet of the flow transits here. - */ - if (cb->dst_node != qrtr_local_nid && - cb->dst_node != QRTR_NODE_BCAST && - (cb->type == QRTR_TYPE_DATA || cb->type == QRTR_TYPE_RESUME_TX)) { - struct sockaddr_qrtr from = {AF_QIPCRTR, - cb->src_node, cb->src_port}; - struct sockaddr_qrtr to = {AF_QIPCRTR, - cb->dst_node, cb->dst_port}; - struct qrtr_node *dst; - - dst = qrtr_node_lookup(cb->dst_node); - if (!dst || dst == node) { - if (dst) - qrtr_node_release(dst); - goto err; - } - - if (cb->type == QRTR_TYPE_RESUME_TX) { - struct sk_buff *clone; - - clone = skb_clone(skb, GFP_ATOMIC); - if (clone) - qrtr_tx_resume(node, clone); - } - - pr_debug("qrtr: fwd %u:%u -> %u:%u type %d len %zu\n", - cb->src_node, cb->src_port, - cb->dst_node, cb->dst_port, cb->type, size); - - if (skb_cow_head(skb, sizeof(struct qrtr_hdr_v1))) { - qrtr_node_release(dst); - goto err; - } - - qrtr_node_enqueue(dst, skb, cb->type, &from, &to); - qrtr_node_release(dst); - - return 0; - } - if (cb->type == QRTR_TYPE_RESUME_TX) { qrtr_tx_resume(node, skb); } else { diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index d650e0b0ba5b..67267304e7e9 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-q6dsp-common-y := q6dsp-common.o q6dsp-lpass-ports.o q6dsp-lpass-clocks.o -snd-q6apm-y := q6apm.o audioreach.o topology.o q6apm-voice-proto.o +snd-q6apm-y := q6apm.o audioreach.o topology.o obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += snd-q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 461e65526506..06946add653a 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -2,12 +2,10 @@ // Copyright (c) 2021, Linaro Limited #include -#include #include #include #include #include -#include #include #include #include @@ -17,22 +15,13 @@ #include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" -#include "q6prm.h" #define AUDIOREACH_BE_PCM_BASE 16 -struct q6apm_dai_priv_data { - struct clk *mclk; - struct clk *bclk; - struct clk *eclk; - bool mclk_enabled, bclk_enabled, eclk_enabled; -}; - struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; - struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -235,6 +224,21 @@ 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) { @@ -262,73 +266,6 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } -static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - return q6apm_lpass_dai_startup(substream, dai); -} - -static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - - if (dai_data->priv[dai->id].mclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].mclk); - dai_data->priv[dai->id].mclk_enabled = false; - } - - if (dai_data->priv[dai->id].bclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].bclk); - dai_data->priv[dai->id].bclk_enabled = false; - } - - if (dai_data->priv[dai->id].eclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].eclk); - dai_data->priv[dai->id].eclk_enabled = false; - } - q6apm_lpass_dai_shutdown(substream, dai); -} - -static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - struct clk *sysclk = NULL; - bool *enabled = NULL; - int ret = 0; - - switch (clk_id) { - case LPAIF_MI2S_MCLK: - sysclk = dai_data->priv[dai->id].mclk; - enabled = &dai_data->priv[dai->id].mclk_enabled; - break; - case LPAIF_MI2S_BCLK: - sysclk = dai_data->priv[dai->id].bclk; - enabled = &dai_data->priv[dai->id].bclk_enabled; - break; - case LPAIF_MI2S_ECLK: - sysclk = dai_data->priv[dai->id].eclk; - enabled = &dai_data->priv[dai->id].eclk_enabled; - break; - default: - return -EINVAL; - } - - if (sysclk) { - if (*enabled) - return 0; - - clk_set_rate(sysclk, freq); - ret = clk_prepare_enable(sysclk); - if (ret) { - dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); - return ret; - } - - *enabled = true; - } - - return ret; -} - static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -350,12 +287,11 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6i2s_dai_startup, - .shutdown = q6i2s_lpass_dai_shutdown, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, - .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -376,73 +312,6 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; -static int of_q6apm_parse_dai_data(struct device *dev, - struct q6apm_lpass_dai_data *data) -{ - int ret; - - for_each_child_of_node_scoped(dev->of_node, node) { - struct q6apm_dai_priv_data *priv; - int id; - - ret = of_property_read_u32(node, "reg", &id); - if (ret || id < 0 || id >= APM_PORT_MAX) { - dev_err(dev, "valid dai id not found:%d\n", ret); - continue; - } - - switch (id) { - /* MI2S specific properties */ - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case SENARY_MI2S_RX ... SENARY_MI2S_TX: - priv = &data->priv[id]; - priv->mclk = of_clk_get_by_name(node, "mclk"); - if (IS_ERR(priv->mclk)) { - if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) - return dev_err_probe(dev, PTR_ERR(priv->mclk), - "unable to get mi2s mclk\n"); - priv->mclk = NULL; - } - - priv->bclk = of_clk_get_by_name(node, "bclk"); - if (IS_ERR(priv->bclk)) { - if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) { - if (priv->mclk) { - clk_put(priv->mclk); - priv->mclk = NULL; - } - return dev_err_probe(dev, PTR_ERR(priv->bclk), - "unable to get mi2s bclk\n"); - } - priv->bclk = NULL; - } - - priv->eclk = of_clk_get_by_name(node, "eclk"); - if (IS_ERR(priv->eclk)) { - if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) { - if (priv->mclk) { - clk_put(priv->mclk); - priv->mclk = NULL; - } - if (priv->bclk) { - clk_put(priv->bclk); - priv->bclk = NULL; - } - return dev_err_probe(dev, PTR_ERR(priv->eclk), - "unable to get mi2s eclk\n"); - } - priv->eclk = NULL; - } - break; - default: - break; - } - } - - return 0; -} - static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -450,16 +319,12 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; - int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); - ret = of_q6apm_parse_dai_data(dev, dai_data); - if (ret) - return ret; memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6apm-voice-proto.c b/sound/soc/qcom/qdsp6/q6apm-voice-proto.c deleted file mode 100644 index 76817a881e8c..000000000000 --- a/sound/soc/qcom/qdsp6/q6apm-voice-proto.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Voice-call bring-up prototype for the Fairphone (Gen. 6): replay - * pre-generated APM command sequences through the q6apm GPR channel. - * - * The blobs reproduce, byte for byte, the GRAPH_OPEN/SET_CFG/PREPARE/START - * sequence the stock Android PAL/AGM/GSL stack sends to set up the - * handset voice-call graphs (mined from the device ACDB; generated by - * utilities/mkvoiceblobs.py in the bring-up repo). The modem's voice - * engine and the ADSP VCPM service wire up the vocoder themselves once - * these graphs are running. - * - * Local prototype only, NOT for upstream. Interface: - * echo > /sys/kernel/debug/q6apm-voice-proto/play - * where is relative to /lib/firmware. Playback aborts on the - * first command the DSP rejects. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "audioreach.h" -#include "q6apm.h" - -#define Q6VP_MAGIC 0x50563651 /* "Q6VP" little-endian */ -#define Q6VP_VERSION 1 - -/* - * A record opcode with this bit set is sent out-of-band: the payload is - * placed in an ADSP-mapped shared-memory region and the GPR command carries - * only an apm_cmd_header referencing it. Stock GSL sends every voice - * GRAPH_OPEN this way; it is mandatory because a voice graph must be opened - * atomically as one command (all subgraphs + cross-subgraph module - * connections and control links), and the whole payload (~4.3-4.7 KB) - * exceeds the ~4 KiB in-band GPR/GLINK intent limit. Splitting the open - * per-subgraph to fit in-band makes the cross-referencing TX DevicePP - * subgraph invalid (rejected at open) — see journal/calls.md 2026-07-08. - */ -#define Q6VP_OOB_FLAG 0x80000000 -#define Q6VP_GRAPH_ID 0xf000 /* private mem-map handle owner */ -#define Q6VP_BUF_SZ 0x40000 /* 256 KiB scratch region */ -#define Q6VP_SID_MASK 0xf - -struct q6vp_hdr { - __le32 magic; - __le32 version; - __le32 num_records; -} __packed; - -struct q6vp_rec { - __le32 opcode; - __le32 size; -} __packed; - -static struct q6apm *q6vp_apm; -static struct dentry *q6vp_dir; -static struct device *q6vp_dais_dev; -static struct audioreach_graph_info *q6vp_info; -static void *q6vp_buf; -static dma_addr_t q6vp_buf_iova; -static phys_addr_t q6vp_buf_dsp_addr; - -static int q6vp_send(uint32_t opcode, const void *payload, uint32_t size) -{ - struct gpr_pkt *pkt; - int ret; - - pkt = audioreach_alloc_apm_cmd_pkt(size, opcode, 0); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - memcpy((void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE, payload, size); - - ret = q6apm_send_cmd_sync(q6vp_apm, pkt, 0); - kfree(pkt); - - return ret; -} - -/* - * Lazily set up the shared-memory region used for OOB commands: allocate a - * DMA buffer against the q6apm-dais child device (it carries the iommus - * mapping the ADSP expects, same as the PCM data path), register a private - * graph_info so the APM_CMD_RSP_SHARED_MEM_MAP_REGIONS callback has a slot - * to store the handle in, and map the region with the DSP. The mapping is - * kept until the module goes away. - */ -static int q6vp_oob_init(void) -{ - struct device *dev = q6vp_apm->dev; - struct of_phandle_args args; - struct platform_device *pdev; - struct device_node *np; - u64 sid = 0; - u32 graph_id = Q6VP_GRAPH_ID; - int ret; - - if (q6vp_info && q6vp_info->mem_map_handle) - return 0; - - if (!q6vp_dais_dev) { - np = of_get_compatible_child(dev->of_node, "qcom,q6apm-dais"); - if (!np) { - dev_err(dev, "voice-proto: no q6apm-dais node\n"); - return -ENODEV; - } - if (!of_parse_phandle_with_fixed_args(np, "iommus", 1, 0, - &args)) { - sid = args.args[0] & Q6VP_SID_MASK; - of_node_put(args.np); - } - pdev = of_find_device_by_node(np); - of_node_put(np); - if (!pdev) { - dev_err(dev, "voice-proto: no q6apm-dais device\n"); - return -ENODEV; - } - q6vp_dais_dev = &pdev->dev; - - q6vp_buf = dma_alloc_coherent(q6vp_dais_dev, Q6VP_BUF_SZ, - &q6vp_buf_iova, GFP_KERNEL); - if (!q6vp_buf) - return -ENOMEM; - q6vp_buf_dsp_addr = q6vp_buf_iova | (sid << 32); - } - - if (!q6vp_info) { - q6vp_info = kzalloc(sizeof(*q6vp_info), GFP_KERNEL); - if (!q6vp_info) - return -ENOMEM; - INIT_LIST_HEAD(&q6vp_info->sg_list); - q6vp_info->id = Q6VP_GRAPH_ID; - ret = idr_alloc_u32(&q6vp_apm->graph_info_idr, q6vp_info, - &graph_id, graph_id, GFP_KERNEL); - if (ret < 0) { - kfree(q6vp_info); - q6vp_info = NULL; - return ret; - } - } - - ret = q6apm_map_memory_fixed_region(q6vp_dais_dev, Q6VP_GRAPH_ID, - q6vp_buf_dsp_addr, Q6VP_BUF_SZ); - if (ret) { - dev_err(dev, "voice-proto: mem map failed (%d)\n", ret); - return ret; - } - - dev_info(dev, "voice-proto: OOB region mapped, handle 0x%x\n", - q6vp_info->mem_map_handle); - return 0; -} - -static int q6vp_send_oob(uint32_t opcode, const void *payload, uint32_t size) -{ - struct apm_cmd_header *cmd_header; - struct gpr_pkt *pkt; - int ret; - - if (size > Q6VP_BUF_SZ) - return -EFBIG; - - ret = q6vp_oob_init(); - if (ret) - return ret; - - memcpy(q6vp_buf, payload, size); - - pkt = audioreach_alloc_apm_cmd_pkt(0, opcode, 0); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - /* - * The region is mapped in absolute-address mode (mainline's - * q6apm_map_memory_fixed_region passes property_flag 0, unlike GSL - * which maps with IS_OFFSET_MODE and then references offset 0), so - * the header carries the full DSP-visible address of the buffer. - */ - cmd_header = (void *)pkt + GPR_HDR_SIZE; - cmd_header->payload_address_lsw = lower_32_bits(q6vp_buf_dsp_addr); - cmd_header->payload_address_msw = upper_32_bits(q6vp_buf_dsp_addr); - cmd_header->mem_map_handle = q6vp_info->mem_map_handle; - cmd_header->payload_size = size; - - ret = q6apm_send_cmd_sync(q6vp_apm, pkt, 0); - kfree(pkt); - - return ret; -} - -static int q6vp_play(const char *name) -{ - const struct firmware *fw; - const struct q6vp_hdr *hdr; - const struct q6vp_rec *rec; - struct device *dev = q6vp_apm->dev; - size_t off; - uint32_t i, num, opcode, size; - int ret, err = 0; - - ret = request_firmware(&fw, name, dev); - if (ret) { - dev_err(dev, "voice-proto: cannot load %s (%d)\n", name, ret); - return ret; - } - - hdr = (const struct q6vp_hdr *)fw->data; - if (fw->size < sizeof(*hdr) || - le32_to_cpu(hdr->magic) != Q6VP_MAGIC || - le32_to_cpu(hdr->version) != Q6VP_VERSION) { - dev_err(dev, "voice-proto: %s: bad header\n", name); - ret = -EINVAL; - goto out; - } - - num = le32_to_cpu(hdr->num_records); - off = sizeof(*hdr); - for (i = 0; i < num; i++) { - if (off + sizeof(*rec) > fw->size) { - ret = -EINVAL; - goto out; - } - rec = (const struct q6vp_rec *)(fw->data + off); - opcode = le32_to_cpu(rec->opcode); - size = le32_to_cpu(rec->size); - off += sizeof(*rec); - if (off + size > fw->size) { - ret = -EINVAL; - goto out; - } - - if (opcode & Q6VP_OOB_FLAG) - ret = q6vp_send_oob(opcode & ~Q6VP_OOB_FLAG, - fw->data + off, size); - else - ret = q6vp_send(opcode, fw->data + off, size); - dev_info(dev, "voice-proto: %s[%u] opcode 0x%08x size %u -> %d\n", - name, i, opcode & ~Q6VP_OOB_FLAG, size, ret); - /* keep going on DSP rejections; each result is logged */ - if (ret) - err = ret; - - off += ALIGN(size, 4); - } - -out: - release_firmware(fw); - return ret ? ret : err; -} - -static ssize_t q6vp_play_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - char name[128]; - int ret; - - if (!q6vp_apm) - return -ENODEV; - if (count >= sizeof(name)) - return -EINVAL; - if (copy_from_user(name, ubuf, count)) - return -EFAULT; - name[count] = '\0'; - strim(name); - - ret = q6vp_play(name); - - return ret ? ret : count; -} - -static const struct file_operations q6vp_play_fops = { - .open = simple_open, - .write = q6vp_play_write, - .llseek = default_llseek, -}; - -void q6apm_voice_proto_init(struct q6apm *apm) -{ - q6vp_apm = apm; - q6vp_dir = debugfs_create_dir("q6apm-voice-proto", NULL); - debugfs_create_file("play", 0200, q6vp_dir, NULL, &q6vp_play_fops); -} - -void q6apm_voice_proto_exit(void) -{ - debugfs_remove_recursive(q6vp_dir); - q6vp_dir = NULL; - - if (q6vp_info) { - if (q6vp_info->mem_map_handle) - q6apm_unmap_memory_fixed_region(q6vp_dais_dev, - Q6VP_GRAPH_ID); - idr_remove(&q6vp_apm->graph_info_idr, Q6VP_GRAPH_ID); - kfree(q6vp_info); - q6vp_info = NULL; - } - if (q6vp_buf) { - dma_free_coherent(q6vp_dais_dev, Q6VP_BUF_SZ, q6vp_buf, - q6vp_buf_iova); - q6vp_buf = NULL; - } - q6vp_dais_dev = NULL; - q6vp_apm = NULL; -} diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index cd7a9a1e0b9b..2ab378fb5032 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -764,8 +764,6 @@ static int apm_probe(gpr_device_t *gdev) g_apm = apm; - q6apm_voice_proto_init(apm); - q6apm_get_apm_state(apm); ret = snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); @@ -783,7 +781,6 @@ static int apm_probe(gpr_device_t *gdev) static void apm_remove(gpr_device_t *gdev) { - q6apm_voice_proto_exit(); of_platform_depopulate(&gdev->dev); snd_soc_unregister_component(&gdev->dev); } diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 736a2b53f303..376a36700c53 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -157,8 +157,4 @@ int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id); int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir); - -/* FP6 voice-call bring-up prototype (q6apm-voice-proto.c) */ -void q6apm_voice_proto_init(struct q6apm *apm); -void q6apm_voice_proto_exit(void); #endif /* __APM_GRAPH_ */ diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index bbbe6d368a41..a988a32086fe 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,10 +3,6 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ -#define LPAIF_MI2S_MCLK 1 -#define LPAIF_MI2S_BCLK 2 -#define LPAIF_MI2S_ECLK 3 - /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */ diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 9546d9233d9b..ce28bfac329a 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -12,78 +12,17 @@ #include #include #include "qdsp6/q6afe.h" -#include "qdsp6/q6apm.h" -#include "qdsp6/q6prm.h" #include "common.h" #include "sdw.h" -#define I2S_MCLKFS 256 - -#define I2S_MCLK_RATE(rate) \ - ((rate) * (I2S_MCLKFS)) -#define I2S_BIT_RATE(rate, channels, format) \ - ((rate) * (channels) * (format)) - -static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), - SND_SOC_DAPM_SPK("DP0 Jack", NULL), - SND_SOC_DAPM_SPK("DP1 Jack", NULL), - SND_SOC_DAPM_SPK("DP2 Jack", NULL), - SND_SOC_DAPM_SPK("DP3 Jack", NULL), - SND_SOC_DAPM_SPK("DP4 Jack", NULL), - SND_SOC_DAPM_SPK("DP5 Jack", NULL), - SND_SOC_DAPM_SPK("DP6 Jack", NULL), - SND_SOC_DAPM_SPK("DP7 Jack", NULL), -}; - -struct snd_soc_common { - const char *driver_name; - const struct snd_soc_dapm_widget *dapm_widgets; - int num_dapm_widgets; - const struct snd_soc_dapm_route *dapm_routes; - int num_dapm_routes; - const struct snd_kcontrol_new *controls; - int num_controls; - unsigned int codec_dai_fmt; - bool codec_sysclk_set; - bool mi2s_mclk_enable; - bool mi2s_bclk_enable; - bool wcd_jack; -}; - struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; - struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; -static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) -{ - int rate = params_rate(params); - - switch (rate) { - case 11025: - case 44100: - case 88200: - return I2S_MCLK_RATE(44100); - default: - break; - } - - return I2S_MCLK_RATE(rate); -} - -static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) -{ - return I2S_BIT_RATE(params_rate(params), - params_channels(params), - snd_pcm_format_width(params_format(params))); -} - static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -93,6 +32,11 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; 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: case WSA_CODEC_DMA_RX_1: /* @@ -121,10 +65,7 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - if (data->snd_soc_common_priv->wcd_jack) - return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); - - return 0; + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -156,63 +97,6 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); - int mclk_freq = sc8280xp_get_mclk_freq(params); - int bclk_freq = sc8280xp_get_bclk_freq(params); - int ret; - - 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: - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - if (ret && ret != -ENOTSUPP) - return ret; - - if (data->snd_soc_common_priv->codec_dai_fmt) { - ret = snd_soc_dai_set_fmt(codec_dai, - data->snd_soc_common_priv->codec_dai_fmt); - if (ret && ret != -ENOTSUPP) - return ret; - } - - if (data->snd_soc_common_priv->mi2s_mclk_enable) { - ret = snd_soc_dai_set_sysclk(cpu_dai, - LPAIF_MI2S_MCLK, mclk_freq, - SND_SOC_CLOCK_OUT); - if (ret) - return ret; - } - - if (data->snd_soc_common_priv->mi2s_bclk_enable) { - ret = snd_soc_dai_set_sysclk(cpu_dai, - LPAIF_MI2S_BCLK, bclk_freq, - SND_SOC_CLOCK_OUT); - if (ret) - return ret; - } - - if (data->snd_soc_common_priv->codec_sysclk_set) { - ret = snd_soc_dai_set_sysclk(codec_dai, - 0, mclk_freq, - SND_SOC_CLOCK_IN); - if (ret) - return ret; - } - break; - default: - break; - } - - return 0; -} - static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -234,7 +118,6 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sc8280xp_be_ops = { .startup = qcom_snd_sdw_startup, .shutdown = qcom_snd_sdw_shutdown, - .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, }; @@ -245,7 +128,7 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card) int i; for_each_card_prelinks(card, i, link) { - if (link->no_pcm == 1 || link->num_codecs > 0) { + if (link->no_pcm == 1) { link->init = sc8280xp_snd_init; link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; link->ops = &sc8280xp_be_ops; @@ -263,133 +146,38 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - + card->owner = THIS_MODULE; /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); - if (!data->snd_soc_common_priv) - return -ENODEV; - - card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); - card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; - card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; - card->dapm_routes = data->snd_soc_common_priv->dapm_routes; - card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; - card->controls = data->snd_soc_common_priv->controls; - card->num_controls = data->snd_soc_common_priv->num_controls; - ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = data->snd_soc_common_priv->driver_name; + card->driver_name = of_device_get_match_data(dev); sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } -static struct snd_soc_common kaanapali_priv_data = { - .driver_name = "kaanapali", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common milos_priv_data = { - .driver_name = "milos", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .mi2s_bclk_enable = true, - .wcd_jack = true, -}; - -static struct snd_soc_common qcs9100_priv_data = { - .driver_name = "sa8775p", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -}; - -static struct snd_soc_common qcs615_priv_data = { - .driver_name = "qcs615", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .mi2s_mclk_enable = true, -}; - -static struct snd_soc_common qcm6490_priv_data = { - .driver_name = "qcm6490", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common qcs6490_priv_data = { - .driver_name = "qcs6490", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common qcs8275_priv_data = { - .driver_name = "qcs8300", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -}; - -static struct snd_soc_common sc8280xp_priv_data = { - .driver_name = "sc8280xp", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common sm8450_priv_data = { - .driver_name = "sm8450", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common sm8550_priv_data = { - .driver_name = "sm8550", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common sm8650_priv_data = { - .driver_name = "sm8650", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - -static struct snd_soc_common sm8750_priv_data = { - .driver_name = "sm8750", - .dapm_widgets = sc8280xp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), - .wcd_jack = true, -}; - static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data}, - {.compatible = "qcom,milos-sndcard", .data = &milos_priv_data}, - {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data}, - {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data}, - {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data}, - {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data}, - {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data}, - {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data}, - {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data}, - {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data}, - {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data}, - {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data}, + {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, + {.compatible = "qcom,milos-sndcard", "milos"}, + {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, + {.compatible = "qcom,qcs615-sndcard", "qcs615"}, + {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, + {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, + {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, + {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, + {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, + {.compatible = "qcom,sm8450-sndcard", "sm8450"}, + {.compatible = "qcom,sm8550-sndcard", "sm8550"}, + {.compatible = "qcom,sm8650-sndcard", "sm8650"}, + {.compatible = "qcom,sm8750-sndcard", "sm8750"}, {} };