From d14d77bafb4fc2ca26634b9b9c388d6ffa811ab9 Mon Sep 17 00:00:00 2001 From: Lei Qian Date: Mon, 28 Oct 2019 11:28:15 +0800 Subject: [PATCH] audio: merge from mainline PD#SWPL-15312 Change-Id: I827a1db0d6c948682812ea04a6db2c7b5757dbfb Signed-off-by: Lei Qian --- drivers/amlogic/Kconfig | 2 + drivers/amlogic/Makefile | 2 + drivers/amlogic/hifi4dsp/Kconfig | 11 + drivers/amlogic/hifi4dsp/Makefile | 19 + drivers/amlogic/hifi4dsp/hifi4dsp_api.h | 43 + drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c | 397 ++++++ drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h | 228 ++++ drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c | 259 ++++ drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h | 58 + drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c | 323 +++++ drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h | 97 ++ drivers/amlogic/hifi4dsp/hifi4dsp_module.c | 1141 +++++++++++++++++ drivers/amlogic/hifi4dsp/hifi4dsp_priv.h | 78 ++ drivers/amlogic/hifi4dsp/tm2_dsp_top.c | 525 ++++++++ drivers/amlogic/hifi4dsp/tm2_dsp_top.h | 36 + .../media/vin/tvin/hdmirx/hdmi_rx_hw.c | 232 ++-- drivers/extcon/extcon.c | 20 + include/linux/extcon.h | 4 + sound/soc/amlogic/auge/Makefile | 4 +- sound/soc/amlogic/auge/audio_clks.c | 13 +- sound/soc/amlogic/auge/audio_clks.h | 9 + sound/soc/amlogic/auge/audio_controller.c | 21 +- sound/soc/amlogic/auge/card.c | 120 +- sound/soc/amlogic/auge/ddr_mngr.c | 177 +-- sound/soc/amlogic/auge/ddr_mngr.h | 9 + sound/soc/amlogic/auge/earc.c | 823 +++++++++--- sound/soc/amlogic/auge/earc_hw.c | 647 ++++++++-- sound/soc/amlogic/auge/earc_hw.h | 125 +- sound/soc/amlogic/auge/effects_hw_v2.c | 38 +- sound/soc/amlogic/auge/effects_hw_v2.h | 7 +- sound/soc/amlogic/auge/effects_v2.c | 50 +- sound/soc/amlogic/auge/effects_v2.h | 8 +- sound/soc/amlogic/auge/extn.c | 105 +- sound/soc/amlogic/auge/iomap.c | 81 -- sound/soc/amlogic/auge/iomap.h | 16 - sound/soc/amlogic/auge/loopback.c | 78 +- sound/soc/amlogic/auge/loopback.h | 4 + sound/soc/amlogic/auge/loopback_hw.c | 36 +- sound/soc/amlogic/auge/loopback_hw.h | 11 +- sound/soc/amlogic/auge/regs.h | 3 + sound/soc/amlogic/auge/sm1,clocks.c | 24 + sound/soc/amlogic/auge/spdif.c | 63 +- sound/soc/amlogic/auge/tdm.c | 92 +- sound/soc/amlogic/auge/tdm_hw.c | 107 +- sound/soc/amlogic/auge/tdm_hw.h | 15 +- sound/soc/amlogic/auge/tdm_match_table.c | 41 +- sound/soc/amlogic/auge/tm2,clocks.c | 24 + sound/soc/amlogic/common/Makefile | 3 +- sound/soc/amlogic/common/iomapres.c | 75 ++ sound/soc/amlogic/common/spdif_info.c | 5 + sound/soc/codecs/amlogic/ad82584f.c | 21 +- sound/soc/codecs/amlogic/tas5782m.c | 18 +- sound/soc/codecs/amlogic/tas5805.c | 4 +- 53 files changed, 5615 insertions(+), 737 deletions(-) create mode 100644 drivers/amlogic/hifi4dsp/Kconfig create mode 100644 drivers/amlogic/hifi4dsp/Makefile create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_api.h create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_module.c create mode 100644 drivers/amlogic/hifi4dsp/hifi4dsp_priv.h create mode 100644 drivers/amlogic/hifi4dsp/tm2_dsp_top.c create mode 100644 drivers/amlogic/hifi4dsp/tm2_dsp_top.h create mode 100644 sound/soc/amlogic/common/iomapres.c diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 0b7cd11350a2..914146f80a1e 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -142,6 +142,8 @@ source "drivers/amlogic/dolby_fw/Kconfig" source "drivers/amlogic/ircut/Kconfig" +source "drivers/amlogic/hifi4dsp/Kconfig" + source "drivers/amlogic/pixel_probe/Kconfig" source "drivers/amlogic/firmware/Kconfig" diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index 753e8a5144c0..54a86c6227da 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -106,6 +106,8 @@ obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bluetooth/ obj-$(CONFIG_AMLOGIC_WIFI) += wifi/ +obj-$(CONFIG_AMLOGIC_HIFI4DSP) += hifi4dsp/ + obj-$(CONFIG_AMLOGIC_POWER) += power/ obj-$(CONFIG_AMLOGIC_PCIE) += pci/ diff --git a/drivers/amlogic/hifi4dsp/Kconfig b/drivers/amlogic/hifi4dsp/Kconfig new file mode 100644 index 000000000000..622e835e1e07 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/Kconfig @@ -0,0 +1,11 @@ +# audio hifi4dsp configuration +# +menu "AMLOGIC HiFi4DSP process" + +config AMLOGIC_HIFI4DSP + tristate "hifi4dsp control support" + default n + help + support the amlogic hifi4dsp; + +endmenu diff --git a/drivers/amlogic/hifi4dsp/Makefile b/drivers/amlogic/hifi4dsp/Makefile new file mode 100644 index 000000000000..afac2b187f4a --- /dev/null +++ b/drivers/amlogic/hifi4dsp/Makefile @@ -0,0 +1,19 @@ + +# Makefile for tm2 hifi4dsp + +hifi4dsp-objs = hifi4dsp_module.o \ + hifi4dsp_firmware.o \ + hifi4dsp_dsp.o \ + hifi4dsp_ipc.o \ + tm2_dsp_top.o + +#audiodsp-objs += pcmenc_module.o pcmenc_stream.o +#audiodsp-objs += spdif_module.o + +ifneq ($(KBUILD_SRC),) +TOP_KBUILD_SRC := $(KBUILD_SRC)/ +endif + +obj-$(CONFIG_AMLOGIC_HIFI4DSP) +=hifi4dsp.o + +#EXTRA_CFLAGS = -DENABLE_WAIT_FORMAT diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_api.h b/drivers/amlogic/hifi4dsp/hifi4dsp_api.h new file mode 100644 index 000000000000..04c60226a01a --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_api.h @@ -0,0 +1,43 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_api.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __AML_HIFI4DSP_API_H__ +#define __AML_HIFI4DSP_API_H__ + +struct hifi4dsp_info_t { + char id; /*dsp_id 0,1,2...*/ + char fw_id; + char fw_name[32]; /*name of firmware which used for dsp*/ + long phy_addr; /*phy address of firmware wille be loaded on*/ +}; + +#define HIFI4DSP_IOC_MAGIC 'H' + +#define HIFI4DSP_LOAD _IOWR(HIFI4DSP_IOC_MAGIC, 1, struct hifi4dsp_info_t) +#define HIFI4DSP_RESET _IOWR(HIFI4DSP_IOC_MAGIC, 2, struct hifi4dsp_info_t) +#define HIFI4DSP_START _IOWR(HIFI4DSP_IOC_MAGIC, 3, struct hifi4dsp_info_t) +#define HIFI4DSP_STOP _IOWR(HIFI4DSP_IOC_MAGIC, 4, struct hifi4dsp_info_t) +#define HIFI4DSP_SLEEP _IOWR(HIFI4DSP_IOC_MAGIC, 5, struct hifi4dsp_info_t) +#define HIFI4DSP_WAKE _IOWR(HIFI4DSP_IOC_MAGIC, 6, struct hifi4dsp_info_t) + +#define HIFI4DSP_GET_INFO _IOWR((HIFI4DSP_IOC_MAGIC), (18), \ + struct hifi4dsp_info_t) + +#define HIFI4DSP_TEST _IO(HIFI4DSP_IOC_MAGIC, 255) + + +#endif /*__AML_AUDIO_HIFI4DSP_API_H__ */ diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c new file mode 100644 index 000000000000..c0edcf96a145 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c @@ -0,0 +1,397 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" + +//static struct hifi4dsp_dsp *dsp_g; + +/* Internal generic low-level hifi4dsp share memory write/read functions*/ +void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value) +{ + writel(value, addr + offset); +} +EXPORT_SYMBOL(hifi4dsp_smem_write); + +u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset) +{ + return readl(addr + offset); +} +EXPORT_SYMBOL(hifi4dsp_smem_read); + +void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value) +{ + memcpy_toio(addr + offset, &value, sizeof(value)); +} +EXPORT_SYMBOL(hifi4dsp_smem_write64); + +u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset) +{ + u64 val; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + return val; +} +EXPORT_SYMBOL(hifi4dsp_smem_read64); + +static inline void _hifi4dsp_memcpy_toio_32(u32 __iomem *dest, + u32 *src, size_t bytes) +{ + int i, words = bytes >> 2; + + for (i = 0; i < words; i++) + writel(src[i], dest + i); +} + +static inline void _hifi4dsp_memcpy_fromio_32(u32 *dest, + const __iomem u32 *src, size_t bytes) +{ + int i, words = bytes >> 2; + + for (i = 0; i < words; i++) + dest[i] = readl(src + i); +} + +void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp, + void __iomem *dest, void *src, size_t bytes) +{ + _hifi4dsp_memcpy_toio_32(dest, src, bytes); +} +EXPORT_SYMBOL(hifi4dsp_memcpy_toio_32); + +void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp, void *dest, + void __iomem *src, size_t bytes) +{ + _hifi4dsp_memcpy_fromio_32(dest, src, bytes); +} +EXPORT_SYMBOL(hifi4dsp_memcpy_fromio_32); + +/* Public API */ +void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp, u32 offset, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&dsp->spinlock, flags); + dsp->ops->write(dsp->addr.smem, offset, value); + spin_unlock_irqrestore(&dsp->spinlock, flags); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_write); + +u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp, u32 offset) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&dsp->spinlock, flags); + val = dsp->ops->read(dsp->addr.smem, offset); + spin_unlock_irqrestore(&dsp->spinlock, flags); + + return val; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_read); + +void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp, u32 offset, u64 value) +{ + unsigned long flags; + + spin_lock_irqsave(&dsp->spinlock, flags); + dsp->ops->write64(dsp->addr.smem, offset, value); + spin_unlock_irqrestore(&dsp->spinlock, flags); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64); + +u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp, u32 offset) +{ + unsigned long flags; + u64 val; + + spin_lock_irqsave(&dsp->spinlock, flags); + val = dsp->ops->read64(dsp->addr.smem, offset); + spin_unlock_irqrestore(&dsp->spinlock, flags); + + return val; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64); + +void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u32 value) +{ + dsp->ops->write(dsp->addr.smem, offset, value); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_write_unlocked); + +u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset) +{ + return dsp->ops->read(dsp->addr.smem, offset); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_read_unlocked); + +void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u64 value) +{ + dsp->ops->write64(dsp->addr.smem, offset, value); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64_unlocked); + +u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset) +{ + return dsp->ops->read64(dsp->addr.smem, offset); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64_unlocked); + +int hifi4dsp_dsp_smem_update_bits_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new); + + return change; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_unlocked); + +void hifi4dsp_dsp_smem_update_bits_forced_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u32 mask, u32 value) +{ + unsigned int old, new; + u32 ret; + + ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced_unlocked); + +int hifi4dsp_dsp_smem_update_bits64_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = hifi4dsp_dsp_smem_read64_unlocked(dsp, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + hifi4dsp_dsp_smem_write64_unlocked(dsp, offset, new); + + return change; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64_unlocked); + +int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&dsp->spinlock, flags); + change = hifi4dsp_dsp_smem_update_bits_unlocked(dsp, + offset, mask, value); + spin_unlock_irqrestore(&dsp->spinlock, flags); + return change; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits); + +void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp, + u32 offset, u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&dsp->spinlock, flags); + hifi4dsp_dsp_smem_update_bits_forced_unlocked(dsp, offset, mask, value); + spin_unlock_irqrestore(&dsp->spinlock, flags); +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced); + +int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp, + u32 offset, u64 mask, u64 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&dsp->spinlock, flags); + change = hifi4dsp_dsp_smem_update_bits64_unlocked(dsp, + offset, mask, value); + spin_unlock_irqrestore(&dsp->spinlock, flags); + return change; +} +EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64); + +int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp, + u32 outbox_offset, size_t outbox_size, + u32 inbox_offset, size_t inbox_size) +{ + dsp->mailbox.out_base = dsp->addr.smem + outbox_offset; + dsp->mailbox.in_base = dsp->addr.smem + inbox_offset; + dsp->mailbox.out_size = outbox_size; + dsp->mailbox.in_size = inbox_size; + return 0; +} +EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_init); + +void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes) +{ + memcpy_toio(dsp->mailbox.out_base, message, bytes); +} +EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_write); + +void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes) +{ + memcpy_fromio(message, dsp->mailbox.out_base, bytes); +} +EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_read); + +void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes) +{ + memcpy_toio(dsp->mailbox.in_base, message, bytes); +} +EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_write); + +void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes) +{ + memcpy_fromio(message, dsp->mailbox.in_base, bytes); +} +EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_read); + +int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp) +{ + if (dsp->ops->boot) + dsp->ops->boot(dsp); + pr_debug("%s done\n", __func__); + return 0; +} +EXPORT_SYMBOL(hifi4dsp_dsp_boot); + +void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp) +{ + if (dsp->ops->reset) + dsp->ops->reset(dsp); + pr_debug("%s done\n", __func__); +} +EXPORT_SYMBOL(hifi4dsp_dsp_reset); + +void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp) +{ + if (dsp->ops->sleep) + dsp->ops->sleep(dsp); +} +EXPORT_SYMBOL(hifi4dsp_dsp_sleep); + +int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp) +{ + if (dsp->ops->wake) + return dsp->ops->wake(dsp); + return 0; +} +EXPORT_SYMBOL(hifi4dsp_dsp_wake); + +void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp) +{ + if (dsp->ops->dump) + dsp->ops->dump(dsp); +} +EXPORT_SYMBOL(hifi4dsp_dsp_dump); + +struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv, + struct hifi4dsp_pdata *pdata, + struct hifi4dsp_dsp_device *dsp_dev) +{ + int err = 0; + struct hifi4dsp_dsp *dsp; + + dsp = kzalloc(sizeof(struct hifi4dsp_dsp), GFP_KERNEL); + if (dsp == NULL) + goto dsp_malloc_error; + + mutex_init(&dsp->mutex); + spin_lock_init(&dsp->spinlock); + spin_lock_init(&dsp->fw_spinlock); + INIT_LIST_HEAD(&dsp->fw_list); + + dsp->id = pdata->id; + dsp->irq = pdata->irq; + dsp->major_id = MAJOR(priv->dev->devt); + dsp->dev = priv->dev; + dsp->pdata = pdata; + dsp->priv = priv; + dsp->ops = dsp_dev->ops; + + /* Initialise Audio DSP */ + if (dsp->ops->init) { + err = dsp->ops->init(dsp, pdata); + if (err < 0) + return NULL; + } + + /*Register the ISR here if necessary*/ + /* + * err = request_threaded_irq(dsp->irq, dsp->ops->irq_handler, + * dsp_dev->thread, IRQF_SHARED, "HIFI4DSP", dsp); + * if (err) + * goto irq_err; + */ + goto dsp_new_done; + /* + * irq_err: + * if (dsp->ops->free) + * dsp->ops->free(dsp); + */ +dsp_malloc_error: +dsp_new_done: + return dsp; +} +EXPORT_SYMBOL(hifi4dsp_dsp_new); + +MODULE_AUTHOR("Shuyu Li"); +MODULE_DESCRIPTION("HiFi DSP Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h new file mode 100644 index 000000000000..66c21ccb2f3f --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h @@ -0,0 +1,228 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _HIFI4DSP_DSP_H +#define _HIFI4DSP_DSP_H + +#include +#include +#include +#include +#include + +#include "hifi4dsp_firmware.h" + +struct firmware; +struct hifi4dsp_pdata; +struct hifi4dsp_dsp; + +/* + * DSP memory offsets and addresses. + */ +struct hifi4dsp_addr { + u32 smem_paddr; + u32 smem_size; + u32 reg_paddr; + u32 reg_size; + u32 fw_paddr; + u32 fw_size; + void __iomem *smem; + void __iomem *reg; + void __iomem *fw; +}; +/* + * DSP Mailbox configuration. + */ +struct hifi4dsp_mailbox { + void __iomem *in_base; + void __iomem *out_base; + size_t in_size; + size_t out_size; +}; + +/* + * DSP Operations exported by platform Audio DSP driver. + */ +struct hifi4dsp_ops { + int (*boot)(struct hifi4dsp_dsp *); + int (*reset)(struct hifi4dsp_dsp *); + int (*wake)(struct hifi4dsp_dsp *); + int (*sleep)(struct hifi4dsp_dsp *); + /* Shim IO */ + void (*write)(void __iomem *addr, u32 offset, u32 value); + u32 (*read)(void __iomem *addr, u32 offset); + + /* DSP I/DRAM IO */ + void (*ram_read)(struct hifi4dsp_dsp *dsp, + void *dest, void __iomem *src, size_t bytes); + void (*ram_write)(struct hifi4dsp_dsp *dsp, + void __iomem *dest, void *src, size_t bytes); + void (*write64)(void __iomem *addr, u32 offset, u64 value); + u64 (*read64)(void __iomem *addr, u32 offset); + + void (*dump)(struct hifi4dsp_dsp *); + + /* IRQ handlers */ + irqreturn_t (*irq_handler)(int irq, void *context); + + /* hifi4dsp init and free */ + int (*init)(struct hifi4dsp_dsp *dsp, struct hifi4dsp_pdata *pdata); + void (*free)(struct hifi4dsp_dsp *dsp); + + /* FW module parser/loader */ + int (*parse_fw)(struct hifi4dsp_firmware *dsp_fw, void *pinfo); +}; + +/* + * hifi4dsp dsp device. + */ +struct hifi4dsp_dsp_device { + struct hifi4dsp_ops *ops; + irqreturn_t (*thread)(int irq, void *thread_context); + void *thread_context; +}; + +/* + * hifi4dsp Platform Data. + */ +struct hifi4dsp_pdata { + int id; + const char *name; + int irq; + unsigned int clk_freq; + phys_addr_t reg_paddr; + unsigned int reg_size; + void *reg; + /* Share memory */ + phys_addr_t smem_paddr; + unsigned int smem_size; + + /* Firmware */ + char fw_name[32]; + phys_addr_t fw_paddr; /*physical address of fw data*/ + void *fw_buf; + int fw_size; + int fw_fmt; + int fw_max_size; + + void *ops; + void *dsp; /*pointer to dsp*/ + void *priv; /*pointer to priv*/ +}; + +struct hifi4dsp_dsp { + u32 id; + int irq; + int freq; + /* runtime */ + spinlock_t spinlock; /* used for IPC */ + struct mutex mutex; /* used for fw */ + struct device *dev; + struct device *dma_dev; + u32 major_id; + void *thread_context; + + struct hifi4dsp_dsp_device *dsp_dev; + /* operations */ + struct hifi4dsp_ops *ops; + + /* base addresses */ + struct hifi4dsp_addr addr; + + /* mailbox */ + struct hifi4dsp_mailbox mailbox; + + /* platform data */ + struct hifi4dsp_pdata *pdata; + + /*fw support*/ + struct hifi4dsp_firmware *dsp_fw;/*def fw*/ + u32 fw_cnt; + spinlock_t fw_spinlock; + struct list_head fw_list; + + u32 intr_status; + struct firmware *fw; + + struct clk *dsp_clk; + struct clk *dsp_gate; + + void *info; + void *priv; +}; + +/* Internal generic low-level hifi4dsp share memory write/read functions*/ +extern void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value); +extern u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset); +extern void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value); +extern u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset); +extern void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp, + void __iomem *dest, void *src, size_t bytes); +extern void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp, + void *dest, void __iomem *src, size_t bytes); + +extern void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp, + u32 offset, u32 value); +extern u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp, + u32 offset); +extern void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp, + u32 offset, u64 value); +extern u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp, + u32 offset); +extern void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u32 value); +extern u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset); +extern void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset, u64 value); +extern u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp, + u32 offset); +extern int hifi4dsp_dsp_smem_update_bits_unlocked( + struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value); +extern void hifi4dsp_dsp_smem_update_bits_forced_unlocked( + struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value); +extern int hifi4dsp_dsp_smem_update_bits64_unlocked( + struct hifi4dsp_dsp *dsp, u32 offset, u64 mask, u64 value); +extern int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp, + u32 offset, u32 mask, u32 value); +extern void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp, + u32 offset, u32 mask, u32 value); +extern int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp, + u32 offset, u64 mask, u64 value); + +extern int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp, + u32 outbox_offset, size_t outbox_size, + u32 inbox_offset, size_t inbox_size); +extern void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes); +extern void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes); +extern void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes); +extern void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp, + void *message, size_t bytes); +extern int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp); +extern void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp); +extern void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp); +extern int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp); +extern void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp); +//extern struct hifi4dsp_dsp * hifi4dsp_dsp_new(struct hifi4dsp_priv *priv, +// struct hifi4dsp_pdata *pdata, struct hifi4dsp_ops *ops); +extern struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv, + struct hifi4dsp_pdata *pdata, struct hifi4dsp_dsp_device *dsp_dev); + +#endif /*_HIFI4DSP_DSP_H*/ diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c new file mode 100644 index 000000000000..c7ab5cbed5fe --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c @@ -0,0 +1,259 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" + +static inline void hifi4dsp_fw_memcpy(void __iomem *dest, + void *src, u32 bytes) +{ + memcpy_toio(dest, src, bytes); +} + +/* general a new hifi4dsp_firmware object, called by hi-level functions */ +struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp, + const struct firmware *fw, void *private) +{ + struct hifi4dsp_firmware *dsp_fw; + + dsp_fw = kzalloc(sizeof(struct hifi4dsp_firmware), GFP_KERNEL); + if (dsp_fw == NULL) + goto dsp_fw_malloc_err; + + dsp_fw->dsp = dsp; + dsp_fw->priv = dsp->priv; + if (private != NULL) + dsp_fw->private = private; + if (fw != NULL) + dsp_fw->size = fw->size; + pr_debug("%s done\n", __func__); + +dsp_fw_malloc_err: + return dsp_fw; +} +/*dsp_fw must be initied before this operation, + *special the *dsp pointer must not be null + */ +int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw) +{ + unsigned long flags; + struct hifi4dsp_dsp *dsp; + + if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL)) + return -1; + dsp = dsp_fw->dsp; + spin_lock_irqsave(&dsp->fw_spinlock, flags); + list_add_tail(&dsp_fw->list, &dsp->fw_list); + dsp->fw_cnt++; + dsp_fw->id = dsp->fw_cnt; + spin_unlock_irqrestore(&dsp->fw_spinlock, flags); + pr_debug("%s done\n", __func__); + return 0; +} + +static struct hifi4dsp_firmware *hifi4dsp_fw_search_by_name( + struct hifi4dsp_dsp *dsp, char *name) +{ + struct hifi4dsp_firmware *dsp_fw = NULL; + struct hifi4dsp_firmware *pfw = NULL; + unsigned long flags; + + spin_lock_irqsave(&dsp->fw_spinlock, flags); + if (list_empty(&dsp->fw_list)) { + pfw = NULL; + pr_err("Firmware list is empty\n"); + } + + list_for_each_entry(dsp_fw, &dsp->fw_list, list) { + if (memcmp(dsp_fw->name, name, strlen(name)) == 0) { + pfw = dsp_fw; + break; + } + } + spin_unlock_irqrestore(&dsp->fw_spinlock, flags); + return pfw; +} + +int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col) +{ + int i = 0, n = 0, size = 0; + const u8 *pdata; + char str[1024]; + char a_str[24]; + + pdata = (u8 *)buf; + size = bytes; + memset(str, '\0', sizeof(a_str)); + memset(str, '\0', sizeof(str)); + while (n < size) { + sprintf(a_str, "%p: ", pdata); + strcat(str, a_str); + col = ((size-n) > col)?col:(size-n); + for (i = 0; i < col; i++) { + sprintf(a_str, "%02x ", *(pdata+i)); + strcat(str, a_str); + } + pr_info("%s\n", str); + memset(str, '\0', sizeof(str));/*re-init buf*/ + pdata += col; + n += col; + } + + return 0; +} + + +//resource res; +int hifi4dsp_fw_copy_to_ddr(const struct firmware *fw, + struct hifi4dsp_firmware *dsp_fw) +{ + int fw_bytes = 0; + const u8 *fw_src; + void *fw_dst; + + fw_src = fw->data; + fw_bytes = fw->size; + fw_dst = dsp_fw->buf; + hifi4dsp_dump_memory(fw_src, 32, 16); + hifi4dsp_dump_memory(fw_src+fw_bytes-32, 32, 16); + pr_debug("%s fw_src:0x%p, pdata_dst=0x%p ,szie=%d bytes\n", + __func__, fw_src, fw_dst, fw_bytes); + //memcpy(fw_dst, fw_src, fw_bytes); + memcpy_toio(fw_dst, fw_src, fw_bytes); + //do memory barrier + //mb(); + /*TODO, if need add membarrier code*/ + hifi4dsp_dump_memory(fw_dst, 32, 16); + hifi4dsp_dump_memory(fw_dst+fw_bytes-32, 32, 16); + + return 0; +} + +int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw) +{ + const struct firmware *fw; + struct hifi4dsp_priv *priv; + struct hifi4dsp_dsp *dsp; + int err = 0; + + pr_info("%s loading firmware %s\n", __func__, dsp_fw->name); + + if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL)) + return -1; + priv = dsp_fw->priv; + dsp = dsp_fw->dsp; + err = request_firmware(&fw, dsp_fw->name, priv->dev); + if (err < 0) { + HIFI4DSP_PRNT("can't load the %s,err=%d\n", dsp_fw->name, err); + goto done; + } + if (fw == NULL) { + HIFI4DSP_PRNT("firmware pointer==NULL\n"); + goto done; + } + if (dsp_fw == NULL) { + HIFI4DSP_PRNT("hifi4dsp_firmware pointer==NULL\n"); + err = ENOMEM; + goto release; + } + dsp_fw->size = fw->size; + hifi4dsp_fw_copy_to_ddr(fw, dsp_fw); +release: + release_firmware(fw); +done: + return err; +} + +int hifi4dsp_fw_reload(struct hifi4dsp_dsp *dsp, + struct hifi4dsp_firmware *dsp_fw) +{ + int err = 0; + + return err; +} + +int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw) +{ + int err = 0; + + return err; +} + +/* create a hifi4dsp_firmware object and + * add it to the fw_list of hifi4dsp_dsp + */ +struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp, + char *name) +{ + struct hifi4dsp_firmware *dsp_fw; + int str_len = 0; + + dsp_fw = hifi4dsp_fw_search_by_name(dsp, name); + if (dsp_fw != NULL) { + pr_info("%s firmware( %s ) has been registered\n", + __func__, name); + return dsp_fw; + } + dsp_fw = hifi4dsp_fw_new(dsp, NULL, NULL); + if (dsp_fw != NULL) { + str_len = sizeof(dsp_fw->name) - 1; + strncpy(dsp_fw->name, name, str_len); + hifi4dsp_fw_add(dsp_fw); + pr_info("%s %s done\n", __func__, dsp_fw->name); + } + return dsp_fw; +} + +/* free single firmware object */ +void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw) +{ + struct hifi4dsp_dsp *dsp = dsp_fw->dsp; + + mutex_lock(&dsp->mutex); + list_del(&dsp_fw->list); + kfree(dsp_fw); + mutex_unlock(&dsp->mutex); +} + +/* free all firmware objects */ +void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_firmware *dsp_fw, *t; + + mutex_lock(&dsp->mutex); + list_for_each_entry_safe(dsp_fw, t, &dsp->fw_list, list) { + list_del(&dsp_fw->list); + kfree(dsp_fw); + } + mutex_unlock(&dsp->mutex); +} + diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h new file mode 100644 index 000000000000..fba82c01eca6 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h @@ -0,0 +1,58 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _HIFI4DSP_FIRMWARE_H +#define _HIFI4DSP_FIRMWARE_H + +struct firmware; +struct hifi4dsp_priv; +struct hifi4dsp_dsp; + +struct hifi4dsp_firmware_block { + int id; + struct list_head list; + phys_addr_t paddr; +}; + +struct hifi4dsp_firmware { + int id; + char name[32]; + int size; + int fmt; + struct list_head list; /*fw list of DSP*/ + phys_addr_t paddr; /*physical address of fw data*/ + void *buf; /*virtual address of fw data*/ + + struct hifi4dsp_priv *priv; + struct hifi4dsp_dsp *dsp; + + void *private; +}; + +extern int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw); +extern int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw); +extern void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw); +extern int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw); +extern void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp); +extern struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp, + const struct firmware *fw, void *private); +extern struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp, + char *fw_name); +extern int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col); + + +#endif /*_HIFI4DSP_FIRMWARE_H*/ diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c new file mode 100644 index 000000000000..20bfae1c415d --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c @@ -0,0 +1,323 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" +#include "hifi4dsp_ipc.h" + +/* locks held by caller */ +static struct hifi4dsp_ipc_message *ipc_get_empty_msg( + struct hifi4dsp_ipc *ipc) +{ + struct hifi4dsp_ipc_message *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, + struct hifi4dsp_ipc_message, list); + list_del(&msg->list); + } + + return msg; +} + +static int tx_wait_done(struct hifi4dsp_ipc *ipc, + struct hifi4dsp_ipc_message *msg, void *rx_data) +{ + unsigned long flags; + int ret; + + /* wait for DSP completion (in all cases atm inc pending) */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_MSG_TIMEOUT_MSECS)); + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + if (ret == 0) { + if (ipc->ops.debug_info != NULL) + ipc->ops.debug_info(ipc, "message timeout"); + + list_del(&msg->list); + ret = -ETIMEDOUT; + } else { + + /* copy the data returned from DSP */ + if (msg->rx_size) + memcpy(rx_data, msg->rx_data, msg->rx_size); + ret = msg->errno; + } + + list_add_tail(&msg->list, &ipc->empty_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return ret; +} + +static int ipc_tx_message(struct hifi4dsp_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, + size_t rx_bytes, int wait) +{ + struct hifi4dsp_ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + msg = ipc_get_empty_msg(ipc); + if (msg == NULL) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return -EBUSY; + } + + msg->header = header; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->pending = false; + msg->complete = false; + + if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL)) + ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); + + list_add_tail(&msg->list, &ipc->tx_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + kthread_queue_work(&ipc->kworker, &ipc->kwork); + + if (wait) + return tx_wait_done(ipc, msg, rx_data); + else + return 0; +} + +static int ipc_msg_empty_list_init(struct hifi4dsp_ipc *ipc) +{ + int i; + + ipc->msg = kzalloc(sizeof(struct hifi4dsp_ipc_message) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (ipc->msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + ipc->msg[i].tx_data = kzalloc + (ipc->tx_data_max_size, GFP_KERNEL); + if (ipc->msg[i].tx_data == NULL) + goto free_mem; + + ipc->msg[i].rx_data = kzalloc + (ipc->rx_data_max_size, GFP_KERNEL); + if (ipc->msg[i].rx_data == NULL) { + kfree(ipc->msg[i].tx_data); + goto free_mem; + } + + init_waitqueue_head(&ipc->msg[i].waitq); + list_add(&ipc->msg[i].list, &ipc->empty_list); + } + + return 0; + +free_mem: + while (i > 0) { + kfree(ipc->msg[i-1].tx_data); + kfree(ipc->msg[i-1].rx_data); + --i; + } + kfree(ipc->msg); + + return -ENOMEM; +} + +static void ipc_tx_msgs(struct kthread_work *work) +{ + struct hifi4dsp_ipc *ipc = + container_of(work, struct hifi4dsp_ipc, kwork); + struct hifi4dsp_ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + if (list_empty(&ipc->tx_list) || ipc->pending) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy, we will TX messages after IRQ. + * also postpone if we are in the middle of procesing completion irq + */ + if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { + pr_err("ipc_tx_msgs dsp busy\n"); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, + struct hifi4dsp_ipc_message, list); + list_move(&msg->list, &ipc->rx_list); + + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); +} + +int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_wait); + +int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + NULL, 0, 0); +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_nowait); + +struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg( + struct hifi4dsp_ipc *ipc, u64 header) +{ + struct hifi4dsp_ipc_message *msg; + u64 mask = 0; + + if (ipc->ops.reply_msg_match != NULL) + header = ipc->ops.reply_msg_match(header, &mask); + + if (list_empty(&ipc->rx_list)) { + pr_err("error: rx list empty but received 0x%llx\n", + header); + return NULL; + } + + list_for_each_entry(msg, &ipc->rx_list, list) { + if ((msg->header & mask) == header) + return msg; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_reply_find_msg); + +/* locks held by caller */ +void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc, + struct hifi4dsp_ipc_message *msg) +{ + msg->complete = true; + + if (!msg->wait) + list_add_tail(&msg->list, &ipc->empty_list); + else + wake_up(&msg->waitq); +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_msg_reply_complete); + +void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc) +{ + struct hifi4dsp_ipc_message *msg, *tmp; + unsigned long flags; + int tx_drop_cnt = 0, rx_drop_cnt = 0; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + tx_drop_cnt++; + } + + list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) { + list_move(&msg->list, &ipc->empty_list); + rx_drop_cnt++; + } + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + if (tx_drop_cnt || rx_drop_cnt) + pr_err("dropped IPC tx_msg cnt %d, rx_msg=%d\n", + tx_drop_cnt, rx_drop_cnt); +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_drop_all); + +int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc) +{ + int ret; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->rx_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + + ret = ipc_msg_empty_list_init(ipc); + if (ret < 0) + return -ENOMEM; + + /* start the IPC message thread */ + kthread_init_worker(&ipc->kworker); + ipc->tx_thread = kthread_run(kthread_worker_fn, + &ipc->kworker, "%s", + dev_name(ipc->dev)); + if (IS_ERR(ipc->tx_thread)) { + pr_err("error: failed to create message TX task\n"); + ret = PTR_ERR(ipc->tx_thread); + kfree(ipc->msg); + return ret; + } + + kthread_init_work(&ipc->kwork, ipc_tx_msgs); + return 0; +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_init); + +void hifi4dsp_ipc_finish(struct hifi4dsp_ipc *ipc) +{ + int i; + + if (ipc->tx_thread) + kthread_stop(ipc->tx_thread); + + if (ipc->msg) { + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + kfree(ipc->msg[i].tx_data); + kfree(ipc->msg[i].rx_data); + } + kfree(ipc->msg); + } +} +EXPORT_SYMBOL_GPL(hifi4dsp_ipc_finish); + diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h new file mode 100644 index 000000000000..efb239ab4f61 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h @@ -0,0 +1,97 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _HIFI4DSP_IPC_H +#define _HIFI4DSP_IPC_H + +#include +#include +#include +#include +#include +#include +#include + +#include "hifi4dsp_dsp.h" + +struct hifi4dsp_ipc; + +#define IPC_MAILBOX_MAX_BYTES 256 +#define IPC_EMPTY_LIST_SIZE 8 +#define IPC_MSG_TIMEOUT_MSECS 300 /* IPC message timeout (msecs) */ + +struct hifi4dsp_ipc_message { + struct list_head list; + u64 header; + + char *tx_data; + size_t tx_size; + char *rx_data; + size_t rx_size; + + wait_queue_head_t waitq; + bool pending; + bool complete; + bool wait; + int errno; +}; + +struct hifi4dsp_ipc_plat_ops { + void (*tx_msg)(struct hifi4dsp_ipc *, struct hifi4dsp_ipc_message *); + void (*tx_data_copy)(struct hifi4dsp_ipc_message *, char *, size_t); + bool (*is_dsp_busy)(struct hifi4dsp_dsp *dsp); + void (*debug_info)(struct hifi4dsp_ipc *, const char *); + u64 (*reply_msg_match)(u64 header, u64 *mask); +}; + +struct hifi4dsp_ipc { + struct device *dev; + struct hifi4dsp_dsp *dsp; + + /* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + bool pending; + struct hifi4dsp_ipc_message *msg; + int tx_data_max_size; + int rx_data_max_size; + + struct hifi4dsp_ipc_plat_ops ops; +}; + +int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + +int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes); + +struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg( + struct hifi4dsp_ipc *ipc, u64 header); + +void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc, + struct hifi4dsp_ipc_message *msg); + +void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc); +int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc); +void hifi4dsp_ipc_fini(struct hifi4dsp_ipc *ipc); + +#endif /*_HIFI4DSP_IPC_H*/ diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_module.c b/drivers/amlogic/hifi4dsp/hifi4dsp_module.c new file mode 100644 index 000000000000..6dd3b51c40e4 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_module.c @@ -0,0 +1,1141 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_module.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#define DEBUG + +#define pr_fmt(fmt) "hifi4dsp: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hifi4dsp_api.h" +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" +#include "hifi4dsp_ipc.h" + +#include "tm2_dsp_top.h" + +#define HIFI4DSP_NUM 2 +#define HIFI4DSP_MAX_CNT 2 +#define DSP_PHY_ADDR_0 0x30000000 +#define DSP_PHY_ADDR_1 0x30800000 + +struct hifi4dsp_priv *hifi4dsp_p[HIFI4DSP_MAX_CNT]; +unsigned int hifi4dsp_debug_flag = 1; + +#define DEF_RESERVE_MEM_SIZE 0x800000 + +#define HIFI4DSP_TM2_ALL_SMEM_SIZE (0x00300000) /* 3 MBytes */ +#define HIFI4DSP_TM2_BASE_PADDR DSP_PHY_ADDR_0 +#define HIFI4DSP_TM2_IRAM_OFFSET (0x00000000) +#define HIFI4DSP_TM2_DRAM_OFFSET (0x00100000 + \ + HIFI4DSP_TM2_IRAM_OFFSET) +#define HIFI4DSP_TM2_SMEM_OFFSET (0x00040000 + \ + HIFI4DSP_TM2_DRAM_OFFSET) +#define HIFI4DSP_TM2_MAILBOX_OFFSET (0x00004000 + \ + HIFI4DSP_TM2_SMEM_OFFSET) +#define HIFI4DSP_TM2_TIMESTAMP_OFFSET (0x00000800 +\ + HIFI4DSP_TM2_MAILBOX_OFFSET) +#define HIFI4DSP_TM2_IPC_MAX_SIZE 256 + +#define IPC_MSG_HEADER 0x10 + +#define MASK_BF(x, mask, shift) (((x&mask)< AP */ +#define IPC_STS_AP 0x08 /* IPC AP -> DSP */ +/*IPC_STS_DSP bf*/ +#define IPC_STS_DSP_DONE (0x1 << 30) +#define IPC_STS_DSP_BUSY (0x1 << 31) +#define IPC_STS_DSP_DONE_MASK (0x1 << 30) +#define IPC_STS_DSP_BUSY_MASK (0x1 << 31) + +/*IPC_STS_AP bf*/ +#define IPC_STS_AP_DONE (0x1 << 30) +#define IPC_STS_AP_BUSY (0x1 << 31) +#define IPC_STS_AP_DONE_MASK (0x1 << 30) +#define IPC_STS_AP_BUSY_MASK (0x1 << 31) + +#define INT_VEC_DSPB_MBOX7 (249) // sec tx +#define INT_VEC_DSPB_MBOX5 (248) // tx +#define INT_VEC_DSPB_MBOX6 (247) // sec rx +#define INT_VEC_DSPB_MBOX4 (246) // rx + +#define INT_VEC_DSPA_MBOX7 (245) // sec tx +#define INT_VEC_DSPA_MBOX5 (244) // tx +#define INT_VEC_DSPA_MBOX6 (243) // sec rx +#define INT_VEC_DSPA_MBOX4 (242) // rx + +static int hifi4dsp_tm2_dsp_load_fw(struct hifi4dsp_dsp *dsp); +static int hifi4dsp_tm2_dsp_reset(struct hifi4dsp_dsp *dsp); +static int hifi4dsp_tm2_dsp_start(struct hifi4dsp_dsp *dsp); +static int hifi4dsp_tm2_dsp_stop(struct hifi4dsp_dsp *dsp); +static int hifi4dsp_tm2_dsp_sleep(struct hifi4dsp_dsp *dsp); +static int hifi4dsp_tm2_dsp_wake(struct hifi4dsp_dsp *dsp); + +static int hifi4dsp_miscdev_open(struct inode *inode, struct file *fp) +{ + /* + *struct hifi4dsp_priv *priv; + *dev = conainer_of(inode->i_cdev,struct globalmem_dev,cdev); + *flip->private_data = dev; + */ + int minor = iminor(inode); + int major = imajor(inode); + struct hifi4dsp_priv *priv; + struct miscdevice *c; + struct hifi4dsp_miscdev_t *pmscdev_t; + + c = fp->private_data; + priv = hifi4dsp_p[0]; + pmscdev_t = container_of(c, struct hifi4dsp_miscdev_t, dsp_miscdev); + if (pmscdev_t == NULL) { + pr_err("hifi4dsp_miscdev_t == NULL\n"); + return -1; + } + if (pmscdev_t->priv == NULL) { + pr_err("hifi4dsp_miscdev_t -> priv==NULL\n"); + return -1; + } + priv = pmscdev_t->priv; + if (priv == NULL) { + pr_err("hifi4dsp_miscdev_t pointer *pmscdev_t==NULL\n"); + return -1; + } + fp->private_data = priv; + + pr_debug("%s,%s,major=%d,minor=%d\n", __func__, + priv->dev->kobj.name, + major, + minor); + return 0; +} + +static int hifi4dsp_miscdev_release(struct inode *inode, struct file *fp) +{ + return 0; +} + +static long hifi4dsp_miscdev_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct hifi4dsp_priv *priv; + struct hifi4dsp_dsp *dsp; + struct hifi4dsp_info_t *info; + void __user *argp = (void __user *)arg; + + pr_debug("%s\n", __func__); + if (fp->private_data == NULL) { + pr_err("%s error: fp->private_data is null", __func__); + return -1; + } + priv = (struct hifi4dsp_priv *)fp->private_data; + if (priv == NULL) { + pr_err("%s error: hifi4dsp_priv is null", __func__); + return -1; + } + dsp = priv->dsp; + if (dsp == NULL) { + pr_err("%s hifi4dsp_dsp is null:\n", __func__); + return -1; + } + if (priv->dsp->dsp_fw == NULL) { + pr_err("%s hifi4dsp_firmware is null:\n", __func__); + return -1; + } + if (priv->dsp->fw == NULL) { + pr_err("%s firmware is null:\n", __func__); + return -1; + } + pr_debug("%s %s\n", __func__, priv->dev->kobj.name); + info = kmalloc(sizeof(struct hifi4dsp_info_t), GFP_KERNEL); + if (!info) + return -1; + + switch (cmd) { + case HIFI4DSP_TEST: + pr_debug("%s HIFI4DSP_TEST\n", __func__); + break; + case HIFI4DSP_LOAD: + pr_debug("%s HIFI4DSP_LOAD\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_load_fw(priv->dsp); + break; + case HIFI4DSP_RESET: + pr_debug("%s HIFI4DSP_RESET\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_reset(priv->dsp); + break; + case HIFI4DSP_START: + pr_debug("%s HIFI4DSP_START\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_start(priv->dsp); + break; + case HIFI4DSP_STOP: + pr_debug("%s HIFI4DSP_STOP\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_stop(priv->dsp); + break; + case HIFI4DSP_SLEEP: + pr_debug("%s HIFI4DSP_SLEEP\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_sleep(priv->dsp); + break; + case HIFI4DSP_WAKE: + pr_debug("%s HIFI4DSP_WAKE\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + priv->dsp->info = info; + hifi4dsp_tm2_dsp_wake(priv->dsp); + break; + case HIFI4DSP_GET_INFO: + pr_debug("%s HIFI4DSP_GET_INFO\n", __func__); + ret = copy_from_user(info, argp, + sizeof(struct hifi4dsp_info_t)); + pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__, + info->fw_name); + strcpy(info->fw_name, "1234abcdef"); + ret = copy_to_user(argp, info, + sizeof(struct hifi4dsp_info_t)); + pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__, + info->fw_name); + break; + default: + pr_err("%s ioctl CMD error\n", __func__); + break; + } + kfree(info); + priv->dsp->info = NULL; + + return ret; +} + +static int hifi4dsp_tm2_dsp_load_fw(struct hifi4dsp_dsp *dsp) +{ + int err = 0; + struct hifi4dsp_firmware *new_dsp_fw; + struct hifi4dsp_info_t *info = dsp->info; + + if (strlen(info->fw_name) == 0) + return -1; + pr_debug("info->fw_name:%s\n", info->fw_name); + new_dsp_fw = hifi4dsp_fw_register(dsp, info->fw_name); + if (new_dsp_fw == NULL) { + pr_err("register firmware:%s error\n", info->fw_name); + return -1; + } + dsp->dsp_fw = new_dsp_fw; /*set newest fw as def fw of dsp*/ + strcpy(new_dsp_fw->name, info->fw_name); + if (info->phy_addr != 0) { /*to be improved*/ + //info->phy_addr may !=0, but illegal + new_dsp_fw->paddr = info->phy_addr; + /*TODO*/ + /*new_dsp_fw->buf = phys_to_virt(new_dsp_fw->paddr);*/ + new_dsp_fw->buf = dsp->pdata->fw_buf; + } else { + new_dsp_fw->paddr = dsp->pdata->fw_paddr; + new_dsp_fw->buf = dsp->pdata->fw_buf; + } + pr_debug("new hifi4dsp_firmware, name=%s, paddr=0x%llx, buf=0x%p\n", + new_dsp_fw->name, + (unsigned long long)new_dsp_fw->paddr, + new_dsp_fw->buf); + hifi4dsp_fw_load(new_dsp_fw); + return err; +} + +static int hifi4dsp_tm2_dsp_init(struct hifi4dsp_priv *priv, + struct device *dev, struct hifi4dsp_pdata *pdata, + struct hifi4dsp_dsp_device *dsp_dev) +{ + int err = 0; + struct hifi4dsp_dsp *new_dsp; + struct hifi4dsp_firmware *new_dsp_fw; + struct firmware *fw; + + pr_debug("%s\n", __func__); + /* init dsp */ + new_dsp = hifi4dsp_dsp_new(priv, pdata, dsp_dev); + if (new_dsp == NULL) { + pr_err("%s get new hifi4dsp_dsp error\n", __func__); + err = -ENODEV; + goto dsp_new_err; + } + + /*malloc firmware*/ + fw = kzalloc(sizeof(struct firmware), GFP_KERNEL); + if (new_dsp == NULL) { + pr_err("kzalloc new firmware error\n"); + return -ENOMEM; + } + new_dsp->fw = fw; + /*init id, irq, clk*/ + new_dsp->id = pdata->id; + new_dsp->irq = pdata->irq; + new_dsp->freq = pdata->clk_freq; + + /*init addr*/ + new_dsp->addr.fw_paddr = pdata->fw_paddr; + new_dsp->addr.fw = pdata->fw_buf; + new_dsp->addr.reg = pdata->reg; + new_dsp->addr.reg_size = pdata->reg_size; + /*init mailbox*/ + + /* reset */ + hifi4dsp_dsp_reset(new_dsp); + /* malloc hifi4dsp_firmware & init */ + new_dsp_fw = hifi4dsp_fw_new(new_dsp, new_dsp->fw, priv); + new_dsp->dsp_fw = new_dsp_fw; + + /* add to priv*/ + priv->dsp_fw = new_dsp_fw; + priv->dsp = new_dsp; + + hifi4dsp_dsp_boot(priv->dsp); + + pr_info("%s done\n", __func__); +dsp_new_err: + return err; +} + +static int hifi4dsp_tm2_dsp_reset(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + pr_debug("%s\n", __func__); + if (dsp->info != NULL) + info = (struct hifi4dsp_info_t *)dsp->info; + + dsp->info = NULL; + return 0; +} + +static int hifi4dsp_tm2_dsp_boot(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + info = (struct hifi4dsp_info_t *)dsp->info; + pr_debug("%s\n", __func__); + + + dsp->info = NULL; + return 0; +} + +static int hifi4dsp_tm2_dsp_start(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + pr_debug("%s\n", __func__); + info = (struct hifi4dsp_info_t *)dsp->info; + pr_debug("dsp_id: %d\n", dsp->id); + pr_debug("dsp_freqence: %d Hz\n", dsp->freq); + pr_debug("dsp_start_addr: 0x%llx\n", + (unsigned long long)dsp->dsp_fw->paddr); + + if (dsp->dsp_clk == NULL) + pr_err("dsp_clk=NULL\n"); + if (dsp->dsp_gate == NULL) + pr_err("dsp_gate=NULL\n"); + + //clk_set_rate(dsp->dsp_clk, dsp->freq); + //clk_prepare_enable(dsp->dsp_clk); + //clk_prepare_enable(dsp->dsp_gate); + + tm2_dsp_bootup(dsp->id, dsp->dsp_fw->paddr, dsp->freq); + dsp->info = NULL; + + return 0; +} + +static int hifi4dsp_tm2_dsp_stop(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + info = (struct hifi4dsp_info_t *)dsp->info; + pr_debug("%s\n", __func__); + + + dsp->info = NULL; + return 0; +} + +static int hifi4dsp_tm2_dsp_sleep(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + info = (struct hifi4dsp_info_t *)dsp->info; + pr_debug("%s\n", __func__); + + + dsp->info = NULL; + return 0; +} + +static int hifi4dsp_tm2_dsp_wake(struct hifi4dsp_dsp *dsp) +{ + struct hifi4dsp_info_t *info; + + info = (struct hifi4dsp_info_t *)dsp->info; + pr_debug("%s\n", __func__); + + + dsp->info = NULL; + return 0; +} + +static void hifi4dsp_tm2_dsp_free(struct hifi4dsp_dsp *dsp) +{ + pr_debug("%s\n", __func__); + + hifi4dsp_fw_free_all(dsp); + //kfree(NULL) is safe and check is probably not required + + kfree(dsp->dsp_fw); + dsp->dsp_fw = NULL; + + kfree(dsp->fw); + dsp->fw = NULL; + + kfree(dsp->ops); + dsp->ops = NULL; + + kfree(dsp->pdata); + dsp->pdata = NULL; + + kfree(dsp); +} + +static irqreturn_t hifi4dsp_tm2_dsp_irq(int irq, void *p) +{ + irqreturn_t ret = IRQ_NONE; + u32 isrx; + struct hifi4dsp_dsp *dsp = (struct hifi4dsp_dsp *) p; + + spin_lock(&dsp->spinlock); + isrx = hifi4dsp_dsp_smem_read64_unlocked(dsp, IPC_STS_AP); + //need improvement, process for rx_request rx_done from dsp + spin_unlock(&dsp->spinlock); + ret = IRQ_WAKE_THREAD; + return ret; +} + +static inline u32 hifi4dsp_tm2_ipc_header_msg_id(u64 header) +{ + header &= IPC_MSG_HEADER_DATA_BYTES(IPC_MSG_HEADER_ID_MASK); + return header >> IPC_MSG_HEADER_ID_SHIFT; +} + +static inline u32 hifi4dsp_tm2_ipc_header_msg_data_bytes(u64 header) +{ + header &= IPC_MSG_HEADER_DATA_BYTES(IPC_MSG_HEADER_DATA_BYTES_MASK); + return header >> IPC_MSG_HEADER_DATA_BYTES_SHIFT; +} + +static int hifi4dsp_tm2_process_notification(struct hifi4dsp_priv *priv, + unsigned long *flags) +{ + struct hifi4dsp_dsp *dsp = priv->dsp; + u32 header; + u32 msg_id; + int handled = 1; + + /*todo, read msg header, get MSG_ID*/ + header = hifi4dsp_dsp_smem_read_unlocked(dsp, 0); + msg_id = hifi4dsp_tm2_ipc_header_msg_id(header); + + switch (msg_id) { + case IPC_MSG_DSP_2_AP_0: + break; + case IPC_MSG_DSP_2_AP_1: + break; + } + + return handled; +} + +static int hifi4dsp_tm2_process_reply(struct hifi4dsp_priv *priv, u64 header) +{ + struct hifi4dsp_ipc_message *msg; + + msg = hifi4dsp_ipc_reply_find_msg(&priv->ipc, header); + if (msg == NULL) + return 1; + + if (header & IPC_MSG_HEADER_WITH_DATA(header)) { + msg->rx_size = hifi4dsp_tm2_ipc_header_msg_data_bytes(header); + hifi4dsp_dsp_mailbox_inbox_read(priv->dsp, + msg->rx_data, msg->rx_size); + } + + list_del(&msg->list); + /* wake up */ + hifi4dsp_ipc_tx_msg_reply_complete(&priv->ipc, msg); + + return 1; +} + +static irqreturn_t hifi4dsp_tm2_dsp_irq_thread(int irq, void *context) +{ + struct hifi4dsp_dsp *dsp = (struct hifi4dsp_dsp *) context; + struct hifi4dsp_priv *priv = dsp->priv; + struct hifi4dsp_ipc *ipc = &priv->ipc; + u64 header; + u32 ap_sts; + unsigned long flags; + + spin_lock_irqsave(&dsp->spinlock, flags); + + ap_sts = hifi4dsp_dsp_smem_read_unlocked(dsp, IPC_STS_AP); + header = hifi4dsp_dsp_smem_read64_unlocked(dsp, IPC_MSG_HEADER); + if (header & (IPC_STS_AP_BUSY&ap_sts)) { + if (header&IPC_MSG_HEADER_NOTIFY_MASK) { + /* msg */ + hifi4dsp_tm2_process_notification(priv, &flags); + } else { + /* reply msg */ + hifi4dsp_tm2_process_reply(priv, header); + } + /* + * clear DSP BUSY bit and set DONE bit. Tell DSP we have + * processed the message and can accept new. Clear data part + * of the header + */ + hifi4dsp_dsp_smem_update_bits64_unlocked(dsp, IPC_STS_AP, + IPC_STS_AP_BUSY|IPC_STS_AP_DONE, IPC_STS_AP_DONE); + /* unmask msg request interrupts */ + //hifi4dsp_dsp_smem_update_bits_unlocked(dsp, 0); + } + + spin_unlock_irqrestore(&dsp->spinlock, flags); + + /* continue to send any remaining messages... */ + kthread_queue_work(&ipc->kworker, &ipc->kwork); + + return IRQ_HANDLED; +} + +/*transfer param from pdata to dsp*/ +static int hifi4dsp_tm2_resource_map(struct hifi4dsp_dsp *dsp, + struct hifi4dsp_pdata *pdata) +{ + int ret = 0; + + if (!pdata) { + pr_err("%s error\n", __func__); + ret = -1; + } + return ret; +} + +static int hifi4dsp_tm2_init(struct hifi4dsp_dsp *dsp, + struct hifi4dsp_pdata *pdata) +{ + struct device *dev; + int ret = -ENODEV; + + dev = dsp->dev; + pr_debug("%s\n", __func__); + ret = hifi4dsp_tm2_resource_map(dsp, pdata); + if (ret < 0) { + dev_err(dev, "failed to map resources\n"); + return ret; + } + + //ret = dma_coerce_mask_and_coherent(dsp->dma_dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + /* enable interrupt from both arm sides and dsp side */ + /* todo */ + return 0; +} + +static void hifi4dsp_tm2_free(struct hifi4dsp_priv *priv) +{ + pr_debug("%s\n", __func__); + if (priv->dsp) { + hifi4dsp_tm2_dsp_free(priv->dsp); + priv->dsp = NULL; + } + if (priv->dsp_fw) { + priv->dsp_fw = NULL; + return; + } +} + +static int hifi4dsp_tm2_load_and_parse_fw(struct hifi4dsp_firmware *dsp_fw, + void *pinfo) +{ + struct hifi4dsp_info_t *info; + + info = (struct hifi4dsp_info_t *)pinfo; + pr_debug("%s\n", __func__); + hifi4dsp_tm2_dsp_load_fw(dsp_fw->dsp); + return 0; +} + +struct hifi4dsp_ops; +struct hifi4dsp_ops hifi4dsp_tm2_dsp_ops = { + .boot = hifi4dsp_tm2_dsp_boot, + .reset = hifi4dsp_tm2_dsp_reset, + .sleep = hifi4dsp_tm2_dsp_sleep, + .wake = hifi4dsp_tm2_dsp_wake, + + .write = hifi4dsp_smem_write, + .read = hifi4dsp_smem_read, + .write64 = hifi4dsp_smem_write64, + .read64 = hifi4dsp_smem_read64, + .ram_read = hifi4dsp_memcpy_fromio_32, + .ram_write = hifi4dsp_memcpy_toio_32, + + .irq_handler = hifi4dsp_tm2_dsp_irq, + .init = hifi4dsp_tm2_init, + .free = hifi4dsp_tm2_dsp_free, + .parse_fw = hifi4dsp_tm2_load_and_parse_fw, +}; + +static struct hifi4dsp_pdata dsp_pdatas[] = {/*ARRAY_SIZE(dsp_pdatas)*/ + { + .id = 0, + .name = "hifi4dsp0", + .reg_paddr = 0xFF680000, + .reg_size = 0x00100000, + .clk_freq = 400*1000*1000, + .smem_paddr = DSP_PHY_ADDR_0, + .smem_size = HIFI4DSP_TM2_ALL_SMEM_SIZE, + .irq = 242, + .fw_paddr = DSP_PHY_ADDR_0, + }, + { + .id = 1, + .name = "hifi4dsp1", + .reg_paddr = 0xFF690000, + .reg_size = 0x00100000, + .clk_freq = 400*1000*1000, + .irq = 246, + .fw_paddr = DSP_PHY_ADDR_1, + }, +}; + +static struct hifi4dsp_dsp_device hifi4dsp_dev = { + .thread = hifi4dsp_tm2_dsp_irq_thread, + .ops = &hifi4dsp_tm2_dsp_ops, +}; + +static void hifi4dsp_tm2_ipc_dbg(struct hifi4dsp_ipc *ipc, + const char *dbg_info) +{ + struct hifi4dsp_dsp *dsp = ipc->dsp; + + if (dsp == NULL) { + pr_err("%s ipc->dsp is null\n", __func__); + return; + } + /*dump mailbox register info*/ + pr_debug("%s %s\n", __func__, dbg_info); +} + +/* + * call mailbox function to send the msg or + * directlly to write the msg to share register/memory + * then send notification msg though mailbox to DSP + */ +static void hifi4dsp_tm2_ipc_tx_msg(struct hifi4dsp_ipc *ipc, + struct hifi4dsp_ipc_message *msg) +{ + if (msg->header) + hifi4dsp_dsp_mailbox_outbox_write(ipc->dsp, + msg->tx_data, + msg->tx_size); + /*hifi4dsp_dsp_smem_write_unlocked(ipc->dsp, + * MBOX_TX_SET, + * (u32)(msg->header); + */ +} + +/* + * msg content = lower 32-bit of the header + data + * + */ +static void hifi4dsp_tm2_ipc_data_copy(struct hifi4dsp_ipc_message *msg, + char *tx_data, size_t tx_bytes) +{ + *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1); + memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes); + msg->tx_size += sizeof(u32); +} + +/* + * read from mailbox register or + * share register between dsp and AP or + * share memory eara + * to judge if dsp is busy + */ +static u64 hifi4dsp_tm2_ipc_reply_msg_match(u64 header, u64 *mask) +{ + /* match reply to message sent based on msg and stream IDs */ + *mask = IPC_MSG_HEADER_ID_MASK; + header &= *mask; + return header; +} + +/* + * read from mailbox register or + * share register between dsp and AP or + * share memory eara + * to judge if dsp is busy + */ +static bool hifi4dsp_tm2_is_dsp_busy(struct hifi4dsp_dsp *dsp) +{ + u32 is_busy = 0; + + pr_debug("%s %s busy\n", __func__, is_busy?"":"not"); + return 0; +} + +struct hifi4dsp_ipc_plat_ops hifi4dsp_tm2_ipc_ops = { + .tx_msg = hifi4dsp_tm2_ipc_tx_msg, + .tx_data_copy = hifi4dsp_tm2_ipc_data_copy, + .reply_msg_match = hifi4dsp_tm2_ipc_reply_msg_match, + .is_dsp_busy = hifi4dsp_tm2_is_dsp_busy, + .debug_info = hifi4dsp_tm2_ipc_dbg, +}; + +struct hifi4dsp_priv *hifi4dsp_privdata() +{ + return hifi4dsp_p[0]; +} + +static int hifi4dsp_platform_remove(struct platform_device *pdev) +{ + struct hifi4dsp_priv *priv; + int id = 0, dsp_cnt = 0; + + dsp_cnt = ARRAY_SIZE(dsp_pdatas); + priv = hifi4dsp_privdata(); + for (id = 0; id < dsp_cnt; id++) { + if (!priv) + continue; + hifi4dsp_tm2_free(priv); + if (priv->dev) + device_destroy(priv->class, priv->dev->devt); + priv += 1; + } + kfree(priv); + priv = NULL; + for (id = 0; id < dsp_cnt; id++) + hifi4dsp_p[id] = NULL; + + return 0; +} + +static const struct file_operations hifi4dsp_miscdev_fops = { + .owner = THIS_MODULE, + .open = hifi4dsp_miscdev_open, + .read = NULL, + .write = NULL, + .release = hifi4dsp_miscdev_release, + .unlocked_ioctl = hifi4dsp_miscdev_ioctl, + .compat_ioctl = hifi4dsp_miscdev_ioctl, +}; + +static struct miscdevice hifi4dsp_miscdev[] = { + { + .minor = MISC_DYNAMIC_MINOR, + .name = "hifi4dsp0", + .fops = &hifi4dsp_miscdev_fops, + }, + { + .minor = MISC_DYNAMIC_MINOR, + .name = "hifi4dsp1", + .fops = &hifi4dsp_miscdev_fops, + } +}; + +static int hifi4dsp_priv_data_init(struct hifi4dsp_priv *priv, + struct hifi4dsp_pdata *pdata) +{ + //priv->fw_id = 0; + priv->dsp_is_started = false; + priv->dsp_dev = &hifi4dsp_dev; + priv->pdata = pdata; + HIFI4DSP_PRNT("%s done\n", __func__); + return 0; +} + +static void *hifi4dsp_mm_vmap(phys_addr_t phys, unsigned long size) +{ + u32 offset, npages; + struct page **pages = NULL; + pgprot_t pgprot = PAGE_KERNEL; + void *vaddr; + int i; + + offset = offset_in_page(phys); + npages = DIV_ROUND_UP(size + offset, PAGE_SIZE); + + pages = vmalloc(sizeof(struct page *) * npages); + if (!pages) + return NULL; + for (i = 0; i < npages; i++) { + pages[i] = phys_to_page(phys); + phys += PAGE_SIZE; + } + /* pgprot = pgprot_writecombine(PAGE_KERNEL); */ + + vaddr = vmap(pages, npages, VM_MAP, pgprot); + if (!vaddr) { + pr_err("vmaped fail, size: %d\n", + npages << PAGE_SHIFT); + vfree(pages); + return NULL; + } + vfree(pages); + pr_debug("[HIGH-MEM-MAP] pa(%lx) to va(%p), size: %d\n", + (unsigned long)phys, vaddr, npages << PAGE_SHIFT); + + return vaddr; +} + +/*of read clk_gate, clk*/ +static inline int of_read_dsp_irq( + struct platform_device *pdev, int dsp_id) +{ + int irq = -1; + + if (dsp_id == 0) + irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspa"); + else if (dsp_id == 1) + irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspb"); + + pr_debug("%s %s irq=%d\n", __func__, + (irq < 0)?"error":"successful", irq); + + return irq; +} + +/*of read clk_gate, clk*/ +static inline struct clk *of_read_dsp_clk( + struct platform_device *pdev, int dsp_id) +{ + struct clk *p_clk = NULL; + char clk_name[20]; + + if (dsp_id == 0) { + strcpy(clk_name, "dspa_clk"); + p_clk = devm_clk_get(&pdev->dev, clk_name); + } else if (dsp_id == 1) { + strcpy(clk_name, "dspb_clk"); + p_clk = devm_clk_get(&pdev->dev, clk_name); + } + if (!p_clk) + pr_err("%s %s error\n", __func__, clk_name); + + return p_clk; +} + +/*of read clk_gate, clk*/ +static inline struct clk *of_read_dsp_clk_gate( + struct platform_device *pdev, int dsp_id) +{ + struct clk *p_clk_gate = NULL; + char clk_name[20]; + + if (dsp_id == 0) { + strcpy(clk_name, "dspa_gate"); + p_clk_gate = devm_clk_get(&pdev->dev, clk_name); + } else if (dsp_id == 1) { + strcpy(clk_name, "dspb_gate"); + p_clk_gate = devm_clk_get(&pdev->dev, clk_name); + } + if (!p_clk_gate) + pr_err("%s %s error\n", __func__, clk_name); + + return p_clk_gate; +} + +static int hifi4dsp_platform_probe(struct platform_device *pdev) +{ + int ret = 0; + int i = 0, id = 0; + unsigned int dsp_cnt = 0; + struct hifi4dsp_priv *priv; + struct hifi4dsp_pdata *pdata; + struct hifi4dsp_miscdev_t *p_dsp_miscdev; + struct miscdevice *pmscdev; + struct page *page; + struct resource res_mem; + int mem_bytes; + void *fw_addr = NULL; + void __iomem *dsp_regbase; + int irq; + struct device_node *np; + struct clk *dsp_clk = NULL; + struct clk *dsp_gate = NULL; + + np = pdev->dev.of_node; + dsp_cnt = ARRAY_SIZE(dsp_pdatas); + pr_debug("%s pdatas: dsp_cnt=%d\n", __func__, dsp_cnt); + ret = of_property_read_u32(np, "dsp-cnt", &dsp_cnt); + if (ret < 0) { + dev_err(&pdev->dev, "Can't retrieve dsp-cnt\n"); + ret = -EINVAL; + goto dsp_cnt_error; + } + pr_debug("%s of read dsp-cnt=%d\n", __func__, dsp_cnt); + + /*init miscdev_t, miscdevice*/ + p_dsp_miscdev = NULL; + p_dsp_miscdev = kcalloc(dsp_cnt, sizeof(struct hifi4dsp_miscdev_t), + GFP_KERNEL); + if (p_dsp_miscdev == NULL) { + HIFI4DSP_PRNT("kzalloc for p_dsp_miscdev error\n"); + ret = -ENOMEM; + goto miscdev_malloc_error; + } + + /*init hifi4dsp_priv*/ + priv = NULL; + priv = kcalloc(dsp_cnt, sizeof(struct hifi4dsp_priv), + GFP_KERNEL); + if (priv == NULL) { + HIFI4DSP_PRNT("kzalloc for hifi4dsp_priv error\n"); + ret = -ENOMEM; + goto priv_malloc_error; + } + /*of read reserved memory*/ + ret = of_reserved_mem_device_init(&pdev->dev); + if (ret) { + pr_err("reserved memory init fail:%d\n", ret); + ret = -ENOMEM; + goto reserved_mem_alloc_error; + } + + for (i = 0; i < dsp_cnt; i++) { + id = i; + p_dsp_miscdev += i; + priv += i; + pr_info("register dsp-%d start\n", id); + + memcpy(&(p_dsp_miscdev->dsp_miscdev), + &hifi4dsp_miscdev[id], sizeof(struct miscdevice)); + pmscdev = &p_dsp_miscdev->dsp_miscdev; + + p_dsp_miscdev->priv = priv; + + /*of read reg base, ioremap it*/ + //res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (of_address_to_resource(np, id, &res_mem)) { + ret = -EINVAL; + pr_err("%s didn't get iomem from dts\n", __func__); + goto error2; + } + dsp_regbase = ioremap_nocache(res_mem.start, + resource_size(&res_mem)); + pr_debug("dsp_regbase ioremap, reg:%x->%p, size:%x, %s\n", + (u32)res_mem.start, + dsp_regbase, + (u32)resource_size(&res_mem), + (dsp_regbase != NULL)?"successful":"error"); + + /*of read irq num*/ + irq = of_read_dsp_irq(pdev, id); + if (irq < 0) { + ret = -EINVAL; + goto error2; + } + + /*cma alloc mem for firmware*/ + if (of_property_read_u32(np, "reserved_mem_size", &mem_bytes)) { + mem_bytes = DEF_RESERVE_MEM_SIZE; + pr_debug("of read reserved_mem_size error, def value:0x%x\n", + mem_bytes); + } else + pr_info("reserved_mem_size:0x%x\n", mem_bytes); + + page = dma_alloc_from_contiguous(&pdev->dev, + mem_bytes >> PAGE_SHIFT, 0); + if (!page) { + pr_err("alloc page failed, ret:%p\n", page); + return -ENOMEM; + } + if (!PageHighMem(page)) { + //fw_addr = page_address(page); + fw_addr = phys_to_virt(page_to_phys(page)); + } else { + fw_addr = hifi4dsp_mm_vmap(page_to_phys(page), + mem_bytes); + pr_info("kernel addr map phys:0x%llx->virt:0x%p\n", + (unsigned long long)page_to_phys(page), + fw_addr); + } + pr_debug("get page:%p, 0x%lx, phys:0x%llx, virt:0x%p\n", + page, + page_to_pfn(page), + (unsigned long long)page_to_phys(page), + page_address(page)); + + /*of read clk_gate, clk*/ + dsp_gate = of_read_dsp_clk_gate(pdev, id); + dsp_clk = of_read_dsp_clk(pdev, id); + priv->p_clk = dsp_clk; + priv->p_clk_gate = dsp_gate; + + /*register dsp device*/ + //pmscdev = &hifi4dsp_miscdev; + ret = misc_register(pmscdev); + if (ret) { + pr_err("register vad_miscdev error\n"); + goto error2; + } + priv->dev = pmscdev->this_device; + + pdata = &dsp_pdatas[i]; + pdata->fw_paddr = page_to_phys(page); + pdata->fw_buf = fw_addr; + pdata->fw_max_size = mem_bytes; + pdata->reg_size = (u32)resource_size(&res_mem); + pdata->reg = dsp_regbase; + id = pdata->id; + pdata->irq = irq; + hifi4dsp_priv_data_init(priv, pdata); + hifi4dsp_tm2_dsp_init(priv, priv->dev, + priv->pdata, priv->dsp_dev); + priv->dsp->dsp_clk = priv->p_clk; + priv->dsp->dsp_gate = priv->p_clk_gate; + + hifi4dsp_p[i] = priv; + //priv += 1; + priv->dev = pmscdev->this_device; + dev_set_drvdata(priv->dev, priv); + + pr_info("register dsp-%d done\n", id); + } + ret = 0; + //tm2_dsp_hw_init(priv->dsp->id, priv->dsp->freq); + tm2_dsp_regs_iomem_init(); + + pr_info("%s done\n", __func__); + + goto done; + +error2: +reserved_mem_alloc_error: + kfree(priv); +priv_malloc_error: + kfree(p_dsp_miscdev); +miscdev_malloc_error: +dsp_cnt_error: +done: + return ret; +} + +static const struct of_device_id hifi4dsp_device_id[] = { + { + .compatible = "amlogic, hifi4dsp", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, hifi4dsp_device_id); + +static struct platform_driver hifi4dsp_platform_driver = { + .driver = { + .name = "hifi4dsp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hifi4dsp_device_id), + }, + .probe = hifi4dsp_platform_probe, + .remove = hifi4dsp_platform_remove, +}; +module_platform_driver(hifi4dsp_platform_driver); + +MODULE_AUTHOR("Shuyu Li"); +MODULE_DESCRIPTION("HiFi DSP Module Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h b/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h new file mode 100644 index 000000000000..3a3939372a3f --- /dev/null +++ b/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h @@ -0,0 +1,78 @@ +/* + * drivers/amlogic/hifi4dsp/hifi4dsp_priv.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _HIFI4DSP_PRIV_H +#define _HIFI4DSP_PRIV_H +#include +#include +#include + +/* + * #include + * #include + */ +//#include "hifi4dsp_control.h" +#include + +#include + +#include "hifi4dsp_api.h" +#include "hifi4dsp_dsp.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_ipc.h" + +struct class; + +struct hifi4dsp_priv { + char name[12]; + struct class *class; + struct device *dev; + struct device *dma_dev; + + u32 dsp_freq; + bool dsp_is_started; + + struct hifi4dsp_dsp *dsp; + struct hifi4dsp_dsp_device *dsp_dev; + struct hifi4dsp_firmware *dsp_fw; + struct hifi4dsp_mailbox *mailbox; + struct hifi4dsp_pdata *pdata; + struct hifi4dsp_ipc ipc; + + struct clk *p_clk; + struct clk *p_clk_gate; +}; + +struct hifi4dsp_miscdev_t { + struct miscdevice dsp_miscdev; + struct hifi4dsp_priv *priv; +}; + +struct hifi4dsp_resource_t { + struct resource res_iomem; + struct clk *p_clk_gate; + struct clk *p_clk; + int irq; +}; + +struct hifi4dsp_priv *hifi4dsp_privdata(void); + +#ifndef HIFI4DSP_PRNT +#define HIFI4DSP_PRNT(...) pr_info(__VA_ARGS__) +#endif + +#endif /*_HIFI4DSP_PRIV_H*/ diff --git a/drivers/amlogic/hifi4dsp/tm2_dsp_top.c b/drivers/amlogic/hifi4dsp/tm2_dsp_top.c new file mode 100644 index 000000000000..b8625ca6eb63 --- /dev/null +++ b/drivers/amlogic/hifi4dsp/tm2_dsp_top.c @@ -0,0 +1,525 @@ +/* + * drivers/amlogic/hifi4dsp/tm2_dsp_top.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" + +/*AO_RTI*/ +#define AO_RTI_ADDR_BASE (0xff800000) +#define REG_AO_RTI_GEN_PWR_SLEEP0 (0x03a << 2) +#define REG_AO_RTI_GEN_PWR_ISO0 (0x03b << 2) + +/*HIU*/ +#define HUI_ADDR_BASE (0xff63c000) +#define REG_HHI_DSP_CLK_CNTL (0x0fc << 2) +#define REG_HHI_DSP_MEM_PD_REG0 (0x044 << 2) + +/*reset*/ +#define RESET_ADDR_BASE (0xffd01000) +#define REG_RESET1_LEVEL (0x84) +#define REG_RESET4_LEVEL (0x90) + +/*DSP TOP*/ +#define DSPA_REG_BASE (0xff680000) +#define DSPB_REG_BASE (0xff690000) + +#define REG_DSP_CFG0 (0x0) +#define REG_DSP_CFG1 (0x4) +#define REG_DSP_CFG2 (0x8) +#define REG_DSP_RESET_VEC (0x004 << 2) + +struct reg_iomem_t { + void __iomem *dsp_addr; + void __iomem *dspa_addr; + void __iomem *dspb_addr; + void __iomem *hiu_addr; /*HIU*/ + void __iomem *ao_rti_addr; /*AO_RTI*/ + void __iomem *reset_addr; + void __iomem *sleep_addr; +}; + +static struct reg_iomem_t g_regbases; +static bool regs_iomem_is_inited; + +static void __iomem *reg_iomem_init(phys_addr_t paddr, u32 size) +{ + void __iomem *vaddr = NULL; + + vaddr = ioremap_nocache(paddr, size); + pr_debug("%s phys: %llx, virt: %p, size:%x Bytes\n", + __func__, + (unsigned long long)paddr, + vaddr, + size); + return vaddr; +} + +static inline void tm2_dsp_top_reg_dump(char *name, + void __iomem *reg_base, u32 reg_offset) +{ + pr_info("%s (%p) = 0x%x\n", name, + (reg_base + reg_offset), + readl(reg_base + reg_offset)); +} + +void tm2_dsp_regs_iomem_init(void) +{ + if (regs_iomem_is_inited == true) { + pr_info("%s has been done\n", __func__); + return; + } + + g_regbases.dspa_addr = reg_iomem_init(DSPA_REG_BASE, 0x8B*4); + g_regbases.dspb_addr = reg_iomem_init(DSPB_REG_BASE, 0x8B*4); + + g_regbases.hiu_addr = reg_iomem_init(HUI_ADDR_BASE, 0xFF*4); + g_regbases.ao_rti_addr = reg_iomem_init(AO_RTI_ADDR_BASE, 0xFF*4); + g_regbases.reset_addr = reg_iomem_init(RESET_ADDR_BASE, 0x40*4); + + regs_iomem_is_inited = true; + + pr_debug("%s done\n", __func__); +} + +void tm2_dsp_regs_iounmem(void) +{ + iounmap(g_regbases.dspa_addr); + iounmap(g_regbases.dspb_addr); + + iounmap(g_regbases.hiu_addr); + iounmap(g_regbases.ao_rti_addr); + iounmap(g_regbases.reset_addr); + + regs_iomem_is_inited = false; + + pr_debug("%s done\n", __func__); +} + +static inline void __iomem *get_hiu_addr(void) +{ + return g_regbases.hiu_addr; +} + +static inline void __iomem *get_ao_rti_addr(void) +{ + return g_regbases.ao_rti_addr; +} + +static inline void __iomem *get_reset_addr(void) +{ + return g_regbases.reset_addr; +} + +static inline void __iomem *get_dsp_addr(int dsp_id) +{ + if (dsp_id == 1) + return g_regbases.dspb_addr; + else + return g_regbases.dspa_addr; +} + +/* + * clk_util_set_dsp_clk + * freq_sel: 0:286MHz fclk_7 + * 1:400MHz fclk_5 + * 2:500MHz fclk_2/2 + * 3:667MHz fclk_3 + * 4:1000MHz fclk_2 + * others:286MHz fclk/7 + */ +//crt_clk_div_mux4_ns #(8) +// u_crt_dspa_clk_mux_div( +// .clk0 (fclk_div2 ), +// .clk1 (fclk_div3 ), +// .clk2 (fclk_div5 ), +// .clk3 (fclk_div7 ), +// .reset_n (crt_reset_n ), +// .force_oscin_clk (1'b0 ), +// .cts_oscin_clk (1'b0 ), +// +// .clk_div (hi_dsp_clk_cntl[7:0] ), +// .clk_en (hi_dsp_clk_cntl[15] ), +// .clk_sel (hi_dsp_clk_cntl[9:8] ), +// .clk_out (cts_dspa_clk )); + +static void clk_util_set_dsp_clk(uint32_t id, uint32_t freq_sel) +{ + uint32_t clk_sel = 0; + uint32_t clk_div = 0; + uint32_t read; + void __iomem *reg; + + reg = get_hiu_addr() + REG_HHI_DSP_CLK_CNTL; + switch (freq_sel) { + case 1: + clk_sel = 2; + clk_div = 0; + pr_debug("CLK_UTIL:dsp:fclk/5:400MHz\n"); + break; + case 2: + clk_sel = 0; + clk_div = 1; + pr_debug("CLK_UTIL:dsp:fclk/4:500MHz\n"); + break; + case 3: + //clk_sel = 1; + //clk_div = 0; + //pr_debug("CLK_UTIL:dsp:fclk/3:667MHz\n"); + break; + case 4: + clk_sel = 1; + clk_div = 1; + pr_debug("CLK_UTIL:dsp:fclk/3/2:333MHz\n"); + break; + case 5: + clk_sel = 0; + clk_div = 3; + pr_debug("CLK_UTIL:dsp:fclk/2:250MHz\n"); + break; + case 6: + clk_sel = 2; + clk_div = 1; + pr_debug("CLK_UTIL:dsp:fclk/4/2:200MHz\n"); + break; + case 7: + clk_sel = 2; + clk_div = 3; + pr_debug("CLK_UTIL:dsp:fclk/4/4:100MHz\n"); + break; + case 8: + clk_sel = 4; + clk_div = 0; + pr_debug("CLK_UTIL:dsp:oscin:24MHz\n"); + break; + case 10: + //clk_sel = 0; + //clk_div = 0; + //pr_debug("CLK_UTIL:dsp:fclk/2:1000MHz\n"); + break; + default: + clk_sel = 3; + clk_div = 0; + pr_debug("CLK_UTIL:dsp:fclk/7:286MHz\n"); + break; + } + + read = readl(reg); + if (id == 0) { + //read = (read & ~((0x3<<8) | (0xff<<0))); + //read = read | ((1<<15) | (clk_sel<<8) | (clk_div<<0)); + if (read & (1 << 15)) { //if sync_mux ==1, sel mux 0 + read &= (~((1 << 15) | (0xf << 0) | (0x7 << 4))); + read |= (1 << 7) | (clk_div << 0) | (clk_sel << 4); + } else { + read &= (~((1 << 15) | (0xf << 8) | (0x7 << 12))); + read |= (1 << 7) | (clk_div << 8); + read |= (clk_sel << 12) | (1 << 15); + } + } else { + //read = (read & ~((0x3<<24) | (0xff<<16))); + //read = read | ((1<<31) | (clk_sel<<24) | (clk_div<<16)); + if (read & (1 << 31)) { //if sync_mux ==1, sel mux 0 + read &= (~((1 << 31) | (0xf << 16) | (0x7 << 20))); + read |= (1 << 23) | (clk_div << 16) | (clk_sel << 20); + } else { + read &= (~((1 << 31) | (0xf << 24) | (0x7 << 28))); + read |= (1 << 23) | (clk_div << 24); + read |= (clk_sel << 28) | (1 << 31); + } + } + writel(read, reg); + + pr_debug("%s\n", __func__); + +} + +static void start_dsp(uint32_t dsp_id, uint32_t reset_addr) +{ + uint32_t StatVectorSel; + uint32_t strobe = 1; + uint32_t tmp; + uint32_t read; + void __iomem *reg; + + reg = get_dsp_addr(dsp_id); + StatVectorSel = (reset_addr != 0xfffa0000); + + // the bit 0 is no use, dsp in tm2 is non-secure + tmp = 0x1 | StatVectorSel<<1 | strobe<<2; + scpi_init_dsp_cfg0(dsp_id, reset_addr, tmp); + + + read = readl(reg+REG_DSP_CFG0); + pr_debug("REG_DSP_CFG0 read=0x%x\n", read); + if (dsp_id == 0) { + read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0))); + read = read | (1 << 29) | (0 << 0); // 29 irq_clk_en + } else { + read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0))); + read = read | (1 << 29) | (1 << 0); + } + writel(read, reg+REG_DSP_CFG0); + tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0); + + pr_debug("%s\n", __func__); + +} + +static void power_switch_to_dsp_a(int pwr_cntl) +{ + uint32_t tmp; + void __iomem *reg; + + pr_info("[PWR]: Power %s DSP A\n", pwr_cntl?"On":"Off"); + if (pwr_cntl == 1) { + // Powerup dsp a + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0; + tmp = readl(reg) & (~(0x1<<21)); + writel(tmp, reg);// power on + udelay(5); + + // Power up memory + reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0; + tmp = readl(reg) & (~(0xffff<<0)); + writel(tmp, reg); + udelay(5); + + // reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) & (~(0x1<<0)); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) & (~(0x1<<20)); + writel(tmp, reg); + + // remove isolation + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0; + tmp = readl(reg) & (~(0x1<<21)); + writel(tmp, reg); + + // pull up reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) | (0x1<<0); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) | (0x1<<20); + writel(tmp, reg); + } else { + // reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) & (~(0x1<<0)); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) & (~(0x1<<20)); + writel(tmp, reg); + + // add isolation + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0; + tmp = readl(reg) | (0x1<<21); + writel(tmp, reg); + udelay(5); + + // power down memory + reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0; + tmp = readl(reg) | (0xffff<<0); + writel(tmp, reg); + udelay(5); + + // power down dsp a + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0; + tmp = readl(reg) | (0x1<<21); + writel(tmp, reg); + udelay(5); + } + +} + +static void power_switch_to_dsp_b(int pwr_cntl) +{ + uint32_t tmp; + void __iomem *reg; + + if (pwr_cntl == 1) { + pr_info("[PWR]: Power on DSP B\n"); + // Powerup dsp a + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0; + tmp = readl(reg) & (~(0x1<<22)); + writel(tmp, reg);// power on + udelay(5); + + // Power up memory + reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0; + tmp = readl(reg) & (~(0xffff<<16)); + writel(tmp, reg); + udelay(5); + + // reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) & (~(0x1<<1)); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) & (~(0x1<<21)); + writel(tmp, reg); + + // remove isolation + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0; + tmp = readl(reg) & (~(0x1<<22)); + writel(tmp, reg); + + // pull up reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) | (0x1<<1); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) | (0x1<<21); + writel(tmp, reg); + } else { + pr_info("[PWR]: Power off DSP B\n"); + // reset + reg = get_reset_addr() + REG_RESET4_LEVEL; + tmp = readl(reg) & (~(0x1<<1)); + writel(tmp, reg); + + reg = get_reset_addr() + REG_RESET1_LEVEL; + tmp = readl(reg) & (~(0x1<<21)); + writel(tmp, reg); + + // add isolation + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0; + tmp = readl(reg) | (0x1<<22); + writel(tmp, reg); + udelay(5); + + // power down memory + reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0; + tmp = readl(reg) | (0xffff<<16); + writel(tmp, reg); + udelay(5); + + // power down dsp a + reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0; + tmp = readl(reg) | (0x1<<22); + writel(tmp, reg); + udelay(5); + } + +} + +static void tm2_dsp_power_switch(int dsp_id, int pwr_cntl) +{ + if (dsp_id == 0) + power_switch_to_dsp_a(pwr_cntl); + else if (dsp_id == 1) + power_switch_to_dsp_b(pwr_cntl); + else + pr_err("%s param: dsp_id=%d error\n", __func__, dsp_id); +} + +void tm2_dsp_top_regs_dump(int dsp_id) +{ + void __iomem *reg; + + pr_debug("%s\n", __func__); + + reg = get_dsp_addr(dsp_id); + pr_debug("%s base=%p\n", __func__, reg); + + tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0); + tm2_dsp_top_reg_dump("REG_DSP_CFG1", reg, REG_DSP_CFG1); + tm2_dsp_top_reg_dump("REG_DSP_CFG2", reg, REG_DSP_CFG2); + tm2_dsp_top_reg_dump("REG_DSP_RESET_VEC", reg, REG_DSP_RESET_VEC); + + reg = get_hiu_addr(); + tm2_dsp_top_reg_dump("REG_HHI_DSP_CLK_CNTL", reg, + REG_HHI_DSP_CLK_CNTL); + tm2_dsp_top_reg_dump("REG_HHI_DSP_MEM_PD_REG0", reg, + REG_HHI_DSP_MEM_PD_REG0); + + reg = get_ao_rti_addr(); + tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_SLEEP0", reg, + REG_AO_RTI_GEN_PWR_SLEEP0); + tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_ISO0", reg, + REG_AO_RTI_GEN_PWR_ISO0); + + reg = get_reset_addr(); + tm2_dsp_top_reg_dump("REG_RESET1_LEVEL", reg, REG_RESET1_LEVEL); + tm2_dsp_top_reg_dump("REG_RESET4_LEVEL", reg, REG_RESET4_LEVEL); + +} + +static void tm2_dsp_set_clk(int dsp_id, int freq_sel) +{ + clk_util_set_dsp_clk(dsp_id, freq_sel); +} + +void tm2_dsp_hw_init(int dsp_id, int freq_sel) +{ + int pwr_cntl = 1; + + tm2_dsp_regs_iomem_init(); + tm2_dsp_set_clk(dsp_id, freq_sel); + tm2_dsp_power_switch(dsp_id, pwr_cntl); + + pr_debug("%s done\n", __func__); +} + +void tm2_dsp_start(int dsp_id, int freq_sel) +{ + start_dsp(dsp_id, freq_sel); +} + +void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel) +{ + int pwr_cntl = 1; + + //reset_addr = 0x30000000; + //dsp_id = 0; + freq_sel = 1; + + pr_debug("%s dsp_id=%d, address=0x%x\n", + __func__, dsp_id, reset_addr); + + tm2_dsp_set_clk(dsp_id, freq_sel); + tm2_dsp_power_switch(dsp_id, pwr_cntl); + tm2_dsp_start(dsp_id, reset_addr); + + msleep(5*1000); + tm2_dsp_top_regs_dump(dsp_id); +} diff --git a/drivers/amlogic/hifi4dsp/tm2_dsp_top.h b/drivers/amlogic/hifi4dsp/tm2_dsp_top.h new file mode 100644 index 000000000000..7173b28beddd --- /dev/null +++ b/drivers/amlogic/hifi4dsp/tm2_dsp_top.h @@ -0,0 +1,36 @@ +/* + * drivers/amlogic/hifi4dsp/tm2_dsp_top.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _TM2_DSP_TOP_H +#define _TM2_DSP_TOP_H + +#include +#include +#include +#include +#include + +#include "hifi4dsp_priv.h" +#include "hifi4dsp_firmware.h" +#include "hifi4dsp_dsp.h" + +extern void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel); +extern void tm2_dsp_regs_iomem_init(void); +extern void tm2_dsp_hw_init(int dsp_id, int freq_sel); +extern void tm2_dsp_top_regs_dump(int dsp_id); + +#endif /*_TM2_DSP_TOP_H*/ diff --git a/drivers/amlogic/media/vin/tvin/hdmirx/hdmi_rx_hw.c b/drivers/amlogic/media/vin/tvin/hdmirx/hdmi_rx_hw.c index 5b06ef0ba93d..f18f280f1cd8 100644 --- a/drivers/amlogic/media/vin/tvin/hdmirx/hdmi_rx_hw.c +++ b/drivers/amlogic/media/vin/tvin/hdmirx/hdmi_rx_hw.c @@ -89,6 +89,8 @@ int hdcp22_on; MODULE_PARM_DESC(hdcp22_on, "\n hdcp22_on\n"); module_param(hdcp22_on, int, 0664); +/* test for HBR CTS, audio module can set it to force 8ch */ +int hbr_force_8ch = 1; /* * hdcp14_key_mode:hdcp1.4 key handle method select * NORMAL_MODE:systemcontrol path @@ -1793,130 +1795,146 @@ else */ int hdmirx_audio_init(void) { -/* 0=I2S 2-channel; 1=I2S 4 x 2-channel. */ -int err = 0; -unsigned long data32 = 0; + /* 0=I2S 2-channel; 1=I2S 4 x 2-channel. */ + int err = 0; + unsigned long data32 = 0; -data32 |= 7 << 13; -data32 |= 0 << 12; -data32 |= 1 << 11; -data32 |= 0 << 10; + data32 |= 7 << 13; + data32 |= 0 << 12; + data32 |= 1 << 11; + data32 |= 0 << 10; -data32 |= 0 << 9; -data32 |= 1 << 8; -data32 |= 1 << 6; -data32 |= 3 << 4; -data32 |= 0 << 3; -data32 |= acr_mode << 2; -data32 |= acr_mode << 1; -data32 |= acr_mode << 0; -hdmirx_wr_top(TOP_ACR_CNTL_STAT, data32); + data32 |= 0 << 9; + data32 |= 1 << 8; + data32 |= 1 << 6; + data32 |= 3 << 4; + data32 |= 0 << 3; + data32 |= acr_mode << 2; + data32 |= acr_mode << 1; + data32 |= acr_mode << 0; + hdmirx_wr_top(TOP_ACR_CNTL_STAT, data32); -/* - *recover to default value, bit[27:24] - *set aud_pll_lock filter - *data32 = 0; - *data32 |= 0 << 28; - *data32 |= 0 << 24; - *hdmirx_wr_dwc(DWC_AUD_PLL_CTRL, data32); - */ + if (rx.chip_id >= CHIP_ID_TL1) { + data32 = 0; + data32 |= 0 << 2;/*meas_mode*/ + data32 |= 1 << 1;/*enable*/ + data32 |= 1 << 0;/*reset*/ + if (acr_mode) + data32 |= 2 << 16;/*aud pll*/ + else + data32 |= 500 << 16;/*acr*/ + hdmirx_wr_top(TOP_AUDMEAS_CTRL, data32); + hdmirx_wr_top(TOP_AUDMEAS_CYCLES_M1, 65535); + /*start messure*/ + hdmirx_wr_top(TOP_AUDMEAS_CTRL, data32 & (~0x1)); + } -/* AFIFO depth 1536word.*/ -/*increase start threshold to middle position */ -data32 = 0; -data32 |= 160 << 18; /* start */ -data32 |= 200 << 9; /* max */ -data32 |= 8 << 0; /* min */ -hdmirx_wr_dwc(DWC_AUD_FIFO_TH, data32); + /* + *recover to default value, bit[27:24] + *set aud_pll_lock filter + *data32 = 0; + *data32 |= 0 << 28; + *data32 |= 0 << 24; + *hdmirx_wr_dwc(DWC_AUD_PLL_CTRL, data32); + */ -/* recover to default value.*/ -/*remain code for some time.*/ -/*if no side effect then remove it */ -/* - *data32 = 0; - *data32 |= 1 << 16; - *data32 |= 0 << 0; - *hdmirx_wr_dwc(DWC_AUD_FIFO_CTRL, data32); - */ + /* AFIFO depth 1536word.*/ + /*increase start threshold to middle position */ + data32 = 0; + data32 |= 160 << 18; /* start */ + data32 |= 200 << 9; /* max */ + data32 |= 8 << 0; /* min */ + hdmirx_wr_dwc(DWC_AUD_FIFO_TH, data32); -data32 = 0; -data32 |= 0 << 8; -data32 |= 1 << 7; -data32 |= aud_ch_map << 2; -data32 |= 1 << 0; -hdmirx_wr_dwc(DWC_AUD_CHEXTR_CTRL, data32); + /* recover to default value.*/ + /*remain code for some time.*/ + /*if no side effect then remove it */ + /* + *data32 = 0; + *data32 |= 1 << 16; + *data32 |= 0 << 0; + *hdmirx_wr_dwc(DWC_AUD_FIFO_CTRL, data32); + */ -data32 = 0; -/* [22:21] aport_shdw_ctrl */ -data32 |= 3 << 21; -/* [20:19] auto_aclk_mute */ -data32 |= auto_aclk_mute << 19; -/* [16:10] aud_mute_speed */ -data32 |= 1 << 10; -/* [7] aud_avmute_en */ -data32 |= aud_avmute_en << 7; -/* [6:5] aud_mute_sel */ -data32 |= aud_mute_sel << 5; -/* [4:3] aud_mute_mode */ -data32 |= 1 << 3; -/* [2:1] aud_ttone_fs_sel */ -data32 |= 0 << 1; -/* [0] testtone_en */ -data32 |= 0 << 0; -hdmirx_wr_dwc(DWC_AUD_MUTE_CTRL, data32); + data32 = 0; + data32 |= 0 << 8; + data32 |= 1 << 7; + data32 |= aud_ch_map << 2; + data32 |= 1 << 0; + hdmirx_wr_dwc(DWC_AUD_CHEXTR_CTRL, data32); -/* recover to default value.*/ -/*remain code for some time.*/ -/*if no side effect then remove it */ -/* - *data32 = 0; - *data32 |= 0 << 16; - *data32 |= 0 << 12; - *data32 |= 0 << 4; - *data32 |= 0 << 1; - *data32 |= 0 << 0; - *hdmirx_wr_dwc(DWC_AUD_PAO_CTRL, data32); - */ + data32 = 0; + /* [22:21] aport_shdw_ctrl */ + data32 |= 3 << 21; + /* [20:19] auto_aclk_mute */ + data32 |= auto_aclk_mute << 19; + /* [16:10] aud_mute_speed */ + data32 |= 1 << 10; + /* [7] aud_avmute_en */ + data32 |= aud_avmute_en << 7; + /* [6:5] aud_mute_sel */ + data32 |= aud_mute_sel << 5; + /* [4:3] aud_mute_mode */ + data32 |= 1 << 3; + /* [2:1] aud_ttone_fs_sel */ + data32 |= 0 << 1; + /* [0] testtone_en */ + data32 |= 0 << 0; + hdmirx_wr_dwc(DWC_AUD_MUTE_CTRL, data32); -/* recover to default value.*/ -/*remain code for some time.*/ -/*if no side effect then remove it */ -/* - *data32 = 0; - *data32 |= 0 << 8; - *hdmirx_wr_dwc(DWC_PDEC_AIF_CTRL, data32); - */ + /* recover to default value.*/ + /*remain code for some time.*/ + /*if no side effect then remove it */ + /* + *data32 = 0; + *data32 |= 0 << 16; + *data32 |= 0 << 12; + *data32 |= 0 << 4; + *data32 |= 0 << 1; + *data32 |= 0 << 0; + *hdmirx_wr_dwc(DWC_AUD_PAO_CTRL, data32); + */ -data32 = 0; -/* [4:2] deltacts_irqtrig */ -data32 |= 0 << 2; -/* [1:0] cts_n_meas_mode */ -data32 |= 0 << 0; -/* DEFAULT: {27'd0, 3'd0, 2'd1} */ -hdmirx_wr_dwc(DWC_PDEC_ACRM_CTRL, data32); + /* recover to default value.*/ + /*remain code for some time.*/ + /*if no side effect then remove it */ + /* + *data32 = 0; + *data32 |= 0 << 8; + *hdmirx_wr_dwc(DWC_PDEC_AIF_CTRL, data32); + */ -hdmirx_wr_bits_dwc(DWC_AUD_CTRL, DWC_AUD_HBR_ENABLE, 1); + data32 = 0; + /* [4:2] deltacts_irqtrig */ + data32 |= 0 << 2; + /* [1:0] cts_n_meas_mode */ + data32 |= 0 << 0; + /* DEFAULT: {27'd0, 3'd0, 2'd1} */ + hdmirx_wr_dwc(DWC_PDEC_ACRM_CTRL, data32); -/* SAO cfg, disable I2S output, no use */ -data32 = 0; -data32 |= 1 << 10; -data32 |= 0 << 9; -data32 |= 0x0f << 5; -data32 |= 0 << 1; -data32 |= 1 << 0; -hdmirx_wr_dwc(DWC_AUD_SAO_CTRL, data32); + /* unsupport HBR serial mode. invalid bit */ + /* hdmirx_wr_bits_dwc(DWC_AUD_CTRL, DWC_AUD_HBR_ENABLE, 1); */ -data32 = 0; -data32 |= 1 << 6; -data32 |= 0xf << 2; -hdmirx_wr_dwc(DWC_PDEC_ASP_CTRL, data32); + /* SAO cfg, disable I2S output, no use */ + data32 = 0; + data32 |= 1 << 10; + data32 |= 0 << 9; + data32 |= 0x0f << 5; + data32 |= 0 << 1; + data32 |= 1 << 0; + hdmirx_wr_dwc(DWC_AUD_SAO_CTRL, data32); -return err; + data32 = 0; + data32 |= 1 << 6; + data32 |= 0xf << 2; + hdmirx_wr_dwc(DWC_PDEC_ASP_CTRL, data32); + + return err; } /* -* snps phy g3 initial -*/ + * snps phy g3 initial + */ void snps_phyg3_init(void) { unsigned int data32; diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index b886a608ef8d..c7ad282b0532 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -151,6 +151,26 @@ struct __extcon_info { .id = EXTCON_SPDIFIN_AUDIOTYPE, .name = "SPDIFIN-AUDIOTYPE", }, + [EXTCON_EARCRX_ATNDTYP_ARC] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_EARCRX_ATNDTYP_ARC, + .name = "EARCRX-ARC", + }, + [EXTCON_EARCRX_ATNDTYP_EARC] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_EARCRX_ATNDTYP_EARC, + .name = "EARCRX-EARC", + }, + [EXTCON_EARCTX_ATNDTYP_ARC] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_EARCTX_ATNDTYP_ARC, + .name = "EARCTX-ARC", + }, + [EXTCON_EARCTX_ATNDTYP_EARC] = { + .type = EXTCON_TYPE_MISC, + .id = EXTCON_EARCTX_ATNDTYP_EARC, + .name = "EARCTX-EARC", + }, #endif /* Display external connector */ [EXTCON_DISP_HDMI] = { diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 190a878ba6f7..6ac7d0a0d398 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -67,6 +67,10 @@ #ifdef CONFIG_AMLOGIC_SND_SOC_AUGE #define EXTCON_SPDIFIN_SAMPLERATE 28 /* spdif in sample rate changed */ #define EXTCON_SPDIFIN_AUDIOTYPE 29 /* spdif in PcPd detect */ +#define EXTCON_EARCRX_ATNDTYP_ARC 30 /* attended type, RX ARC */ +#define EXTCON_EARCRX_ATNDTYP_EARC 31 /* attended type, RX eARC */ +#define EXTCON_EARCTX_ATNDTYP_ARC 32 /* attended type, TX ARC */ +#define EXTCON_EARCTX_ATNDTYP_EARC 33 /* attended type, TX eARC */ #endif /* Display external connector */ #define EXTCON_DISP_HDMI 40 /* High-Definition Multimedia Interface */ diff --git a/sound/soc/amlogic/auge/Makefile b/sound/soc/amlogic/auge/Makefile index fb7a97342ce9..f5d514c34454 100644 --- a/sound/soc/amlogic/auge/Makefile +++ b/sound/soc/amlogic/auge/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \ tl1,clocks.o \ sm1,clocks.o \ tm2,clocks.o \ - card.o \ card_utils.o \ tdm.o \ tdm_hw.o \ @@ -37,4 +36,5 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_AUGE) += audio_controller.o \ vad_hw.o \ vad_dev.o \ earc.o \ - earc_hw.o + earc_hw.o \ + card.o diff --git a/sound/soc/amlogic/auge/audio_clks.c b/sound/soc/amlogic/auge/audio_clks.c index 4aa82c362867..a63211b2a616 100644 --- a/sound/soc/amlogic/auge/audio_clks.c +++ b/sound/soc/amlogic/auge/audio_clks.c @@ -99,6 +99,8 @@ static int audio_clocks_probe(struct platform_device *pdev) return ret; } + pr_info("%s done\n", __func__); + return 0; } @@ -109,7 +111,16 @@ static struct platform_driver audio_clocks_driver = { }, .probe = audio_clocks_probe, }; -module_platform_driver(audio_clocks_driver); + +int __init audio_clocks_init(void) +{ + int ret; + + ret = platform_driver_register(&audio_clocks_driver); + + return ret; +} +core_initcall(audio_clocks_init); MODULE_AUTHOR("Amlogic, Inc."); MODULE_DESCRIPTION("Amlogic audio clocks ASoc driver"); diff --git a/sound/soc/amlogic/auge/audio_clks.h b/sound/soc/amlogic/auge/audio_clks.h index ede4ec982ced..a97cfc2fcbfb 100644 --- a/sound/soc/amlogic/auge/audio_clks.h +++ b/sound/soc/amlogic/auge/audio_clks.h @@ -84,6 +84,15 @@ do { \ &_name##_gate.hw, &clk_gate_ops, \ CLK_SET_RATE_NO_REPARENT) +#define REGISTER_CLK_COM_PARENTS(_name, pnames) \ + clk_register_composite(NULL, #_name, \ + pnames##_parent_names, \ + ARRAY_SIZE(pnames##_parent_names), \ + &_name##_mux.hw, &clk_mux_ops, \ + &_name##_div.hw, &clk_divider_ops, \ + &_name##_gate.hw, &clk_gate_ops, \ + CLK_SET_RATE_NO_REPARENT) + struct audio_clk_init { int clk_num; int (*clk_gates)(struct clk **clks, void __iomem *iobase); diff --git a/sound/soc/amlogic/auge/audio_controller.c b/sound/soc/amlogic/auge/audio_controller.c index 04845c530853..b16a04cd141a 100644 --- a/sound/soc/amlogic/auge/audio_controller.c +++ b/sound/soc/amlogic/auge/audio_controller.c @@ -26,6 +26,8 @@ #include #include +#include + #include "audio_io.h" #include "regs.h" @@ -35,11 +37,8 @@ static unsigned int aml_audio_mmio_read(struct aml_audio_controller *actrlr, unsigned int reg) { struct regmap *regmap = actrlr->regmap; - unsigned int val; - regmap_read(regmap, (reg << 2), &val); - - return val; + return mmio_read(regmap, reg); } static int aml_audio_mmio_write(struct aml_audio_controller *actrlr, @@ -47,7 +46,12 @@ static int aml_audio_mmio_write(struct aml_audio_controller *actrlr, { struct regmap *regmap = actrlr->regmap; - return regmap_write(regmap, (reg << 2), value); + pr_debug("audio top reg:[%s] addr: [%#x] val: [%#x]\n", + top_register_table[reg].name, + top_register_table[reg].addr, + value); + + return mmio_write(regmap, reg, value); } static int aml_audio_mmio_update_bits(struct aml_audio_controller *actrlr, @@ -55,7 +59,12 @@ static int aml_audio_mmio_update_bits(struct aml_audio_controller *actrlr, { struct regmap *regmap = actrlr->regmap; - return regmap_update_bits(regmap, (reg << 2), mask, value); + pr_debug("audio top reg:[%s] addr: [%#x] mask: [%#x] val: [%#x]\n", + top_register_table[reg].name, + top_register_table[reg].addr, + mask, value); + + return mmio_update_bits(regmap, reg, mask, value); } struct aml_audio_ctrl_ops aml_actrl_mmio_ops = { diff --git a/sound/soc/amlogic/auge/card.c b/sound/soc/amlogic/auge/card.c index 8ba494927289..5b9b82b50d0d 100644 --- a/sound/soc/amlogic/auge/card.c +++ b/sound/soc/amlogic/auge/card.c @@ -84,7 +84,7 @@ struct aml_card_data { int micphone_gpio_det; int mic_detect_flag; bool mic_det_enable; - + bool av_mute_enable; struct aml_chipset_info *chipinfo; }; @@ -559,29 +559,48 @@ static int aml_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } + dai_link->cpu_of_node = of_parse_phandle(cpu, DAI, 0); + if (!dai_link->cpu_of_node) { + dev_err(dev, "error getting cpu phandle\n"); + return -EINVAL; + } + ret = aml_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "%s, dai fmt not found\n", + __func__); goto dai_link_of_err; - + } of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); ret = aml_card_parse_cpu(cpu, dai_link, DAI, CELL, &single_cpu); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "%s, dai-link idx:%d, error getting cpu dai name:%s\n", + __func__, + idx, + dai_link->cpu_dai_name); goto dai_link_of_err; + } -#if 0 - ret = aml_card_parse_codec(codec, dai_link, DAI, CELL); -#else ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); -#endif - if (ret < 0) + + if (ret < 0) { + dev_err(dev, "%s, dai-link idx:%d, error getting codec dai name:%s\n", + __func__, + idx, + dai_link->codec_dai_name); goto dai_link_of_err; + } ret = aml_card_parse_platform(plat, dai_link, DAI, CELL); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "%s, platform not found\n", + __func__); + goto dai_link_of_err; + } ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, &cpu_dai->rx_slot_mask, @@ -605,12 +624,6 @@ static int aml_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; -#if 0 - ret = aml_card_parse_clk_codec(codec, dai_link, codec_dai); - if (ret < 0) - goto dai_link_of_err; -#endif - ret = aml_card_canonicalize_dailink(dai_link); if (ret < 0) goto dai_link_of_err; @@ -749,20 +762,28 @@ static int aml_card_parse_gpios(struct device_node *node, snd_soc_add_card_controls(soc_card, card_controls, ARRAY_SIZE(card_controls)); } - pr_info("add line-out mute controls\n"); } #if !defined(CONFIG_ARCH_MESON64_ODROID_COMMON) - priv->avout_mute_desc = gpiod_get(dev, - "avout_mute", GPIOF_OUT_INIT_LOW); + if (IS_ERR_OR_NULL(priv->avout_mute_desc)) { + priv->avout_mute_desc = gpiod_get(dev, + "avout_mute", GPIOF_OUT_INIT_LOW); + } if (!IS_ERR(priv->avout_mute_desc)) { - msleep(500); - gpiod_direction_output(priv->avout_mute_desc, - GPIOF_OUT_INIT_HIGH); - pr_info("av out status: %s\n", - gpiod_get_value(priv->avout_mute_desc) ? - "high" : "low"); - + if (!priv->av_mute_enable) { + msleep(500); + gpiod_direction_output(priv->avout_mute_desc, + GPIOF_OUT_INIT_HIGH); + pr_info("av out status: %s\n", + gpiod_get_value(priv->avout_mute_desc) ? + "high" : "low"); + } else { + gpiod_direction_output(priv->avout_mute_desc, + GPIOF_OUT_INIT_LOW); + pr_info("av out status: %s\n", + gpiod_get_value(priv->avout_mute_desc) ? + "high" : "low"); + } } #endif return 0; @@ -778,7 +799,6 @@ static void aml_init_work(struct work_struct *init_work) struct aml_card_data, init_work); dev = aml_priv_to_dev(priv); np = dev->of_node; - aml_card_parse_gpios(np, priv); } @@ -823,6 +843,7 @@ static int aml_card_parse_of(struct device_node *node, ret = aml_card_dai_link_of(np, priv, i, false); if (ret < 0) { + dev_err(dev, "parse dai_link-%d fail\n", i); of_node_put(np); goto card_parse_end; } @@ -876,6 +897,29 @@ static const struct of_device_id auge_of_match[] = { }; MODULE_DEVICE_TABLE(of, auge_of_match); +static int card_suspend_pre(struct snd_soc_card *card) +{ + struct aml_card_data *priv = snd_soc_card_get_drvdata(card); + + priv->av_mute_enable = 1; + INIT_WORK(&priv->init_work, aml_init_work); + schedule_work(&priv->init_work); + pr_info("it is card_pre_suspend\n"); + return 0; +} + +static int card_resume_post(struct snd_soc_card *card) +{ + struct aml_card_data *priv = snd_soc_card_get_drvdata(card); + + priv->av_mute_enable = 0; + INIT_WORK(&priv->init_work, aml_init_work); + schedule_work(&priv->init_work); + pr_info("it is card_post_resume\n"); + return 0; + +} + static int aml_card_probe(struct platform_device *pdev) { struct aml_card_data *priv; @@ -916,13 +960,17 @@ static int aml_card_probe(struct platform_device *pdev) priv->snd_card.dev = dev; priv->snd_card.dai_link = priv->dai_link; priv->snd_card.num_links = num; + priv->snd_card.suspend_pre = card_suspend_pre; + priv->snd_card.resume_post = card_resume_post; if (np && of_device_is_available(np)) { ret = aml_card_parse_of(np, priv); if (ret < 0) { - dev_err(dev, "%s, parse error %d\n", - __func__, ret); + dev_err(dev, "%s, aml_card_parse_of error %d %s\n", + __func__, + ret, + (ret == -EPROBE_DEFER) ? "PROBE RETRY" : ""); goto err; } @@ -960,6 +1008,7 @@ static int aml_card_probe(struct platform_device *pdev) sizeof(priv->dai_props->codec_dai)); } + platform_set_drvdata(pdev, priv); snd_soc_card_set_drvdata(&priv->snd_card, priv); ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); @@ -986,7 +1035,7 @@ static int aml_card_probe(struct platform_device *pdev) audio_jack_detect(priv); audio_extcon_register(priv, dev); } - + priv->av_mute_enable = 0; INIT_WORK(&priv->init_work, aml_init_work); schedule_work(&priv->init_work); @@ -1009,6 +1058,16 @@ static int aml_card_remove(struct platform_device *pdev) return aml_card_clean_reference(card); } +static void aml_card_platform_shutdown(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct aml_card_data *priv = snd_soc_card_get_drvdata(card); + + priv->av_mute_enable = 1; + INIT_WORK(&priv->init_work, aml_init_work); + schedule_work(&priv->init_work); +} + static struct platform_driver aml_card = { .driver = { .name = "asoc-aml-card", @@ -1017,6 +1076,7 @@ static struct platform_driver aml_card = { }, .probe = aml_card_probe, .remove = aml_card_remove, + .shutdown = aml_card_platform_shutdown, }; module_platform_driver(aml_card); diff --git a/sound/soc/amlogic/auge/ddr_mngr.c b/sound/soc/amlogic/auge/ddr_mngr.c index 5d20f725943c..0030db0905c1 100644 --- a/sound/soc/amlogic/auge/ddr_mngr.c +++ b/sound/soc/amlogic/auge/ddr_mngr.c @@ -106,7 +106,6 @@ static struct toddr *register_toddr_l(struct device *dev, /* (1 << 31)|(1 << mask_bit));*/ to->dev = dev; - to->actrl = actrl; to->in_use = true; pr_debug("toddrs[%d] registered by device %s\n", i, dev_name(dev)); return to; @@ -147,7 +146,6 @@ static int unregister_toddr_l(struct device *dev, void *data) free_irq(to->irq, data); to->dev = NULL; - to->actrl = NULL; to->in_use = false; pr_debug("toddrs[%d] released by device %s\n", i, dev_name(dev)); @@ -901,7 +899,6 @@ static struct frddr *register_frddr_l(struct device *dev, return NULL; } from->dev = dev; - from->actrl = actrl; from->in_use = true; pr_debug("frddrs[%d] registered by device %s\n", i, dev_name(dev)); return from; @@ -942,7 +939,6 @@ static int unregister_frddr_l(struct device *dev, void *data) free_irq(from->irq, data); from->dev = NULL; - from->actrl = NULL; from->in_use = false; pr_debug("frddrs[%d] released by device %s\n", i, dev_name(dev)); return 0; @@ -1019,10 +1015,6 @@ int aml_check_sharebuffer_valid(struct frddr *fr, int ss_sel) if (frddrs[i].in_use && (frddrs[i].fifo_id != current_fifo_id) && (frddrs[i].dest == ss_sel)) { - - pr_debug("%s, ss_sel:%d used, not for share buffer at same time\n", - __func__, - ss_sel); ret = 0; break; } @@ -1295,20 +1287,42 @@ void aml_frddr_set_format(struct frddr *fr, static void aml_aed_enable(struct frddr_attach *p_attach_aed, bool enable) { struct frddr *fr = fetch_frddr_by_src(p_attach_aed->attach_module); + int aed_version = check_aed_version(); - if (check_aed_v2()) { + if (aed_version == VERSION2 || aed_version == VERSION3) { struct aml_audio_controller *actrl = fr->actrl; unsigned int reg_base = fr->reg_base; unsigned int reg; reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2, reg_base); - aml_audiobus_update_bits(actrl, - reg, 0x1 << 3, enable << 3); - - aed_set_ctrl(enable, 0, p_attach_aed->attach_module); - aed_set_format(fr->msb, fr->type, fr->fifo_id); - aed_enable(enable); - } else { + if (enable) { + aml_audiobus_update_bits(actrl, + reg, 0x1 << 3, enable << 3); + if (aed_version == VERSION3) { + aed_set_ctrl(enable, 0, + p_attach_aed->attach_module, 1); + aed_set_format(fr->msb, + fr->type, fr->fifo_id, 1); + } else { + aed_set_ctrl(enable, 0, + p_attach_aed->attach_module, 0); + aed_set_format(fr->msb, + fr->type, fr->fifo_id, 0); + } + aed_enable(enable); + } else { + aed_enable(enable); + if (aed_version == VERSION3) { + aed_set_ctrl(enable, 0, + p_attach_aed->attach_module, 1); + } else { + aed_set_ctrl(enable, 0, + p_attach_aed->attach_module, 0); + } + aml_audiobus_update_bits(actrl, + reg, 0x1 << 3, enable << 3); + } + } else if (aed_version == VERSION1) { if (enable) { /* frddr type and bit depth for AED */ aml_aed_format_set(fr->dest); @@ -1479,7 +1493,8 @@ static int toddr_src_enum_set(struct snd_kcontrol *kcontrol, static int frddr_src_idx = -1; static const char *const frddr_src_sel_texts[] = { - "TDMOUT_A", "TDMOUT_B", "TDMOUT_C", "SPDIFOUT_A", "SPDIFOUT_B" + "TDMOUT_A", "TDMOUT_B", "TDMOUT_C", + "SPDIFOUT_A", "SPDIFOUT_B", "EARCTX_DMAC" }; static const struct soc_enum frddr_output_source_enum = @@ -1493,7 +1508,7 @@ int frddr_src_get(void) const char *frddr_src_get_str(int idx) { - if (idx < 0 || idx > 4) + if (idx < 0 || idx >= FRDDR_MAX) return NULL; return frddr_src_sel_texts[idx]; @@ -1627,77 +1642,93 @@ static struct notifier_block ddr_pm_notifier_block = { .notifier_call = ddr_pm_event, }; +/* table Must in order */ +static struct ddr_info ddr_info[] = { + {EE_AUDIO_TODDR_A_CTRL0, EE_AUDIO_FRDDR_A_CTRL0, "toddr_a", "frddr_a"}, + {EE_AUDIO_TODDR_B_CTRL0, EE_AUDIO_FRDDR_B_CTRL0, "toddr_b", "frddr_b"}, + {EE_AUDIO_TODDR_C_CTRL0, EE_AUDIO_FRDDR_C_CTRL0, "toddr_c", "frddr_c"}, + {EE_AUDIO_TODDR_D_CTRL0, EE_AUDIO_FRDDR_D_CTRL0, "toddr_d", "frddr_d"}, +}; + +static int ddr_get_toddr_base_addr_by_idx(int idx) +{ + return ddr_info[idx].toddr_addr; +} + +static int ddr_get_frddr_base_addr_by_idx(int idx) +{ + return ddr_info[idx].frddr_addr; +} + +static char *ddr_get_toddr_name_by_idx(int idx) +{ + return ddr_info[idx].toddr_name; +} + +static char *ddr_get_frddr_name_by_idx(int idx) +{ + return ddr_info[idx].frddr_name; +} + static int aml_ddr_mngr_platform_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + struct device_node *node_prt = NULL; + struct platform_device *pdev_parent; + struct aml_audio_controller *actrl = NULL; struct ddr_chipinfo *p_ddr_chipinfo; int ddr_num = 3; /* early chipset support max 3 ddr num */ int i, ret; + /* get audio controller */ + node_prt = of_get_parent(node); + if (!node_prt) + return -ENXIO; + + pdev_parent = of_find_device_by_node(node_prt); + of_node_put(node_prt); + actrl = (struct aml_audio_controller *) + platform_get_drvdata(pdev_parent); + p_ddr_chipinfo = (struct ddr_chipinfo *) of_device_get_match_data(&pdev->dev); - if (!p_ddr_chipinfo) - dev_warn_once(&pdev->dev, - "check whether to update ddr_mngr chipinfo\n"); - - /* irqs */ - toddrs[DDR_A].irq = platform_get_irq_byname(pdev, "toddr_a"); - toddrs[DDR_B].irq = platform_get_irq_byname(pdev, "toddr_b"); - toddrs[DDR_C].irq = platform_get_irq_byname(pdev, "toddr_c"); - - frddrs[DDR_A].irq = platform_get_irq_byname(pdev, "frddr_a"); - frddrs[DDR_B].irq = platform_get_irq_byname(pdev, "frddr_b"); - frddrs[DDR_C].irq = platform_get_irq_byname(pdev, "frddr_c"); - - if (p_ddr_chipinfo - && (p_ddr_chipinfo->fifo_num == 4)) { - toddrs[DDR_D].irq = platform_get_irq_byname(pdev, "toddr_d"); - frddrs[DDR_D].irq = platform_get_irq_byname(pdev, "frddr_d"); - if (toddrs[DDR_D].irq < 0 || frddrs[DDR_D].irq < 0) - dev_err(&pdev->dev, "check irq for DDR_D\n"); - ddr_num = p_ddr_chipinfo->fifo_num; + if (!p_ddr_chipinfo) { + dev_err(&pdev->dev, + "check to update ddr_mngr chipinfo\n"); + return -EINVAL; } + if (p_ddr_chipinfo->fifo_num == 2) + ddr_num = p_ddr_chipinfo->fifo_num; + else if (p_ddr_chipinfo->fifo_num == 4) + ddr_num = p_ddr_chipinfo->fifo_num; + for (i = 0; i < ddr_num; i++) { - pr_info("%d, irqs toddr %d, frddr %d\n", - i, toddrs[i].irq, frddrs[i].irq); + toddrs[i].irq = + platform_get_irq_byname(pdev, + ddr_get_toddr_name_by_idx(i)); + toddrs[i].reg_base = ddr_get_toddr_base_addr_by_idx(i); + toddrs[i].fifo_id = i; + toddrs[i].chipinfo = p_ddr_chipinfo; + toddrs[i].actrl = actrl; + + frddrs[i].irq = + platform_get_irq_byname(pdev, + ddr_get_frddr_name_by_idx(i)); + frddrs[i].reg_base = ddr_get_frddr_base_addr_by_idx(i); + frddrs[i].fifo_id = i; + frddrs[i].chipinfo = p_ddr_chipinfo; + frddrs[i].actrl = actrl; + + dev_info(&pdev->dev, "%d, irqs toddr %d, frddr %d\n", + i, toddrs[i].irq, frddrs[i].irq); + if (toddrs[i].irq <= 0 || frddrs[i].irq <= 0) { - dev_err(&pdev->dev, "platform_get_irq_byname failed\n"); + dev_err(&pdev->dev, "%s, get irq failed\n", __func__); return -ENXIO; } } - /* inits */ - toddrs[DDR_A].reg_base = EE_AUDIO_TODDR_A_CTRL0; - toddrs[DDR_B].reg_base = EE_AUDIO_TODDR_B_CTRL0; - toddrs[DDR_C].reg_base = EE_AUDIO_TODDR_C_CTRL0; - toddrs[DDR_A].fifo_id = DDR_A; - toddrs[DDR_B].fifo_id = DDR_B; - toddrs[DDR_C].fifo_id = DDR_C; - - frddrs[DDR_A].reg_base = EE_AUDIO_FRDDR_A_CTRL0; - frddrs[DDR_B].reg_base = EE_AUDIO_FRDDR_B_CTRL0; - frddrs[DDR_C].reg_base = EE_AUDIO_FRDDR_C_CTRL0; - frddrs[DDR_A].fifo_id = DDR_A; - frddrs[DDR_B].fifo_id = DDR_B; - frddrs[DDR_C].fifo_id = DDR_C; - - if (p_ddr_chipinfo) { - toddrs[DDR_A].chipinfo = p_ddr_chipinfo; - toddrs[DDR_B].chipinfo = p_ddr_chipinfo; - toddrs[DDR_C].chipinfo = p_ddr_chipinfo; - frddrs[DDR_A].chipinfo = p_ddr_chipinfo; - frddrs[DDR_B].chipinfo = p_ddr_chipinfo; - frddrs[DDR_C].chipinfo = p_ddr_chipinfo; - - if (p_ddr_chipinfo->fifo_num == 4) { - toddrs[DDR_D].reg_base = EE_AUDIO_TODDR_D_CTRL0; - toddrs[DDR_D].fifo_id = DDR_D; - - frddrs[DDR_D].reg_base = EE_AUDIO_FRDDR_D_CTRL0; - frddrs[DDR_D].fifo_id = DDR_D; - } - } - ret = register_pm_notifier(&ddr_pm_notifier_block); if (ret) pr_warn("[%s] failed to register PM notifier %d\n", diff --git a/sound/soc/amlogic/auge/ddr_mngr.h b/sound/soc/amlogic/auge/ddr_mngr.h index dc7dca26d29f..35e104c64f15 100644 --- a/sound/soc/amlogic/auge/ddr_mngr.h +++ b/sound/soc/amlogic/auge/ddr_mngr.h @@ -90,6 +90,8 @@ enum frddr_dest { TDMOUT_C, SPDIFOUT_A, SPDIFOUT_B, + EARCTX_DMAC, + FRDDR_MAX }; enum status_sel { @@ -228,6 +230,13 @@ struct frddr { struct ddr_chipinfo *chipinfo; }; +struct ddr_info { + unsigned int toddr_addr; + unsigned int frddr_addr; + char *toddr_name; + char *frddr_name; +}; + /* to ddrs */ struct toddr *fetch_toddr_by_src(int toddr_src); struct toddr *aml_audio_register_toddr(struct device *dev, diff --git a/sound/soc/amlogic/auge/earc.c b/sound/soc/amlogic/auge/earc.c index 30b5f30d0aec..a69b2ca4f64e 100644 --- a/sound/soc/amlogic/auge/earc.c +++ b/sound/soc/amlogic/auge/earc.c @@ -16,6 +16,7 @@ * Audio External Input/Out drirver * such as fratv, frhdmirx */ +#define DEBUG #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +36,7 @@ #include #include +#include #include "ddr_mngr.h" #include "earc_hw.h" @@ -54,17 +57,29 @@ struct earc { struct clk *clk_tx_cmdc_srcpll; struct clk *clk_tx_dmac_srcpll; + struct regmap *tx_cmdc_map; + struct regmap *tx_dmac_map; + struct regmap *tx_top_map; + struct regmap *rx_cmdc_map; + struct regmap *rx_dmac_map; + struct regmap *rx_top_map; + struct toddr *tddr; struct frddr *fddr; - int irq_rx_cmdc; - int irq_rx_dmac; - int irq_tx_cmdc; - int irq_tx_dmac; + int irq_earc_rx; + int irq_earc_tx; - int sysclk_freq; + /* external connect */ + struct extcon_dev *rx_edev; + struct extcon_dev *tx_edev; + + bool tx_dmac_clk_on; + bool rx_dmac_clk_on; }; +static struct earc *s_earc; + #define PREALLOC_BUFFER_MAX (256 * 1024) #define EARC_RATES (SNDRV_PCM_RATE_8000_192000) @@ -94,10 +109,10 @@ static const struct snd_pcm_hardware earc_hardware = { .channels_max = 32, }; -static irqreturn_t earc_ddr_isr(int irq, void *devid) +static irqreturn_t earc_ddr_isr(int irq, void *data) { struct snd_pcm_substream *substream = - (struct snd_pcm_substream *)devid; + (struct snd_pcm_substream *)data; if (!snd_pcm_running(substream)) return IRQ_HANDLED; @@ -107,23 +122,193 @@ static irqreturn_t earc_ddr_isr(int irq, void *devid) return IRQ_HANDLED; } -static irqreturn_t earc_rx_cmdc_isr(int irq, void *devid) +static void earcrx_update_attend_event(struct earc *p_earc, + bool is_earc, bool state) { + if (state) { + if (is_earc) { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_ARC, false); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_EARC, state); + } else { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_ARC, state); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_EARC, false); + } + } else { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_ARC, state); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCRX_ATNDTYP_EARC, state); + } +} + +static irqreturn_t earc_rx_isr(int irq, void *data) +{ + struct earc *p_earc = (struct earc *)data; + unsigned int status0 = earcrx_cdmc_get_irqs(p_earc->rx_top_map); + + if (status0 & INT_EARCRX_CMDC_IDLE2) { + earcrx_update_attend_event(p_earc, + false, true); + + pr_debug("%s EARCRX_CMDC_IDLE2\n", __func__); + } + if (status0 & INT_EARCRX_CMDC_IDLE1) { + earcrx_update_attend_event(p_earc, + false, false); + + pr_debug("%s EARCRX_CMDC_IDLE1\n", __func__); + } + if (status0 & INT_EARCRX_CMDC_DISC2) + pr_debug("%s EARCRX_CMDC_DISC2\n", __func__); + if (status0 & INT_EARCRX_CMDC_DISC1) + pr_debug("%s EARCRX_CMDC_DISC1\n", __func__); + if (status0 & INT_EARCRX_CMDC_EARC) { + earcrx_update_attend_event(p_earc, + true, true); + + pr_info("%s EARCRX_CMDC_EARC\n", __func__); + } + if (status0 & INT_EARCRX_CMDC_HB_STATUS) + pr_debug("%s EARCRX_CMDC_HB_STATUS\n", __func__); + if (status0 & INT_EARCRX_CMDC_LOSTHB) + pr_debug("%s EARCRX_CMDC_LOSTHB\n", __func__); + if (status0 & INT_EARCRX_CMDC_TIMEOUT) { + earcrx_update_attend_event(p_earc, + false, false); + + pr_debug("%s EARCRX_CMDC_TIMEOUT\n", __func__); + } + + if (status0) + earcrx_cdmc_clr_irqs(p_earc->rx_top_map, status0); + + if (p_earc->rx_dmac_clk_on) { + unsigned int status1 = earcrx_dmac_get_irqs(p_earc->rx_top_map); + + if (status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_PAPB) + pr_debug("%s ARCRX_C_FIND_PAPB\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_C_VALID_CHANGE) + pr_debug("%s ARCRX_C_VALID_CHANGE\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_NONPCM2PCM) + pr_debug("%s ARCRX_C_FIND_NONPCM2PCM\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_C_PCPD_CHANGE) + pr_debug("%s ARCRX_C_PCPD_CHANGE\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_C_CH_STATUS_CHANGE) + pr_debug("%s ARCRX_C_CH_STATUS_CHANGE\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_I_SAMPLE_MODE_CHANGE) + pr_debug("%s ARCRX_I_SAMPLE_MODE_CHANGE\n", __func__); + if (status1 & INT_ARCRX_BIPHASE_DECODE_R_PARITY_ERR) + pr_debug("%s ARCRX_R_PARITY_ERR\n", __func__); + + if (status1) + earcrx_dmac_clr_irqs(p_earc->rx_top_map, status1); + } + return IRQ_HANDLED; } -static irqreturn_t earc_rx_dmac_isr(int irq, void *devid) +static void earctx_update_attend_event(struct earc *p_earc, + bool is_earc, bool state) { - return IRQ_HANDLED; + if (state) { + if (is_earc) { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_ARC, + false); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_EARC, + state); + } else { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_ARC, + state); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_EARC, + false); + } + } else { + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_ARC, + state); + extcon_set_state_sync(p_earc->rx_edev, + EXTCON_EARCTX_ATNDTYP_EARC, + state); + } } -static irqreturn_t earc_tx_cmdc_isr(int irq, void *devid) +static irqreturn_t earc_tx_isr(int irq, void *data) { - return IRQ_HANDLED; -} + struct earc *p_earc = (struct earc *)data; + unsigned int status0 = earctx_cdmc_get_irqs(p_earc->tx_top_map); + + if (status0 & INT_EARCTX_CMDC_IDLE2) { + earctx_update_attend_event(p_earc, + false, true); + + pr_debug("%s EARCTX_CMDC_IDLE2\n", __func__); + } + if (status0 & INT_EARCTX_CMDC_IDLE1) { + earctx_update_attend_event(p_earc, + false, false); + + pr_debug("%s EARCTX_CMDC_IDLE1\n", __func__); + } + if (status0 & INT_EARCTX_CMDC_DISC2) + pr_debug("%s EARCTX_CMDC_DISC2\n", __func__); + if (status0 & INT_EARCTX_CMDC_DISC1) + pr_debug("%s EARCTX_CMDC_DISC1\n", __func__); + if (status0 & INT_EARCTX_CMDC_EARC) { + earctx_update_attend_event(p_earc, + true, true); + + pr_info("%s EARCTX_CMDC_EARC\n", __func__); + } + if (status0 & INT_EARCTX_CMDC_HB_STATUS) + pr_debug("%s EARCTX_CMDC_HB_STATUS\n", __func__); + if (status0 & INT_EARCTX_CMDC_LOSTHB) + pr_debug("%s EARCTX_CMDC_LOSTHB\n", __func__); + if (status0 & INT_EARCTX_CMDC_TIMEOUT) { + earctx_update_attend_event(p_earc, + false, false); + + pr_debug("%s EARCTX_CMDC_TIMEOUT\n", __func__); + } + if (status0 & INT_EARCTX_CMDC_STATUS_CH) + pr_debug("%s EARCTX_CMDC_STATUS_CH\n", __func__); + if (status0 & INT_EARCTX_CMDC_RECV_NACK) + pr_debug("%s EARCTX_CMDC_RECV_NACK\n", __func__); + if (status0 & INT_EARCTX_CMDC_RECV_NORSP) + pr_debug("%s EARCTX_CMDC_RECV_NORSP\n", __func__); + if (status0 & INT_EARCTX_CMDC_RECV_UNEXP) + pr_debug("%s EARCTX_CMDC_RECV_UNEXP\n", __func__); + + if (status0) + earctx_cdmc_clr_irqs(p_earc->tx_top_map, status0); + + if (p_earc->tx_dmac_clk_on) { + unsigned int status1 = earctx_dmac_get_irqs(p_earc->tx_top_map); + + if (status1 & INT_EARCTX_FEM_C_HOLD_CLR) + pr_debug("%s EARCTX_FEM_C_HOLD_CLR\n", __func__); + if (status1 & INT_EARCTX_FEM_C_HOLD_START) + pr_debug("%s EARCTX_FEM_C_HOLD_START\n", __func__); + if (status1 & INT_EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS) + pr_debug("%s EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS\n", + __func__); + if (status1 & INT_EARCTX_ERRCORR_C_FIFO_OVERFLOW) + pr_debug("%s EARCTX_ERRCORR_C_FIFO_OVERFLOW\n", + __func__); + if (status1 & INT_EARCTX_ERRCORR_C_FIFO_EMPTY) + pr_debug("%s EARCTX_ERRCORR_C_FIFO_EMPTY\n", __func__); + + if (status1) + earctx_dmac_clr_irqs(p_earc->tx_top_map, status1); + } -static irqreturn_t earc_tx_dmac_isr(int irq, void *devid) -{ return IRQ_HANDLED; } @@ -133,9 +318,8 @@ static int earc_open(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct device *dev = rtd->platform->dev; struct earc *p_earc; - int ret = 0; - pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + pr_info("asoc debug: %s\n", __func__); p_earc = (struct earc *)dev_get_drvdata(dev); @@ -149,26 +333,6 @@ static int earc_open(struct snd_pcm_substream *substream) dev_err(dev, "failed to claim from ddr\n"); return -ENXIO; } - if (p_earc->irq_tx_cmdc > 0) { - ret = request_irq(p_earc->irq_tx_cmdc, - earc_tx_cmdc_isr, 0, "tx_cmdc", - p_earc); - if (ret) { - dev_err(p_earc->dev, "failed to claim irq_tx_cmdc %u\n", - p_earc->irq_tx_cmdc); - return ret; - } - } - if (p_earc->irq_tx_dmac > 0) { - ret = request_irq(p_earc->irq_tx_dmac, - earc_tx_dmac_isr, 0, "tx_dmac", - p_earc); - if (ret) { - dev_err(p_earc->dev, "failed to claim irq_tx_dmac %u\n", - p_earc->irq_tx_dmac); - return ret; - } - } } else { p_earc->tddr = aml_audio_register_toddr(dev, p_earc->actrl, @@ -177,23 +341,6 @@ static int earc_open(struct snd_pcm_substream *substream) dev_err(dev, "failed to claim to ddr\n"); return -ENXIO; } - - ret = request_irq(p_earc->irq_rx_cmdc, - earc_rx_cmdc_isr, 0, "rx_cmdc", - p_earc); - if (ret) { - dev_err(p_earc->dev, "failed to claim irq_rx_cmdc %u\n", - p_earc->irq_rx_cmdc); - return ret; - } - ret = request_irq(p_earc->irq_rx_dmac, - earc_rx_dmac_isr, 0, "rx_dmac", - p_earc); - if (ret) { - dev_err(p_earc->dev, "failed to claim rx_dmac %u\n", - p_earc->irq_rx_dmac); - return ret; - } } runtime->private_data = p_earc; @@ -206,21 +353,13 @@ static int earc_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct earc *p_earc = runtime->private_data; - pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + pr_info("asoc debug: %s\n", __func__); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) aml_audio_unregister_frddr(p_earc->dev, substream); - - if (p_earc->irq_tx_cmdc > 0) - free_irq(p_earc->irq_tx_cmdc, p_earc); - - if (p_earc->irq_tx_dmac > 0) - free_irq(p_earc->irq_tx_dmac, p_earc); - } else { + else aml_audio_unregister_toddr(p_earc->dev, substream); - free_irq(p_earc->irq_rx_cmdc, p_earc); - free_irq(p_earc->irq_rx_dmac, p_earc); - } + runtime->private_data = NULL; return 0; @@ -339,7 +478,7 @@ struct snd_soc_platform_driver earc_platform = { static int earc_dai_probe(struct snd_soc_dai *cpu_dai) { - pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + pr_info("asoc debug: %s\n", __func__); return 0; } @@ -359,19 +498,66 @@ static int earc_dai_prepare( if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { struct frddr *fr = p_earc->fddr; - enum frddr_dest dst = frddr_src_get(); + enum frddr_dest dst = EARCTX_DMAC; + unsigned int fifo_id, frddr_type = 0; + struct iec958_chsts chsts; pr_info("%s Expected frddr dst:%s\n", __func__, frddr_src_get_str(dst)); + switch (bit_depth) { + case 8: + frddr_type = 0; + break; + case 16: + frddr_type = 1; + break; + case 24: + frddr_type = 4; + break; + case 32: + frddr_type = 3; + break; + default: + pr_err("runtime format invalid bitwidth: %d\n", + bit_depth); + break; + } + fifo_id = aml_frddr_get_fifo_id(fr); + + pr_info("%s, frddr_index:%d, bit_depth:%d, frddr_type:%d\n", + __func__, + fr->fifo_id, bit_depth, frddr_type); + + aml_frddr_set_format(fr, + runtime->channels, + bit_depth - 1, + frddr_type); aml_frddr_select_dst(fr, dst); aml_frddr_set_fifos(fr, 0x40, 0x20); + + earctx_dmac_init(p_earc->tx_top_map, p_earc->tx_dmac_map); + earctx_dmac_set_format(p_earc->tx_dmac_map, + fr->fifo_id, + bit_depth - 1, + frddr_type); + + /* check and set channel status info */ + spdif_get_channel_status_info(&chsts, runtime->rate); + earctx_set_channel_status_info(p_earc->tx_dmac_map, &chsts); } else { struct toddr *to = p_earc->tddr; unsigned int msb = 0, lsb = 0, toddr_type = 0; unsigned int src = EARCRX_DMAC; struct toddr_fmt fmt; + enum attend_type type = + earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map); + + if (type == ATNDTYP_DISCNCT) { + dev_err(p_earc->dev, "Neither eARC or ARC is attended!\n"); + return -ENOTCONN; + } if (bit_depth == 32) toddr_type = 3; @@ -405,10 +591,8 @@ static int earc_dai_prepare( aml_toddr_set_format(to, &fmt); aml_toddr_set_fifos(to, 0x40); - earcrx_cmdc_init(); - earcrx_dmac_init(); - earc_arc_init(); - + earcrx_dmac_init(p_earc->rx_top_map, p_earc->rx_dmac_map); + earcrx_arc_init(p_earc->rx_dmac_map); } return 0; @@ -427,12 +611,17 @@ static int earc_dai_trigger(struct snd_pcm_substream *substream, int cmd, dev_info(substream->pcm->card->dev, "eARC/ARC TX enable\n"); aml_frddr_enable(p_earc->fddr, true); + earctx_enable(p_earc->tx_top_map, + p_earc->tx_cmdc_map, + p_earc->tx_dmac_map, + true); } else { dev_info(substream->pcm->card->dev, "eARC/ARC RX enable\n"); aml_toddr_enable(p_earc->tddr, true); - - earc_rx_enable(true); + earcrx_enable(p_earc->rx_cmdc_map, + p_earc->rx_dmac_map, + true); } break; case SNDRV_PCM_TRIGGER_STOP: @@ -441,12 +630,17 @@ static int earc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dev_info(substream->pcm->card->dev, "eARC/ARC TX disable\n"); + earctx_enable(p_earc->tx_top_map, + p_earc->tx_cmdc_map, + p_earc->tx_dmac_map, + false); aml_frddr_enable(p_earc->fddr, false); } else { dev_info(substream->pcm->card->dev, "eARC/ARC RX disable\n"); - earc_rx_enable(false); - + earcrx_enable(p_earc->rx_cmdc_map, + p_earc->rx_dmac_map, + false); aml_toddr_enable(p_earc->tddr, false); } break; @@ -466,10 +660,24 @@ static int earc_dai_hw_params( unsigned int rate = params_rate(params); int ret = 0; - pr_info("%s:rate:%d, sysclk:%d\n", - __func__, - rate, - p_earc->sysclk_freq); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + int freq = rate * 128 * 5; + + if (spdif_is_4x_clk()) { + pr_debug("set 4x audio clk for 958\n"); + freq *= 4; + } else { + pr_debug("set normal 512 fs /4 fs\n"); + } + + clk_set_rate(p_earc->clk_tx_dmac_srcpll, freq * 4); + clk_set_rate(p_earc->clk_tx_dmac, freq); + + pr_info("%s, tx dmac clk, set freq: %d, get freq:%lu\n", + __func__, + freq, + clk_get_rate(p_earc->clk_tx_dmac)); + } return ret; } @@ -488,16 +696,16 @@ static int earc_dai_set_sysclk(struct snd_soc_dai *cpu_dai, { struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai); - p_earc->sysclk_freq = freq; - pr_info("earc_dai_set_sysclk, %d, %d, %d\n", - clk_id, freq, dir); + pr_info("%s, %d, %d, %d\n", + __func__, clk_id, freq, dir); - clk_set_rate(p_earc->clk_rx_cmdc, 10000000); - clk_set_rate(p_earc->clk_rx_dmac, 250000000); + if (clk_id == 1) { + clk_set_rate(p_earc->clk_rx_dmac, 500000000); - pr_info("earc rx cmdc clk:%lu rx dmac clk:%lu\n", - clk_get_rate(p_earc->clk_rx_cmdc), - clk_get_rate(p_earc->clk_rx_dmac)); + pr_info("earc rx cmdc clk:%lu rx dmac clk:%lu\n", + clk_get_rate(p_earc->clk_rx_cmdc), + clk_get_rate(p_earc->clk_rx_dmac)); + } return 0; } @@ -509,44 +717,50 @@ static int earc_dai_startup( struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai); int ret; - /* enable clock gate */ - if (!IS_ERR(p_earc->clk_rx_gate)) { - ret = clk_prepare_enable(p_earc->clk_rx_gate); - if (ret) { - pr_err("Can't enable earc rx_gate: %d\n", ret); - goto err; - } - } + pr_info("%s\n", __func__); - audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x1 << 6); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* tx dmac clk */ + if (!IS_ERR(p_earc->clk_tx_dmac)) { + ret = clk_prepare_enable(p_earc->clk_tx_dmac); + if (ret) { + dev_err(p_earc->dev, + "Can't enable earc clk_tx_dmac: %d\n", + ret); + goto err; + } + p_earc->tx_dmac_clk_on = true; + } + if (!IS_ERR(p_earc->clk_tx_dmac_srcpll)) { + ret = clk_prepare_enable(p_earc->clk_tx_dmac_srcpll); + if (ret) { + dev_err(p_earc->dev, + "Can't enable earc clk_tx_dmac_srcpll:%d\n", + ret); + goto err; + } + } + } else { + /* rx dmac clk */ + if (!IS_ERR(p_earc->clk_rx_dmac)) { + ret = clk_prepare_enable(p_earc->clk_rx_dmac); + if (ret) { + dev_err(p_earc->dev, + "Can't enable earc clk_rx_dmac: %d\n", + ret); + goto err; + } + p_earc->rx_dmac_clk_on = true; + } - /* enable clock */ - if (!IS_ERR(p_earc->clk_rx_cmdc)) { - ret = clk_prepare_enable(p_earc->clk_rx_cmdc); - if (ret) { - pr_err("Can't enable earc clk_rx_cmdc: %d\n", ret); - goto err; - } - } - if (!IS_ERR(p_earc->clk_rx_dmac)) { - ret = clk_prepare_enable(p_earc->clk_rx_dmac); - if (ret) { - pr_err("Can't enable earc clk_rx_dmac: %d\n", ret); - goto err; - } - } - if (!IS_ERR(p_earc->clk_tx_cmdc)) { - ret = clk_prepare_enable(p_earc->clk_tx_cmdc); - if (ret) { - pr_err("Can't enable earc clk_tx_cmdc: %d\n", ret); - goto err; - } - } - if (!IS_ERR(p_earc->clk_tx_dmac)) { - ret = clk_prepare_enable(p_earc->clk_tx_dmac); - if (ret) { - pr_err("Can't enable earc clk_tx_dmac: %d\n", ret); - goto err; + if (!IS_ERR(p_earc->clk_rx_dmac_srcpll)) { + ret = clk_prepare_enable(p_earc->clk_rx_dmac_srcpll); + if (ret) { + dev_err(p_earc->dev, + "Can't enable earc clk_rx_dmac_srcpll: %d\n", + ret); + goto err; + } } } @@ -563,19 +777,23 @@ static void earc_dai_shutdown( { struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai); - /* disable clock and gate */ - if (!IS_ERR(p_earc->clk_rx_cmdc)) - clk_disable_unprepare(p_earc->clk_rx_cmdc); - if (!IS_ERR(p_earc->clk_rx_dmac)) - clk_disable_unprepare(p_earc->clk_rx_dmac); - if (!IS_ERR(p_earc->clk_tx_cmdc)) - clk_disable_unprepare(p_earc->clk_tx_cmdc); - if (!IS_ERR(p_earc->clk_tx_dmac)) - clk_disable_unprepare(p_earc->clk_tx_dmac); - if (!IS_ERR(p_earc->clk_rx_gate)) - clk_disable_unprepare(p_earc->clk_rx_gate); + pr_info("%s\n", __func__); - audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x0 << 6); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!IS_ERR(p_earc->clk_tx_dmac)) { + clk_disable_unprepare(p_earc->clk_tx_dmac); + p_earc->tx_dmac_clk_on = false; + } + if (!IS_ERR(p_earc->clk_tx_dmac_srcpll)) + clk_disable_unprepare(p_earc->clk_tx_dmac_srcpll); + } else { + if (!IS_ERR(p_earc->clk_rx_dmac)) { + clk_disable_unprepare(p_earc->clk_rx_dmac); + p_earc->rx_dmac_clk_on = false; + } + if (!IS_ERR(p_earc->clk_rx_dmac_srcpll)) + clk_disable_unprepare(p_earc->clk_rx_dmac_srcpll); + } } static struct snd_soc_dai_ops earc_dai_ops = { @@ -610,9 +828,83 @@ static struct snd_soc_dai_driver earc_dai[] = { }, }; + +static const char *const attended_type[] = { + "DISCONNECT", + "ARC", + "eARC" +}; + +const struct soc_enum attended_type_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(attended_type), + attended_type); + +int earcrx_get_attend_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct earc *p_earc = dev_get_drvdata(component->dev); + enum attend_type type = + earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map); + + ucontrol->value.integer.value[0] = type; + + return 0; +} + +int earcrx_set_attend_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct earc *p_earc = dev_get_drvdata(component->dev); + enum cmdc_st state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map); + + if (state != CMDC_ST_IDLE2) + return 0; + + /* only support set cmdc from idle to ARC */ + + return 0; +} + +static int earcrx_arc_get_enable( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct earc *p_earc = dev_get_drvdata(component->dev); + enum attend_type type = + earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map); + + ucontrol->value.integer.value[0] = (bool)(type == ATNDTYP_ARC); + + return 0; +} + +static int earcrx_arc_set_enable( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct earc *p_earc = dev_get_drvdata(component->dev); + + earcrx_cmdc_arc_connect( + p_earc->rx_cmdc_map, + (bool)ucontrol->value.integer.value[0]); + + return 0; +} + static const struct snd_kcontrol_new earc_controls[] = { + SOC_ENUM_EXT("eARC_RX attended type", + attended_type_enum, + earcrx_get_attend_type, + earcrx_set_attend_type), - + SOC_SINGLE_BOOL_EXT("HDMI ARC Switch", + 0, + earcrx_arc_get_enable, + earcrx_arc_set_enable), }; static const struct snd_soc_component_driver earc_component = { @@ -633,6 +925,178 @@ static const struct of_device_id earc_device_id[] = { MODULE_DEVICE_TABLE(of, earc_device_id); +static const unsigned int earcrx_extcon[] = { + EXTCON_EARCRX_ATNDTYP_ARC, + EXTCON_EARCRX_ATNDTYP_EARC, + EXTCON_NONE, +}; + +static int earcrx_extcon_register(struct earc *p_earc) +{ + int ret = 0; + + /* earc or arc connect */ + p_earc->rx_edev = devm_extcon_dev_allocate(p_earc->dev, earcrx_extcon); + if (IS_ERR(p_earc->rx_edev)) { + pr_err("failed to allocate earc extcon!!!\n"); + ret = -ENOMEM; + return ret; + } + p_earc->rx_edev->dev.parent = p_earc->dev; + p_earc->rx_edev->name = "earcrx"; + + dev_set_name(&p_earc->rx_edev->dev, "earcrx"); + ret = extcon_dev_register(p_earc->rx_edev); + if (ret < 0) { + pr_err("earc extcon failed to register!!\n"); + return ret; + } + + return ret; +} + +void earc_hdmitx_hpdst(bool st) +{ + struct earc *p_earc = s_earc; + + if (!p_earc) + return; + + pr_info("%s, %s\n", + __func__, + st ? "plugin" : "plugout"); + + /* ensure clock gate */ + audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x1 << 6); + + earcrx_cmdc_hpd_detect(p_earc->rx_cmdc_map, st); +} + +static int earcrx_cmdc_setup(struct earc *p_earc) +{ + int ret = 0; + + /* set cmdc clk */ + audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x1 << 6); + if (!IS_ERR(p_earc->clk_rx_cmdc)) { + clk_set_rate(p_earc->clk_rx_cmdc, 10000000); + + ret = clk_prepare_enable(p_earc->clk_rx_cmdc); + if (ret) { + pr_err("Can't enable earc clk_rx_cmdc: %d\n", ret); + return ret; + } + } + + /* rx cmdc init */ + earcrx_cmdc_init(p_earc->rx_top_map); + /* Default: arc arc_initiated */ + earcrx_cmdc_arc_connect(p_earc->rx_cmdc_map, true); + + ret = devm_request_threaded_irq(p_earc->dev, + p_earc->irq_earc_rx, + NULL, + earc_rx_isr, + IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, + "earc_rx", + p_earc); + if (ret) { + dev_err(p_earc->dev, "failed to claim earc_rx %u\n", + p_earc->irq_earc_rx); + return ret; + } + + return ret; +} + +static const unsigned int earctx_extcon[] = { + EXTCON_EARCTX_ATNDTYP_ARC, + EXTCON_EARCTX_ATNDTYP_EARC, + EXTCON_NONE, +}; + +static int earctx_extcon_register(struct earc *p_earc) +{ + int ret = 0; + + /* earc or arc connect */ + p_earc->tx_edev = devm_extcon_dev_allocate(p_earc->dev, earcrx_extcon); + if (IS_ERR(p_earc->tx_edev)) { + pr_err("failed to allocate earc extcon!!!\n"); + ret = -ENOMEM; + return ret; + } + p_earc->tx_edev->dev.parent = p_earc->dev; + p_earc->tx_edev->name = "earctx"; + + dev_set_name(&p_earc->tx_edev->dev, "earctx"); + ret = extcon_dev_register(p_earc->tx_edev); + if (ret < 0) { + pr_err("earc extcon failed to register!!\n"); + return ret; + } + + return ret; +} + +void earc_hdmirx_hpdst(int earc_port, bool st) +{ + struct earc *p_earc = s_earc; + + if (!p_earc) + return; + + pr_info("%s, earc_port:%d %s\n", + __func__, + earc_port, + st ? "plugin" : "plugout"); + + earctx_cmdc_int_mask(p_earc->tx_top_map); + earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, st); + earctx_cmdc_hpd_detect(p_earc->tx_top_map, + p_earc->tx_cmdc_map, + earc_port, st); +} + +static int earctx_cmdc_setup(struct earc *p_earc) +{ + int ret = 0; + + /* set cmdc clk */ + audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 5, 0x1 << 5); + if (!IS_ERR(p_earc->clk_tx_cmdc)) { + clk_set_rate(p_earc->clk_tx_cmdc, 10000000); + + ret = clk_prepare_enable(p_earc->clk_tx_cmdc); + if (ret) { + pr_err("Can't enable earc clk_tx_cmdc: %d\n", ret); + return ret; + } + } + + ret = devm_request_threaded_irq(p_earc->dev, + p_earc->irq_earc_tx, + NULL, + earc_tx_isr, + IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, + "earc_tx", + p_earc); + if (ret) { + dev_err(p_earc->dev, "failed to claim earc_tx %u\n", + p_earc->irq_earc_tx); + return ret; + } + + /* tx cmdc init */ + earctx_cmdc_init(p_earc->tx_top_map); + /* Default: arc arc_initiated */ + earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, true); + + return ret; +} + static int earc_platform_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -643,6 +1107,7 @@ static int earc_platform_probe(struct platform_device *pdev) struct earc *p_earc = NULL; int ret = 0; + pr_info("%s\n", __func__); p_earc = devm_kzalloc(dev, sizeof(struct earc), GFP_KERNEL); if (!p_earc) @@ -662,6 +1127,38 @@ static int earc_platform_probe(struct platform_device *pdev) platform_get_drvdata(pdev_parent); p_earc->actrl = actrl; + p_earc->tx_cmdc_map = regmap_resource(dev, "tx_cmdc"); + if (!p_earc->tx_cmdc_map) { + dev_err(dev, + "Can't get earctx_cmdc regmap!!\n"); + } + p_earc->tx_dmac_map = regmap_resource(dev, "tx_dmac"); + if (!p_earc->tx_dmac_map) { + dev_err(dev, + "Can't get earctx_dmac regmap!!\n"); + } + p_earc->tx_top_map = regmap_resource(dev, "tx_top"); + if (!p_earc->tx_top_map) { + dev_err(dev, + "Can't get earctx_top regmap!!\n"); + } + + p_earc->rx_cmdc_map = regmap_resource(dev, "rx_cmdc"); + if (!p_earc->rx_cmdc_map) { + dev_err(dev, + "Can't get earcrx_cdmc regmap!!\n"); + } + p_earc->rx_dmac_map = regmap_resource(dev, "rx_dmac"); + if (!p_earc->rx_dmac_map) { + dev_err(dev, + "Can't get earcrx_dmac regmap!!\n"); + } + p_earc->rx_top_map = regmap_resource(dev, "rx_top"); + if (!p_earc->rx_top_map) { + dev_err(dev, + "Can't get earcrx_top regmap!!\n"); + } + /* clock gate */ p_earc->clk_rx_gate = devm_clk_get(&pdev->dev, "rx_gate"); if (IS_ERR(p_earc->clk_rx_gate)) { @@ -754,26 +1251,19 @@ static int earc_platform_probe(struct platform_device *pdev) } /* irqs */ - p_earc->irq_rx_cmdc = - platform_get_irq_byname(pdev, "rx_cmdc"); - if (p_earc->irq_rx_cmdc < 0) { - dev_err(dev, "platform get irq rx_cmdc failed\n"); - return p_earc->irq_rx_cmdc; + p_earc->irq_earc_rx = + platform_get_irq_byname(pdev, "earc_rx"); + if (p_earc->irq_earc_rx < 0) { + dev_err(dev, "platform get irq earc_rx failed\n"); + return p_earc->irq_earc_rx; } - p_earc->irq_rx_dmac = - platform_get_irq_byname(pdev, "rx_dmac"); - if (p_earc->irq_rx_dmac < 0) { - dev_err(dev, "platform get irq rx_dmac failed\n"); - return p_earc->irq_rx_dmac; - } - p_earc->irq_tx_cmdc = - platform_get_irq_byname(pdev, "tx_cmdc"); - if (p_earc->irq_tx_cmdc < 0) - dev_err(dev, "platform get irq tx_cmdc failed, Check whether support eARC TX\n"); - p_earc->irq_tx_dmac = - platform_get_irq_byname(pdev, "tx_dmac"); - if (p_earc->irq_tx_dmac < 0) - dev_err(dev, "platform get irq tx_dmac failed, Check whether support eARC TX\n"); + p_earc->irq_earc_tx = + platform_get_irq_byname(pdev, "earc_tx"); + if (p_earc->irq_earc_tx < 0) + dev_err(dev, "platform get irq earc_tx failed, Check whether support eARC TX\n"); + + pr_info("%s, irq_earc_rx:%d, irq_earc_tx:%d\n", + __func__, p_earc->irq_earc_rx, p_earc->irq_earc_tx); ret = snd_soc_register_component(&pdev->dev, &earc_component, @@ -785,6 +1275,20 @@ static int earc_platform_probe(struct platform_device *pdev) return ret; } + s_earc = p_earc; + + /* RX */ + if (p_earc->irq_earc_rx > 0) { + earcrx_extcon_register(p_earc); + earcrx_cmdc_setup(p_earc); + } + + /* TX */ + if (p_earc->irq_earc_tx > 0) { + earctx_extcon_register(p_earc); + earctx_cmdc_setup(p_earc); + } + pr_info("%s, register soc platform\n", __func__); return devm_snd_soc_register_platform(dev, &earc_platform); @@ -797,7 +1301,12 @@ struct platform_driver earc_driver = { }, .probe = earc_platform_probe, }; -module_platform_driver(earc_driver); + +static int __init earc_init(void) +{ + return platform_driver_register(&earc_driver); +} +arch_initcall_sync(earc_init); MODULE_AUTHOR("Amlogic, Inc."); MODULE_DESCRIPTION("Amlogic eARC/ARC TX/RX ASoc driver"); diff --git a/sound/soc/amlogic/auge/earc_hw.c b/sound/soc/amlogic/auge/earc_hw.c index 0b217bb2cad0..2a857e8fd59c 100644 --- a/sound/soc/amlogic/auge/earc_hw.c +++ b/sound/soc/amlogic/auge/earc_hw.c @@ -15,112 +15,583 @@ * */ #include +#include +#include #include "earc_hw.h" - -void earcrx_cmdc_init(void) +void earcrx_cmdc_init(struct regmap *top_map) { /* set irq mask */ - earcrx_top_write(EARCRX_CMDC_INT_MASK, - (0 << 15) | /* idle2_int */ - (0 << 14) | /* idle1_int */ - (0 << 13) | /* disc2_int */ - (0 << 12) | /* disc1_int */ - (0 << 11) | /* earc_int */ - (1 << 10) | /* hb_status_int */ - (0 << 9) | /* losthb_int */ - (0 << 8) | /* timeout_int */ - (0 << 7) | /* status_ch_int */ - (0 << 6) | /* int_rec_invalid_id */ - (0 << 5) | /* int_rec_invalid_offset */ - (0 << 4) | /* int_rec_unexp */ - (0 << 3) | /* int_rec_ecc_err */ - (0 << 2) | /* int_rec_parity_err */ - (0 << 1) | /* int_recv_packet */ - (0 << 0) /* int_rec_time_out */ - ); + mmio_write(top_map, EARCRX_CMDC_INT_MASK, + (1 << 15) | /* idle2_int */ + (1 << 14) | /* idle1_int */ + (1 << 13) | /* disc2_int */ + (1 << 12) | /* disc1_int */ + (1 << 11) | /* earc_int */ + (1 << 10) | /* hb_status_int */ + (1 << 9) | /* losthb_int */ + (1 << 8) | /* timeout_int */ + (0 << 7) | /* status_ch_int */ + (0 << 6) | /* int_rec_invalid_id */ + (0 << 5) | /* int_rec_invalid_offset */ + (0 << 4) | /* int_rec_unexp */ + (0 << 3) | /* int_rec_ecc_err */ + (0 << 2) | /* int_rec_parity_err */ + (0 << 1) | /* int_recv_packet */ + (0 << 0) /* int_rec_time_out */ + ); - earcrx_top_write(EARCRX_ANA_CTRL0, 0x90884814); - earcrx_top_write(EARCRX_PLL_CTRL3, 0x242000); - earcrx_top_write(EARCRX_PLL_CTRL0, 0x10800400); + mmio_write(top_map, EARCRX_ANA_CTRL0, + 0x1 << 31 | /* earcrx_en_d2a */ + 0x10 << 24 | /* earcrx_cmdcrx_reftrim */ + 0x8 << 20 | /* earcrx_idr_trim */ + 0x10 << 15 | /* earcrx_rterm_trim */ + 0x4 << 12 | /* earcrx_cmdctx_ack_hystrim */ + 0x10 << 7 | /* earcrx_cmdctx_ack_reftrim */ + 0x1 << 4 | /* earcrx_cmdcrx_rcfilter_sel */ + 0x4 << 0 /* earcrx_cmdcrx_hystrim */ + ); + + mmio_write(top_map, EARCRX_PLL_CTRL3, + 0x2 << 20 | /* earcrx_pll_bias_adj */ + 0x4 << 16 | /* earcrx_pll_rou */ + 0x1 << 13 /* earcrx_pll_dco_sdm_e */ + ); + + mmio_write(top_map, EARCRX_PLL_CTRL0, + 0x1 << 28 | /* earcrx_pll_en */ + 0x1 << 23 | /* earcrx_pll_dmacrx_sqout_rstn_sel */ + 0x1 << 10 /* earcrx_pll_n */ + ); } -void earcrx_dmac_init(void) +void earcrx_cmdc_arc_connect(struct regmap *cmdc_map, bool init) { - earcrx_dmac_write(EARCRX_DMAC_SYNC_CTRL0, - (1 << 16) | /* reg_ana_buf_data_sel_en */ - (3 << 12) | /* reg_ana_buf_data_sel */ - (7 << 8) | /* reg_ana_clr_cnt */ - (7 << 4) /* reg_ana_set_cnt */ - ); - earcrx_dmac_write(EARCRX_DMAC_UBIT_CTRL0, - (47 << 16) | /* reg_fifo_thd */ - (1 << 12) | /* reg_user_lr */ - (29 << 0) /* reg_data_bit */ - ); - earcrx_dmac_write(EARCRX_ANA_RST_CTRL0, 1 << 31); + if (init) + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_VSM_CTRL0, + 0x7 << 25, + 0x1 << 27 | /* arc_initiated */ + 0x0 << 26 | /* arc_terminated */ + 0x1 << 25 /* arc_enable */ + ); + else + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_VSM_CTRL0, + 0x7 << 25, + 0x0 << 27 | /* arc_initiated */ + 0x1 << 26 | /* arc_terminated */ + 0x0 << 25 /* arc_enable */ + ); } -void earc_arc_init(void) +void earcrx_cmdc_hpd_detect(struct regmap *cmdc_map, bool st) { - earcrx_dmac_write(EARCRX_SPDIFIN_CTRL0, - (1 << 31) | /* reg_work_en */ - (1 << 30) | /* reg_chnum_sel */ - (1 << 25) | /* reg_findpapb_en */ - (0xFFF<<12) /* reg_nonpcm2pcm_th */ - ); - earcrx_dmac_write(EARCRX_SPDIFIN_CTRL2, - (1 << 14) | /* reg_earc_auto */ - (1 << 13) /* reg_earcin_papb_lr */ - ); - earcrx_dmac_write(EARCRX_SPDIFIN_CTRL3, - (0xEC37<<16) | /* reg_earc_pa_value */ - (0x5A5A<<0) /* reg_earc_pb_value */ - ); -} + if (st) { + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_VSM_CTRL0, + 0x1 << 19, + 0x1 << 19 /* comma_cnt_rst */ + ); -void earc_rx_enable(bool enable) -{ - if (enable) { - earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0, - 1 << 30, /* reg_rst_afifo_out_n */ - 1 << 30); - - earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0, - 1 << 29, /* reg_rst_afifo_in_n */ - 0x1 << 29); - - earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0, - 1 << 29, /* reg_rst_afifo_out_n */ - 1 << 29 - ); - earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0, - 1 << 28, /* reg_rst_afifo_in_n */ - 1 << 28 /* reg_rst_afifo_in_n */ - ); + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_VSM_CTRL0, + 0x1 << 19 | 0xff << 0, + 0x1 << 19 | /* comma_cnt_rst */ + 0xff << 0 + ); } else { - earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0, - 0x3 << 29, - 0x0 << 29); + /* soft reset */ + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_TOP_CTRL1, + 0xf << 0, + 0xf << 0); + mmio_update_bits(cmdc_map, + EARC_RX_CMDC_TOP_CTRL1, + 0xf << 0, + 0x0 << 0); + } +} - earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0, - 0x3 << 28, 0x0 << 28); +void earcrx_dmac_init(struct regmap *top_map, struct regmap *dmac_map) +{ + mmio_write(top_map, EARCRX_DMAC_INT_MASK, + (0x0 << 17) | /* earcrx_ana_rst c_new_format_set */ + (0x0 << 16) | /* earcrx_ana_rst c_earcrx_div2_hold_set */ + (0x0 << 15) | /* earcrx_err_correct c_bcherr_int_set */ + (0x0 << 14) | /* earcrx_err_correct r_afifo_overflow_set */ + (0x0 << 13) | /* earcrx_err_correct r_fifo_overflow_set */ + (0x0 << 12) | /* earcrx_user_bit_check r_fifo_overflow */ + (0x0 << 11) | /* earcrx_user_bit_check c_fifo_thd_pass */ + (0x0 << 10) | /* earcrx_user_bit_check c_u_pk_lost_int_set */ + (0x0 << 9) | /* arcrx_user_bit_check c_iu_pk_end */ + (0x0 << 8) | /* arcrx_biphase_decode c_chst_mute_clr */ + (0x1 << 7) | /* arcrx_biphase_decode c_find_papb */ + (0x1 << 6) | /* arcrx_biphase_decode c_valid_change */ + (0x1 << 5) | /* arcrx_biphase_decode c_find_nonpcm2pcm */ + (0x1 << 4) | /* arcrx_biphase_decode c_pcpd_change */ + (0x1 << 3) | /* arcrx_biphase_decode c_ch_status_change */ + (0x1 << 2) | /* arcrx_biphase_decode sample_mod_change */ + (0x1 << 1) | /* arcrx_biphase_decode r_parity_err */ + (0x0 << 0) /* arcrx_dmac_sync afifo_overflow */ + ); + + mmio_write(dmac_map, EARCRX_DMAC_SYNC_CTRL0, + (1 << 16) | /* reg_ana_buf_data_sel_en */ + (3 << 12) | /* reg_ana_buf_data_sel */ + (7 << 8) | /* reg_ana_clr_cnt */ + (7 << 4) /* reg_ana_set_cnt */ + ); + mmio_write(dmac_map, EARCRX_DMAC_UBIT_CTRL0, + (47 << 16) | /* reg_fifo_thd */ + (1 << 12) | /* reg_user_lr */ + (29 << 0) /* reg_data_bit */ + ); + mmio_write(dmac_map, EARCRX_ANA_RST_CTRL0, 1 << 31); +} + +void earcrx_arc_init(struct regmap *dmac_map) +{ + unsigned int spdifin_clk = 500000000; + + /* sysclk/rate/32(bit)/2(ch)/2(bmc) */ + unsigned int counter_32k = (spdifin_clk / (32000 * 64)); + unsigned int counter_44k = (spdifin_clk / (44100 * 64)); + unsigned int counter_48k = (spdifin_clk / (48000 * 64)); + unsigned int counter_88k = (spdifin_clk / (88200 * 64)); + unsigned int counter_96k = (spdifin_clk / (96000 * 64)); + unsigned int counter_176k = (spdifin_clk / (176400 * 64)); + unsigned int counter_192k = (spdifin_clk / (192000 * 64)); + unsigned int mode0_th = 3 * (counter_32k + counter_44k) >> 1; + unsigned int mode1_th = 3 * (counter_44k + counter_48k) >> 1; + unsigned int mode2_th = 3 * (counter_48k + counter_88k) >> 1; + unsigned int mode3_th = 3 * (counter_88k + counter_96k) >> 1; + unsigned int mode4_th = 3 * (counter_96k + counter_176k) >> 1; + unsigned int mode5_th = 3 * (counter_176k + counter_192k) >> 1; + unsigned int mode0_timer = counter_32k >> 1; + unsigned int mode1_timer = counter_44k >> 1; + unsigned int mode2_timer = counter_48k >> 1; + unsigned int mode3_timer = counter_88k >> 1; + unsigned int mode4_timer = counter_96k >> 1; + unsigned int mode5_timer = (counter_176k >> 1); + unsigned int mode6_timer = (counter_192k >> 1); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL0, + 0x0 << 28 | /* detect by max_width */ + (spdifin_clk / 10000) << 0 /* base timer */ + ); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL1, + mode0_th << 20 | + mode1_th << 10 | + mode2_th << 0); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL2, + mode3_th << 20 | + mode4_th << 10 | + mode5_th << 0); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL3, + (mode0_timer << 24) | + (mode1_timer << 16) | + (mode2_timer << 8) | + (mode3_timer << 0) + ); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL4, + (mode4_timer << 24) | + (mode5_timer << 16) | + (mode6_timer << 8) + ); + + mmio_write(dmac_map, + EARCRX_SPDIFIN_CTRL0, + 0x1 << 31 | /* reg_work_en */ + 0x1 << 30 | /* reg_chnum_sel */ + 0x1 << 25 | /* reg_findpapb_en */ + 0x1 << 24 | /* nonpcm2pcm_th enable */ + 0xFFF << 12 /* reg_nonpcm2pcm_th */ + ); + mmio_write(dmac_map, + EARCRX_SPDIFIN_CTRL2, + (1 << 14) | /* reg_earc_auto */ + (1 << 13) /* reg_earcin_papb_lr */ + ); + mmio_write(dmac_map, + EARCRX_SPDIFIN_CTRL3, + (0xEC37 << 16) | /* reg_earc_pa_value */ + (0x5A5A << 0) /* reg_earc_pb_value */ + ); +} + +enum cmdc_st earcrx_cmdc_get_state(struct regmap *cmdc_map) +{ + int val = mmio_read(cmdc_map, EARC_RX_CMDC_STATUS0); + enum cmdc_st state = (enum cmdc_st)(val & 0x7); + + return state; +} + +enum attend_type earcrx_cmdc_get_attended_type(struct regmap *cmdc_map) +{ + int val = mmio_read(cmdc_map, EARC_RX_CMDC_STATUS0); + enum cmdc_st state = (enum cmdc_st)(val & 0x7); + enum attend_type type = ATNDTYP_DISCNCT; + + if ((val & (1 << 0x3)) && (state == CMDC_ST_ARC)) + type = ATNDTYP_ARC; + else if ((val & (1 << 0x4)) && (state == CMDC_ST_EARC)) + type = ATNDTYP_EARC; + + return type; +} + +void earcrx_cdmc_clr_irqs(struct regmap *top_map, int clr) +{ + mmio_write(top_map, EARCRX_CMDC_INT_PENDING, clr); +} + +int earcrx_cdmc_get_irqs(struct regmap *top_map) +{ + return mmio_read(top_map, EARCRX_CMDC_INT_PENDING); +} + +void earcrx_dmac_clr_irqs(struct regmap *top_map, int clr) +{ + mmio_write(top_map, EARCRX_DMAC_INT_PENDING, clr); +} + +int earcrx_dmac_get_irqs(struct regmap *top_map) +{ + return mmio_read(top_map, EARCRX_DMAC_INT_PENDING); +} + +void earcrx_enable(struct regmap *cmdc_map, + struct regmap *dmac_map, bool enable) +{ + enum attend_type type = earcrx_cmdc_get_attended_type(cmdc_map); + + if (enable) { + mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0, + 1 << 30, /* reg_rst_afifo_out_n */ + 1 << 30); + + mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0, + 1 << 29, /* reg_rst_afifo_in_n */ + 0x1 << 29); + + mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0, + 1 << 29, /* reg_rst_afifo_out_n */ + 1 << 29 + ); + mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0, + 1 << 28, /* reg_rst_afifo_in_n */ + 1 << 28 /* reg_rst_afifo_in_n */ + ); + } else { + mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0, + 0x3 << 29, + 0x0 << 29); + + mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0, + 0x3 << 28, 0x0 << 28); } - earcrx_dmac_update_bits(EARCRX_DMAC_SYNC_CTRL0, - 1 << 31, /* reg_work_en */ - enable << 31); + if (type == ATNDTYP_EARC) + mmio_update_bits(dmac_map, EARCRX_DMAC_SYNC_CTRL0, + 1 << 31, /* reg_work_en */ + enable << 31); + else if (type == ATNDTYP_ARC) { + mmio_update_bits(dmac_map, + EARCRX_SPDIFIN_SAMPLE_CTRL0, + 0x1 << 31, /* reg_work_enable */ + enable << 31); - earcrx_dmac_update_bits(EARCRX_DMAC_UBIT_CTRL0, - 1 << 31, /* reg_work_enable */ - enable << 31); + mmio_write(dmac_map, EARCRX_DMAC_SYNC_CTRL0, 0x0); + } - earcrx_dmac_update_bits(EARCRX_ERR_CORRECT_CTRL0, - 1 << 31, - enable << 31); /* reg_work_en */ + mmio_update_bits(dmac_map, EARCRX_DMAC_UBIT_CTRL0, + 1 << 31, /* reg_work_enable */ + enable << 31); - earcrx_dmac_update_bits(EARCRX_DMAC_TOP_CTRL0, - 1 << 31, - enable << 31); /* reg_top_work_en */ + mmio_update_bits(dmac_map, EARCRX_ERR_CORRECT_CTRL0, + 1 << 31, + enable << 31); /* reg_work_en */ + + mmio_update_bits(dmac_map, EARCRX_DMAC_TOP_CTRL0, + 1 << 31, + enable << 31); /* reg_top_work_en */ +} + +void earctx_cmdc_int_mask(struct regmap *top_map) +{ + mmio_write(top_map, EARCTX_CMDC_INT_MASK, + (0x0 << 17) | /* hpd_high_int */ + (0x0 << 16) | /* hpd_low_int */ + (0x1 << 15) | /* idle2_int */ + (0x1 << 14) | /* idle1_int */ + (0x1 << 13) | /* disc2_int */ + (0x1 << 12) | /* disc1_int */ + (0x1 << 11) | /* earc_int */ + (0x0 << 10) | /* hb_status_int */ + (0x1 << 9) | /* losthb_int */ + (0x1 << 8) | /* timeout_int */ + (0x0 << 7) | /* status_ch_int */ + (0x0 << 6) | /* int_recv_finished */ + (0x0 << 5) | /* int_recv_nack */ + (0x0 << 4) | /* int_recv_norsp */ + (0x0 << 3) | /* int_recv_unexp */ + (0x0 << 2) | /* int_recv_data */ + (0x0 << 1) | /* int_recv_ack */ + (0x0 << 0) /* int_recv_ecc_err */ + ); +} + +void earctx_cmdc_init(struct regmap *top_map) +{ + /* ana */ + mmio_write(top_map, EARCTX_ANA_CTRL0, + 0x1 << 31 | /* earctx_en_d2a */ + 0x1 << 28 | /* earctx_cmdcrx_rcfilter_sel */ + 0x4 << 26 | /* earctx_cmdcrx_hystrim */ + 0x8 << 20 | /* earctx_idr_trim */ + 0x10 << 12 | /* earctx_rterm_trim */ + 0x4 << 8 | /* earctx_dmac_slew_con */ + 0x4 << 5 | /* earctx_cmdctx_ack_hystrim */ + 0x10 << 0 /* earctx_cmdctx_ack_reftrim */ + ); +} + +void earctx_cmdc_arc_connect(struct regmap *cmdc_map, bool init) +{ + if (init) + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL0, + 0x7 << 25, + 0x1 << 27 | /* arc_initiated */ + 0x0 << 26 | /* arc_terminated */ + 0x1 << 25 /* arc_enable */ + ); + else + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL0, + 0x7 << 25, + 0x0 << 27 | /* arc_initiated */ + 0x1 << 26 | /* arc_terminated */ + 0x0 << 25 /* arc_enable */ + ); +} + +void earctx_cmdc_hpd_detect(struct regmap *top_map, + struct regmap *cmdc_map, + int earc_port, bool st) +{ + /* select hdmirx_hpd */ + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL1, + 0x1 << 8, /* cntl_hpd_sel */ + 0x1 << 8); + + /* select hdmi_hpd mux */ + mmio_update_bits(top_map, EARCTX_TOP_CTRL0, + 0xf << 8, + 0x1 << 11 | /* hdmi_hpd invent */ + earc_port << 8 /* hdmi_hpd mux, port 0/1/2 */ + ); + + if (st) { + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL0, + 0x1 << 19, + 0x1 << 19 /* comma_cnt_rst */ + ); + + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL0, + 0x1 << 19 | 0xf << 20, + 0x0 << 19 | /* comma_cnt_rst */ + 0x3 << 20 | 0x3 << 22 + ); + + /* no timeout */ + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL5, + 0x3 << 0, + 0x1 << 1 + ); + + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_VSM_CTRL1, + 0xff << 0, + 0xa << 0 /* comma_cnt_th */ + ); + } else { + /* soft reset */ + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_TOP_CTRL1, + 0xf << 0, + 0xf << 0); + mmio_update_bits(cmdc_map, + EARC_TX_CMDC_TOP_CTRL1, + 0xf << 0, + 0x0 << 0); + } +} + +void earctx_dmac_init(struct regmap *top_map, struct regmap *dmac_map) +{ + mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0, + 0x3 << 28, + 0x0 << 28); + mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0, + 0x1 << 29, /* afifo out reset */ + 0x1 << 29); + mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0, + 0x1 << 28 | /* afifo in reset */ + 0x1 << 26 | /* user Bit select */ + 0x1 << 24 | /* chdata select*/ + 0x1 << 20 | /* reg_data_sel, 1: data from 27bit */ + 0x1 << 19 | /* 0: lsb first */ + 0x1 << 18 | /* biphase encode valid Bit value sel */ + 0xff << 4, /* lane mask */ + 0x1 << 28 | + 0x1 << 26 | + 0x1 << 24 | + 0x1 << 20 | + 0x0 << 19 | + 0x1 << 18 | + 0x3 << 4 // ODO: lane 0 now + ); + + mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL0, + 0x1 << 16 | /* reg_ubit_fifo_init_n */ + 0x7 << 8 | /* r channel select */ + 0x7 << 4, /* l channel select */ + 0x1 << 16 | + 0x1 << 8 | + 0x0 << 4); + + mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL4, + 0xf << 17, + 0x1 << 19 | /* userBit sel, 1: reg_value */ + 0x1 << 18 | /* validBit sel, 1: reg_value */ + 0x1 << 17 /* reg_chst_sel, 1: reg_value */ + ); +} + +void earctx_dmac_set_format(struct regmap *dmac_map, + int frddr_idx, int msb, int frddr_type) +{ + mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL1, + 0x7 << 24 | /* frddr src */ + 0xff << 16 | /* waiting count after enabled */ + 0x1f << 8 | /* msb position */ + 0x7 << 4, /* frddr type */ + frddr_idx << 24 | + 0x40 << 16 | + msb << 8 | + frddr_type << 4); + + mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL4, + 0x1f << 25, + 0x1f << 25 /* msb */ + ); + + /* for raw data */ + mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL3, + 0x1f << 24, + 0x17 << 24 + ); +} + +void earctx_set_channel_status_info(struct regmap *dmac_map, + struct iec958_chsts *chsts) +{ + /* ch status = reg_chsts0~B */ + + /* L Channel */ + mmio_write(dmac_map, EARCTX_SPDIFOUT_CHSTS0, + ((chsts->chstat1_l >> 8) & 0xf) << 24 | chsts->chstat0_l); + + /* R Channel */ + mmio_write(dmac_map, EARCTX_SPDIFOUT_CHSTS6, + ((chsts->chstat1_r >> 8) & 0xf) << 24 | chsts->chstat0_r); +} + +enum attend_type earctx_cmdc_get_attended_type(struct regmap *cmdc_map) +{ + int val = mmio_read(cmdc_map, EARC_TX_CMDC_STATUS0); + enum cmdc_st state = (enum cmdc_st)(val & 0x7); + enum attend_type tx_type = ATNDTYP_DISCNCT; + + if ((val & (1 << 0x3)) && (state == CMDC_ST_ARC)) + tx_type = ATNDTYP_ARC; + else if ((val & (1 << 0x4)) && (state == CMDC_ST_EARC)) + tx_type = ATNDTYP_EARC; + + return tx_type; +} + +void earctx_cdmc_clr_irqs(struct regmap *top_map, int clr) +{ + mmio_write(top_map, EARCTX_CMDC_INT_PENDING, clr); +} + +int earctx_cdmc_get_irqs(struct regmap *top_map) +{ + return mmio_read(top_map, EARCTX_CMDC_INT_PENDING); +} + +void earctx_dmac_clr_irqs(struct regmap *top_map, int clr) +{ + mmio_write(top_map, EARCTX_DMAC_INT_PENDING, clr); +} + +int earctx_dmac_get_irqs(struct regmap *top_map) +{ + return mmio_read(top_map, EARCTX_DMAC_INT_PENDING); +} + +void earctx_enable(struct regmap *top_map, + struct regmap *cmdc_map, + struct regmap *dmac_map, + bool enable) +{ + mmio_update_bits(dmac_map, EARCTX_SPDIFOUT_CTRL0, + 0x1 << 31, + enable << 31); + + mmio_update_bits(dmac_map, EARC_RX_CMDC_BIPHASE_CTRL1, + 0x1 << 30, + enable << 30); + + if (enable) + mmio_write(top_map, EARCTX_DMAC_INT_MASK, + (0x1 << 4) | /* fe_modulation c_hold_clr */ + (0x1 << 3) | /* fe_modulation c_hold_start */ + (0x1 << 2) | /* err_correct c_fifo_thd_less_pass */ + (0x1 << 1) | /* err_correct r_fifo_overflow_set */ + (0x1 << 0) /* err_correct c_fifo_empty_set */ + ); + else + mmio_write(top_map, EARCTX_DMAC_INT_MASK, + 0x0 + ); + + if (earctx_cmdc_get_attended_type(cmdc_map) == ATNDTYP_EARC) { + if (spdifout_is_raw()) { + mmio_update_bits(dmac_map, EARCTX_ERR_CORRT_CTRL3, + 0x1 << 29, + enable << 29); + } + mmio_update_bits(dmac_map, EARCTX_FE_CTRL0, + 0x1 << 30, + enable << 30); + } } diff --git a/sound/soc/amlogic/auge/earc_hw.h b/sound/soc/amlogic/auge/earc_hw.h index 1630733add2f..6e26d32fe75a 100644 --- a/sound/soc/amlogic/auge/earc_hw.h +++ b/sound/soc/amlogic/auge/earc_hw.h @@ -19,9 +19,126 @@ #include "regs.h" #include "iomap.h" +#include +#include + +#define INT_EARCRX_CMDC_IDLE2 (0x1 << 15) +#define INT_EARCRX_CMDC_IDLE1 (0x1 << 14) +#define INT_EARCRX_CMDC_DISC2 (0x1 << 13) +#define INT_EARCRX_CMDC_DISC1 (0x1 << 12) +#define INT_EARCRX_CMDC_EARC (0x1 << 11) +#define INT_EARCRX_CMDC_HB_STATUS (0x1 << 10) +#define INT_EARCRX_CMDC_LOSTHB (0x1 << 9) +#define INT_EARCRX_CMDC_TIMEOUT (0x1 << 8) +#define INT_EARCRX_CMDC_STATUS_CH (0x1 << 7) +#define INT_EARCRX_CMDC_REC_INVALID_ID (0x1 << 6) +#define INT_EARCRX_CMDC_REC_INVALID_OFFSET (0x1 << 5) +#define INT_EARCRX_CMDC_REC_UNEXP (0x1 << 4) +#define INT_EARCRX_CMDC_REC_ECC_ERR (0x1 << 3) +#define INT_EARCRX_CMDC_REC_PARITY_ERR (0x1 << 2) +#define INT_EARCRX_CMDC_RECV_PACKET (0x1 << 1) +#define INT_EARCRX_CMDC_REC_TIME_OUT (0x1 << 0) + +#define INT_EARCRX_ANA_RST_C_NEW_FORMAT_SET (0x1 << 17) +#define INT_EARCRX_ANA_RST_C_EARCRX_DIV2_HOLD_SET (0x1 << 16) +#define INT_EARCRX_ERR_CORRECT_C_BCHERR_INT_SET (0x1 << 15) +#define INT_EARCRX_ERR_CORRECT_R_AFIFO_OVERFLOW_SET (0x1 << 14) +#define INT_EARCRX_ERR_CORRECT_R_FIFO_OVERFLOW_SET (0x1 << 13) +#define INT_EARCRX_USER_BIT_CHECK_R_FIFO_OVERFLOW (0x1 << 12) +#define INT_EARCRX_USER_BIT_CHECK_C_FIFO_THD_PASS (0x1 << 11) +#define INT_EARCRX_USER_BIT_CHECK_C_U_PK_LOST_INT_SET (0x1 << 10) +#define INT_ARCRX_USER_BIT_CHECK_C_IU_PK_END (0x1 << 9) +#define INT_ARCRX_BIPHASE_DECODE_C_CHST_MUTE_CLR (0x1 << 8) +#define INT_ARCRX_BIPHASE_DECODE_C_FIND_PAPB (0x1 << 7) +#define INT_ARCRX_BIPHASE_DECODE_C_VALID_CHANGE (0x1 << 6) +#define INT_ARCRX_BIPHASE_DECODE_C_FIND_NONPCM2PCM (0x1 << 5) +#define INT_ARCRX_BIPHASE_DECODE_C_PCPD_CHANGE (0x1 << 4) +#define INT_ARCRX_BIPHASE_DECODE_C_CH_STATUS_CHANGE (0x1 << 3) +#define INT_ARCRX_BIPHASE_DECODE_I_SAMPLE_MODE_CHANGE (0x1 << 2) +#define INT_ARCRX_BIPHASE_DECODE_R_PARITY_ERR (0x1 << 1) +#define INT_ARCRX_DMAC_SYNC_AFIFO_OVERFLOW (0x1 << 0) + +#define INT_EARCTX_CMDC_HPD_H (0x1 << 17) +#define INT_EARCTX_CMDC_HPD_L (0x1 << 16) +#define INT_EARCTX_CMDC_IDLE2 (0x1 << 15) +#define INT_EARCTX_CMDC_IDLE1 (0x1 << 14) +#define INT_EARCTX_CMDC_DISC2 (0x1 << 13) +#define INT_EARCTX_CMDC_DISC1 (0x1 << 12) +#define INT_EARCTX_CMDC_EARC (0x1 << 11) +#define INT_EARCTX_CMDC_HB_STATUS (0x1 << 10) +#define INT_EARCTX_CMDC_LOSTHB (0x1 << 9) +#define INT_EARCTX_CMDC_TIMEOUT (0x1 << 8) +#define INT_EARCTX_CMDC_STATUS_CH (0x1 << 7) +#define INT_EARCTX_CMDC_RECV_FINISHED (0x1 << 6) +#define INT_EARCTX_CMDC_RECV_NACK (0x1 << 5) +#define INT_EARCTX_CMDC_RECV_NORSP (0x1 << 4) +#define INT_EARCTX_CMDC_RECV_UNEXP (0x1 << 3) +#define INT_EARCTX_CMDC_RECV_DATA (0x1 << 2) +#define INT_EARCTX_CMDC_RECV_ACK (0x1 << 1) +#define INT_EARCTX_CMDC_RECV_ECC_ERR (0x1 << 0) + +#define INT_EARCTX_FEM_C_HOLD_CLR (0x1 << 4) +#define INT_EARCTX_FEM_C_HOLD_START (0x1 << 3) +#define INT_EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS (0x1 << 2) +#define INT_EARCTX_ERRCORR_C_FIFO_OVERFLOW (0x1 << 1) +#define INT_EARCTX_ERRCORR_C_FIFO_EMPTY (0x1 << 0) + +/* cmdc discovery and disconnect state */ +enum cmdc_st { + CMDC_ST_OFF, + CMDC_ST_IDLE1, + CMDC_ST_IDLE2, + CMDC_ST_DISC1, + CMDC_ST_DISC2, + CMDC_ST_EARC, + CMDC_ST_ARC +}; + +/* attended type: disconect, ARC, eARC */ +enum attend_type { + ATNDTYP_DISCNCT, + ATNDTYP_ARC, + ATNDTYP_EARC +}; + +enum tx_hd_hdp_mux { + GPIOW_1, + GPIOW_9, + GPIOW_5 +}; + +void earcrx_cmdc_init(struct regmap *top_map); +void earcrx_cmdc_arc_connect(struct regmap *cmdc_map, bool init); +void earcrx_cmdc_hpd_detect(struct regmap *cmdc_map, bool st); +void earcrx_dmac_init(struct regmap *top_map, struct regmap *dmac_map); +void earcrx_arc_init(struct regmap *dmac_map); +enum cmdc_st earcrx_cmdc_get_state(struct regmap *cmdc_map); +enum attend_type earcrx_cmdc_get_attended_type(struct regmap *cmdc_map); +void earcrx_cdmc_clr_irqs(struct regmap *top_map, int clr); +int earcrx_cdmc_get_irqs(struct regmap *top_map); +void earcrx_dmac_clr_irqs(struct regmap *top_map, int clr); +int earcrx_dmac_get_irqs(struct regmap *top_map); +void earcrx_enable(struct regmap *cmdc_map, + struct regmap *dmac_map, bool enable); +void earctx_cmdc_int_mask(struct regmap *top_map); + +void earctx_cmdc_init(struct regmap *top_map); +void earctx_cmdc_arc_connect(struct regmap *cmdc_map, bool init); +void earctx_cmdc_hpd_detect(struct regmap *top_map, + struct regmap *cmdc_map, + int earc_port, bool st); +void earctx_dmac_init(struct regmap *top_map, struct regmap *dmac_map); +void earctx_dmac_set_format(struct regmap *dmac_map, + int frddr_idx, int msb, int frddr_type); +void earctx_set_channel_status_info(struct regmap *dmac_map, + struct iec958_chsts *chsts); +void earctx_cdmc_clr_irqs(struct regmap *top_map, int clr); +int earctx_cdmc_get_irqs(struct regmap *top_map); +void earctx_dmac_clr_irqs(struct regmap *top_map, int clr); +int earctx_dmac_get_irqs(struct regmap *top_map); +void earctx_enable(struct regmap *top_map, + struct regmap *cmdc_map, + struct regmap *dmac_map, + bool enable); -extern void earcrx_cmdc_init(void); -extern void earcrx_dmac_init(void); -extern void earc_arc_init(void); -extern void earc_rx_enable(bool enable); #endif diff --git a/sound/soc/amlogic/auge/effects_hw_v2.c b/sound/soc/amlogic/auge/effects_hw_v2.c index b5db9d64587f..dfbc2b436ea9 100644 --- a/sound/soc/amlogic/auge/effects_hw_v2.c +++ b/sound/soc/amlogic/auge/effects_hw_v2.c @@ -235,7 +235,35 @@ void aed_set_lane_and_channels(int lane_mask, int ch_mask) 0xff << 12, (ch_num - 1) << 12); } -void aed_set_ctrl(bool enable, int sel, enum frddr_dest module) +/* max 16ch, 8 lane*/ +void aed_set_lane_and_channels_v3(int lane_mask, int ch_mask) +{ + int ch_num = 0, lane_num = 0, i = 0; + int val_ch = ch_mask & 0xffff; + int val_lane = lane_mask & 0xff; + + for (i = 0; i < 16; i++) { + if ((val_ch & 0x1) == 0x1) + ch_num++; + if ((val_lane & 0x1) == 0x1) + lane_num = i; + val_ch >>= 1; + val_lane >>= 1; + } + + eqdrc_update_bits(AED_TOP_CTL1, + 0xf << 20 | 0xf << 16 | 0x1 << 5 | 0x1 << 4 | 0xf, + (lane_num * 2 + 1) << 20 | (lane_num * 2) << 16 | + 0x1 << 5 | 0x1 << 4 | (ch_num - 1)); + + eqdrc_update_bits(AED_TOP_CTL2, + 0xff << 12, (ch_num - 1) << 12); + + /*pr_info("ch_num = %d, lane_num = %d", ch_num, lane_num);*/ +} + + +void aed_set_ctrl(bool enable, int sel, enum frddr_dest module, int offset) { int mask = 0, val = 0; @@ -258,7 +286,8 @@ void aed_set_ctrl(bool enable, int sel, enum frddr_dest module) return; } - eqdrc_update_bits(AED_TOP_REQ_CTL, mask, val); + /*AED_TOP_REQ_CTL or AED_TOP_CTL2*/ + eqdrc_update_bits((AED_TOP_REQ_CTL + offset), mask, val); /* Effect Module */ if (module <= TDMOUT_C && module >= TDMOUT_A) { @@ -271,11 +300,12 @@ void aed_set_ctrl(bool enable, int sel, enum frddr_dest module) } -void aed_set_format(int msb, enum ddr_types frddr_type, enum ddr_num source) +void aed_set_format(int msb, enum ddr_types frddr_type, + enum ddr_num source, int offset) { eqdrc_update_bits(AED_TOP_CTL, 0x7 << 11 | 0x1f << 6 | 0x3 << 4, - frddr_type << 11 | msb << 6 | source << 4); + frddr_type << 11 | msb << 6 | source << (4 - offset)); } void aed_enable(bool enable) diff --git a/sound/soc/amlogic/auge/effects_hw_v2.h b/sound/soc/amlogic/auge/effects_hw_v2.h index cfa8e0fa78a8..ae2807001adc 100644 --- a/sound/soc/amlogic/auge/effects_hw_v2.h +++ b/sound/soc/amlogic/auge/effects_hw_v2.h @@ -33,8 +33,11 @@ void aed_set_multiband_drc_enable(bool enable); void aed_set_volume(unsigned int master_volume, unsigned int Lch_vol, unsigned int Rch_vol); void aed_set_lane_and_channels(int lane_mask, int ch_mask); -void aed_set_ctrl(bool enable, int sel, enum frddr_dest module); -void aed_set_format(int msb, enum ddr_types frddr_type, enum ddr_num source); +void aed_set_lane_and_channels_v3(int lane_mask, int ch_mask); +void aed_set_ctrl(bool enable, int sel, + enum frddr_dest module, int offset); +void aed_set_format(int msb, enum ddr_types frddr_type, + enum ddr_num source, int offset); void aed_enable(bool enable); void aed_set_mixer_params(void); void aed_eq_taps(unsigned int eq1_taps); diff --git a/sound/soc/amlogic/auge/effects_v2.c b/sound/soc/amlogic/auge/effects_v2.c index 86a696b1df70..fe4571f7dbd5 100644 --- a/sound/soc/amlogic/auge/effects_v2.c +++ b/sound/soc/amlogic/auge/effects_v2.c @@ -49,8 +49,10 @@ enum { struct effect_chipinfo { /* v1 is for G12X(g12a, g12b) * v2 is for tl1 + * v3 is for sm1/tm2 */ - bool v2; + int version; + bool reserved_frddr; }; struct audioeffect { @@ -90,17 +92,24 @@ static struct audioeffect *get_audioeffects(void) return s_effect; } -bool check_aed_v2(void) +int get_aed_dst(void) { struct audioeffect *p_effect = get_audioeffects(); if (!p_effect) - return false; + return -1; + else + return p_effect->effect_module; +} - if (p_effect->chipinfo && p_effect->chipinfo->v2) - return true; +int check_aed_version(void) +{ + struct audioeffect *p_effect = get_audioeffects(); - return false; + if ((!p_effect) || (!p_effect->chipinfo)) + return -1; + + return p_effect->chipinfo->version; } static int eqdrc_clk_set(struct audioeffect *p_effect) @@ -577,7 +586,13 @@ int card_add_effect_v2_kcontrols(struct snd_soc_card *card) } static struct effect_chipinfo tl1_effect_chipinfo = { - .v2 = true, + .version = VERSION2, + .reserved_frddr = true, +}; + +static struct effect_chipinfo sm1_effect_chipinfo = { + .version = VERSION3, + .reserved_frddr = true, }; static const struct of_device_id effect_device_id[] = { @@ -589,8 +604,8 @@ static const struct of_device_id effect_device_id[] = { .data = &tl1_effect_chipinfo, }, { - .compatible = "amlogic, tl1-effect", - .data = &tl1_effect_chipinfo, + .compatible = "amlogic, snd-effect-v3", + .data = &sm1_effect_chipinfo, }, {} }; @@ -701,8 +716,16 @@ static int effect_platform_probe(struct platform_device *pdev) p_effect->ch_mask = channel_mask; p_effect->effect_module = eqdrc_module; + p_effect->dev = dev; + s_effect = p_effect; + dev_set_drvdata(&pdev->dev, p_effect); + /*set eq/drc module lane & channels*/ - aed_set_lane_and_channels(lane_mask, channel_mask); + if (check_aed_version() == VERSION3) + aed_set_lane_and_channels_v3(lane_mask, channel_mask); + else + aed_set_lane_and_channels(lane_mask, channel_mask); + /*set master & channel volume gain to 0dB*/ aed_set_volume(0xc0, 0x30, 0x30); /*set default mixer gain*/ @@ -720,9 +743,10 @@ static int effect_platform_probe(struct platform_device *pdev) /*set EQ/DRC module enable*/ aml_set_aed(1, p_effect->effect_module); - p_effect->dev = dev; - s_effect = p_effect; - dev_set_drvdata(&pdev->dev, p_effect); + if (p_effect->chipinfo && + p_effect->chipinfo->reserved_frddr) { + aml_aed_set_frddr_reserved(); + } return 0; } diff --git a/sound/soc/amlogic/auge/effects_v2.h b/sound/soc/amlogic/auge/effects_v2.h index 9c5051ae9996..86cdeed72d6f 100644 --- a/sound/soc/amlogic/auge/effects_v2.h +++ b/sound/soc/amlogic/auge/effects_v2.h @@ -17,7 +17,13 @@ #ifndef __EFFECTS_V2_H__ #define __EFFECTS_V2_H__ -extern bool check_aed_v2(void); +enum { + VERSION1 = 0, + VERSION2, + VERSION3 +}; + +extern int check_aed_version(void); extern int card_add_effect_v2_kcontrols(struct snd_soc_card *card); #endif diff --git a/sound/soc/amlogic/auge/extn.c b/sound/soc/amlogic/auge/extn.c index 78ec04f952bd..3cfb815da868 100644 --- a/sound/soc/amlogic/auge/extn.c +++ b/sound/soc/amlogic/auge/extn.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "ddr_mngr.h" #include "audio_utils.h" @@ -45,11 +46,19 @@ #define MAX_INT 0x7ffffff +#define DYNC_KCNTL_CNT 2 + struct extn_chipinfo { /* try to check papb before fetch pcpd * no nonpcm2pcm irq for tl1 */ bool no_nonpcm2pcm_clr; + + /* eARC-ARC or CEC-ARC + * CEC-ARC: tl1 + * eARC-ARC: sm1/tm2, etc + */ + bool cec_arc; }; struct extn { @@ -89,6 +98,8 @@ struct extn { bool nonpcm_flag; struct extn_chipinfo *chipinfo; + struct snd_kcontrol *controls[DYNC_KCNTL_CNT]; + }; #define PREALLOC_BUFFER (256 * 1024) @@ -134,6 +145,17 @@ static irqreturn_t extn_ddr_isr(int irq, void *devid) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct device *dev = rtd->platform->dev; struct extn *p_extn = (struct extn *)dev_get_drvdata(dev); + int timeout_thres = 5; + +#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_HDMI + int sample_rate_index = get_hdmi_sample_rate_index(); + + /*192K audio*/ + if (sample_rate_index == 7) + timeout_thres = 10; + else + timeout_thres = 5; +#endif if (!snd_pcm_running(substream)) return IRQ_HANDLED; @@ -356,10 +378,19 @@ struct snd_soc_platform_driver extn_platform = { .pcm_new = extn_new, }; +static int extn_create_controls(struct snd_card *card, + struct extn *p_extn); + static int extn_dai_probe(struct snd_soc_dai *cpu_dai) { + struct snd_card *card = cpu_dai->component->card->snd_card; + struct extn *p_extn = snd_soc_dai_get_drvdata(cpu_dai); + pr_info("asoc debug: %s-%d\n", __func__, __LINE__); + if (p_extn->chipinfo && p_extn->chipinfo->cec_arc) + extn_create_controls(card, p_extn); + return 0; } @@ -607,8 +638,10 @@ static int arc_get_src( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct extn *p_extn = dev_get_drvdata(component->dev); + struct extn *p_extn = snd_kcontrol_chip(kcontrol); + + if (!p_extn) + return 0; ucontrol->value.integer.value[0] = p_extn->arc_src; @@ -619,8 +652,10 @@ static int arc_set_src( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct extn *p_extn = dev_get_drvdata(component->dev); + struct extn *p_extn = snd_kcontrol_chip(kcontrol); + + if (!p_extn) + return 0; p_extn->arc_src = ucontrol->value.integer.value[0]; @@ -633,8 +668,10 @@ static int arc_get_enable( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct extn *p_extn = dev_get_drvdata(component->dev); + struct extn *p_extn = snd_kcontrol_chip(kcontrol); + + if (!p_extn) + return 0; ucontrol->value.integer.value[0] = p_extn->arc_en; @@ -645,8 +682,10 @@ static int arc_set_enable( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct extn *p_extn = dev_get_drvdata(component->dev); + struct extn *p_extn = snd_kcontrol_chip(kcontrol); + + if (!p_extn) + return 0; p_extn->arc_en = ucontrol->value.integer.value[0]; @@ -662,6 +701,9 @@ static int frhdmirx_get_mode( struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct extn *p_extn = dev_get_drvdata(component->dev); + if (!p_extn) + return 0; + ucontrol->value.integer.value[0] = p_extn->hdmirx_mode; return 0; @@ -674,6 +716,9 @@ static int frhdmirx_set_mode( struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct extn *p_extn = dev_get_drvdata(component->dev); + if (!p_extn) + return 0; + p_extn->hdmirx_mode = ucontrol->value.integer.value[0]; return 0; @@ -745,6 +790,9 @@ static int hdmirx_audio_type_get_enum( struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct extn *p_extn = dev_get_drvdata(component->dev); + if (!p_extn) + return 0; + ucontrol->value.enumerated.item[0] = hdmiin_check_audio_type(p_extn); @@ -752,8 +800,7 @@ static int hdmirx_audio_type_get_enum( } #endif -static const struct snd_kcontrol_new extn_controls[] = { - /* Out */ +static const struct snd_kcontrol_new extn_arc_controls[DYNC_KCNTL_CNT] = { SOC_ENUM_EXT("HDMI ARC Source", arc_src_enum, arc_get_src, @@ -763,6 +810,34 @@ static const struct snd_kcontrol_new extn_controls[] = { 0, arc_get_enable, arc_set_enable), +}; + +static int extn_create_controls(struct snd_card *card, + struct extn *p_extn) +{ + int i, err = 0; + + memset(p_extn->controls, 0, sizeof(p_extn->controls)); + + for (i = 0; i < DYNC_KCNTL_CNT; i++) { + p_extn->controls[i] = + snd_ctl_new1(&extn_arc_controls[i], p_extn); + err = snd_ctl_add(card, p_extn->controls[i]); + if (err < 0) + goto __error; + } + + return 0; + +__error: + for (i = 0; i < DYNC_KCNTL_CNT; i++) + if (p_extn->controls[i]) + snd_ctl_remove(card, p_extn->controls[i]); + + return err; +} + +static const struct snd_kcontrol_new extn_controls[] = { /* In */ SOC_SINGLE_BOOL_EXT("SPDIFIN PAO", @@ -831,12 +906,12 @@ static const struct snd_soc_component_driver extn_component = { struct extn_chipinfo tl1_extn_chipinfo = { .no_nonpcm2pcm_clr = true, + .cec_arc = true, }; static const struct of_device_id extn_device_id[] = { { .compatible = "amlogic, snd-extn", - .data = &tl1_extn_chipinfo, }, { .compatible = "amlogic, tl1-snd-extn", @@ -898,10 +973,10 @@ static int extn_platform_probe(struct platform_device *pdev) /* Default: PAO mode */ p_extn->hdmirx_mode = 1; - ret = snd_soc_register_component(&pdev->dev, - &extn_component, - extn_dai, - ARRAY_SIZE(extn_dai)); + ret = devm_snd_soc_register_component(&pdev->dev, + &extn_component, + extn_dai, + ARRAY_SIZE(extn_dai)); if (ret) { dev_err(&pdev->dev, "snd_soc_register_component failed\n"); diff --git a/sound/soc/amlogic/auge/iomap.c b/sound/soc/amlogic/auge/iomap.c index e196591b3328..58bef78704ad 100644 --- a/sound/soc/amlogic/auge/iomap.c +++ b/sound/soc/amlogic/auge/iomap.c @@ -231,87 +231,6 @@ void vad_update_bits(unsigned int reg, } EXPORT_SYMBOL(vad_update_bits); -int earcrx_cmdc_read(unsigned int reg) -{ - int ret, val = 0; - - ret = aml_snd_read(IO_EARCRX_CMDC, reg, &val); - - if (ret) { - pr_err("read audio reg %x error %d\n", reg, ret); - return -1; - } - return val; -} -EXPORT_SYMBOL(earcrx_cmdc_read); - -void earcrx_cmdc_write(unsigned int reg, unsigned int val) -{ - aml_snd_write(IO_EARCRX_CMDC, reg, val); -} -EXPORT_SYMBOL(earcrx_cmdc_write); - -void earcrx_cmdc_update_bits(unsigned int reg, - unsigned int mask, unsigned int val) -{ - aml_snd_update_bits(IO_EARCRX_CMDC, reg, mask, val); -} -EXPORT_SYMBOL(earcrx_cmdc_update_bits); - -int earcrx_dmac_read(unsigned int reg) -{ - int ret, val = 0; - - ret = aml_snd_read(IO_EARCRX_DMAC, reg, &val); - - if (ret) { - pr_err("read audio reg %x error %d\n", reg, ret); - return -1; - } - return val; -} -EXPORT_SYMBOL(earcrx_dmac_read); - -void earcrx_dmac_write(unsigned int reg, unsigned int val) -{ - aml_snd_write(IO_EARCRX_DMAC, reg, val); -} -EXPORT_SYMBOL(earcrx_dmac_write); - -void earcrx_dmac_update_bits(unsigned int reg, - unsigned int mask, unsigned int val) -{ - aml_snd_update_bits(IO_EARCRX_DMAC, reg, mask, val); -} -EXPORT_SYMBOL(earcrx_dmac_update_bits); - -int earcrx_top_read(unsigned int reg) -{ - int ret, val = 0; - - ret = aml_snd_read(IO_EARCRX_TOP, reg, &val); - - if (ret) { - pr_err("read audio reg %x error %d\n", reg, ret); - return -1; - } - return val; -} -EXPORT_SYMBOL(earcrx_top_read); - -void earcrx_top_write(unsigned int reg, unsigned int val) -{ - aml_snd_write(IO_EARCRX_TOP, reg, val); -} -EXPORT_SYMBOL(earcrx_top_write); - -void earcrx_top_update_bits(unsigned int reg, - unsigned int mask, unsigned int val) -{ - aml_snd_update_bits(IO_EARCRX_TOP, reg, mask, val); -} -EXPORT_SYMBOL(earcrx_top_update_bits); - static int snd_iomap_probe(struct platform_device *pdev) { struct resource res; diff --git a/sound/soc/amlogic/auge/iomap.h b/sound/soc/amlogic/auge/iomap.h index 07b8b54c2d89..ae3c9287cc48 100644 --- a/sound/soc/amlogic/auge/iomap.h +++ b/sound/soc/amlogic/auge/iomap.h @@ -25,9 +25,6 @@ enum{ IO_EQDRC_BUS, IO_RESET, IO_VAD, - IO_EARCRX_CMDC, - IO_EARCRX_DMAC, - IO_EARCRX_TOP, IO_MAX, }; @@ -61,17 +58,4 @@ extern int vad_read(unsigned int reg); extern void vad_write(unsigned int reg, unsigned int val); extern void vad_update_bits(unsigned int reg, unsigned int mask, unsigned int val); - -extern int earcrx_cmdc_read(unsigned int reg); -extern void earcrx_cmdc_write(unsigned int reg, unsigned int val); -extern void earcrx_cmdc_update_bits(unsigned int reg, - unsigned int mask, unsigned int val); -extern int earcrx_dmac_read(unsigned int reg); -extern void earcrx_dmac_write(unsigned int reg, unsigned int val); -extern void earcrx_dmac_update_bits(unsigned int reg, - unsigned int mask, unsigned int val); -extern int earcrx_top_read(unsigned int reg); -extern void earcrx_top_write(unsigned int reg, unsigned int val); -extern void earcrx_top_update_bits(unsigned int reg, - unsigned int mask, unsigned int val); #endif diff --git a/sound/soc/amlogic/auge/loopback.c b/sound/soc/amlogic/auge/loopback.c index 95178bd1e949..5c2e6a198340 100644 --- a/sound/soc/amlogic/auge/loopback.c +++ b/sound/soc/amlogic/auge/loopback.c @@ -75,7 +75,8 @@ struct loopback { unsigned int datalb_chnum; unsigned int datalb_chmask; unsigned int datalb_lane_mask; /* related with data lane */ - + unsigned int lb_format; + unsigned int lb_lane_chmask; unsigned int sysclk_freq; struct toddr *tddr; @@ -430,8 +431,8 @@ static int loopback_dai_startup( /* datalb */ switch (p_loopback->datalb_src) { - case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC: - tdminlb_startup(p_loopback); + case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D: + /*tdminlb_startup(p_loopback);*/ break; case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB: break; @@ -472,8 +473,8 @@ static void loopback_dai_shutdown( /* datalb */ switch (p_loopback->datalb_src) { - case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC: - tdminlb_shutdown(p_loopback); + case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D: + /*tdminlb_shutdown(p_loopback);*/ break; case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB: break; @@ -554,12 +555,7 @@ static int loopback_set_ctrl(struct loopback *p_loopback, int bitwidth) } switch (p_loopback->datalb_src) { - case TDMINLB_TDMOUTA: - case TDMINLB_TDMOUTB: - case TDMINLB_TDMOUTC: - case TDMINLB_PAD_TDMINA: - case TDMINLB_PAD_TDMINB: - case TDMINLB_PAD_TDMINC: + case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D: if (bitwidth == 24) { datalb_toddr_type = 4; datalb_msb = 32 - 1; @@ -603,9 +599,10 @@ static int loopback_set_ctrl(struct loopback *p_loopback, int bitwidth) lb_set_datain_cfg(p_loopback->id, &datain_cfg); lb_set_datalb_cfg(p_loopback->id, &datalb_cfg); - tdminlb_set_format(1); /* tdmin_lb i2s mode */ + tdminlb_set_format(p_loopback->lb_format == SND_SOC_DAIFMT_I2S); tdminlb_set_lanemask_and_chswap(0x76543210, - p_loopback->datalb_lane_mask); + p_loopback->datalb_lane_mask, + p_loopback->lb_lane_chmask); tdminlb_set_ctrl(p_loopback->datalb_src); return 0; @@ -714,6 +711,10 @@ static int loopback_dai_prepare( case TDMINLB_PAD_TDMINB: case TDMINLB_PAD_TDMINC: break; + case TDMINLB_PAD_TDMINA_D: + case TDMINLB_PAD_TDMINB_D: + case TDMINLB_PAD_TDMINC_D: + break; case SPDIFINLB_SPDIFOUTA: case SPDIFINLB_SPDIFOUTB: break; @@ -861,8 +862,8 @@ static int loopback_dai_hw_params( /* datalb */ switch (p_loopback->datalb_src) { - case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC: - datalb_tdminlb_set_clk(p_loopback); + case TDMINLB_TDMOUTA ... TDMINLB_PAD_TDMINC_D: + /*datalb_tdminlb_set_clk(p_loopback);*/ break; case SPDIFINLB_SPDIFOUTA ... SPDIFINLB_SPDIFOUTB: break; @@ -1008,6 +1009,9 @@ static const char *const datalb_tdminlb_texts[] = { "TDMIN_A", "TDMIN_B", "TDMIN_C", + "TDMIN_A_D", + "TDMIN_B_D", + "TDMIN_C_D", }; static const struct soc_enum datalb_tdminlb_enum = @@ -1247,6 +1251,39 @@ err: return -EINVAL; } +static unsigned int loopback_parse_format(struct device_node *node) +{ + unsigned int format = 0; + int ret = 0; + const char *str; + struct { + char *name; + unsigned int val; + } fmt_table[] = { + {"i2s", SND_SOC_DAIFMT_I2S}, + {"dsp_a", SND_SOC_DAIFMT_DSP_A}, + {"dsp_b", SND_SOC_DAIFMT_DSP_B} + }; + + ret = of_property_read_string(node, "datalb-format", &str); + if (ret == 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(fmt_table); i++) { + if (strcmp(str, fmt_table[i].name) == 0) { + format |= fmt_table[i].val; + break; + } + } + } + + /* default format is I2S */ + if (format == 0) + format = SND_SOC_DAIFMT_I2S; + + return format; +} + static int loopback_parse_of( struct device_node *node, struct loopback *p_loopback) @@ -1320,6 +1357,15 @@ static int loopback_parse_of( goto fail; } + p_loopback->lb_format = loopback_parse_format(node); + snd_soc_of_get_slot_mask + (node, + "datalb-channels-mask", + &p_loopback->lb_lane_chmask); + if (p_loopback->lb_lane_chmask == 0) { + /* default format is I2S and mask two channels */ + p_loopback->lb_lane_chmask = 0x3; + } pr_info("\tdatain_src:%d, datain_chnum:%d, datain_chumask:%x\n", p_loopback->datain_src, p_loopback->datain_chnum, @@ -1331,6 +1377,8 @@ static int loopback_parse_of( pr_info("\tdatain_lane_mask:0x%x, datalb_lane_mask:0x%x\n", p_loopback->datain_lane_mask, p_loopback->datalb_lane_mask); + pr_info("datalb_format: %d, chmask for lanes: %#x\n", + p_loopback->lb_format, p_loopback->lb_lane_chmask); ret = datain_parse_of(node, p_loopback); if (ret) { diff --git a/sound/soc/amlogic/auge/loopback.h b/sound/soc/amlogic/auge/loopback.h index bc0c075974a1..3fcd434bb227 100644 --- a/sound/soc/amlogic/auge/loopback.h +++ b/sound/soc/amlogic/auge/loopback.h @@ -55,6 +55,10 @@ enum datalb_src { TDMINLB_PAD_TDMINB, TDMINLB_PAD_TDMINC, + TDMINLB_PAD_TDMINA_D, + TDMINLB_PAD_TDMINB_D, + TDMINLB_PAD_TDMINC_D, + SPDIFINLB_SPDIFOUTA, SPDIFINLB_SPDIFOUTB, }; diff --git a/sound/soc/amlogic/auge/loopback_hw.c b/sound/soc/amlogic/auge/loopback_hw.c index 0508769c9475..6b64e9863cce 100644 --- a/sound/soc/amlogic/auge/loopback_hw.c +++ b/sound/soc/amlogic/auge/loopback_hw.c @@ -22,35 +22,40 @@ #include "regs.h" #include "iomap.h" +static unsigned int +get_tdmin_id_from_lb_src(enum datalb_src lb_src) +{ + return lb_src % TDMINLB_PAD_TDMINA; +} -void tdminlb_set_clk(int datatlb_src, int sclk_div, int ratio, bool enable) +void tdminlb_set_clk(enum datalb_src lb_src, + int sclk_div, int ratio, bool enable) { unsigned int bclk_sel, fsclk_sel; unsigned int tdmin_src; /* config for external codec */ - if (datatlb_src >= 3) { - unsigned int clk_id = datatlb_src - 3; + if (lb_src >= TDMINLB_PAD_TDMINA) { + unsigned int id = get_tdmin_id_from_lb_src(lb_src); unsigned int offset, reg; unsigned int fsclk_hi; fsclk_hi = ratio / 2; - bclk_sel = clk_id; - fsclk_sel = clk_id; + bclk_sel = id; + fsclk_sel = id; /*sclk, lrclk*/ offset = EE_AUDIO_MST_B_SCLK_CTRL0 - EE_AUDIO_MST_A_SCLK_CTRL0; - reg = EE_AUDIO_MST_A_SCLK_CTRL0 + offset * clk_id; + reg = EE_AUDIO_MST_A_SCLK_CTRL0 + offset * id; audiobus_update_bits(reg, 0x3 << 30 | 0x3ff << 20 | 0x3ff<<10 | 0x3ff, (enable ? 0x3 : 0x0) << 30 | sclk_div << 20 | fsclk_hi << 10 | ratio); - - tdmin_src = datatlb_src - 3; + tdmin_src = id; } else - tdmin_src = datatlb_src; + tdmin_src = lb_src; audiobus_update_bits( EE_AUDIO_CLK_TDMIN_LB_CTRL, @@ -72,11 +77,11 @@ void tdminlb_set_format(int i2s_fmt) { audiobus_update_bits(EE_AUDIO_TDMIN_LB_CTRL, 0x1 << 30, - i2s_fmt << 30 /* 0:tdm mode; 1: i2s mode; */ + !!i2s_fmt << 30 /* 0:tdm mode; 1: i2s mode; */ ); } -void tdminlb_set_ctrl(int src) +void tdminlb_set_ctrl(enum datalb_src src) { audiobus_update_bits( EE_AUDIO_TDMIN_LB_CTRL, @@ -101,14 +106,13 @@ static void tdminlb_set_lane_mask(int lane, int mask) audiobus_write(EE_AUDIO_TDMIN_LB_MASK0 + lane, mask); } -void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask) +void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask, unsigned int mask) { - unsigned int mask; unsigned int i; - pr_debug("tdmin_lb lane swap:0x%x mask:0x%x\n", swap, lane_mask); - - mask = 0x3; /* i2s format */ + pr_debug + ("%s() lane swap:0x%x lane mask:0x%x, chmask:%#x\n", + __func__, swap, lane_mask, mask); /* channel swap */ audiobus_write(EE_AUDIO_TDMIN_LB_SWAP0, swap); diff --git a/sound/soc/amlogic/auge/loopback_hw.h b/sound/soc/amlogic/auge/loopback_hw.h index 776299a0ea9a..b88c0607bf4f 100644 --- a/sound/soc/amlogic/auge/loopback_hw.h +++ b/sound/soc/amlogic/auge/loopback_hw.h @@ -19,6 +19,7 @@ #define __AML_LOOPBACK_HW_H__ #include +#include "loopback.h" struct data_cfg { /* @@ -46,20 +47,20 @@ struct data_cfg { bool ch_ctrl_switch; }; -extern void tdminlb_set_clk(int datatlb_src, - int sclk_div, int ratio, bool enable); +void tdminlb_set_clk(enum datalb_src lb_src, + int sclk_div, int ratio, bool enable); extern void tdminlb_set_format(int i2s_fmt); -extern void tdminlb_set_ctrl(int src); +void tdminlb_set_ctrl(enum datalb_src src); extern void tdminlb_enable(int tdm_index, int in_enable); extern void tdminlb_fifo_enable(int is_enable); extern void tdminlb_set_format(int i2s_fmt); -extern void tdminlb_set_lanemask_and_chswap(int swap, int lane_mask); - +void tdminlb_set_lanemask_and_chswap + (int swap, int lane_mask, unsigned int mask); extern void tdminlb_set_src(int src); extern void lb_set_datain_src(int id, int src); diff --git a/sound/soc/amlogic/auge/regs.h b/sound/soc/amlogic/auge/regs.h index c6ae28f443ca..1ac6346707f9 100644 --- a/sound/soc/amlogic/auge/regs.h +++ b/sound/soc/amlogic/auge/regs.h @@ -880,6 +880,9 @@ enum clk_sel { #define AED_TOP_CTL 0x83 #define AED_TOP_REQ_CTL 0x84 +#define AED_TOP_CTL0 0x83 +#define AED_TOP_CTL1 0x84 +#define AED_TOP_CTL2 0x85 /* * VAD, Voice activity detection diff --git a/sound/soc/amlogic/auge/sm1,clocks.c b/sound/soc/amlogic/auge/sm1,clocks.c index 34fbd393ea14..e287ae5d0563 100644 --- a/sound/soc/amlogic/auge/sm1,clocks.c +++ b/sound/soc/amlogic/auge/sm1,clocks.c @@ -34,6 +34,11 @@ static const char *const audioclk_parent_names[] = { "i_slv_sclk_d", "i_slv_sclk_e", "i_slv_sclk_f", "i_slv_sclk_g", "i_slv_sclk_h", "i_slv_sclk_i", "i_slv_sclk_j"}; +static const char *const mclk_pad_parent_names[] = { + "mclk_a", "mclk_b", "mclk_c", + "mclk_d", "mclk_e", "mclk_f" +}; + CLOCK_GATE(audio_ddr_arb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 0); CLOCK_GATE(audio_pdm, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 1); CLOCK_GATE(audio_tdmina, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 2); @@ -194,6 +199,15 @@ CLOCK_COM_GATE(mclk_d, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_D_CTRL(1)), 31); CLOCK_COM_MUX(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 0x7, 24); CLOCK_COM_DIV(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 0, 16); CLOCK_COM_GATE(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 31); +/* mclk_pad0 */ +CLOCK_COM_MUX(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 8); +CLOCK_COM_DIV(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0, 8); +CLOCK_COM_GATE(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 15); +/* mclk_pad1 */ +CLOCK_COM_MUX(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 24); +CLOCK_COM_DIV(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 16, 8); +CLOCK_COM_GATE(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 31); + /* mclk_f */ CLOCK_COM_MUX(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0x7, 24); CLOCK_COM_DIV(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0, 16); @@ -369,6 +383,16 @@ static int sm1_clks_init(struct clk **clks, void __iomem *iobase) clks[CLKID_EARCRX_DMAC] = REGISTER_CLK_COM(earcrx_dmac); WARN_ON(IS_ERR_OR_NULL(clks[CLKID_EARCRX_DMAC])); + IOMAP_COM_CLK(mclk_pad0, iobase); + clks[CLKID_AUDIO_MCLK_PAD0] = + REGISTER_CLK_COM_PARENTS(mclk_pad0, mclk_pad); + WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD0])); + + IOMAP_COM_CLK(mclk_pad1, iobase); + clks[CLKID_AUDIO_MCLK_PAD1] = + REGISTER_CLK_COM_PARENTS(mclk_pad1, mclk_pad); + WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD1])); + return 0; } diff --git a/sound/soc/amlogic/auge/spdif.c b/sound/soc/amlogic/auge/spdif.c index e4e42fbd9647..2e45e6f32504 100644 --- a/sound/soc/amlogic/auge/spdif.c +++ b/sound/soc/amlogic/auge/spdif.c @@ -273,6 +273,62 @@ static int aml_audio_set_spdif_mute(struct snd_kcontrol *kcontrol, return 0; } +static int aml_spdif_platform_suspend( + struct platform_device *pdev, pm_message_t state) +{ + struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev); + struct pinctrl_state *pstate = NULL; + int stream = SNDRV_PCM_STREAM_PLAYBACK; + + if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) { + pstate = pinctrl_lookup_state + (p_spdif->pin_ctl, "spdif_pins_mute"); + if (!IS_ERR_OR_NULL(pstate)) + pinctrl_select_state(p_spdif->pin_ctl, pstate); + } + aml_spdif_enable(p_spdif->actrl, + stream, p_spdif->id, false); + pr_info("%s is mute\n", __func__); + return 0; +} + +static int aml_spdif_platform_resume(struct platform_device *pdev) +{ + struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev); + struct pinctrl_state *state = NULL; + int stream = SNDRV_PCM_STREAM_PLAYBACK; + + if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) { + state = pinctrl_lookup_state + (p_spdif->pin_ctl, "spdif_pins"); + if (!IS_ERR_OR_NULL(state)) + pinctrl_select_state(p_spdif->pin_ctl, state); + } + aml_spdif_enable(p_spdif->actrl, + stream, p_spdif->id, true); + pr_info("%s is unmute\n", __func__); + + return 0; +} + +static void aml_spdif_platform_shutdown(struct platform_device *pdev) +{ + struct aml_spdif *p_spdif = dev_get_drvdata(&pdev->dev); + struct pinctrl_state *pstate = NULL; + int stream = SNDRV_PCM_STREAM_PLAYBACK; + + if (!IS_ERR_OR_NULL(p_spdif->pin_ctl)) { + pstate = pinctrl_lookup_state + (p_spdif->pin_ctl, "spdif_pins_mute"); + if (!IS_ERR_OR_NULL(pstate)) + pinctrl_select_state(p_spdif->pin_ctl, pstate); + } + aml_spdif_enable(p_spdif->actrl, + stream, p_spdif->id, false); + pr_info("%s is mute\n", __func__); + +} + static int aml_audio_get_spdif_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -658,7 +714,7 @@ static void spdifin_status_event(struct aml_spdif *p_spdif) if (p_spdif->chipinfo && p_spdif->chipinfo->pcpd_separated) { if (intrpt_status & 0x8) { - pr_info("Pc changed, try to read spdifin audio type\n"); + pr_debug("Pc changed, try to read spdifin audio type\n"); extcon_set_state(p_spdif->edev, EXTCON_SPDIFIN_AUDIOTYPE, 1); @@ -672,7 +728,7 @@ static void spdifin_status_event(struct aml_spdif *p_spdif) #endif } if (intrpt_status & 0x10) - pr_info("Pd changed\n"); + pr_debug("Pd changed\n"); } else { if (intrpt_status & 0x8) pr_debug("CH status changed\n"); @@ -1586,6 +1642,9 @@ struct platform_driver aml_spdif_driver = { .of_match_table = aml_spdif_device_id, }, .probe = aml_spdif_platform_probe, + .suspend = aml_spdif_platform_suspend, + .resume = aml_spdif_platform_resume, + .shutdown = aml_spdif_platform_shutdown, }; module_platform_driver(aml_spdif_driver); diff --git a/sound/soc/amlogic/auge/tdm.c b/sound/soc/amlogic/auge/tdm.c index 4ebe2de7a735..98b8a368ed46 100644 --- a/sound/soc/amlogic/auge/tdm.c +++ b/sound/soc/amlogic/auge/tdm.c @@ -83,6 +83,8 @@ struct aml_tdm { struct clk *clk; struct clk *clk_gate; struct clk *mclk; + /* mclk mux out to pad */ + struct clk *mclk2pad; struct clk *samesrc_srcpll; struct clk *samesrc_clk; bool contns_clk; @@ -723,12 +725,11 @@ static int aml_tdm_set_lanes(struct aml_tdm *p_tdm, #if 1 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { /* set lanes mask acordingly */ - if (p_tdm->chipinfo - && p_tdm->chipinfo->oe_fn - && p_tdm->setting.lane_oe_mask_out) + lane_mask = setting->lane_mask_out; + /* compatible using oe masks */ + if (!lane_mask && setting->lane_oe_mask_out) lane_mask = setting->lane_oe_mask_out; - else - lane_mask = setting->lane_mask_out; + for (i = 0; i < p_tdm->lane_cnt; i++) { if (((1 << i) & lane_mask) && lanes) { aml_tdm_set_channel_mask(p_tdm->actrl, @@ -844,12 +845,17 @@ static int aml_tdm_set_clk_pad(struct aml_tdm *p_tdm) if (p_tdm->chipinfo && (!p_tdm->chipinfo->mclkpad_no_offset)) mpad_offset = 1; - aml_tdm_clk_pad_select(p_tdm->actrl, - p_tdm->mclk_pad, - mpad_offset, - p_tdm->id, - p_tdm->id, - p_tdm->clk_sel); + if (p_tdm->mclk_pad >= 0) { + aml_tdm_mclk_pad_select(p_tdm->actrl, + p_tdm->mclk_pad, + mpad_offset, + p_tdm->clk_sel); + } + + aml_tdm_sclk_pad_select(p_tdm->actrl, + mpad_offset, + p_tdm->id, + p_tdm->id); return 0; } @@ -1131,21 +1137,17 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int lanes_oe_out_cnt = 0, lanes_oe_in_cnt = 0; unsigned int force_oe = 0, oe_val = 0; unsigned int lanes_lb_cnt = 0; - int out_lanes, in_lanes; + int out_lanes = 0, in_lanes = 0; int in_src = -1; lanes_out_cnt = pop_count(p_tdm->setting.lane_mask_out); lanes_in_cnt = pop_count(p_tdm->setting.lane_mask_in); - lanes_oe_out_cnt = pop_count(p_tdm->setting.lane_oe_mask_out); - lanes_oe_in_cnt = pop_count(p_tdm->setting.lane_oe_mask_in); lanes_lb_cnt = pop_count(p_tdm->setting.lane_lb_mask_in); pr_debug("%s(), txmask(%#x), rxmask(%#x)\n", __func__, tx_mask, rx_mask); pr_debug("\tlanes_out_cnt(%d), lanes_in_cnt(%d)\n", lanes_out_cnt, lanes_in_cnt); - pr_debug("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n", - lanes_oe_out_cnt, lanes_oe_in_cnt); pr_debug("\tlanes_lb_cnt(%d)\n", lanes_lb_cnt); pr_debug("\tslots(%d), slot_width(%d)\n", @@ -1179,9 +1181,30 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, p_tdm->setting.lane_lb_mask_in & p_tdm->setting.lane_oe_mask_in); + lanes_oe_out_cnt = pop_count(p_tdm->setting.lane_oe_mask_out); + lanes_oe_in_cnt = pop_count(p_tdm->setting.lane_oe_mask_in); + pr_debug + ("\tlanes_oe_out_cnt(%d), lanes_oe_in_cnt(%d)\n", + lanes_oe_out_cnt, lanes_oe_in_cnt); + if (lanes_oe_out_cnt) { - force_oe = p_tdm->setting.lane_oe_mask_out; + unsigned int oe_fn_version = p_tdm->chipinfo->oe_fn; + + force_oe = (1 << p_tdm->chipinfo->lane_cnt) - 1; oe_val = p_tdm->setting.lane_oe_mask_out; + if (oe_fn_version == OE_FUNCTION_V1) { + aml_tdm_set_oe_v1 + (p_tdm->actrl, p_tdm->id, + force_oe, oe_val); + } else if (oe_fn_version == OE_FUNCTION_V2) { + aml_tdm_set_oe_v2 + (p_tdm->actrl, p_tdm->id, + force_oe, oe_val); + } else { + pr_err + ("%s(), oe version(%d) not support\n", + __func__, oe_fn_version); + } } if (lanes_lb_cnt) @@ -1201,7 +1224,7 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, } } - out_lanes = lanes_out_cnt + lanes_oe_out_cnt; + out_lanes = lanes_out_cnt; in_lanes = lanes_in_cnt + lanes_oe_in_cnt + lanes_lb_cnt; if (p_tdm->chipinfo @@ -1216,8 +1239,7 @@ static int aml_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, if (out_lanes > 0 && out_lanes <= LANE_MAX3) aml_tdm_set_slot_out(p_tdm->actrl, - p_tdm->id, slots, slot_width, - force_oe, oe_val); + p_tdm->id, slots, slot_width); /* constrains hw channels_max by DTS configs */ drv->playback.channels_max = slots * out_lanes; @@ -1564,7 +1586,7 @@ static int aml_tdm_platform_probe(struct platform_device *pdev) ret = of_parse_tdm_lane_slot_out(node, &p_tdm->setting.lane_mask_out); if (ret < 0) - p_tdm->setting.lane_mask_out = 0x0; + p_tdm->setting.lane_mask_out = 0x1; /* get tdm lanes oe info. if not, set to default 0 */ ret = of_parse_tdm_lane_oe_slot_in(node, @@ -1606,6 +1628,28 @@ static int aml_tdm_platform_probe(struct platform_device *pdev) return ret; } + /* clk tree style after SM1, instead of legacy prop */ + p_tdm->mclk2pad = devm_clk_get(&pdev->dev, "mclk_pad"); + if (!IS_ERR(p_tdm->mclk2pad)) { + ret = clk_set_parent(p_tdm->mclk2pad, p_tdm->mclk); + if (ret) { + dev_err(&pdev->dev, "Can't set tdm mclk_pad parent\n"); + return -EINVAL; + } + clk_prepare_enable(p_tdm->mclk2pad); + p_tdm->mclk_pad = -1; + } else { + /* mclk pad ctrl */ + ret = of_property_read_u32(node, "mclk_pad", + &p_tdm->mclk_pad); + if (ret < 0) { + /* No mclk in defalut if chip needs mclk pad mux. */ + p_tdm->mclk_pad = -1; + dev_warn_once(&pdev->dev, + "neither mclk_pad nor mclk2pad set\n"); + } + } + /* complete mclk for tdm */ if (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR) == 0xa) meson_clk_measure((1<<16) | 0x67); @@ -1625,12 +1669,6 @@ static int aml_tdm_platform_probe(struct platform_device *pdev) /*set default clk for output*/ aml_set_default_tdm_clk(p_tdm); - /* mclk pad ctrl */ - ret = of_property_read_u32(node, "mclk_pad", - &p_tdm->mclk_pad); - if (ret < 0) - p_tdm->mclk_pad = -1; /* not use mclk in defalut. */ - p_tdm->dev = dev; /* For debug to disable share buffer */ p_tdm->en_share = 1; diff --git a/sound/soc/amlogic/auge/tdm_hw.c b/sound/soc/amlogic/auge/tdm_hw.c index 97dc1d593438..78266627e12f 100644 --- a/sound/soc/amlogic/auge/tdm_hw.c +++ b/sound/soc/amlogic/auge/tdm_hw.c @@ -454,34 +454,64 @@ void aml_update_tdmin_rev_ws(struct aml_audio_controller *actrl, off_set = EE_AUDIO_TDMIN_B_CTRL - EE_AUDIO_TDMIN_A_CTRL; reg_in = EE_AUDIO_TDMIN_A_CTRL + off_set * idx; - aml_audiobus_update_bits(actrl, reg_in, + aml_audiobus_update_bits + (actrl, reg_in, 0x1 << 25, is_rev << 25); } -void aml_tdm_set_slot_out( +void aml_tdm_set_oe_v1( struct aml_audio_controller *actrl, - int index, int slots, int slot_width, - int force_oe, int oe_val) + int index, int force_oe, int oe_val) { unsigned int reg, offset; offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0; reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index; - aml_audiobus_update_bits(actrl, reg, - 0x3ff, ((slots - 1) << 5) | (slot_width - 1)); if (force_oe) { + offset = EE_AUDIO_TDMOUT_B_CTRL2 - EE_AUDIO_TDMOUT_A_CTRL2; + reg = EE_AUDIO_TDMOUT_A_CTRL2 + offset * index; + aml_audiobus_update_bits(actrl, reg, 0xf << 24, force_oe << 24); + /* force oe val, in or out */ + reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index; + aml_audiobus_update_bits(actrl, reg, 0xf, oe_val); + } +} + +void aml_tdm_set_oe_v2( + struct aml_audio_controller *actrl, + int index, int force_oe, int oe_val) +{ + unsigned int reg, offset; + + if (force_oe) { + offset = EE_AUDIO_TDMOUT_B_CTRL2 - EE_AUDIO_TDMOUT_A_CTRL2; + reg = EE_AUDIO_TDMOUT_A_CTRL2 + offset * index; + + aml_audiobus_update_bits(actrl, reg, 0xff << 8, force_oe << 8); + /* force oe val, in or out */ if (oe_val) { - reg = EE_AUDIO_TDMOUT_A_CTRL1 + offset * index; - aml_audiobus_update_bits(actrl, reg, - 0xf << 0, oe_val << 0); + aml_audiobus_update_bits + (actrl, reg, 0xff << 16, oe_val << 16); } } } +void aml_tdm_set_slot_out( + struct aml_audio_controller *actrl, + int index, int slots, int slot_width) +{ + unsigned int reg, offset; + + offset = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0; + reg = EE_AUDIO_TDMOUT_A_CTRL0 + offset * index; + aml_audiobus_update_bits + (actrl, reg, 0x3ff, ((slots - 1) << 5) | (slot_width - 1)); +} + void aml_tdm_set_slot_in( struct aml_audio_controller *actrl, int index, int in_src, int slot_width) @@ -675,39 +705,40 @@ void aml_tdmout_enable_gain(int tdmout_id, int en) audiobus_update_bits(reg, 0x1 << 26, !!en << 26); } -void aml_tdm_clk_pad_select( - struct aml_audio_controller *actrl, - int mpad, int mpad_offset, int mclk_sel, - int tdm_index, int clk_sel) +void aml_tdm_mclk_pad_select(struct aml_audio_controller *actrl, + int mpad, int mpad_offset, int mclk_sel) { unsigned int reg, mask_offset, val_offset; - if (mpad >= 0) { - switch (mpad) { - case 0: - mask_offset = 0x7 << 0; - val_offset = mclk_sel << 0; - break; - case 1: - mask_offset = 0x7 << 4; - val_offset = mclk_sel << 4; - break; - default: - mask_offset = 0; - val_offset = 0; - pr_info("unknown tdm mpad:%d\n", mpad); - break; - } + switch (mpad) { + case 0: + mask_offset = 0x7 << 0; + val_offset = mclk_sel << 0; + break; + case 1: + mask_offset = 0x7 << 4; + val_offset = mclk_sel << 4; + break; + default: + mask_offset = 0x7 << 4; + val_offset = 0; + pr_info("unknown tdm mpad:%d\n", mpad); + break; + } - reg = EE_AUDIO_MST_PAD_CTRL0(mpad_offset); - if (actrl) - aml_audiobus_update_bits(actrl, reg, - mask_offset, val_offset); - else - audiobus_update_bits(reg, - mask_offset, val_offset); - } else - pr_warn("mclk is not configured\n"); + reg = EE_AUDIO_MST_PAD_CTRL0(mpad_offset); + if (actrl) + aml_audiobus_update_bits(actrl, reg, + mask_offset, val_offset); + else + audiobus_update_bits(reg, + mask_offset, val_offset); +} + +void aml_tdm_sclk_pad_select(struct aml_audio_controller *actrl, + int mpad_offset, int tdm_index, int clk_sel) +{ + unsigned int reg, mask_offset, val_offset; reg = EE_AUDIO_MST_PAD_CTRL1(mpad_offset); switch (tdm_index) { diff --git a/sound/soc/amlogic/auge/tdm_hw.h b/sound/soc/amlogic/auge/tdm_hw.h index bb9b9bd51079..c17617bff8fb 100644 --- a/sound/soc/amlogic/auge/tdm_hw.h +++ b/sound/soc/amlogic/auge/tdm_hw.h @@ -107,8 +107,7 @@ extern void aml_update_tdmin_rev_ws(struct aml_audio_controller *actrl, extern void aml_tdm_set_slot_out( struct aml_audio_controller *actrl, - int index, int slots, int slot_width, - int force_oe, int oe_val); + int index, int slots, int slot_width); extern void aml_tdm_set_slot_in( struct aml_audio_controller *actrl, @@ -151,6 +150,10 @@ extern void aml_tdm_clk_pad_select( struct aml_audio_controller *actrl, int mpad, int mpad_offset, int mclk_sel, int tdm_index, int clk_sel); +void aml_tdm_mclk_pad_select(struct aml_audio_controller *actrl, + int mpad, int mpad_offset, int mclk_sel); +void aml_tdm_sclk_pad_select(struct aml_audio_controller *actrl, + int mpad_offset, int tdm_index, int clk_sel); extern void i2s_to_hdmitx_ctrl(int tdm_index); extern void i2s_to_hdmitx_disable(void); @@ -164,4 +167,12 @@ void aml_tdm_mute_capture( int tdm_index, bool mute, int lane_cnt); +void aml_tdm_out_reset(unsigned int tdm_id, int offset); +void aml_tdm_set_oe_v1( + struct aml_audio_controller *actrl, + int index, int force_oe, int oe_val); +void aml_tdm_set_oe_v2( + struct aml_audio_controller *actrl, + int index, int force_oe, int oe_val); + #endif diff --git a/sound/soc/amlogic/auge/tdm_match_table.c b/sound/soc/amlogic/auge/tdm_match_table.c index 7b45f00420b4..a4e348ead1e0 100644 --- a/sound/soc/amlogic/auge/tdm_match_table.c +++ b/sound/soc/amlogic/auge/tdm_match_table.c @@ -15,6 +15,15 @@ * */ +/* For OE function V1: + * OE is set by EE_AUDIO_TDMOUT_A_CTRL0 & EE_AUDIO_TDMOUT_A_CTRL1 + */ +#define OE_FUNCTION_V1 1 +/* For OE function V2: + * OE is set by EE_AUDIO_TDMOUT_A_CTRL2 + */ +#define OE_FUNCTION_V2 2 + struct tdm_chipinfo { /* device id */ unsigned int id; @@ -26,7 +35,7 @@ struct tdm_chipinfo { bool sclk_ws_inv; /* output en (oe) for pinmux */ - bool oe_fn; + unsigned int oe_fn; /* clk pad */ bool no_mclkpad_ctrl; @@ -76,7 +85,7 @@ struct tdm_chipinfo g12a_tdma_chipinfo = { struct tdm_chipinfo g12a_tdmb_chipinfo = { .id = TDM_B, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .mclkpad_no_offset = true, }; @@ -84,7 +93,7 @@ struct tdm_chipinfo g12a_tdmb_chipinfo = { struct tdm_chipinfo g12a_tdmc_chipinfo = { .id = TDM_C, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .mclkpad_no_offset = true, }; @@ -92,7 +101,7 @@ struct tdm_chipinfo g12a_tdmc_chipinfo = { struct tdm_chipinfo g12a_tdminlb_chipinfo = { .id = TDM_LB, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .mclkpad_no_offset = true, }; @@ -100,7 +109,7 @@ struct tdm_chipinfo g12a_tdminlb_chipinfo = { struct tdm_chipinfo tl1_tdma_chipinfo = { .id = TDM_A, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .adc_fn = true, }; @@ -108,7 +117,7 @@ struct tdm_chipinfo tl1_tdma_chipinfo = { struct tdm_chipinfo tl1_tdmb_chipinfo = { .id = TDM_B, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .adc_fn = true, }; @@ -116,7 +125,7 @@ struct tdm_chipinfo tl1_tdmb_chipinfo = { struct tdm_chipinfo tl1_tdmc_chipinfo = { .id = TDM_C, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .adc_fn = true, }; @@ -124,7 +133,7 @@ struct tdm_chipinfo tl1_tdmc_chipinfo = { struct tdm_chipinfo tl1_tdminlb_chipinfo = { .id = TDM_LB, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V1, .same_src_fn = true, .adc_fn = true, }; @@ -132,7 +141,7 @@ struct tdm_chipinfo tl1_tdminlb_chipinfo = { struct tdm_chipinfo sm1_tdma_chipinfo = { .id = TDM_A, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .lane_cnt = LANE_MAX0, }; @@ -140,7 +149,7 @@ struct tdm_chipinfo sm1_tdma_chipinfo = { struct tdm_chipinfo sm1_tdmb_chipinfo = { .id = TDM_B, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .lane_cnt = LANE_MAX3, }; @@ -148,7 +157,7 @@ struct tdm_chipinfo sm1_tdmb_chipinfo = { struct tdm_chipinfo sm1_tdmc_chipinfo = { .id = TDM_C, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .lane_cnt = LANE_MAX1, }; @@ -156,7 +165,7 @@ struct tdm_chipinfo sm1_tdmc_chipinfo = { struct tdm_chipinfo sm1_tdminlb_chipinfo = { .id = TDM_LB, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .lane_cnt = LANE_MAX3, }; @@ -164,7 +173,7 @@ struct tdm_chipinfo sm1_tdminlb_chipinfo = { struct tdm_chipinfo tm2_tdma_chipinfo = { .id = TDM_A, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .adc_fn = true, .lane_cnt = LANE_MAX3, @@ -173,7 +182,7 @@ struct tdm_chipinfo tm2_tdma_chipinfo = { struct tdm_chipinfo tm2_tdmb_chipinfo = { .id = TDM_B, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .adc_fn = true, .lane_cnt = LANE_MAX1, @@ -182,7 +191,7 @@ struct tdm_chipinfo tm2_tdmb_chipinfo = { struct tdm_chipinfo tm2_tdmc_chipinfo = { .id = TDM_C, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .adc_fn = true, .lane_cnt = LANE_MAX1, @@ -191,7 +200,7 @@ struct tdm_chipinfo tm2_tdmc_chipinfo = { struct tdm_chipinfo tm2_tdminlb_chipinfo = { .id = TDM_LB, .sclk_ws_inv = true, - .oe_fn = true, + .oe_fn = OE_FUNCTION_V2, .same_src_fn = true, .lane_cnt = LANE_MAX3, }; diff --git a/sound/soc/amlogic/auge/tm2,clocks.c b/sound/soc/amlogic/auge/tm2,clocks.c index a491ceb72741..32f2289b4b84 100644 --- a/sound/soc/amlogic/auge/tm2,clocks.c +++ b/sound/soc/amlogic/auge/tm2,clocks.c @@ -34,6 +34,11 @@ static const char *const audioclk_parent_names[] = { "i_slv_sclk_d", "i_slv_sclk_e", "i_slv_sclk_f", "i_slv_sclk_g", "i_slv_sclk_h", "i_slv_sclk_i", "i_slv_sclk_j"}; +static const char *const mclk_pad_parent_names[] = { + "mclk_a", "mclk_b", "mclk_c", + "mclk_d", "mclk_e", "mclk_f" +}; + CLOCK_GATE(audio_ddr_arb, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 0); CLOCK_GATE(audio_pdm, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 1); CLOCK_GATE(audio_tdmina, AUD_ADDR_OFFSET(EE_AUDIO_CLK_GATE_EN0), 2); @@ -214,6 +219,15 @@ CLOCK_COM_GATE(mclk_e, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_E_CTRL(1)), 31); CLOCK_COM_MUX(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0x7, 24); CLOCK_COM_DIV(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 0, 16); CLOCK_COM_GATE(mclk_f, AUD_ADDR_OFFSET(EE_AUDIO_MCLK_F_CTRL(1)), 31); +/* mclk_pad0 */ +CLOCK_COM_MUX(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 8); +CLOCK_COM_DIV(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0, 8); +CLOCK_COM_GATE(mclk_pad0, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 15); +/* mclk_pad1 */ +CLOCK_COM_MUX(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 0x7, 24); +CLOCK_COM_DIV(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 16, 8); +CLOCK_COM_GATE(mclk_pad1, AUD_ADDR_OFFSET(EE_AUDIO_MST_PAD_CTRL0(1)), 31); + /* spdifin */ CLOCK_COM_MUX(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0x7, 24); CLOCK_COM_DIV(spdifin, AUD_ADDR_OFFSET(EE_AUDIO_CLK_SPDIFIN_CTRL), 0, 8); @@ -385,6 +399,16 @@ static int tm2_clks_init(struct clk **clks, void __iomem *iobase) clks[CLKID_EARCRX_DMAC] = REGISTER_CLK_COM(earcrx_dmac); WARN_ON(IS_ERR_OR_NULL(clks[CLKID_EARCRX_DMAC])); + IOMAP_COM_CLK(mclk_pad0, iobase); + clks[CLKID_AUDIO_MCLK_PAD0] = + REGISTER_CLK_COM_PARENTS(mclk_pad0, mclk_pad); + WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD0])); + + IOMAP_COM_CLK(mclk_pad1, iobase); + clks[CLKID_AUDIO_MCLK_PAD1] = + REGISTER_CLK_COM_PARENTS(mclk_pad1, mclk_pad); + WARN_ON(IS_ERR_OR_NULL(clks[CLKID_AUDIO_MCLK_PAD1])); + return 0; } diff --git a/sound/soc/amlogic/common/Makefile b/sound/soc/amlogic/common/Makefile index 41087df65185..cc8b7a9d6291 100644 --- a/sound/soc/amlogic/common/Makefile +++ b/sound/soc/amlogic/common/Makefile @@ -1,3 +1,4 @@ obj-y += notify.o \ spdif_info.o \ - misc.o + misc.o \ + iomapres.o diff --git a/sound/soc/amlogic/common/iomapres.c b/sound/soc/amlogic/common/iomapres.c new file mode 100644 index 000000000000..5d2d80e5ee9a --- /dev/null +++ b/sound/soc/amlogic/common/iomapres.c @@ -0,0 +1,75 @@ +/* + * sound/soc/amlogic/common/iomapres.c + * + * Copyright (C) 2019 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include + +#include + +struct regmap *regmap_resource(struct device *dev, char *name) +{ + struct resource res; + void __iomem *base; + struct device_node *node = dev->of_node; + static struct regmap_config aud_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + int i; + + i = of_property_match_string(node, "reg-names", name); + if (of_address_to_resource(node, i, &res)) + return ERR_PTR(-ENOENT); + + base = devm_ioremap_resource(dev, &res); + if (IS_ERR(base)) + return ERR_CAST(base); + + pr_info("%s, %s, start:%#x, size:%#x\n", + __func__, name, (u32)res.start, (u32)resource_size(&res)); + + aud_regmap_config.max_register = resource_size(&res) - 4; + aud_regmap_config.name = + devm_kasprintf(dev, GFP_KERNEL, "%s-%s", node->name, name); + if (!aud_regmap_config.name) + return ERR_PTR(-ENOMEM); + + return devm_regmap_init_mmio(dev, base, &aud_regmap_config); +} + +unsigned int mmio_read(struct regmap *map, unsigned int reg_ofs) +{ + unsigned int val; + + regmap_read(map, (reg_ofs << 2), &val); + + return val; +} + +int mmio_write(struct regmap *map, unsigned int reg_ofs, unsigned int value) +{ + return regmap_write(map, (reg_ofs << 2), value); +} + +int mmio_update_bits(struct regmap *map, + unsigned int reg_ofs, + unsigned int mask, + unsigned int value) +{ + return regmap_update_bits(map, (reg_ofs << 2), mask, value); +} diff --git a/sound/soc/amlogic/common/spdif_info.c b/sound/soc/amlogic/common/spdif_info.c index 5caeee5768c2..efc1f46c14cd 100644 --- a/sound/soc/amlogic/common/spdif_info.c +++ b/sound/soc/amlogic/common/spdif_info.c @@ -34,6 +34,11 @@ unsigned int IEC958_mode_codec; EXPORT_SYMBOL(IEC958_mode_codec); +bool spdifout_is_raw(void) +{ + return (IEC958_mode_codec && IEC958_mode_codec != 9); +} + bool spdif_is_4x_clk(void) { bool is_4x = false; diff --git a/sound/soc/codecs/amlogic/ad82584f.c b/sound/soc/codecs/amlogic/ad82584f.c index f76a2f9c07f6..c1c69cbd597b 100644 --- a/sound/soc/codecs/amlogic/ad82584f.c +++ b/sound/soc/codecs/amlogic/ad82584f.c @@ -69,7 +69,7 @@ static int ad82584f_set_eq_drc(struct snd_soc_codec *codec); /* Power-up register defaults */ static const struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = { - {0x00, 0x00},//##State_Control_1 + {0x00, 0x04},//##State_Control_1 {0x01, 0x04},//##State_Control_2 {0x02, 0x30},//##State_Control_3 {0x03, 0x4e},//##Master_volume_control @@ -207,7 +207,7 @@ struct reg_default ad82584f_reg_defaults[AD82584F_REGISTER_COUNT] = { }; static const int m_reg_tab[AD82584F_REGISTER_COUNT][2] = { - {0x00, 0x00},//##State_Control_1 + {0x00, 0x04},//##State_Control_1 {0x01, 0x04},//##State_Control_2 {0x02, 0x30},//##State_Control_3 {0x03, 0x4e},//##Master_volume_control @@ -856,7 +856,7 @@ static int ad82584f_probe(struct snd_soc_codec *codec) { struct ad82584f_priv *ad82584f = snd_soc_codec_get_drvdata(codec); struct ad82584f_platform_data *pdata = ad82584f->pdata; - int ret; + int ret = 0; #ifdef CONFIG_HAS_EARLYSUSPEND ad82584f->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; @@ -866,12 +866,15 @@ static int ad82584f_probe(struct snd_soc_codec *codec) register_early_suspend(&(ad82584f->early_suspend)); #endif - ret = devm_gpio_request_one(codec->dev, pdata->reset_pin, - GPIOF_OUT_INIT_LOW, - "ad82584f-reset-pin"); - if (ret < 0) { - dev_err(codec->dev, "ad82584f get gpio error!\n"); - return -1; + if (pdata->reset_pin > 0) { + ret = devm_gpio_request_one(codec->dev, pdata->reset_pin, + GPIOF_OUT_INIT_LOW, + "ad82584f-reset-pin"); + + if (ret < 0) { + dev_err(codec->dev, "ad82584f get gpio error!\n"); + return -1; + } } ad82584f_init(codec); diff --git a/sound/soc/codecs/amlogic/tas5782m.c b/sound/soc/codecs/amlogic/tas5782m.c index d7bbfe383f13..4222e628c9a9 100644 --- a/sound/soc/codecs/amlogic/tas5782m.c +++ b/sound/soc/codecs/amlogic/tas5782m.c @@ -133,7 +133,7 @@ static int tas5782m_ch2_vol_info(struct snd_kcontrol *kcontrol, static int tas5782m_ch1_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; @@ -148,7 +148,7 @@ static int tas5782m_ch1_mute_info(struct snd_kcontrol *kcontrol, static int tas5782m_ch2_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE; uinfo->count = 1; @@ -361,14 +361,14 @@ static const struct snd_kcontrol_new tas5782m_snd_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Ch1 Mute", + .name = "Ch1 Switch", .info = tas5782m_ch1_mute_info, .get = tas5782m_ch1_mute_get, .put = tas5782m_ch1_mute_set, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Ch2 Mute", + .name = "Ch2 Switch", .info = tas5782m_ch2_mute_info, .get = tas5782m_ch2_mute_get, .put = tas5782m_ch2_mute_set, @@ -671,6 +671,11 @@ static void tas5782m_init_func(struct work_struct *p_work) } tas5782m_init_i2s_tdm_mode(codec, g_sample_bitsize); + + tas5782m_set_mute(codec, tas5782m->Ch1_mute, tas5782m->Ch2_mute); + + tas5782m_set_volume(codec, 255-tas5782m->Ch1_vol, 0); + tas5782m_set_volume(codec, 255-tas5782m->Ch2_vol, 1); } static int tas5782m_probe(struct snd_soc_codec *codec) @@ -688,6 +693,11 @@ static int tas5782m_probe(struct snd_soc_codec *codec) tas5782m = snd_soc_codec_get_drvdata(codec); tas5782m->codec = codec; + tas5782m->Ch1_mute = 0; + tas5782m->Ch2_mute = 0; + tas5782m->Ch1_vol = 0; + tas5782m->Ch2_vol = 0; + INIT_WORK(&tas5782m->work, tas5782m_init_func); schedule_work(&tas5782m->work); diff --git a/sound/soc/codecs/amlogic/tas5805.c b/sound/soc/codecs/amlogic/tas5805.c index c697b09ebce3..99f8ac773dfb 100644 --- a/sound/soc/codecs/amlogic/tas5805.c +++ b/sound/soc/codecs/amlogic/tas5805.c @@ -649,10 +649,10 @@ const uint32_t tas5805m_volume[] = { #define TAS5805_EQPARAM_LENGTH 610 #define TAS5805_EQ_LENGTH 245 #define FILTER_PARAM_BYTE 244 -static int m_eq_tab[TAS5805_EQPARAM_LENGTH][2] = {0}; +static int m_eq_tab[TAS5805_EQPARAM_LENGTH][2]; #define TAS5805_DRC_PARAM_LENGTH 29 #define TAS5805_DRC_PARAM_COUNT 58 -static int m_drc_tab[TAS5805_DRC_PARAM_LENGTH][2] = {0}; +static int m_drc_tab[TAS5805_DRC_PARAM_LENGTH][2]; struct tas5805m_priv { struct regmap *regmap;