diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts index 7bbcb280b05f..13fbc66e2394 100644 --- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts +++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts @@ -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"; @@ -265,6 +237,7 @@ }; }; + /* i2s-dai-link { link-name = "Senary MI2S Playback"; @@ -280,6 +253,7 @@ sound-dai = <&q6apm>; }; }; + */ }; wcd9378: audio-codec { @@ -770,28 +744,9 @@ }; &i2c1 { + /* Samsung NFC @ 0x27 */ + status = "okay"; - - /* - * Samsung S3NRN4V NFC controller. XI is driven by the RF_CLK2 PMIC - * buffer; the chip has no oscillator of its own and gates the clock - * via its CLK_REQ line, so the clock must be voted on in response - * to it. - */ - nfc@27 { - compatible = "samsung,s3nrn4v"; - reg = <0x27>; - - clk-req-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; - clocks = <&rpmhcc RPMH_RF_CLK2>; - en-gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; - interrupt-parent = <&tlmm>; - interrupts = <31 IRQ_TYPE_EDGE_RISING>; - pinctrl-0 = <&nfc_clk_req_default>, <&nfc_irq_default>, - <&nfc_pd_default>; - pinctrl-names = "default"; - wake-gpios = <&tlmm 7 GPIO_ACTIVE_HIGH>; - }; }; &i2c3 { @@ -1210,21 +1165,6 @@ <13 1>, /* NC */ <63 2>; /* WLAN UART */ - nfc_clk_req_default: nfc-clk-req-default-state { - pins = "gpio6"; - function = "gpio"; - drive-strength = <2>; - 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"; @@ -1239,13 +1179,6 @@ bias-disable; }; - nfc_irq_default: nfc-irq-default-state { - pins = "gpio31"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - qup_uart11_sleep_cts: qup-uart11-sleep-cts-state { pins = "gpio48"; function = "gpio"; @@ -1313,17 +1246,6 @@ bias-pull-down; }; - /* - * Pulled up so the NFC chip stays powered down while the line is - * not driven (it is the chip's active-high power-down input). - */ - nfc_pd_default: nfc-pd-default-state { - pins = "gpio56"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - sdc2_card_det_n: sdc2-card-det-state { pins = "gpio65"; function = "gpio"; diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 7cd4d751e1d2..fb313e591e85 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -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 diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 59ea5cf17287..5bd227f8c120 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -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 diff --git a/drivers/iio/magnetometer/qmc6308.c b/drivers/iio/magnetometer/qmc6308.c deleted file mode 100644 index ceb4b98402bb..000000000000 --- a/drivers/iio/magnetometer/qmc6308.c +++ /dev/null @@ -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 - * - * 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/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index 59317eaad7ac..af0fa8bd970b 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -122,47 +122,11 @@ static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb) return 0; } -static int s3fwrn5_nci_setup(struct nci_dev *ndev) -{ - struct s3fwrn5_info *info = nci_get_drvdata(ndev); - - /* - * Runs after CORE_RESET, before CORE_INIT. The S3NRN4V needs its - * reference clock configured here (the downstream stack does it in the - * bootloader, before CORE_RESET, but this is the earliest hook the NCI - * core offers and the chip accepts it). - */ - if (info->variant == S3FWRN5_VARIANT_S3NRN4V) - return s3fwrn5_nci_clk_cfg(info); - - return 0; -} - static int s3fwrn5_nci_post_setup(struct nci_dev *ndev) { struct s3fwrn5_info *info = nci_get_drvdata(ndev); int ret; - if (info->variant == S3FWRN5_VARIANT_S3NRN4V) { - /* - * The S3NRN4V ships with working firmware behind a bootloader - * protocol this driver does not implement, so there is no - * download step; the NCI core has already done CORE_RESET + - * CORE_INIT. Just (re)load the RF registers via DUAL_OPTION. - */ - ret = s3fwrn5_nci_rf_configure_dual(info, "sec_s3nrn4v_hwreg.bin", - "sec_s3nrn4v_swreg.bin"); - /* - * Keep going even if the blobs could not be loaded: the chip - * still enumerates and falls back to the RF registers programmed - * in its flash, so NFC may work anyway. - */ - if (ret < 0) - dev_warn(&ndev->nfc_dev->dev, - "rfreg configure failed (%d)\n", ret); - return 0; - } - if (s3fwrn5_firmware_init(info)) { //skip bootloader mode return 0; @@ -188,14 +152,13 @@ static const struct nci_ops s3fwrn5_nci_ops = { .open = s3fwrn5_nci_open, .close = s3fwrn5_nci_close, .send = s3fwrn5_nci_send, - .setup = s3fwrn5_nci_setup, .post_setup = s3fwrn5_nci_post_setup, .prop_ops = s3fwrn5_nci_prop_ops, .n_prop_ops = ARRAY_SIZE(s3fwrn5_nci_prop_ops), }; int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - const struct s3fwrn5_phy_ops *phy_ops, enum s3fwrn5_variant variant) + const struct s3fwrn5_phy_ops *phy_ops) { struct s3fwrn5_info *info; int ret; @@ -207,7 +170,6 @@ int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, info->phy_id = phy_id; info->pdev = pdev; info->phy_ops = phy_ops; - info->variant = variant; mutex_init(&info->mutex); s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD); diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index 7d20e737e402..91b8d1445efd 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -23,76 +23,9 @@ struct s3fwrn5_i2c_phy { struct i2c_client *i2c_dev; struct clk *clk; - /* - * Optional hardware clock-request handshake. When a CLK_REQ GPIO is - * wired, the chip drives it high while it needs its XI clock -- notably - * to generate the poll/reader carrier -- and the clock is gated on it - * instead of being left always-on (which never lets the chip's TX PLL - * lock on a fresh clock start, leaving it unable to poll). - */ - struct gpio_desc *gpio_clk_req; - bool clk_on; - struct mutex clk_lock; /* serialises clk_on against the CLK_REQ irq */ - unsigned int irq_skip:1; }; -static void s3fwrn5_i2c_clk_set_locked(struct s3fwrn5_i2c_phy *phy, bool on) -{ - lockdep_assert_held(&phy->clk_lock); - - if (on && !phy->clk_on) { - int ret = clk_prepare_enable(phy->clk); - - if (ret == 0) - phy->clk_on = true; - else - dev_warn_once(&phy->i2c_dev->dev, - "failed to enable clock (%d); NFC may not poll\n", - ret); - } else if (!on && phy->clk_on) { - clk_disable_unprepare(phy->clk); - phy->clk_on = false; - } -} - -/* - * Apply the current CLK_REQ level. Reading the GPIO under clk_lock makes - * concurrent callers (the CLK_REQ irq thread and the probe-time seeding) - * safe: whoever runs last applies a level read after the earlier update, - * never a stale one. - */ -static void s3fwrn5_i2c_clk_sync(struct s3fwrn5_i2c_phy *phy) -{ - int level; - - mutex_lock(&phy->clk_lock); - level = gpiod_get_value_cansleep(phy->gpio_clk_req); - if (level >= 0) - s3fwrn5_i2c_clk_set_locked(phy, level > 0); - else - dev_warn_once(&phy->i2c_dev->dev, - "failed to read CLK_REQ (%d); keeping clock state\n", - level); - mutex_unlock(&phy->clk_lock); -} - -static void s3fwrn5_i2c_clk_disable_action(void *data) -{ - struct s3fwrn5_i2c_phy *phy = data; - - mutex_lock(&phy->clk_lock); - s3fwrn5_i2c_clk_set_locked(phy, false); - mutex_unlock(&phy->clk_lock); -} - -static irqreturn_t s3fwrn5_i2c_clk_req_thread(int irq, void *phy_id) -{ - s3fwrn5_i2c_clk_sync(phy_id); - - return IRQ_HANDLED; -} - static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode) { struct s3fwrn5_i2c_phy *phy = phy_id; @@ -213,7 +146,6 @@ out: static int s3fwrn5_i2c_probe(struct i2c_client *client) { - enum s3fwrn5_variant variant; struct s3fwrn5_i2c_phy *phy; int ret; @@ -240,61 +172,15 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client) * S3FWRN5 depends on a clock input ("XI" pin) to function properly. * Depending on the hardware configuration this could be an always-on * oscillator or some external clock that must be explicitly enabled. - * - * If a CLK_REQ GPIO is wired, the chip gates the clock itself (driving - * CLK_REQ high when it needs XI); service that handshake. Otherwise just - * make sure the clock is running before starting S3FWRN5. + * Make sure the clock is running before starting S3FWRN5. */ - mutex_init(&phy->clk_lock); - phy->gpio_clk_req = devm_gpiod_get_optional(&client->dev, "clk-req", - GPIOD_IN); - if (IS_ERR(phy->gpio_clk_req)) - return PTR_ERR(phy->gpio_clk_req); + phy->clk = devm_clk_get_optional_enabled(&client->dev, NULL); + if (IS_ERR(phy->clk)) + return dev_err_probe(&client->dev, PTR_ERR(phy->clk), + "failed to get clock\n"); - if (phy->gpio_clk_req) { - int clk_req_irq; - - phy->clk = devm_clk_get_optional(&client->dev, NULL); - if (IS_ERR(phy->clk)) - return dev_err_probe(&client->dev, PTR_ERR(phy->clk), - "failed to get clock\n"); - - /* - * Unlike the always-on branch below, this clock is enabled by - * hand from the CLK_REQ handler, so devm will not disable it on - * unbind. Gate it off explicitly if it is still on at teardown. - */ - ret = devm_add_action_or_reset(&client->dev, - s3fwrn5_i2c_clk_disable_action, - phy); - if (ret) - return ret; - - clk_req_irq = gpiod_to_irq(phy->gpio_clk_req); - if (clk_req_irq < 0) - return clk_req_irq; - - ret = devm_request_threaded_irq(&client->dev, clk_req_irq, NULL, - s3fwrn5_i2c_clk_req_thread, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "s3fwrn5_clk_req", phy); - if (ret) - return ret; - - /* Seed the clock state from the current CLK_REQ level. */ - s3fwrn5_i2c_clk_sync(phy); - } else { - phy->clk = devm_clk_get_optional_enabled(&client->dev, NULL); - if (IS_ERR(phy->clk)) - return dev_err_probe(&client->dev, PTR_ERR(phy->clk), - "failed to get clock\n"); - } - - variant = (uintptr_t)i2c_get_match_data(client); ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->i2c_dev->dev, - &i2c_phy_ops, variant); + &i2c_phy_ops); if (ret < 0) return ret; @@ -319,17 +205,13 @@ static void s3fwrn5_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id s3fwrn5_i2c_id_table[] = { - { .name = S3FWRN5_I2C_DRIVER_NAME, .driver_data = S3FWRN5_VARIANT_FWDL }, - { .name = "s3nrn4v", .driver_data = S3FWRN5_VARIANT_S3NRN4V }, - { } + { S3FWRN5_I2C_DRIVER_NAME }, + {} }; MODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table); -static const struct of_device_id of_s3fwrn5_i2c_match[] = { - { .compatible = "samsung,s3fwrn5-i2c", - .data = (void *)S3FWRN5_VARIANT_FWDL, }, - { .compatible = "samsung,s3nrn4v", - .data = (void *)S3FWRN5_VARIANT_S3NRN4V, }, +static const struct of_device_id of_s3fwrn5_i2c_match[] __maybe_unused = { + { .compatible = "samsung,s3fwrn5-i2c", }, {} }; MODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match); @@ -337,7 +219,7 @@ MODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match); static struct i2c_driver s3fwrn5_i2c_driver = { .driver = { .name = S3FWRN5_I2C_DRIVER_NAME, - .of_match_table = of_s3fwrn5_i2c_match, + .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match), }, .probe = s3fwrn5_i2c_probe, .remove = s3fwrn5_i2c_remove, diff --git a/drivers/nfc/s3fwrn5/nci.c b/drivers/nfc/s3fwrn5/nci.c index 7034fb810e18..5a9de11bbece 100644 --- a/drivers/nfc/s3fwrn5/nci.c +++ b/drivers/nfc/s3fwrn5/nci.c @@ -8,9 +8,6 @@ #include #include -#include -#include -#include #include "s3fwrn5.h" #include "nci.h" @@ -23,7 +20,7 @@ static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb) return 0; } -const struct nci_driver_ops s3fwrn5_nci_prop_ops[5] = { +const struct nci_driver_ops s3fwrn5_nci_prop_ops[4] = { { .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, NCI_PROP_SET_RFREG), @@ -44,11 +41,6 @@ const struct nci_driver_ops s3fwrn5_nci_prop_ops[5] = { NCI_PROP_FW_CFG), .rsp = s3fwrn5_nci_prop_rsp, }, - { - .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, - NCI_PROP_DUAL_OPTION), - .rsp = s3fwrn5_nci_prop_rsp, - }, }; #define S3FWRN5_RFREG_SECTION_SIZE 252 @@ -125,112 +117,3 @@ out: release_firmware(fw); return ret; } - -/* - * Configure the reference clock. The S3NRN4V expects the single-byte FW_CFG - * form (just the clock-speed selector). The downstream stack sends this in the - * bootloader before CORE_RESET; the earliest the mainline NCI core lets us in - * is the ->setup hook (after CORE_RESET, before CORE_INIT), which works. - */ -int s3fwrn5_nci_clk_cfg(struct s3fwrn5_info *info) -{ - u8 clk_speed = NCI_PROP_FW_CFG_CLK_SPEED; - - return nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 1, &clk_speed); -} - -/* - * S3NRN4V RF register update. The HW and SW register blobs are merged into a - * single stream (HW first) and pushed via the DUAL_OPTION command: - * START_UPDATE, one SET_OPTION per 252-byte section, then STOP_UPDATE carrying - * a 16-bit checksum (running sum of the merged stream as 32-bit words). - */ -int s3fwrn5_nci_rf_configure_dual(struct s3fwrn5_info *info, - const char *hw_name, const char *sw_name) -{ - const struct firmware *hw_fw = NULL, *sw_fw = NULL; - struct nci_prop_dual_set_option_cmd set_option; - struct device *dev = &info->ndev->nfc_dev->dev; - size_t merged_size, i, len; - u8 *merged = NULL; - u8 stop_cmd[3]; - u32 checksum; - u8 sub_oid; - int ret; - - ret = request_firmware(&hw_fw, hw_name, dev); - if (ret < 0) - return ret; - ret = request_firmware(&sw_fw, sw_name, dev); - if (ret < 0) - goto out_hw; - - merged_size = hw_fw->size + sw_fw->size; - - /* - * The stream is checksummed as 32-bit words and pushed in at most 256 - * sections (the section index is a single byte); reject blobs that - * would silently break either. - */ - if (merged_size % 4 || - merged_size > 256 * NCI_PROP_DUAL_SECTION_SIZE) { - dev_err(dev, "invalid rfreg blob size (%zu)\n", merged_size); - ret = -EINVAL; - goto out; - } - - merged = kmalloc(merged_size, GFP_KERNEL); - if (!merged) { - ret = -ENOMEM; - goto out; - } - memcpy(merged, hw_fw->data, hw_fw->size); - memcpy(merged + hw_fw->size, sw_fw->data, sw_fw->size); - - /* Running sum of the merged stream as little-endian 32-bit words. */ - checksum = 0; - for (i = 0; i + 4 <= merged_size; i += 4) - checksum += get_unaligned_le32(merged + i); - - dev_dbg(dev, "rfreg dual-option update: %s + %s\n", hw_name, sw_name); - - /* START_UPDATE */ - sub_oid = NCI_PROP_DUAL_SUB_START_UPDATE; - ret = nci_prop_cmd(info->ndev, NCI_PROP_DUAL_OPTION, 1, &sub_oid); - if (ret < 0) { - dev_err(dev, "Unable to start rfreg update\n"); - goto out; - } - - /* SET_OPTION per section */ - set_option.sub_oid = NCI_PROP_DUAL_SUB_SET_OPTION; - set_option.index = 0; - for (i = 0; i < merged_size; i += NCI_PROP_DUAL_SECTION_SIZE) { - len = min_t(size_t, merged_size - i, NCI_PROP_DUAL_SECTION_SIZE); - memcpy(set_option.data, merged + i, len); - ret = nci_prop_cmd(info->ndev, NCI_PROP_DUAL_OPTION, - len + 2, (__u8 *)&set_option); - if (ret < 0) { - dev_err(dev, "rfreg update error (code=%d)\n", ret); - goto out; - } - set_option.index++; - } - - /* STOP_UPDATE with checksum */ - stop_cmd[0] = NCI_PROP_DUAL_SUB_STOP_UPDATE; - put_unaligned_le16(checksum, &stop_cmd[1]); - ret = nci_prop_cmd(info->ndev, NCI_PROP_DUAL_OPTION, 3, stop_cmd); - if (ret < 0) { - dev_err(dev, "Unable to stop rfreg update\n"); - goto out; - } - - dev_dbg(dev, "rfreg dual-option update: success\n"); -out: - kfree(merged); - release_firmware(sw_fw); -out_hw: - release_firmware(hw_fw); - return ret; -} diff --git a/drivers/nfc/s3fwrn5/nci.h b/drivers/nfc/s3fwrn5/nci.h index 23179ba095a1..bc4bce2bbc4d 100644 --- a/drivers/nfc/s3fwrn5/nci.h +++ b/drivers/nfc/s3fwrn5/nci.h @@ -40,13 +40,6 @@ struct nci_prop_stop_rfreg_rsp { #define NCI_PROP_FW_CFG 0x28 -/* - * Single-byte FW_CFG payload (clock-speed selector) for the S3NRN4V reference - * clock. Taken from the vendor configuration for this part (the encoding is - * not documented). - */ -#define NCI_PROP_FW_CFG_CLK_SPEED 0x11 - struct nci_prop_fw_cfg_cmd { __u8 clk_type; __u8 clk_speed; @@ -57,30 +50,7 @@ struct nci_prop_fw_cfg_rsp { __u8 status; }; -/* - * The S3NRN4V updates its RF registers through a single "dual option" command - * (a sub-OID selects the operation) instead of the START/SET/STOP_RFREG - * opcodes above, and expects the HW and SW register blobs merged into one - * stream. - */ -#define NCI_PROP_DUAL_OPTION 0x2a - -#define NCI_PROP_DUAL_SUB_START_UPDATE 0x01 -#define NCI_PROP_DUAL_SUB_SET_OPTION 0x02 -#define NCI_PROP_DUAL_SUB_STOP_UPDATE 0x03 - -#define NCI_PROP_DUAL_SECTION_SIZE 252 - -struct nci_prop_dual_set_option_cmd { - __u8 sub_oid; /* NCI_PROP_DUAL_SUB_SET_OPTION */ - __u8 index; - __u8 data[NCI_PROP_DUAL_SECTION_SIZE]; -}; - -extern const struct nci_driver_ops s3fwrn5_nci_prop_ops[5]; +extern const struct nci_driver_ops s3fwrn5_nci_prop_ops[4]; int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name); -int s3fwrn5_nci_rf_configure_dual(struct s3fwrn5_info *info, - const char *hw_name, const char *sw_name); -int s3fwrn5_nci_clk_cfg(struct s3fwrn5_info *info); #endif /* __LOCAL_S3FWRN5_NCI_H_ */ diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h index 2d8c12091fba..2b492236090b 100644 --- a/drivers/nfc/s3fwrn5/s3fwrn5.h +++ b/drivers/nfc/s3fwrn5/s3fwrn5.h @@ -21,17 +21,6 @@ enum s3fwrn5_mode { S3FWRN5_MODE_FW, }; -enum s3fwrn5_variant { - /* S3FWRN5 / S3FWRN82: firmware is downloaded by this driver */ - S3FWRN5_VARIANT_FWDL, - /* - * S3NRN4V: ships with working firmware behind a bootloader protocol - * this driver does not implement; skip the download, configure the - * clock (FW_CFG) and update the RF registers via the DUAL_OPTION cmd. - */ - S3FWRN5_VARIANT_S3NRN4V, -}; - struct s3fwrn5_phy_ops { void (*set_wake)(void *id, bool sleep); void (*set_mode)(void *id, enum s3fwrn5_mode); @@ -47,7 +36,6 @@ struct s3fwrn5_info { const struct s3fwrn5_phy_ops *phy_ops; struct s3fwrn5_fw_info fw_info; - enum s3fwrn5_variant variant; struct mutex mutex; }; @@ -90,7 +78,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb) } int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - const struct s3fwrn5_phy_ops *phy_ops, enum s3fwrn5_variant variant); + const struct s3fwrn5_phy_ops *phy_ops); void s3fwrn5_remove(struct nci_dev *ndev); int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb, diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 47172d739a41..540a4ddb0b05 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -137,7 +137,7 @@ static int s3fwrn82_uart_probe(struct serdev_device *serdev) } ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev, - &uart_phy_ops, S3FWRN5_VARIANT_FWDL); + &uart_phy_ops); if (ret < 0) goto err_serdev; diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index dbdf51188cf5..a6805d5405cd 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -10,12 +10,9 @@ #include #include -#include #include #include #include -#include -#include #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, ®_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, ®_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); diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index f16f9f8c40a2..1fee589608d6 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -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_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; }; diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 06946add653a..006b283484d9 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -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) { diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index ce28bfac329a..a36666999ac1 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -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: