Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
September 2020
- 137 participants
- 405 discussions
Hello,
syzbot found the following issue on:
HEAD commit: dff9f829 Add linux-next specific files for 20200908
git tree: linux-next
console output: https://syzkaller.appspot.com/x/log.txt?x=1425773e900000
kernel config: https://syzkaller.appspot.com/x/.config?x=37b3426c77bda44c
dashboard link: https://syzkaller.appspot.com/bug?extid=561a74f84100162990b2
compiler: gcc (GCC) 10.1.0-syz 20200507
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=126577ae900000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=121e4e21900000
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+561a74f84100162990b2(a)syzkaller.appspotmail.com
========================================================
WARNING: possible irq lock inversion dependency detected
5.9.0-rc4-next-20200908-syzkaller #0 Not tainted
--------------------------------------------------------
swapper/0/0 just changed the state of lock:
ffff8882153bc908 (&group->lock){..-.}-{2:2}, at: _snd_pcm_stream_lock_irqsave+0x9f/0xd0 sound/core/pcm_native.c:170
but this lock took another, SOFTIRQ-READ-unsafe lock in the past:
(&card->ctl_files_rwlock){.+.+}-{2:2}
and interrupts could create inverse lock ordering between them.
other info that might help us debug this:
Possible interrupt unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&card->ctl_files_rwlock);
local_irq_disable();
lock(&group->lock);
lock(&card->ctl_files_rwlock);
<Interrupt>
lock(&group->lock);
*** DEADLOCK ***
1 lock held by swapper/0/0:
#0: ffffc90000007d68 ((&dpcm->timer)){+.-.}-{0:0}, at: lockdep_copy_map include/linux/lockdep.h:35 [inline]
#0: ffffc90000007d68 ((&dpcm->timer)){+.-.}-{0:0}, at: call_timer_fn+0xdb/0x760 kernel/time/timer.c:1403
the shortest dependencies between 2nd lock and 1st lock:
-> (&card->ctl_files_rwlock){.+.+}-{2:2} {
HARDIRQ-ON-R at:
lock_acquire+0x1f3/0xaf0 kernel/locking/lockdep.c:5398
__raw_read_lock include/linux/rwlock_api_smp.h:149 [inline]
_raw_read_lock+0x5b/0x70 kernel/locking/spinlock.c:223
snd_ctl_notify.part.0+0x36/0x550 sound/core/control.c:153
snd_ctl_notify+0x8f/0xb0 sound/core/control.c:181
__snd_ctl_add_replace+0x638/0x800 sound/core/control.c:382
snd_ctl_add_replace+0x76/0x130 sound/core/control.c:399
snd_card_dummy_new_mixer sound/drivers/dummy.c:885 [inline]
snd_dummy_probe+0xbbf/0x1050 sound/drivers/dummy.c:1080
platform_drv_probe+0x87/0x140 drivers/base/platform.c:747
really_probe+0x282/0x9f0 drivers/base/dd.c:553
driver_probe_device+0xfe/0x1d0 drivers/base/dd.c:738
__device_attach_driver+0x1c2/0x220 drivers/base/dd.c:844
bus_for_each_drv+0x15f/0x1e0 drivers/base/bus.c:431
__device_attach+0x228/0x470 drivers/base/dd.c:912
bus_probe_device+0x1e4/0x290 drivers/base/bus.c:491
device_add+0xb17/0x1c40 drivers/base/core.c:2926
platform_device_add+0x34f/0x6d0 drivers/base/platform.c:597
platform_device_register_full+0x38c/0x4e0 drivers/base/platform.c:720
platform_device_register_resndata include/linux/platform_device.h:131 [inline]
platform_device_register_simple include/linux/platform_device.h:160 [inline]
alsa_card_dummy_init+0x1cc/0x2e0 sound/drivers/dummy.c:1168
do_one_initcall+0x10a/0x7b0 init/main.c:1204
do_initcall_level init/main.c:1277 [inline]
do_initcalls init/main.c:1293 [inline]
do_basic_setup init/main.c:1313 [inline]
kernel_init_freeable+0x5e9/0x66d init/main.c:1512
kernel_init+0xd/0x1c0 init/main.c:1402
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:294
SOFTIRQ-ON-R at:
lock_acquire+0x1f3/0xaf0 kernel/locking/lockdep.c:5398
__raw_read_lock include/linux/rwlock_api_smp.h:149 [inline]
_raw_read_lock+0x5b/0x70 kernel/locking/spinlock.c:223
snd_ctl_notify.part.0+0x36/0x550 sound/core/control.c:153
snd_ctl_notify+0x8f/0xb0 sound/core/control.c:181
__snd_ctl_add_replace+0x638/0x800 sound/core/control.c:382
snd_ctl_add_replace+0x76/0x130 sound/core/control.c:399
snd_card_dummy_new_mixer sound/drivers/dummy.c:885 [inline]
snd_dummy_probe+0xbbf/0x1050 sound/drivers/dummy.c:1080
platform_drv_probe+0x87/0x140 drivers/base/platform.c:747
really_probe+0x282/0x9f0 drivers/base/dd.c:553
driver_probe_device+0xfe/0x1d0 drivers/base/dd.c:738
__device_attach_driver+0x1c2/0x220 drivers/base/dd.c:844
bus_for_each_drv+0x15f/0x1e0 drivers/base/bus.c:431
__device_attach+0x228/0x470 drivers/base/dd.c:912
bus_probe_device+0x1e4/0x290 drivers/base/bus.c:491
device_add+0xb17/0x1c40 drivers/base/core.c:2926
platform_device_add+0x34f/0x6d0 drivers/base/platform.c:597
platform_device_register_full+0x38c/0x4e0 drivers/base/platform.c:720
platform_device_register_resndata include/linux/platform_device.h:131 [inline]
platform_device_register_simple include/linux/platform_device.h:160 [inline]
alsa_card_dummy_init+0x1cc/0x2e0 sound/drivers/dummy.c:1168
do_one_initcall+0x10a/0x7b0 init/main.c:1204
do_initcall_level init/main.c:1277 [inline]
do_initcalls init/main.c:1293 [inline]
do_basic_setup init/main.c:1313 [inline]
kernel_init_freeable+0x5e9/0x66d init/main.c:1512
kernel_init+0xd/0x1c0 init/main.c:1402
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:294
(null) at:
general protection fault, probably for non-canonical address 0xdffffc0000000002: 0000 [#1] PREEMPT SMP KASAN
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.9.0-rc4-next-20200908-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
RIP: 0010:print_lock_trace kernel/locking/lockdep.c:1751 [inline]
RIP: 0010:print_lock_class_header kernel/locking/lockdep.c:2240 [inline]
RIP: 0010:print_shortest_lock_dependencies.cold+0x110/0x2af kernel/locking/lockdep.c:2263
Code: 48 8b 04 24 48 c1 e8 03 42 80 3c 20 00 74 09 48 8b 3c 24 e8 dd fb de f9 48 8b 04 24 48 8b 00 48 8d 78 14 48 89 fa 48 c1 ea 03 <42> 0f b6 0c 22 48 89 fa 83 e2 07 83 c2 03 38 ca 7c 08 84 c9 0f 85
RSP: 0018:ffffc90000007870 EFLAGS: 00010003
RAX: 0000000000000001 RBX: ffffffff8c8d3cc0 RCX: 0000000000000000
RDX: 0000000000000002 RSI: ffffffff815bc817 RDI: 0000000000000015
RBP: ffffc900000079a0 R08: 0000000000000004 R09: ffff8880ae620f8b
R10: 0000000000000000 R11: 6c756e2820202020 R12: dffffc0000000000
R13: ffffffff8c6de1d0 R14: 0000000000000009 R15: 0000000000000001
FS: 0000000000000000(0000) GS:ffff8880ae600000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200000c0 CR3: 0000000093f18000 CR4: 00000000001506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<IRQ>
print_irq_inversion_bug.part.0+0x2c6/0x2ee kernel/locking/lockdep.c:3769
print_irq_inversion_bug kernel/locking/lockdep.c:4377 [inline]
check_usage_forwards kernel/locking/lockdep.c:3803 [inline]
mark_lock_irq kernel/locking/lockdep.c:3935 [inline]
mark_lock.cold+0x38/0x10d kernel/locking/lockdep.c:4375
mark_usage kernel/locking/lockdep.c:4260 [inline]
__lock_acquire+0x117d/0x55d0 kernel/locking/lockdep.c:4750
lock_acquire+0x1f3/0xaf0 kernel/locking/lockdep.c:5398
__raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:110 [inline]
_raw_spin_lock_irqsave+0x94/0xd0 kernel/locking/spinlock.c:159
_snd_pcm_stream_lock_irqsave+0x9f/0xd0 sound/core/pcm_native.c:170
snd_pcm_period_elapsed+0x24/0x250 sound/core/pcm_lib.c:1799
loopback_jiffies_timer_function+0x1a8/0x220 sound/drivers/aloop.c:669
call_timer_fn+0x1ac/0x760 kernel/time/timer.c:1413
expire_timers kernel/time/timer.c:1458 [inline]
__run_timers.part.0+0x67c/0xaa0 kernel/time/timer.c:1755
__run_timers kernel/time/timer.c:1736 [inline]
run_timer_softirq+0xae/0x1a0 kernel/time/timer.c:1768
__do_softirq+0x1f7/0xa91 kernel/softirq.c:298
asm_call_on_stack+0xf/0x20 arch/x86/entry/entry_64.S:706
</IRQ>
__run_on_irqstack arch/x86/include/asm/irq_stack.h:22 [inline]
run_on_irqstack_cond arch/x86/include/asm/irq_stack.h:48 [inline]
do_softirq_own_stack+0x9d/0xd0 arch/x86/kernel/irq_64.c:77
invoke_softirq kernel/softirq.c:393 [inline]
__irq_exit_rcu kernel/softirq.c:423 [inline]
irq_exit_rcu+0x235/0x280 kernel/softirq.c:435
sysvec_apic_timer_interrupt+0x51/0xf0 arch/x86/kernel/apic/apic.c:1091
asm_sysvec_apic_timer_interrupt+0x12/0x20 arch/x86/include/asm/idtentry.h:581
RIP: 0010:native_safe_halt+0xe/0x10 arch/x86/include/asm/irqflags.h:61
Code: ff 4c 89 ef e8 23 c4 b4 f9 e9 8e fe ff ff 48 89 df e8 16 c4 b4 f9 eb 8a cc cc cc cc e9 07 00 00 00 0f 00 2d 94 f6 4a 00 fb f4 <c3> 90 e9 07 00 00 00 0f 00 2d 84 f6 4a 00 f4 c3 cc cc 55 53 e8 e9
RSP: 0018:ffffffff89a07cd8 EFLAGS: 00000293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 1ffffffff15796a9
RDX: ffffffff89a99dc0 RSI: ffffffff87fdd050 RDI: 0000000000000000
RBP: ffff8880a67b3064 R08: 0000000000000001 R09: 0000000000000001
R10: 0000000000000000 R11: 0000000000000001 R12: ffff8880a67b3064
R13: 1ffffffff1340fa5 R14: ffff8880a67b3065 R15: 0000000000000001
arch_safe_halt arch/x86/include/asm/paravirt.h:150 [inline]
acpi_safe_halt+0x95/0x180 drivers/acpi/processor_idle.c:111
acpi_idle_do_entry+0x15c/0x1b0 drivers/acpi/processor_idle.c:524
acpi_idle_enter+0x403/0xac0 drivers/acpi/processor_idle.c:650
cpuidle_enter_state+0x150/0xa70 drivers/cpuidle/cpuidle.c:243
cpuidle_enter+0x4a/0xa0 drivers/cpuidle/cpuidle.c:355
call_cpuidle kernel/sched/idle.c:132 [inline]
cpuidle_idle_call kernel/sched/idle.c:213 [inline]
do_idle+0x48e/0x730 kernel/sched/idle.c:273
cpu_startup_entry+0x14/0x20 kernel/sched/idle.c:369
start_kernel+0x490/0x4b1 init/main.c:1048
secondary_startup_64+0xa4/0xb0 arch/x86/kernel/head_64.S:243
Modules linked in:
---[ end trace 7b8dcf330d33be72 ]---
RIP: 0010:print_lock_trace kernel/locking/lockdep.c:1751 [inline]
RIP: 0010:print_lock_class_header kernel/locking/lockdep.c:2240 [inline]
RIP: 0010:print_shortest_lock_dependencies.cold+0x110/0x2af kernel/locking/lockdep.c:2263
Code: 48 8b 04 24 48 c1 e8 03 42 80 3c 20 00 74 09 48 8b 3c 24 e8 dd fb de f9 48 8b 04 24 48 8b 00 48 8d 78 14 48 89 fa 48 c1 ea 03 <42> 0f b6 0c 22 48 89 fa 83 e2 07 83 c2 03 38 ca 7c 08 84 c9 0f 85
RSP: 0018:ffffc90000007870 EFLAGS: 00010003
RAX: 0000000000000001 RBX: ffffffff8c8d3cc0 RCX: 0000000000000000
RDX: 0000000000000002 RSI: ffffffff815bc817 RDI: 0000000000000015
RBP: ffffc900000079a0 R08: 0000000000000004 R09: ffff8880ae620f8b
R10: 0000000000000000 R11: 6c756e2820202020 R12: dffffc0000000000
R13: ffffffff8c6de1d0 R14: 0000000000000009 R15: 0000000000000001
FS: 0000000000000000(0000) GS:ffff8880ae600000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200000c0 CR3: 0000000093f18000 CR4: 00000000001506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller(a)googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
syzbot can test patches for this issue, for details see:
https://goo.gl/tpsmEJ#testing-patches
1
0
[PATCH] soundwire: cadence: fix race condition between suspend and Slave device alerts
by Bard Liao 09 Sep '20
by Bard Liao 09 Sep '20
09 Sep '20
From: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
In system suspend stress cases, the SOF CI reports timeouts. The root
cause is that an alert is generated while the system suspends. The
interrupt handling generates transactions on the bus that will never
be handled because the interrupts are disabled in parallel.
As a result, the transaction never completes and times out on resume.
This error doesn't seem too problematic since it happens in a work
queue, and the system recovers without issues.
Nevertheless, this race condition should not happen. When doing a
system suspend, or when disabling interrupts, we should make sure the
current transaction can complete, and prevent new work from being
queued.
BugLink: https://github.com/thesofproject/linux/issues/2344
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan(a)linux.intel.com>
Reviewed-by: Rander Wang <rander.wang(a)linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao(a)linux.intel.com>
---
drivers/soundwire/cadence_master.c | 24 +++++++++++++++++++++++-
drivers/soundwire/cadence_master.h | 1 +
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 24eafe0aa1c3..1330ffc47596 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -791,7 +791,16 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
CDNS_MCP_INT_SLAVE_MASK, 0);
int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
- schedule_work(&cdns->work);
+
+ /*
+ * Deal with possible race condition between interrupt
+ * handling and disabling interrupts on suspend.
+ *
+ * If the master is in the process of disabling
+ * interrupts, don't schedule a workqueue
+ */
+ if (cdns->interrupt_enabled)
+ schedule_work(&cdns->work);
}
cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
@@ -924,6 +933,19 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state)
slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state);
}
+ cdns->interrupt_enabled = state;
+
+ /*
+ * Complete any on-going status updates before updating masks,
+ * and cancel queued status updates.
+ *
+ * There could be a race with a new interrupt thrown before
+ * the 3 mask updates below are complete, so in the interrupt
+ * we use the 'interrupt_enabled' status to prevent new work
+ * from being queued.
+ */
+ if (!state)
+ cancel_work_sync(&cdns->work);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0);
cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1);
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index fdec62b912d3..4d1aab5b5ec2 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -133,6 +133,7 @@ struct sdw_cdns {
bool link_up;
unsigned int msg_count;
+ bool interrupt_enabled;
struct work_struct work;
--
2.17.1
4
8
Fix slimbus case being broken thanks to a typo.
Fixes: 5bd773242f75 ("soundwire: qcom: avoid dependency on CONFIG_SLIMBUS")
Signed-off-by: Jonathan Marek <jonathan(a)marek.ca>
---
This should be squashed into the problematic patch if possible,
but I'm not sure if that's possible since its already in linux-next?
drivers/soundwire/qcom.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 100af93a5eab..c406a079d237 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -780,7 +780,7 @@ static int qcom_swrm_probe(struct platform_device *pdev)
if (!ctrl)
return -ENOMEM;
-#if IS_ENABLED(CONFIG_SLIBMUS)
+#if IS_ENABLED(CONFIG_SLIMBUS)
if (dev->parent->bus == &slimbus_bus) {
#else
if (false) {
--
2.26.1
3
2
Sdw stream operation APIs can be called once per stream. Move these
operations to dailink ops. The linked series is "soundwire: Remove sdw
stream operations from Intel soundwire dai".
Reviewed-by: Vinod Koul <vkoul(a)kernel.org>
Changes in v3:
- s/ASOC/ASoC
Pierre-Louis Bossart (3):
ASoC: soc-dai: clarify return value for get_sdw_stream()
ASoC: Intel: sof_sdw: add dailink .trigger callback
ASoC: Intel: sof_sdw: add dailink .prepare and .hw_free callback
include/sound/soc-dai.h | 3 +-
sound/soc/intel/boards/sof_sdw.c | 81 ++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
--
2.17.1
4
7
alsa-project/tinycompress pull request #7 was opened from vinodkoul:
Add SPDX tags and remove the license text, Also adds SPDX tags to files which have this missing
Request URL : https://github.com/alsa-project/tinycompress/pull/7
Patch URL : https://github.com/alsa-project/tinycompress/pull/7.patch
Repository URL: https://github.com/alsa-project/tinycompress
1
0
Some codecs may report fake PARITY errors in the initial state. This
series will filter them out.
Pierre-Louis Bossart (7):
soundwire: bus: use property to set interrupt masks
soundwire: bus: filter-out unwanted interrupt reports
soundwire: slave: add first_interrupt_done status
soundwire: bus: use quirk to filter out invalid parity errors
ASoC: codecs: realtek-soundwire: ignore initial PARITY errors
soundwire: bus: export broadcast read/write capability for tests
soundwire: cadence: add parity error injection through debugfs
drivers/soundwire/bus.c | 93 ++++++++++++++++++++++++------
drivers/soundwire/bus.h | 4 ++
drivers/soundwire/cadence_master.c | 86 +++++++++++++++++++++++++++
drivers/soundwire/slave.c | 1 +
include/linux/soundwire/sdw.h | 9 +++
sound/soc/codecs/max98373-sdw.c | 3 +
sound/soc/codecs/rt1308-sdw.c | 3 +
sound/soc/codecs/rt5682-sdw.c | 5 ++
sound/soc/codecs/rt700-sdw.c | 5 ++
sound/soc/codecs/rt711-sdw.c | 5 ++
sound/soc/codecs/rt715-sdw.c | 5 ++
sound/soc/codecs/wsa881x.c | 1 +
12 files changed, 202 insertions(+), 18 deletions(-)
--
2.17.1
6
19
[PATCH V2] ASoC: Intel: boards: Use FS as nau8825 sysclk in nau88125_* machine
by Radoslaw Biernacki 08 Sep '20
by Radoslaw Biernacki 08 Sep '20
08 Sep '20
This single fix address two issues on machines with nau88125:
1) Audio distortion, due to lack of required clock rate on MCLK line
2) Loud audible "pops" on headphones if there is no sysclk during nau8825
playback power up sequence
Explanation for:
1) Due to Skylake HW limitation, MCLK pin can only output 24MHz clk
rate (it can be only connected to XTAL parent clk). The BCLK pin
can be driven by dividers and therefore FW is able to set it to rate
required by chosen audio format. According to nau8825 datasheet, 256*FS
sysclk gives the best audio quality and the only way to achieve this
(taking into account the above limitations) its to regenerate the MCLK
from BCLK on nau8825 side by FFL. Without required clk rate, audio is
distorted by added harmonics.
2) Currently Skylake does not output MCLK/FS when the back-end DAI op
hw_param is called, so we cannot switch to MCLK/FS in hw_param. This
patch reduces pop by letting nau8825 keep using its internal VCO clock
during widget power up sequence, until SNDRV_PCM_TRIGGER_START when
MCLK/FS is available. Once device resumes, the system will only enable
power sequence for playback without doing hardware parameter, audio
format, and PLL configure. In the mean time, the jack detecion sequence
has changed PLL parameters and switched to internal clock. Thus, the
playback signal distorted without correct PLL parameters. That is why
we need to configure the PLL again in SNDRV_PCM_TRIGGER_RESUME case.
Signed-off-by: John Hsu <KCHSU0(a)nuvoton.com>
Signed-off-by: Yong Zhi <yong.zhi(a)intel.com>
Signed-off-by: Mac Chiang <mac.chiang(a)intel.com>
Signed-off-by: Ben Zhang <benzh(a)chromium.org>
Signed-off-by: Radoslaw Biernacki <rad(a)semihalf.com>
---
.../soc/intel/boards/skl_nau88l25_max98357a.c | 72 +++++++++++++-----
sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 73 ++++++++++++++-----
2 files changed, 107 insertions(+), 38 deletions(-)
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index d7b8154c43a4..0f3cea1342d1 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -47,7 +48,7 @@ enum {
};
static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
+ struct snd_kcontrol *k, int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
@@ -60,14 +61,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO;
}
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- } else {
+ if (!SND_SOC_DAPM_EVENT_ON(event)) {
ret = snd_soc_dai_set_sysclk(codec_dai,
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
if (ret < 0) {
@@ -292,24 +286,40 @@ static const struct snd_soc_ops skylake_nau8825_fe_ops = {
.startup = skl_fe_startup,
};
-static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int skylake_nau8825_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
+ int ret = 0;
- if (ret < 0)
- dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ break;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate,
+ runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate,
+ runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ msleep(20);
+ break;
+ }
return ret;
}
-static const struct snd_soc_ops skylake_nau8825_ops = {
- .hw_params = skylake_nau8825_hw_params,
+static struct snd_soc_ops skylake_nau8825_ops = {
+ .trigger = skylake_nau8825_trigger,
};
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -630,10 +640,34 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
return hdac_hdmi_jack_port_init(component, &card->dapm);
}
+static int __maybe_unused skylake_nau8825_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ dev_dbg(codec_dai->dev, "playback_active:%d playback_widget->active:%d codec_dai->rate:%d\n",
+ codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK],
+ codec_dai->playback_widget->active,
+ codec_dai->rate);
+
+ if (codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] &&
+ codec_dai->playback_widget->active)
+ snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0,
+ SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
/* skylake audio machine driver for SPT + NAU88L25 */
static struct snd_soc_card skylake_audio_card = {
.name = "sklnau8825max",
.owner = THIS_MODULE,
+ .resume_post = skylake_nau8825_resume_post,
.dai_link = skylake_dais,
.num_links = ARRAY_SIZE(skylake_dais),
.controls = skylake_controls,
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index 4b317bcf6ea0..be44a40067f0 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -57,7 +58,7 @@ static const struct snd_kcontrol_new skylake_controls[] = {
};
static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
+ struct snd_kcontrol *k, int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
@@ -70,14 +71,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO;
}
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "set sysclk err = %d\n", ret);
- return -EIO;
- }
- } else {
+ if (!SND_SOC_DAPM_EVENT_ON(event)) {
ret = snd_soc_dai_set_sysclk(codec_dai,
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
if (ret < 0) {
@@ -85,6 +79,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO;
}
}
+
return ret;
}
@@ -344,24 +339,40 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int skylake_nau8825_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai,
- NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
+ int ret = 0;
- if (ret < 0)
- dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ break;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate,
+ runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, runtime->rate,
+ runtime->rate * 256);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ msleep(20);
+ break;
+ }
return ret;
}
-static const struct snd_soc_ops skylake_nau8825_ops = {
- .hw_params = skylake_nau8825_hw_params,
+static struct snd_soc_ops skylake_nau8825_ops = {
+ .trigger = skylake_nau8825_trigger,
};
static const unsigned int channels_dmic[] = {
@@ -671,10 +682,34 @@ static int skylake_card_late_probe(struct snd_soc_card *card)
return hdac_hdmi_jack_port_init(component, &card->dapm);
}
+static int __maybe_unused skylake_nau8825_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ dev_dbg(codec_dai->dev, "playback_active:%d playback_widget->active:%d codec_dai->rate:%d\n",
+ codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK],
+ codec_dai->playback_widget->active,
+ codec_dai->rate);
+
+ if (codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] &&
+ codec_dai->playback_widget->active)
+ snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0,
+ SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
/* skylake audio machine driver for SPT + NAU88L25 */
static struct snd_soc_card skylake_audio_card = {
.name = "sklnau8825adi",
.owner = THIS_MODULE,
+ .resume_post = skylake_nau8825_resume_post,
.dai_link = skylake_dais,
.num_links = ARRAY_SIZE(skylake_dais),
.controls = skylake_controls,
--
2.17.1
5
8
08 Sep '20
From: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
lpass variant structure in audio driver has I2S and HDMI register fields,
which are used exclusively. To optimize memory, replace with union to
select either I2S or HDMI register fields.
Signed-off-by: Srinivasa Rao <srivasam(a)codeaurora.org>
Signed-off-by: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
---
sound/soc/qcom/lpass-apq8016.c | 24 ++++----
sound/soc/qcom/lpass-cpu.c | 27 +++++----
sound/soc/qcom/lpass-hdmi.c | 57 +++++++++----------
sound/soc/qcom/lpass-hdmi.h | 24 ++++----
sound/soc/qcom/lpass-ipq806x.c | 24 ++++----
sound/soc/qcom/lpass-lpaif-reg.h | 3 +-
sound/soc/qcom/lpass-sc7180.c | 115 ++++++++++++++++++++-------------------
sound/soc/qcom/lpass.h | 107 ++++++++++++++++++++----------------
8 files changed, 201 insertions(+), 180 deletions(-)
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 5c8ae22..eb3ad12 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -228,9 +228,9 @@ static int apq8016_lpass_exit(struct platform_device *pdev)
static struct lpass_variant apq8016_data = {
- .i2sctrl_reg_base = 0x1000,
- .i2sctrl_reg_stride = 0x1000,
- .i2s_ports = 4,
+ .intf.i2s_intf.i2sctrl_reg_base = 0x1000,
+ .intf.i2s_intf.i2sctrl_reg_stride = 0x1000,
+ .intf.i2s_intf.i2s_ports = 4,
.irq_reg_base = 0x6000,
.irq_reg_stride = 0x1000,
.irq_ports = 3,
@@ -242,15 +242,15 @@ static struct lpass_variant apq8016_data = {
.wrdma_reg_stride = 0x1000,
.wrdma_channel_start = 5,
.wrdma_channels = 2,
- .loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),
- .spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),
- .spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),
- .spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),
- .micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),
- .micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),
- .micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),
- .wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),
- .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 4, 0x1000),
+ .intf.i2s_intf.loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),
+ .intf.i2s_intf.spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),
+ .intf.i2s_intf.spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),
+ .intf.i2s_intf.spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),
+ .intf.i2s_intf.micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),
+ .intf.i2s_intf.micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),
+ .intf.i2s_intf.micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),
+ .intf.i2s_intf.wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),
+ .intf.i2s_intf.bitwidth = REG_FIELD_ID(0x1000, 0, 0, 4, 0x1000),
.rdma_dyncclk = REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000),
.rdma_bursten = REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000),
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 566d79a..58620b2 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -34,16 +34,17 @@ static int lpass_cpu_init_i2sctl_bitfields(struct device *dev,
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
-
- i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback);
- i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken);
- i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode);
- i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono);
- i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen);
- i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode);
- i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono);
- i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc);
- i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth);
+ struct lpass_i2s_rsrc *irsrc = &v->intf.i2s_intf;
+
+ i2sctl->loopback = devm_regmap_field_alloc(dev, map, irsrc->loopback);
+ i2sctl->spken = devm_regmap_field_alloc(dev, map, irsrc->spken);
+ i2sctl->spkmode = devm_regmap_field_alloc(dev, map, irsrc->spkmode);
+ i2sctl->spkmono = devm_regmap_field_alloc(dev, map, irsrc->spkmono);
+ i2sctl->micen = devm_regmap_field_alloc(dev, map, irsrc->micen);
+ i2sctl->micmode = devm_regmap_field_alloc(dev, map, irsrc->micmode);
+ i2sctl->micmono = devm_regmap_field_alloc(dev, map, irsrc->micmono);
+ i2sctl->wssrc = devm_regmap_field_alloc(dev, map, irsrc->wssrc);
+ i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, irsrc->bitwidth);
if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) ||
IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) ||
@@ -363,9 +364,10 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
+ struct lpass_i2s_rsrc *irsrc = &v->intf.i2s_intf;
int i;
- for (i = 0; i < v->i2s_ports; ++i)
+ for (i = 0; i < irsrc->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
@@ -405,9 +407,10 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
+ struct lpass_i2s_rsrc *irsrc = &v->intf.i2s_intf;
int i;
- for (i = 0; i < v->i2s_ports; ++i)
+ for (i = 0; i < irsrc->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
index 7e18113..b0270e2 100644
--- a/sound/soc/qcom/lpass-hdmi.c
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -25,6 +25,7 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
{
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
+ struct lpass_hdmi_rsrc *hdmi_rsrc = &v->intf.hdmi_intf;
unsigned int i;
struct lpass_hdmi_tx_ctl *tx_ctl;
struct lpass_hdmitx_legacy *legacy;
@@ -43,9 +44,9 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
tx_ctl = drvdata->tx_ctl;
tx_ctl->soft_reset = devm_regmap_field_alloc(dev, map,
- v->soft_reset);
+ hdmi_rsrc->soft_reset);
tx_ctl->force_reset = devm_regmap_field_alloc(dev, map,
- v->force_reset);
+ hdmi_rsrc->force_reset);
if (IS_ERR(tx_ctl->soft_reset) || IS_ERR(tx_ctl->force_reset))
return -EINVAL;
@@ -56,7 +57,7 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
legacy = drvdata->legacy;
legacy->legacy_en = devm_regmap_field_alloc(dev, map,
- v->legacy_en);
+ hdmi_rsrc->legacy_en);
if (IS_ERR(legacy->legacy_en))
return -EINVAL;
@@ -67,9 +68,9 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
vbit_ctl = drvdata->vbit_ctl;
vbit_ctl->replace_vbit = devm_regmap_field_alloc(dev, map,
- v->replace_vbit);
+ hdmi_rsrc->replace_vbit);
vbit_ctl->vbit_stream = devm_regmap_field_alloc(dev, map,
- v->vbit_stream);
+ hdmi_rsrc->vbit_stream);
if (IS_ERR(vbit_ctl->replace_vbit) || IS_ERR(vbit_ctl->vbit_stream))
return -EINVAL;
@@ -80,7 +81,7 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
return -ENOMEM;
tx_parity = drvdata->tx_parity;
- tx_parity->calc_en = devm_regmap_field_alloc(dev, map, v->calc_en);
+ tx_parity->calc_en = devm_regmap_field_alloc(dev, map, hdmi_rsrc->calc_en);
if (IS_ERR(tx_parity->calc_en))
return -EINVAL;
@@ -92,19 +93,19 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
return -ENOMEM;
meta_ctl = drvdata->meta_ctl;
- meta_ctl->mute = devm_regmap_field_alloc(dev, map, v->mute);
+ meta_ctl->mute = devm_regmap_field_alloc(dev, map, hdmi_rsrc->mute);
meta_ctl->as_sdp_cc = devm_regmap_field_alloc(dev, map,
- v->as_sdp_cc);
+ hdmi_rsrc->as_sdp_cc);
meta_ctl->as_sdp_ct = devm_regmap_field_alloc(dev, map,
- v->as_sdp_ct);
+ hdmi_rsrc->as_sdp_ct);
meta_ctl->aif_db4 = devm_regmap_field_alloc(dev, map,
- v->aif_db4);
+ hdmi_rsrc->aif_db4);
meta_ctl->frequency = devm_regmap_field_alloc(dev, map,
- v->frequency);
+ hdmi_rsrc->frequency);
meta_ctl->mst_index = devm_regmap_field_alloc(dev, map,
- v->mst_index);
+ hdmi_rsrc->mst_index);
meta_ctl->dptx_index = devm_regmap_field_alloc(dev, map,
- v->dptx_index);
+ hdmi_rsrc->dptx_index);
if (IS_ERR(meta_ctl->mute) || IS_ERR(meta_ctl->as_sdp_cc) ||
IS_ERR(meta_ctl->as_sdp_ct) || IS_ERR(meta_ctl->aif_db4) ||
@@ -119,23 +120,23 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
sstream_ctl = drvdata->sstream_ctl;
sstream_ctl->sstream_en = devm_regmap_field_alloc(dev, map,
- v->sstream_en);
+ hdmi_rsrc->sstream_en);
sstream_ctl->dma_sel = devm_regmap_field_alloc(dev, map,
- v->dma_sel);
+ hdmi_rsrc->dma_sel);
sstream_ctl->auto_bbit_en = devm_regmap_field_alloc(dev, map,
- v->auto_bbit_en);
+ hdmi_rsrc->auto_bbit_en);
sstream_ctl->layout = devm_regmap_field_alloc(dev, map,
- v->layout);
+ hdmi_rsrc->layout);
sstream_ctl->layout_sp = devm_regmap_field_alloc(dev, map,
- v->layout_sp);
+ hdmi_rsrc->layout_sp);
sstream_ctl->dp_audio = devm_regmap_field_alloc(dev, map,
- v->dp_audio);
+ hdmi_rsrc->dp_audio);
sstream_ctl->set_sp_on_en = devm_regmap_field_alloc(dev, map,
- v->set_sp_on_en);
+ hdmi_rsrc->set_sp_on_en);
sstream_ctl->dp_staffing_en = devm_regmap_field_alloc(dev, map,
- v->dp_staffing_en);
+ hdmi_rsrc->dp_staffing_en);
sstream_ctl->dp_sp_b_hw_en = devm_regmap_field_alloc(dev, map,
- v->dp_sp_b_hw_en);
+ hdmi_rsrc->dp_sp_b_hw_en);
if (IS_ERR(sstream_ctl->sstream_en) || IS_ERR(sstream_ctl->dma_sel) ||
IS_ERR(sstream_ctl->auto_bbit_en) ||
@@ -155,7 +156,7 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
ch_msb = drvdata->ch_msb[i];
ch_msb->msb_bits = devm_regmap_field_alloc(dev, map,
- v->msb_bits);
+ hdmi_rsrc->msb_bits);
if (IS_ERR(ch_msb->msb_bits))
return -EINVAL;
@@ -166,7 +167,7 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
ch_lsb = drvdata->ch_lsb[i];
ch_lsb->lsb_bits = devm_regmap_field_alloc(dev, map,
- v->lsb_bits);
+ hdmi_rsrc->lsb_bits);
if (IS_ERR(ch_lsb->lsb_bits))
return -EINVAL;
@@ -178,13 +179,13 @@ int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
hdmi_tx_dmactl = drvdata->hdmi_tx_dmactl[i];
hdmi_tx_dmactl->use_hw_chs = devm_regmap_field_alloc(dev, map,
- v->use_hw_chs);
+ hdmi_rsrc->use_hw_chs);
hdmi_tx_dmactl->use_hw_usr = devm_regmap_field_alloc(dev, map,
- v->use_hw_usr);
+ hdmi_rsrc->use_hw_usr);
hdmi_tx_dmactl->hw_chs_sel = devm_regmap_field_alloc(dev, map,
- v->hw_chs_sel);
+ hdmi_rsrc->hw_chs_sel);
hdmi_tx_dmactl->hw_usr_sel = devm_regmap_field_alloc(dev, map,
- v->hw_usr_sel);
+ hdmi_rsrc->hw_usr_sel);
if (IS_ERR(hdmi_tx_dmactl->use_hw_chs) ||
IS_ERR(hdmi_tx_dmactl->use_hw_usr) ||
IS_ERR(hdmi_tx_dmactl->hw_chs_sel) ||
diff --git a/sound/soc/qcom/lpass-hdmi.h b/sound/soc/qcom/lpass-hdmi.h
index f91f322..0866e82 100644
--- a/sound/soc/qcom/lpass-hdmi.h
+++ b/sound/soc/qcom/lpass-hdmi.h
@@ -49,27 +49,27 @@
#define LPASS_HDMI_TX_CTL_ADDR(v) \
- (v->hdmi_tx_ctl_addr)
+ (v->intf.hdmi_intf.hdmi_tx_ctl_addr)
#define LPASS_HDMI_TX_LEGACY_ADDR(v) \
- (v->hdmi_legacy_addr)
+ (v->intf.hdmi_intf.hdmi_legacy_addr)
#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) \
- (v->hdmi_vbit_addr)
+ (v->intf.hdmi_intf.hdmi_vbit_addr)
#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \
- (v->hdmi_ch_lsb_addr + \
- v->ch_stride * (port))
+ (v->intf.hdmi_intf.hdmi_ch_lsb_addr + \
+ v->intf.hdmi_intf.ch_stride * (port))
#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \
- (v->hdmi_ch_msb_addr + \
- v->ch_stride * (port))
+ (v->intf.hdmi_intf.hdmi_ch_msb_addr + \
+ v->intf.hdmi_intf.ch_stride * (port))
#define LPASS_HDMI_TX_DMA_ADDR(v, port) \
- (v->hdmi_dmactl_addr + \
- v->hdmi_dma_stride * (port))
+ (v->intf.hdmi_intf.hdmi_dmactl_addr + \
+ v->intf.hdmi_intf.hdmi_dma_stride * (port))
#define LPASS_HDMI_TX_PARITY_ADDR(v) \
- (v->hdmi_parity_addr)
+ (v->intf.hdmi_intf.hdmi_parity_addr)
#define LPASS_HDMI_TX_DP_ADDR(v) \
- (v->hdmi_DP_addr)
+ (v->intf.hdmi_intf.hdmi_DP_addr)
#define LPASS_HDMI_TX_SSTREAM_ADDR(v) \
- (v->hdmi_sstream_addr)
+ (v->intf.hdmi_intf.hdmi_sstream_addr)
struct lpass_sstream_ctl {
struct regmap_field *sstream_en;
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index 72f09b3..525d89b 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -110,9 +110,9 @@ static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
}
static struct lpass_variant ipq806x_data = {
- .i2sctrl_reg_base = 0x0010,
- .i2sctrl_reg_stride = 0x04,
- .i2s_ports = 5,
+ .intf.i2s_intf.i2sctrl_reg_base = 0x0010,
+ .intf.i2s_intf.i2sctrl_reg_stride = 0x04,
+ .intf.i2s_intf.i2s_ports = 5,
.irq_reg_base = 0x3000,
.irq_reg_stride = 0x1000,
.irq_ports = 3,
@@ -123,15 +123,15 @@ static struct lpass_variant ipq806x_data = {
.wrdma_reg_stride = 0x1000,
.wrdma_channel_start = 5,
.wrdma_channels = 4,
- .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4),
- .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4),
- .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4),
- .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4),
- .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4),
- .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4),
- .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4),
- .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4),
- .bitwidth = REG_FIELD_ID(0x0010, 0, 0, 5, 0x4),
+ .intf.i2s_intf.loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4),
+ .intf.i2s_intf.spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4),
+ .intf.i2s_intf.spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4),
+ .intf.i2s_intf.spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4),
+ .intf.i2s_intf.micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4),
+ .intf.i2s_intf.micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4),
+ .intf.i2s_intf.micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4),
+ .intf.i2s_intf.wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4),
+ .intf.i2s_intf.bitwidth = REG_FIELD_ID(0x0010, 0, 0, 5, 0x4),
.rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000),
.rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000),
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index ecf9be1..c74c348 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -9,7 +9,8 @@
/* LPAIF I2S */
#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
- (v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
+ (v->intf.i2s_intf.i2sctrl_reg_base + (addr) + \
+ v->intf.i2s_intf.i2sctrl_reg_stride * (port))
#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 34db061..13f2ab0 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -177,9 +177,9 @@ static int sc7180_lpass_exit(struct platform_device *pdev)
}
static struct lpass_variant sc7180_data = {
- .i2sctrl_reg_base = 0x1000,
- .i2sctrl_reg_stride = 0x1000,
- .i2s_ports = 3,
+ .intf.i2s_intf.i2sctrl_reg_base = 0x1000,
+ .intf.i2s_intf.i2sctrl_reg_stride = 0x1000,
+ .intf.i2s_intf.i2s_ports = 3,
.irq_reg_base = 0x9000,
.irq_reg_stride = 0x1000,
.irq_ports = 3,
@@ -192,20 +192,20 @@ static struct lpass_variant sc7180_data = {
.wrdma_channel_start = 5,
.wrdma_channels = 4,
- .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
- .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
- .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
- .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
- .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
- .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
- .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
- .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
- .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 3, 0x1000),
+ .intf.i2s_intf.loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
+ .intf.i2s_intf.spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
+ .intf.i2s_intf.spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
+ .intf.i2s_intf.spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
+ .intf.i2s_intf.micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
+ .intf.i2s_intf.micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
+ .intf.i2s_intf.micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
+ .intf.i2s_intf.wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
+ .intf.i2s_intf.bitwidth = REG_FIELD_ID(0x1000, 0, 0, 3, 0x1000),
.rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
.rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
.rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
- .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
+ .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
.rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
.rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
@@ -238,18 +238,19 @@ static struct lpass_variant sc7180_data = {
.free_dma_channel = sc7180_lpass_free_dma_channel,
};
+
static const struct lpass_variant sc7180_hdmi_data = {
- .hdmi_tx_ctl_addr = 0x1000,
- .hdmi_legacy_addr = 0x1008,
- .hdmi_vbit_addr = 0x610c0,
- .hdmi_ch_lsb_addr = 0x61048,
- .hdmi_ch_msb_addr = 0x6104c,
- .ch_stride = 0x8,
- .hdmi_parity_addr = 0x61034,
- .hdmi_dmactl_addr = 0x61038,
- .hdmi_dma_stride = 0x4,
- .hdmi_DP_addr = 0x610c8,
- .hdmi_sstream_addr = 0x6101c,
+ .intf.hdmi_intf.hdmi_tx_ctl_addr = 0x1000,
+ .intf.hdmi_intf.hdmi_legacy_addr = 0x1008,
+ .intf.hdmi_intf.hdmi_vbit_addr = 0x610c0,
+ .intf.hdmi_intf.hdmi_ch_lsb_addr = 0x61048,
+ .intf.hdmi_intf.hdmi_ch_msb_addr = 0x6104c,
+ .intf.hdmi_intf.ch_stride = 0x8,
+ .intf.hdmi_intf.hdmi_parity_addr = 0x61034,
+ .intf.hdmi_intf.hdmi_dmactl_addr = 0x61038,
+ .intf.hdmi_intf.hdmi_dma_stride = 0x4,
+ .intf.hdmi_intf.hdmi_DP_addr = 0x610c8,
+ .intf.hdmi_intf.hdmi_sstream_addr = 0x6101c,
.irq_reg_base = 0x63000,
.irq_ports = 1,
.rdma_reg_base = 0x64000,
@@ -265,39 +266,39 @@ static const struct lpass_variant sc7180_hdmi_data = {
.rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
.rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
- .sstream_en = REG_FIELD(0x6101c, 0, 0),
- .dma_sel = REG_FIELD(0x6101c, 1, 2),
- .auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
- .layout = REG_FIELD(0x6101c, 4, 4),
- .layout_sp = REG_FIELD(0x6101c, 5, 8),
- .set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
- .dp_audio = REG_FIELD(0x6101c, 11, 11),
- .dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
- .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
-
- .mute = REG_FIELD(0x610c8, 0, 0),
- .as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
- .as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
- .aif_db4 = REG_FIELD(0x610c8, 8, 15),
- .frequency = REG_FIELD(0x610c8, 16, 21),
- .mst_index = REG_FIELD(0x610c8, 28, 29),
- .dptx_index = REG_FIELD(0x610c8, 30, 31),
-
- .soft_reset = REG_FIELD(0x1000, 31, 31),
- .force_reset = REG_FIELD(0x1000, 30, 30),
-
- .use_hw_chs = REG_FIELD(0x61038, 0, 0),
- .use_hw_usr = REG_FIELD(0x61038, 1, 1),
- .hw_chs_sel = REG_FIELD(0x61038, 2, 4),
- .hw_usr_sel = REG_FIELD(0x61038, 5, 6),
-
- .replace_vbit = REG_FIELD(0x610c0, 0, 0),
- .vbit_stream = REG_FIELD(0x610c0, 1, 1),
-
- .legacy_en = REG_FIELD(0x1008, 0, 0),
- .calc_en = REG_FIELD(0x61034, 0, 0),
- .lsb_bits = REG_FIELD(0x61048, 0, 31),
- .msb_bits = REG_FIELD(0x6104c, 0, 31),
+ .intf.hdmi_intf.sstream_en = REG_FIELD(0x6101c, 0, 0),
+ .intf.hdmi_intf.dma_sel = REG_FIELD(0x6101c, 1, 2),
+ .intf.hdmi_intf.auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
+ .intf.hdmi_intf.layout = REG_FIELD(0x6101c, 4, 4),
+ .intf.hdmi_intf.layout_sp = REG_FIELD(0x6101c, 5, 8),
+ .intf.hdmi_intf.set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
+ .intf.hdmi_intf.dp_audio = REG_FIELD(0x6101c, 11, 11),
+ .intf.hdmi_intf.dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
+ .intf.hdmi_intf.dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
+
+ .intf.hdmi_intf.mute = REG_FIELD(0x610c8, 0, 0),
+ .intf.hdmi_intf.as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
+ .intf.hdmi_intf.as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
+ .intf.hdmi_intf.aif_db4 = REG_FIELD(0x610c8, 8, 15),
+ .intf.hdmi_intf.frequency = REG_FIELD(0x610c8, 16, 21),
+ .intf.hdmi_intf.mst_index = REG_FIELD(0x610c8, 28, 29),
+ .intf.hdmi_intf.dptx_index = REG_FIELD(0x610c8, 30, 31),
+
+ .intf.hdmi_intf.soft_reset = REG_FIELD(0x1000, 31, 31),
+ .intf.hdmi_intf.force_reset = REG_FIELD(0x1000, 30, 30),
+
+ .intf.hdmi_intf.use_hw_chs = REG_FIELD(0x61038, 0, 0),
+ .intf.hdmi_intf.use_hw_usr = REG_FIELD(0x61038, 1, 1),
+ .intf.hdmi_intf.hw_chs_sel = REG_FIELD(0x61038, 2, 4),
+ .intf.hdmi_intf.hw_usr_sel = REG_FIELD(0x61038, 5, 6),
+
+ .intf.hdmi_intf.replace_vbit = REG_FIELD(0x610c0, 0, 0),
+ .intf.hdmi_intf.vbit_stream = REG_FIELD(0x610c0, 1, 1),
+
+ .intf.hdmi_intf.legacy_en = REG_FIELD(0x1008, 0, 0),
+ .intf.hdmi_intf.calc_en = REG_FIELD(0x61034, 0, 0),
+ .intf.hdmi_intf.lsb_bits = REG_FIELD(0x61048, 0, 31),
+ .intf.hdmi_intf.msb_bits = REG_FIELD(0x6104c, 0, 31),
.clk_name = (const char*[]) {
"pcnoc-sway-clk",
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 3c2a64a..5d80205 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -100,22 +100,7 @@ struct lpass_data {
struct lpass_dp_metadata_ctl *meta_ctl;
struct lpass_sstream_ctl *sstream_ctl;
};
-
-/* Vairant data per each SOC */
-struct lpass_variant {
- u32 i2sctrl_reg_base;
- u32 i2sctrl_reg_stride;
- u32 i2s_ports;
- u32 irq_reg_base;
- u32 irq_reg_stride;
- u32 irq_ports;
- u32 rdma_reg_base;
- u32 rdma_reg_stride;
- u32 rdma_channels;
- u32 wrdma_reg_base;
- u32 wrdma_reg_stride;
- u32 wrdma_channels;
-
+struct lpass_hdmi_rsrc {
/* HDMI specific controls */
u32 hdmi_tx_ctl_addr;
u32 hdmi_legacy_addr;
@@ -129,36 +114,6 @@ struct lpass_variant {
u32 hdmi_DP_addr;
u32 hdmi_sstream_addr;
- /* I2SCTL Register fields */
- struct reg_field loopback;
- struct reg_field spken;
- struct reg_field spkmode;
- struct reg_field spkmono;
- struct reg_field micen;
- struct reg_field micmode;
- struct reg_field micmono;
- struct reg_field wssrc;
- struct reg_field bitwidth;
-
- /* RD_DMA Register fields */
- struct reg_field rdma_bursten;
- struct reg_field rdma_burst8;
- struct reg_field rdma_burst16;
- struct reg_field rdma_dynburst;
- struct reg_field rdma_wpscnt;
- struct reg_field rdma_intf;
- struct reg_field rdma_fifowm;
- struct reg_field rdma_enable;
- struct reg_field rdma_dyncclk;
-
- /* WR_DMA Register fields */
- struct reg_field wrdma_bursten;
- struct reg_field wrdma_wpscnt;
- struct reg_field wrdma_intf;
- struct reg_field wrdma_fifowm;
- struct reg_field wrdma_enable;
- struct reg_field wrdma_dyncclk;
-
/* HDMI SSTREAM CTRL fields */
struct reg_field sstream_en;
struct reg_field dma_sel;
@@ -204,6 +159,66 @@ struct lpass_variant {
/* HDMI CH MSB */
struct reg_field msb_bits;
+};
+
+struct lpass_i2s_rsrc {
+ u32 i2sctrl_reg_base;
+ u32 i2sctrl_reg_stride;
+ u32 i2s_ports;
+
+ /* I2SCTL Register fields */
+ struct reg_field loopback;
+ struct reg_field spken;
+ struct reg_field spkmode;
+ struct reg_field spkmono;
+ struct reg_field micen;
+ struct reg_field micmode;
+ struct reg_field micmono;
+ struct reg_field wssrc;
+ struct reg_field bitwidth;
+};
+
+union audio_rsrc_interface {
+ struct lpass_hdmi_rsrc hdmi_intf;
+ struct lpass_i2s_rsrc i2s_intf;
+};
+
+/* Vairant data per each SOC */
+struct lpass_variant {
+ u32 irq_reg_base;
+ u32 irq_reg_stride;
+ u32 irq_ports;
+ u32 rdma_reg_base;
+ u32 rdma_reg_stride;
+ u32 rdma_channels;
+ u32 wrdma_reg_base;
+ u32 wrdma_reg_stride;
+ u32 wrdma_channels;
+
+ /* Interface differentiation variable */
+ int id;
+
+ /* Interface related rsrc */
+ union audio_rsrc_interface intf;
+
+ /* RD_DMA Register fields */
+ struct reg_field rdma_bursten;
+ struct reg_field rdma_burst8;
+ struct reg_field rdma_burst16;
+ struct reg_field rdma_dynburst;
+ struct reg_field rdma_wpscnt;
+ struct reg_field rdma_intf;
+ struct reg_field rdma_fifowm;
+ struct reg_field rdma_enable;
+ struct reg_field rdma_dyncclk;
+
+ /* WR_DMA Register fields */
+ struct reg_field wrdma_bursten;
+ struct reg_field wrdma_wpscnt;
+ struct reg_field wrdma_intf;
+ struct reg_field wrdma_fifowm;
+ struct reg_field wrdma_enable;
+ struct reg_field wrdma_dyncclk;
/**
* on SOCs like APQ8016 the channel control bits start
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
1
0
08 Sep '20
From: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
Upadate lpass cpu and platform driver to support audio over dp.
Also add lpass-hdmi.c and lpass-hdmi.h.
Signed-off-by: Srinivasa Rao <srivasam(a)codeaurora.org>
Signed-off-by: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
---
sound/soc/qcom/Kconfig | 5 +
sound/soc/qcom/Makefile | 2 +
sound/soc/qcom/lpass-cpu.c | 66 ++--
sound/soc/qcom/lpass-hdmi.c | 684 +++++++++++++++++++++++++++++++++++++++
sound/soc/qcom/lpass-hdmi.h | 129 ++++++++
sound/soc/qcom/lpass-lpaif-reg.h | 48 ++-
sound/soc/qcom/lpass-platform.c | 304 +++++++++++++----
sound/soc/qcom/lpass.h | 84 ++++-
8 files changed, 1237 insertions(+), 85 deletions(-)
create mode 100644 sound/soc/qcom/lpass-hdmi.c
create mode 100644 sound/soc/qcom/lpass-hdmi.h
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index a607ace..509584c 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -12,6 +12,10 @@ config SND_SOC_LPASS_CPU
tristate
select REGMAP_MMIO
+config SND_SOC_LPASS_HDMI
+ tristate
+ select REGMAP_MMIO
+
config SND_SOC_LPASS_PLATFORM
tristate
select REGMAP_MMIO
@@ -30,6 +34,7 @@ config SND_SOC_LPASS_SC7180
tristate
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
+ select SND_SOC_LPASS_HDMI
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 7972c94..0bd90d7 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -1,12 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
# Platform
snd-soc-lpass-cpu-objs := lpass-cpu.o
+snd-soc-lpass-hdmi-objs := lpass-hdmi.o
snd-soc-lpass-platform-objs := lpass-platform.o
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
snd-soc-lpass-apq8016-objs := lpass-apq8016.o
snd-soc-lpass-sc7180-objs := lpass-sc7180.o
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
+obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 1ee6d8b..566d79a 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -554,6 +554,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct of_device_id *match;
int ret, i, dai_id;
+ unsigned int id;
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
if (dsp_of_node) {
@@ -572,8 +573,10 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
drvdata->variant = (struct lpass_variant *)match->data;
variant = drvdata->variant;
+ id = variant->dai_driver->id;
- of_lpass_cpu_parse_dai_data(dev, drvdata);
+ if (id != HDMI)
+ of_lpass_cpu_parse_dai_data(dev, drvdata);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -584,18 +587,33 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR((void const __force *)drvdata->lpaif);
}
- lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
+ if (id != HDMI) {
+ lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
variant->wrdma_channels +
variant->wrdma_channel_start);
- drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif,
- &lpass_cpu_regmap_config);
- if (IS_ERR(drvdata->lpaif_map)) {
- dev_err(dev, "error initializing regmap: %ld\n",
+ drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif,
+ &lpass_cpu_regmap_config);
+ if (IS_ERR(drvdata->lpaif_map)) {
+ dev_err(dev, "error initializing regmap: %ld\n",
+ PTR_ERR(drvdata->lpaif_map));
+ return PTR_ERR(drvdata->lpaif_map);
+ }
+ } else {
+#ifdef CONFIG_SND_SOC_LPASS_SC7180
+ lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant,
+ variant->rdma_channels);
+ drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif,
+ &lpass_hdmi_regmap_config);
+ if (IS_ERR(drvdata->lpaif_map)) {
+ dev_err(dev, "error initializing regmap: %ld\n",
PTR_ERR(drvdata->lpaif_map));
- return PTR_ERR(drvdata->lpaif_map);
+ return PTR_ERR(drvdata->lpaif_map);
+ }
+#else
+ return -EINVAL;
+#endif
}
-
if (variant->init) {
ret = variant->init(pdev);
if (ret) {
@@ -606,6 +624,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
for (i = 0; i < variant->num_dai; i++) {
dai_id = variant->dai_driver[i].id;
+ if (dai_id == HDMI)
+ continue;
+
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev,
variant->dai_osr_clk_names[i]);
if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
@@ -629,18 +650,27 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
}
}
- /* Allocation for i2sctl regmap fields */
- drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl),
- GFP_KERNEL);
+ if (id != HDMI) {
+ /* Allocation for i2sctl regmap fields */
+ drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl),
+ GFP_KERNEL);
- /* Initialize bitfields for dai I2SCTL register */
- ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl,
- drvdata->lpaif_map);
- if (ret) {
- dev_err(dev, "error init i2sctl field: %d\n", ret);
- return ret;
+ /* Initialize bitfields for dai I2SCTL register */
+ ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl,
+ drvdata->lpaif_map);
+ if (ret)
+ dev_err(dev, "error init i2sctl field: %d\n", ret);
+ } else {
+#ifdef CONFIG_SND_SOC_LPASS_SC7180
+ ret = lpass_hdmi_init_bitfields(dev, drvdata->lpaif_map);
+ if (ret) {
+ dev_err(dev, "%s error hdmi init failed\n", __func__);
+ return ret;
+ }
+#else
+ return -EINVAL;
+#endif
}
-
ret = devm_snd_soc_register_component(dev,
&lpass_cpu_comp_driver,
variant->dai_driver,
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
new file mode 100644
index 0000000..7e18113
--- /dev/null
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <dt-bindings/sound/sc7180-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+#include "lpass-hdmi.h"
+
+int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
+ unsigned int i;
+ struct lpass_hdmi_tx_ctl *tx_ctl;
+ struct lpass_hdmitx_legacy *legacy;
+ struct lpass_vbit_ctrl *vbit_ctl;
+ struct lpass_hdmi_tx_parity *tx_parity;
+ struct lpass_dp_metadata_ctl *meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl;
+ struct lpass_hdmi_tx_ch_msb *ch_msb;
+ struct lpass_hdmi_tx_ch_lsb *ch_lsb;
+ struct lpass_hdmitx_dmactl *hdmi_tx_dmactl;
+
+ drvdata->tx_ctl = devm_kzalloc(dev, sizeof(struct lpass_hdmi_tx_ctl),
+ GFP_KERNEL);
+ if (drvdata->tx_ctl == NULL)
+ return -ENOMEM;
+
+ tx_ctl = drvdata->tx_ctl;
+ tx_ctl->soft_reset = devm_regmap_field_alloc(dev, map,
+ v->soft_reset);
+ tx_ctl->force_reset = devm_regmap_field_alloc(dev, map,
+ v->force_reset);
+ if (IS_ERR(tx_ctl->soft_reset) || IS_ERR(tx_ctl->force_reset))
+ return -EINVAL;
+
+ drvdata->legacy = devm_kzalloc(dev, sizeof(struct lpass_hdmitx_legacy),
+ GFP_KERNEL);
+ if (drvdata->legacy == NULL)
+ return -ENOMEM;
+
+ legacy = drvdata->legacy;
+ legacy->legacy_en = devm_regmap_field_alloc(dev, map,
+ v->legacy_en);
+ if (IS_ERR(legacy->legacy_en))
+ return -EINVAL;
+
+ drvdata->vbit_ctl = devm_kzalloc(dev, sizeof(struct lpass_vbit_ctrl),
+ GFP_KERNEL);
+ if (drvdata->vbit_ctl == NULL)
+ return -ENOMEM;
+
+ vbit_ctl = drvdata->vbit_ctl;
+ vbit_ctl->replace_vbit = devm_regmap_field_alloc(dev, map,
+ v->replace_vbit);
+ vbit_ctl->vbit_stream = devm_regmap_field_alloc(dev, map,
+ v->vbit_stream);
+ if (IS_ERR(vbit_ctl->replace_vbit) || IS_ERR(vbit_ctl->vbit_stream))
+ return -EINVAL;
+
+ drvdata->tx_parity = devm_kzalloc(dev,
+ sizeof(struct lpass_hdmi_tx_parity), GFP_KERNEL);
+
+ if (drvdata->tx_parity == NULL)
+ return -ENOMEM;
+
+ tx_parity = drvdata->tx_parity;
+ tx_parity->calc_en = devm_regmap_field_alloc(dev, map, v->calc_en);
+
+ if (IS_ERR(tx_parity->calc_en))
+ return -EINVAL;
+
+ drvdata->meta_ctl = devm_kzalloc(dev,
+ sizeof(struct lpass_dp_metadata_ctl), GFP_KERNEL);
+
+ if (drvdata->meta_ctl == NULL)
+ return -ENOMEM;
+
+ meta_ctl = drvdata->meta_ctl;
+ meta_ctl->mute = devm_regmap_field_alloc(dev, map, v->mute);
+ meta_ctl->as_sdp_cc = devm_regmap_field_alloc(dev, map,
+ v->as_sdp_cc);
+ meta_ctl->as_sdp_ct = devm_regmap_field_alloc(dev, map,
+ v->as_sdp_ct);
+ meta_ctl->aif_db4 = devm_regmap_field_alloc(dev, map,
+ v->aif_db4);
+ meta_ctl->frequency = devm_regmap_field_alloc(dev, map,
+ v->frequency);
+ meta_ctl->mst_index = devm_regmap_field_alloc(dev, map,
+ v->mst_index);
+ meta_ctl->dptx_index = devm_regmap_field_alloc(dev, map,
+ v->dptx_index);
+
+ if (IS_ERR(meta_ctl->mute) || IS_ERR(meta_ctl->as_sdp_cc) ||
+ IS_ERR(meta_ctl->as_sdp_ct) || IS_ERR(meta_ctl->aif_db4) ||
+ IS_ERR(meta_ctl->frequency) || IS_ERR(meta_ctl->mst_index) ||
+ IS_ERR(meta_ctl->dptx_index))
+ return -EINVAL;
+
+ drvdata->sstream_ctl = devm_kzalloc(dev,
+ sizeof(struct lpass_sstream_ctl), GFP_KERNEL);
+ if (drvdata->sstream_ctl == NULL)
+ return -ENOMEM;
+
+ sstream_ctl = drvdata->sstream_ctl;
+ sstream_ctl->sstream_en = devm_regmap_field_alloc(dev, map,
+ v->sstream_en);
+ sstream_ctl->dma_sel = devm_regmap_field_alloc(dev, map,
+ v->dma_sel);
+ sstream_ctl->auto_bbit_en = devm_regmap_field_alloc(dev, map,
+ v->auto_bbit_en);
+ sstream_ctl->layout = devm_regmap_field_alloc(dev, map,
+ v->layout);
+ sstream_ctl->layout_sp = devm_regmap_field_alloc(dev, map,
+ v->layout_sp);
+ sstream_ctl->dp_audio = devm_regmap_field_alloc(dev, map,
+ v->dp_audio);
+ sstream_ctl->set_sp_on_en = devm_regmap_field_alloc(dev, map,
+ v->set_sp_on_en);
+ sstream_ctl->dp_staffing_en = devm_regmap_field_alloc(dev, map,
+ v->dp_staffing_en);
+ sstream_ctl->dp_sp_b_hw_en = devm_regmap_field_alloc(dev, map,
+ v->dp_sp_b_hw_en);
+
+ if (IS_ERR(sstream_ctl->sstream_en) || IS_ERR(sstream_ctl->dma_sel) ||
+ IS_ERR(sstream_ctl->auto_bbit_en) ||
+ IS_ERR(sstream_ctl->layout) || IS_ERR(sstream_ctl->layout_sp) ||
+ IS_ERR(sstream_ctl->dp_audio) ||
+ IS_ERR(sstream_ctl->set_sp_on_en) ||
+ IS_ERR(sstream_ctl->dp_staffing_en) ||
+ IS_ERR(sstream_ctl->dp_sp_b_hw_en))
+ return -EINVAL;
+
+ for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) {
+ drvdata->ch_msb[i] = devm_kzalloc(dev,
+ sizeof(struct lpass_hdmi_tx_ch_msb), GFP_KERNEL);
+ if (drvdata->ch_msb[i] == NULL)
+ return -ENOMEM;
+
+ ch_msb = drvdata->ch_msb[i];
+
+ ch_msb->msb_bits = devm_regmap_field_alloc(dev, map,
+ v->msb_bits);
+ if (IS_ERR(ch_msb->msb_bits))
+ return -EINVAL;
+
+ drvdata->ch_lsb[i] = devm_kzalloc(dev,
+ sizeof(struct lpass_hdmi_tx_ch_lsb), GFP_KERNEL);
+ if (drvdata->ch_lsb[i] == NULL)
+ return -ENOMEM;
+
+ ch_lsb = drvdata->ch_lsb[i];
+ ch_lsb->lsb_bits = devm_regmap_field_alloc(dev, map,
+ v->lsb_bits);
+ if (IS_ERR(ch_lsb->lsb_bits))
+ return -EINVAL;
+
+
+ drvdata->hdmi_tx_dmactl[i] = devm_kzalloc(dev,
+ sizeof(struct lpass_hdmitx_dmactl), GFP_KERNEL);
+ if (drvdata->hdmi_tx_dmactl[i] == NULL)
+ return -ENOMEM;
+
+ hdmi_tx_dmactl = drvdata->hdmi_tx_dmactl[i];
+ hdmi_tx_dmactl->use_hw_chs = devm_regmap_field_alloc(dev, map,
+ v->use_hw_chs);
+ hdmi_tx_dmactl->use_hw_usr = devm_regmap_field_alloc(dev, map,
+ v->use_hw_usr);
+ hdmi_tx_dmactl->hw_chs_sel = devm_regmap_field_alloc(dev, map,
+ v->hw_chs_sel);
+ hdmi_tx_dmactl->hw_usr_sel = devm_regmap_field_alloc(dev, map,
+ v->hw_usr_sel);
+ if (IS_ERR(hdmi_tx_dmactl->use_hw_chs) ||
+ IS_ERR(hdmi_tx_dmactl->use_hw_usr) ||
+ IS_ERR(hdmi_tx_dmactl->hw_chs_sel) ||
+ IS_ERR(hdmi_tx_dmactl->hw_usr_sel))
+ return -EINVAL;
+ }
+ return 0;
+
+}
+EXPORT_SYMBOL(lpass_hdmi_init_bitfields);
+
+static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ unsigned int ret;
+ unsigned int bitwidth;
+ unsigned int word_length;
+ unsigned int ch_sts_buf0;
+ unsigned int ch_sts_buf1;
+ unsigned int data_format;
+ unsigned int sampling_freq;
+ unsigned int ch = 0;
+
+ bitwidth = snd_pcm_format_width(format);
+ if (bitwidth < 0) {
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return bitwidth;
+ }
+
+ switch (bitwidth) {
+ case 16:
+ word_length = LPASS_DP_AUDIO_BITWIDTH16;
+ break;
+ case 24:
+ word_length = LPASS_DP_AUDIO_BITWIDTH24;
+ break;
+ default:
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 32000:
+ sampling_freq = LPASS_SAMPLING_FREQ32;
+ break;
+ case 44100:
+ sampling_freq = LPASS_SAMPLING_FREQ44;
+ break;
+ case 48000:
+ sampling_freq = LPASS_SAMPLING_FREQ48;
+ break;
+
+ default:
+ dev_err(dai->dev, "%s invalid bit width given : %d\n",
+ __func__, bitwidth);
+ return -EINVAL;
+ }
+ data_format = LPASS_DATA_FORMAT_LINEAR;
+ ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK)
+ | ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK));
+ ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK;
+
+ ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to softreset enable : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to softreset disable : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->legacy->legacy_en,
+ LPASS_HDMITX_LEGACY_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to legacy_en field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->tx_parity->calc_en,
+ HDMITX_PARITY_CALC_EN);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to tx_parity field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit,
+ REPLACE_VBIT);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to replace vbit field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream,
+ LINEAR_PCM_DATA);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to vbit stream field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->ch_msb[0]->msb_bits, ch_sts_buf1);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to ch_sts_buf1 field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->ch_lsb[0]->lsb_bits, ch_sts_buf0);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to ch_sts_buf0 field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs,
+ HW_MODE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to use_hw_chs field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel,
+ SW_MODE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to hw_chs_sel field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr,
+ HW_MODE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to use_hw_usr field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel,
+ SW_MODE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to hw_usr_sel field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->mute,
+ LPASS_MUTE_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to mute field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->as_sdp_cc,
+ channels - 1);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to as_sdp_cc field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->as_sdp_ct,
+ LPASS_META_DEFAULT_VAL);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to as_sdp_ct field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->aif_db4,
+ LPASS_META_DEFAULT_VAL);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to aif_db4 field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->frequency, sampling_freq);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to frequency field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->mst_index,
+ LPASS_META_DEFAULT_VAL);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to mst_index : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->dptx_index,
+ LPASS_META_DEFAULT_VAL);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dptx_index field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->sstream_en,
+ LPASS_SSTREAM_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to sstream_en field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->dma_sel, ch);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dma_sel field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->auto_bbit_en,
+ LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to auto_bbit_en field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->layout,
+ LPASS_SSTREAM_DEFAULT_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to layout field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->layout_sp,
+ LPASS_LAYOUT_SP_DEFAULT);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to layout_sp field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->dp_audio,
+ LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dp_audio field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->set_sp_on_en,
+ LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to set_sp_on_en field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->dp_sp_b_hw_en,
+ LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dp_sp_b_hw_en field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->dp_staffing_en,
+ LPASS_SSTREAM_DEFAULT_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dp_staffing_en field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+
+
+static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = regmap_field_write(drvdata->sstream_ctl->sstream_en,
+ LPASS_SSTREAM_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to sstream_en field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->mute,
+ LPASS_MUTE_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to mute field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int lpass_hdmi_daiops_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+ int ret = -EINVAL;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+
+ ret = regmap_field_write(drvdata->sstream_ctl->sstream_en,
+ LPASS_SSTREAM_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to sstream_en field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->mute,
+ LPASS_MUTE_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to mute field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+
+ ret = regmap_field_write(drvdata->sstream_ctl->sstream_en,
+ LPASS_SSTREAM_DISABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to sstream_en field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->meta_ctl->mute,
+ LPASS_MUTE_ENABLE);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to mute field : %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(drvdata->sstream_ctl->dp_audio, 0);
+ if (ret) {
+ dev_err(dai->dev, "%s error writing to dp_audio field: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
+ }
+ return ret;
+}
+
+const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = {
+ .hw_params = lpass_hdmi_daiops_hw_params,
+ .prepare = lpass_hdmi_daiops_prepare,
+ .trigger = lpass_hdmi_daiops_trigger,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops);
+
+
+
+static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
+ return true;
+
+ for (i = 0; i < v->rdma_channels; i++) {
+ if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
+ return true;
+ }
+
+ if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_DP_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
+ return true;
+
+ if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v))
+ return true;
+
+ for (i = 0; i < v->rdma_channels; ++i) {
+ if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
+ return true;
+
+ }
+ return false;
+}
+
+static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg)
+{
+ struct lpass_data *drvdata = dev_get_drvdata(dev);
+ struct lpass_variant *v = drvdata->variant;
+ int i;
+
+ if (reg == LPASS_HDMI_TX_CTL_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v))
+ return true;
+
+ for (i = 0; i < v->rdma_channels; i++) {
+ if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i))
+ return true;
+ if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i))
+ return true;
+ }
+
+ if (reg == LPASS_HDMI_TX_PARITY_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_DP_ADDR(v))
+ return true;
+ if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v))
+ return true;
+
+ if (reg == LPASS_HDMITX_APP_IRQEN_REG(v))
+ return true;
+ if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v))
+ return true;
+
+ for (i = 0; i < v->rdma_channels; ++i) {
+ if (reg == LPAIF_HDMI_RDMACTL_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABASE_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMAPER_REG(v, i))
+ return true;
+ if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
+ return true;
+ }
+
+ return false;
+}
+
+static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ return true;
+
+}
+struct regmap_config lpass_hdmi_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .writeable_reg = lpass_hdmi_regmap_writeable,
+ .readable_reg = lpass_hdmi_regmap_readable,
+ .volatile_reg = lpass_hdmi_regmap_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+EXPORT_SYMBOL(lpass_hdmi_regmap_config);
+
+MODULE_DESCRIPTION("QTi LPASS HDMI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-hdmi.h b/sound/soc/qcom/lpass-hdmi.h
new file mode 100644
index 0000000..f91f322
--- /dev/null
+++ b/sound/soc/qcom/lpass-hdmi.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ *
+ * lpass_hdmi.h - Definitions for the QTi LPASS HDMI
+ */
+
+#ifndef __LPASS_HDMI_H__
+#define __LPASS_HDMI_H__
+
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define LPASS_HDMITX_LEGACY_DISABLE 0x0
+#define LPASS_HDMITX_LEGACY_ENABLE 0x1
+#define LPASS_DP_AUDIO_BITWIDTH16 0x0
+#define LPASS_DP_AUDIO_BITWIDTH24 0xb
+#define LPASS_DATA_FORMAT_SHIFT 0x1
+#define LPASS_DATA_FORMAT_MASK 0x2
+#define LPASS_FREQ_BIT_SHIFT 24
+#define LPASS_FREQ_BIT_MASK 0xf000000
+#define LPASS_DATA_FORMAT_LINEAR 0x0
+#define LPASS_DATA_FORMAT_NON_LINEAR 0x1
+#define LPASS_SAMPLING_FREQ32 0x3
+#define LPASS_SAMPLING_FREQ44 0x0
+#define LPASS_SAMPLING_FREQ48 0x2
+#define LPASS_TX_CTL_RESET 0x1
+#define LPASS_TX_CTL_CLEAR 0x0
+#define LPASS_SSTREAM_ENABLE 1
+#define LPASS_SSTREAM_DISABLE 0
+#define LPASS_LAYOUT_SP_DEFAULT 0xf
+#define LPASS_SSTREAM_DEFAULT_ENABLE 1
+#define LPASS_SSTREAM_DEFAULT_DISABLE 0
+#define LPASS_WORDLENGTH_MASK 0xf
+#define LPASS_MUTE_ENABLE 1
+#define LPASS_MUTE_DISABLE 0
+#define LPASS_META_DEFAULT_VAL 0
+#define HW_MODE 1
+#define SW_MODE 0
+#define LEGACY_LPASS_LPAIF 1
+#define LEGACY_LPASS_HDMI 0
+#define REPLACE_VBIT 0x1
+#define LINEAR_PCM_DATA 0x0
+#define NON_LINEAR_PCM_DATA 0x1
+#define HDMITX_PARITY_CALC_EN 0x1
+#define HDMITX_PARITY_CALC_DIS 0x0
+
+
+#define LPASS_HDMI_TX_CTL_ADDR(v) \
+ (v->hdmi_tx_ctl_addr)
+
+#define LPASS_HDMI_TX_LEGACY_ADDR(v) \
+ (v->hdmi_legacy_addr)
+#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) \
+ (v->hdmi_vbit_addr)
+#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \
+ (v->hdmi_ch_lsb_addr + \
+ v->ch_stride * (port))
+#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \
+ (v->hdmi_ch_msb_addr + \
+ v->ch_stride * (port))
+#define LPASS_HDMI_TX_DMA_ADDR(v, port) \
+ (v->hdmi_dmactl_addr + \
+ v->hdmi_dma_stride * (port))
+#define LPASS_HDMI_TX_PARITY_ADDR(v) \
+ (v->hdmi_parity_addr)
+#define LPASS_HDMI_TX_DP_ADDR(v) \
+ (v->hdmi_DP_addr)
+#define LPASS_HDMI_TX_SSTREAM_ADDR(v) \
+ (v->hdmi_sstream_addr)
+
+struct lpass_sstream_ctl {
+ struct regmap_field *sstream_en;
+ struct regmap_field *dma_sel;
+ struct regmap_field *auto_bbit_en;
+ struct regmap_field *layout;
+ struct regmap_field *layout_sp;
+ struct regmap_field *set_sp_on_en;
+ struct regmap_field *dp_audio;
+ struct regmap_field *dp_staffing_en;
+ struct regmap_field *dp_sp_b_hw_en;
+};
+
+struct lpass_dp_metadata_ctl {
+ struct regmap_field *mute;
+ struct regmap_field *as_sdp_cc;
+ struct regmap_field *as_sdp_ct;
+ struct regmap_field *aif_db4;
+ struct regmap_field *frequency;
+ struct regmap_field *mst_index;
+ struct regmap_field *dptx_index;
+};
+
+struct lpass_hdmi_tx_ctl {
+ struct regmap_field *soft_reset;
+ struct regmap_field *force_reset;
+};
+
+struct lpass_hdmitx_dmactl {
+ struct regmap_field *use_hw_chs;
+ struct regmap_field *use_hw_usr;
+ struct regmap_field *hw_chs_sel;
+ struct regmap_field *hw_usr_sel;
+};
+
+struct lpass_vbit_ctrl {
+ struct regmap_field *replace_vbit;
+ struct regmap_field *vbit_stream;
+};
+
+struct lpass_hdmitx_legacy {
+ struct regmap_field *legacy_en;
+};
+
+struct lpass_hdmi_tx_parity {
+ struct regmap_field *calc_en;
+};
+
+struct lpass_hdmi_tx_ch_lsb {
+ struct regmap_field *lsb_bits;
+};
+
+struct lpass_hdmi_tx_ch_msb {
+ struct regmap_field *msb_bits;
+};
+
+#endif /* __LPASS_HDMI_H__ */
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index 5258e60..ecf9be1 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -70,6 +70,29 @@
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
+
+#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \
+ ((v->irq_reg_base) + (addr))
+
+#define LPASS_HDMITX_APP_IRQEN_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x4)
+#define LPASS_HDMITX_APP_IRQSTAT_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x8)
+#define LPASS_HDMITX_APP_IRQCLEAR_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0xC)
+
+#define IRQ_EN(v, port)\
+ ((v->dai_driver->id == HDMI) ? \
+ LPASS_HDMITX_APP_IRQEN_REG(v) : \
+ LPAIF_IRQEN_REG(v, port))
+
+#define IRQ_STAT(v, port)\
+ ((v->dai_driver->id == HDMI) ? \
+ LPASS_HDMITX_APP_IRQSTAT_REG(v) : \
+ LPAIF_IRQSTAT_REG(v, port))
+
+#define IRQ_CLEAR(v, port)\
+ ((v->dai_driver->id == HDMI) ? \
+ LPASS_HDMITX_APP_IRQCLEAR_REG(v) : \
+ LPAIF_IRQCLEAR_REG(v, port))
+
#define LPAIF_IRQ_BITSTRIDE 3
#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
@@ -77,8 +100,22 @@
#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+#define LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) (1 << (14 + chan))
+#define LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan) (1 << (24 + chan))
+#define LPAIF_IRQ_HDMI_METADONE BIT(23)
/* LPAIF DMA */
+#define LPAIF_HDMI_RDMA_REG_ADDR(v, addr, chan) \
+ (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
+
+#define LPAIF_HDMI_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_HDMI_RDMACTL_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_HDMI_RDMABASE_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x04, (chan))
+#define LPAIF_HDMI_RDMABUFF_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_HDMI_RDMACURR_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define LPAIF_HDMI_RDMAPER_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x10, (chan))
+#define LPAIF_HDMI_RDMAPERCNT_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
@@ -103,10 +140,15 @@
#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+#define LPAIF_INTFDMA_REG(v, chan, reg) \
+ ((v->dai_driver->id == HDMI) ? \
+ LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \
+ LPAIF_RDMA##reg##_REG(v, chan))
+
#define __LPAIF_DMA_REG(v, chan, dir, reg) \
- (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
- LPAIF_RDMA##reg##_REG(v, chan) : \
- LPAIF_WRDMA##reg##_REG(v, chan)
+ ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
+ (LPAIF_INTFDMA_REG(v, chan, reg)) : \
+ LPAIF_WRDMA##reg##_REG(v, chan))
#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL)
#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE)
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index df692ed..3422fa4 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -23,7 +23,7 @@ struct lpass_pcm_data {
int i2s_port;
};
-#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
+#define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024)
#define LPASS_PLATFORM_PERIODS 2
static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
@@ -56,44 +56,73 @@ static int lpass_platform_alloc_dmactl_fields(struct device *dev,
struct lpass_data *drvdata = dev_get_drvdata(dev);
struct lpass_variant *v = drvdata->variant;
struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
+ unsigned int dai_id = v->dai_driver->id;
+
drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
GFP_KERNEL);
if (drvdata->rd_dmactl == NULL)
return -ENOMEM;
- drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
- GFP_KERNEL);
- if (drvdata->wr_dmactl == NULL)
- return -ENOMEM;
-
rd_dmactl = drvdata->rd_dmactl;
- wr_dmactl = drvdata->wr_dmactl;
rd_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->rdma_bursten);
rd_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->rdma_wpscnt);
rd_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->rdma_fifowm);
- rd_dmactl->intf = devm_regmap_field_alloc(dev, map, v->rdma_intf);
rd_dmactl->enable = devm_regmap_field_alloc(dev, map, v->rdma_enable);
rd_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->rdma_dyncclk);
if (IS_ERR(rd_dmactl->bursten) || IS_ERR(rd_dmactl->wpscnt) ||
- IS_ERR(rd_dmactl->fifowm) || IS_ERR(rd_dmactl->intf) ||
- IS_ERR(rd_dmactl->enable) || IS_ERR(rd_dmactl->dyncclk))
+ IS_ERR(rd_dmactl->fifowm) || IS_ERR(rd_dmactl->dyncclk) ||
+ IS_ERR(rd_dmactl->enable))
return -EINVAL;
- wr_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->wrdma_bursten);
- wr_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->wrdma_wpscnt);
- wr_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->wrdma_fifowm);
- wr_dmactl->intf = devm_regmap_field_alloc(dev, map, v->wrdma_intf);
- wr_dmactl->enable = devm_regmap_field_alloc(dev, map, v->wrdma_enable);
- wr_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->wrdma_dyncclk);
+ switch (dai_id) {
+ case HDMI:
+ rd_dmactl->burst8 = devm_regmap_field_alloc(dev, map, v->rdma_burst8);
+ rd_dmactl->burst16 = devm_regmap_field_alloc(dev, map, v->rdma_burst16);
+ rd_dmactl->dynburst = devm_regmap_field_alloc(dev, map, v->rdma_dynburst);
- if (IS_ERR(wr_dmactl->bursten) || IS_ERR(wr_dmactl->wpscnt) ||
- IS_ERR(wr_dmactl->fifowm) || IS_ERR(wr_dmactl->intf) ||
- IS_ERR(wr_dmactl->enable) || IS_ERR(wr_dmactl->dyncclk))
- return -EINVAL;
+ if (IS_ERR(rd_dmactl->burst8) || IS_ERR(rd_dmactl->burst16) ||
+ IS_ERR(rd_dmactl->dynburst))
+ return -EINVAL;
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ rd_dmactl->intf = devm_regmap_field_alloc(dev, map, v->rdma_intf);
+ if (IS_ERR(rd_dmactl->intf))
+ return -EINVAL;
+ drvdata->wr_dmactl = devm_kzalloc(dev,
+ sizeof(struct lpaif_dmactl), GFP_KERNEL);
+ if (drvdata->wr_dmactl == NULL)
+ return -ENOMEM;
+
+ wr_dmactl = drvdata->wr_dmactl;
+
+ wr_dmactl->bursten = devm_regmap_field_alloc(dev, map,
+ v->wrdma_bursten);
+ wr_dmactl->wpscnt = devm_regmap_field_alloc(dev, map,
+ v->wrdma_wpscnt);
+ wr_dmactl->fifowm = devm_regmap_field_alloc(dev, map,
+ v->wrdma_fifowm);
+ wr_dmactl->intf = devm_regmap_field_alloc(dev, map,
+ v->wrdma_intf);
+ wr_dmactl->enable = devm_regmap_field_alloc(dev, map,
+ v->wrdma_enable);
+ wr_dmactl->dyncclk = devm_regmap_field_alloc(dev, map,
+ v->wrdma_dyncclk);
+
+ if (IS_ERR(wr_dmactl->bursten) || IS_ERR(wr_dmactl->wpscnt) ||
+ IS_ERR(wr_dmactl->fifowm) || IS_ERR(wr_dmactl->intf) ||
+ IS_ERR(wr_dmactl->enable) || IS_ERR(wr_dmactl->dyncclk))
+ return -EINVAL;
+ break;
+ default:
+ dev_err(dev, "%s: alloc dma channels failed for %d interface\n",
+ __func__, dai_id);
+ break;
+ }
return 0;
}
@@ -106,7 +135,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct lpass_variant *v = drvdata->variant;
int ret, dma_ch, dir = substream->stream;
- struct lpass_pcm_data *data;
+ struct lpass_pcm_data *data = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -174,6 +203,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -184,7 +214,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
struct lpaif_dmactl *dmactl;
int id, dir = substream->stream;
int bitwidth;
- int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+ int ret;
+ int dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+ unsigned int dai_id = cpu_dai->driver->id;
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
dmactl = drvdata->rd_dmactl;
@@ -207,18 +239,48 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
return ret;
}
- regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
+ ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
if (ret) {
dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret);
return ret;
}
- regmap_fields_write(dmactl->intf, id, LPAIF_DMACTL_AUDINTF(dma_port));
- if (ret) {
- dev_err(soc_runtime->dev, "error updating audintf field: %d\n", ret);
- return ret;
- }
+ switch (dai_id) {
+ case HDMI:
+ ret = regmap_fields_write(dmactl->burst8, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->burst16, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_fields_write(dmactl->dynburst, id,
+ LPAIF_DMACTL_BURSTEN_INCR4);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret);
+ return ret;
+ }
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ ret = regmap_fields_write(dmactl->intf, id,
+ LPAIF_DMACTL_AUDINTF(dma_port));
+ if (ret) {
+ dev_err(soc_runtime->dev, "error updating audio interface field: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id);
+ break;
+ }
switch (bitwidth) {
case 16:
switch (channels) {
@@ -249,16 +311,24 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
regval = LPAIF_DMACTL_WPSCNT_ONE;
break;
case 2:
- regval = LPAIF_DMACTL_WPSCNT_TWO;
+ regval = (dai_id == HDMI ?
+ LPAIF_DMACTL_WPSCNT_ONE :
+ LPAIF_DMACTL_WPSCNT_TWO);
break;
case 4:
- regval = LPAIF_DMACTL_WPSCNT_FOUR;
+ regval = (dai_id == HDMI ?
+ LPAIF_DMACTL_WPSCNT_TWO :
+ LPAIF_DMACTL_WPSCNT_FOUR);
break;
case 6:
- regval = LPAIF_DMACTL_WPSCNT_SIX;
+ regval = (dai_id == HDMI ?
+ LPAIF_DMACTL_WPSCNT_THREE :
+ LPAIF_DMACTL_WPSCNT_SIX);
break;
case 8:
- regval = LPAIF_DMACTL_WPSCNT_EIGHT;
+ regval = (dai_id == HDMI ?
+ LPAIF_DMACTL_WPSCNT_FOUR :
+ LPAIF_DMACTL_WPSCNT_EIGHT);
break;
default:
dev_err(soc_runtime->dev,
@@ -268,7 +338,8 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
}
break;
default:
- dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
+ dev_err(soc_runtime->dev,
+ "invalid PCM config given: bw=%d,ch=%u\n",
bitwidth, channels);
return -EINVAL;
}
@@ -366,6 +437,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
int cmd)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *rt = substream->runtime;
struct lpass_pcm_data *pcm_data = rt->private_data;
@@ -373,6 +445,9 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
struct lpaif_dmactl *dmactl;
int ret, ch, id;
int dir = substream->stream;
+ unsigned int reg_irqclr = 0, val_irqclr = 0;
+ unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0;
+ unsigned int dai_id = cpu_dai->driver->id;
ch = pcm_data->dma_ch;
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -387,31 +462,65 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /* clear status before enabling interrupts */
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch));
+ ret = regmap_fields_write(dmactl->enable, id,
+ LPAIF_DMACTL_ENABLE_ON);
if (ret) {
dev_err(soc_runtime->dev,
- "error writing to irqclear reg: %d\n", ret);
+ "error writing to rdmactl reg: %d\n", ret);
return ret;
}
+ switch (dai_id) {
+ case HDMI:
+ ret = regmap_fields_write(dmactl->dyncclk, id,
+ LPAIF_DMACTL_DYNCLK_ON);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
+ return ret;
+ }
+ reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
+ val_irqclr = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+
+ reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
+ val_mask = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ val_irqen = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ reg_irqclr = LPAIF_IRQCLEAR_REG(v,
+ LPAIF_IRQ_PORT_HOST);
+ val_irqclr = LPAIF_IRQ_ALL(ch);
- ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch),
- LPAIF_IRQ_ALL(ch));
+
+ reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = LPAIF_IRQ_ALL(ch);
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
+
+ ret = regmap_write(drvdata->lpaif_map, reg_irqclr, val_irqclr);
if (ret) {
dev_err(soc_runtime->dev,
- "error writing to irqen reg: %d\n", ret);
+ "error writing to irqclear reg: %d\n", ret);
return ret;
}
-
- ret = regmap_fields_write(dmactl->enable, id,
- LPAIF_DMACTL_ENABLE_ON);
+ ret = regmap_update_bits(drvdata->lpaif_map,
+ reg_irqen, val_mask, val_irqen);
if (ret) {
dev_err(soc_runtime->dev,
- "error writing to rdmactl reg: %d\n", ret);
+ "error writing to irqen reg: %d\n", ret);
return ret;
}
break;
@@ -425,10 +534,37 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
"error writing to rdmactl reg: %d\n", ret);
return ret;
}
+ switch (dai_id) {
+ case HDMI:
+ ret = regmap_fields_write(dmactl->dyncclk, id,
+ LPAIF_DMACTL_DYNCLK_OFF);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "error writing to rdmactl reg: %d\n", ret);
+ return ret;
+ }
+
+ reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
+ val_mask = (LPAIF_IRQ_ALL(ch) |
+ LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch));
+ val_irqen = 0;
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+ val_mask = LPAIF_IRQ_ALL(ch);
+ val_irqen = 0;
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ALL(ch), 0);
+ reg_irqen,
+ val_mask, val_irqen);
if (ret) {
dev_err(soc_runtime->dev,
"error writing to irqen reg: %d\n", ret);
@@ -489,14 +625,32 @@ static irqreturn_t lpass_dma_interrupt_handler(
int chan, u32 interrupts)
{
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct lpass_variant *v = drvdata->variant;
irqreturn_t ret = IRQ_NONE;
int rv;
+ unsigned int reg = 0, val = 0;
+ unsigned int dai_id = cpu_dai->driver->id;
if (interrupts & LPAIF_IRQ_PER(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_PER(chan));
+ switch (dai_id) {
+ case HDMI:
+ reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
+ val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan));
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+ val = 0;
+ break;
+ default:
+ dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
+ rv = regmap_write(drvdata->lpaif_map, reg,
+ LPAIF_IRQ_PER(chan) | val);
if (rv) {
dev_err(soc_runtime->dev,
"error writing to irqclear reg: %d\n", rv);
@@ -507,9 +661,8 @@ static irqreturn_t lpass_dma_interrupt_handler(
}
if (interrupts & LPAIF_IRQ_XRUN(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_XRUN(chan));
+ rv = regmap_write(drvdata->lpaif_map, reg,
+ LPAIF_IRQ_XRUN(chan) | val);
if (rv) {
dev_err(soc_runtime->dev,
"error writing to irqclear reg: %d\n", rv);
@@ -521,9 +674,8 @@ static irqreturn_t lpass_dma_interrupt_handler(
}
if (interrupts & LPAIF_IRQ_ERR(chan)) {
- rv = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
- LPAIF_IRQ_ERR(chan));
+ rv = regmap_write(drvdata->lpaif_map, reg,
+ LPAIF_IRQ_ERR(chan) | val);
if (rv) {
dev_err(soc_runtime->dev,
"error writing to irqclear reg: %d\n", rv);
@@ -534,6 +686,16 @@ static irqreturn_t lpass_dma_interrupt_handler(
ret = IRQ_HANDLED;
}
+ if (interrupts & val) {
+ rv = regmap_write(drvdata->lpaif_map, reg, val);
+ if (rv) {
+ dev_err(soc_runtime->dev,
+ "error writing to irqclear reg: %d\n", rv);
+ return IRQ_NONE;
+ }
+ ret = IRQ_HANDLED;
+ }
+
return ret;
}
@@ -543,9 +705,11 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
struct lpass_variant *v = drvdata->variant;
unsigned int irqs;
int rv, chan;
+ unsigned int val;
+ unsigned int dai_id = v->dai_driver->id;
rv = regmap_read(drvdata->lpaif_map,
- LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+ IRQ_STAT(v, LPAIF_IRQ_PORT_HOST), &irqs);
if (rv) {
pr_err("error reading from irqstat reg: %d\n", rv);
return IRQ_NONE;
@@ -553,7 +717,22 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
/* Handle per channel interrupts */
for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
- if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+ switch (dai_id) {
+ case HDMI:
+ val = LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) |
+ LPAIF_IRQ_HDMI_METADONE |
+ LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan);
+ break;
+ case MI2S_PRIMARY:
+ case MI2S_SECONDARY:
+ val = 0;
+ break;
+ default:
+ pr_err("%s: invalid %d interface\n", __func__, dai_id);
+ return -EINVAL;
+ }
+ if (irqs & (LPAIF_IRQ_ALL(chan) | val)
+ && drvdata->substream[chan]) {
rv = lpass_dma_interrupt_handler(
drvdata->substream[chan],
drvdata, chan, irqs);
@@ -644,15 +823,14 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+ IRQ_EN(v, LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
return ret;
}
ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
- lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
- "lpass-irq-lpaif", drvdata);
+ lpass_platform_lpaif_irq, 0, pdev->name, drvdata);
if (ret) {
dev_err(&pdev->dev, "irq request failed: %d\n", ret);
return ret;
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 51c9991..3c2a64a 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -12,10 +12,14 @@
#include <linux/compiler.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <dt-bindings/sound/sc7180-lpass.h>
#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
#define LPASS_MAX_MI2S_PORTS (8)
#define LPASS_MAX_DMA_CHANNELS (8)
+#define LPASS_MAX_HDMI_DMA_CHANNELS (4)
+
+
struct lpaif_i2sctl {
struct regmap_field *loopback;
@@ -32,6 +36,9 @@ struct lpaif_i2sctl {
struct lpaif_dmactl {
struct regmap_field *bursten;
+ struct regmap_field *burst8;
+ struct regmap_field *burst16;
+ struct regmap_field *dynburst;
struct regmap_field *wpscnt;
struct regmap_field *intf;
struct regmap_field *fifowm;
@@ -81,6 +88,17 @@ struct lpass_data {
struct lpaif_i2sctl *i2sctl;
struct lpaif_dmactl *rd_dmactl;
struct lpaif_dmactl *wr_dmactl;
+
+ /* Regmap fields of HDMI_CTRL registers*/
+ struct lpass_hdmi_tx_ctl *tx_ctl;
+ struct lpass_hdmitx_legacy *legacy;
+ struct lpass_vbit_ctrl *vbit_ctl;
+ struct lpass_hdmi_tx_ch_msb *ch_msb[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct lpass_hdmi_tx_ch_lsb *ch_lsb[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct lpass_hdmi_tx_parity *tx_parity;
+ struct lpass_hdmitx_dmactl *hdmi_tx_dmactl[LPASS_MAX_HDMI_DMA_CHANNELS];
+ struct lpass_dp_metadata_ctl *meta_ctl;
+ struct lpass_sstream_ctl *sstream_ctl;
};
/* Vairant data per each SOC */
@@ -98,6 +116,19 @@ struct lpass_variant {
u32 wrdma_reg_stride;
u32 wrdma_channels;
+ /* HDMI specific controls */
+ u32 hdmi_tx_ctl_addr;
+ u32 hdmi_legacy_addr;
+ u32 hdmi_vbit_addr;
+ u32 hdmi_ch_lsb_addr;
+ u32 hdmi_ch_msb_addr;
+ u32 ch_stride;
+ u32 hdmi_parity_addr;
+ u32 hdmi_dmactl_addr;
+ u32 hdmi_dma_stride;
+ u32 hdmi_DP_addr;
+ u32 hdmi_sstream_addr;
+
/* I2SCTL Register fields */
struct reg_field loopback;
struct reg_field spken;
@@ -111,6 +142,9 @@ struct lpass_variant {
/* RD_DMA Register fields */
struct reg_field rdma_bursten;
+ struct reg_field rdma_burst8;
+ struct reg_field rdma_burst16;
+ struct reg_field rdma_dynburst;
struct reg_field rdma_wpscnt;
struct reg_field rdma_intf;
struct reg_field rdma_fifowm;
@@ -125,6 +159,52 @@ struct lpass_variant {
struct reg_field wrdma_enable;
struct reg_field wrdma_dyncclk;
+ /* HDMI SSTREAM CTRL fields */
+ struct reg_field sstream_en;
+ struct reg_field dma_sel;
+ struct reg_field auto_bbit_en;
+ struct reg_field layout;
+ struct reg_field layout_sp;
+ struct reg_field set_sp_on_en;
+ struct reg_field dp_audio;
+ struct reg_field dp_staffing_en;
+ struct reg_field dp_sp_b_hw_en;
+
+ /* HDMI DP METADATA CTL fields */
+ struct reg_field mute;
+ struct reg_field as_sdp_cc;
+ struct reg_field as_sdp_ct;
+ struct reg_field aif_db4;
+ struct reg_field frequency;
+ struct reg_field mst_index;
+ struct reg_field dptx_index;
+
+ /* HDMI TX CTRL fields */
+ struct reg_field soft_reset;
+ struct reg_field force_reset;
+
+ /* HDMI TX DMA CTRL */
+ struct reg_field use_hw_chs;
+ struct reg_field use_hw_usr;
+ struct reg_field hw_chs_sel;
+ struct reg_field hw_usr_sel;
+
+ /* HDMI VBIT CTRL */
+ struct reg_field replace_vbit;
+ struct reg_field vbit_stream;
+
+ /* HDMI TX LEGACY */
+ struct reg_field legacy_en;
+
+ /* HDMI TX PARITY */
+ struct reg_field calc_en;
+
+ /* HDMI CH LSB */
+ struct reg_field lsb_bits;
+
+ /* HDMI CH MSB */
+ struct reg_field msb_bits;
+
/**
* on SOCs like APQ8016 the channel control bits start
* at different offset to ipq806x
@@ -154,5 +234,7 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
-
+extern const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops;
+struct regmap_config lpass_hdmi_regmap_config;
+extern int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map);
#endif /* __LPASS_H__ */
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
1
0
08 Sep '20
From: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
Add support for audio playback over DP in lpass sc7180 platform driver.
Add lpass_variant structure for hdmi data configuaration.
Signed-off-by: Srinivasa Rao <srivasam(a)codeaurora.org>
Signed-off-by: V Sujith Kumar Reddy <vsujithk(a)codeaurora.org>
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla(a)linaro.org>
---
sound/soc/qcom/lpass-sc7180.c | 119 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 167bf2c..34db061 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -62,6 +62,24 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
},
};
+static struct snd_soc_dai_driver sc7180_lpass_cpu_hdmi_dai_driver[] = {
+ [0] = {
+ .id = HDMI,
+ .name = "Hdmi",
+ .playback = {
+ .stream_name = "Hdmi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &asoc_qcom_lpass_hdmi_dai_ops,
+ },
+};
+
static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
int direction)
{
@@ -88,13 +106,37 @@ static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
return chan;
}
+static int sc7180_lpass_alloc_hdmi_dma_channel(struct lpass_data *drvdata,
+ int direction)
+{
+ struct lpass_variant *v = drvdata->variant;
+ int chan = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+ v->rdma_channels);
+
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+ }
+ set_bit(chan, &drvdata->dma_ch_bit_map);
+
+ return chan;
+}
static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
{
clear_bit(chan, &drvdata->dma_ch_bit_map);
return 0;
}
+static int sc7180_lpass_free_hdmi_dma_channel(struct lpass_data *drvdata, int chan)
+{
+ clear_bit(chan, &drvdata->dma_ch_bit_map);
+
+ return 0;
+}
+
static int sc7180_lpass_init(struct platform_device *pdev)
{
@@ -196,8 +238,85 @@ static struct lpass_variant sc7180_data = {
.free_dma_channel = sc7180_lpass_free_dma_channel,
};
+static const struct lpass_variant sc7180_hdmi_data = {
+ .hdmi_tx_ctl_addr = 0x1000,
+ .hdmi_legacy_addr = 0x1008,
+ .hdmi_vbit_addr = 0x610c0,
+ .hdmi_ch_lsb_addr = 0x61048,
+ .hdmi_ch_msb_addr = 0x6104c,
+ .ch_stride = 0x8,
+ .hdmi_parity_addr = 0x61034,
+ .hdmi_dmactl_addr = 0x61038,
+ .hdmi_dma_stride = 0x4,
+ .hdmi_DP_addr = 0x610c8,
+ .hdmi_sstream_addr = 0x6101c,
+ .irq_reg_base = 0x63000,
+ .irq_ports = 1,
+ .rdma_reg_base = 0x64000,
+ .rdma_reg_stride = 0x1000,
+ .rdma_channels = 4,
+
+ .rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
+ .rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
+ .rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
+ .rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
+ .rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
+ .rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
+ .rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
+ .rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
+
+ .sstream_en = REG_FIELD(0x6101c, 0, 0),
+ .dma_sel = REG_FIELD(0x6101c, 1, 2),
+ .auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
+ .layout = REG_FIELD(0x6101c, 4, 4),
+ .layout_sp = REG_FIELD(0x6101c, 5, 8),
+ .set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
+ .dp_audio = REG_FIELD(0x6101c, 11, 11),
+ .dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
+ .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
+
+ .mute = REG_FIELD(0x610c8, 0, 0),
+ .as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
+ .as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
+ .aif_db4 = REG_FIELD(0x610c8, 8, 15),
+ .frequency = REG_FIELD(0x610c8, 16, 21),
+ .mst_index = REG_FIELD(0x610c8, 28, 29),
+ .dptx_index = REG_FIELD(0x610c8, 30, 31),
+
+ .soft_reset = REG_FIELD(0x1000, 31, 31),
+ .force_reset = REG_FIELD(0x1000, 30, 30),
+
+ .use_hw_chs = REG_FIELD(0x61038, 0, 0),
+ .use_hw_usr = REG_FIELD(0x61038, 1, 1),
+ .hw_chs_sel = REG_FIELD(0x61038, 2, 4),
+ .hw_usr_sel = REG_FIELD(0x61038, 5, 6),
+
+ .replace_vbit = REG_FIELD(0x610c0, 0, 0),
+ .vbit_stream = REG_FIELD(0x610c0, 1, 1),
+
+ .legacy_en = REG_FIELD(0x1008, 0, 0),
+ .calc_en = REG_FIELD(0x61034, 0, 0),
+ .lsb_bits = REG_FIELD(0x61048, 0, 31),
+ .msb_bits = REG_FIELD(0x6104c, 0, 31),
+
+ .clk_name = (const char*[]) {
+ "pcnoc-sway-clk",
+ "audio-core",
+ "pcnoc-mport-clk",
+ },
+ .num_clks = 3,
+ .dai_driver = sc7180_lpass_cpu_hdmi_dai_driver,
+ .num_dai = ARRAY_SIZE(sc7180_lpass_cpu_hdmi_dai_driver),
+ .init = sc7180_lpass_init,
+ .exit = sc7180_lpass_exit,
+ .alloc_dma_channel = sc7180_lpass_alloc_hdmi_dma_channel,
+ .free_dma_channel = sc7180_lpass_free_hdmi_dma_channel,
+
+};
+
static const struct of_device_id sc7180_lpass_cpu_device_id[] = {
{.compatible = "qcom,sc7180-lpass-cpu", .data = &sc7180_data},
+ {.compatible = "qcom,sc7180-lpass-hdmi", .data = &sc7180_hdmi_data},
{}
};
--
Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc.,
is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
1
0