From 9466a08fb80a8b9b7f48ef5914ea4883c435a985 Mon Sep 17 00:00:00 2001 From: "Dylan.Wu" Date: Thu, 4 Dec 2025 16:32:18 +0800 Subject: [PATCH 1/4] ACPI: Add helper to check OEM_ID in ACPI IORT table adds a utility function acpi_check_oem_id() to detect whether the system firmware identifies a specific OEM_ID in the ACPI IORT table Signed-off-by: wenxue.ding Signed-off-by: Dylan.Wu" --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 52cc005cc39ac..a870e14af8dad 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2880,6 +2880,34 @@ static int arm_smmu_dev_disable_feature(struct device *dev, } } +/* + * Check OEM_ID with a specific OEM provider. + */ +#ifdef CONFIG_ACPI +static bool acpi_check_oem_id(struct device *dev, const char *oem_id) +{ + if (oem_id && has_acpi_companion(dev)) { + struct acpi_table_header *iort_table; + + acpi_status status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); + + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + const char *msg = acpi_format_exception(status); + + pr_warn("Failed to get table, %s\n", msg); + } + return 0; + } + + if (!strncmp(iort_table->oem_id, oem_id, 6)) + return 1; + + } + return 0; +} +#endif + /* * HiSilicon PCIe tune and trace device can be used to trace TLP headers on the * PCIe link and save the data to memory by DMA. The hardware is restricted to From 5dbc4608380f2ccead5d36710dec8ff7a7541cf7 Mon Sep 17 00:00:00 2001 From: "Dylan.Wu" Date: Thu, 4 Dec 2025 16:35:02 +0800 Subject: [PATCH 2/4] iommu: Add CIXTEK-specific DMA domain handling based on ACPI OEM_ID extends the PCI device domain selection logic by introducing a vendor-specific path for CIXTEK platforms. Signed-off-by: wenxue.ding Signed-off-by: Dylan.Wu" --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index a870e14af8dad..4d0b9243bd2d7 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2923,7 +2923,11 @@ static int arm_smmu_def_domain_type(struct device *dev) if (IS_HISI_PTT_DEVICE(pdev)) return IOMMU_DOMAIN_IDENTITY; - } + } else { + if (acpi_check_oem_id(dev, "CIXTEK")) + return IOMMU_DOMAIN_DMA; + } + return 0; } From da71ed200080d8857c5ed56a2de4d1f5fe24399c Mon Sep 17 00:00:00 2001 From: "Dylan.Wu" Date: Thu, 4 Dec 2025 17:43:08 +0800 Subject: [PATCH 3/4] Update defconfig and cdns3 USB driver settings updates align the USB controller settings with current system requirements and enhance compatibility and stability for USB gadget operations. Signed-off-by: Dylan.Wu" --- arch/arm64/configs/deepin_arm64_desktop_defconfig | 4 ++-- drivers/usb/cdns3/Kconfig | 2 +- drivers/usb/cdns3/cdnsp-gadget.c | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/configs/deepin_arm64_desktop_defconfig b/arch/arm64/configs/deepin_arm64_desktop_defconfig index 40c0c15a00528..1d364a968448f 100644 --- a/arch/arm64/configs/deepin_arm64_desktop_defconfig +++ b/arch/arm64/configs/deepin_arm64_desktop_defconfig @@ -3606,7 +3606,7 @@ CONFIG_USBIP_HOST=m CONFIG_USBIP_VUDC=m CONFIG_USB_CDNS_SUPPORT=y CONFIG_USB_CDNS3=y -CONFIG_USB_CDNSP_CIX=y +CONFIG_USB_CDNSP_CIX=m CONFIG_USB_CDNS3_GADGET=y CONFIG_USB_CDNS3_HOST=y # CONFIG_USB_CDNS3_PCI_WRAP is not set @@ -3753,7 +3753,7 @@ CONFIG_TYPEC_RT1719=m CONFIG_TYPEC_HD3SS3220=m CONFIG_TYPEC_STUSB160X=m CONFIG_TYPEC_WUSB3801=m -CONFIG_TYPEC_RTS5453=y +CONFIG_TYPEC_RTS5453=m CONFIG_TYPEC_MUX_FSA4480=m CONFIG_TYPEC_MUX_GPIO_SBU=m CONFIG_TYPEC_MUX_PI3USB30532=m diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 872def3e5950a..8889af3fd9e4e 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -24,7 +24,7 @@ config USB_CDNS3 as module, the module will be called cdns3.ko. config USB_CDNSP_CIX - bool "Cadence USBSSP Dual-Role Controller for Cix platform" + tristate "Cadence USBSSP Dual-Role Controller for Cix platform" depends on USB_CDNS_SUPPORT depends on ARM64 || COMPILE_TEST help diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 38e693cd3efc0..3b4f0da1d027b 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -2071,3 +2071,4 @@ int cdnsp_gadget_init(struct cdns *cdns) return 0; } +EXPORT_SYMBOL(cdnsp_gadget_init); From 4abada2b038eff068f809083b8fcee1272da9b74 Mon Sep 17 00:00:00 2001 From: "Zichar.Zhang" Date: Thu, 27 Nov 2025 17:27:33 +0800 Subject: [PATCH 4/4] CIX: update hda driver and put it into hda directory aligns the driver with the existing HDA subsystem layout, improves code structure, and simplifies future maintenance Signed-off-by: Zichar.Zhang Signed-off-by: Dylan.Wu" --- .../configs/deepin_arm64_desktop_defconfig | 3 +- drivers/soc/cix/acpi/acpi_resource_lookup.c | 3 + include/sound/hda_codec.h | 1 + include/sound/hdaudio.h | 6 + sound/hda/hdac_controller.c | 25 +- sound/hda/hdac_device.c | 1 + sound/hda/hdac_stream.c | 17 +- sound/pci/hda/Kconfig | 13 + sound/pci/hda/Makefile | 2 + sound/pci/hda/hda_cix_ipbloq.c | 900 ++++++++++++++++++ sound/pci/hda/hda_codec.c | 23 +- sound/pci/hda/hda_controller.c | 7 +- sound/pci/hda/patch_realtek.c | 14 + sound/soc/cix/Kconfig | 17 - sound/soc/cix/Makefile | 4 - sound/soc/cix/hda_pcm.c | 383 -------- sound/soc/cix/hda_pcm.h | 38 - sound/soc/cix/hdac.c | 345 ------- sound/soc/cix/hdac.h | 138 --- sound/soc/cix/hdacodec.c | 356 ------- sound/soc/cix/hdacodec.h | 91 -- sound/soc/cix/ipb_hda.c | 489 ---------- sound/soc/cix/patch_realtek.c | 755 --------------- 23 files changed, 1002 insertions(+), 2629 deletions(-) create mode 100644 sound/pci/hda/hda_cix_ipbloq.c delete mode 100644 sound/soc/cix/hda_pcm.c delete mode 100644 sound/soc/cix/hda_pcm.h delete mode 100644 sound/soc/cix/hdac.c delete mode 100644 sound/soc/cix/hdac.h delete mode 100644 sound/soc/cix/hdacodec.c delete mode 100644 sound/soc/cix/hdacodec.h delete mode 100644 sound/soc/cix/ipb_hda.c delete mode 100644 sound/soc/cix/patch_realtek.c diff --git a/arch/arm64/configs/deepin_arm64_desktop_defconfig b/arch/arm64/configs/deepin_arm64_desktop_defconfig index 1d364a968448f..cc1b196224cf7 100644 --- a/arch/arm64/configs/deepin_arm64_desktop_defconfig +++ b/arch/arm64/configs/deepin_arm64_desktop_defconfig @@ -3267,6 +3267,7 @@ CONFIG_SND_VX222=m CONFIG_SND_YMFPCI=m CONFIG_SND_HDA_INTEL=y CONFIG_SND_HDA_PHYTIUM=m +CONFIG_SND_HDA_CIX_IPBLOQ=m CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y @@ -3310,8 +3311,6 @@ CONFIG_SND_ATMEL_SOC=m CONFIG_SND_SOC_CIX=m CONFIG_SND_SOC_CDNS_I2S_SC_CIX=m CONFIG_SND_SOC_CDNS_I2S_MC_CIX=m -CONFIG_SND_SOC_IPBLOQ_HDA_CIX=m -CONFIG_SND_HDACODEC_REALTEK_CIX=m CONFIG_SND_DESIGNWARE_I2S=m CONFIG_SND_DESIGNWARE_PCM=y CONFIG_SND_SOC_FSL_ASRC=m diff --git a/drivers/soc/cix/acpi/acpi_resource_lookup.c b/drivers/soc/cix/acpi/acpi_resource_lookup.c index 63ba85e82fa17..56ac53eba6e32 100644 --- a/drivers/soc/cix/acpi/acpi_resource_lookup.c +++ b/drivers/soc/cix/acpi/acpi_resource_lookup.c @@ -174,8 +174,11 @@ static int reset_lookup_handle(const union acpi_object *obj, void *data) static int rmem_dev_set_dma(struct device *dev, phys_addr_t base, size_t size) { +dev_err(dev, "==== dev[%s], dev->dma[%s]", dev ? "1": "NULL", dev->dma_mem ? "1": "NULL"); if (!dev || dev->dma_mem) return -EINVAL; +dev_err(dev, "==== memblock_is_region_memory[%d], memblock_is_map_memory[%d]", + memblock_is_region_memory(base, size), memblock_is_map_memory(base)); if (!memblock_is_region_memory(base, size) || memblock_is_map_memory(base)) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 36b063775447a..948cc2b6b7b6a 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -258,6 +258,7 @@ struct hda_codec { unsigned int forced_resume:1; /* forced resume for jack */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ + unsigned int reconfig_init_verbs:1; /* need reconfig init verbs when system resume */ #ifdef CONFIG_PM unsigned long power_on_acct; diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 7e4ede31259b2..8c47583fb16c2 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -292,6 +292,12 @@ struct hdac_bus { const struct hdac_bus_ops *ops; const struct hdac_ext_bus_ops *ext_ops; + /* address translation from host to hdac */ + dma_addr_t (*addr_host_to_hdac)(struct hdac_bus *bus, dma_addr_t addr); + + /* configure init verbs */ + int (*config_init_verbs)(struct hdac_bus *bus, unsigned int vendor_id); + /* h/w resources */ unsigned long addr; void __iomem *remap_addr; diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 3bb2969e4e334..a3001cb4cbcde 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -43,13 +43,17 @@ static void azx_clear_corbrp(struct hdac_bus *bus) void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) { WARN_ON_ONCE(!bus->rb.area); + dma_addr_t corb_addr, rirb_addr; spin_lock_irq(&bus->reg_lock); /* CORB set up */ bus->corb.addr = bus->rb.addr; bus->corb.buf = (__le32 *)bus->rb.area; - snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); - snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); + corb_addr = bus->corb.addr; + if (bus->addr_host_to_hdac) + corb_addr = bus->addr_host_to_hdac(bus, bus->corb.addr); + snd_hdac_chip_writel(bus, CORBLBASE, (u32)corb_addr); + snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(corb_addr)); /* set the corb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); @@ -69,8 +73,11 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); bus->rirb.wp = bus->rirb.rp = 0; memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); - snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); - snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); + rirb_addr = bus->rirb.addr; + if (bus->addr_host_to_hdac) + rirb_addr = bus->addr_host_to_hdac(bus, bus->rirb.addr); + snd_hdac_chip_writel(bus, RIRBLBASE, (u32)rirb_addr); + snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(rirb_addr)); /* set the rirb size to 256 entries (ULI requires explicitly) */ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); @@ -552,6 +559,8 @@ static void azx_int_clear(struct hdac_bus *bus) */ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) { + dma_addr_t posbuf_addr; + if (bus->chip_init) return false; @@ -569,8 +578,12 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) /* program the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { - snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); - snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); + posbuf_addr = bus->posbuf.addr; + if (bus->addr_host_to_hdac) + posbuf_addr = bus->addr_host_to_hdac(bus, bus->posbuf.addr); + + snd_hdac_chip_writel(bus, DPLBASE, (u32)posbuf_addr); + snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(posbuf_addr)); } bus->chip_init = true; diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 0a9223c18d77c..6ea3977e8cfbd 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -50,6 +50,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, dev->bus = &snd_hda_bus_type; dev->release = default_release; dev->groups = hdac_dev_attr_groups; + dev_set_uevent_suppress(dev, 1); dev_set_name(dev, "%s", name); device_enable_async_suspend(dev); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 2312266939b21..484fd24cc80ad 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -278,6 +278,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_runtime *runtime; + dma_addr_t bdl_addr, posbuf_addr; unsigned int val; if (azx_dev->substream) @@ -305,17 +306,24 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev) snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1); /* program the BDL address */ + bdl_addr = azx_dev->bdl.addr; + if (bus->addr_host_to_hdac) + bdl_addr = bus->addr_host_to_hdac(bus, azx_dev->bdl.addr); /* lower BDL address */ - snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)bdl_addr); /* upper BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->bdl.addr)); + upper_32_bits(bdl_addr)); /* enable the position buffer */ if (bus->use_posbuf && bus->posbuf.addr) { + posbuf_addr = bus->posbuf.addr; + if (bus->addr_host_to_hdac) + posbuf_addr = bus->addr_host_to_hdac(bus, bus->posbuf.addr); + if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE)) snd_hdac_chip_writel(bus, DPLBASE, - (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE); + (u32)posbuf_addr| AZX_DPLBASE_ENABLE); } /* set the interrupt enable bits in the descriptor control register */ @@ -475,6 +483,9 @@ static int setup_bdle(struct hdac_bus *bus, return -EINVAL; addr = snd_sgbuf_get_addr(dmab, ofs); + if (bus->addr_host_to_hdac) + addr = bus->addr_host_to_hdac(bus, addr); + /* program the address field of the BDL entry */ bdl[0] = cpu_to_le32((u32)addr); bdl[1] = cpu_to_le32(upper_32_bits(addr)); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 2f8c8ed575315..314534368aaa7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -58,6 +58,19 @@ config SND_HDA_TEGRA To compile this driver as a module, choose M here: the module will be called snd-hda-tegra. +config SND_HDA_CIX_IPBLOQ + tristate "CIX IPBLOQ HD Audio" + select SND_HDA + select SND_HDA_ALIGNED_MMIO + help + Say Y here to support the HDA controller present in CIX SoCs + + This options enables support for the HD Audio controller + present in some CIX SoCs. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-cix-ipbloq. + if SND_HDA config SND_HDA_HWDEP diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 5671283da45d7..0b02d3640669c 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,6 +2,7 @@ snd-hda-intel-objs := hda_intel.o snd-hda-tegra-objs := hda_tegra.o snd-hda-phytium-objs := hda_phytium.o +snd-hda-cix-ipbloq-objs := hda_cix_ipbloq.o snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o snd-hda-codec-y += hda_controller.o @@ -72,3 +73,4 @@ obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o obj-$(CONFIG_SND_HDA_PHYTIUM) += snd-hda-phytium.o +obj-$(CONFIG_SND_HDA_CIX_IPBLOQ) += snd-hda-cix-ipbloq.o diff --git a/sound/pci/hda/hda_cix_ipbloq.c b/sound/pci/hda/hda_cix_ipbloq.c new file mode 100644 index 0000000000000..e1c5daa56749f --- /dev/null +++ b/sound/pci/hda/hda_cix_ipbloq.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2024 Cix Technology Group Co., Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "hda_controller.h" + +#define CIX_IPBLOQ_ADDR_HOST_TO_HDAC_OFFSET 0x90000000 + +#define CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS 1000 +#define CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS 100 + +struct cix_ipbloq_hda { + struct azx chip; + struct device *dev; + void __iomem *regs; + + struct reset_control_bulk_data resets[1]; + struct clk_bulk_data clocks[2]; + unsigned int nresets; + unsigned int nclocks; + + struct work_struct probe_work; + + struct gpio_desc *pdb_gpiod; + struct gpio_desc *depop_mute_gpiod; + + const char *sname; +}; + +static const struct hda_controller_ops cix_ipbloq_hda_ops; + +/* alc256 cix evb init verb table */ +static unsigned int alc256_cix_evb_init_verbs[] = { + /* Realtek High Definition Audio Configuration - Version : 5.0.3.3 + * Realtek HD Audio Codec : ALC256 + * PCI PnP ID : PCI\VEN_8086&DEV_2668&SUBSYS_129E10EC + * HDA Codec PnP ID : HDAUDIO\FUNC_01&VEN_10EC&DEV_0256&SUBSYS_10EC129E + * The number of verb command block : 16 + * + * NID 0x12 : 0x90A60130 + * NID 0x13 : 0x40000000 + * NID 0x14 : 0x90170110 + * NID 0x18 : 0x411111F0 + * NID 0x19 : 0x04A11040 + * NID 0x1A : 0x411111F0 + * NID 0x1B : 0x411111F0 + * NID 0x1D : 0x4068996D + * NID 0x1E : 0x411111F0 + * NID 0x21 : 0x04211020 + */ + + /* ==== HDA Codec Subsystem ID Verb-table ===== */ + /* HDA Codec Subsystem ID : 0x10EC129E */ + 0x0017209E, + 0x00172112, + 0x001722EC, + 0x00172310, + + /* ==== Pin Widget Verb-table ===== */ + /* Widget node 0x01 */ + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + /* 1bit reset */ + 0x0205001A, + 0x0204C00B, + 0x0205001A, + 0x0204800B, + /* Pin widget 0x12 - DMIC */ + 0x01271C30, + 0x01271D01, + 0x01271EA6, + 0x01271F90, + /* Pin widget 0x13 - DMIC */ + 0x01371C00, + 0x01371D00, + 0x01371E00, + 0x01371F40, + /* Pin widget 0x14 - Front (Port-D) */ + 0x01471C10, + 0x01471D01, + 0x01471E17, + 0x01471F90, + /* Pin widget 0x18 - NPC */ + 0x01871CF0, + 0x01871D11, + 0x01871E11, + 0x01871F41, + /* Pin widget 0x19 - MIC2 (Port-F) */ + 0x01971C40, + 0x01971D10, + 0x01971EA1, + 0x01971F04, + /* Pin widget 0x1A - LINE1 (Port-C) */ + 0x01A71CF0, + 0x01A71D11, + 0x01A71E11, + 0x01A71F41, + /* Pin widget 0x1B - LINE2 (Port-E) */ + 0x01B71CF0, + 0x01B71D11, + 0x01B71E11, + 0x01B71F41, + /* Pin widget 0x1D - BEEP-IN */ + 0x01D71C6D, + 0x01D71D99, + 0x01D71E68, + 0x01D71F40, + /* Pin widget 0x1E - S/PDIF-OUT */ + 0x01E71CF0, + 0x01E71D11, + 0x01E71E11, + 0x01E71F41, + /* Pin widget 0x21 - HP1-OUT (Port-I) */ + 0x02171C20, + 0x02171D10, + 0x02171E21, + 0x02171F04, + + 0x02050010, + 0x02040020, + 0x02050038, + 0x02046981, + + 0x02050008, + 0x02046A6C, + 0x0205001B, + 0x02040A4B, + + 0x0205003C, + 0x02040354, + 0x0205003C, + 0x02040314, + + 0x02050046, + 0x02040004, + 0x05750003, + 0x057409A3, +}; + +/* alc256 cix orion o6 init verb table */ +static unsigned int alc256_cix_orion_o6_init_verbs[] = { + /* Realtek High Definition Audio Configuration - Version : 5.0.3.3 + * Realtek HD Audio Codec : ALC256 + * PCI PnP ID : PCI\VEN_8086&DEV_2668&SUBSYS_129E10EC + * HDA Codec PnP ID : HDAUDIO\FUNC_01&VEN_10EC&DEV_0256&SUBSYS_10EC129E + * The number of verb command block : 16 + * + * NID 0x12 : 0x40000000 + * NID 0x13 : 0x411111F0 + * NID 0x14 : 0x90170110 + * NID 0x18 : 0x411111F0 + * NID 0x19 : 0x01A11030 + * NID 0x1A : 0x02A19040 + * NID 0x1B : 0x02014020 + * NID 0x1D : 0x4045C069 + * NID 0x1E : 0x411111F0 + * NID 0x21 : 0x0121101F + */ + + /* ==== HDA Codec Subsystem ID Verb-table ===== */ + /* HDA Codec Subsystem ID : 0x10EC129E */ + 0x0017209E, + 0x00172112, + 0x001722EC, + 0x00172310, + + /* ==== Pin Widget Verb-table ===== */ + /* Widget node 0x01 */ + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + /* 1bit reset */ + 0x0205001A, + 0x0204C00B, + 0x0205001A, + 0x0204800B, + /* Pin widget 0x12 - DMIC */ + 0x01271C00, + 0x01271D00, + 0x01271E00, + 0x01271F40, + /* Pin widget 0x13 - DMIC */ + 0x01371CF0, + 0x01371D11, + 0x01371E11, + 0x01371F41, + /* Pin widget 0x14 - Front (Port-D) */ + 0x01471C10, + 0x01471D01, + 0x01471E17, + 0x01471F90, + /* Pin widget 0x18 - NPC */ + 0x01871CF0, + 0x01871D11, + 0x01871E11, + 0x01871F41, + /* Pin widget 0x19 - MIC2 (Port-F) */ + 0x01971C30, + 0x01971D10, + 0x01971EA1, + 0x01971F01, + /* Pin widget 0x1A - LINE1 (Port-C) */ + 0x01A71C40, + 0x01A71D90, + 0x01A71EA1, + 0x01A71F02, + /* Pin widget 0x1B - LINE2 (Port-E) */ + 0x01B71C20, + 0x01B71D40, + 0x01B71E01, + 0x01B71F02, + /* Pin widget 0x1D - BEEP-IN */ + 0x01D71C69, + 0x01D71DC0, + 0x01D71E45, + 0x01D71F40, + /* Pin widget 0x1E - S/PDIF-OUT */ + 0x01E71CF0, + 0x01E71D11, + 0x01E71E11, + 0x01E71F41, + /* Pin widget 0x21 - HP1-OUT (Port-I) */ + 0x02171C1F, + 0x02171D10, + 0x02171E21, + 0x02171F01, + + 0x02050010, + 0x02040020, + 0x02050038, + 0x02046981, + + 0x02050008, + 0x02046A4C, + 0x0205001B, + 0x02040A4B, + + 0x0205003C, + 0x02040354, + 0x0205003C, + 0x02040314, + + 0x02050046, + 0x02040004, + 0x05750003, + 0x057409A3, +}; + +/* alc269 cix orapi 6p init verb table */ +static unsigned int alc269_cix_orapi_6p_init_verbs[] = { + /* Realtek High Definition Audio Configuration - Version : 5.0.3.3 + * Realtek HD Audio Codec : ALC269-VC3 + * PCI PnP ID : PCI\VEN_8086&DEV_2668&SUBSYS_129E10EC + * HDA Codec PnP ID : HDAUDIO\FUNC_01&VEN_10EC&DEV_0269&SUBSYS_10EC129E + * The number of verb command block : 17 + * + * NID 0x12 : 0x40000000 + * NID 0x14 : 0x90170110 + * NID 0x15 : 0x0421101F + * NID 0x17 : 0x411111F0 + * NID 0x18 : 0x04A11020 + * NID 0x19 : 0x90A7012F + * NID 0x1A : 0x411111F0 + * NID 0x1B : 0x411111F0 + * NID 0x1D : 0x40538205 + * NID 0x1E : 0x411111F0 + * NID 0x20 : 0x0000FFFF + */ + + /* ==== HDA Codec Subsystem ID Verb-table ===== */ + /* HDA Codec Subsystem ID : 0x10EC129E */ + 0x0017209E, + 0x00172112, + 0x001722EC, + 0x00172310, + + /* ==== Pin Widget Verb-table ===== */ + /* Widget node 0x01 */ + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + 0x0017FF00, + /* Pin widget 0x12 - DMIC */ + 0x01271C00, + 0x01271D00, + 0x01271E00, + 0x01271F40, + /* Pin widget 0x14 - SPEAKER-OUT (Port-D) */ + 0x01471C10, + 0x01471D01, + 0x01471E17, + 0x01471F90, + /* Pin widget 0x15 - HP-OUT (Port-A) */ + 0x01571C1F, + 0x01571D10, + 0x01571E21, + 0x01571F04, + /* Pin widget 0x17 - MONO-OUT (Port-H) */ + 0x01771CF0, + 0x01771D11, + 0x01771E11, + 0x01771F41, + /* Pin widget 0x18 - MIC1 (Port-B) */ + 0x01871C20, + 0x01871D10, + 0x01871EA1, + 0x01871F04, + /* Pin widget 0x19 - MIC2 (Port-F) */ + 0x01971C2F, + 0x01971D01, + 0x01971EA7, + 0x01971F90, + /* Pin widget 0x1A - LINE1 (Port-C) */ + 0x01A71CF0, + 0x01A71D11, + 0x01A71E11, + 0x01A71F41, + /* Pin widget 0x1B - LINE2 (Port-E) */ + 0x01B71CF0, + 0x01B71D11, + 0x01B71E11, + 0x01B71F41, + /* Pin widget 0x1D - PC-BEEP */ + 0x01D71C05, + 0x01D71D82, + 0x01D71E53, + 0x01D71F40, + /* Pin widget 0x1E - S/PDIF-OUT */ + 0x01E71CF0, + 0x01E71D11, + 0x01E71E11, + 0x01E71F41, + /* Widget node 0x20 */ + 0x02050018, + 0x02040184, + 0x0205001C, + 0x02040800, + /* Widget node 0x20 - 1 */ + 0x02050024, + 0x02040000, + 0x02050004, + 0x02040080, + /* Widget node 0x20 - 2 */ + 0x02050008, + 0x02040300, + 0x0205000C, + 0x02043F00, + /* Widget node 0x20 - 3 */ + 0x02050015, + 0x02048002, + 0x02050015, + 0x02048002, + /* Widget node 0x0C */ + 0x00C37080, + 0x00270610, + 0x00D37080, + 0x00370610, +}; + +static dma_addr_t cix_ipbloq_hda_addr_host_to_hdac(struct hdac_bus *bus, dma_addr_t addr) +{ + dma_addr_t addr_adj; + + addr_adj = addr - CIX_IPBLOQ_ADDR_HOST_TO_HDAC_OFFSET; + dev_dbg(bus->dev, "addr = 0x%llx, addr_adj = 0x%llx\n", addr, addr_adj); + + return addr_adj; +} + +static int cix_ipbloq_hda_config_init_verbs(struct hdac_bus *bus, unsigned int vendor_id) +{ + struct snd_card *card = dev_get_drvdata(bus->dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + unsigned int *init_verbs, size = 0; + int i; + + dev_dbg(bus->dev, "vendor id = 0x%x\n", vendor_id); + + switch (vendor_id) { + case 0x10ec0256: + if (hda->sname && !strcmp(hda->sname, "CIX SKY1 EVB HDA")) { + dev_dbg(bus->dev, "CIX SKY1 EVB HDA\n"); + init_verbs = alc256_cix_evb_init_verbs; + size = ARRAY_SIZE(alc256_cix_evb_init_verbs); + } else if (hda->sname && !strcmp(hda->sname, "CIX SKY1 ORION O6 HDA")) { + dev_dbg(bus->dev, "CIX SKY1 ORION O6 HDA\n"); + init_verbs = alc256_cix_orion_o6_init_verbs; + size = ARRAY_SIZE(alc256_cix_orion_o6_init_verbs); + } + break; + case 0x10ec0269: + if (hda->sname && !strcmp(hda->sname, "CIX SKY1 ORAPI 6P HDA")) { + dev_dbg(bus->dev, "CIX SKY1 ORAPI 6P HDA\n"); + init_verbs = alc269_cix_orapi_6p_init_verbs; + size = ARRAY_SIZE(alc269_cix_orapi_6p_init_verbs); + } + break; + default: + dev_err(bus->dev, "unsupport codec chip\n"); + return -EINVAL; + } + + for (i = 0; i < size; i++) + bus->ops->command(bus, init_verbs[i]); + + return 0; +} + +static int cix_ipbloq_hda_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + + return 0; +} + +static int cix_ipbloq_hda_dev_free(struct snd_device *device) +{ + struct azx *chip = device->device_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + + cancel_work_sync(&hda->probe_work); + + if (azx_bus(chip)->chip_init) { + azx_stop_all_streams(chip); + azx_stop_chip(chip); + } + + azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(azx_bus(chip)); + + return 0; +} + +static int cix_ipbloq_hda_init_chip(struct azx *chip, struct platform_device *pdev) +{ + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + struct hdac_bus *bus = azx_bus(chip); + struct resource *res; + + hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(hda->regs)) { + dev_err(&pdev->dev, "failed to get and ioremap resource\n"); + return PTR_ERR(hda->regs); + } + + bus->remap_addr = hda->regs; + bus->addr = res->start; + + return 0; +} + +static int cix_ipbloq_hda_init(struct azx *chip, struct platform_device *pdev) +{ + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + struct hdac_bus *bus = azx_bus(chip); + struct snd_card *card = chip->card; + const char *sname = NULL, *drv_name = "cix-ipbloq-hda"; + unsigned short gcap; + int irq_id, err; + + err = cix_ipbloq_hda_init_chip(chip, pdev); + if (err) + return err; + + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) { + dev_err(&pdev->dev, "failed to get the irq\n"); + return irq_id; + } + + err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (err) { + dev_err(chip->card->dev, + "Unable to request IRQ %d, disabling device\n", + irq_id); + return err; + } + + bus->irq = irq_id; + bus->dma_stop_delay = 100; + card->sync_irq = bus->irq; + + gcap = azx_readw(chip, GCAP); + dev_info(card->dev, "chipset global capabilities = 0x%x\n", gcap); + + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) { + dev_err(card->dev, "failed to initialize streams: %d\n", err); + return err; + } + + err = azx_alloc_stream_pages(chip); + if (err < 0) { + dev_err(card->dev, "failed to allocate stream pages: %d\n", err); + return err; + } + + /* initialize chip */ + azx_init_chip(chip, 1); + + /* codec detection */ + if (!bus->codec_mask) { + dev_err(card->dev, "no codecs found\n"); + return -ENODEV; + } + dev_info(card->dev, "codec detection mask = 0x%lx\n", bus->codec_mask); + + /* driver name */ + strscpy(card->driver, drv_name, sizeof(card->driver)); + + /* shortname for card */ + device_property_read_string(&pdev->dev, "cix,model", &sname); + if (!sname) + sname = drv_name; + /* use to distinguish boards later when select verb table */ + hda->sname = sname; + + if (strlen(sname) > sizeof(card->shortname)) + dev_info(card->dev, "truncating shortname for card\n"); + strscpy(card->shortname, sname, sizeof(card->shortname)); + + /* longname for card */ + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, bus->addr, bus->irq); + + return 0; +} + +static void cix_ipbloq_hda_probe_work(struct work_struct *work) +{ + struct cix_ipbloq_hda *hda = container_of(work, struct cix_ipbloq_hda, probe_work); + struct platform_device *pdev = to_platform_device(hda->dev); + struct azx *chip = &hda->chip; + struct hdac_bus *bus = azx_bus(chip); + int err; + + pm_runtime_get_sync(hda->dev); + + to_hda_bus(bus)->bus_probing = 1; + + err = cix_ipbloq_hda_init(chip, pdev); + if (err < 0) + goto out_free; + + /* create codec instances */ + err = azx_probe_codecs(chip, 8); + if (err < 0) + goto out_free; + + err = azx_codec_configure(chip); + if (err < 0) + goto out_free; + + err = snd_card_register(chip->card); + if (err < 0) + goto out_free; + + chip->running = 1; + + to_hda_bus(bus)->bus_probing = 0; + + snd_hda_set_power_save(&chip->bus, CIX_IPBLOQ_POWER_SAVE_DEFAULT_TIME_MS); + + dev_info(hda->dev, "cix ipbloq hda probed\n"); + + out_free: + pm_runtime_put(hda->dev); + return; /* no error return from async probe */ +} + +static int cix_ipbloq_hda_create(struct snd_card *card, + unsigned int driver_caps, + struct cix_ipbloq_hda *hda) +{ + static const struct snd_device_ops ops = { + .dev_disconnect = cix_ipbloq_hda_dev_disconnect, + .dev_free = cix_ipbloq_hda_dev_free, + }; + struct azx *chip; + int err; + + chip = &hda->chip; + + mutex_init(&chip->open_mutex); + chip->card = card; + chip->ops = &cix_ipbloq_hda_ops; + chip->driver_caps = driver_caps; + chip->driver_type = driver_caps & 0xff; + chip->dev_index = 0; + chip->single_cmd = 0; + chip->codec_probe_mask = -1; + chip->align_buffer_size = 1; + chip->jackpoll_interval = msecs_to_jiffies(CIX_IPBLOQ_JACKPOLL_DEFAULT_TIME_MS); + INIT_LIST_HEAD(&chip->pcm_list); + + /* HD-audio controllers appear pretty inaccurate about the update-IRQ timing. + * The IRQ is issued before actually the data is processed. So use stream + * link position by default instead of dma position buffer. + */ + chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib; + + INIT_WORK(&hda->probe_work, cix_ipbloq_hda_probe_work); + + err = azx_bus_init(chip, NULL); + if (err < 0) { + dev_err(hda->dev, "failed to init bus, err = %d\n", err); + return err; + } + + /* RIRBSTS.RINTFL cannot be cleared, cause interrupt storm */ + chip->bus.core.polling_mode = 1; + chip->bus.core.not_use_interrupts = 1; + + chip->bus.core.aligned_mmio = 1; + chip->bus.jackpoll_in_suspend = 1; + + /* host and hdac has different memory view */ + chip->bus.core.addr_host_to_hdac = cix_ipbloq_hda_addr_host_to_hdac; + + /* config init verbs TODO: config from BIOS + */ + chip->bus.core.config_init_verbs = cix_ipbloq_hda_config_init_verbs; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + dev_err(card->dev, "failed to create device, err = %d\n", err); + return err; + } + + return 0; +} + +static int cix_ipbloq_hda_probe(struct platform_device *pdev) +{ + const unsigned int driver_flags = AZX_DCAPS_PM_RUNTIME; + struct snd_card *card; + struct azx *chip; + struct cix_ipbloq_hda *hda; + int err; + + hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); + if (!hda) { + dev_err(&pdev->dev, "failed to allocate memory for hda\n"); + return -ENOMEM; + } + hda->dev = &pdev->dev; + chip = &hda->chip; + + err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err < 0) { + dev_err(&pdev->dev, "failed to crate card, err = %d\n", err); + return err; + } + + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (!pdev->dev.dma_mem) { + /* + * if dev.dma_mem not allcated + * we should try to get it from dts + */ + err = of_reserved_mem_device_init(&pdev->dev); + if (err && err != -ENODEV) { + dev_err(&pdev->dev, + "failed to init reserved mem for DMA, err = %d\n", err); + goto out_free; + } + } + + hda->resets[hda->nresets++].id = "hda"; + err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets, + hda->resets); + if (err) { + dev_err(&pdev->dev, "failed to get reset, err = %d\n", err); + goto out_free; + } + + hda->clocks[hda->nclocks++].id = "sysclk"; + hda->clocks[hda->nclocks++].id = "clk48m"; + err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); + if (err < 0) { + dev_err(&pdev->dev, "failed to get clk, err = %d\n", err); + goto out_free; + } + + hda->pdb_gpiod = devm_gpiod_get_optional(&pdev->dev, "pdb", GPIOD_OUT_HIGH); + if (IS_ERR(hda->pdb_gpiod)) { + err = PTR_ERR(hda->pdb_gpiod); + dev_err(&pdev->dev, "failed to get pdb gpio, err: %d\n", err); + goto out_free; + } + msleep(20); + + hda->depop_mute_gpiod = devm_gpiod_get_optional(&pdev->dev, "depop-mute", GPIOD_OUT_HIGH); + if (IS_ERR(hda->depop_mute_gpiod)) { + err = PTR_ERR(hda->depop_mute_gpiod); + dev_err(&pdev->dev, "failed to get depop gpio, err: %d\n", err); + goto out_free; + } + gpiod_set_value_cansleep(hda->depop_mute_gpiod, 1); + + err = cix_ipbloq_hda_create(card, driver_flags, hda); + if (err < 0) + goto out_free; + card->private_data = chip; + + dev_set_drvdata(&pdev->dev, card); + + pm_runtime_enable(hda->dev); + if (!azx_has_pm_runtime(chip)) + pm_runtime_forbid(hda->dev); + + schedule_work(&hda->probe_work); + + return 0; + +out_free: + snd_card_free(card); + return err; +} + +static void cix_ipbloq_hda_remove(struct platform_device *pdev) +{ + snd_card_free(dev_get_drvdata(&pdev->dev)); + + pm_runtime_disable(&pdev->dev); +} + +static void cix_ipbloq_hda_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +static int __maybe_unused cix_ipbloq_hda_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + int rc; + + rc = pm_runtime_force_suspend(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + + gpiod_set_value_cansleep(hda->depop_mute_gpiod, 0); + + gpiod_set_value_cansleep(hda->pdb_gpiod, 0); + + return 0; +} + +static int __maybe_unused cix_ipbloq_hda_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + int rc; + + gpiod_set_value_cansleep(hda->pdb_gpiod, 1); + msleep(20); + + gpiod_set_value_cansleep(hda->depop_mute_gpiod, 1); + + rc = pm_runtime_force_resume(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + +static int __maybe_unused cix_ipbloq_hda_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + + dev_dbg(dev, "%s\n", __func__); + + if (chip && chip->running) { + azx_stop_chip(chip); + azx_enter_link_reset(chip); + } + + clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); + + return 0; +} + +static int __maybe_unused cix_ipbloq_hda_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct cix_ipbloq_hda *hda = container_of(chip, struct cix_ipbloq_hda, chip); + int rc; + + dev_dbg(dev, "%s\n", __func__); + + rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); + if (rc) { + dev_err(dev, "failed to enable clk bulk, rc: %d\n", rc); + return rc; + } + + rc = reset_control_bulk_assert(hda->nresets, hda->resets); + if (rc) { + dev_err(dev, "failed to assert reset bulk, rc: %d\n", rc); + return rc; + } + + usleep_range(10, 20); + + rc = reset_control_bulk_deassert(hda->nresets, hda->resets); + if (rc) { + dev_err(dev, "failed to deassert reset bulk, rc: %d\n", rc); + return rc; + } + + if (chip && chip->running) + azx_init_chip(chip, 1); + + return 0; +} + +static const struct dev_pm_ops cix_ipbloq_hda_pm = { + SET_SYSTEM_SLEEP_PM_OPS(cix_ipbloq_hda_suspend, + cix_ipbloq_hda_resume) + SET_RUNTIME_PM_OPS(cix_ipbloq_hda_runtime_suspend, + cix_ipbloq_hda_runtime_resume, NULL) +}; + +static const struct of_device_id cix_ipbloq_hda_match[] = { + { .compatible = "cix,sky1-ipbloq-hda", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, cix_ipbloq_hda_match); + +static const struct acpi_device_id cix_ipbloq_hda_acpi_match[] = { + { "CIXH6020" }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cix_ipbloq_hda_acpi_match); + +static struct platform_driver cix_ipbloq_hda_driver = { + .driver = { + .name = "cix-ipbloq-hda", + .pm = &cix_ipbloq_hda_pm, + .of_match_table = cix_ipbloq_hda_match, + .acpi_match_table = cix_ipbloq_hda_acpi_match, + }, + .probe = cix_ipbloq_hda_probe, + .remove_new = cix_ipbloq_hda_remove, + .shutdown = cix_ipbloq_hda_shutdown, +}; +module_platform_driver(cix_ipbloq_hda_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CIX IPBLOQ HDA bus driver"); +MODULE_AUTHOR("Joakim Zhang "); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 068814dad3638..bdc2940657722 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2919,6 +2919,10 @@ static void hda_call_codec_resume(struct hda_codec *codec) codec->power_jiffies = jiffies; hda_set_power_state(codec, AC_PWRST_D0); + if (codec->reconfig_init_verbs && codec->bus->core.config_init_verbs) { + codec->bus->core.config_init_verbs(&codec->bus->core, codec->core.vendor_id); + codec->reconfig_init_verbs = 0; + } restore_shutup_pins(codec); hda_exec_init_verbs(codec); snd_hda_jack_set_dirty_all(codec); @@ -2972,6 +2976,15 @@ static int hda_codec_runtime_resume(struct device *dev) return 0; } +static int hda_codec_runtime_idle(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend) + return -EBUSY; + return 0; +} + #endif /* CONFIG_PM */ #ifdef CONFIG_PM_SLEEP @@ -2988,6 +3001,9 @@ static void hda_codec_pm_complete(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + if (!codec->reconfig_init_verbs && codec->bus->core.config_init_verbs) + codec->reconfig_init_verbs = 1; + /* If no other pm-functions are called between prepare() and complete() */ if (dev->power.power_state.event == PM_EVENT_SUSPEND) dev->power.power_state = PMSG_RESUME; @@ -3005,6 +3021,11 @@ static int hda_codec_pm_suspend(struct device *dev) static int hda_codec_pm_resume(struct device *dev) { + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (!codec->reconfig_init_verbs && codec->bus->core.config_init_verbs) + codec->reconfig_init_verbs = 1; + dev->power.power_state = PMSG_RESUME; return pm_runtime_force_resume(dev); } @@ -3044,7 +3065,7 @@ const struct dev_pm_ops hda_codec_driver_pm = { .restore = hda_codec_pm_restore, #endif /* CONFIG_PM_SLEEP */ SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, - NULL) + hda_codec_runtime_idle) }; /* suspend the codec at shutdown; called from driver's shutdown callback */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 5a75ce69c7a47..b3aad122ca7f4 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1252,7 +1252,12 @@ static int probe_codec(struct azx *chip, int addr) if (err < 0 || res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); - return 0; + + /* config init verbs if required, such as not config by BIOS */ + if (bus->config_init_verbs) + err = bus->config_init_verbs(bus, res); + + return err; } void snd_hda_bus_reset(struct hda_bus *bus) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 40a8fd1411e8b..e76590f5c7834 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7419,6 +7419,14 @@ static void alc_fixup_headset_mic(struct hda_codec *codec, } } +static void alc_fixup_3kpull_low(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->en_3kpull_low = false; +} enum { ALC269_FIXUP_GPIO2, @@ -7703,6 +7711,7 @@ enum { ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, ALC294_FIXUP_CS35L41_I2C_2, + ALC256_FIXUP_DIS_3KPULL_LOW, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9944,6 +9953,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC256_FIXUP_DIS_3KPULL_LOW] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_3kpull_low, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -10465,6 +10478,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x129e, "CIX Phecda Board", ALC256_FIXUP_DIS_3KPULL_LOW), SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), diff --git a/sound/soc/cix/Kconfig b/sound/soc/cix/Kconfig index a90129803c0c5..a9bdff1f7800d 100644 --- a/sound/soc/cix/Kconfig +++ b/sound/soc/cix/Kconfig @@ -28,21 +28,4 @@ config SND_SOC_CDNS_I2S_MC_CIX Say Y or M if you want to add driver support for Cadence I2S-MC (MultiChannel) controller. -config SND_SOC_IPBLOQ_HDA_CIX - tristate "IPBLOG HDA Driver" - select SND_HDA_CORE - select SND_HDA_EXT_CORE - select SND_HDA_ALIGNED_MMIO - depends on ARM64 || COMPILE_TEST - help - Say Y or M if you want add support for ipbloq hda - -config SND_HDACODEC_REALTEK_CIX - tristate "Build Realtek HD-audio codec support" - depends on SND_SOC_IPBLOQ_HDA_CIX - depends on ARM64 || COMPILE_TEST - help - Say Y or M here to include Realtek HD-audio codec support in - snd-soc-hda driver, such as ALC256. - endmenu diff --git a/sound/soc/cix/Makefile b/sound/soc/cix/Makefile index 0b211f5701fb2..db10f02d750d2 100644 --- a/sound/soc/cix/Makefile +++ b/sound/soc/cix/Makefile @@ -2,12 +2,8 @@ # Cixtech Platform Support cix-snd-soc-cdns-i2s-sc-objs := cdns_i2s_sc.o cix-snd-soc-cdns-i2s-mc-objs := cdns_i2s_mc.o -cix-snd-soc-hda-objs := hdac.o hda_pcm.o ipb_hda.o -cix-snd-hda-codec-realtek-objs := hdacodec.o patch_realtek.o snd-soc-sky1-sound-card-objs := card-utils.o sky1-card.o obj-$(CONFIG_SND_SOC_CDNS_I2S_SC_CIX) += cix-snd-soc-cdns-i2s-sc.o obj-$(CONFIG_SND_SOC_CDNS_I2S_MC_CIX) += cix-snd-soc-cdns-i2s-mc.o -obj-$(CONFIG_SND_SOC_IPBLOQ_HDA_CIX) += cix-snd-soc-hda.o -obj-$(CONFIG_SND_HDACODEC_REALTEK_CIX) += cix-snd-hda-codec-realtek.o obj-${CONFIG_SND_SOC_CIX} += snd-soc-sky1-sound-card.o diff --git a/sound/soc/cix/hda_pcm.c b/sound/soc/cix/hda_pcm.c deleted file mode 100644 index 4b4ac53227186..0000000000000 --- a/sound/soc/cix/hda_pcm.c +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright 2024 Cix Technology Group Co., Ltd. -#include "hda_pcm.h" -#include "hdac.h" - -#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) - -static const struct snd_pcm_hardware hda_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - /* No full-resume yet implemented */ - /* SNDRV_PCM_INFO_RESUME |*/ - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ - SNDRV_PCM_INFO_HAS_LINK_ATIME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), - .formats = HDA_FORMATS, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 44100, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = AZX_MAX_BUF_SIZE, - .period_bytes_min = 128, - .period_bytes_max = AZX_MAX_BUF_SIZE / 2, - .periods_min = 2, - .periods_max = AZX_MAX_FRAG, - .fifo_size = 0, -}; - -/* assign a stream for the PCM */ -static inline struct hdac_stream * -hdac_assign_stream(struct hdac *hdac, struct snd_pcm_substream *ss) -{ - struct hdac_stream *hstr; - - hstr = snd_hdac_stream_assign(hdac_bus(hdac), ss); - if (!hstr) - return NULL; - - return hstr; -} - -/* release the assigned stream */ -static inline void hdac_release_stream(struct hdac_stream *hstr) -{ - snd_hdac_stream_release(hstr); -} - -/* get the assigned stream */ -static inline struct hdac_stream * -get_hdac_stream(struct snd_pcm_substream *ss) -{ - return ss->runtime->private_data; -} - -/* get the hdac data */ -static inline struct hdac *hdac_data(struct snd_pcm_substream *ss) -{ - return snd_pcm_substream_chip(ss); -} - -int hda_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - struct hdac *hdac; - struct hdac_bus *bus; - size_t size; - int ret, i; - - hdac = &p_ipb_hda->hdac; - bus = hdac_bus(hdac); - - p_ipb_hda->hdac.card = card; - - ret = dma_coerce_mask_and_coherent(hdac->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - /* buffer pre-allocation */ - size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; - if (size > MAX_PREALLOC_SIZE) - size = MAX_PREALLOC_SIZE; - - for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { - struct snd_pcm_substream *ss = pcm->streams[i].substream; - - ss->private_data = hdac; - } - - snd_pcm_set_managed_buffer_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, - hdac->dev, - size, MAX_PREALLOC_SIZE); - - return 0; -} -EXPORT_SYMBOL_GPL(hda_pcm_new); - -int hda_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct snd_pcm_runtime *runtime = ss->runtime; - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct hdac *hdac = &p_ipb_hda->hdac; - struct hdac_stream *hstr; - int err; - int buff_step; - - if (hdac_acquire_irq(hdac) < 0) { - dev_err(hdac->card->dev, "failed to register hda irq"); - return -EINVAL; - } - - mutex_lock(&hdac->open_mutex); - hstr = hdac_assign_stream(hdac, ss); - if (hstr == NULL) { - err = -EBUSY; - goto unlock; - } - - runtime->private_data = hstr; - - snd_soc_set_runtime_hwparams(ss, &hda_pcm_hw); - snd_pcm_limit_hw_rates(runtime); - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - - /* avoid wrap-around with wall-clock */ - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, - 20, - 178000000); - - if (hdac->align_buffer_size) - /* constrain buffer sizes to be multiple of 128 - * bytes. This is more efficient in terms of memory - * access but isn't required by the HDA spec and - * prevents users from specifying exact period/buffer - * sizes. For example for 44.1kHz, a period size set - * to 20ms will be rounded to 19.59ms. - */ - buff_step = 128; - else - /* Don't enforce steps on buffer sizes, still need to - * be multiple of 4 bytes (HDA spec). Tested on Intel - * HDA controllers, may not work on all devices where - * option needs to be disabled - */ - buff_step = 4; - - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - buff_step); - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - buff_step); - - /* disable LINK_ATIME timestamps for capture streams - * until we figure out how to handle digital inputs - */ - if (ss->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; - } - - snd_pcm_set_sync(ss); - mutex_unlock(&hdac->open_mutex); - - return 0; - - unlock: - mutex_unlock(&hdac->open_mutex); - return err; -} -EXPORT_SYMBOL_GPL(hda_pcm_open); - -int hda_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct hdac *hdac = &p_ipb_hda->hdac; - struct hdac_stream *hstr = get_hdac_stream(ss); - - mutex_lock(&hdac->open_mutex); - hdac_clear_irq(hdac); - hdac_release_stream(hstr); - mutex_unlock(&hdac->open_mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(hda_pcm_close); - -int hda_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(ss); - struct hdac_stream *hstr = get_hdac_stream(ss); - int ret = 0; - - if (dmab) - hdac_pcm_stream_fixedup_remap((unsigned int *)(&dmab->addr), - p_ipb_hda->remap_offset); - - hstr->bufsize = 0; - hstr->period_bytes = 0; - hstr->format_val = 0; - - return ret; -} -EXPORT_SYMBOL_GPL(hda_pcm_hw_params); - -int hda_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct hdac_stream *hstr = get_hdac_stream(ss); - - /* reset BDL address */ - snd_hdac_stream_cleanup(hstr); - - hstr->prepared = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(hda_pcm_hw_free); - -int hda_pcm_prepare(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct hdac *hdac = &p_ipb_hda->hdac; - struct hdac_stream *hstr = get_hdac_stream(ss); - struct snd_pcm_runtime *runtime = ss->runtime; - unsigned int format_val, stream_tag; - int err; - - snd_hdac_stream_reset(hstr); - format_val = snd_hdac_calc_stream_format(runtime->rate, - runtime->channels, - runtime->format, - 32, - 0); - if (!format_val) { - dev_err(hdac->card->dev, - "invalid format_val, rate=%d, ch=%d, format=%d\n", - runtime->rate, runtime->channels, runtime->format); - err = -EINVAL; - goto error; - } - - err = snd_hdac_stream_set_params(hstr, format_val); - if (err < 0) - goto error; - - snd_hdac_stream_setup(hstr); - stream_tag = hstr->stream_tag; - hstr->prepared = 1; - -error: - return err; -} -EXPORT_SYMBOL_GPL(hda_pcm_prepare); - -int hda_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *ss, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct hdac *hdac = &p_ipb_hda->hdac; - struct hdac_bus *bus = hdac_bus(hdac); - struct hdac_stream *hstr = get_hdac_stream(ss); - struct snd_pcm_substream *s; - bool start; - int sbits = 0; - int sync_reg; - - sync_reg = AZX_REG_SSYNC; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - start = true; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - start = false; - break; - default: - return -EINVAL; - } - - spin_lock(&bus->reg_lock); - - /* first, set SYNC bits of corresponding streams */ - snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); - - snd_pcm_group_for_each_entry(s, ss) { - if (s->pcm->card != ss->pcm->card) - continue; - if (start) - snd_hdac_stream_start(hstr); - else - snd_hdac_stream_stop(hstr); - } - spin_unlock(&bus->reg_lock); - - snd_hdac_stream_sync(hstr, start, sbits); - - spin_lock(&bus->reg_lock); - /* reset SYNC bits */ - snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); - if (start) - snd_hdac_stream_timecounter_init(hstr, sbits); - spin_unlock(&bus->reg_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(hda_pcm_trigger); - -unsigned int cix_azx_get_pos_posbuf(struct hdac *hdac, struct hdac_stream *hstr) -{ - return snd_hdac_stream_get_pos_posbuf(hstr); -} - -static unsigned int azx_get_position(struct hdac *hdac, - struct hdac_stream *hstr) -{ - struct snd_pcm_substream *ss = hstr->substream; - unsigned int pos; - int stream = ss->stream; - int delay = 0; - - if (hdac->get_position[stream]) - pos = hdac->get_position[stream](hdac, hstr); - else /* use the position buffer as default */ - pos = cix_azx_get_pos_posbuf(hdac, hstr); - - pos = snd_hdac_stream_readl(hstr, SD_LPIB); - - if (pos >= hstr->bufsize) - pos = 0; - - if (ss->runtime) { - snd_hdac_chip_updatel(hdac_bus(hdac), INTCTL, - AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, - AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); - - ss->runtime->delay = delay; - } - - return pos; -} - -snd_pcm_uframes_t hda_pcm_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct snd_pcm_runtime *runtime = ss->runtime; - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct hda_ipbloq *p_ipb_hda = snd_soc_dai_get_drvdata(cpu_dai); - struct hdac *hdac = &p_ipb_hda->hdac; - struct hdac_stream *hstr = get_hdac_stream(ss); - - return bytes_to_frames(runtime, - azx_get_position(hdac, hstr)); -} -EXPORT_SYMBOL_GPL(hda_pcm_pointer); diff --git a/sound/soc/cix/hda_pcm.h b/sound/soc/cix/hda_pcm.h deleted file mode 100644 index e49a071335ec5..0000000000000 --- a/sound/soc/cix/hda_pcm.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright 2024 Cix Technology Group Co., Ltd. */ - -#ifndef __HDA_PCM_H__ -#define __HDA_PCM_H__ -#include -#include -#include -#include -#include -#include - -#define HDA_RATES (SNDRV_PCM_RATE_8000_192000) -#define HDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ - | SNDRV_PCM_FMTBIT_S24_LE \ - | SNDRV_PCM_FMTBIT_S24_3LE \ - | SNDRV_PCM_FMTBIT_S32_LE) - -struct hda_ipbloq; - -int hda_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *ss); -int hda_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *ss); -int hda_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *params); -int hda_pcm_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *ss); -int hda_pcm_prepare(struct snd_soc_component *component, - struct snd_pcm_substream *ss); -int hda_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *ss, int cmd); -snd_pcm_uframes_t hda_pcm_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *ss); -int hda_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd); -#endif diff --git a/sound/soc/cix/hdac.c b/sound/soc/cix/hdac.c deleted file mode 100644 index 13d785dbec9c4..0000000000000 --- a/sound/soc/cix/hdac.c +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright 2024 Cix Technology Group Co., Ltd. - -#include -#include "hdac.h" - -static unsigned int azx_command_addr(u32 cmd) -{ - unsigned int addr = cmd >> 28; - - if (addr >= HDA_MAX_CODECS) { - snd_BUG(); - addr = 0; - } - - return addr; -} - -/* receive a response */ -static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - struct hdac *hdac = bus_to_hdac(bus); - int err; - - again: - err = snd_hdac_bus_get_response(bus, addr, res); - if (!err) - return 0; - - if (hdac->no_response_fallback) - return -EIO; - - if (!bus->polling_mode) { - dev_warn(hdac->card->dev, - "hdac_get_response timeout, switching to polling mode: last cmd=0x%08x\n", - bus->last_cmd[addr]); - bus->polling_mode = 1; - goto again; - } - - if (hdac->probing) { - /* If this critical timeout happens during the codec probing - * phase, this is likely an access to a non-existing codec - * slot. Better to return an error and reset the system. - */ - return -EIO; - } - - /* no fallback mechanism? */ - if (!hdac->fallback_to_single_cmd) - return -EIO; - - /* a fatal communication error; need either to reset or to fallback - * to the single_cmd mode - */ - if (hdac->allow_bus_reset && !hdac->response_reset && !hdac->in_reset) { - hdac->response_reset = 1; - dev_err(hdac->card->dev, - "No response from codec, resetting bus: last cmd=0x%08x\n", - bus->last_cmd[addr]); - return -EAGAIN; /* give a chance to retry */ - } - - dev_err(hdac->card->dev, - "hdac_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", - bus->last_cmd[addr]); - hdac->single_cmd = 1; - hdac->response_reset = 0; - snd_hdac_bus_stop_cmd_io(bus); - return -EIO; -} - -/* - * Use the single immediate command instead of CORB/RIRB for simplicity - * - * Note: according to Intel, this is not preferred use. The command was - * intended for the BIOS only, and may get confused with unsolicited - * responses. So, we shouldn't use it for normal operation from the - * driver. - * I left the codes, however, for debugging/testing purposes. - */ -//#define __FPGA_DEBUG__ -/* receive a response */ -static int azx_single_wait_for_response(struct hdac_bus *bus, unsigned int addr) -{ - int timeout = 50; - - while (timeout--) { -#ifdef __FPGA_DEBUG__ - udelay(100); - pr_info("\tget rsp: IRS(0x68h)=0x%x\n", - snd_hdac_chip_readw(bus, IRS)); -#endif - /* check IRV busy bit */ - if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) { - /* reuse rirb.res as the response return value */ - bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR); - return 0; - } - udelay(1); - } - - printk_ratelimited("get_response timeout: IRS=0x%x\n", snd_hdac_chip_readw(bus, IRS)); - bus->rirb.res[addr] = -1; - - return -EIO; -} - -/* send a command */ -static int azx_single_send_cmd(struct hdac_bus *bus, u32 val) -{ - unsigned int addr = azx_command_addr(val); - int timeout = 50; - -#ifdef __FPGA_DEBUG__ - pr_info("send verb cmd:0x%x, addr:0x%x\n", val, addr); -#endif - - bus->last_cmd[azx_command_addr(val)] = val; - while (timeout--) { - /* check ICB busy bit */ - if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) { - - snd_hdac_chip_writew(bus, IRS, snd_hdac_chip_readw(bus, IRS) | - AZX_IRS_VALID); - - snd_hdac_chip_writel(bus, IC, val); - - snd_hdac_chip_writew(bus, IRS, snd_hdac_chip_readw(bus, IRS) | - AZX_IRS_BUSY); - - return azx_single_wait_for_response(bus, addr); - } - udelay(1); - } - - return -EIO; -} - -/* receive a response */ -static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - if (res) - *res = bus->rirb.res[addr]; - return 0; -} - -/* - * The below are the main callbacks from hda_codec. - * - * They are just the skeleton to call sub-callbacks according to the - * current setting of hdac->single_cmd. - */ - -/* send a command */ -static int hdac_send_cmd(struct hdac_bus *bus, unsigned int val) -{ - struct hdac *hdac = bus_to_hdac(bus); - - if (hdac->single_cmd) - return azx_single_send_cmd(bus, val); - else - return snd_hdac_bus_send_cmd(bus, val); -} - -/* get a response */ -static int hdac_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - struct hdac *hdac = bus_to_hdac(bus); - - if (hdac->single_cmd) - return azx_single_get_response(bus, addr, res); - else - return azx_rirb_get_response(bus, addr, res); -} - -/*static*/ const struct hdac_bus_ops bus_core_ops = { - .command = hdac_send_cmd, - .get_response = hdac_get_response, -}; - -/* - * interrupt handler - */ -static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) -{ - /* check whether this IRQ is really acceptable */ - spin_unlock(&bus->reg_lock); - snd_pcm_period_elapsed(s->substream); - spin_lock(&bus->reg_lock); -} - -static irqreturn_t hdac_interrupt(int irq, void *dev_id) -{ - struct hdac *hdac = dev_id; - struct hdac_bus *bus = hdac_bus(hdac); - u32 status; - bool active, handled = false; - int repeat = 0; /* count for avoiding endless loop */ - - spin_lock(&bus->reg_lock); - - do { - status = hdac_readl(hdac, INTSTS); - if (status == 0 || status == 0xffffffff) - break; - - handled = true; - active = false; - if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update)) - active = true; - - status = hdac_readb(hdac, RIRBSTS); - if (status & RIRB_INT_MASK) { - /* - * Clearing the interrupt status here ensures that no - * interrupt gets masked after the RIRB wp is read in - * snd_hdac_bus_update_rirb. This avoids a possible - * race condition where codec response in RIRB may - * remain unserviced by IRQ, eventually falling back - * to polling mode in azx_rirb_get_response. - */ - hdac_writeb(hdac, RIRBSTS, RIRB_INT_MASK); - active = true; - if (status & RIRB_INT_RESPONSE) - snd_hdac_bus_update_rirb(bus); - } - } while (active && ++repeat < 10); - - spin_unlock(&bus->reg_lock); - - return IRQ_RETVAL(handled); -} - -void hdac_clear_irq(struct hdac *hdac) -{ - free_irq(hdac->irq, hdac); -} -EXPORT_SYMBOL_GPL(hdac_clear_irq); - -int hdac_acquire_irq(struct hdac *hdac) -{ - struct hdac_bus *bus = hdac_bus(hdac); - - if (request_irq(hdac->irq, hdac_interrupt, - IRQF_SHARED, - "hda irq", hdac)) { - dev_err(hdac->card->dev, - "unable to grab IRQ %d, disabling device\n", - hdac->irq); - return -1; - } - - bus->irq = hdac->irq; - hdac->card->sync_irq = bus->irq; - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_acquire_irq); - -static int stream_direction(struct hdac *hdac, unsigned char index) -{ - if (index >= hdac->capture_index_offset && - index < hdac->capture_index_offset + hdac->capture_streams) - return SNDRV_PCM_STREAM_CAPTURE; - return SNDRV_PCM_STREAM_PLAYBACK; -} - -/* initialize SD streams */ -int cix_init_streams(struct hdac *hdac) -{ - int i; - - /* initialize each stream (aka device) - * assign the starting bdl address to each stream (device) - * and initialize - */ - for (i = 0; i < hdac->num_streams; i++) { - struct hdac_stream *hs = kzalloc(sizeof(*hs), GFP_KERNEL); - int dir, tag; - - if (!hs) - return -ENOMEM; - - dir = stream_direction(hdac, i); - /* stream tag must be unique throughout - * the stream direction group, - * valid values 1...15 - * use separate stream tag if the flag - * AZX_DCAPS_SEPARATE_STREAM_TAG is used - */ - tag = i + 1; - snd_hdac_stream_init(hdac_bus(hdac), hs, - i, dir, tag); - } - - return 0; -} -EXPORT_SYMBOL_GPL(cix_init_streams); - -void cix_free_streams(struct hdac *hdac) -{ - struct hdac_bus *bus = hdac_bus(hdac); - struct hdac_stream *s; - - while (!list_empty(&bus->stream_list)) { - s = list_first_entry(&bus->stream_list, struct hdac_stream, list); - list_del(&s->list); - kfree(s); - } -} -EXPORT_SYMBOL_GPL(cix_free_streams); - -static int hdac_fixedup_regmap_addr(unsigned int *addr, unsigned int remap_offset) -{ - /* check whether need to do fixed up regmap */ - if (*addr <= remap_offset) - return 0; - - *addr -= remap_offset; - - return 0; -} - -int hdac_bus_stream_fixedup_remap(struct hdac_bus *bus, unsigned int remap_offset) -{ - struct hdac_stream *s; - - list_for_each_entry(s, &bus->stream_list, list) - if (s->bdl.addr) - hdac_fixedup_regmap_addr((unsigned int *)&(s->bdl.addr), remap_offset); - - hdac_fixedup_regmap_addr((unsigned int *)&(bus->posbuf.addr), remap_offset); - hdac_fixedup_regmap_addr((unsigned int *)&(bus->rb.addr), remap_offset); - - return 0; -} - -int hdac_pcm_stream_fixedup_remap(unsigned int *addr, unsigned int remap_offset) -{ - return hdac_fixedup_regmap_addr(addr, remap_offset); -} diff --git a/sound/soc/cix/hdac.h b/sound/soc/cix/hdac.h deleted file mode 100644 index ab75fa1609342..0000000000000 --- a/sound/soc/cix/hdac.h +++ /dev/null @@ -1,138 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright 2024 Cix Technology Group Co., Ltd. */ - -#ifndef __HDAC_H__ -#define __HDAC_H__ - -#include -#include -#include -#include -#include -#include - -struct hdac; -typedef unsigned int (*azx_get_pos_callback_t)(struct hdac *, struct hdac_stream *); -typedef int (*azx_get_delay_callback_t)(struct hdac *, struct hdac_stream *, unsigned int pos); - -struct hdac { - struct device *dev; - //struct hdac_bus bus; - struct hda_bus hbus; - - struct regmap *regmap; - void __iomem *remap_addr; - void __iomem *regs; - - int irq; - - struct snd_card *card; - int dev_index; - const char *modelname; - - /* chip type specific */ - int driver_type; - unsigned int driver_caps; - int playback_streams; - int playback_index_offset; - int capture_streams; - int capture_index_offset; - int num_streams; - int jackpoll_interval; /* jack poll interval in jiffies */ - - /* Register interaction. */ - const struct hda_controller_ops *ops; - - /* position adjustment callbacks */ - azx_get_pos_callback_t get_position[2]; - azx_get_delay_callback_t get_delay[2]; - - /* locks */ - struct mutex open_mutex; /* Prevents concurrent open/close operations */ - - /* PCM */ - struct list_head pcm_list; /* azx_pcm list */ - - /* HD codec */ - int codec_probe_mask; /* copied from probe_mask option */ - unsigned int beep_mode; - - /* flags */ - int bdl_pos_adj; - unsigned int running:1; - unsigned int fallback_to_single_cmd:1; - unsigned int single_cmd:1; - unsigned int probing:1; /* codec probing phase */ - unsigned int snoop:1; - unsigned int uc_buffer:1; /* non-cached pages for stream buffers */ - unsigned int align_buffer_size:1; - unsigned int region_requested:1; - unsigned int disabled:1; /* disabled by vga_switcheroo */ - unsigned int pm_prepared:1; - - /* assigned PCMs */ - DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); - - /* misc op flags */ - unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ - /* status for codec/controller */ - unsigned int shutdown :1; /* being unloaded */ - unsigned int response_reset:1; /* controller was reset */ - unsigned int in_reset:1; /* during reset operation */ - unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ - unsigned int bus_probing :1; /* during probing process */ - unsigned int keep_power:1; /* keep power up for notification */ - - int primary_dig_out_type; /* primary digital out PCM type */ - unsigned int mixer_assigned; /* codec addr for mixer name */ -}; - -/* Both the CPU DAI and platform drivers will access this data */ -struct hda_ipbloq { - struct device *dev; - - const struct snd_soc_component_driver *comp_drv; - const struct regmap_config *regmap_cfg; - struct regmap *cru_regmap; - struct reset_control *hda_rst; - - struct clk *sysclk; - struct clk *clk48m; - struct clk *clkrst; - - struct hdac hdac; - - /* hda dma access ddr by remapped, - * fixed up dma address for hda registers - */ - unsigned int remap_offset; - - /* for codec daguther board power */ - struct gpio_desc *pdb_gpiod; -}; - -#define hdac_bus(hdac) (&(hdac)->hbus.core) -#define bus_to_hdac(_bus) container_of(_bus, struct hdac, hbus.core) - -#define hdac_writel(hdac, reg, value) \ - snd_hdac_chip_writel(hdac_bus(hdac), reg, value) -#define hdac_readl(hdac, reg) \ - snd_hdac_chip_readl(hdac_bus(hdac), reg) -#define hdac_writew(hdac, reg, value) \ - snd_hdac_chip_writew(hdac_bus(hdac), reg, value) -#define hdac_readw(hdac, reg) \ - snd_hdac_chip_readw(hdac_bus(hdac), reg) -#define hdac_writeb(hdac, reg, value) \ - snd_hdac_chip_writeb(hdac_bus(hdac), reg, value) -#define hdac_readb(chhdacip, reg) \ - snd_hdac_chip_readb(hdac_bus(hdac), reg) - -int cix_init_streams(struct hdac *hdac); -void cix_free_streams(struct hdac *hdac); - -void hdac_clear_irq(struct hdac *hdac); -int hdac_acquire_irq(struct hdac *hdac); - -int hdac_bus_stream_fixedup_remap(struct hdac_bus *bus, unsigned int remap_offset); -int hdac_pcm_stream_fixedup_remap(unsigned int *addr, unsigned int remap_offset); -#endif diff --git a/sound/soc/cix/hdacodec.c b/sound/soc/cix/hdacodec.c deleted file mode 100644 index 7bca3bdd5344e..0000000000000 --- a/sound/soc/cix/hdacodec.c +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright 2024 Cix Technology Group Co., Ltd. -#include "hdac.h" -#include "hdacodec.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DEV_NAME_LEN 32 - -static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr); - -int cix_snd_hda_codec_set_name(struct hda_codec *codec, const char *name) -{ - int err; - - if (!name) - return 0; - err = snd_hdac_device_set_chip_name(&codec->core, name); - if (err < 0) - return err; - - return 0; -} - -static int hda_codec_config(struct hda_codec_priv *hcp, int stream, - struct snd_pcm_hw_params *params) -{ - struct hda_codec *codec = hcp->codec; - struct hda_codec_pdata *codec_pdata = hcp->codec_pdata; - struct hdac_device *hdev = &codec->core; - unsigned int format_val, i; - hda_nid_t nid; - - // TODO: update verbs by params - codec_pdata->ops->hw_params(codec, stream); - - format_val = snd_hdac_calc_stream_format(params_rate(params), - params_channels(params), - params_format(params), - 32, - 0); - - nid = hdev->start_nid; - for (i = 0; i < hdev->num_nodes; i++, nid++) { - unsigned int type; - - type = get_wcaps_type(get_wcaps(hdev, nid)); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (type == AC_WID_AUD_OUT) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, - format_val); - continue; - } - } else { - if (type == AC_WID_AUD_IN) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, - format_val); - continue; - } - } - } - - return 0; -} - -static int hda_codec_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 hda_codec_priv *hcp = snd_soc_component_get_drvdata(component); - struct hda_codec *codec = hcp->codec; - - if (!codec) { - dev_err(component->dev, "no codec setups yet"); - return -EINVAL; - } - - dev_info(component->dev, "bitdepth:%d, channels:%d, rate:%d\n", - params_width(params), - params_channels(params), - params_rate(params)); - - hcp->codec_params[substream->stream] = - devm_kzalloc(hcp->dev, sizeof(struct snd_pcm_hw_params), - GFP_KERNEL); - if (!hcp->codec_params[substream->stream]) - return -ENOMEM; - - memcpy(hcp->codec_params[substream->stream], - params, sizeof(struct snd_pcm_hw_params)); - - return hda_codec_config(hcp, substream->stream, params); -} - -static int hda_codec_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct hda_codec_priv *hcp = snd_soc_component_get_drvdata(component); - - if (hcp->codec_params[substream->stream]) { - devm_kfree(hcp->dev, hcp->codec_params[substream->stream]); - hcp->codec_params[substream->stream] = NULL; - } - - return 0; -} - -static const struct snd_soc_dai_ops hda_codec_dai_ops = { - .hw_params = hda_codec_hw_params, - .hw_free = hda_codec_hw_free, -}; - -static const struct snd_soc_component_driver hda_codec_dev = { - .use_pmdown_time = 1, - .endianness = 1, -}; - -static struct snd_soc_dai_driver hda_codec_dai[] = { - { - .name = HDA_CODEC_DRV_NAME, - .playback = { - .stream_name = "Playback", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_8000_192000, - .formats = HDA_CODEC_FMT, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_8000_192000, - .formats = HDA_CODEC_FMT, - }, - .ops = &hda_codec_dai_ops, - } -}; - -static void snd_hda_codec_dev_release(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - snd_hdac_device_exit(&codec->core); - kfree(codec->modelname); - kfree(codec->wcaps); - kfree(codec); -} - - -struct hda_codec *cix_snd_hda_codec_device_init(struct hda_bus *bus, - unsigned int codec_addr, - const char *fmt, ...) -{ - va_list vargs; - char name[DEV_NAME_LEN]; - struct hda_codec *codec; - int err; - - if (snd_BUG_ON(!bus)) - return ERR_PTR(-EINVAL); - if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return ERR_PTR(-EINVAL); - - codec = devm_kzalloc(bus->core.dev, sizeof(*codec), GFP_KERNEL); - if (!codec) - return ERR_PTR(-ENOMEM); - - va_start(vargs, fmt); - vsprintf(name, fmt, vargs); - va_end(vargs); - - err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); - if (err < 0) { - devm_kfree(bus->core.dev, codec); - return ERR_PTR(err); - } - - codec->bus = bus; - codec->depop_delay = -1; - codec->core.dev.release = snd_hda_codec_dev_release; - codec->core.type = HDA_DEV_LEGACY; - - return codec; -} - -static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr) -{ - struct hda_codec *codec; - int ret; - - codec = cix_snd_hda_codec_device_init(to_hda_bus(bus), addr, - "hdaudio%dD%d", bus->idx, addr); - if (IS_ERR(codec)) { - dev_err(bus->dev, - "device init failed for hdac device\n"); - return codec; - } - - codec->core.type = HDA_DEV_ASOC; - - ret = snd_hdac_device_register(&codec->core); - if (ret) { - dev_err(bus->dev, - "failed to register hdac device\n"); - put_device(&codec->core.dev); - return ERR_PTR(ret); - } - - return codec; -} - -extern const struct of_device_id snd_hda_id_realtek_of_match[]; -extern struct hdac_bus *get_ipb_hda_bus(void); - -int hda_codec_probe(struct platform_device *pdev) -{ - struct hda_codec_bus_pdata *hcbd = pdev->dev.platform_data; - struct device *dev = &pdev->dev; - hda_codec_patch_t patch; - struct hdac_bus *bus; - struct hda_codec_priv *hcp; - struct hda_codec_pdata *codec_pdata; - int ret; - - codec_pdata = (struct hda_codec_pdata *)device_get_match_data(dev); - if (codec_pdata) - patch = codec_pdata->data; - - hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); - if (!hcp) - return -ENOMEM; - - if (hcbd) { - hcp->hcbdata = *hcbd; - bus = (struct hdac_bus *)(hcbd->bus); - } else { - dev_warn(dev, - "no platform data, get bus from controller data\n"); - - bus = get_ipb_hda_bus(); - if (!bus) - return -EPROBE_DEFER; - hcp->codec_pdata = codec_pdata; - } - hcp->dev = dev; - hcp->bus = bus; - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - platform_set_drvdata(pdev, hcp); - - /* ASoC specific initialization */ - ret = devm_snd_soc_register_component(dev, &hda_codec_dev, - hda_codec_dai, - ARRAY_SIZE(hda_codec_dai)); - - if (ret < 0) { - dev_err(dev, "failed to register hda codec component"); - pm_runtime_disable(dev); - return ret; - } - - dev_info(dev, "%s, %s probed\n", __func__, HDA_CODEC_DRV_NAME); - - return ret; -} - -int hda_codec_remove(struct platform_device *pdev) -{ - struct hda_codec_priv *hcp = dev_get_drvdata(&pdev->dev); - - pm_runtime_disable(hcp->dev); - - return 0; -} - -int __maybe_unused hda_codec_runtime_suspend(struct device *dev) -{ - struct hda_codec_priv *hcp = dev_get_drvdata(dev); - struct hda_codec *codec = hcp->codec; - - if (codec) { - if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec); - - snd_hdac_device_unregister(&codec->core); - } - - return 0; -} - -int __maybe_unused hda_codec_runtime_resume(struct device *dev) -{ - struct hda_codec_priv *hcp = dev_get_drvdata(dev); - struct hda_codec_pdata *codec_pdata = hcp->codec_pdata; - hda_codec_patch_t patch = codec_pdata->data; - struct hdac_bus *bus = hcp->bus; - struct hda_codec *codec; - int addr = 0; - int ret; - int i; - - // TODO: the given codec address - codec = hda_codec_device_init(bus, addr); - if (IS_ERR(codec)) { - dev_err(dev, - "failed to create hdac codec, error:0x%x\n", - PTR_ERR_OR_ZERO(codec)); - return -EINVAL; - } - hcp->codec = codec; - - cix_snd_hda_codec_set_name(codec, dev_name(dev)); - - if (codec && patch) { - ret = patch(codec); - if (ret < 0) - goto error_exit; - } - - for (i = 0; i < SNDRV_PCM_STREAM_LAST + 1; i++) { - if (hcp->codec_params[i]) - hda_codec_config(hcp, i, hcp->codec_params[i]); - } - - return ret; - - error_exit: - snd_hdac_device_unregister(&codec->core); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - - return ret; -} - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HDA codec Driver"); -MODULE_AUTHOR("Xing Wang"); diff --git a/sound/soc/cix/hdacodec.h b/sound/soc/cix/hdacodec.h deleted file mode 100644 index 6b18afb4f8457..0000000000000 --- a/sound/soc/cix/hdacodec.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright 2024 Cix Technology Group Co., Ltd. */ -#ifndef __HDACODEC_H__ -#define __HDACODEC_H__ - -#include -#include -#include -#include - -#include - -#define HDA_CODEC_DRV_NAME "hda-audio-codec" - -#define HDA_CODEC_FMT (SNDRV_PCM_FMTBIT_S16_LE) - -struct hda_codec_ext_ops { - int (*startup)(struct hda_codec *codec); - void (*shutdown)(struct hda_codec *codec); - int (*hw_params)(struct hda_codec *codec, int stream); - int (*prepare)(struct hda_codec *codec, int stream); -}; - -/* hda codec data */ -struct hda_codec_pdata { - const struct hda_codec_ext_ops *ops; - - void *data; -}; - -struct hda_codec_bus_pdata { - void *bus; -}; - -struct hda_codec_priv { - struct device *dev; - struct hdac_bus *bus; - struct hda_codec_bus_pdata hcbdata; - struct hda_codec_pdata *codec_pdata; //for codec callback - struct hda_codec *codec; - struct snd_pcm_hw_params *codec_params[SNDRV_PCM_STREAM_LAST + 1]; -}; - -/* - * get widget capabilities - */ -#define get_wcaps(codec, nid) \ - snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) - -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -int hda_codec_driver_probe(struct device *dev); -int hda_codec_driver_remove(struct device *dev); - -int hdacodec_dev_probe(struct hdac_device *hdev); -int hdacodec_dev_remove(struct hdac_device *hdev); -int hdacodec_dev_match(struct hdac_device *dev, struct hdac_driver *drv); - -int hda_codec_init(struct hdac_bus *bus); - -int hda_codec_probe(struct platform_device *pdev); -int hda_codec_remove(struct platform_device *pdev); - -int __maybe_unused hda_codec_runtime_suspend(struct device *dev); -int __maybe_unused hda_codec_runtime_resume(struct device *dev); - -static const struct dev_pm_ops hda_codec_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) -}; - -#define HDA_CODEC_DRIVER_REGISTER(of_id, acpi_id) \ -static struct platform_driver hda_codec_driver = { \ - .driver = { \ - .name = HDA_CODEC_DRV_NAME, \ - .of_match_table = of_match_ptr(of_id), \ - .acpi_match_table = ACPI_PTR(acpi_id), \ - .pm = &hda_codec_pm_ops, \ - }, \ - .probe = hda_codec_probe, \ - .remove = hda_codec_remove, \ -}; \ -module_platform_driver(hda_codec_driver) - -#endif diff --git a/sound/soc/cix/ipb_hda.c b/sound/soc/cix/ipb_hda.c deleted file mode 100644 index e45a653cb5f2a..0000000000000 --- a/sound/soc/cix/ipb_hda.c +++ /dev/null @@ -1,489 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright 2024 Cix Technology Group Co., Ltd. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hdac.h" -#include "hda_pcm.h" -#include "hdacodec.h" - -#define HDA_DRV_NAME "ipbloq-hda" - -#define SKY1_AUDSS_CRU_INFO_REGMAP 0x24 -#define SKY1_AUDSS_CRU_INFO_REGMAP_STEP 0x10000000 - -static const struct regmap_config ipb_hda_regmap_cfg = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0xfffffff, -}; - -static int hda_dai_setfmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - return 0; -} - -static int hda_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return 0; -} - -static void hda_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - -} - -static int hda_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct hda_ipbloq *p_ipb_hda = dev_get_drvdata(dai->dev); - struct hdac *hdac = substream->private_data; - struct hdac_bus *bus; - - hdac = &p_ipb_hda->hdac; - bus = hdac_bus(hdac); - - return 0; -} - -static int hda_dai_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return 0; -} - -static int hda_dai_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - /* Enable HDA */ - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - /* disable HDA */ - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hda_clks_enable(struct hda_ipbloq *p_ipb_hda) -{ - int ret; - - ret = clk_prepare_enable(p_ipb_hda->sysclk); - if (ret) { - dev_err(p_ipb_hda->dev, "enabling sysclk failed\n"); - return ret; - } - - ret = clk_prepare_enable(p_ipb_hda->clk48m); - if (ret) { - dev_err(p_ipb_hda->dev, "enabling clk48m failed\n"); - goto err_sysclk; - } - - return 0; - -err_sysclk: - clk_disable_unprepare(p_ipb_hda->sysclk); - - return 0; -} - -static int hda_clks_disable(struct hda_ipbloq *p_ipb_hda) -{ - clk_disable_unprepare(p_ipb_hda->sysclk); - clk_disable_unprepare(p_ipb_hda->clk48m); - - return 0; -} - -static void hda_sw_rst(struct hda_ipbloq *p_ipb_hda) -{ - /* reset */ - reset_control_assert(p_ipb_hda->hda_rst); - - usleep_range(1, 2); - - /* release reset */ - reset_control_deassert(p_ipb_hda->hda_rst); -} - -static const struct snd_soc_dai_ops hda_dai_ops = { - .set_fmt = hda_dai_setfmt, - .startup = hda_dai_startup, - .shutdown = hda_dai_shutdown, - .hw_params = hda_dai_hw_params, - .hw_free = hda_dai_hw_free, - .trigger = hda_dai_trigger, -}; - -static struct snd_soc_dai_driver ipb_hda_dai_drv = { - .name = HDA_DRV_NAME, - .playback = { - .stream_name = "HDA Playback", - .channels_min = 1, - .channels_max = 16, - .rates = HDA_RATES, - .formats = HDA_FORMATS, - }, - .capture = { - .stream_name = "HDA Capture", - .channels_min = 1, - .channels_max = 16, - .rates = HDA_RATES, - .formats = HDA_FORMATS, - }, - .id = 0, - .ops = &hda_dai_ops, -}; - -static const struct snd_soc_component_driver ipb_hda_component = { - .name = HDA_DRV_NAME, - .open = hda_pcm_open, - .close = hda_pcm_close, - .hw_params = hda_pcm_hw_params, - .hw_free = hda_pcm_hw_free, - .prepare = hda_pcm_prepare, - .trigger = hda_pcm_trigger, - .pointer = hda_pcm_pointer, - .pcm_construct = hda_pcm_new, -}; - -static const struct hda_ipbloq ipb_hda_data = { - .comp_drv = &ipb_hda_component, - .regmap_cfg = &ipb_hda_regmap_cfg, -}; - -static int phb_hda_init(struct hda_ipbloq *p_ipb_hda) -{ - struct hdac *hdac; - struct hdac_bus *bus; - unsigned short gcap; - int cp_streams, pb_streams, start_idx; - int err; - - if (!p_ipb_hda) - return -EINVAL; - - hdac = &p_ipb_hda->hdac; - bus = hdac_bus(hdac); - - hdac->single_cmd = 1; - - gcap = snd_hdac_chip_readw(bus, GCAP); - dev_info(bus->dev, "chipset global capabilities = 0x%x\n", gcap); - - /* read number of streams from GCAP register */ - cp_streams = (gcap >> 8) & 0x0f; - pb_streams = (gcap >> 12) & 0x0f; - - if (!pb_streams && !cp_streams) { - dev_err(bus->dev, "no streams found in GCAP definitions?\n"); - return -EIO; - } - - hdac->num_streams = cp_streams + pb_streams; - bus->num_streams = cp_streams + pb_streams; - - /* initialize streams */ - snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); - start_idx = cp_streams; - snd_hdac_ext_stream_init_all(bus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); - - cix_init_streams(hdac); - err = snd_hdac_bus_alloc_stream_pages(bus); - if (err < 0) { - pr_err("hdac bus alloc stream failed\n"); - return err; - } - - /* fix remap offset for hda dma */ - if (p_ipb_hda->cru_regmap) { - int val; - - regmap_read(p_ipb_hda->cru_regmap, - SKY1_AUDSS_CRU_INFO_REGMAP, &val); - p_ipb_hda->remap_offset = - val * SKY1_AUDSS_CRU_INFO_REGMAP_STEP; - hdac_bus_stream_fixedup_remap(bus, p_ipb_hda->remap_offset); - } - - /* reset and start the controller */ - snd_hdac_bus_init_chip(bus, true); - - /* disable cmd io, use pio now */ - snd_hdac_bus_stop_cmd_io(bus); - - if (!bus->codec_mask) { - usleep_range(10000, 12000); - bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); - dev_info(bus->dev, "codecs retry: 0x%lx!\n", bus->codec_mask); - } - - /* codec detection */ - if (!bus->codec_mask) { - dev_err(bus->dev, "no codecs found!\n"); - return -ENODEV; - } - - hdac->running = 1; - - return err; -} - -extern const struct hdac_bus_ops bus_core_ops; - -// TODO: to improve it that to pass hdac bus to codec by platform data -struct hdac_bus *ipb_hda_bus; -struct hdac_bus *get_ipb_hda_bus(void) -{ - return ipb_hda_bus; -} -EXPORT_SYMBOL_GPL(get_ipb_hda_bus); - -static int ipb_hda_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct hda_ipbloq *p_ipb_hda, *drvdata; - struct hdac *hdac; - struct hdac_bus *bus; - struct resource *res; - int ret; - - drvdata = (struct hda_ipbloq *)device_get_match_data(dev); - if (!drvdata) - return -EINVAL; - - p_ipb_hda = devm_kzalloc(dev, sizeof(struct hda_ipbloq), GFP_KERNEL); - if (!p_ipb_hda) - return -ENOMEM; - p_ipb_hda->comp_drv = drvdata->comp_drv; - p_ipb_hda->regmap_cfg = drvdata->regmap_cfg; - p_ipb_hda->dev = dev; - - hdac = &(p_ipb_hda->hdac); - bus = hdac_bus(hdac); - hdac->dev = dev; - mutex_init(&hdac->open_mutex); - - snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, NULL); - bus->use_posbuf = 1; - bus->aligned_mmio = 1; - - platform_set_drvdata(pdev, p_ipb_hda); - - hdac->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(hdac->regs)) - return PTR_ERR(hdac->regs); - - hdac->regmap = devm_regmap_init_mmio(&pdev->dev, hdac->regs, - p_ipb_hda->regmap_cfg); - if (IS_ERR(hdac->regmap)) { - return dev_err_probe(dev, PTR_ERR(hdac->regmap), - "Failed to initialise regmap\n"); - } - - bus->remap_addr = hdac->regs; - bus->addr = res->start; - - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (!ACPI_COMPANION(dev)) { - ret = of_reserved_mem_device_init(dev); - if (ret && ret != -ENODEV) { - dev_err(dev, "Failed to init reserved mem for DMA\n"); - return ret; - } - } - - hdac->irq = platform_get_irq(pdev, 0); - if (hdac->irq < 0) { - dev_err(dev, "failed to get irq:%d", hdac->irq); - return hdac->irq; - } - - p_ipb_hda->sysclk = devm_clk_get(dev, "sysclk"); - if (IS_ERR(p_ipb_hda->sysclk)) { - dev_err(dev, "failed to get sysclk: %ld\n", - PTR_ERR(p_ipb_hda->sysclk)); - return PTR_ERR(p_ipb_hda->sysclk); - } - - p_ipb_hda->clk48m = devm_clk_get(dev, "clk48m"); - if (IS_ERR(p_ipb_hda->clk48m)) { - dev_err(dev, "failed to get clk48m: %ld\n", - PTR_ERR(p_ipb_hda->clk48m)); - return PTR_ERR(p_ipb_hda->clk48m); - } - - hda_clks_enable(p_ipb_hda); - - p_ipb_hda->pdb_gpiod = devm_gpiod_get_optional(dev, "pdb", GPIOD_OUT_HIGH); - if (IS_ERR(p_ipb_hda->pdb_gpiod)) { - ret = PTR_ERR(p_ipb_hda->pdb_gpiod); - dev_err(dev, "failed to pdb gpio, ret: %d\n", ret); - return ret; - } - usleep_range(600, 800); - - p_ipb_hda->cru_regmap = device_syscon_regmap_lookup_by_property(dev, "cru-ctrl"); - if (PTR_ERR(p_ipb_hda->cru_regmap) == -ENODEV) - p_ipb_hda->cru_regmap = NULL; - else if (IS_ERR(p_ipb_hda->cru_regmap)) - ret = PTR_ERR(p_ipb_hda->cru_regmap); - - p_ipb_hda->hda_rst = devm_reset_control_get(dev, "hda"); - if (IS_ERR(p_ipb_hda->hda_rst)) - return PTR_ERR(p_ipb_hda->hda_rst); - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - if (!pm_runtime_enabled(dev)) - goto err_pm_runtime; - - pm_runtime_get_sync(dev); - - hda_sw_rst(p_ipb_hda); - - phb_hda_init(p_ipb_hda); - - pm_runtime_put(dev); - - ipb_hda_bus = bus; - - ret = devm_snd_soc_register_component(dev, &ipb_hda_component, - &ipb_hda_dai_drv, 1); - - dev_info(dev, "IPB_HDA driver probed\n"); - - return 0; - -err_pm_runtime: - pm_runtime_disable(dev); - - return ret; -} - -static int ipb_hda_remove(struct platform_device *pdev) -{ - struct hda_ipbloq *p_ipb_hda = dev_get_drvdata(&pdev->dev); - struct hdac *hdac; - struct hdac_bus *bus; - int ret = 0; - - hdac = &(p_ipb_hda->hdac); - bus = hdac_bus(hdac); - - cix_free_streams(hdac); - - pm_runtime_disable(&pdev->dev); - - return ret; -} - -/* - * power management - */ -static int __maybe_unused ipb_hda_runtime_suspend(struct device *dev) -{ - struct hda_ipbloq *p_ipb_hda = dev_get_drvdata(dev); - struct hdac *hdac = &(p_ipb_hda->hdac); - struct hdac_bus *bus = hdac_bus(hdac); - - gpiod_set_value(p_ipb_hda->pdb_gpiod, 0); - - if (hdac->running) { - snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_enter_link_reset(bus); - - hdac->running = 0; - } - - hda_clks_disable(p_ipb_hda); - - return 0; -} - -static int __maybe_unused ipb_hda_runtime_resume(struct device *dev) -{ - struct hda_ipbloq *p_ipb_hda = dev_get_drvdata(dev); - struct hdac *hdac = &(p_ipb_hda->hdac); - struct hdac_bus *bus = hdac_bus(hdac); - int ret = 0; - - gpiod_set_value(p_ipb_hda->pdb_gpiod, 1); - - usleep_range(11000, 13000); - - ret = hda_clks_enable(p_ipb_hda); - - hda_sw_rst(p_ipb_hda); - - if (!hdac->running) { - /* reset and start the controller */ - snd_hdac_bus_init_chip(bus, true); - - /* disable cmd io, use pio now */ - snd_hdac_bus_stop_cmd_io(bus); - - hdac->running = 1; - } - - return ret; -} - -static const struct dev_pm_ops ipb_hda_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(ipb_hda_runtime_suspend, ipb_hda_runtime_resume, NULL) -}; - -static const struct of_device_id ipb_hda_of_match[] = { - { - .compatible = "ipbloq,hda", - .data = &ipb_hda_data, - }, - {} -}; -MODULE_DEVICE_TABLE(of, ipb_hda_of_match); - -static const struct acpi_device_id cix_ipb_hda_acpi_match[] = { - { "CIXH6020", (kernel_ulong_t)&ipb_hda_data }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, cix_ipb_hda_acpi_match); - -static struct platform_driver ipb_hda_drv = { - .driver = { - .name = HDA_DRV_NAME, - .of_match_table = of_match_ptr(ipb_hda_of_match), - .acpi_match_table = ACPI_PTR(cix_ipb_hda_acpi_match), - .pm = &ipb_hda_pm_ops, - }, - .probe = ipb_hda_probe, - .remove = ipb_hda_remove, -}; -module_platform_driver(ipb_hda_drv); - -MODULE_DESCRIPTION("IPBloq HDA driver for Cix Technology"); -MODULE_AUTHOR("Xing.Wang "); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/cix/patch_realtek.c b/sound/soc/cix/patch_realtek.c deleted file mode 100644 index ba8dba235d555..0000000000000 --- a/sound/soc/cix/patch_realtek.c +++ /dev/null @@ -1,755 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright 2024 Cix Technology Group Co., Ltd. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hdacodec.h" - -#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) -#define AMP_OUT_MUTE 0xb080 -#define PIN_OUT (AC_PINCTL_OUT_EN) - -/* extra amp-initialization sequence types */ -enum { - ALC_INIT_UNDEFINED, - ALC_INIT_NONE, - ALC_INIT_DEFAULT, -}; - -enum { - ALC_HEADSET_MODE_UNKNOWN, - ALC_HEADSET_MODE_UNPLUGGED, - ALC_HEADSET_MODE_HEADSET, - ALC_HEADSET_MODE_MIC, - ALC_HEADSET_MODE_HEADPHONE, -}; - -enum { - ALC_HEADSET_TYPE_UNKNOWN, - ALC_HEADSET_TYPE_CTIA, - ALC_HEADSET_TYPE_OMTP, -}; - -enum { - ALC_KEY_MICMUTE_INDEX, -}; - -struct alc_spec { - /* GPIO bits */ - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - bool gpio_write_delay; /* add a delay before writing gpio_data */ - - struct mutex coef_mutex; - - hda_nid_t headset_mic_pin; - hda_nid_t headphone_mic_pin; - int current_headset_mode; - int current_headset_type; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*power_hook)(struct hda_codec *codec); - void (*shutup)(struct hda_codec *codec); - - int init_amp; - int codec_variant; /* flag for other variants */ - - unsigned int no_depop_delay:1; - unsigned int done_hp_init:1; - unsigned int no_shutup_pins:1; - unsigned int ultra_low_power:1; - unsigned int has_hs_key:1; - unsigned int no_internal_mic_pin:1; - unsigned int en_3kpull_low:1; - - unsigned int coef0; - struct input_dev *kb_dev; -}; - -/* - * COEF access helper functions - */ - -static void coef_mutex_lock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_power_up_pm(codec); - mutex_lock(&spec->coef_mutex); -} - -static void coef_mutex_unlock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - mutex_unlock(&spec->coef_mutex); - snd_hda_power_down_pm(codec); -} - -static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - - return val; -} - -static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - coef_mutex_lock(codec); - val = __alc_read_coefex_idx(codec, nid, coef_idx); - coef_mutex_unlock(codec); - - return val; -} - -#define alc_read_coef_idx(codec, coef_idx) \ - alc_read_coefex_idx(codec, 0x20, coef_idx) - -static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); -} - -static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - coef_mutex_lock(codec); - __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); - coef_mutex_unlock(codec); -} - -#define alc_write_coef_idx(codec, coef_idx, coef_val) \ - alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) - -static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, unsigned int bits_set) -{ - unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); - - if (val != -1) - __alc_write_coefex_idx(codec, nid, coef_idx, - (val & ~mask) | bits_set); -} - -static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, unsigned int bits_set) -{ - coef_mutex_lock(codec); - __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); - coef_mutex_unlock(codec); -} - -#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ - alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) - -static unsigned int alc_get_coef0(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - - return spec->coef0; -} - -/* coef writes/updates batch */ -struct coef_fw { - unsigned char nid; - unsigned char idx; - unsigned short mask; - unsigned short val; -}; - -#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ - { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } -#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) -#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) -#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) - -static void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->init_hook) - spec->init_hook(codec); - - // TODO: may add verbs config - - return 0; -} - -void alc_free(struct hda_codec *codec) -{ -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec && spec->shutup) - spec->shutup(codec); -} - -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - - return 0; -} - -static int alc_resume(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->no_depop_delay) - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - - return 0; -} - -static const struct hda_codec_ops alc_patch_ops = { - .init = alc_init, - .free = alc_free, - .resume = alc_resume, - .suspend = alc_suspend, -}; - -#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) - -/* - * Rename codecs appropriately from COEF value or subvendor id - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -static const struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->core.vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - - return 0; -} - -/* common preparation job for alc_spec */ -static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) -{ - struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - int err; - - if (!spec) - return -ENOMEM; - codec->spec = spec; - - codec->single_adc_amp = 1; - codec->spdif_status_reset = 1; - codec->forced_resume = 1; - codec->patch_ops = alc_patch_ops; - mutex_init(&spec->coef_mutex); - - err = alc_codec_rename_from_preset(codec); - if (err < 0) { - kfree(spec); - return err; - } - - return 0; -} - -/* - * ALC269 - */ - -/* different alc269-variants */ -enum { - ALC269_TYPE_ALC269VA, - ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC269VC, - ALC269_TYPE_ALC269VD, - ALC269_TYPE_ALC280, - ALC269_TYPE_ALC282, - ALC269_TYPE_ALC283, - ALC269_TYPE_ALC284, - ALC269_TYPE_ALC293, - ALC269_TYPE_ALC286, - ALC269_TYPE_ALC298, - ALC269_TYPE_ALC255, - ALC269_TYPE_ALC256, - ALC269_TYPE_ALC257, - ALC269_TYPE_ALC215, - ALC269_TYPE_ALC225, - ALC269_TYPE_ALC245, - ALC269_TYPE_ALC287, - ALC269_TYPE_ALC294, - ALC269_TYPE_ALC300, - ALC269_TYPE_ALC623, - ALC269_TYPE_ALC700, -}; - -/* get a primary headphone pin if available */ -static hda_nid_t alc_get_hp_pin(struct alc_spec *spec) -{ - return 0; -} - -static void alc256_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (spec->ultra_low_power) { - alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1); - alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2); - alc_update_coef_idx(codec, 0x08, 7<<4, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 0); - alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); - msleep(30); - } - - if (!hp_pin) - hp_pin = 0x21; - - msleep(30); - - hp_pin_sense = false; - - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); - - alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); - /* - * Expose headphone mic (or possibly Line In on some machines) instead - * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See - * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of - * this register. - */ - alc_write_coef_idx(codec, 0x36, 0x5757); -} - -static void alc256_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - hp_pin_sense = false; - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(85); - - /* 3k pull low control for Headset jack. */ - /* NOTE: call this before clearing the pin, otherwise codec stalls */ - /* If disable 3k pulldown control for alc257, - * the Mic detection will not work correctly - * when booting with headset plugged. - * So skip setting it for the codec alc257 - */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - if (hp_pin_sense || spec->ultra_low_power) - msleep(100); - - if (spec->ultra_low_power) { - msleep(50); - alc_update_coef_idx(codec, 0x03, 1<<1, 0); - alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4); - alc_update_coef_idx(codec, 0x08, 3<<2, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15); - alc_update_coef_idx(codec, 0x0e, 7<<6, 0); - msleep(30); - } -} - -static int alc269_suspend(struct hda_codec *codec) -{ - return alc_suspend(codec); -} - -static int alc269_resume(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - codec->patch_ops.init(codec); - - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - - /* on some machine, the BIOS will clear the codec gpio data when enter - * suspend, and won't restore the data after resume, so we restore it - * in the driver. - */ - if (spec->gpio_data) - alc_write_gpio_data(codec); - - return 0; -} - -static void alc269_shutup(struct hda_codec *codec) -{ -} - -static void alc269_fill_coef(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int val; - - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - - if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8817); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x016) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8814); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - /* Power up output pin */ - alc_update_coef_idx(codec, 0x04, 0, 1<<11); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - val = alc_read_coef_idx(codec, 0xd); - if (val != -1 && (val & 0x0c00) >> 10 != 0x1) { - /* Capless ramp up clock control */ - alc_write_coef_idx(codec, 0xd, val | (1<<10)); - } - val = alc_read_coef_idx(codec, 0x17); - if (val != -1 && (val & 0x01c0) >> 6 != 0x4) { - /* Class D power on reset */ - alc_write_coef_idx(codec, 0x17, val | (1<<7)); - } - } - - /* HP */ - alc_update_coef_idx(codec, 0x4, 0, 1<<11); -} - -static int alc256_pb_config(struct hdac_bus *bus) -{ - static unsigned int verb_table[] = { - 0xf0000 - , 0xf0002 - , 0x02050046 - , 0x02040004 - , 0x0205001B - , 0x02040A4B - , 0x02050038 - , 0x02046901 - - , 0x00220011 - , 0x00270610 - , 0x0023b057 - , 0x0143b000 - , 0x01470740 - , 0x01470C02 - , 0x0213b000 - , 0x021707C0 - }; - int i; - - if (!bus) { - pr_err("codec alc256 config bus null\n"); - return -1; - } - - for (i = 0; i < ARRAY_SIZE(verb_table); i++) - bus->ops->command(bus, verb_table[i]); - - return 0; -} - -static int alc256_cp_config(struct hdac_bus *bus) -{ - static unsigned int verb_table[] = { - 0x02040004 - , 0x0205001B - , 0x02040A4B - , 0x02050038 - , 0x02046901 - - //check headset status - , 0x02050046 - , 0x020C0000 - - , 0x01970724 - , 0x01937002 - , 0x02337100 - , 0x00837017 - , 0x00820011 - , 0x00870610 - }; - int i; - - if (!bus) { - pr_err("codec alc256 config bus null\n"); - return -1; - } - - for (i = 0; i < ARRAY_SIZE(verb_table); i++) - bus->ops->command(bus, verb_table[i]); - - return 0; -} - -static int alc_hw_params(struct hda_codec *codec, int stream) -{ - struct hdac_bus *bus = codec->core.bus; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - alc256_pb_config(bus); - else - alc256_cp_config(bus); - - return 0; -} - -static void alc_default_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - return; - - msleep(30); - - hp_pin_sense = false; - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - if (hp_pin_sense) - msleep(100); -} - -static void alc_default_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) { - alc269_shutup(codec); - return; - } - - hp_pin_sense = false; - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - if (hp_pin_sense) - msleep(100); -} - - -/* - */ -static int patch_alc269(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - spec = codec->spec; - codec->power_save_node = 0; - - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; - - spec->shutup = alc_default_shutup; - spec->init_hook = alc_default_init; - - switch (codec->core.vendor_id) { - case 0x10ec0269: - spec->codec_variant = ALC269_TYPE_ALC269VA; - switch (alc_get_coef0(codec) & 0x00f0) { - case 0x0010: - spec->codec_variant = ALC269_TYPE_ALC269VB; - break; - case 0x0020: - spec->codec_variant = ALC269_TYPE_ALC269VC; - break; - case 0x0030: - spec->codec_variant = ALC269_TYPE_ALC269VD; - break; - default: - break; - } - if (err < 0) - goto error; - spec->shutup = alc269_shutup; - spec->init_hook = alc269_fill_coef; - alc269_fill_coef(codec); - break; - case 0x10ec0256: - spec->codec_variant = ALC269_TYPE_ALC256; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - - break; - case 0x10ec0257: - spec->codec_variant = ALC269_TYPE_ALC257; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - - break; - - } - - codec->patch_ops.init(codec); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * patch entries - */ -const struct hda_codec_ext_ops alc_ops = { - .hw_params = alc_hw_params, -}; - -const struct hda_codec_pdata alc_codec_pdata = { - .ops = &alc_ops, - .data = &patch_alc269, -}; - -const struct of_device_id snd_hda_id_realtek_of_match[] = { - { .compatible = "realtek,alc256", .data = &alc_codec_pdata}, - { .compatible = "realtek,alc257", .data = &alc_codec_pdata}, - { .compatible = "realtek,alc269", .data = &alc_codec_pdata}, - {} -}; -MODULE_DEVICE_TABLE(of, snd_hda_id_realtek_of_match); - -static const struct acpi_device_id snd_hda_id_realtek_acpi_match[] = { - { "CIXH6030", (kernel_ulong_t)&alc_codec_pdata}, /* alc256 */ - { "CIXH6031", (kernel_ulong_t)&alc_codec_pdata}, /* alc257 */ - { "CIXH6032", (kernel_ulong_t)&alc_codec_pdata}, /* alc269 */ - { }, -}; -MODULE_DEVICE_TABLE(acpi, snd_hda_id_realtek_acpi_match); - -HDA_CODEC_DRIVER_REGISTER(snd_hda_id_realtek_of_match, - snd_hda_id_realtek_acpi_match); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Realtek HDA codec Driver"); -MODULE_AUTHOR("Xing Wang");