[PATCH v2] ASoC: rt721-sdca: Add RT721 SDCA driver
This is the initial codec driver for rt721-sdca.
Signed-off-by: Jack Yu jack.yu@realtek.com --- sound/soc/codecs/Kconfig | 7 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt721-sdca-sdw.c | 550 +++++++++ sound/soc/codecs/rt721-sdca-sdw.h | 150 +++ sound/soc/codecs/rt721-sdca.c | 1790 +++++++++++++++++++++++++++++ sound/soc/codecs/rt721-sdca.h | 277 +++++ 6 files changed, 2776 insertions(+) create mode 100644 sound/soc/codecs/rt721-sdca-sdw.c create mode 100644 sound/soc/codecs/rt721-sdca-sdw.h create mode 100644 sound/soc/codecs/rt721-sdca.c create mode 100644 sound/soc/codecs/rt721-sdca.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7092842480ef..74c14da4ae09 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -222,6 +222,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT712_SDCA_DMIC_SDW imply SND_SOC_RT715_SDW imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT721_SDCA_SDW imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW @@ -1743,6 +1744,12 @@ config SND_SOC_RT712_SDCA_DMIC_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ
+config SND_SOC_RT721_SDCA_SDW + tristate "Realtek RT721 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT722_SDCA_SDW tristate "Realtek RT722 SDCA Codec - SDW" depends on SOUNDWIRE diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 54cbc3feae32..3477c6352acc 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -259,6 +259,7 @@ snd-soc-rt712-sdca-y := rt712-sdca.o rt712-sdca-sdw.o snd-soc-rt712-sdca-dmic-y := rt712-sdca-dmic.o snd-soc-rt715-y := rt715.o rt715-sdw.o snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-y := rt9120.o snd-soc-rtq9128-y := rtq9128.o @@ -662,6 +663,7 @@ obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c new file mode 100644 index 000000000000..3a8dbbc01152 --- /dev/null +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt721-sdca-sdw.c -- rt721 SDCA ALSA SoC audio driver +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> + +#include "rt721-sdca.h" +#include "rt721-sdca-sdw.h" + +static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f0a: + case 0x2f35: + case 0x2f50: + case 0x2f51: + case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV, + RT721_SDCA_CTL_XUV, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, + RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01: + case 0x2f51: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV, + RT721_SDCA_CTL_XUV, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, + RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0900007: + case 0x0a00005: + case 0x0c00005: + case 0x0d00014: + case 0x0310100: + case 0x2000001: + case 0x2000002: + case 0x2000003: + case 0x2000013: + case 0x200003c: + case 0x2000046: + case 0x5810000: + case 0x5810036: + case 0x5810037: + case 0x5810038: + case 0x5810039: + case 0x5b10018: + case 0x5b10019: + case 0x5f00045: + case 0x5f00048: + case 0x6100000: + case 0x6100005: + case 0x6100006: + case 0x610000d: + case 0x6100010: + case 0x6100011: + case 0x6100013: + case 0x6100015: + case 0x6100017: + case 0x6100025: + case 0x6100029: + case 0x610002c ... 0x610002f: + case 0x6100053 ... 0x6100055: + case 0x6100057: + case 0x610005a: + case 0x610005b: + case 0x610006a: + case 0x610006d: + case 0x6100092: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, + RT721_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, + RT721_SDCA_CTL_FU_CH_GAIN, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_04): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R): + return true; + default: + return false; + } +} + +static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0310100: + case 0x0a00005: + case 0x0c00005: + case 0x0d00014: + case 0x2000000: + case 0x200000d: + case 0x2000019: + case 0x2000020: + case 0x2000030: + case 0x2000046: + case 0x2000067: + case 0x2000084: + case 0x2000086: + case 0x5810000: + case 0x5810036: + case 0x5810037: + case 0x5810038: + case 0x5810039: + case 0x5b10018: + case 0x5b10019: + return true; + default: + return false; + } +} + +static const struct regmap_config rt721_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt721_sdca_readable_register, + .volatile_reg = rt721_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt721_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt721_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt721_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt721_sdca_mbq_readable_register, + .volatile_reg = rt721_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt721_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt721_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt721_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt721->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt721->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt721_sdca_jack_init set. + */ + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt721->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt721_sdca_io_init(&slave->dev, slave); +} + +static int rt721_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + sdw_slave_read_prop(slave); + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* + * port = 1 for headphone playback + * port = 2 for headset-mic capture + * port = 3 for speaker playback + * port = 6 for digital-mic capture + */ + prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + /* Three data lanes are supported by rt721-sdca codec */ + prop->lane_control_support = true; + + return 0; +} + +static int rt721_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + if (cancel_delayed_work_sync(&rt721->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt721->scp_sdca_stat2) + scp_sdca_stat2 = rt721->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt721->disable_irq_lock); + + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + + rt721->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + + rt721->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt721->scp_sdca_stat2 |= scp_sdca_stat2; + do { + /* clear flag */ + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0); + if (ret < 0) + goto io_error; + } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) { + ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt721->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt721->scp_sdca_stat1, rt721->scp_sdca_stat2); + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1); + ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2); + + if (status->sdca_cascade && !rt721->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt721->jack_detect_work, msecs_to_jiffies(280)); + + mutex_unlock(&rt721->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt721->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static const struct sdw_slave_ops rt721_sdca_slave_ops = { + .read_prop = rt721_sdca_read_prop, + .interrupt_callback = rt721_sdca_interrupt_callback, + .update_status = rt721_sdca_update_status, +}; + +static int rt721_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt721_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt721_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt721_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev); + + if (rt721->hw_init) { + cancel_delayed_work_sync(&rt721->jack_detect_work); + cancel_delayed_work_sync(&rt721->jack_btn_check_work); + } + + if (rt721->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt721->calibrate_mutex); + mutex_destroy(&rt721->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt721_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt721_sdca_id); + +static int __maybe_unused rt721_sdca_dev_suspend(struct device *dev) +{ + struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); + + if (!rt721->hw_init) + return 0; + + cancel_delayed_work_sync(&rt721->jack_detect_work); + cancel_delayed_work_sync(&rt721->jack_btn_check_work); + + regcache_cache_only(rt721->regmap, true); + regcache_cache_only(rt721->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt721_sdca_dev_system_suspend(struct device *dev) +{ + struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt721_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt721_sdca->disable_irq_lock); + rt721_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt721_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt721_sdca_dev_suspend(dev); +} + +#define RT721_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt721_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt721->first_hw_init) + return 0; + + if (!slave->unattach_request) { + mutex_lock(&rt721->disable_irq_lock); + if (rt721->disable_irq == true) { + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + rt721->disable_irq = false; + } + mutex_unlock(&rt721->disable_irq_lock); + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT721_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt721->regmap, false); + regcache_sync(rt721->regmap); + regcache_cache_only(rt721->mbq_regmap, false); + regcache_sync(rt721->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt721_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt721_sdca_sdw_driver = { + .driver = { + .name = "rt721-sdca", + .owner = THIS_MODULE, + .pm = &rt721_sdca_pm, + }, + .probe = rt721_sdca_sdw_probe, + .remove = rt721_sdca_sdw_remove, + .ops = &rt721_sdca_slave_ops, + .id_table = rt721_sdca_id, +}; +module_sdw_driver(rt721_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu jack.yu@realtek.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h new file mode 100644 index 000000000000..214b31b82583 --- /dev/null +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt721-sdca-sdw.h -- RT721 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + */ + +#ifndef __RT721_SDW_H__ +#define __RT721_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt721_sdca_reg_defaults[] = { + { 0x202d, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x08 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0e }, + { 0x2f06, 0x01 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f35, 0x00 }, + { 0x2f50, 0xf0 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x2f5a, 0x00 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x27 }, + { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, +}; + +static const struct reg_default rt721_sdca_mbq_defaults[] = { + { 0x0900007, 0xc004 }, + { 0x2000001, 0x0000 }, + { 0x2000002, 0x0000 }, + { 0x2000003, 0x0000 }, + { 0x2000013, 0x8001 }, + { 0x200003c, 0x0000 }, + { 0x2000046, 0x3400 }, + { 0x5f00044, 0x6040 }, + { 0x5f00045, 0x3333 }, + { 0x5f00048, 0x0000 }, + { 0x6100005, 0x0005 }, + { 0x6100006, 0x0000 }, + { 0x610000d, 0x0051 }, + { 0x6100010, 0x0180 }, + { 0x6100011, 0x0000 }, + { 0x6100013, 0x0000 }, + { 0x6100015, 0x0000 }, + { 0x6100017, 0x8049 }, + { 0x6100025, 0x1000 }, + { 0x6100029, 0x0809 }, + { 0x610002c, 0x2828 }, + { 0x610002d, 0x2929 }, + { 0x610002e, 0x3529 }, + { 0x610002f, 0x2901 }, + { 0x6100053, 0x2630 }, + { 0x6100054, 0x2a2a }, + { 0x6100055, 0x152f }, + { 0x6100057, 0x2200 }, + { 0x610005a, 0x2a4b }, + { 0x610005b, 0x2a00 }, + { 0x610006a, 0x0102 }, + { 0x610006d, 0x0102 }, + { 0x6100092, 0x4f61 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN, + CH_L), 0xfe00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN, + CH_R), 0xfe00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_01), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_02), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_03), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_04), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R), + 0x0000 }, +}; + +#endif /* __RT721_SDW_H__ */ diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c new file mode 100644 index 000000000000..cca749ebbd31 --- /dev/null +++ b/sound/soc/codecs/rt721-sdca.c @@ -0,0 +1,1790 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt721-sdca.c -- rt721 SDCA ALSA SoC audio driver +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <sound/pcm.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/slab.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "rt721-sdca.h" + +static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721) +{ + /* Power down group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8000); + /* VREF_HV_EN_AUTO_FAST */ + rt721_sdca_index_write(rt721, RT721_ANA_POW_PART, + RT721_VREF1_HV_CTRL1, 0xe000); + /* Power up group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8007); + /* Set AD07/08 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL9, 0x2a2a); + /* Set AD10 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL10, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL6, 0x2a2a); + /* Set DMIC1/DMIC2 IT entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL5, 0x2626); + /* Set AD10 FU entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL8, 0x1e00); + /* Set DMIC1/DMIC2 FU input gain floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL7, 0x1515); + /* Set DMIC2 FU input gain channel floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_CH_FLOAT_CTL3, 0x0304); + /* Set AD10 FU channel floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_CH_FLOAT_CTL4, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_CTL1, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Set usd_flag_sel, usd_in_sel */ + regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01); + /* Set RC calibration */ + rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL, + RT721_RC_CALIB_CTRL0, 0x0b00); + rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL, + RT721_RC_CALIB_CTRL0, 0x0b40); + /* Fine tune PDE2A latency */ + regmap_write(rt721->regmap, 0x2f5c, 0x25); +} + +static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) +{; + /* Power down group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8000); + /* VREF_HV_EN_AUTO_FAST */ + rt721_sdca_index_write(rt721, RT721_ANA_POW_PART, + RT721_VREF1_HV_CTRL1, 0xe000); + /* Power up group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8007); + /* Reset dc_cal_top */ + regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420); + /* Turn back to normal dc_cal_top */ + regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421); + /* W1C Trigger Calibration */ + regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421); + /* DAC04 FU entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_CH_FLOAT_CTL6, 0x5561); + /* Set EAPD high */ + rt721_sdca_index_write(rt721, RT721_VENDOR_REG, + RT721_GPIO_PAD_CTRL5, 0x8003); + /* Enable vf707_r14 */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04); + /* FU 23 SPK mute control - L */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00); + /* FU 23 SPK mute control - R */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); + /* FU 55 DAC04 mute control - L */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00); + /* FU 55 DAC04 mute control - R */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); +} + +static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) +{ + /* Power down group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8000); + /* VREF_HV_EN_AUTO_FAST */ + rt721_sdca_index_write(rt721, RT721_ANA_POW_PART, + RT721_VREF1_HV_CTRL1, 0xe000); + /* Power up group1/2/3_mic_pdb */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_MISC_POWER_CTL31, 0x8007); + /* GE0 mode related control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_GE_REL_CTRL1, 0x8011); + /* Button A, B, C, D bypass mode */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_UMP_HID_CTRL3, 0xcf00); + /* HID1 slot enable */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_UMP_HID_CTRL4, 0x000f); + /* Report ID for HID1 */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_UMP_HID_CTRL1, 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_UMP_HID_CTRL5, 0x0c12); + /* Set JD de-bounce clock control */ + rt721_sdca_index_write(rt721, RT721_JD_CTRL, + RT721_JD_1PIN_GAT_CTRL2, 0xc002); + /* RC calibration -1 */ + rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL, + RT721_RC_CALIB_CTRL0, 0x0b00); + /* RC calibration -2 */ + rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL, + RT721_RC_CALIB_CTRL0, 0x0b40); + /* pow_clk_12p288mhz_dre03 change to register mode */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_UAJ_TOP_TCON14, 0x3333); + /* Tune calibration timing control */ + regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036); + /* calibration HP amp output select control from Efuse */ + regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00); + /* FSM related control */ + rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL, + RT721_HP_AMP_2CH_CAL1, 0x0140); + /* HP calibration related control */ + regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021); + /* W1C HP calibration*/ + regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021); + /* reg_sel_cin_hp_0010/0011 */ + rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL, + RT721_HP_AMP_2CH_CAL18, 0x5522); + regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000); + /* sel_sensing_lr_hp */ + regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f); + /* Release HP-JD */ + rt721_sdca_index_write(rt721, RT721_CBJ_CTRL, + RT721_CBJ_A0_GAT_CTRL1, 0x2a02); + /* en_osw gating auto done bit */ + rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL, + RT721_HP_AMP_2CH_CAL4, 0xa105); + /* pow_clk_en_sw_amp_detect_sel to register mode */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_UAJ_TOP_TCON14, 0x3b33); + /* cp_sw_hp to auto mode */ + regmap_write(rt721->mbq_regmap, 0x310400, 0x3023); + /* pow_clk_en_sw_amp_detect power up */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_UAJ_TOP_TCON14, 0x3f33); + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_UAJ_TOP_TCON13, 0x6048); + /* switch size detect threshold */ + regmap_write(rt721->mbq_regmap, 0x310401, 0x3000); + regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00); + /* en_hp_amp_detect auto mode */ + regmap_write(rt721->mbq_regmap, 0x310300, 0x000f); + /* amp detect threshold */ + regmap_write(rt721->mbq_regmap, 0x310301, 0x3000); + regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00); + /* gating_sdw_link_rst_n_1_cbj_reg */ + rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL, + RT721_UAJ_TOP_TCON17, 0x0008); + /* CKXEN_SDAC chopper function */ + rt721_sdca_index_write(rt721, RT721_DAC_CTRL, + RT721_DAC_2CH_CTRL3, 0x55ff); + /* CKXSEL_SDAC chopper frequency */ + rt721_sdca_index_write(rt721, RT721_DAC_CTRL, + RT721_DAC_2CH_CTRL4, 0xcc00); + /* Bias current for SDAC */ + rt721_sdca_index_write(rt721, RT721_ANA_POW_PART, + RT721_MBIAS_LV_CTRL2, 0x6677); + /* VREF2 level selection */ + rt721_sdca_index_write(rt721, RT721_ANA_POW_PART, + RT721_VREF2_LV_CTRL1, 0x7600); + /* ADC09/MIC2 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL2, 0x1234); + /* LINE2 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL3, 0x3512); + /* DAC03/HP power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL1, 0x4040); + /* ADC27 power entity floating control */ + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_ENT_FLOAT_CTL4, 0x1201); + /* Fine tune PDE40 latency */ + regmap_write(rt721->regmap, 0x2f58, 0x07); +} + +int rt721_sdca_index_write(struct rt721_sdca_priv *rt721, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt721->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt721->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +int rt721_sdca_index_read(struct rt721_sdca_priv *rt721, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt721->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt721->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt721_sdca_index_update_bits(struct rt721_sdca_priv *rt721, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt721_sdca_index_read(rt721, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt721_sdca_index_write(rt721, nid, reg, tmp); +} + +static int rt721_sdca_btn_type(unsigned char *buffer) +{ + if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || + (*(buffer + 1) == 0x01) || (*(buffer + 1) == 0x10)) + return SND_JACK_BTN_2; + else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || + (*(buffer + 1) == 0x02) || (*(buffer + 1) == 0x20)) + return SND_JACK_BTN_3; + else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || + (*(buffer + 1) == 0x04) || (*(buffer + 1) == 0x40)) + return SND_JACK_BTN_0; + else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || + (*(buffer + 1) == 0x08) || (*(buffer + 1) == 0x80)) + return SND_JACK_BTN_1; + + return 0; +} + +static unsigned int rt721_sdca_button_detect(struct rt721_sdca_priv *rt721) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt721->regmap, + RT721_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt721_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int rt721_sdca_headset_detect(struct rt721_sdca_priv *rt721) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt721->jack_type = 0; + break; + case 0x03: + rt721->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt721->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt721->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt721_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt721_sdca_priv *rt721 = + container_of(work, struct rt721_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt721->hs_jack) + return; + + if (!rt721->component->card || !rt721->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ + if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) { + ret = rt721_sdca_headset_detect(rt721); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt721->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt721_sdca_button_detect(rt721); + + if (rt721->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt721->slave->dev, + "in %s, jack_type=%d\n", __func__, rt721->jack_type); + dev_dbg(&rt721->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + pr_info("rtk debug, jack_type=%d\n", rt721->jack_type); + pr_info("rtk debug, btn_type=0x%x\n", btn_type); + dev_dbg(&rt721->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt721->scp_sdca_stat1, rt721->scp_sdca_stat2); + + snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt721->hs_jack, rt721->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt721->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt721_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt721_sdca_priv *rt721 = + container_of(work, struct rt721_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49, + RT721_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01, + RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt721->regmap, + RT721_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt721_sdca_btn_type(&buf[1]); + } else + rt721->jack_type = 0; + + dev_dbg(&rt721->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt721->hs_jack, rt721->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt721->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) +{ + mutex_lock(&rt721->calibrate_mutex); + if (rt721->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt721->slave->dev, "in %s enable\n", __func__); + rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_UAJ_CTL, 0x036E); + /* set XU(et03h) & XU(et0Dh) to Not bypassed */ + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU03, + RT721_SDCA_CTL_SELECTED_MODE, 0), 0); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU0D, + RT721_SDCA_CTL_SELECTED_MODE, 0), 0); + /* trigger GE interrupt */ + rt721_sdca_index_update_bits(rt721, RT721_HDA_SDCA_FLOAT, + RT721_GE_REL_CTRL1, 0x4000, 0x4000); + } + mutex_unlock(&rt721->calibrate_mutex); +} + +static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + int ret; + + rt721->hs_jack = hs_jack; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt721_sdca_jack_init(rt721); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt721_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendA = 0x200; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt721->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt721->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) { + /* boost gain */ + gain_l_val = gain_l_val * tendB; + } else if (mc->shift == 1) { + /* FU33 boost gain */ + if (gain_l_val == 0) + gain_l_val = 0x8000; + else + gain_l_val = (gain_l_val - 1) * tendA; + } else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) { + /* boost gain */ + gain_r_val = gain_r_val * tendB; + } else if (mc->shift == 1) { + /* FU33 boost gain */ + if (gain_r_val == 0) + gain_r_val = 0x8000; + else + gain_r_val = (gain_r_val - 1) * tendA; + } else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + /* Lch*/ + regmap_write(rt721->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt721->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt721->mbq_regmap, mc->reg, &read_l); + regmap_read(rt721->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return changed; + + return -EIO; +} + +static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt721->mbq_regmap, mc->reg, &read_l); + regmap_read(rt721->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else { + ctl_r = ctl_l; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt721_sdca_set_fu1e_capture_ctl(struct rt721_sdca_priv *rt721) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt721->fu1e_mixer_mute); i++) { + ch_mute = rt721->fu1e_dapm_mute || rt721->fu1e_mixer_mute[i]; + err = regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt721_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct rt721_sdca_dmic_kctrl_priv *p = + (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt721->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt721_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct rt721_sdca_dmic_kctrl_priv *p = + (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt721->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt721->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt721_sdca_set_fu1e_capture_ctl(rt721); + if (err < 0) + return err; + + return changed; +} + +static int rt721_sdca_set_fu0f_capture_ctl(struct rt721_sdca_priv *rt721) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt721_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt721->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt721->fu0f_mixer_r_mute; + return 0; +} + +static int rt721_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt721->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt721->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt721->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt721->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt721_sdca_set_fu0f_capture_ctl(rt721); + if (err < 0) + return err; + + return changed; +} + +static int rt721_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt721_sdca_dmic_kctrl_priv *p = + (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static int rt721_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct rt721_sdca_dmic_kctrl_priv *p = + (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt721->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / boost_step; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt721_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt721_sdca_dmic_kctrl_priv *p = + (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt721->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * boost_step; + else { /* ADC gain */ + gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt721->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt721->slave->dev, "%#08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +#define RT721_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt721_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT721_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt721_sdca_fu_info, \ + .get = rt721_sdca_fu1e_capture_get, \ + .put = rt721_sdca_fu1e_capture_put, \ + .private_value = RT721_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT721_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt721_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT721_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); +static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0); + +static const struct snd_kcontrol_new rt721_sdca_controls[] = { + /* Headphone playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv), + /* Headset mic capture settings */ + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, + RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0, + rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, + RT721_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, + RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0, + rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv), + /* AMP playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv), + /* DMIC capture settings */ + RT721_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4), + RT721_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, + RT721_SDCA_CTL_FU_VOLUME, CH_01), + rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv), + RT721_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, + RT721_SDCA_CTL_FU_CH_GAIN, CH_01), + rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put, + 4, 3, boost_vol_tlv), +}; + +static int rt721_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft, mask; + + if (strstr(ucontrol->id.name, "ADC 09 Mux")) { + mask_sft = 12; + mask = 0x7; + } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) { + mask_sft = 10; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) { + mask_sft = 8; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) { + mask_sft = 6; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) { + mask_sft = 4; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) { + mask_sft = 2; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) { + mask_sft = 0; + mask = 0x3; + } else + return -EINVAL; + + rt721_sdca_index_read(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & mask; + + return 0; +} + +static int rt721_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft, mask; + unsigned int check; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 09 Mux")) { + mask_sft = 12; + mask = 0x7; + } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) { + mask_sft = 10; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) { + mask_sft = 8; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) { + mask_sft = 6; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) { + mask_sft = 4; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) { + mask_sft = 2; + mask = 0x3; + } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) { + mask_sft = 0; + mask = 0x3; + } else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + rt721_sdca_index_read(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_MUX_CTL0, &val2); + + if (strstr(ucontrol->id.name, "ADC 09 Mux")) + val2 = (val2 >> mask_sft) & 0x7; + else + val2 = (val2 >> mask_sft) & 0x3; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + rt721_sdca_index_read(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_MUX_CTL0, &check); + rt721_sdca_index_update_bits(rt721, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_MUX_CTL0, mask << mask_sft, + val << mask_sft); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc09_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", +}; +static const char * const adc07_10_mux_text[] = { + "DMIC1 RE", + "DMIC1 FE", + "DMIC2 RE", + "DMIC2 FE", +}; + +static SOC_ENUM_SINGLE_DECL( + rt721_adc09_enum, SND_SOC_NOPM, 0, adc09_mux_text); +static SOC_ENUM_SINGLE_DECL( + rt721_dmic_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static const struct snd_kcontrol_new rt721_sdca_adc09_mux = + SOC_DAPM_ENUM_EXT("ADC 09 Mux", rt721_adc09_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc08_r_mux = + SOC_DAPM_ENUM_EXT("ADC 08 R Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc08_l_mux = + SOC_DAPM_ENUM_EXT("ADC 08 L Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc10_r_mux = + SOC_DAPM_ENUM_EXT("ADC 10 R Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc10_l_mux = + SOC_DAPM_ENUM_EXT("ADC 10 L Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc07_r_mux = + SOC_DAPM_ENUM_EXT("ADC 07 R Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); +static const struct snd_kcontrol_new rt721_sdca_adc07_l_mux = + SOC_DAPM_ENUM_EXT("ADC 07 L Mux", rt721_dmic_enum, + rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put); + + +static int rt721_sdca_fu42_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(100); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, + RT721_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt721_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, + RT721_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt721_sdca_fu23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, + RT721_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt721_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt721->fu1e_dapm_mute = false; + rt721_sdca_set_fu1e_capture_ctl(rt721); + break; + case SND_SOC_DAPM_PRE_PMD: + rt721->fu1e_dapm_mute = true; + rt721_sdca_set_fu1e_capture_ctl(rt721); + break; + } + return 0; +} + +static int rt721_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt721->fu0f_dapm_mute = false; + rt721_sdca_set_fu0f_capture_ctl(rt721); + break; + case SND_SOC_DAPM_PRE_PMD: + rt721->fu0f_dapm_mute = true; + rt721_sdca_set_fu0f_capture_ctl(rt721); + break; + } + return 0; +} + +static int rt721_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt721_sdca_pde41_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt721_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt721_sdca_pde34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt721_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC3_4"), + + SND_SOC_DAPM_SUPPLY("PDE 41", SND_SOC_NOPM, 0, 0, + rt721_sdca_pde41_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt721_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt721_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, + rt721_sdca_pde34_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt721_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 23", NULL, SND_SOC_NOPM, 0, 0, + rt721_sdca_fu23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0, + rt721_sdca_fu42_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt721_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt721_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 09 Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc09_mux), + SND_SOC_DAPM_MUX("ADC 08 R Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc08_r_mux), + SND_SOC_DAPM_MUX("ADC 08 L Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc08_l_mux), + SND_SOC_DAPM_MUX("ADC 10 R Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc10_r_mux), + SND_SOC_DAPM_MUX("ADC 10 L Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc10_l_mux), + SND_SOC_DAPM_MUX("ADC 07 R Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc07_r_mux), + SND_SOC_DAPM_MUX("ADC 07 L Mux", SND_SOC_NOPM, 0, 0, + &rt721_sdca_adc07_l_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt721_sdca_audio_map[] = { + {"FU 42", NULL, "DP1RX"}, + {"FU 21", NULL, "DP3RX"}, + {"FU 23", NULL, "DP3RX"}, + + {"ADC 09 Mux", "MIC2", "MIC2"}, + {"ADC 09 Mux", "LINE1", "LINE1"}, + {"ADC 09 Mux", "LINE2", "LINE2"}, + {"ADC 07 R Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 07 R Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 07 R Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 07 R Mux", "DMIC2 FE", "DMIC3_4"}, + {"ADC 07 L Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 07 L Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 07 L Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 07 L Mux", "DMIC2 FE", "DMIC3_4"}, + {"ADC 08 R Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 08 R Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 08 R Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 08 R Mux", "DMIC2 FE", "DMIC3_4"}, + {"ADC 08 L Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 08 L Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 08 L Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 08 L Mux", "DMIC2 FE", "DMIC3_4"}, + {"ADC 10 R Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 10 R Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 10 R Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 10 R Mux", "DMIC2 FE", "DMIC3_4"}, + {"ADC 10 L Mux", "DMIC1 RE", "DMIC1_2"}, + {"ADC 10 L Mux", "DMIC1 FE", "DMIC1_2"}, + {"ADC 10 L Mux", "DMIC2 RE", "DMIC3_4"}, + {"ADC 10 L Mux", "DMIC2 FE", "DMIC3_4"}, + {"FU 36", NULL, "PDE 34"}, + {"FU 36", NULL, "ADC 09 Mux"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "ADC 07 R Mux"}, + {"FU 113", NULL, "ADC 07 L Mux"}, + {"FU 113", NULL, "ADC 10 R Mux"}, + {"FU 113", NULL, "ADC 10 L Mux"}, + {"DP2TX", NULL, "FU 36"}, + {"DP6TX", NULL, "FU 113"}, + + {"HP", NULL, "PDE 47"}, + {"HP", NULL, "FU 42"}, + {"SPK", NULL, "PDE 41"}, + {"SPK", NULL, "FU 21"}, + {"SPK", NULL, "FU 23"}, +}; + +static int rt721_sdca_parse_dt(struct rt721_sdca_priv *rt721, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt721->jd_src); + + return 0; +} + +static int rt721_sdca_probe(struct snd_soc_component *component) +{ + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + int ret; + + rt721_sdca_parse_dt(rt721, &rt721->slave->dev); + rt721->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt721 = { + .probe = rt721_sdca_probe, + .controls = rt721_sdca_controls, + .num_controls = ARRAY_SIZE(rt721_sdca_controls), + .dapm_widgets = rt721_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt721_sdca_dapm_widgets), + .dapm_routes = rt721_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt721_sdca_audio_map), + .set_jack = rt721_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt721_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt721_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt721_sdca_pcm_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt721->slave) + return -EINVAL; + + /* + * RT721_AIF1 with port = 1 for headphone playback + * RT721_AIF1 with port = 2 for headset-mic capture + * RT721_AIF2 with port = 3 for speaker playback + * RT721_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT721_AIF1) + port = 1; + else if (dai->id == RT721_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT721_AIF1) + port = 2; + else if (dai->id == RT721_AIF3) + port = 6; + else + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt721->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = RT721_SDCA_RATE_8000HZ; + break; + case 16000: + sampling_rate = RT721_SDCA_RATE_16000HZ; + break; + case 24000: + sampling_rate = RT721_SDCA_RATE_24000HZ; + break; + case 32000: + sampling_rate = RT721_SDCA_RATE_32000HZ; + break; + case 44100: + sampling_rate = RT721_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT721_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT721_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT721_SDCA_RATE_192000HZ; + break; + case 384000: + sampling_rate = RT721_SDCA_RATE_384000HZ; + break; + case 768000: + sampling_rate = RT721_SDCA_RATE_768000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + if (dai->id == RT721_AIF1) { + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + } + + if (dai->id == RT721_AIF2) + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + if (dai->id == RT721_AIF3) + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + return 0; +} + +static int rt721_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt721->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt721->slave, sdw_stream); + return 0; +} + +#define RT721_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT721_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt721_sdca_ops = { + .hw_params = rt721_sdca_pcm_hw_params, + .hw_free = rt721_sdca_pcm_hw_free, + .set_stream = rt721_sdca_set_sdw_stream, + .shutdown = rt721_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt721_sdca_dai[] = { + { + .name = "rt721-sdca-aif1", + .id = RT721_AIF1, + .playback = { + .stream_name = "DP1 Headphone Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT721_STEREO_RATES, + .formats = RT721_FORMATS, + }, + .capture = { + .stream_name = "DP2 Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT721_STEREO_RATES, + .formats = RT721_FORMATS, + }, + .ops = &rt721_sdca_ops, + }, + { + .name = "rt721-sdca-aif2", + .id = RT721_AIF2, + .playback = { + .stream_name = "DP3 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT721_STEREO_RATES, + .formats = RT721_FORMATS, + }, + .ops = &rt721_sdca_ops, + }, + { + .name = "rt721-sdca-aif3", + .id = RT721_AIF3, + .capture = { + .stream_name = "DP6 DMic Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT721_STEREO_RATES, + .formats = RT721_FORMATS, + }, + .ops = &rt721_sdca_ops, + } +}; + +int rt721_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt721_sdca_priv *rt721; + + rt721 = devm_kzalloc(dev, sizeof(*rt721), GFP_KERNEL); + if (!rt721) + return -ENOMEM; + + dev_set_drvdata(dev, rt721); + rt721->slave = slave; + rt721->regmap = regmap; + rt721->mbq_regmap = mbq_regmap; + + regcache_cache_only(rt721->regmap, true); + regcache_cache_only(rt721->mbq_regmap, true); + + mutex_init(&rt721->calibrate_mutex); + mutex_init(&rt721->disable_irq_lock); + + INIT_DELAYED_WORK(&rt721->jack_detect_work, rt721_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt721->jack_btn_check_work, rt721_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt721->hw_init = false; + rt721->first_hw_init = false; + rt721->fu1e_dapm_mute = true; + rt721->fu0f_dapm_mute = true; + rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true; + rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] = + rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true; + + return devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai)); +} + +int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); + + rt721->disable_irq = false; + + if (rt721->hw_init) + return 0; + + regcache_cache_only(rt721->regmap, false); + regcache_cache_only(rt721->mbq_regmap, false); + if (rt721->first_hw_init) { + regcache_cache_bypass(rt721->regmap, true); + regcache_cache_bypass(rt721->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + rt721_sdca_dmic_preset(rt721); + rt721_sdca_amp_preset(rt721); + rt721_sdca_jack_preset(rt721); + if (rt721->first_hw_init) { + regcache_cache_bypass(rt721->regmap, false); + regcache_mark_dirty(rt721->regmap); + regcache_cache_bypass(rt721->mbq_regmap, false); + regcache_mark_dirty(rt721->mbq_regmap); + } else + rt721->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt721->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu jack.yu@realtek.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h new file mode 100644 index 000000000000..372a788d384d --- /dev/null +++ b/sound/soc/codecs/rt721-sdca.h @@ -0,0 +1,277 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt721-sdca.h -- RT721 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + */ + +#ifndef __RT721_H__ +#define __RT721_H__ + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt721_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct mutex calibrate_mutex; + struct mutex disable_irq_lock; + bool disable_irq; + /* For Headset jack & Headphone */ + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + int jd_src; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; + /* For DMIC */ + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt721_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* NID */ +#define RT721_ANA_POW_PART 0x01 +#define RT721_DAC_CTRL 0x04 +#define RT721_JD_CTRL 0x09 +#define RT721_CBJ_CTRL 0x0a +#define RT721_CAP_PORT_CTRL 0x0c +#define RT721_CLASD_AMP_CTRL 0x0d +#define RT721_VENDOR_REG 0x20 +#define RT721_RC_CALIB_CTRL 0x40 +#define RT721_VENDOR_EQ_L 0x53 +#define RT721_VENDOR_EQ_R 0x54 +#define RT721_VENDOR_HP_CALI 0x56 +#define RT721_VENDOR_CHARGE_PUMP 0x57 +#define RT721_VENDOR_CLASD_CALI 0x58 +#define RT721_VENDOR_IMS_DRE 0x5b +#define RT721_VENDOR_SPK_EFUSE 0x5c +#define RT721_VENDOR_LEVEL_CTRL 0x5d +#define RT721_VENDOR_ANA_CTL 0x5f +#define RT721_HDA_SDCA_FLOAT 0x61 + +/* Index (NID:01h) */ +#define RT721_MBIAS_LV_CTRL2 0x07 +#define RT721_VREF1_HV_CTRL1 0x0a +#define RT721_VREF2_LV_CTRL1 0x0b + +/* Index (NID:04h) */ +#define RT721_DAC_2CH_CTRL3 0x02 +#define RT721_DAC_2CH_CTRL4 0x03 + +/* Index (NID:09h) */ +#define RT721_JD_1PIN_GAT_CTRL2 0x07 + +/* Index (NID:0ah) */ +#define RT721_CBJ_A0_GAT_CTRL1 0x04 +#define RT721_CBJ_A0_GAT_CTRL2 0x05 + +/* Index (NID:0Ch) */ +#define RT721_HP_AMP_2CH_CAL1 0x05 +#define RT721_HP_AMP_2CH_CAL4 0x08 +#define RT721_HP_AMP_2CH_CAL18 0x1b + +/* Index (NID:0dh) */ +#define RT721_CLASD_AMP_2CH_CAL 0x14 + +/* Index (NID:20h) */ +#define RT721_JD_PRODUCT_NUM 0x00 +#define RT721_ANALOG_BIAS_CTL3 0x04 +#define RT721_JD_CTRL1 0x09 +#define RT721_LDO2_3_CTL1 0x0e +#define RT721_GPIO_PAD_CTRL5 0x13 +#define RT721_LDO1_CTL 0x1a +#define RT721_HP_JD_CTRL 0x24 +#define RT721_VD_HIDDEN_CTRL 0x26 +#define RT721_CLSD_CTRL6 0x3c +#define RT721_COMBO_JACK_AUTO_CTL1 0x45 +#define RT721_COMBO_JACK_AUTO_CTL2 0x46 +#define RT721_COMBO_JACK_AUTO_CTL3 0x47 +#define RT721_DIGITAL_MISC_CTRL4 0x4a +#define RT721_VREFO_GAT 0x63 +#define RT721_FSM_CTL 0x67 +#define RT721_SDCA_INTR_REC 0x82 +#define RT721_SW_CONFIG1 0x8a +#define RT721_SW_CONFIG2 0x8b + +/* Index (NID:40h) */ +#define RT721_RC_CALIB_CTRL0 0x00 + +/* Index (NID:58h) */ +#define RT721_DAC_DC_CALI_CTL1 0x01 +#define RT721_DAC_DC_CALI_CTL2 0x02 +#define RT721_DAC_DC_CALI_CTL3 0x03 + +/* Index (NID:5fh) */ +#define RT721_MISC_POWER_CTL0 0x00 +#define RT721_MISC_POWER_CTL31 0x31 +#define RT721_UAJ_TOP_TCON13 0x44 +#define RT721_UAJ_TOP_TCON14 0x45 +#define RT721_UAJ_TOP_TCON17 0x48 + +/* Index (NID:61h) */ +#define RT721_HDA_LEGACY_MUX_CTL0 0x00 +#define RT721_HDA_LEGACY_UAJ_CTL 0x02 +#define RT721_HDA_LEGACY_CTL1 0x05 +#define RT721_HDA_LEGACY_RESET_CTL 0x06 +#define RT721_GE_REL_CTRL1 0x0d +#define RT721_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT721_GE_SDCA_RST_CTRL 0x10 +#define RT721_INT_RST_EN_CTRL 0x11 +#define RT721_XU_EVENT_EN 0x13 +#define RT721_INLINE_CTL2 0x17 +#define RT721_UMP_HID_CTRL1 0x18 +#define RT721_UMP_HID_CTRL2 0x19 +#define RT721_UMP_HID_CTRL3 0x1a +#define RT721_UMP_HID_CTRL4 0x1b +#define RT721_UMP_HID_CTRL5 0x1c +#define RT721_FUNC_FLOAT_CTL0 0x22 +#define RT721_FUNC_FLOAT_CTL1 0x23 +#define RT721_FUNC_FLOAT_CTL2 0x24 +#define RT721_FUNC_FLOAT_CTL3 0x25 +#define RT721_ENT_FLOAT_CTL0 0x29 +#define RT721_ENT_FLOAT_CTL1 0x2c +#define RT721_ENT_FLOAT_CTL2 0x2d +#define RT721_ENT_FLOAT_CTL3 0x2e +#define RT721_ENT_FLOAT_CTL4 0x2f +#define RT721_CH_FLOAT_CTL1 0x45 +#define RT721_CH_FLOAT_CTL2 0x46 +#define RT721_ENT_FLOAT_CTL5 0x53 +#define RT721_ENT_FLOAT_CTL6 0x54 +#define RT721_ENT_FLOAT_CTL7 0x55 +#define RT721_ENT_FLOAT_CTL8 0x57 +#define RT721_ENT_FLOAT_CTL9 0x5a +#define RT721_ENT_FLOAT_CTL10 0x5b +#define RT721_CH_FLOAT_CTL3 0x6a +#define RT721_CH_FLOAT_CTL4 0x6d +#define RT721_CH_FLOAT_CTL5 0x70 +#define RT721_CH_FLOAT_CTL6 0x92 + +/* Parameter & Verb control 01 (0x26)(NID:20h) */ +#define RT721_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* Buffer address for HID */ +#define RT721_BUF_ADDR_HID1 0x44030000 +#define RT721_BUF_ADDR_HID2 0x44030020 + +/* RT721 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT721 SDCA entity */ +#define RT721_SDCA_ENT_HID01 0x01 +#define RT721_SDCA_ENT_XUV 0x03 +#define RT721_SDCA_ENT_GE49 0x49 +#define RT721_SDCA_ENT_USER_FU05 0x05 +#define RT721_SDCA_ENT_USER_FU06 0x06 +#define RT721_SDCA_ENT_USER_FU0F 0x0f +#define RT721_SDCA_ENT_USER_FU10 0x19 +#define RT721_SDCA_ENT_USER_FU1E 0x1e +#define RT721_SDCA_ENT_FU15 0x15 +#define RT721_SDCA_ENT_PDE23 0x23 +#define RT721_SDCA_ENT_PDE40 0x40 +#define RT721_SDCA_ENT_PDE41 0x41 +#define RT721_SDCA_ENT_PDE11 0x11 +#define RT721_SDCA_ENT_PDE12 0x12 +#define RT721_SDCA_ENT_PDE2A 0x2a +#define RT721_SDCA_ENT_CS01 0x01 +#define RT721_SDCA_ENT_CS11 0x11 +#define RT721_SDCA_ENT_CS1F 0x1f +#define RT721_SDCA_ENT_CS1C 0x1c +#define RT721_SDCA_ENT_CS31 0x31 +#define RT721_SDCA_ENT_OT23 0x42 +#define RT721_SDCA_ENT_IT26 0x26 +#define RT721_SDCA_ENT_IT09 0x09 +#define RT721_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT721_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT721_SDCA_ENT_XU03 0x03 +#define RT721_SDCA_ENT_XU0D 0x0d +#define RT721_SDCA_ENT_FU55 0x55 + +/* RT721 SDCA control */ +#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT721_SDCA_CTL_FU_MUTE 0x01 +#define RT721_SDCA_CTL_FU_VOLUME 0x02 +#define RT721_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT721_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT721_SDCA_CTL_SELECTED_MODE 0x01 +#define RT721_SDCA_CTL_DETECTED_MODE 0x02 +#define RT721_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT721_SDCA_CTL_VENDOR_DEF 0x30 +#define RT721_SDCA_CTL_XUV 0x34 +#define RT721_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT721 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_08 0x08 +#define CH_09 0x09 +#define CH_0A 0x0a + +/* sample frequency index */ +#define RT721_SDCA_RATE_8000HZ 0x01 +#define RT721_SDCA_RATE_11025HZ 0x02 +#define RT721_SDCA_RATE_12000HZ 0x03 +#define RT721_SDCA_RATE_16000HZ 0x04 +#define RT721_SDCA_RATE_22050HZ 0x05 +#define RT721_SDCA_RATE_24000HZ 0x06 +#define RT721_SDCA_RATE_32000HZ 0x07 +#define RT721_SDCA_RATE_44100HZ 0x08 +#define RT721_SDCA_RATE_48000HZ 0x09 +#define RT721_SDCA_RATE_88200HZ 0x0a +#define RT721_SDCA_RATE_96000HZ 0x0b +#define RT721_SDCA_RATE_176400HZ 0x0c +#define RT721_SDCA_RATE_192000HZ 0x0d +#define RT721_SDCA_RATE_384000HZ 0x0e +#define RT721_SDCA_RATE_768000HZ 0x0f + +enum { + RT721_AIF1, /* For headset mic and headphone */ + RT721_AIF2, /* For speaker */ + RT721_AIF3, /* For dmic */ + RT721_AIFS, +}; + +enum rt721_sdca_jd_src { + RT721_JD_NULL, + RT721_JD1, + RT721_JD2, +}; + +int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt721_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt721_sdca_index_write(struct rt721_sdca_priv *rt721, + unsigned int nid, unsigned int reg, unsigned int value); +int rt721_sdca_index_read(struct rt721_sdca_priv *rt721, + unsigned int nid, unsigned int reg, unsigned int *value); + +int rt721_sdca_jack_detect(struct rt721_sdca_priv *rt721, bool *hp, bool *mic); +#endif /* __RT721_H__ */
On 9/24/24 11:03, Jack Yu wrote:
This is the initial codec driver for rt721-sdca.
I wouldn't hurt to provide a short description. 722 has 3 functions, what about 721?
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_01):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_02):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_03):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_04):
- case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
return true;
That tells us it has 3 functions (jack, mic, amp), so what's different to RT722? could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
+static int rt721_sdca_read_prop(struct sdw_slave *slave) +{
- struct sdw_slave_prop *prop = &slave->prop;
- int nval;
- int i, j;
- u32 bit;
- unsigned long addr;
- struct sdw_dpn_prop *dpn;
- sdw_slave_read_prop(slave);
I thought we agreed to use a helper to read only the number of lanes from platform firmware?
Bard, did you share this already?
- prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
- prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
- prop->paging_support = true;
- /*
* port = 1 for headphone playback
* port = 2 for headset-mic capture
* port = 3 for speaker playback
* port = 6 for digital-mic capture
*/
- prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
- prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
- nval = hweight32(prop->source_ports);
- prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop), GFP_KERNEL);
- if (!prop->src_dpn_prop)
return -ENOMEM;
- i = 0;
- dpn = prop->src_dpn_prop;
- addr = prop->source_ports;
- for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].type = SDW_DPN_FULL;
dpn[i].simple_ch_prep_sm = true;
dpn[i].ch_prep_timeout = 10;
i++;
- }
- /* do this again for sink now */
- nval = hweight32(prop->sink_ports);
- prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
- if (!prop->sink_dpn_prop)
return -ENOMEM;
- j = 0;
- dpn = prop->sink_dpn_prop;
- addr = prop->sink_ports;
- for_each_set_bit(bit, &addr, 32) {
dpn[j].num = bit;
dpn[j].type = SDW_DPN_FULL;
dpn[j].simple_ch_prep_sm = true;
dpn[j].ch_prep_timeout = 10;
j++;
- }
- /* set the timeout values */
- prop->clk_stop_timeout = 200;
- /* wake-up event */
- prop->wake_capable = 1;
- /* Three data lanes are supported by rt721-sdca codec */
- prop->lane_control_support = true;
this doesn't seem needed if we already read information on lane support.
+static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721) +{
- /* Power down group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
- /* VREF_HV_EN_AUTO_FAST */
- rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
- /* Power up group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
- /* Set AD07/08 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL9, 0x2a2a);
- /* Set AD10 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL10, 0x2a00);
- /* Set DMIC1/DMIC2 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL6, 0x2a2a);
- /* Set DMIC1/DMIC2 IT entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL5, 0x2626);
- /* Set AD10 FU entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL8, 0x1e00);
- /* Set DMIC1/DMIC2 FU input gain floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL7, 0x1515);
- /* Set DMIC2 FU input gain channel floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL3, 0x0304);
- /* Set AD10 FU channel floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL4, 0x0304);
- /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_HDA_LEGACY_CTL1, 0x0000);
- /* Enable vf707_r12_05/vf707_r13_05 */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
- /* Set usd_flag_sel, usd_in_sel */
- regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
- /* Set RC calibration */
- rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
- rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
- /* Fine tune PDE2A latency */
- regmap_write(rt721->regmap, 0x2f5c, 0x25);
+}
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) +{;
- /* Power down group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
- /* VREF_HV_EN_AUTO_FAST */
- rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
- /* Power up group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
- /* Reset dc_cal_top */
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
- /* Turn back to normal dc_cal_top */
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
- /* W1C Trigger Calibration */
- regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
- /* DAC04 FU entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL6, 0x5561);
- /* Set EAPD high */
- rt721_sdca_index_write(rt721, RT721_VENDOR_REG,
RT721_GPIO_PAD_CTRL5, 0x8003);
- /* Enable vf707_r14 */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
- /* FU 23 SPK mute control - L */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
- /* FU 23 SPK mute control - R */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
- /* FU 55 DAC04 mute control - L */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
- /* FU 55 DAC04 mute control - R */
- regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+}
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) +{
- /* Power down group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
- /* VREF_HV_EN_AUTO_FAST */
- rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
- /* Power up group1/2/3_mic_pdb */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
- /* GE0 mode related control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_GE_REL_CTRL1, 0x8011);
- /* Button A, B, C, D bypass mode */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL3, 0xcf00);
- /* HID1 slot enable */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL4, 0x000f);
- /* Report ID for HID1 */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL1, 0x1100);
- /* OSC/OOC for slot 2, 3 */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL5, 0x0c12);
- /* Set JD de-bounce clock control */
- rt721_sdca_index_write(rt721, RT721_JD_CTRL,
RT721_JD_1PIN_GAT_CTRL2, 0xc002);
- /* RC calibration -1 */
- rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
- /* RC calibration -2 */
- rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
- /* pow_clk_12p288mhz_dre03 change to register mode */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3333);
- /* Tune calibration timing control */
- regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
- /* calibration HP amp output select control from Efuse */
- regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
- /* FSM related control */
- rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL1, 0x0140);
- /* HP calibration related control */
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
- /* W1C HP calibration*/
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
- /* reg_sel_cin_hp_0010/0011 */
- rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL18, 0x5522);
- regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
- /* sel_sensing_lr_hp */
- regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
- /* Release HP-JD */
- rt721_sdca_index_write(rt721, RT721_CBJ_CTRL,
RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
- /* en_osw gating auto done bit */
- rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL4, 0xa105);
- /* pow_clk_en_sw_amp_detect_sel to register mode */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3b33);
- /* cp_sw_hp to auto mode */
- regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
- /* pow_clk_en_sw_amp_detect power up */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3f33);
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON13, 0x6048);
- /* switch size detect threshold */
- regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
- regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
- /* en_hp_amp_detect auto mode */
- regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
- /* amp detect threshold */
- regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
- regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
- /* gating_sdw_link_rst_n_1_cbj_reg */
- rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON17, 0x0008);
- /* CKXEN_SDAC chopper function */
- rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL3, 0x55ff);
- /* CKXSEL_SDAC chopper frequency */
- rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL4, 0xcc00);
- /* Bias current for SDAC */
- rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_MBIAS_LV_CTRL2, 0x6677);
- /* VREF2 level selection */
- rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF2_LV_CTRL1, 0x7600);
- /* ADC09/MIC2 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL2, 0x1234);
- /* LINE2 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL3, 0x3512);
- /* DAC03/HP power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL1, 0x4040);
- /* ADC27 power entity floating control */
- rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL4, 0x1201);
- /* Fine tune PDE40 latency */
- regmap_write(rt721->regmap, 0x2f58, 0x07);
+}
same here, shouldn't this come from ACPI/blind write tables?
+enum rt721_sdca_jd_src {
- RT721_JD_NULL,
- RT721_JD1,
- RT721_JD2,
+};
is this supported in SDCA? I can't recall seeing this for other drivers.
-----Original Message----- From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Sent: Tuesday, September 24, 2024 5:36 PM To: Jack Yu jack.yu@realtek.com; broonie@kernel.org; lgirdwood@gmail.com Cc: alsa-devel@alsa-project.org; lars@metafoo.de; Flove(HsinFu) flove@realtek.com; Oder Chiou oder_chiou@realtek.com; Shuming [范書 銘] shumingf@realtek.com; Derek [方德義] derek.fang@realtek.com; Bard Liao yung-chuan.liao@linux.intel.com Subject: Re: [PATCH v2] ASoC: rt721-sdca: Add RT721 SDCA driver
External mail.
On 9/24/24 11:03, Jack Yu wrote:
This is the initial codec driver for rt721-sdca.
I wouldn't hurt to provide a short description. 722 has 3 functions, what about 721?
Okay, I'll add short description here in next v3-patch.
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC,
RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY,
RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_01):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY,
RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_02):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY,
RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_03):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY,
RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_04):
case SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
case SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
return true;
That tells us it has 3 functions (jack, mic, amp), so what's different to RT722? could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
RT721 has 3 functions just like RT722, but it's still a different codec and from internal discussion, we think it's better to separate the driver for future code management.
+static int rt721_sdca_read_prop(struct sdw_slave *slave) {
struct sdw_slave_prop *prop = &slave->prop;
int nval;
int i, j;
u32 bit;
unsigned long addr;
struct sdw_dpn_prop *dpn;
sdw_slave_read_prop(slave);
I thought we agreed to use a helper to read only the number of lanes from platform firmware?
Bard, did you share this already?
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->paging_support = true;
/*
* port = 1 for headphone playback
* port = 2 for headset-mic capture
* port = 3 for speaker playback
* port = 6 for digital-mic capture
*/
prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
nval = hweight32(prop->source_ports);
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->src_dpn_prop), GFP_KERNEL);
if (!prop->src_dpn_prop)
return -ENOMEM;
i = 0;
dpn = prop->src_dpn_prop;
addr = prop->source_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].type = SDW_DPN_FULL;
dpn[i].simple_ch_prep_sm = true;
dpn[i].ch_prep_timeout = 10;
i++;
}
/* do this again for sink now */
nval = hweight32(prop->sink_ports);
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
if (!prop->sink_dpn_prop)
return -ENOMEM;
j = 0;
dpn = prop->sink_dpn_prop;
addr = prop->sink_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[j].num = bit;
dpn[j].type = SDW_DPN_FULL;
dpn[j].simple_ch_prep_sm = true;
dpn[j].ch_prep_timeout = 10;
j++;
}
/* set the timeout values */
prop->clk_stop_timeout = 200;
/* wake-up event */
prop->wake_capable = 1;
/* Three data lanes are supported by rt721-sdca codec */
prop->lane_control_support = true;
this doesn't seem needed if we already read information on lane support.
It seems we still need it to be set as latest upsteam code still need it. If the upstream code is updated regarding to this, we'll do the modification to it.
+static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721) {
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* Set AD07/08 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL9, 0x2a2a);
/* Set AD10 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL10, 0x2a00);
/* Set DMIC1/DMIC2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL6, 0x2a2a);
/* Set DMIC1/DMIC2 IT entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL5, 0x2626);
/* Set AD10 FU entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL8, 0x1e00);
/* Set DMIC1/DMIC2 FU input gain floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL7, 0x1515);
/* Set DMIC2 FU input gain channel floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL3, 0x0304);
/* Set AD10 FU channel floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL4, 0x0304);
/* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_HDA_LEGACY_CTL1, 0x0000);
/* Enable vf707_r12_05/vf707_r13_05 */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY,
RT721_SDCA_ENT_IT26,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
/* Set usd_flag_sel, usd_in_sel */
regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
/* Set RC calibration */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* Fine tune PDE2A latency */
regmap_write(rt721->regmap, 0x2f5c, 0x25); }
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
It seems there is no api or function to support blind write from ACPI from latest upstream code, and we think it's easier for us to manage the different function's blind write.
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) {;
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* Reset dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
/* Turn back to normal dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
/* W1C Trigger Calibration */
regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
/* DAC04 FU entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL6, 0x5561);
/* Set EAPD high */
rt721_sdca_index_write(rt721, RT721_VENDOR_REG,
RT721_GPIO_PAD_CTRL5, 0x8003);
/* Enable vf707_r14 */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_OT23,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
/* FU 23 SPK mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 23 SPK mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
/* FU 55 DAC04 mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 55 DAC04 mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); }
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) {
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* GE0 mode related control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_GE_REL_CTRL1, 0x8011);
/* Button A, B, C, D bypass mode */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL3, 0xcf00);
/* HID1 slot enable */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL4, 0x000f);
/* Report ID for HID1 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL1, 0x1100);
/* OSC/OOC for slot 2, 3 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL5, 0x0c12);
/* Set JD de-bounce clock control */
rt721_sdca_index_write(rt721, RT721_JD_CTRL,
RT721_JD_1PIN_GAT_CTRL2, 0xc002);
/* RC calibration -1 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
/* RC calibration -2 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* pow_clk_12p288mhz_dre03 change to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3333);
/* Tune calibration timing control */
regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
/* calibration HP amp output select control from Efuse */
regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
/* FSM related control */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL1, 0x0140);
/* HP calibration related control */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
/* W1C HP calibration*/
regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
/* reg_sel_cin_hp_0010/0011 */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL18, 0x5522);
regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
/* sel_sensing_lr_hp */
regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
/* Release HP-JD */
rt721_sdca_index_write(rt721, RT721_CBJ_CTRL,
RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
/* en_osw gating auto done bit */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL4, 0xa105);
/* pow_clk_en_sw_amp_detect_sel to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3b33);
/* cp_sw_hp to auto mode */
regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
/* pow_clk_en_sw_amp_detect power up */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3f33);
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON13, 0x6048);
/* switch size detect threshold */
regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
/* en_hp_amp_detect auto mode */
regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
/* amp detect threshold */
regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
/* gating_sdw_link_rst_n_1_cbj_reg */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON17, 0x0008);
/* CKXEN_SDAC chopper function */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL3, 0x55ff);
/* CKXSEL_SDAC chopper frequency */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL4, 0xcc00);
/* Bias current for SDAC */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_MBIAS_LV_CTRL2, 0x6677);
/* VREF2 level selection */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF2_LV_CTRL1, 0x7600);
/* ADC09/MIC2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL2, 0x1234);
/* LINE2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL3, 0x3512);
/* DAC03/HP power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL1, 0x4040);
/* ADC27 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL4, 0x1201);
/* Fine tune PDE40 latency */
regmap_write(rt721->regmap, 0x2f58, 0x07); }
same here, shouldn't this come from ACPI/blind write tables?
Same reply as above one.
+enum rt721_sdca_jd_src {
RT721_JD_NULL,
RT721_JD1,
RT721_JD2,
+};
is this supported in SDCA? I can't recall seeing this for other drivers.
I'll remove this in next patch.
On Tue, Sep 24, 2024 at 11:54:25AM +0000, Jack Yu wrote:
That tells us it has 3 functions (jack, mic, amp), so what's different to RT722? could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
RT721 has 3 functions just like RT722, but it's still a different codec and from internal discussion, we think it's better to separate the driver for future code management.
As I mentioned with the events it's possible there's some room for factoring out some of the code for the common bits that are shared between multiple devices. Look at how Cirrus' Arizona drivers for example, each chip has specific support in a separate driver but there's a lot of shared code.
/* Set RC calibration */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* Fine tune PDE2A latency */
regmap_write(rt721->regmap, 0x2f5c, 0x25); }
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
It seems there is no api or function to support blind write from ACPI from latest upstream code, and we think it's easier for us to manage the different function's blind write.
It's always possible for you to add shared code for things like parsing ACPI tables (any references to the spec for blind writes here?).
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) {;
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* Reset dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
/* Turn back to normal dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
/* W1C Trigger Calibration */
regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
/* DAC04 FU entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL6, 0x5561);
/* Set EAPD high */
rt721_sdca_index_write(rt721, RT721_VENDOR_REG,
RT721_GPIO_PAD_CTRL5, 0x8003);
/* Enable vf707_r14 */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_OT23,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
/* FU 23 SPK mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 23 SPK mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
/* FU 55 DAC04 mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 55 DAC04 mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); }
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) {
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* GE0 mode related control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_GE_REL_CTRL1, 0x8011);
/* Button A, B, C, D bypass mode */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL3, 0xcf00);
/* HID1 slot enable */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL4, 0x000f);
/* Report ID for HID1 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL1, 0x1100);
/* OSC/OOC for slot 2, 3 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL5, 0x0c12);
/* Set JD de-bounce clock control */
rt721_sdca_index_write(rt721, RT721_JD_CTRL,
RT721_JD_1PIN_GAT_CTRL2, 0xc002);
/* RC calibration -1 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
/* RC calibration -2 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* pow_clk_12p288mhz_dre03 change to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3333);
/* Tune calibration timing control */
regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
/* calibration HP amp output select control from Efuse */
regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
/* FSM related control */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL1, 0x0140);
/* HP calibration related control */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
/* W1C HP calibration*/
regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
/* reg_sel_cin_hp_0010/0011 */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL18, 0x5522);
regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
/* sel_sensing_lr_hp */
regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
/* Release HP-JD */
rt721_sdca_index_write(rt721, RT721_CBJ_CTRL,
RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
/* en_osw gating auto done bit */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL4, 0xa105);
/* pow_clk_en_sw_amp_detect_sel to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3b33);
/* cp_sw_hp to auto mode */
regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
/* pow_clk_en_sw_amp_detect power up */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3f33);
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON13, 0x6048);
/* switch size detect threshold */
regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
/* en_hp_amp_detect auto mode */
regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
/* amp detect threshold */
regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
/* gating_sdw_link_rst_n_1_cbj_reg */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON17, 0x0008);
/* CKXEN_SDAC chopper function */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL3, 0x55ff);
/* CKXSEL_SDAC chopper frequency */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL4, 0xcc00);
/* Bias current for SDAC */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_MBIAS_LV_CTRL2, 0x6677);
/* VREF2 level selection */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF2_LV_CTRL1, 0x7600);
/* ADC09/MIC2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL2, 0x1234);
/* LINE2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL3, 0x3512);
/* DAC03/HP power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL1, 0x4040);
/* ADC27 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL4, 0x1201);
/* Fine tune PDE40 latency */
regmap_write(rt721->regmap, 0x2f58, 0x07); }
same here, shouldn't this come from ACPI/blind write tables?
Same reply as above one.
+enum rt721_sdca_jd_src {
RT721_JD_NULL,
RT721_JD1,
RT721_JD2,
+};
is this supported in SDCA? I can't recall seeing this for other drivers.
I'll remove this in next patch.
That tells us it has 3 functions (jack, mic, amp), so what's different to RT722? could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
RT721 has 3 functions just like RT722, but it's still a different codec and from internal discussion, we think it's better to separate the driver for future code management.
As I mentioned with the events it's possible there's some room for factoring out some of the code for the common bits that are shared between multiple devices. Look at how Cirrus' Arizona drivers for example, each chip has specific support in a separate driver but there's a lot of shared code.
/* Set RC calibration */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* Fine tune PDE2A latency */
regmap_write(rt721->regmap, 0x2f5c, 0x25); }
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
It seems there is no api or function to support blind write from ACPI from latest upstream code, and we think it's easier for us to manage the different function's blind write.
The problem is that those blind writes are supposed to be sku-specific, so it's not great to encode a behavior in a generic codec driver.
It's my understanding that the Windows driver will rely on blind writes, it'd seem natural to use the same initialization parameters, no?
It's always possible for you to add shared code for things like parsing ACPI tables (any references to the spec for blind writes here?).
Yes, the code is already available in a prototype, see the initial code here: https://github.com/thesofproject/linux/pull/5010/commits/3e4ff84242dbb7774bb...
The goal was to add a sound/soc/sdca library to parse ACPI function information, extract initialization tables, deal with interrupts, create controls, etc.
That said, it's probably best if Bard and/or Charles take over since I won't be able to work on this short-term.
One of the big opens is how we deal with regmap. In theory each SDCA function is independent from others, but in practice they all share a common control channel and interrupt mechanism.
I initially thought we could have one regmap for each function, but Charles had a different idea that we should handle regmap at the device level. Both options have merits and issues, we didn't really reach a conclusion on this.
One of the other opens is also related to regmap, we now have two types of regmap for SoundWire (sdw- and sdw-mbq), but there are parts of ASoC where components are assumed to have a single regmap.
-----Original Message----- From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Sent: Tuesday, September 24, 2024 11:27 PM To: Mark Brown broonie@kernel.org; Jack Yu jack.yu@realtek.com Cc: lgirdwood@gmail.com; alsa-devel@alsa-project.org; lars@metafoo.de; Flove(HsinFu) flove@realtek.com; Oder Chiou oder_chiou@realtek.com; Shuming [范書銘] shumingf@realtek.com; Derek [方德義] derek.fang@realtek.com; Bard Liao yung-chuan.liao@linux.intel.com; Charles Keepax ckeepax@opensource.cirrus.com Subject: Re: [PATCH v2] ASoC: rt721-sdca: Add RT721 SDCA driver
External mail.
That tells us it has 3 functions (jack, mic, amp), so what's different to
RT722?
could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
RT721 has 3 functions just like RT722, but it's still a different codec and from internal discussion, we think it's better to separate the
driver for future code management.
As I mentioned with the events it's possible there's some room for factoring out some of the code for the common bits that are shared between multiple devices. Look at how Cirrus' Arizona drivers for example, each chip has specific support in a separate driver but there's a lot of shared code.
/* Set RC calibration */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* Fine tune PDE2A latency */
regmap_write(rt721->regmap, 0x2f5c, 0x25); }
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
It seems there is no api or function to support blind write from ACPI from latest upstream code, and we think it's easier for us to manage the
different function's blind write.
The problem is that those blind writes are supposed to be sku-specific, so it's not great to encode a behavior in a generic codec driver.
It's my understanding that the Windows driver will rely on blind writes, it'd seem natural to use the same initialization parameters, no?
It's always possible for you to add shared code for things like parsing ACPI tables (any references to the spec for blind writes here?).
Yes, the code is already available in a prototype, see the initial code here: https://github.com/thesofproject/linux/pull/5010/commits/3e4ff84242dbb777 4bbed785371d27c3afc10a96
Yes, I understand there is already a prototype in github, but it hasn't been merged into upstream and the customer requested Realtek to upstream the first version which already been verified. We'll sure follow this to do the blind write on later codec driver once this PR merged into upstream.
The goal was to add a sound/soc/sdca library to parse ACPI function information, extract initialization tables, deal with interrupts, create controls, etc.
That said, it's probably best if Bard and/or Charles take over since I won't be able to work on this short-term.
One of the big opens is how we deal with regmap. In theory each SDCA function is independent from others, but in practice they all share a common control channel and interrupt mechanism.
I initially thought we could have one regmap for each function, but Charles had a different idea that we should handle regmap at the device level. Both options have merits and issues, we didn't really reach a conclusion on this.
One of the other opens is also related to regmap, we now have two types of regmap for SoundWire (sdw- and sdw-mbq), but there are parts of ASoC where components are assumed to have a single regmap.
-----Original Message----- From: Mark Brown broonie@kernel.org Sent: Tuesday, September 24, 2024 8:11 PM To: Jack Yu jack.yu@realtek.com Cc: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com; lgirdwood@gmail.com; alsa-devel@alsa-project.org; lars@metafoo.de; Flove(HsinFu) flove@realtek.com; Oder Chiou oder_chiou@realtek.com; Shuming [范書銘] shumingf@realtek.com; Derek [方德義] derek.fang@realtek.com; Bard Liao yung-chuan.liao@linux.intel.com Subject: Re: [PATCH v2] ASoC: rt721-sdca: Add RT721 SDCA driver
On Tue, Sep 24, 2024 at 11:54:25AM +0000, Jack Yu wrote:
That tells us it has 3 functions (jack, mic, amp), so what's different to
RT722?
could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
RT721 has 3 functions just like RT722, but it's still a different codec and from internal discussion, we think it's better to separate the driver
for future code management.
As I mentioned with the events it's possible there's some room for factoring out some of the code for the common bits that are shared between multiple devices. Look at how Cirrus' Arizona drivers for example, each chip has specific support in a separate driver but there's a lot of shared code.
Ok, we'll do the first version of common code for Realtek soundwire codec driver.
/* Set RC calibration */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* Fine tune PDE2A latency */
regmap_write(rt721->regmap, 0x2f5c, 0x25); }
Humm, isn't all this supposed to be handled with blind writes? It seems odd to hard-code all this, no?
It seems there is no api or function to support blind write from ACPI from latest upstream code, and we think it's easier for us to manage the
different function's blind write.
It's always possible for you to add shared code for things like parsing ACPI tables (any references to the spec for blind writes here?).
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) {;
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* Reset dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
/* Turn back to normal dc_cal_top */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
/* W1C Trigger Calibration */
regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
/* DAC04 FU entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_CH_FLOAT_CTL6, 0x5561);
/* Set EAPD high */
rt721_sdca_index_write(rt721, RT721_VENDOR_REG,
RT721_GPIO_PAD_CTRL5, 0x8003);
/* Enable vf707_r14 */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_OT23,
RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
/* FU 23 SPK mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 23 SPK mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
/* FU 55 DAC04 mute control - L */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
/* FU 55 DAC04 mute control - R */
regmap_write(rt721->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP,
RT721_SDCA_ENT_FU55,
RT721_SDCA_CTL_FU_MUTE, CH_02),
0x00); }
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) {
/* Power down group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8000);
/* VREF_HV_EN_AUTO_FAST */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF1_HV_CTRL1, 0xe000);
/* Power up group1/2/3_mic_pdb */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_MISC_POWER_CTL31, 0x8007);
/* GE0 mode related control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_GE_REL_CTRL1, 0x8011);
/* Button A, B, C, D bypass mode */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL3, 0xcf00);
/* HID1 slot enable */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL4, 0x000f);
/* Report ID for HID1 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL1, 0x1100);
/* OSC/OOC for slot 2, 3 */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_UMP_HID_CTRL5, 0x0c12);
/* Set JD de-bounce clock control */
rt721_sdca_index_write(rt721, RT721_JD_CTRL,
RT721_JD_1PIN_GAT_CTRL2, 0xc002);
/* RC calibration -1 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b00);
/* RC calibration -2 */
rt721_sdca_index_write(rt721, RT721_RC_CALIB_CTRL,
RT721_RC_CALIB_CTRL0, 0x0b40);
/* pow_clk_12p288mhz_dre03 change to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3333);
/* Tune calibration timing control */
regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
/* calibration HP amp output select control from Efuse */
regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
/* FSM related control */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL1, 0x0140);
/* HP calibration related control */
regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
/* W1C HP calibration*/
regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
/* reg_sel_cin_hp_0010/0011 */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL18, 0x5522);
regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
/* sel_sensing_lr_hp */
regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
/* Release HP-JD */
rt721_sdca_index_write(rt721, RT721_CBJ_CTRL,
RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
/* en_osw gating auto done bit */
rt721_sdca_index_write(rt721, RT721_CAP_PORT_CTRL,
RT721_HP_AMP_2CH_CAL4, 0xa105);
/* pow_clk_en_sw_amp_detect_sel to register mode */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3b33);
/* cp_sw_hp to auto mode */
regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
/* pow_clk_en_sw_amp_detect power up */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON14, 0x3f33);
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON13, 0x6048);
/* switch size detect threshold */
regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
/* en_hp_amp_detect auto mode */
regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
/* amp detect threshold */
regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
/* gating_sdw_link_rst_n_1_cbj_reg */
rt721_sdca_index_write(rt721, RT721_VENDOR_ANA_CTL,
RT721_UAJ_TOP_TCON17, 0x0008);
/* CKXEN_SDAC chopper function */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL3, 0x55ff);
/* CKXSEL_SDAC chopper frequency */
rt721_sdca_index_write(rt721, RT721_DAC_CTRL,
RT721_DAC_2CH_CTRL4, 0xcc00);
/* Bias current for SDAC */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_MBIAS_LV_CTRL2, 0x6677);
/* VREF2 level selection */
rt721_sdca_index_write(rt721, RT721_ANA_POW_PART,
RT721_VREF2_LV_CTRL1, 0x7600);
/* ADC09/MIC2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL2, 0x1234);
/* LINE2 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL3, 0x3512);
/* DAC03/HP power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL1, 0x4040);
/* ADC27 power entity floating control */
rt721_sdca_index_write(rt721, RT721_HDA_SDCA_FLOAT,
RT721_ENT_FLOAT_CTL4, 0x1201);
/* Fine tune PDE40 latency */
regmap_write(rt721->regmap, 0x2f58, 0x07); }
same here, shouldn't this come from ACPI/blind write tables?
Same reply as above one.
+enum rt721_sdca_jd_src {
RT721_JD_NULL,
RT721_JD1,
RT721_JD2,
+};
is this supported in SDCA? I can't recall seeing this for other drivers.
I'll remove this in next patch.
On 9/24/2024 5:36 PM, Pierre-Louis Bossart wrote:
On 9/24/24 11:03, Jack Yu wrote:
This is the initial codec driver for rt721-sdca.
I wouldn't hurt to provide a short description. 722 has 3 functions, what about 721?
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_01):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_02):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_03):
- case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
CH_04):
- case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
- case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
return true;
That tells us it has 3 functions (jack, mic, amp), so what's different to RT722? could we have a single driver for both parts? A lot of this driver seems copy-pasted-renamed.
+static int rt721_sdca_read_prop(struct sdw_slave *slave) +{
- struct sdw_slave_prop *prop = &slave->prop;
- int nval;
- int i, j;
- u32 bit;
- unsigned long addr;
- struct sdw_dpn_prop *dpn;
- sdw_slave_read_prop(slave);
I thought we agreed to use a helper to read only the number of lanes from platform firmware?
Bard, did you share this already?
Not yet, that will be a part of multi-lane support.
participants (4)
-
Jack Yu
-
Liao, Bard
-
Mark Brown
-
Pierre-Louis Bossart