From 9f365cb8bbd0162963d6852651d7c9e30adcb7b5 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 14 May 2024 13:47:23 -0700 Subject: scsi: mpi3mr: Use proper format specifier in mpi3mr_sas_port_add() When building for a 32-bit platform such as ARM or i386, for which size_t is unsigned int, there is a warning due to using an unsigned long format specifier: drivers/scsi/mpi3mr/mpi3mr_transport.c:1370:11: error: format specifies type 'unsigned long' but the argument has type 'unsigned int' [-Werror,-Wformat] 1369 | ioc_warn(mrioc, "skipping port %u, max allowed value is %lu\n", | ~~~ | %u 1370 | i, sizeof(mr_sas_port->phy_mask) * 8); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the proper format specifier for size_t, %zu, to resolve the warning for all platforms. Fixes: 3668651def2c ("scsi: mpi3mr: Sanitise num_phys") Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20240514-mpi3mr-fix-wformat-v1-1-f1ad49217e5e@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/mpi3mr/mpi3mr_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c index 7ca9a7c2709c..5d261c2f2d20 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_transport.c +++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c @@ -1366,7 +1366,7 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, continue; if (i > sizeof(mr_sas_port->phy_mask) * 8) { - ioc_warn(mrioc, "skipping port %u, max allowed value is %lu\n", + ioc_warn(mrioc, "skipping port %u, max allowed value is %zu\n", i, sizeof(mr_sas_port->phy_mask) * 8); goto out_fail; } -- cgit v1.2.3 From 10157b1fc1a762293381e9145041253420dfc6ad Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Tue, 14 May 2024 16:03:44 +0200 Subject: scsi: core: alua: I/O errors for ALUA state transitions When a host is configured with a few LUNs and I/O is running, injecting FC faults repeatedly leads to path recovery problems. The LUNs have 4 paths each and 3 of them come back active after say an FC fault which makes 2 of the paths go down, instead of all 4. This happens after several iterations of continuous FC faults. Reason here is that we're returning an I/O error whenever we're encountering sense code 06/04/0a (LOGICAL UNIT NOT ACCESSIBLE, ASYMMETRIC ACCESS STATE TRANSITION) instead of retrying. [mwilck: The original patch was developed by Rajashekhar M A and Hannes Reinecke. I moved the code to alua_check_sense() as suggested by Mike Christie [1]. Evan Milne had raised the question whether pg->state should be set to transitioning in the UA case [2]. I believe that doing this is correct. SCSI_ACCESS_STATE_TRANSITIONING by itself doesn't cause I/O errors. Our handler schedules an RTPG, which will only result in an I/O error condition if the transitioning timeout expires.] [1] https://lore.kernel.org/all/0bc96e82-fdda-4187-148d-5b34f81d4942@oracle.com/ [2] https://lore.kernel.org/all/CAGtn9r=kicnTDE2o7Gt5Y=yoidHYD7tG8XdMHEBJTBraVEoOCw@mail.gmail.com/ Co-developed-by: Rajashekhar M A Co-developed-by: Hannes Reinecke Signed-off-by: Hannes Reinecke Signed-off-by: Martin Wilck Link: https://lore.kernel.org/r/20240514140344.19538-1-mwilck@suse.com Reviewed-by: Damien Le Moal Reviewed-by: Christoph Hellwig Reviewed-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/scsi/device_handler/scsi_dh_alua.c | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index a226dc1b65d7..4eb0837298d4 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -414,28 +414,40 @@ static char print_alua_state(unsigned char state) } } -static enum scsi_disposition alua_check_sense(struct scsi_device *sdev, - struct scsi_sense_hdr *sense_hdr) +static void alua_handle_state_transition(struct scsi_device *sdev) { struct alua_dh_data *h = sdev->handler_data; struct alua_port_group *pg; + rcu_read_lock(); + pg = rcu_dereference(h->pg); + if (pg) + pg->state = SCSI_ACCESS_STATE_TRANSITIONING; + rcu_read_unlock(); + alua_check(sdev, false); +} + +static enum scsi_disposition alua_check_sense(struct scsi_device *sdev, + struct scsi_sense_hdr *sense_hdr) +{ switch (sense_hdr->sense_key) { case NOT_READY: if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { /* * LUN Not Accessible - ALUA state transition */ - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (pg) - pg->state = SCSI_ACCESS_STATE_TRANSITIONING; - rcu_read_unlock(); - alua_check(sdev, false); + alua_handle_state_transition(sdev); return NEEDS_RETRY; } break; case UNIT_ATTENTION: + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { + /* + * LUN Not Accessible - ALUA state transition + */ + alua_handle_state_transition(sdev); + return NEEDS_RETRY; + } if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) { /* * Power On, Reset, or Bus Device Reset. @@ -502,7 +514,8 @@ static int alua_tur(struct scsi_device *sdev) retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ, ALUA_FAILOVER_RETRIES, &sense_hdr); - if (sense_hdr.sense_key == NOT_READY && + if ((sense_hdr.sense_key == NOT_READY || + sense_hdr.sense_key == UNIT_ATTENTION) && sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) return SCSI_DH_RETRY; else if (retval) -- cgit v1.2.3 From 9fad9d560af5c654bb38e0b07ee54a4e9acdc5cd Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Wed, 8 May 2024 17:22:51 +0000 Subject: scsi: sr: Fix unintentional arithmetic wraparound Running syzkaller with the newly reintroduced signed integer overflow sanitizer produces this report: [ 65.194362] ------------[ cut here ]------------ [ 65.197752] UBSAN: signed-integer-overflow in ../drivers/scsi/sr_ioctl.c:436:9 [ 65.203607] -2147483648 * 177 cannot be represented in type 'int' [ 65.207911] CPU: 2 PID: 10416 Comm: syz-executor.1 Not tainted 6.8.0-rc2-00035-gb3ef86b5a957 #1 [ 65.213585] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 65.219923] Call Trace: [ 65.221556] [ 65.223029] dump_stack_lvl+0x93/0xd0 [ 65.225573] handle_overflow+0x171/0x1b0 [ 65.228219] sr_select_speed+0xeb/0xf0 [ 65.230786] ? __pm_runtime_resume+0xe6/0x130 [ 65.233606] sr_block_ioctl+0x15d/0x1d0 ... Historically, the signed integer overflow sanitizer did not work in the kernel due to its interaction with `-fwrapv` but this has since been changed [1] in the newest version of Clang. It was re-enabled in the kernel with Commit 557f8c582a9b ("ubsan: Reintroduce signed overflow sanitizer"). Firstly, let's change the type of "speed" to unsigned long as sr_select_speed()'s only caller passes in an unsigned long anyways. $ git grep '\.select_speed' | drivers/scsi/sr.c: .select_speed = sr_select_speed, ... | static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi, | unsigned long arg) | { | ... | return cdi->ops->select_speed(cdi, arg); | } Next, let's add an extra check to make sure we don't exceed 0xffff/177 (350) since 0xffff is the max speed. This has two benefits: 1) we deal with integer overflow before it happens and 2) we properly respect the max speed of 0xffff. There are some "magic" numbers here but I did not want to change more than what was necessary. Link: https://github.com/llvm/llvm-project/pull/82432 [1] Closes: https://github.com/KSPP/linux/issues/357 Cc: linux-hardening@vger.kernel.org Signed-off-by: Justin Stitt Link: https://lore.kernel.org/r/20240508-b4-b4-sio-sr_select_speed-v2-1-00b68f724290@google.com Reviewed-by: Kees Cook Signed-off-by: Martin K. Petersen --- Documentation/cdrom/cdrom-standard.rst | 4 ++-- drivers/scsi/sr.h | 2 +- drivers/scsi/sr_ioctl.c | 5 ++++- include/linux/cdrom.h | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/cdrom/cdrom-standard.rst b/Documentation/cdrom/cdrom-standard.rst index 7964fe134277..6c1303cff159 100644 --- a/Documentation/cdrom/cdrom-standard.rst +++ b/Documentation/cdrom/cdrom-standard.rst @@ -217,7 +217,7 @@ current *struct* is:: int (*media_changed)(struct cdrom_device_info *, int); int (*tray_move)(struct cdrom_device_info *, int); int (*lock_door)(struct cdrom_device_info *, int); - int (*select_speed)(struct cdrom_device_info *, int); + int (*select_speed)(struct cdrom_device_info *, unsigned long); int (*get_last_session) (struct cdrom_device_info *, struct cdrom_multisession *); int (*get_mcn)(struct cdrom_device_info *, struct cdrom_mcn *); @@ -396,7 +396,7 @@ action need be taken, and the return value should be 0. :: - int select_speed(struct cdrom_device_info *cdi, int speed) + int select_speed(struct cdrom_device_info *cdi, unsigned long speed) Some CD-ROM drives are capable of changing their head-speed. There are several reasons for changing the speed of a CD-ROM drive. Badly diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index 1175f2e213b5..dc899277b3a4 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -65,7 +65,7 @@ int sr_disk_status(struct cdrom_device_info *); int sr_get_last_session(struct cdrom_device_info *, struct cdrom_multisession *); int sr_get_mcn(struct cdrom_device_info *, struct cdrom_mcn *); int sr_reset(struct cdrom_device_info *); -int sr_select_speed(struct cdrom_device_info *cdi, int speed); +int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed); int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *); int sr_is_xa(Scsi_CD *); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 5b0b35e60e61..a0d2556a27bb 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -425,11 +425,14 @@ int sr_reset(struct cdrom_device_info *cdi) return 0; } -int sr_select_speed(struct cdrom_device_info *cdi, int speed) +int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed) { Scsi_CD *cd = cdi->handle; struct packet_command cgc; + /* avoid exceeding the max speed or overflowing integer bounds */ + speed = clamp(0, speed, 0xffff / 177); + if (speed == 0) speed = 0xffff; /* set to max */ else diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 98c6fd0b39b6..fdfb61ccf55a 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -77,7 +77,7 @@ struct cdrom_device_ops { unsigned int clearing, int slot); int (*tray_move) (struct cdrom_device_info *, int); int (*lock_door) (struct cdrom_device_info *, int); - int (*select_speed) (struct cdrom_device_info *, int); + int (*select_speed) (struct cdrom_device_info *, unsigned long); int (*get_last_session) (struct cdrom_device_info *, struct cdrom_multisession *); int (*get_mcn) (struct cdrom_device_info *, -- cgit v1.2.3 From 51071f0831ea975fc045526dd7e17efe669dc6e1 Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Wed, 15 May 2024 14:40:59 +0530 Subject: scsi: qedf: Don't process stag work during unload and recovery Stag work can cause issues during unload and recovery, hence don't process it. Signed-off-by: Saurav Kashyap Signed-off-by: Nilesh Javali Link: https://lore.kernel.org/r/20240515091101.18754-2-skashyap@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index fd12439cbaab..c457caae07b3 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3997,6 +3997,22 @@ void qedf_stag_change_work(struct work_struct *work) struct qedf_ctx *qedf = container_of(work, struct qedf_ctx, stag_work.work); + if (!qedf) { + QEDF_ERR(&qedf->dbg_ctx, "qedf is NULL"); + return; + } + + if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, + "Already is in recovery, hence not calling software context reset.\n"); + return; + } + + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n"); + return; + } + printk_ratelimited("[%s]:[%s:%d]:%d: Performing software context reset.", dev_name(&qedf->pdev->dev), __func__, __LINE__, qedf->dbg_ctx.host_no); -- cgit v1.2.3 From 78e88472b60936025b83eba57cffa59d3501dc07 Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Wed, 15 May 2024 14:41:00 +0530 Subject: scsi: qedf: Wait for stag work during unload If stag work is already scheduled and unload is called, it can lead to issues as unload cleans up the work element. Wait for stag work to get completed before cleanup during unload. Signed-off-by: Saurav Kashyap Signed-off-by: Nilesh Javali Link: https://lore.kernel.org/r/20240515091101.18754-3-skashyap@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf.h | 1 + drivers/scsi/qedf/qedf_main.c | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 5058e01b65a2..98afdfe63600 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -363,6 +363,7 @@ struct qedf_ctx { #define QEDF_IN_RECOVERY 5 #define QEDF_DBG_STOP_IO 6 #define QEDF_PROBING 8 +#define QEDF_STAG_IN_PROGRESS 9 unsigned long flags; /* Miscellaneous state flags */ int fipvlan_retries; u8 num_queues; diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index c457caae07b3..eca0190d9a82 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -318,11 +318,18 @@ static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did, */ if (resp == fc_lport_flogi_resp) { qedf->flogi_cnt++; + qedf->flogi_pending++; + + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n"); + qedf->flogi_pending = 0; + } + if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) { schedule_delayed_work(&qedf->stag_work, 2); return NULL; } - qedf->flogi_pending++; + return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp, arg, timeout); } @@ -912,13 +919,14 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) struct qedf_ctx *qedf; struct qed_link_output if_link; + qedf = lport_priv(lport); + if (lport->vport) { + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); printk_ratelimited("Cannot issue host reset on NPIV port.\n"); return; } - qedf = lport_priv(lport); - qedf->flogi_pending = 0; /* For host reset, essentially do a soft link up/down */ atomic_set(&qedf->link_state, QEDF_LINK_DOWN); @@ -938,6 +946,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) if (!if_link.link_up) { QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Physical link is not up.\n"); + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); return; } /* Flush and wait to make sure link down is processed */ @@ -950,6 +959,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) "Queue link up work.\n"); queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 0); + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); } /* Reset the host by gracefully logging out and then logging back in */ @@ -3721,6 +3731,7 @@ static void __qedf_remove(struct pci_dev *pdev, int mode) { struct qedf_ctx *qedf; int rc; + int cnt = 0; if (!pdev) { QEDF_ERR(NULL, "pdev is NULL.\n"); @@ -3738,6 +3749,17 @@ static void __qedf_remove(struct pci_dev *pdev, int mode) return; } +stag_in_prog: + if (test_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Stag in progress, cnt=%d.\n", cnt); + cnt++; + + if (cnt < 5) { + msleep(500); + goto stag_in_prog; + } + } + if (mode != QEDF_MODE_RECOVERY) set_bit(QEDF_UNLOADING, &qedf->flags); @@ -4013,6 +4035,8 @@ void qedf_stag_change_work(struct work_struct *work) return; } + set_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); + printk_ratelimited("[%s]:[%s:%d]:%d: Performing software context reset.", dev_name(&qedf->pdev->dev), __func__, __LINE__, qedf->dbg_ctx.host_no); -- cgit v1.2.3 From 6c3bb589debd763dc4b94803ddf3c13b4fcca776 Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Wed, 15 May 2024 14:41:01 +0530 Subject: scsi: qedf: Set qed_slowpath_params to zero before use Zero qed_slowpath_params before use. Signed-off-by: Saurav Kashyap Signed-off-by: Nilesh Javali Link: https://lore.kernel.org/r/20240515091101.18754-4-skashyap@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index eca0190d9a82..49adddf978cc 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3473,6 +3473,7 @@ retry_probe: } /* Start the Slowpath-process */ + memset(&slowpath_params, 0, sizeof(struct qed_slowpath_params)); slowpath_params.int_mode = QED_INT_MODE_MSIX; slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER; slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER; -- cgit v1.2.3 From e4f5f8298cf6ddae43210d236ad65ac2c6379559 Mon Sep 17 00:00:00 2001 From: Deming Wang Date: Mon, 13 May 2024 07:59:56 -0400 Subject: scsi: mpt3sas: Add missing kerneldoc parameter descriptions Add missing kerneldoc parameter descriptions to _scsih_set_debug_level(). Signed-off-by: Deming Wang Link: https://lore.kernel.org/r/20240513115956.1576-1-wangdeming@inspur.com Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 89ef43a5ef86..12d08d8ba538 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -302,8 +302,8 @@ struct _scsi_io_transfer { /** * _scsih_set_debug_level - global setting of ioc->logging_level. - * @val: ? - * @kp: ? + * @val: value of the parameter to be set + * @kp: pointer to kernel_param structure * * Note: The logging levels are defined in mpt3sas_debug.h. */ -- cgit v1.2.3 From 637c435f08ea7e77c53a2ad590b651d0de225e3b Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 16 May 2024 11:30:41 +0300 Subject: wifi: ath11k: Fix error path in ath11k_pcic_ext_irq_config If one of the dummy allocation fails in ath11k_pcic_ext_irq_config(), the previous allocated devices might leak due to returning without deallocating the devices. Instead of returning on the error path, deallocate all the previously allocated net_devices and then return. Fixes: bca592ead825 ("wifi: ath11k: allocate dummy net_device dynamically") Signed-off-by: Breno Leitao Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://msgid.link/20240508185902.70975-1-leitao@debian.org --- drivers/net/wireless/ath/ath11k/pcic.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 79eb3f9c902f..debe7c5919ef 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -561,6 +561,7 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) { int i, j, n, ret, num_vectors = 0; u32 user_base_data = 0, base_vector = 0; + struct ath11k_ext_irq_grp *irq_grp; unsigned long irq_flags; ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, @@ -574,14 +575,16 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) irq_flags |= IRQF_NOBALANCING; for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + irq_grp = &ab->ext_irq_grp[i]; u32 num_irq = 0; irq_grp->ab = ab; irq_grp->grp_id = i; irq_grp->napi_ndev = alloc_netdev_dummy(0); - if (!irq_grp->napi_ndev) - return -ENOMEM; + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + goto fail_allocate; + } netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath11k_pcic_ext_grp_napi_poll); @@ -606,11 +609,8 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) int irq = ath11k_pcic_get_msi_irq(ab, vector); if (irq < 0) { - for (n = 0; n <= i; n++) { - irq_grp = &ab->ext_irq_grp[n]; - free_netdev(irq_grp->napi_ndev); - } - return irq; + ret = irq; + goto fail_irq; } ab->irq_num[irq_idx] = irq; @@ -635,6 +635,15 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) } return 0; +fail_irq: + /* i ->napi_ndev was properly allocated. Free it also */ + i += 1; +fail_allocate: + for (n = 0; n < i; n++) { + irq_grp = &ab->ext_irq_grp[n]; + free_netdev(irq_grp->napi_ndev); + } + return ret; } int ath11k_pcic_config_irq(struct ath11k_base *ab) -- cgit v1.2.3 From 21ae74e1bf18331ae5e279bd96304b3630828009 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 17 May 2024 10:00:28 +0300 Subject: wifi: ath10k: fix QCOM_RPROC_COMMON dependency If ath10k_snoc is built-in, while Qualcomm remoteprocs are built as modules, compilation fails with: /usr/bin/aarch64-linux-gnu-ld: drivers/net/wireless/ath/ath10k/snoc.o: in function `ath10k_modem_init': drivers/net/wireless/ath/ath10k/snoc.c:1534: undefined reference to `qcom_register_ssr_notifier' /usr/bin/aarch64-linux-gnu-ld: drivers/net/wireless/ath/ath10k/snoc.o: in function `ath10k_modem_deinit': drivers/net/wireless/ath/ath10k/snoc.c:1551: undefined reference to `qcom_unregister_ssr_notifier' Add corresponding dependency to ATH10K_SNOC Kconfig entry so that it's built as module if QCOM_RPROC_COMMON is built as module too. Fixes: 747ff7d3d742 ("ath10k: Don't always treat modem stop events as crashes") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Baryshkov Signed-off-by: Kalle Valo Link: https://msgid.link/20240511-ath10k-snoc-dep-v1-1-9666e3af5c27@linaro.org --- drivers/net/wireless/ath/ath10k/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index e6ea884cafc1..4f385f4a8cef 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -45,6 +45,7 @@ config ATH10K_SNOC depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_SMEM + depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n select QCOM_SCM select QCOM_QMI_HELPERS help -- cgit v1.2.3 From 5134acb15d9ef27aa2b90aad46d4e89fcef79fdc Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 22 May 2024 10:32:43 -0700 Subject: efi/libstub: zboot.lds: Discard .discard sections When building ARCH=loongarch defconfig + CONFIG_UNWINDER_ORC=y using LLVM, there is a warning from ld.lld when linking the EFI zboot image due to the use of unreachable() in number() in vsprintf.c: ld.lld: warning: drivers/firmware/efi/libstub/lib.a(vsprintf.stub.o):(.discard.unreachable+0x0): has non-ABS relocation R_LARCH_32_PCREL against symbol '' If the compiler cannot eliminate the default case for any reason, the .discard.unreachable section will remain in the final binary but the entire point of any section prefixed with .discard is that it is only used at compile time, so it can be discarded via /DISCARD/ in a linker script. The asm-generic vmlinux.lds.h includes .discard and .discard.* in the COMMON_DISCARDS macro but that is not used for zboot.lds, as it is not a kernel image linker script. Add .discard and .discard.* to /DISCARD/ in zboot.lds, so that any sections meant to be discarded at link time are not included in the final zboot image. This issue is not specific to LoongArch, it is just the first architecture to select CONFIG_OBJTOOL, which defines annotate_unreachable() as an asm statement to add the .discard.unreachable section, and use the EFI stub. Closes: https://github.com/ClangBuiltLinux/linux/issues/2023 Signed-off-by: Nathan Chancellor Acked-by: Huacai Chen Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/zboot.lds | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index ac8c0ef85158..af2c82f7bd90 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -41,6 +41,7 @@ SECTIONS } /DISCARD/ : { + *(.discard .discard.*) *(.modinfo .init.modinfo) } } -- cgit v1.2.3 From 7c23b186ab892088f76a3ad9dbff1685ffe2e832 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Sun, 19 May 2024 13:33:53 -0300 Subject: efi: pstore: Return proper errors on UEFI failures Right now efi-pstore either returns 0 (success) or -EIO; but we do have a function to convert UEFI errors in different standard error codes, helping to narrow down potential issues more accurately. So, let's use this helper here. Signed-off-by: Guilherme G. Piccoli Reviewed-by: Kees Cook Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-pstore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 5b9dc26e6bcb..552c78f5f059 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -136,7 +136,7 @@ static int efi_pstore_read_func(struct pstore_record *record, &size, record->buf); if (status != EFI_SUCCESS) { kfree(record->buf); - return -EIO; + return efi_status_to_err(status); } /* @@ -189,7 +189,7 @@ static ssize_t efi_pstore_read(struct pstore_record *record) return 0; if (status != EFI_SUCCESS) - return -EIO; + return efi_status_to_err(status); /* skip variables that don't concern us */ if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID)) @@ -227,7 +227,7 @@ static int efi_pstore_write(struct pstore_record *record) record->size, record->psi->buf, true); efivar_unlock(); - return status == EFI_SUCCESS ? 0 : -EIO; + return efi_status_to_err(status); }; static int efi_pstore_erase(struct pstore_record *record) @@ -238,7 +238,7 @@ static int efi_pstore_erase(struct pstore_record *record) PSTORE_EFI_ATTRIBUTES, 0, NULL); if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) - return -EIO; + return efi_status_to_err(status); return 0; } -- cgit v1.2.3 From e6722ea6b9ed731f7392277d76ca912dfffca7ee Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 6 Jan 2024 13:48:25 +0100 Subject: i2c: synquacer: Remove a clk reference from struct synquacer_i2c 'pclk' is only used locally in the probe. Remove it from the 'synquacer_i2c' structure. Also remove a useless debug message. Signed-off-by: Christophe JAILLET Acked-by: Ard Biesheuvel Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-synquacer.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 31ecb2c7e978..4eccbcd0fbfc 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -138,7 +138,6 @@ struct synquacer_i2c { int irq; struct device *dev; void __iomem *base; - struct clk *pclk; u32 pclkrate; u32 speed_khz; u32 timeout_ms; @@ -535,6 +534,7 @@ static const struct i2c_adapter synquacer_i2c_ops = { static int synquacer_i2c_probe(struct platform_device *pdev) { struct synquacer_i2c *i2c; + struct clk *pclk; u32 bus_speed; int ret; @@ -550,13 +550,12 @@ static int synquacer_i2c_probe(struct platform_device *pdev) device_property_read_u32(&pdev->dev, "socionext,pclk-rate", &i2c->pclkrate); - i2c->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); - if (IS_ERR(i2c->pclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk), + pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pclk), "failed to get and enable clock\n"); - dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk); - i2c->pclkrate = clk_get_rate(i2c->pclk); + i2c->pclkrate = clk_get_rate(pclk); if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE || i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) -- cgit v1.2.3 From e61bcf42d290e73025bab38e0e55a5586c2d8ad5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 15 Apr 2024 22:50:27 +0200 Subject: i2c: Remove I2C_CLASS_SPD Remove this class after all users have been gone. Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti --- include/linux/i2c.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 5e6cd43a6dbd..9709537370ee 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -852,7 +852,6 @@ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) /* i2c adapter classes (bitmask) */ #define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ -#define I2C_CLASS_SPD (1<<7) /* Memory modules */ /* Warn users that the adapter doesn't support classes anymore */ #define I2C_CLASS_DEPRECATED (1<<8) -- cgit v1.2.3 From 2360497238261f17d4a3f6cbc02d6dbd8951c23c Mon Sep 17 00:00:00 2001 From: Zhang Lixu Date: Thu, 23 May 2024 09:14:01 +0800 Subject: HID: intel-ish-hid: Fix build error for COMPILE_TEST kernel test robot reported build error due to a pointer type mismatch: .../ishtp/loader.c:172:8: error: incompatible pointer types passing '__le64 *' (aka 'unsigned long long *') to parameter of type 'dma_addr_t *' (aka 'unsigned int *') The issue arises because the driver, which is primarily intended for x86-64, is also built for i386 when COMPILE_TEST is enabled. Resolve type mismatch by using a temporary dma_addr_t variable to hold the DMA address. Populate this temporary variable in dma_alloc_coherent() function, and then convert and store the address in the fragment->fragment_tbl[i].ddr_adrs field in the correct format. Similarly, convert the ddr_adrs field back to dma_addr_t when freeing the DMA buffer with dma_free_coherent(). Fixes: 579a267e4617 ("HID: intel-ish-hid: Implement loading firmware from host feature") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202405201313.SAStVPrT-lkp@intel.com/ Signed-off-by: Zhang Lixu Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/loader.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-hid/ishtp/loader.c index 993f8b390e57..2785b04a2f5a 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.c +++ b/drivers/hid/intel-ish-hid/ishtp/loader.c @@ -138,12 +138,13 @@ static void release_dma_bufs(struct ishtp_device *dev, struct loader_xfer_dma_fragment *fragment, void **dma_bufs, u32 fragment_size) { + dma_addr_t dma_addr; int i; for (i = 0; i < FRAGMENT_MAX_NUM; i++) { if (dma_bufs[i]) { - dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], - fragment->fragment_tbl[i].ddr_adrs); + dma_addr = le64_to_cpu(fragment->fragment_tbl[i].ddr_adrs); + dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], dma_addr); dma_bufs[i] = NULL; } } @@ -164,15 +165,16 @@ static int prepare_dma_bufs(struct ishtp_device *dev, struct loader_xfer_dma_fragment *fragment, void **dma_bufs, u32 fragment_size) { + dma_addr_t dma_addr; u32 offset = 0; int i; for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) { - dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, - &fragment->fragment_tbl[i].ddr_adrs, GFP_KERNEL); + dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, &dma_addr, GFP_KERNEL); if (!dma_bufs[i]) return -ENOMEM; + fragment->fragment_tbl[i].ddr_adrs = cpu_to_le64(dma_addr); fragment->fragment_tbl[i].length = clamp(ish_fw->size - offset, 0, fragment_size); fragment->fragment_tbl[i].fw_off = offset; memcpy(dma_bufs[i], ish_fw->data + offset, fragment->fragment_tbl[i].length); -- cgit v1.2.3 From 0a3f9f7fc59feb8a91a2793b8b60977895c72365 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Wed, 15 May 2024 11:30:51 +0800 Subject: HID: nvidia-shield: Add missing check for input_ff_create_memless Add check for the return value of input_ff_create_memless() and return the error if it fails in order to catch the error. Fixes: 09308562d4af ("HID: nvidia-shield: Initial driver implementation with Thunderstrike support") Signed-off-by: Chen Ni Reviewed-by: Rahul Rameshbabu Signed-off-by: Jiri Kosina --- drivers/hid/hid-nvidia-shield.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c index 58b15750dbb0..ff9078ad1961 100644 --- a/drivers/hid/hid-nvidia-shield.c +++ b/drivers/hid/hid-nvidia-shield.c @@ -283,7 +283,9 @@ static struct input_dev *shield_haptics_create( return haptics; input_set_capability(haptics, EV_FF, FF_RUMBLE); - input_ff_create_memless(haptics, NULL, play_effect); + ret = input_ff_create_memless(haptics, NULL, play_effect); + if (ret) + goto err; ret = input_register_device(haptics); if (ret) -- cgit v1.2.3 From ed281c6ab6eb8a914f06c74dfeaebde15b34a3f4 Mon Sep 17 00:00:00 2001 From: Carl Huang Date: Tue, 21 May 2024 11:08:10 +0300 Subject: wifi: ath11k: fix WCN6750 firmware crash caused by 17 num_vdevs WCN6750 firmware crashes because of num_vdevs changed from 4 to 17 in ath11k_init_wmi_config_qca6390() as the ab->hw_params.num_vdevs is 17. This is caused by commit f019f4dff2e4 ("wifi: ath11k: support 2 station interfaces") which assigns ab->hw_params.num_vdevs directly to config->num_vdevs in ath11k_init_wmi_config_qca6390(), therefore WCN6750 firmware crashes as it can't support such a big num_vdevs. Fix it by assign 3 to num_vdevs in hw_params for WCN6750 as 3 is sufficient too. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01371-QCAMSLSWPLZ-1 Fixes: f019f4dff2e4 ("wifi: ath11k: support 2 station interfaces") Reported-by: Luca Weiss Tested-by: Luca Weiss Closes: https://lore.kernel.org/r/D15TIIDIIESY.D1EKKJLZINMA@fairphone.com/ Signed-off-by: Carl Huang Signed-off-by: Kalle Valo Link: https://msgid.link/20240520030757.2209395-1-quic_cjhuang@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 3cc817a3b4a4..b82e8fb28541 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -604,7 +604,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = true, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 16 + 1, + .num_vdevs = 3, .num_peers = 512, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), -- cgit v1.2.3 From 6e16782d6b4a724f9c9dcd49471219643593b60c Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 21 May 2024 11:08:11 +0300 Subject: wifi: ath11k: move power type check to ASSOC stage when connecting to 6 GHz AP With commit bc8a0fac8677 ("wifi: mac80211: don't set bss_conf in parsing") ath11k fails to connect to 6 GHz AP. This is because currently ath11k checks AP's power type in ath11k_mac_op_assign_vif_chanctx() which would be called in AUTH stage. However with above commit power type is not available until ASSOC stage. As a result power type check fails and therefore connection fails. Fix this by moving power type check to ASSOC stage, also move regulatory rules update there because it depends on power type. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Fixes: bc8a0fac8677 ("wifi: mac80211: don't set bss_conf in parsing") Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240424064019.4847-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 4f62e38ba48b..9b96dbb21d83 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7988,8 +7988,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); int ret; - struct cur_regulatory_info *reg_info; - enum ieee80211_ap_reg_power power_type; mutex_lock(&ar->conf_mutex); @@ -8000,17 +7998,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, if (ath11k_wmi_supports_6ghz_cc_ext(ar) && ctx->def.chan->band == NL80211_BAND_6GHZ && arvif->vdev_type == WMI_VDEV_TYPE_STA) { - reg_info = &ab->reg_info_store[ar->pdev_idx]; - power_type = vif->bss_conf.power_type; - - ath11k_dbg(ab, ATH11K_DBG_MAC, "chanctx power type %d\n", power_type); - - if (power_type == IEEE80211_REG_UNSET_AP) { - ret = -EINVAL; - goto out; - } - - ath11k_reg_handle_chan_list(ab, reg_info, power_type); arvif->chanctx = *ctx; ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); } @@ -9626,6 +9613,8 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + enum ieee80211_ap_reg_power power_type; + struct cur_regulatory_info *reg_info; struct ath11k_peer *peer; int ret = 0; @@ -9705,6 +9694,29 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", sta->addr, arvif->vdev_id, ret); } + + if (!ret && + ath11k_wmi_supports_6ghz_cc_ext(ar) && + arvif->vdev_type == WMI_VDEV_TYPE_STA && + arvif->chanctx.def.chan && + arvif->chanctx.def.chan->band == NL80211_BAND_6GHZ) { + reg_info = &ar->ab->reg_info_store[ar->pdev_idx]; + power_type = vif->bss_conf.power_type; + + if (power_type == IEEE80211_REG_UNSET_AP) { + ath11k_warn(ar->ab, "invalid power type %d\n", + power_type); + ret = -EINVAL; + } else { + ret = ath11k_reg_handle_chan_list(ar->ab, + reg_info, + power_type); + if (ret) + ath11k_warn(ar->ab, + "failed to handle chan list with power type %d\n", + power_type); + } + } } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { spin_lock_bh(&ar->ab->base_lock); -- cgit v1.2.3 From 803482f472ccc9576c0e606143725d1d8c61019d Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 17 May 2024 17:04:22 -0700 Subject: KVM: x86/mmu: Use SHADOW_NONPRESENT_VALUE for atomic zap in TDP MMU Use SHADOW_NONPRESENT_VALUE when zapping TDP MMU SPTEs with mmu_lock held for read, tdp_mmu_zap_spte_atomic() was simply missed during the initial development. Fixes: 7f01cab84928 ("KVM: x86/mmu: Allow non-zero value for non-present SPTE and removed SPTE") Signed-off-by: Isaku Yamahata [sean: write changelog] Signed-off-by: Sean Christopherson Reviewed-by: Kai Huang Message-ID: <20240518000430.1118488-2-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/tdp_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 1259dd63defc..36539c1b36cd 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -626,7 +626,7 @@ static inline int tdp_mmu_zap_spte_atomic(struct kvm *kvm, * SPTEs. */ handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte, - 0, iter->level, true); + SHADOW_NONPRESENT_VALUE, iter->level, true); return 0; } -- cgit v1.2.3 From 40e8a6901a2c983e3e541a363865e02cf591d458 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:28 -0700 Subject: KVM: VMX: Don't kill the VM on an unexpected #VE Don't terminate the VM on an unexpected #VE, as it's extremely unlikely the #VE is fatal to the guest, and even less likely that it presents a danger to the host. Simply resume the guest on "failure", as the #VE info page's BUSY field will prevent converting any more EPT Violations to #VEs for the vCPU (at least, that's what the BUSY field is supposed to do). Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-8-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 51b2cd13250a..069fbbc1e04a 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5217,8 +5217,8 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) if (is_invalid_opcode(intr_info)) return handle_ud(vcpu); - if (KVM_BUG_ON(is_ve_fault(intr_info), vcpu->kvm)) - return -EIO; + if (WARN_ON_ONCE(is_ve_fault(intr_info))) + return 1; error_code = 0; if (intr_info & INTR_INFO_DELIVER_CODE_MASK) -- cgit v1.2.3 From d1b32ecdc8ad0346ac551866d9f6831995fd70de Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:23 -0700 Subject: KVM: nVMX: Initialize #VE info page for vmcs02 when proving #VE support Point vmcs02.VE_INFORMATION_ADDRESS at the vCPU's #VE info page when initializing vmcs02, otherwise KVM will run L2 with EPT Violation #VE enabled and a VE info address pointing at pfn 0. Fixes: 8131cf5b4fd8 ("KVM: VMX: Introduce test mode related to EPT violation VE") Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-3-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index d5b832126e34..6798fadaa335 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2242,6 +2242,9 @@ static void prepare_vmcs02_constant_state(struct vcpu_vmx *vmx) vmcs_write64(EPT_POINTER, construct_eptp(&vmx->vcpu, 0, PT64_ROOT_4LEVEL)); + if (vmx->ve_info) + vmcs_write64(VE_INFORMATION_ADDRESS, __pa(vmx->ve_info)); + /* All VMFUNCs are currently emulated through L0 vmexits. */ if (cpu_has_vmx_vmfunc()) vmcs_write64(VM_FUNCTION_CONTROL, 0); -- cgit v1.2.3 From 9031b42139b9d45ef806c9a7fee166c1b6443c3c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:24 -0700 Subject: KVM: nVMX: Always handle #VEs in L0 (never forward #VEs from L2 to L1) Always handle #VEs, e.g. due to prove EPT Violation #VE failures, in L0, as KVM does not expose any #VE capabilities to L1, i.e. any and all #VEs are KVM's responsibility. Fixes: 8131cf5b4fd8 ("KVM: VMX: Introduce test mode related to EPT violation VE") Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-4-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 6798fadaa335..643935a0f70a 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -6233,6 +6233,8 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, else if (is_alignment_check(intr_info) && !vmx_guest_inject_ac(vcpu)) return true; + else if (is_ve_fault(intr_info)) + return true; return false; case EXIT_REASON_EXTERNAL_INTERRUPT: return true; -- cgit v1.2.3 From 837d557aba6b816985141ddbeb7649444ed26d3b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:25 -0700 Subject: KVM: x86/mmu: Add sanity checks that KVM doesn't create EPT #VE SPTEs Assert that KVM doesn't set a SPTE to a value that could trigger an EPT Violation #VE on a non-MMIO SPTE, e.g. to help detect bugs even without KVM_INTEL_PROVE_VE enabled, and to help debug actual #VE failures. Note, this will run afoul of TDX support, which needs to reflect emulated MMIO accesses into the guest as #VEs (which was the whole point of adding EPT Violation #VE support in KVM). The obvious fix for that is to exempt MMIO SPTEs, but that's annoyingly difficult now that is_mmio_spte() relies on a per-VM value. However, resolving that conundrum is a future problem, whereas getting KVM_INTEL_PROVE_VE healthy is a current problem. Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-5-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 3 +++ arch/x86/kvm/mmu/spte.h | 9 +++++++++ arch/x86/kvm/mmu/tdp_iter.h | 2 ++ 3 files changed, 14 insertions(+) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 662f62dfb2aa..1ae1c1328268 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -336,16 +336,19 @@ static int is_cpuid_PSE36(void) #ifdef CONFIG_X86_64 static void __set_spte(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); WRITE_ONCE(*sptep, spte); } static void __update_clear_spte_fast(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); WRITE_ONCE(*sptep, spte); } static u64 __update_clear_spte_slow(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); return xchg(sptep, spte); } diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 5dd5405fa07a..52fa004a1fbc 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -3,6 +3,8 @@ #ifndef KVM_X86_MMU_SPTE_H #define KVM_X86_MMU_SPTE_H +#include + #include "mmu.h" #include "mmu_internal.h" @@ -276,6 +278,13 @@ static inline bool is_shadow_present_pte(u64 pte) return !!(pte & SPTE_MMU_PRESENT_MASK); } +static inline bool is_ept_ve_possible(u64 spte) +{ + return (shadow_present_mask & VMX_EPT_SUPPRESS_VE_BIT) && + !(spte & VMX_EPT_SUPPRESS_VE_BIT) && + (spte & VMX_EPT_RWX_MASK) != VMX_EPT_MISCONFIG_WX_VALUE; +} + /* * Returns true if A/D bits are supported in hardware and are enabled by KVM. * When enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can diff --git a/arch/x86/kvm/mmu/tdp_iter.h b/arch/x86/kvm/mmu/tdp_iter.h index fae559559a80..2880fd392e0c 100644 --- a/arch/x86/kvm/mmu/tdp_iter.h +++ b/arch/x86/kvm/mmu/tdp_iter.h @@ -21,11 +21,13 @@ static inline u64 kvm_tdp_mmu_read_spte(tdp_ptep_t sptep) static inline u64 kvm_tdp_mmu_write_spte_atomic(tdp_ptep_t sptep, u64 new_spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(new_spte)); return xchg(rcu_dereference(sptep), new_spte); } static inline void __kvm_tdp_mmu_write_spte(tdp_ptep_t sptep, u64 new_spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(new_spte)); WRITE_ONCE(*rcu_dereference(sptep), new_spte); } -- cgit v1.2.3 From 743f1773366461cb44de297b1caf0a4292eb8fda Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:26 -0700 Subject: KVM: VMX: Dump VMCS on unexpected #VE Dump the VMCS on an unexpected #VE, otherwise it's practically impossible to figure out why the #VE occurred. Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-6-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 069fbbc1e04a..a02b2720023a 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5217,8 +5217,10 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) if (is_invalid_opcode(intr_info)) return handle_ud(vcpu); - if (WARN_ON_ONCE(is_ve_fault(intr_info))) + if (WARN_ON_ONCE(is_ve_fault(intr_info))) { + dump_vmcs(vcpu); return 1; + } error_code = 0; if (intr_info & INTR_INFO_DELIVER_CODE_MASK) -- cgit v1.2.3 From bca99c0356524a620126b789edbaf25934415467 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:27 -0700 Subject: KVM: x86/mmu: Print SPTEs on unexpected #VE Print the SPTEs that correspond to the faulting GPA on an unexpected EPT Violation #VE to help the user debug failures, e.g. to pinpoint which SPTE didn't have SUPPRESS_VE set. Opportunistically assert that the underlying exit reason was indeed an EPT Violation, as the CPU has *really* gone off the rails if a #VE occurs due to a completely unexpected exit reason. Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-7-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/mmu/mmu.c | 40 ++++++++++++++++++++++++++++++++-------- arch/x86/kvm/vmx/vmx.c | 5 +++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index ece45b3f6f20..f8ca74e7678f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2154,6 +2154,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, void *insn, int insn_len); +void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg); void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva); void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, u64 addr, unsigned long roots); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 1ae1c1328268..b7b4426a7221 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4104,23 +4104,31 @@ static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, int *root_level return leaf; } -/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */ -static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) +static int get_sptes_lockless(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, + int *root_level) { - u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; - struct rsvd_bits_validate *rsvd_check; - int root, leaf, level; - bool reserved = false; + int leaf; walk_shadow_page_lockless_begin(vcpu); if (is_tdp_mmu_active(vcpu)) - leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root); + leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, root_level); else - leaf = get_walk(vcpu, addr, sptes, &root); + leaf = get_walk(vcpu, addr, sptes, root_level); walk_shadow_page_lockless_end(vcpu); + return leaf; +} +/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */ +static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) +{ + u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; + struct rsvd_bits_validate *rsvd_check; + int root, leaf, level; + bool reserved = false; + + leaf = get_sptes_lockless(vcpu, addr, sptes, &root); if (unlikely(leaf < 0)) { *sptep = 0ull; return reserved; @@ -5924,6 +5932,22 @@ emulate: } EXPORT_SYMBOL_GPL(kvm_mmu_page_fault); +void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg) +{ + u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; + int root_level, leaf, level; + + leaf = get_sptes_lockless(vcpu, gpa, sptes, &root_level); + if (unlikely(leaf < 0)) + return; + + pr_err("%s %llx", msg, gpa); + for (level = root_level; level >= leaf; level--) + pr_cont(", spte[%d] = 0x%llx", level, sptes[level]); + pr_cont("\n"); +} +EXPORT_SYMBOL_GPL(kvm_mmu_print_sptes); + static void __kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, u64 addr, hpa_t root_hpa) { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a02b2720023a..58832aae2248 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5218,7 +5218,12 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) return handle_ud(vcpu); if (WARN_ON_ONCE(is_ve_fault(intr_info))) { + struct vmx_ve_information *ve_info = vmx->ve_info; + + WARN_ONCE(ve_info->exit_reason != EXIT_REASON_EPT_VIOLATION, + "Unexpected #VE on VM-Exit reason 0x%x", ve_info->exit_reason); dump_vmcs(vcpu); + kvm_mmu_print_sptes(vcpu, ve_info->guest_physical_address, "#VE"); return 1; } -- cgit v1.2.3 From a5dc0c9b557573315633bc78bacf8f548352f95b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:29 -0700 Subject: KVM: VMX: Enumerate EPT Violation #VE support in /proc/cpuinfo Don't suppress printing EPT_VIOLATION_VE in /proc/cpuinfo, knowing whether or not KVM_INTEL_PROVE_VE actually does anything is extremely valuable. A privileged user can get at the information by reading the raw MSR, but the whole point of the VMX flags is to avoid needing to glean information from raw MSR reads. Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-9-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/vmxfeatures.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/vmxfeatures.h b/arch/x86/include/asm/vmxfeatures.h index 266daf5b5b84..695f36664889 100644 --- a/arch/x86/include/asm/vmxfeatures.h +++ b/arch/x86/include/asm/vmxfeatures.h @@ -77,7 +77,7 @@ #define VMX_FEATURE_ENCLS_EXITING ( 2*32+ 15) /* "" VM-Exit on ENCLS (leaf dependent) */ #define VMX_FEATURE_RDSEED_EXITING ( 2*32+ 16) /* "" VM-Exit on RDSEED */ #define VMX_FEATURE_PAGE_MOD_LOGGING ( 2*32+ 17) /* "pml" Log dirty pages into buffer */ -#define VMX_FEATURE_EPT_VIOLATION_VE ( 2*32+ 18) /* "" Conditionally reflect EPT violations as #VE exceptions */ +#define VMX_FEATURE_EPT_VIOLATION_VE ( 2*32+ 18) /* Conditionally reflect EPT violations as #VE exceptions */ #define VMX_FEATURE_PT_CONCEAL_VMX ( 2*32+ 19) /* "" Suppress VMX indicators in Processor Trace */ #define VMX_FEATURE_XSAVES ( 2*32+ 20) /* "" Enable XSAVES and XRSTORS in guest */ #define VMX_FEATURE_MODE_BASED_EPT_EXEC ( 2*32+ 22) /* "ept_mode_based_exec" Enable separate EPT EXEC bits for supervisor vs. user */ -- cgit v1.2.3 From 6af6142e3a62efd6074905e4a94d64956a3f4b7c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 17:04:30 -0700 Subject: KVM: x86: Disable KVM_INTEL_PROVE_VE by default Disable KVM's "prove #VE" support by default, as it provides no functional value, and even its sanity checking benefits are relatively limited. I.e. it should be fully opt-in even on debug kernels, especially since EPT Violation #VE suppression appears to be buggy on some CPUs. Opportunistically add a line in the help text to make it abundantly clear that KVM_INTEL_PROVE_VE should never be enabled in a production environment. Suggested-by: Paolo Bonzini Signed-off-by: Sean Christopherson Message-ID: <20240518000430.1118488-10-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index d64fb2b3eb69..7f47233ddf46 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -97,15 +97,17 @@ config KVM_INTEL config KVM_INTEL_PROVE_VE bool "Check that guests do not receive #VE exceptions" - default KVM_PROVE_MMU || DEBUG_KERNEL - depends on KVM_INTEL + depends on KVM_INTEL && EXPERT help - Checks that KVM's page table management code will not incorrectly let guests receive a virtualization exception. Virtualization exceptions will be trapped by the hypervisor rather than injected in the guest. + Note: some CPUs appear to generate spurious EPT Violations #VEs + that trigger KVM's WARN, in particular with eptad=0 and/or nested + virtualization. + If unsure, say N. config X86_SGX_KVM -- cgit v1.2.3 From 76d5363c20eeeb937b56c0ac6c61e697bd1bf154 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 17 May 2024 11:03:41 -0700 Subject: KVM: x86: Force KVM_WERROR if the global WERROR is enabled Force KVM_WERROR if the global WERROR is enabled to avoid pestering the user about a Kconfig that will ultimately be ignored. Force KVM_WERROR instead of making it mutually exclusive with WERROR to avoid generating a .config builds KVM with -Werror, but has KVM_WERROR=n. Suggested-by: Linus Torvalds Signed-off-by: Sean Christopherson Message-ID: <20240517180341.974251-1-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 7f47233ddf46..fec95a770270 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -44,6 +44,7 @@ config KVM select KVM_VFIO select HAVE_KVM_PM_NOTIFIER if PM select KVM_GENERIC_HARDWARE_ENABLING + select KVM_WERROR if WERROR help Support hosting fully virtualized guest machines using hardware virtualization extensions. You will need a fairly recent @@ -66,7 +67,7 @@ config KVM_WERROR # FRAME_WARN, i.e. KVM_WERROR=y with KASAN=y requires special tuning. # Building KVM with -Werror and KASAN is still doable via enabling # the kernel-wide WERROR=y. - depends on KVM && EXPERT && !KASAN + depends on KVM && ((EXPERT && !KASAN) || WERROR) help Add -Werror to the build flags for KVM. -- cgit v1.2.3 From b4bd556467477420ee3a91fbcba73c579669edc6 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 21 May 2024 19:14:35 -0700 Subject: KVM: SVM: WARN on vNMI + NMI window iff NMIs are outright masked When requesting an NMI window, WARN on vNMI support being enabled if and only if NMIs are actually masked, i.e. if the vCPU is already handling an NMI. KVM's ABI for NMIs that arrive simultanesouly (from KVM's point of view) is to inject one NMI and pend the other. When using vNMI, KVM pends the second NMI simply by setting V_NMI_PENDING, and lets the CPU do the rest (hardware automatically sets V_NMI_BLOCKING when an NMI is injected). However, if KVM can't immediately inject an NMI, e.g. because the vCPU is in an STI shadow or is running with GIF=0, then KVM will request an NMI window and trigger the WARN (but still function correctly). Whether or not the GIF=0 case makes sense is debatable, as the intent of KVM's behavior is to provide functionality that is as close to real hardware as possible. E.g. if two NMIs are sent in quick succession, the probability of both NMIs arriving in an STI shadow is infinitesimally low on real hardware, but significantly larger in a virtual environment, e.g. if the vCPU is preempted in the STI shadow. For GIF=0, the argument isn't as clear cut, because the window where two NMIs can collide is much larger in bare metal (though still small). That said, KVM should not have divergent behavior for the GIF=0 case based on whether or not vNMI support is enabled. And KVM has allowed simultaneous NMIs with GIF=0 for over a decade, since commit 7460fb4a3400 ("KVM: Fix simultaneous NMIs"). I.e. KVM's GIF=0 handling shouldn't be modified without a *really* good reason to do so, and if KVM's behavior were to be modified, it should be done irrespective of vNMI support. Fixes: fa4c027a7956 ("KVM: x86: Add support for SVM's Virtual NMI") Cc: stable@vger.kernel.org Cc: Santosh Shukla Cc: Maxim Levitsky Signed-off-by: Sean Christopherson Message-ID: <20240522021435.1684366-1-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index c8dc25886c16..1d3c8be39479 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3846,16 +3846,27 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); /* - * KVM should never request an NMI window when vNMI is enabled, as KVM - * allows at most one to-be-injected NMI and one pending NMI, i.e. if - * two NMIs arrive simultaneously, KVM will inject one and set - * V_NMI_PENDING for the other. WARN, but continue with the standard - * single-step approach to try and salvage the pending NMI. + * If NMIs are outright masked, i.e. the vCPU is already handling an + * NMI, and KVM has not yet intercepted an IRET, then there is nothing + * more to do at this time as KVM has already enabled IRET intercepts. + * If KVM has already intercepted IRET, then single-step over the IRET, + * as NMIs aren't architecturally unmasked until the IRET completes. + * + * If vNMI is enabled, KVM should never request an NMI window if NMIs + * are masked, as KVM allows at most one to-be-injected NMI and one + * pending NMI. If two NMIs arrive simultaneously, KVM will inject one + * NMI and set V_NMI_PENDING for the other, but if and only if NMIs are + * unmasked. KVM _will_ request an NMI window in some situations, e.g. + * if the vCPU is in an STI shadow or if GIF=0, KVM can't immediately + * inject the NMI. In those situations, KVM needs to single-step over + * the STI shadow or intercept STGI. */ - WARN_ON_ONCE(is_vnmi_enabled(svm)); + if (svm_get_nmi_mask(vcpu)) { + WARN_ON_ONCE(is_vnmi_enabled(svm)); - if (svm_get_nmi_mask(vcpu) && !svm->awaiting_iret_completion) - return; /* IRET will cause a vm exit */ + if (!svm->awaiting_iret_completion) + return; /* IRET will cause a vm exit */ + } /* * SEV-ES guests are responsible for signaling when a vCPU is ready to -- cgit v1.2.3 From d09c05aa35909adb7d29f92f0cd79fdcd1338ef0 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Mon, 20 May 2024 22:30:40 -0400 Subject: scsi: core: Handle devices which return an unusually large VPD page count Peter Schneider reported that a system would no longer boot after updating to 6.8.4. Peter bisected the issue and identified commit b5fc07a5fb56 ("scsi: core: Consult supported VPD page list prior to fetching page") as being the culprit. Turns out the enclosure device in Peter's system reports a byteswapped page length for VPD page 0. It reports "02 00" as page length instead of "00 02". This causes us to attempt to access 516 bytes (page length + header) of information despite only 2 pages being present. Limit the page search scope to the size of our VPD buffer to guard against devices returning a larger page count than requested. Link: https://lore.kernel.org/r/20240521023040.2703884-1-martin.petersen@oracle.com Fixes: b5fc07a5fb56 ("scsi: core: Consult supported VPD page list prior to fetching page") Cc: stable@vger.kernel.org Reported-by: Peter Schneider Closes: https://lore.kernel.org/all/eec6ebbf-061b-4a7b-96dc-ea748aa4d035@googlemail.com/ Tested-by: Peter Schneider Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3e0c0381277a..f0464db3f9de 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -350,6 +350,13 @@ static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page) if (result < SCSI_VPD_HEADER_SIZE) return 0; + if (result > sizeof(vpd)) { + dev_warn_once(&sdev->sdev_gendev, + "%s: long VPD page 0 length: %d bytes\n", + __func__, result); + result = sizeof(vpd); + } + result -= SCSI_VPD_HEADER_SIZE; if (!memchr(&vpd[SCSI_VPD_HEADER_SIZE], page, result)) return 0; -- cgit v1.2.3 From 21d35e335a51bd0b961a4ec07f827712b781e386 Mon Sep 17 00:00:00 2001 From: Matteo Martelli Date: Wed, 22 May 2024 18:46:55 +0200 Subject: ASoC: es8311: dt-bindings: add everest es8311 codec Add DT bindings documentation for the Everest-semi ES8311 codec. Everest-semi ES8311 codec is a low-power mono audio codec with I2S audio interface and I2C control. Signed-off-by: Matteo Martelli Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240522164722.954656-2-matteomartelli3@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/everest,es8316.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml index b6079b3c440d..214f135b7777 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml @@ -4,18 +4,21 @@ $id: http://devicetree.org/schemas/sound/everest,es8316.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Everest ES8316 audio CODEC +title: Everest ES8311 and ES8316 audio CODECs maintainers: - Daniel Drake - Katsuhiro Suzuki + - Matteo Martelli allOf: - $ref: dai-common.yaml# properties: compatible: - const: everest,es8316 + enum: + - everest,es8311 + - everest,es8316 reg: maxItems: 1 -- cgit v1.2.3 From baf9899122b0e585a340e2edc685c56c60b9c23e Mon Sep 17 00:00:00 2001 From: Matteo Martelli Date: Wed, 22 May 2024 18:46:56 +0200 Subject: ASoC: codecs: es8311: add everest es8311 codec support Add support for the Everest-semi ES8311 codec. Everest-semi ES8311 codec is a low-power mono audio codec with I2S audio interface and I2C control. Supported features: * Both master and slave mode. Master clock is optional in slave mode. * Sample rates from 8KHz to 96KHz. * Sample formats: S16_LE, S18_3LE, S20_3LE, S24_3LE, S24_LE and S32_LE. * I2S formats: I2S, LEFT_J, DSP_A, DSP_B. * BCLK and FSYNC clocks inversion. * Component suspend/resume. * ADC, PGA, DAC controls. * ADC DSP controls: volume, fade (ramp rate), ALC, automute, HPF, EQ. * DAC DSP controls: volume, fade (ramp rate), DRC, EQ. * DAPM routes: capture path with input source selection (differential MIC/DMIC) and AIF channel source selection; playback path with DAC channel source selection. Limitations: * Support only for master clocks with a ratio of ADC (or DAC) clock to LRCLK equal to 256. This to keep the default ADC and DAC oversampling and ADC scale settings. Anyway all 8-96KHz sample rates are supported when the ratio of MCLK to sample rate is 32, 64, 128, 256, 384 or 512 (upper limit due to max MCLK freq of 49.2MHz). * Coefficients for ADC HPF and ADC/DAC EQ not supported. * Digital mic supported but not tested. * S18_3LE, S20_3LE and S24_3LE formats supported but not tested. Signed-off-by: Matteo Martelli Link: https://msgid.link/r/20240522164722.954656-3-matteomartelli3@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/es8311.c | 970 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/es8311.h | 162 ++++++++ 4 files changed, 1138 insertions(+) create mode 100644 sound/soc/codecs/es8311.c create mode 100644 sound/soc/codecs/es8311.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4afc43d3f71f..3936fb36d051 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1101,6 +1101,10 @@ config SND_SOC_ES83XX_DSM_COMMON depends on ACPI tristate +config SND_SOC_ES8311 + tristate "Everest Semi ES8311 CODEC" + depends on I2C + config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b4df22186e25..d784b445bb20 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -119,6 +119,7 @@ snd-soc-dmic-y := dmic.o snd-soc-es7134-y := es7134.o snd-soc-es7241-y := es7241.o snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o +snd-soc-es8311-y := es8311.o snd-soc-es8316-y := es8316.o snd-soc-es8326-y := es8326.o snd-soc-es8328-y := es8328.o @@ -516,6 +517,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o +obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c new file mode 100644 index 000000000000..b2ee2d04b0e4 --- /dev/null +++ b/sound/soc/codecs/es8311.c @@ -0,0 +1,970 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8311.c -- es8311 ALSA SoC audio driver + * + * Copyright (C) 2024 Matteo Martelli + * + * Author: Matteo Martelli + */ + +#include "linux/array_size.h" +#include "sound/pcm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "es8311.h" + +#define ES8311_NUM_RATES 10 +#define ES8311_RATES (SNDRV_PCM_RATE_8000_96000) +#define ES8311_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct es8311_priv { + struct regmap *regmap; + struct clk *mclk; + unsigned long mclk_freq; + bool provider; + unsigned int rates[ES8311_NUM_RATES]; + struct snd_pcm_hw_constraint_list constraints; +}; + +static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0); +static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0); + +#define ES8311_DB_LRCK_STEPS \ + "0.25db/4LRCK", \ + "0.25db/8LRCK", \ + "0.25db/16LRCK", \ + "0.25db/32LRCK", \ + "0.25db/64LRCK", \ + "0.25db/128LRCK", \ + "0.25db/256LRCK", \ + "0.25db/512LRCK", \ + "0.25db/1024LRCK", \ + "0.25db/2048LRCK", \ + "0.25db/4096LRCK", \ + "0.25db/8192LRCK", \ + "0.25db/16384LRCK", \ + "0.25db/32768LRCK", \ + "0.25db/65536LRCK", + +static const char *const es8311_level_winsize_txt[] = { + "0.25db/2LRCK", + ES8311_DB_LRCK_STEPS +}; + +static SOC_ENUM_SINGLE_DECL( + es8311_alc_winsize, ES8311_ADC4, + ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt); +static const DECLARE_TLV_DB_RANGE(es8311_level_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0), + 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0), + 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0), + 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0), + 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0), + 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0), +); + +static const char *const es8311_ramprate_txt[] = { + "Disabled", + ES8311_DB_LRCK_STEPS +}; +static SOC_ENUM_SINGLE_DECL( + es8311_adc_ramprate, ES8311_ADC1, + ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt); + +static const char *const es8311_automute_winsize_txt[] = { + "2048 samples", + "4096 samples", + "6144 samples", + "8192 samples", + "10240 samples", + "12288 samples", + "14336 samples", + "16384 samples", + "18432 samples", + "20480 samples", + "22528 samples", + "24576 samples", + "26624 samples", + "28672 samples", + "30720 samples", + "32768 samples", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_automute_winsize, ES8311_ADC6, + ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt); +static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0), + 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0), +); +static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0); + +static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0); +static SOC_ENUM_SINGLE_DECL( + es8311_drc_winsize, ES8311_DAC4, + ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt); +static SOC_ENUM_SINGLE_DECL( + es8311_dac_ramprate, ES8311_DAC6, + ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt); + +static const char *const es8311_out_mode_txt[] = { + "Lineout", + "Headphones" +}; +static SOC_ENUM_SINGLE_DECL( + es8311_out_mode, ES8311_SYS9, + ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt); + +static const struct snd_kcontrol_new es8311_snd_controls[] = { + /* Capture path */ + SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10, + ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0, + es8311_pga_gain_tlv), + SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2, + ES8311_ADC2_INV_SHIFT, 1, 0), + SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2, + ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0, + es8311_adc_scale_tlv), + SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3, + ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0, + es8311_adc_vol_tlv), + SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate), + SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4, + ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize), + SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6, + ES8311_ADC6_AUTOMUTE_NG_SHIFT, + ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv), + SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7, + ES8311_ADC7_AUTOMUTE_VOL_SHIFT, + ES8311_ADC7_AUTOMUTE_VOL_MAX, 0, + es8311_automute_vol_tlv), + SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT, + 1, 0), + SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8, + ES8311_ADC8_EQBYPASS_SHIFT, 1, 1), + SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT, + 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5, + ES8311_ADC5_ALC_MAXLEVEL_SHIFT, + ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5, + ES8311_ADC5_ALC_MINLEVEL_SHIFT, + ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv), + SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize), + + /* Playback path */ + SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0, + ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv), + SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT, + 1, 0), + SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5, + ES8311_DAC5_DRC_MAXLEVEL_SHIFT, + ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv), + SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5, + ES8311_DAC5_DRC_MINLEVEL_SHIFT, + ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv), + SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize), + SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate), + SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6, + ES8311_DAC6_EQBYPASS_SHIFT, 1, 1), + + SOC_ENUM("Output Mode", es8311_out_mode), +}; + +static const char *const es8311_diff_src_txt[] = { + "Disabled", + "MIC1P-MIC1N", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_diff_src_enum, ES8311_SYS10, + ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt); +static const struct snd_kcontrol_new es8311_diff_src_mux = + SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum); + +static const char *const es8311_dmic_src_txt[] = { + "Disabled", + "DMIC from MIC1P", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_dmic_src_enum, ES8311_SYS10, + ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt); +static const struct snd_kcontrol_new es8311_dmic_src_mux = + SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum); + +static const char * const es8311_aif1tx_src_txt[] = { + "ADC + ADC", + "ADC + 0", + "0 + ADC", + "0 + 0", + "DACL + ADC", + "ADC + DACR", + "DACL + DACR", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_aif1tx_src_enum, ES8311_GPIO, + ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt); +static const struct snd_kcontrol_new es8311_aif1tx_src_mux = + SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum); + +static const char * const es8311_dac_src_txt[] = { + "Left", + "Right" +}; +static SOC_ENUM_SINGLE_DECL( + es8311_dac_src_enum, ES8311_SDP_IN, + ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt); +static const struct snd_kcontrol_new es8311_dac_src_mux = + SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum); + +static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT, + 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3, + ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1, + NULL, 0), + + /* Capture path */ + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8311_diff_src_mux), + SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3, + ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3, + ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL, + 0), + SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4, + ES8311_SYS4_PDN_MOD_SHIFT, 1), + SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, + &es8311_dmic_src_mux), + SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0, + &es8311_aif1tx_src_mux), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT, + ES8311_SDP_MUTE_SHIFT, 1), + + /* Playback path */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN, + ES8311_SDP_MUTE_SHIFT, 1), + SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0, + &es8311_dac_src_mux), + SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8, + ES8311_SYS8_PDN_DAC_SHIFT, 1), + SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3, + ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route es8311_dapm_routes[] = { + /* Capture Path */ + { "MIC1", NULL, "Bias" }, + { "MIC1", NULL, "Analog power" }, + { "MIC1", NULL, "Vref" }, + { "Differential Mux", "MIC1P-MIC1N", "MIC1" }, + { "PGA", NULL, "Differential Mux" }, + { "Mono ADC", NULL, "PGA" }, + { "Mono ADC", NULL, "ADC Bias Gen" }, + { "Mono ADC", NULL, "ADC Vref Gen" }, + { "Mono ADC", NULL, "ADC Clock" }, + { "Mono ADC", NULL, "ADC Analog Clock" }, + { "Digital Mic Mux", "Disabled", "Mono ADC" }, + { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" }, + + { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" }, + + { "AIF1TX", NULL, "AIF1TX Source Mux" }, + + /* Playback Path */ + { "Mono DAC Source Mux", "Left", "AIF1RX" }, + { "Mono DAC Source Mux", "Right", "AIF1RX" }, + { "Mono DAC", NULL, "Mono DAC Source Mux" }, + { "Mono DAC", NULL, "DAC Clock" }, + { "Mono DAC", NULL, "DAC Analog Clock" }, + { "OUT", NULL, "Mono DAC" }, + { "OUT", NULL, "Bias" }, + { "OUT", NULL, "Analog power" }, + { "OUT", NULL, "Vref" }, + { "OUT", NULL, "DAC Vref Gen" }, +}; + +/* Bit clock divider values: + * from 1 to 20: the register takes the div value - 1 + * above 20: the register takes the corresponding idx of the div value + * in the following table + 20 + */ +#define ES8311_BCLK_DIV_IDX_OFFSET 20 +static const unsigned int es8311_bclk_divs[] = { + 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72 +}; + +struct es8311_mclk_coeff { + unsigned int rate; + unsigned int mclk; + unsigned int div; + unsigned int mult; + unsigned int div_adc_dac; +}; + +#define ES8311_MCLK_MAX_FREQ 49200000 + +/* Coefficients for common master clock frequencies based on clock table from + * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal + * to 256. This to keep the default adc and dac oversampling and adc scale + * settings. Internal mclk dividers and multipliers are dynamically adjusted to + * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed + * mclks frequencies (see es8311_cmp_adj_mclk_coeff). + * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512 + * (upper limit due to max mclk freq of 49.2MHz). + */ +static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = { + { 8000, 2048000, 1, 1, 1 }, + { 8000, 6144000, 3, 1, 1 }, + { 8000, 18432000, 3, 1, 3 }, + { 11025, 2822400, 1, 1, 1 }, + { 11025, 8467200, 3, 1, 1 }, + { 16000, 4096000, 1, 1, 1 }, + { 16000, 12288000, 3, 1, 1 }, + { 16000, 18432000, 3, 2, 3 }, + { 22050, 5644800, 1, 1, 1 }, + { 22050, 16934400, 3, 1, 1 }, + { 32000, 8192000, 1, 1, 1 }, + { 32000, 12288000, 3, 2, 1 }, + { 32000, 18432000, 3, 4, 3 }, + { 44100, 11289600, 1, 1, 1 }, + { 44100, 33868800, 3, 1, 1 }, + { 48000, 12288000, 1, 1, 1 }, + { 48000, 18432000, 3, 2, 1 }, + { 64000, 8192000, 1, 2, 1 }, + { 64000, 12288000, 3, 4, 1 }, + { 88200, 11289600, 1, 2, 1 }, + { 88200, 33868800, 3, 2, 1 }, + { 96000, 12288000, 1, 2, 1 }, + { 96000, 18432000, 3, 4, 1 }, +}; + +/* Compare coeff with provided mclk_freq and adjust it if needed. + * If frequencies match, return 0 and the unaltered coeff copy into out_coeff. + * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and + * the adjusted coeff copy into out_coeff. + * Return -EINVAL otherwise. + */ +static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq, + const struct es8311_mclk_coeff *coeff, + struct es8311_mclk_coeff *out_coeff) +{ + if (WARN_ON_ONCE(!coeff)) + return -EINVAL; + + unsigned int div = coeff->div; + unsigned int mult = coeff->mult; + bool match = false; + + if (coeff->mclk == mclk_freq) { + match = true; + } else if (mclk_freq % coeff->mclk == 0) { + div = mclk_freq / coeff->mclk; + div *= coeff->div; + if (div <= 8) + match = true; + } else if (coeff->mclk % mclk_freq == 0) { + mult = coeff->mclk / mclk_freq; + if (mult == 2 || mult == 4 || mult == 8) { + mult *= coeff->mult; + if (mult <= 8) + match = true; + } + } + if (!match) + return -EINVAL; + if (out_coeff) { + *out_coeff = *coeff; + out_coeff->div = div; + out_coeff->mult = mult; + } + return 0; +} + +static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate, + struct es8311_mclk_coeff *out_coeff) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) { + const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; + + if (coeff->rate != rate) + continue; + + int ret = + es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff); + if (ret == 0) + return 0; + } + return -EINVAL; +} + +static void es8311_set_sysclk_constraints(unsigned int mclk_freq, + struct es8311_priv *es8311) +{ + unsigned int count = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) && + count < ARRAY_SIZE(es8311->rates); i++) { + const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; + + if (count > 0 && coeff->rate == es8311->rates[count - 1]) + continue; + + int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL); + if (ret == 0) + es8311->rates[count++] = coeff->rate; + } + if (count) { + es8311->constraints.list = es8311->rates; + es8311->constraints.count = count; + } +} + +static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + unsigned int mask = ES8311_DAC1_DAC_DSMMUTE | + ES8311_DAC1_DAC_DEMMUTE; + unsigned int val = mute ? mask : 0; + + regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val); + } + + return 0; +} + +static int es8311_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (es8311->constraints.list) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &es8311->constraints); + } + + return 0; +} + +static int es8311_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + unsigned int wl; + int par_width = params_width(params); + + switch (par_width) { + case 16: + wl = ES8311_SDP_WL_16; + break; + case 18: + wl = ES8311_SDP_WL_18; + break; + case 20: + wl = ES8311_SDP_WL_20; + break; + case 24: + wl = ES8311_SDP_WL_24; + break; + case 32: + wl = ES8311_SDP_WL_32; + break; + default: + return -EINVAL; + } + unsigned int width = (unsigned int)par_width; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, ES8311_SDP_IN, + ES8311_SDP_WL_MASK, + wl << ES8311_SDP_WL_SHIFT); + } else { + snd_soc_component_update_bits(component, ES8311_SDP_OUT, + ES8311_SDP_WL_MASK, + wl << ES8311_SDP_WL_SHIFT); + } + + if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) { + dev_err(component->dev, "mclk frequency %lu too high\n", + es8311->mclk_freq); + return -EINVAL; + } + + unsigned int mclk_freq = es8311->mclk_freq; + unsigned int rate = params_rate(params); + unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON; + + if (!mclk_freq) { + if (es8311->provider) { + dev_err(component->dev, + "mclk not configured, cannot run as master\n"); + return -EINVAL; + } + dev_dbg(component->dev, + "mclk not configured, use bclk as internal mclk\n"); + + clkmgr = ES8311_CLKMGR1_MCLK_SEL; + + mclk_freq = rate * width * 2; + } + + struct es8311_mclk_coeff coeff; + int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff); + if (ret) { + dev_err(component->dev, "unable to find mclk coefficient\n"); + return ret; + } + + unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON | + ES8311_CLKMGR1_BCLK_ON; + + clkmgr |= ES8311_CLKMGR1_BCLK_ON; + snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr); + + if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 || + coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8)) + return -EINVAL; + + unsigned int mult; + + switch (coeff.mult) { + case 1: + mult = 0; + break; + case 2: + mult = 1; + break; + case 4: + mult = 2; + break; + case 8: + mult = 3; + break; + default: + WARN_ON_ONCE(true); + return -EINVAL; + } + + mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK; + clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT | + mult << ES8311_CLKMGR2_MULT_PRE_SHIFT; + snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr); + + mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK; + clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT | + (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT; + snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr); + + if (es8311->provider) { + unsigned int div_lrclk = mclk_freq / rate; + + if (WARN_ON_ONCE(div_lrclk == 0 || + div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1)) + return -EINVAL; + + mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK; + clkmgr = (div_lrclk - 1) >> 8; + snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask, + clkmgr); + clkmgr = (div_lrclk - 1) & 0xFF; + snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr); + + if (div_lrclk % (2 * width) != 0) { + dev_err(component->dev, + "unable to divide mclk %u to generate bclk\n", + mclk_freq); + return -EINVAL; + } + + unsigned int div_bclk = div_lrclk / (2 * width); + + mask = ES8311_CLKMGR6_DIV_BCLK_MASK; + if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) { + clkmgr = div_bclk - 1; + } else { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) { + if (es8311_bclk_divs[i] == div_bclk) + break; + } + if (i == ARRAY_SIZE(es8311_bclk_divs)) { + dev_err(component->dev, + "bclk divider %u not supported\n", + div_bclk); + return -EINVAL; + } + + clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET; + } + snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, + clkmgr); + } + + return 0; +} + +static int es8311_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (freq > ES8311_MCLK_MAX_FREQ) { + dev_err(component->dev, "invalid frequency %u: too high\n", + freq); + return -EINVAL; + } + + if (es8311->mclk_freq == freq) + return 0; + + es8311->mclk_freq = freq; + es8311->constraints.list = NULL; + es8311->constraints.count = 0; + + if (freq == 0) + return 0; + + int ret = clk_set_rate(es8311->mclk, freq); + if (ret) { + dev_err(component->dev, "unable to set mclk rate\n"); + return ret; + } + + es8311_set_sysclk_constraints(freq, es8311); + + return ret; +} + +static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + /* Master mode */ + es8311->provider = true; + + snd_soc_component_update_bits(component, ES8311_RESET, + ES8311_RESET_MSC, + ES8311_RESET_MSC); + break; + case SND_SOC_DAIFMT_CBC_CFC: + /* Slave mode */ + es8311->provider = false; + snd_soc_component_update_bits(component, ES8311_RESET, + ES8311_RESET_MSC, 0); + break; + default: + return -EINVAL; + } + + unsigned int sdp = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sdp |= ES8311_SDP_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + sdp |= ES8311_SDP_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dev_err(component->dev, "right justified mode not supported\n"); + return -EINVAL; + case SND_SOC_DAIFMT_DSP_B: + sdp |= ES8311_SDP_LRP; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + sdp |= ES8311_SDP_FMT_DSP; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + dev_err(component->dev, + "inverted fsync not supported in dsp mode\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + unsigned int clkmgr = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + sdp |= ES8311_SDP_LRP; + break; + case SND_SOC_DAIFMT_IB_NF: + clkmgr |= ES8311_CLKMGR6_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + clkmgr |= ES8311_CLKMGR6_BCLK_INV; + sdp |= ES8311_SDP_LRP; + break; + default: + return -EINVAL; + } + + unsigned int mask = ES8311_CLKMGR6_BCLK_INV; + + snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr); + + mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP; + snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp); + snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp); + + return 0; +} + +static int es8311_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + int ret = clk_prepare_enable(es8311->mclk); + if (ret) { + dev_err(component->dev, + "unable to prepare mclk\n"); + return ret; + } + + snd_soc_component_update_bits( + component, ES8311_SYS3, + ES8311_SYS3_PDN_VMIDSEL_MASK, + ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED); + } + + break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(es8311->mclk); + snd_soc_component_update_bits( + component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK, + ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN); + break; + default: + break; + } + return 0; +} + +static const struct snd_soc_dai_ops es8311_dai_ops = { + .startup = es8311_startup, + .hw_params = es8311_hw_params, + .mute_stream = es8311_mute, + .set_sysclk = es8311_set_sysclk, + .set_fmt = es8311_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver es8311_dai = { + .name = "es8311", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ES8311_RATES, + .formats = ES8311_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ES8311_RATES, + .formats = ES8311_FORMATS, + }, + .ops = &es8311_dai_ops, + .symmetric_rate = 1, +}; + +static void es8311_reset(struct snd_soc_component *component, bool reset) +{ + /* Reset procedure: + * (1) power down state machine and reset codec blocks then, + * (2) after a short delay, power up state machine and leave reset mode. + * Specific delay is not documented, using the same as es8316. + */ + unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK; + + if (reset) { + /* Enter reset mode */ + snd_soc_component_update_bits(component, ES8311_RESET, mask, + ES8311_RESET_RST_MASK); + } else { + /* Leave reset mode */ + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, ES8311_RESET, mask, + ES8311_RESET_CSM_ON); + } +} + +static int es8311_suspend(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311_reset(component, true); + + regcache_cache_only(es8311->regmap, true); + regcache_mark_dirty(es8311->regmap); + + return 0; +} + +static int es8311_resume(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311_reset(component, false); + + regcache_cache_only(es8311->regmap, false); + regcache_sync(es8311->regmap); + + return 0; +} + +static int es8311_component_probe(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(es8311->mclk)) { + dev_err(component->dev, "invalid mclk\n"); + return PTR_ERR(es8311->mclk); + } + + es8311->mclk_freq = clk_get_rate(es8311->mclk); + if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ) + es8311_set_sysclk_constraints(es8311->mclk_freq, es8311); + + es8311_reset(component, true); + es8311_reset(component, false); + + /* Set minimal power up time */ + snd_soc_component_write(component, ES8311_SYS1, 0); + snd_soc_component_write(component, ES8311_SYS2, 0); + + return 0; +} + +static const struct regmap_config es8311_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8311_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct snd_soc_component_driver es8311_component_driver = { + .probe = es8311_component_probe, + .suspend = es8311_suspend, + .resume = es8311_resume, + .set_bias_level = es8311_set_bias_level, + .controls = es8311_snd_controls, + .num_controls = ARRAY_SIZE(es8311_snd_controls), + .dapm_widgets = es8311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets), + .dapm_routes = es8311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int es8311_i2c_probe(struct i2c_client *i2c_client) +{ + struct es8311_priv *es8311; + + struct device *dev = &i2c_client->dev; + + es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL); + if (es8311 == NULL) + return -ENOMEM; + + es8311->regmap = + devm_regmap_init_i2c(i2c_client, &es8311_regmap_config); + if (IS_ERR(es8311->regmap)) + return PTR_ERR(es8311->regmap); + + i2c_set_clientdata(i2c_client, es8311); + + return devm_snd_soc_register_component(dev, &es8311_component_driver, + &es8311_dai, 1); +} + +static const struct i2c_device_id es8311_id[] = { { "es8311", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, es8311_id); + +static const struct of_device_id es8311_of_match[] = { + { + .compatible = "everest,es8311", + }, + {} +}; +MODULE_DEVICE_TABLE(of, es8311_of_match); + +static struct i2c_driver es8311_i2c_driver = { + .driver = { + .name = "es8311", + .of_match_table = es8311_of_match, + }, + .probe = es8311_i2c_probe, + .id_table = es8311_id, +}; + +module_i2c_driver(es8311_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8311 driver"); +MODULE_AUTHOR("Matteo Martelli "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8311.h b/sound/soc/codecs/es8311.h new file mode 100644 index 000000000000..8a3105bb8443 --- /dev/null +++ b/sound/soc/codecs/es8311.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * es8311.c -- es8311 ALSA SoC audio driver + * + * Copyright (C) 2024 Matteo Martelli + * + * Author: Matteo Martelli + */ + +#ifndef _ES8311_H +#define _ES8311_H + +#include + +#define ES8311_RESET 0x00 +#define ES8311_RESET_CSM_ON BIT(7) +#define ES8311_RESET_MSC BIT(6) +#define ES8311_RESET_RST_MASK GENMASK(4, 0) + +/* Clock Manager Registers */ +#define ES8311_CLKMGR1 0x01 +#define ES8311_CLKMGR1_MCLK_SEL BIT(7) +#define ES8311_CLKMGR1_MCLK_ON BIT(5) +#define ES8311_CLKMGR1_BCLK_ON BIT(4) +#define ES8311_CLKMGR1_CLKADC_ON_SHIFT 3 +#define ES8311_CLKMGR1_CLKDAC_ON_SHIFT 2 +#define ES8311_CLKMGR1_ANACLKADC_ON_SHIFT 1 +#define ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT 0 +#define ES8311_CLKMGR2 0x02 +#define ES8311_CLKMGR2_DIV_PRE_MASK GENMASK(7, 5) +#define ES8311_CLKMGR2_DIV_PRE_SHIFT 5 +#define ES8311_CLKMGR2_DIV_PRE_MAX 0x07 +#define ES8311_CLKMGR2_MULT_PRE_MASK GENMASK(4, 3) +#define ES8311_CLKMGR2_MULT_PRE_SHIFT 3 +#define ES8311_CLKMGR3 0x03 +#define ES8311_CLKMGR4 0x04 +#define ES8311_CLKMGR5 0x05 +#define ES8311_CLKMGR5_ADC_DIV_MASK GENMASK(7, 4) +#define ES8311_CLKMGR5_ADC_DIV_SHIFT 4 +#define ES8311_CLKMGR5_DAC_DIV_MASK GENMASK(3, 0) +#define ES8311_CLKMGR5_DAC_DIV_SHIFT 0 +#define ES8311_CLKMGR6 0x06 +#define ES8311_CLKMGR6_BCLK_INV BIT(5) +#define ES8311_CLKMGR6_DIV_BCLK_MASK GENMASK(4, 0) +#define ES8311_CLKMGR7 0x07 +#define ES8311_CLKMGR7_LRCLK_DIV_H_MASK GENMASK(3, 0) +#define ES8311_CLKMGR8 0x08 +#define ES8311_CLKMGR_LRCLK_DIV_MAX 0x0FFF + +/* SDP Mode Registers */ +#define ES8311_SDP_IN 0x09 +#define ES8311_SDP_IN_SEL_SHIFT 7 +#define ES8311_SDP_OUT 0x0A +/* Following values are the same for both SPD_IN and SDP_OUT */ +#define ES8311_SDP_MUTE_SHIFT 6 +#define ES8311_SDP_LRP BIT(5) +#define ES8311_SDP_WL_MASK GENMASK(4, 2) +#define ES8311_SDP_WL_SHIFT 2 +#define ES8311_SDP_WL_24 0x00 +#define ES8311_SDP_WL_20 0x01 +#define ES8311_SDP_WL_18 0x02 +#define ES8311_SDP_WL_16 0x03 +#define ES8311_SDP_WL_32 0x04 +#define ES8311_SDP_FMT_MASK GENMASK(1, 0) +#define ES8311_SDP_FMT_I2S 0x00 +#define ES8311_SDP_FMT_LEFT_J 0x01 +#define ES8311_SDP_FMT_DSP 0x03 + +/* System registers */ +#define ES8311_SYS1 0x0B +#define ES8311_SYS2 0x0C +#define ES8311_SYS3 0x0D +#define ES8311_SYS3_PDN_ANA_SHIFT 7 +#define ES8311_SYS3_PDN_IBIASGEN_SHIFT 6 +#define ES8311_SYS3_PDN_ADCBIASGEN_SHIFT 5 +#define ES8311_SYS3_PDN_ADCVREFGEN_SHIFT 4 +#define ES8311_SYS3_PDN_DACVREFGEN_SHIFT 3 +#define ES8311_SYS3_PDN_VREF_SHIFT 2 +#define ES8311_SYS3_PDN_VMIDSEL_MASK GENMASK(1, 0) +#define ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN 0 +#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED 1 +#define ES8311_SYS3_PDN_VMIDSEL_NORMAL_OPERATION 2 +#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_FAST_SPEED 3 +#define ES8311_SYS4 0x0E +#define ES8311_SYS4_PDN_PGA_SHIFT 6 +#define ES8311_SYS4_PDN_MOD_SHIFT 5 +#define ES8311_SYS5 0x0F +#define ES8311_SYS6 0x10 +#define ES8311_SYS7 0x11 +#define ES8311_SYS8 0x12 +#define ES8311_SYS8_PDN_DAC_SHIFT 1 +#define ES8311_SYS9 0x13 +#define ES8311_SYS9_HPSW_SHIFT 4 +#define ES8311_SYS10 0x14 +#define ES8311_SYS10_DMIC_ON_SHIFT 6 +#define ES8311_SYS10_LINESEL_SHIFT 4 +#define ES8311_SYS10_PGAGAIN_SHIFT 0 +#define ES8311_SYS10_PGAGAIN_MAX 0x0A + +/* ADC Registers*/ +#define ES8311_ADC1 0x15 +#define ES8311_ADC1_RAMPRATE_SHIFT 4 +#define ES8311_ADC2 0x16 +#define ES8311_ADC2_INV_SHIFT 4 +#define ES8311_ADC2_SCALE_SHIFT 0 +#define ES8311_ADC2_SCALE_MAX 0x07 +#define ES8311_ADC3 0x17 +#define ES8311_ADC3_VOLUME_SHIFT 0 +#define ES8311_ADC3_VOLUME_MAX 0xFF +#define ES8311_ADC4 0x18 +#define ES8311_ADC4_ALC_EN_SHIFT 7 +#define ES8311_ADC4_AUTOMUTE_EN_SHIFT 6 +#define ES8311_ADC4_ALC_WINSIZE_SHIFT 0 +#define ES8311_ADC5 0x19 +#define ES8311_ADC5_ALC_MAXLEVEL_SHIFT 4 +#define ES8311_ADC5_ALC_MAXLEVEL_MAX 0x0F +#define ES8311_ADC5_ALC_MINLEVEL_SHIFT 0 +#define ES8311_ADC5_ALC_MINLEVEL_MAX 0x0F +#define ES8311_ADC6 0x1A +#define ES8311_ADC6_AUTOMUTE_WS_SHIFT 4 +#define ES8311_ADC6_AUTOMUTE_NG_SHIFT 0 +#define ES8311_ADC6_AUTOMUTE_NG_MAX 0x0F + +#define ES8311_ADC7 0x1B +#define ES8311_ADC7_AUTOMUTE_VOL_SHIFT 5 +#define ES8311_ADC7_AUTOMUTE_VOL_MAX 0x07 +#define ES8311_ADC8 0x1C +#define ES8311_ADC8_EQBYPASS_SHIFT 6 +#define ES8311_ADC8_HPF_SHIFT 5 + +/* DAC Registers */ +#define ES8311_DAC1 0x31 +#define ES8311_DAC1_DAC_DSMMUTE BIT(6) +#define ES8311_DAC1_DAC_DEMMUTE BIT(5) +#define ES8311_DAC2 0x32 +#define ES8311_DAC2_VOLUME_MAX 0xFF +#define ES8311_DAC3 0x33 +#define ES8311_DAC4 0x34 +#define ES8311_DAC4_DRC_EN_SHIFT 7 +#define ES8311_DAC4_DRC_WINSIZE_SHIFT 0 +#define ES8311_DAC5 0x35 +#define ES8311_DAC5_DRC_MAXLEVEL_SHIFT 4 +#define ES8311_DAC5_DRC_MAXLEVEL_MAX 0x0F +#define ES8311_DAC5_DRC_MINLEVEL_SHIFT 0 +#define ES8311_DAC5_DRC_MINLEVEL_MAX 0x0F +#define ES8311_DAC6 0x37 +#define ES8311_DAC6_RAMPRATE_SHIFT 4 +#define ES8311_DAC6_EQBYPASS_SHIFT 3 + +/* GPIO Registers */ +#define ES8311_GPIO 0x44 +#define ES8311_GPIO_ADC2DAC_SEL_SHIFT 7 +#define ES8311_GPIO_ADCDAT_SEL_SHIFT 4 + +/* Chip Info Registers */ +#define ES8311_CHIPID1 0xFD /* 0x83 */ +#define ES8311_CHIPID2 0xFE /* 0x11 */ +#define ES8311_CHIPVER 0xFF + +#define ES8311_REG_MAX 0xFF + +#endif -- cgit v1.2.3 From 965cc040bf0698e81b0c0aef359ae650b42b428e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 May 2024 16:19:56 +0200 Subject: ASoC: Constify channel mapping array arguments in set_channel_map() There is no need for implementations of DAI set_channel_map() to modify contents of passed arrays with actual channel mapping. Additionally, the caller keeps full ownership of the array. Constify these pointer arguments so the code will be safer and easier to read (documenting the caller's ownership). Acked-by: Charles Keepax Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240520-asoc-x1e80100-4-channel-mapping-v4-1-f657159b4aad@linaro.org Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 4 ++-- include/sound/soc-dai.h | 8 ++++---- sound/soc/codecs/adau7118.c | 6 ++++-- sound/soc/codecs/cs35l41-lib.c | 4 ++-- sound/soc/codecs/cs35l41.c | 3 ++- sound/soc/codecs/max98504.c | 6 ++++-- sound/soc/codecs/wcd9335.c | 6 ++++-- sound/soc/codecs/wcd934x.c | 6 ++++-- sound/soc/qcom/qdsp6/q6afe-dai.c | 16 ++++++++++------ sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 6 ++++-- sound/soc/soc-dai.c | 4 ++-- 11 files changed, 42 insertions(+), 27 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index bb70782d15d0..43c6a9ef8d9f 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -896,8 +896,8 @@ int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap); int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); int cs35l41_set_channels(struct device *dev, struct regmap *reg, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg); void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp); int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index adcd8719d343..15ef268c9845 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -188,8 +188,8 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); @@ -305,8 +305,8 @@ struct snd_soc_dai_ops { unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); int (*set_channel_map)(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); int (*get_channel_map)(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c index a663d37e5776..abc4764697a5 100644 --- a/sound/soc/codecs/adau7118.c +++ b/sound/soc/codecs/adau7118.c @@ -121,8 +121,10 @@ static const struct snd_soc_dapm_widget adau7118_widgets[] = { }; static int adau7118_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct adau7118_data *st = snd_soc_component_get_drvdata(dai->component); diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index e9993a39f7d0..1702f26049d3 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -936,8 +936,8 @@ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsign EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); int cs35l41_set_channels(struct device *dev, struct regmap *reg, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { unsigned int val, mask; int i; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index cb25c33cc9b9..1688c2c688f0 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -673,7 +673,8 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { }; static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, - unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) + const unsigned int *tx_slot, + unsigned int rx_n, const unsigned int *rx_slot) { struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 93412b966b33..6b6a7ece4cec 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -220,8 +220,10 @@ static int max98504_set_tdm_slot(struct snd_soc_dai *dai, return 0; } static int max98504_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai); struct regmap *map = max98504->regmap; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index deb15b95992d..42a99978fe5a 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -1983,8 +1983,10 @@ static int wcd9335_trigger(struct snd_pcm_substream *substream, int cmd, } static int wcd9335_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct wcd9335_codec *wcd; int i; diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index de870c7819ca..fcad2c9fba55 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1923,8 +1923,10 @@ static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd, } static int wcd934x_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct wcd934x_codec *wcd; int i; diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index a9c4f896a7df..7d9628cda875 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -172,8 +172,8 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, } static int q6tdm_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -250,8 +250,10 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream, } static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_ch_mask, - unsigned int rx_num, unsigned int *rx_ch_mask) + unsigned int tx_num, + const unsigned int *tx_ch_mask, + unsigned int rx_num, + const unsigned int *rx_ch_mask) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -407,8 +409,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, } static int q6slim_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id]; diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 68a38f63a2db..6bfbb52345e1 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -25,8 +25,10 @@ struct q6apm_lpass_dai_data { }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_ch_mask, - unsigned int rx_num, unsigned int *rx_ch_mask) + unsigned int tx_num, + const unsigned int *tx_ch_mask, + unsigned int rx_num, + const unsigned int *rx_ch_mask) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index fefe394dce72..03afd5efb24c 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -304,8 +304,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); * configure the relationship between channel number and TDM slot number. */ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { int ret = -ENOTSUPP; -- cgit v1.2.3 From 22ad2e3c21281802cd519454544009da7e7a8ea4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 May 2024 16:19:57 +0200 Subject: ASoC: qcom: q6apm-lpass-dais: Implement proper channel mapping The set_channel_map() implementation in q6apm-lpass-dais driver was copying older pre-Audioreach code from q6afe-dai driver, but not really using it. The code sets active channel mask based on passed channel mapping, but Audioreach code does not use that mask ever. Audioreach module configuration does have on the other hand proper channel mapping field, which should supersed that active channel mask. Drop the unused active channel mask and implement proper mapping of channels in q6apm-lpass-dais driver. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240520-asoc-x1e80100-4-channel-mapping-v4-2-f657159b4aad@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.h | 1 - sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 2c82917b7162..eb9306280988 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -755,7 +755,6 @@ struct audioreach_module_config { u16 data_format; u16 num_channels; - u16 active_channels_mask; u16 dp_idx; u32 channel_allocation; u32 sd_line_mask; diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 6bfbb52345e1..a4ad1d0e6abd 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -33,7 +33,7 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; - int ch_mask; + int i; switch (dai->id) { case WSA_CODEC_DMA_TX_0: @@ -58,7 +58,8 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, tx_num); return -EINVAL; } - ch_mask = *tx_ch_mask; + for (i = 0; i < tx_num; i++) + cfg->channel_map[i] = tx_ch_mask[i]; break; case WSA_CODEC_DMA_RX_0: @@ -81,7 +82,8 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, rx_num); return -EINVAL; } - ch_mask = *rx_ch_mask; + for (i = 0; i < rx_num; i++) + cfg->channel_map[i] = rx_ch_mask[i]; break; default: @@ -90,8 +92,6 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, return -EINVAL; } - cfg->active_channels_mask = ch_mask; - return 0; } -- cgit v1.2.3 From 5d5dd9bb227a1bdbef5ec7d167ac65775d1b99dc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 May 2024 16:19:58 +0200 Subject: ASoC: qcom: qdsp6: Set channel mapping instead of fixed defaults When constructing packets to DSP, the Audioreach code uses 'struct audioreach_module_config' to configure parameters like number of channels, bitrate, sample rate etc, but uses defaults for the channel mapping. Rework this code to copy the channel mapping from 'struct audioreach_module_config', instead of using the default. This requires all callers to fill that structure: add missing initialization of channel mapping. Entire patch makes code more logical and easier to follow: 1. q6apm-dai and q6apm-lpass-dais code which allocates 'struct audioreach_module_config' initializes it fully, so fills both the number of channels and the channel mapping. 2. Audioreach code, which uses 'struct audioreach_module_config' when constructing packets, copies entire contents of passed config, not only pieces of it. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240520-asoc-x1e80100-4-channel-mapping-v4-3-f657159b4aad@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/audioreach.c | 30 +++++++----------------------- sound/soc/qcom/qdsp6/audioreach.h | 1 + sound/soc/qcom/qdsp6/q6apm-dai.c | 3 +++ sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 5 ++++- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 5291deac0a0b..4ebaaf736fb9 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -267,7 +267,7 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt); -static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels) +void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels) { if (num_channels == 1) { ch_map[0] = PCM_CHANNEL_FL; @@ -281,6 +281,7 @@ static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels) ch_map[3] = PCM_CHANNEL_RS; } } +EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping); static void apm_populate_container_config(struct apm_container_obj *cfg, struct audioreach_container *cont) @@ -819,7 +820,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, uint32_t num_channels = cfg->num_channels; int payload_size; struct gpr_pkt *pkt; - int rc; + int rc, i; void *p; payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + @@ -842,18 +843,8 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, media_format->sample_rate = cfg->sample_rate; media_format->bit_width = cfg->bit_width; media_format->num_channels = cfg->num_channels; - - if (num_channels == 1) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - } else if (num_channels == 2) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - media_format->channel_mapping[1] = PCM_CHANNEL_FR; - } else if (num_channels == 4) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - media_format->channel_mapping[1] = PCM_CHANNEL_FR; - media_format->channel_mapping[2] = PCM_CHANNEL_LS; - media_format->channel_mapping[3] = PCM_CHANNEL_RS; - } + for (i = 0; i < num_channels; i++) + media_format->channel_mapping[i] = cfg->channel_map[i]; rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -883,9 +874,6 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, mp3_cfg->q_factor = mcfg->bit_width - 1; mp3_cfg->endianness = PCM_LITTLE_ENDIAN; mp3_cfg->num_channels = mcfg->num_channels; - - audioreach_set_channel_mapping(mp3_cfg->channel_mapping, - mcfg->num_channels); break; case SND_AUDIOCODEC_AAC: media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; @@ -1104,9 +1092,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->num_channels = mcfg->num_channels; media_cfg->q_factor = mcfg->bit_width - 1; media_cfg->bits_per_sample = mcfg->bit_width; - - audioreach_set_channel_mapping(media_cfg->channel_mapping, - num_channels); + memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -1163,9 +1149,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, cfg->q_factor = mcfg->bit_width - 1; cfg->endianness = PCM_LITTLE_ENDIAN; cfg->num_channels = mcfg->num_channels; - - audioreach_set_channel_mapping(cfg->channel_mapping, - num_channels); + memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); } else { rc = audioreach_set_compr_media_format(header, p, mcfg); if (rc) { diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index eb9306280988..61a69df4f50f 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -766,6 +766,7 @@ struct audioreach_module_config { /* Packet Allocation routines */ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token); +void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels); void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode, uint32_t token, uint32_t src_port, uint32_t dest_port); diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index c7df8343b2dc..c9404b5934c7 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -239,6 +239,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.num_channels = runtime->channels; cfg.bit_width = prtd->bits_per_sample; cfg.fmt = SND_AUDIOCODEC_PCM; + audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels); if (prtd->state) { /* clear the previous setup if any */ @@ -665,6 +666,8 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component, cfg.num_channels = 2; cfg.bit_width = prtd->bits_per_sample; cfg.fmt = codec->id; + audioreach_set_default_channel_mapping(cfg.channel_map, + cfg.num_channels); memcpy(&cfg.codec, codec, sizeof(*codec)); ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index a4ad1d0e6abd..ba28ec9dff86 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -106,6 +106,7 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream, cfg->bit_width = params_width(params); cfg->sample_rate = params_rate(params); cfg->num_channels = channels; + audioreach_set_default_channel_mapping(cfg->channel_map, channels); switch (dai->id) { case DISPLAY_PORT_RX_0: @@ -130,10 +131,12 @@ static int q6dma_hw_params(struct snd_pcm_substream *substream, { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + int channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max; cfg->bit_width = params_width(params); cfg->sample_rate = params_rate(params); - cfg->num_channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max; + cfg->num_channels = channels; + audioreach_set_default_channel_mapping(cfg->channel_map, channels); return 0; } -- cgit v1.2.3 From cf03e271fdca8059090622ef27c20576341f1a75 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 May 2024 16:19:59 +0200 Subject: ASoC: qcom: x1e80100: Correct channel mapping X1E80100 CRD board comes with four speakers arranged as left front+back and then right front+back. Using default channel mapping causes front right speaker to play left back stream. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240520-asoc-x1e80100-4-channel-mapping-v4-4-f657159b4aad@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/x1e80100.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index 0e0773a85809..d7e0bd03dffd 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -12,6 +12,7 @@ #include "common.h" #include "qdsp6/q6afe.h" +#include "qdsp6/q6dsp-common.h" #include "sdw.h" struct x1e80100_snd_data { @@ -80,6 +81,23 @@ static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + const unsigned int rx_slot[4] = { PCM_CHANNEL_FL, + PCM_CHANNEL_LB, + PCM_CHANNEL_FR, + PCM_CHANNEL_RB }; + int ret; + + switch (cpu_dai->id) { + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + ARRAY_SIZE(rx_slot), rx_slot); + if (ret) + return ret; + break; + default: + break; + } return qcom_snd_sdw_prepare(substream, sruntime, &data->stream_prepared[cpu_dai->id]); -- cgit v1.2.3 From bad0a07a7e61a54969cdbb188b143e39040acf43 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 21 May 2024 16:46:25 +0800 Subject: ASoC: rt1320: Add RT1320 SDCA vendor-specific driver This is the initial amplifier driver for rt1320. -- add class id 1 Signed-off-by: Shuming Fan v2: add Capture DAI for AEC feedback v3: add some comments for blind writes/patch_code/calling sdw_slave_read_prop v4: add comments for different class id Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240521084625.453554-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 7 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt1320-sdw.c | 2260 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt1320-sdw.h | 94 ++ 4 files changed, 2363 insertions(+) create mode 100644 sound/soc/codecs/rt1320-sdw.c create mode 100644 sound/soc/codecs/rt1320-sdw.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4afc43d3f71f..7502581d1631 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -222,6 +222,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318_SDW + imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 imply SND_SOC_RTQ9128 imply SND_SOC_SDW_MOCKUP @@ -1575,6 +1576,12 @@ config SND_SOC_RT1318_SDW depends on SOUNDWIRE select REGMAP_SOUNDWIRE +config SND_SOC_RT1320_SDW + tristate "Realtek RT1320 Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT5514 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b4df22186e25..f9eb7c073a00 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -222,6 +222,7 @@ snd-soc-rt1308-y := rt1308.o snd-soc-rt1308-sdw-y := rt1308-sdw.o snd-soc-rt1316-sdw-y := rt1316-sdw.o snd-soc-rt1318-sdw-y := rt1318-sdw.o +snd-soc-rt1320-sdw-y := rt1320-sdw.o snd-soc-rt274-y := rt274.o snd-soc-rt286-y := rt286.o snd-soc-rt298-y := rt298.o @@ -614,6 +615,7 @@ obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o +obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c new file mode 100644 index 000000000000..2916fa77b791 --- /dev/null +++ b/sound/soc/codecs/rt1320-sdw.c @@ -0,0 +1,2260 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1320-sdw.c -- rt1320 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt1320-sdw.h" + +/* + * The 'blind writes' is an SDCA term to deal with platform-specific initialization. + * It might include vendor-specific or SDCA control registers. + */ +static const struct reg_sequence rt1320_blind_write[] = { + { 0xc003, 0xe0 }, + { 0xc01b, 0xfc }, + { 0xc5c3, 0xf2 }, + { 0xc5c2, 0x00 }, + { 0xc5c6, 0x10 }, + { 0xc5c4, 0x12 }, + { 0xc5c8, 0x03 }, + { 0xc5d8, 0x0a }, + { 0xc5f7, 0x22 }, + { 0xc5f6, 0x22 }, + { 0xc5d0, 0x0f }, + { 0xc5d1, 0x89 }, + { 0xc057, 0x51 }, + { 0xc054, 0x35 }, + { 0xc053, 0x55 }, + { 0xc052, 0x55 }, + { 0xc051, 0x13 }, + { 0xc050, 0x15 }, + { 0xc060, 0x77 }, + { 0xc061, 0x55 }, + { 0xc063, 0x55 }, + { 0xc065, 0xa5 }, + { 0xc06b, 0x0a }, + { 0xca05, 0xd6 }, + { 0xca25, 0xd6 }, + { 0xcd00, 0x05 }, + { 0xc604, 0x40 }, + { 0xc609, 0x40 }, + { 0xc046, 0xff }, + { 0xc045, 0xff }, + { 0xc044, 0xff }, + { 0xc043, 0xff }, + { 0xc042, 0xff }, + { 0xc041, 0xff }, + { 0xc040, 0xff }, + { 0xcc10, 0x01 }, + { 0xc700, 0xf0 }, + { 0xc701, 0x13 }, + { 0xc901, 0x04 }, + { 0xc900, 0x73 }, + { 0xde03, 0x05 }, + { 0xdd0b, 0x0d }, + { 0xdd0a, 0xff }, + { 0xdd09, 0x0d }, + { 0xdd08, 0xff }, + { 0xc570, 0x08 }, + { 0xe803, 0xbe }, + { 0xc003, 0xc0 }, + { 0xc081, 0xfe }, + { 0xce31, 0x0d }, + { 0xce30, 0xae }, + { 0xce37, 0x0b }, + { 0xce36, 0xd2 }, + { 0xce39, 0x04 }, + { 0xce38, 0x80 }, + { 0xce3f, 0x00 }, + { 0xce3e, 0x00 }, + { 0xd470, 0x8b }, + { 0xd471, 0x18 }, + { 0xc019, 0x10 }, + { 0xd487, 0x3f }, + { 0xd486, 0xc3 }, +}; + +/* + * The 'patch code' is written to the patch code area. + * The patch code area is used for SDCA register expansion flexibility. + */ +static const struct reg_sequence rt1320_patch_code_write[] = { + { 0x10007000, 0x37 }, + { 0x10007001, 0x77 }, + { 0x10007002, 0x00 }, + { 0x10007003, 0x10 }, + { 0x10007004, 0xb7 }, + { 0x10007005, 0xe7 }, + { 0x10007006, 0x00 }, + { 0x10007007, 0x10 }, + { 0x10007008, 0x13 }, + { 0x10007009, 0x07 }, + { 0x1000700a, 0x07 }, + { 0x1000700b, 0x40 }, + { 0x1000700c, 0x23 }, + { 0x1000700d, 0xae }, + { 0x1000700e, 0xe7 }, + { 0x1000700f, 0xda }, + { 0x10007010, 0x37 }, + { 0x10007011, 0x77 }, + { 0x10007012, 0x00 }, + { 0x10007013, 0x10 }, + { 0x10007014, 0x13 }, + { 0x10007015, 0x07 }, + { 0x10007016, 0x47 }, + { 0x10007017, 0x61 }, + { 0x10007018, 0x23 }, + { 0x10007019, 0xa4 }, + { 0x1000701a, 0xe7 }, + { 0x1000701b, 0xde }, + { 0x1000701c, 0x37 }, + { 0x1000701d, 0x77 }, + { 0x1000701e, 0x00 }, + { 0x1000701f, 0x10 }, + { 0x10007020, 0x13 }, + { 0x10007021, 0x07 }, + { 0x10007022, 0x07 }, + { 0x10007023, 0x52 }, + { 0x10007024, 0x23 }, + { 0x10007025, 0xae }, + { 0x10007026, 0xe7 }, + { 0x10007027, 0xde }, + { 0x10007028, 0x37 }, + { 0x10007029, 0x77 }, + { 0x1000702a, 0x00 }, + { 0x1000702b, 0x10 }, + { 0x1000702c, 0x13 }, + { 0x1000702d, 0x07 }, + { 0x1000702e, 0x47 }, + { 0x1000702f, 0x54 }, + { 0x10007030, 0x23 }, + { 0x10007031, 0xaa }, + { 0x10007032, 0xe7 }, + { 0x10007033, 0xe4 }, + { 0x10007034, 0x37 }, + { 0x10007035, 0x87 }, + { 0x10007036, 0x00 }, + { 0x10007037, 0x10 }, + { 0x10007038, 0x13 }, + { 0x10007039, 0x07 }, + { 0x1000703a, 0x47 }, + { 0x1000703b, 0x81 }, + { 0x1000703c, 0x23 }, + { 0x1000703d, 0xa2 }, + { 0x1000703e, 0xe7 }, + { 0x1000703f, 0xe8 }, + { 0x10007040, 0x23 }, + { 0x10007041, 0xa4 }, + { 0x10007042, 0xe7 }, + { 0x10007043, 0xe8 }, + { 0x10007044, 0x37 }, + { 0x10007045, 0x77 }, + { 0x10007046, 0x00 }, + { 0x10007047, 0x10 }, + { 0x10007048, 0x13 }, + { 0x10007049, 0x07 }, + { 0x1000704a, 0x07 }, + { 0x1000704b, 0x59 }, + { 0x1000704c, 0x23 }, + { 0x1000704d, 0xa8 }, + { 0x1000704e, 0xe7 }, + { 0x1000704f, 0xea }, + { 0x10007050, 0x37 }, + { 0x10007051, 0x77 }, + { 0x10007052, 0x00 }, + { 0x10007053, 0x10 }, + { 0x10007054, 0x13 }, + { 0x10007055, 0x07 }, + { 0x10007056, 0x07 }, + { 0x10007057, 0x78 }, + { 0x10007058, 0x23 }, + { 0x10007059, 0xa6 }, + { 0x1000705a, 0xe7 }, + { 0x1000705b, 0xec }, + { 0x1000705c, 0x67 }, + { 0x1000705d, 0x80 }, + { 0x1000705e, 0x00 }, + { 0x1000705f, 0x00 }, + { 0x10007400, 0x37 }, + { 0x10007401, 0xd7 }, + { 0x10007402, 0x00 }, + { 0x10007403, 0x00 }, + { 0x10007404, 0x83 }, + { 0x10007405, 0x27 }, + { 0x10007406, 0x47 }, + { 0x10007407, 0x56 }, + { 0x10007408, 0xb7 }, + { 0x10007409, 0x06 }, + { 0x1000740a, 0x00 }, + { 0x1000740b, 0x02 }, + { 0x1000740c, 0xb3 }, + { 0x1000740d, 0xf7 }, + { 0x1000740e, 0xd7 }, + { 0x1000740f, 0x00 }, + { 0x10007410, 0x63 }, + { 0x10007411, 0x8a }, + { 0x10007412, 0x07 }, + { 0x10007413, 0x00 }, + { 0x10007414, 0x93 }, + { 0x10007415, 0x06 }, + { 0x10007416, 0x10 }, + { 0x10007417, 0x00 }, + { 0x10007418, 0x23 }, + { 0x10007419, 0x83 }, + { 0x1000741a, 0xd1 }, + { 0x1000741b, 0x44 }, + { 0x1000741c, 0x93 }, + { 0x1000741d, 0x07 }, + { 0x1000741e, 0xf0 }, + { 0x1000741f, 0xff }, + { 0x10007420, 0x23 }, + { 0x10007421, 0x22 }, + { 0x10007422, 0xf7 }, + { 0x10007423, 0x56 }, + { 0x10007424, 0x37 }, + { 0x10007425, 0xd7 }, + { 0x10007426, 0x00 }, + { 0x10007427, 0x00 }, + { 0x10007428, 0x83 }, + { 0x10007429, 0x27 }, + { 0x1000742a, 0x47 }, + { 0x1000742b, 0x58 }, + { 0x1000742c, 0x93 }, + { 0x1000742d, 0xf7 }, + { 0x1000742e, 0x17 }, + { 0x1000742f, 0x00 }, + { 0x10007430, 0x63 }, + { 0x10007431, 0x86 }, + { 0x10007432, 0x07 }, + { 0x10007433, 0x00 }, + { 0x10007434, 0x93 }, + { 0x10007435, 0x07 }, + { 0x10007436, 0x10 }, + { 0x10007437, 0x00 }, + { 0x10007438, 0x23 }, + { 0x10007439, 0x22 }, + { 0x1000743a, 0xf7 }, + { 0x1000743b, 0x58 }, + { 0x1000743c, 0xb7 }, + { 0x1000743d, 0xd7 }, + { 0x1000743e, 0x00 }, + { 0x1000743f, 0x00 }, + { 0x10007440, 0x03 }, + { 0x10007441, 0xa7 }, + { 0x10007442, 0x47 }, + { 0x10007443, 0x58 }, + { 0x10007444, 0xb7 }, + { 0x10007445, 0x07 }, + { 0x10007446, 0x00 }, + { 0x10007447, 0x04 }, + { 0x10007448, 0x33 }, + { 0x10007449, 0x77 }, + { 0x1000744a, 0xf7 }, + { 0x1000744b, 0x00 }, + { 0x1000744c, 0x93 }, + { 0x1000744d, 0x07 }, + { 0x1000744e, 0x00 }, + { 0x1000744f, 0x00 }, + { 0x10007450, 0x63 }, + { 0x10007451, 0x0e }, + { 0x10007452, 0x07 }, + { 0x10007453, 0x04 }, + { 0x10007454, 0x37 }, + { 0x10007455, 0x07 }, + { 0x10007456, 0x00 }, + { 0x10007457, 0x11 }, + { 0x10007458, 0x03 }, + { 0x10007459, 0x47 }, + { 0x1000745a, 0x87 }, + { 0x1000745b, 0x0e }, + { 0x1000745c, 0x93 }, + { 0x1000745d, 0x06 }, + { 0x1000745e, 0x40 }, + { 0x1000745f, 0x00 }, + { 0x10007460, 0x13 }, + { 0x10007461, 0x77 }, + { 0x10007462, 0xf7 }, + { 0x10007463, 0x0f }, + { 0x10007464, 0x63 }, + { 0x10007465, 0x02 }, + { 0x10007466, 0xd7 }, + { 0x10007467, 0x0a }, + { 0x10007468, 0x93 }, + { 0x10007469, 0x06 }, + { 0x1000746a, 0x70 }, + { 0x1000746b, 0x00 }, + { 0x1000746c, 0x63 }, + { 0x1000746d, 0x10 }, + { 0x1000746e, 0xd7 }, + { 0x1000746f, 0x04 }, + { 0x10007470, 0x93 }, + { 0x10007471, 0x07 }, + { 0x10007472, 0x60 }, + { 0x10007473, 0x06 }, + { 0x10007474, 0x37 }, + { 0x10007475, 0xd7 }, + { 0x10007476, 0x00 }, + { 0x10007477, 0x00 }, + { 0x10007478, 0x83 }, + { 0x10007479, 0x46 }, + { 0x1000747a, 0x77 }, + { 0x1000747b, 0xa6 }, + { 0x1000747c, 0x93 }, + { 0x1000747d, 0xe6 }, + { 0x1000747e, 0x06 }, + { 0x1000747f, 0xf8 }, + { 0x10007480, 0x93 }, + { 0x10007481, 0xf6 }, + { 0x10007482, 0xf6 }, + { 0x10007483, 0x0f }, + { 0x10007484, 0xa3 }, + { 0x10007485, 0x03 }, + { 0x10007486, 0xd7 }, + { 0x10007487, 0xa6 }, + { 0x10007488, 0x83 }, + { 0x10007489, 0x46 }, + { 0x1000748a, 0x77 }, + { 0x1000748b, 0xa8 }, + { 0x1000748c, 0x93 }, + { 0x1000748d, 0xe6 }, + { 0x1000748e, 0x06 }, + { 0x1000748f, 0xf8 }, + { 0x10007490, 0x93 }, + { 0x10007491, 0xf6 }, + { 0x10007492, 0xf6 }, + { 0x10007493, 0x0f }, + { 0x10007494, 0xa3 }, + { 0x10007495, 0x03 }, + { 0x10007496, 0xd7 }, + { 0x10007497, 0xa8 }, + { 0x10007498, 0xb7 }, + { 0x10007499, 0xc6 }, + { 0x1000749a, 0x00 }, + { 0x1000749b, 0x00 }, + { 0x1000749c, 0x23 }, + { 0x1000749d, 0x84 }, + { 0x1000749e, 0xf6 }, + { 0x1000749f, 0x06 }, + { 0x100074a0, 0xa3 }, + { 0x100074a1, 0x84 }, + { 0x100074a2, 0xf6 }, + { 0x100074a3, 0x06 }, + { 0x100074a4, 0xb7 }, + { 0x100074a5, 0x06 }, + { 0x100074a6, 0x00 }, + { 0x100074a7, 0x04 }, + { 0x100074a8, 0x23 }, + { 0x100074a9, 0x22 }, + { 0x100074aa, 0xd7 }, + { 0x100074ab, 0x58 }, + { 0x100074ac, 0x37 }, + { 0x100074ad, 0xd7 }, + { 0x100074ae, 0x00 }, + { 0x100074af, 0x00 }, + { 0x100074b0, 0x03 }, + { 0x100074b1, 0x27 }, + { 0x100074b2, 0x47 }, + { 0x100074b3, 0x58 }, + { 0x100074b4, 0xb7 }, + { 0x100074b5, 0x06 }, + { 0x100074b6, 0x00 }, + { 0x100074b7, 0x08 }, + { 0x100074b8, 0x33 }, + { 0x100074b9, 0x77 }, + { 0x100074ba, 0xd7 }, + { 0x100074bb, 0x00 }, + { 0x100074bc, 0x63 }, + { 0x100074bd, 0x04 }, + { 0x100074be, 0x07 }, + { 0x100074bf, 0x04 }, + { 0x100074c0, 0x37 }, + { 0x100074c1, 0x07 }, + { 0x100074c2, 0x00 }, + { 0x100074c3, 0x11 }, + { 0x100074c4, 0x03 }, + { 0x100074c5, 0x47 }, + { 0x100074c6, 0xc7 }, + { 0x100074c7, 0x0e }, + { 0x100074c8, 0x93 }, + { 0x100074c9, 0x06 }, + { 0x100074ca, 0x40 }, + { 0x100074cb, 0x00 }, + { 0x100074cc, 0x13 }, + { 0x100074cd, 0x77 }, + { 0x100074ce, 0xf7 }, + { 0x100074cf, 0x0f }, + { 0x100074d0, 0x63 }, + { 0x100074d1, 0x00 }, + { 0x100074d2, 0xd7 }, + { 0x100074d3, 0x04 }, + { 0x100074d4, 0x93 }, + { 0x100074d5, 0x06 }, + { 0x100074d6, 0x70 }, + { 0x100074d7, 0x00 }, + { 0x100074d8, 0x63 }, + { 0x100074d9, 0x00 }, + { 0x100074da, 0xd7 }, + { 0x100074db, 0x04 }, + { 0x100074dc, 0x63 }, + { 0x100074dd, 0x84 }, + { 0x100074de, 0x07 }, + { 0x100074df, 0x02 }, + { 0x100074e0, 0xb7 }, + { 0x100074e1, 0xd6 }, + { 0x100074e2, 0x00 }, + { 0x100074e3, 0x00 }, + { 0x100074e4, 0x03 }, + { 0x100074e5, 0xc7 }, + { 0x100074e6, 0x56 }, + { 0x100074e7, 0xa4 }, + { 0x100074e8, 0x13 }, + { 0x100074e9, 0x67 }, + { 0x100074ea, 0x07 }, + { 0x100074eb, 0xf8 }, + { 0x100074ec, 0x13 }, + { 0x100074ed, 0x77 }, + { 0x100074ee, 0xf7 }, + { 0x100074ef, 0x0f }, + { 0x100074f0, 0xa3 }, + { 0x100074f1, 0x82 }, + { 0x100074f2, 0xe6 }, + { 0x100074f3, 0xa4 }, + { 0x100074f4, 0x37 }, + { 0x100074f5, 0xc7 }, + { 0x100074f6, 0x00 }, + { 0x100074f7, 0x00 }, + { 0x100074f8, 0x23 }, + { 0x100074f9, 0x02 }, + { 0x100074fa, 0xf7 }, + { 0x100074fb, 0x06 }, + { 0x100074fc, 0xb7 }, + { 0x100074fd, 0x07 }, + { 0x100074fe, 0x00 }, + { 0x100074ff, 0x08 }, + { 0x10007500, 0x23 }, + { 0x10007501, 0xa2 }, + { 0x10007502, 0xf6 }, + { 0x10007503, 0x58 }, + { 0x10007504, 0x67 }, + { 0x10007505, 0x80 }, + { 0x10007506, 0x00 }, + { 0x10007507, 0x00 }, + { 0x10007508, 0x93 }, + { 0x10007509, 0x07 }, + { 0x1000750a, 0x80 }, + { 0x1000750b, 0x08 }, + { 0x1000750c, 0x6f }, + { 0x1000750d, 0xf0 }, + { 0x1000750e, 0x9f }, + { 0x1000750f, 0xf6 }, + { 0x10007510, 0x93 }, + { 0x10007511, 0x07 }, + { 0x10007512, 0x80 }, + { 0x10007513, 0x08 }, + { 0x10007514, 0x6f }, + { 0x10007515, 0xf0 }, + { 0x10007516, 0xdf }, + { 0x10007517, 0xfc }, + { 0x10007518, 0x93 }, + { 0x10007519, 0x07 }, + { 0x1000751a, 0x60 }, + { 0x1000751b, 0x06 }, + { 0x1000751c, 0x6f }, + { 0x1000751d, 0xf0 }, + { 0x1000751e, 0x5f }, + { 0x1000751f, 0xfc }, + { 0x10007520, 0x37 }, + { 0x10007521, 0xd7 }, + { 0x10007522, 0x00 }, + { 0x10007523, 0x00 }, + { 0x10007524, 0x83 }, + { 0x10007525, 0x27 }, + { 0x10007526, 0x07 }, + { 0x10007527, 0x53 }, + { 0x10007528, 0xb7 }, + { 0x10007529, 0x06 }, + { 0x1000752a, 0x02 }, + { 0x1000752b, 0x00 }, + { 0x1000752c, 0xb3 }, + { 0x1000752d, 0xf7 }, + { 0x1000752e, 0xd7 }, + { 0x1000752f, 0x00 }, + { 0x10007530, 0x63 }, + { 0x10007531, 0x88 }, + { 0x10007532, 0x07 }, + { 0x10007533, 0x00 }, + { 0x10007534, 0x13 }, + { 0x10007535, 0x06 }, + { 0x10007536, 0xa0 }, + { 0x10007537, 0x05 }, + { 0x10007538, 0x23 }, + { 0x10007539, 0xa8 }, + { 0x1000753a, 0xc1 }, + { 0x1000753b, 0x56 }, + { 0x1000753c, 0x23 }, + { 0x1000753d, 0x28 }, + { 0x1000753e, 0xd7 }, + { 0x1000753f, 0x52 }, + { 0x10007540, 0x67 }, + { 0x10007541, 0x80 }, + { 0x10007542, 0x00 }, + { 0x10007543, 0x00 }, + { 0x10007544, 0x37 }, + { 0x10007545, 0xd7 }, + { 0x10007546, 0x00 }, + { 0x10007547, 0x10 }, + { 0x10007548, 0x83 }, + { 0x10007549, 0x47 }, + { 0x1000754a, 0x07 }, + { 0x1000754b, 0xd9 }, + { 0x1000754c, 0x93 }, + { 0x1000754d, 0x06 }, + { 0x1000754e, 0x20 }, + { 0x1000754f, 0x00 }, + { 0x10007550, 0x93 }, + { 0x10007551, 0xf7 }, + { 0x10007552, 0xf7 }, + { 0x10007553, 0x0f }, + { 0x10007554, 0x63 }, + { 0x10007555, 0x9c }, + { 0x10007556, 0xd7 }, + { 0x10007557, 0x02 }, + { 0x10007558, 0xb7 }, + { 0x10007559, 0xc6 }, + { 0x1000755a, 0x00 }, + { 0x1000755b, 0x00 }, + { 0x1000755c, 0x83 }, + { 0x1000755d, 0xc7 }, + { 0x1000755e, 0x26 }, + { 0x1000755f, 0x04 }, + { 0x10007560, 0x93 }, + { 0x10007561, 0xf7 }, + { 0x10007562, 0xf7 }, + { 0x10007563, 0x07 }, + { 0x10007564, 0x23 }, + { 0x10007565, 0x81 }, + { 0x10007566, 0xf6 }, + { 0x10007567, 0x04 }, + { 0x10007568, 0xb7 }, + { 0x10007569, 0xd6 }, + { 0x1000756a, 0x00 }, + { 0x1000756b, 0x00 }, + { 0x1000756c, 0x83 }, + { 0x1000756d, 0xc7 }, + { 0x1000756e, 0xa6 }, + { 0x1000756f, 0xe1 }, + { 0x10007570, 0x93 }, + { 0x10007571, 0xf7 }, + { 0x10007572, 0xf7 }, + { 0x10007573, 0x07 }, + { 0x10007574, 0x23 }, + { 0x10007575, 0x8d }, + { 0x10007576, 0xf6 }, + { 0x10007577, 0xe0 }, + { 0x10007578, 0x23 }, + { 0x10007579, 0x08 }, + { 0x1000757a, 0x07 }, + { 0x1000757b, 0xd8 }, + { 0x1000757c, 0x83 }, + { 0x1000757d, 0x47 }, + { 0x1000757e, 0x47 }, + { 0x1000757f, 0xd9 }, + { 0x10007580, 0x93 }, + { 0x10007581, 0x87 }, + { 0x10007582, 0x17 }, + { 0x10007583, 0x00 }, + { 0x10007584, 0x93 }, + { 0x10007585, 0xf7 }, + { 0x10007586, 0xf7 }, + { 0x10007587, 0x0f }, + { 0x10007588, 0x23 }, + { 0x10007589, 0x0a }, + { 0x1000758a, 0xf7 }, + { 0x1000758b, 0xd8 }, + { 0x1000758c, 0x67 }, + { 0x1000758d, 0x80 }, + { 0x1000758e, 0x00 }, + { 0x1000758f, 0x00 }, + { 0x10007590, 0xb7 }, + { 0x10007591, 0xd7 }, + { 0x10007592, 0x00 }, + { 0x10007593, 0x00 }, + { 0x10007594, 0x83 }, + { 0x10007595, 0xc7 }, + { 0x10007596, 0x07 }, + { 0x10007597, 0x47 }, + { 0x10007598, 0x93 }, + { 0x10007599, 0xf7 }, + { 0x1000759a, 0x07 }, + { 0x1000759b, 0x01 }, + { 0x1000759c, 0x63 }, + { 0x1000759d, 0x8a }, + { 0x1000759e, 0x07 }, + { 0x1000759f, 0x06 }, + { 0x100075a0, 0x63 }, + { 0x100075a1, 0x02 }, + { 0x100075a2, 0x05 }, + { 0x100075a3, 0x06 }, + { 0x100075a4, 0x37 }, + { 0x100075a5, 0xc7 }, + { 0x100075a6, 0x00 }, + { 0x100075a7, 0x00 }, + { 0x100075a8, 0x83 }, + { 0x100075a9, 0x27 }, + { 0x100075aa, 0xc7 }, + { 0x100075ab, 0x5f }, + { 0x100075ac, 0x23 }, + { 0x100075ad, 0xae }, + { 0x100075ae, 0xf1 }, + { 0x100075af, 0x40 }, + { 0x100075b0, 0xb7 }, + { 0x100075b1, 0x06 }, + { 0x100075b2, 0x00 }, + { 0x100075b3, 0x10 }, + { 0x100075b4, 0xb3 }, + { 0x100075b5, 0xf7 }, + { 0x100075b6, 0xd7 }, + { 0x100075b7, 0x00 }, + { 0x100075b8, 0x63 }, + { 0x100075b9, 0x8c }, + { 0x100075ba, 0x07 }, + { 0x100075bb, 0x04 }, + { 0x100075bc, 0x83 }, + { 0x100075bd, 0x47 }, + { 0x100075be, 0x07 }, + { 0x100075bf, 0x56 }, + { 0x100075c0, 0x93 }, + { 0x100075c1, 0xf7 }, + { 0x100075c2, 0x87 }, + { 0x100075c3, 0x01 }, + { 0x100075c4, 0x63 }, + { 0x100075c5, 0x86 }, + { 0x100075c6, 0x07 }, + { 0x100075c7, 0x04 }, + { 0x100075c8, 0x83 }, + { 0x100075c9, 0x47 }, + { 0x100075ca, 0x17 }, + { 0x100075cb, 0x08 }, + { 0x100075cc, 0x93 }, + { 0x100075cd, 0xf7 }, + { 0x100075ce, 0x47 }, + { 0x100075cf, 0x00 }, + { 0x100075d0, 0x63 }, + { 0x100075d1, 0x80 }, + { 0x100075d2, 0x07 }, + { 0x100075d3, 0x04 }, + { 0x100075d4, 0xb7 }, + { 0x100075d5, 0xc7 }, + { 0x100075d6, 0xc2 }, + { 0x100075d7, 0x3f }, + { 0x100075d8, 0x93 }, + { 0x100075d9, 0x87 }, + { 0x100075da, 0x07 }, + { 0x100075db, 0xfc }, + { 0x100075dc, 0x83 }, + { 0x100075dd, 0xa7 }, + { 0x100075de, 0x47 }, + { 0x100075df, 0x00 }, + { 0x100075e0, 0x93 }, + { 0x100075e1, 0xd7 }, + { 0x100075e2, 0x17 }, + { 0x100075e3, 0x00 }, + { 0x100075e4, 0x93 }, + { 0x100075e5, 0xf7 }, + { 0x100075e6, 0x17 }, + { 0x100075e7, 0x00 }, + { 0x100075e8, 0x63 }, + { 0x100075e9, 0x84 }, + { 0x100075ea, 0x07 }, + { 0x100075eb, 0x02 }, + { 0x100075ec, 0x23 }, + { 0x100075ed, 0x8a }, + { 0x100075ee, 0xf1 }, + { 0x100075ef, 0x40 }, + { 0x100075f0, 0xb7 }, + { 0x100075f1, 0x07 }, + { 0x100075f2, 0x00 }, + { 0x100075f3, 0xc0 }, + { 0x100075f4, 0x37 }, + { 0x100075f5, 0xf7 }, + { 0x100075f6, 0x00 }, + { 0x100075f7, 0x00 }, + { 0x100075f8, 0x93 }, + { 0x100075f9, 0x87 }, + { 0x100075fa, 0xf7 }, + { 0x100075fb, 0xff }, + { 0x100075fc, 0x23 }, + { 0x100075fd, 0x2c }, + { 0x100075fe, 0xf7 }, + { 0x100075ff, 0x06 }, + { 0x10007600, 0x67 }, + { 0x10007601, 0x80 }, + { 0x10007602, 0x00 }, + { 0x10007603, 0x00 }, + { 0x10007604, 0x23 }, + { 0x10007605, 0x8a }, + { 0x10007606, 0x01 }, + { 0x10007607, 0x40 }, + { 0x10007608, 0xb7 }, + { 0x10007609, 0xf7 }, + { 0x1000760a, 0x00 }, + { 0x1000760b, 0x00 }, + { 0x1000760c, 0x23 }, + { 0x1000760d, 0xac }, + { 0x1000760e, 0x07 }, + { 0x1000760f, 0x06 }, + { 0x10007610, 0x67 }, + { 0x10007611, 0x80 }, + { 0x10007612, 0x00 }, + { 0x10007613, 0x00 }, + { 0x10007614, 0x13 }, + { 0x10007615, 0x01 }, + { 0x10007616, 0x01 }, + { 0x10007617, 0xff }, + { 0x10007618, 0x23 }, + { 0x10007619, 0x26 }, + { 0x1000761a, 0x11 }, + { 0x1000761b, 0x00 }, + { 0x1000761c, 0x23 }, + { 0x1000761d, 0x24 }, + { 0x1000761e, 0x81 }, + { 0x1000761f, 0x00 }, + { 0x10007620, 0x37 }, + { 0x10007621, 0xc7 }, + { 0x10007622, 0x00 }, + { 0x10007623, 0x00 }, + { 0x10007624, 0x83 }, + { 0x10007625, 0x47 }, + { 0x10007626, 0x07 }, + { 0x10007627, 0x56 }, + { 0x10007628, 0x93 }, + { 0x10007629, 0xf7 }, + { 0x1000762a, 0x17 }, + { 0x1000762b, 0x00 }, + { 0x1000762c, 0x63 }, + { 0x1000762d, 0x98 }, + { 0x1000762e, 0x07 }, + { 0x1000762f, 0x00 }, + { 0x10007630, 0x83 }, + { 0x10007631, 0x47 }, + { 0x10007632, 0x07 }, + { 0x10007633, 0x56 }, + { 0x10007634, 0x93 }, + { 0x10007635, 0xf7 }, + { 0x10007636, 0x27 }, + { 0x10007637, 0x00 }, + { 0x10007638, 0x63 }, + { 0x10007639, 0x82 }, + { 0x1000763a, 0x07 }, + { 0x1000763b, 0x08 }, + { 0x1000763c, 0x37 }, + { 0x1000763d, 0xd4 }, + { 0x1000763e, 0x00 }, + { 0x1000763f, 0x00 }, + { 0x10007640, 0x83 }, + { 0x10007641, 0x47 }, + { 0x10007642, 0x14 }, + { 0x10007643, 0x47 }, + { 0x10007644, 0x93 }, + { 0x10007645, 0xf7 }, + { 0x10007646, 0x27 }, + { 0x10007647, 0x00 }, + { 0x10007648, 0x63 }, + { 0x10007649, 0x8a }, + { 0x1000764a, 0x07 }, + { 0x1000764b, 0x06 }, + { 0x1000764c, 0x93 }, + { 0x1000764d, 0x05 }, + { 0x1000764e, 0x10 }, + { 0x1000764f, 0x00 }, + { 0x10007650, 0x13 }, + { 0x10007651, 0x05 }, + { 0x10007652, 0x20 }, + { 0x10007653, 0x10 }, + { 0x10007654, 0xef }, + { 0x10007655, 0xa0 }, + { 0x10007656, 0x8f }, + { 0x10007657, 0x9a }, + { 0x10007658, 0x37 }, + { 0x10007659, 0x05 }, + { 0x1000765a, 0x01 }, + { 0x1000765b, 0x00 }, + { 0x1000765c, 0x93 }, + { 0x1000765d, 0x05 }, + { 0x1000765e, 0x00 }, + { 0x1000765f, 0x01 }, + { 0x10007660, 0x13 }, + { 0x10007661, 0x05 }, + { 0x10007662, 0xb5 }, + { 0x10007663, 0xa0 }, + { 0x10007664, 0xef }, + { 0x10007665, 0xa0 }, + { 0x10007666, 0x8f }, + { 0x10007667, 0x99 }, + { 0x10007668, 0x83 }, + { 0x10007669, 0x47 }, + { 0x1000766a, 0x24 }, + { 0x1000766b, 0xe0 }, + { 0x1000766c, 0x13 }, + { 0x1000766d, 0x05 }, + { 0x1000766e, 0x80 }, + { 0x1000766f, 0x3e }, + { 0x10007670, 0x93 }, + { 0x10007671, 0x05 }, + { 0x10007672, 0x00 }, + { 0x10007673, 0x00 }, + { 0x10007674, 0x93 }, + { 0x10007675, 0xe7 }, + { 0x10007676, 0x07 }, + { 0x10007677, 0xf8 }, + { 0x10007678, 0x93 }, + { 0x10007679, 0xf7 }, + { 0x1000767a, 0xf7 }, + { 0x1000767b, 0x0f }, + { 0x1000767c, 0x23 }, + { 0x1000767d, 0x01 }, + { 0x1000767e, 0xf4 }, + { 0x1000767f, 0xe0 }, + { 0x10007680, 0x83 }, + { 0x10007681, 0x47 }, + { 0x10007682, 0x24 }, + { 0x10007683, 0xe0 }, + { 0x10007684, 0x93 }, + { 0x10007685, 0xf7 }, + { 0x10007686, 0xf7 }, + { 0x10007687, 0x0f }, + { 0x10007688, 0x93 }, + { 0x10007689, 0xe7 }, + { 0x1000768a, 0x07 }, + { 0x1000768b, 0x04 }, + { 0x1000768c, 0x23 }, + { 0x1000768d, 0x01 }, + { 0x1000768e, 0xf4 }, + { 0x1000768f, 0xe0 }, + { 0x10007690, 0xef }, + { 0x10007691, 0xe0 }, + { 0x10007692, 0x8f }, + { 0x10007693, 0xb9 }, + { 0x10007694, 0x83 }, + { 0x10007695, 0x47 }, + { 0x10007696, 0x34 }, + { 0x10007697, 0xe0 }, + { 0x10007698, 0x93 }, + { 0x10007699, 0xf7 }, + { 0x1000769a, 0x07 }, + { 0x1000769b, 0x02 }, + { 0x1000769c, 0xe3 }, + { 0x1000769d, 0x9c }, + { 0x1000769e, 0x07 }, + { 0x1000769f, 0xfe }, + { 0x100076a0, 0x37 }, + { 0x100076a1, 0x05 }, + { 0x100076a2, 0x01 }, + { 0x100076a3, 0x00 }, + { 0x100076a4, 0x93 }, + { 0x100076a5, 0x05 }, + { 0x100076a6, 0x00 }, + { 0x100076a7, 0x00 }, + { 0x100076a8, 0x13 }, + { 0x100076a9, 0x05 }, + { 0x100076aa, 0xb5 }, + { 0x100076ab, 0xa0 }, + { 0x100076ac, 0xef }, + { 0x100076ad, 0xa0 }, + { 0x100076ae, 0x0f }, + { 0x100076af, 0x95 }, + { 0x100076b0, 0x83 }, + { 0x100076b1, 0x47 }, + { 0x100076b2, 0x14 }, + { 0x100076b3, 0x47 }, + { 0x100076b4, 0x93 }, + { 0x100076b5, 0xf7 }, + { 0x100076b6, 0xd7 }, + { 0x100076b7, 0x0f }, + { 0x100076b8, 0xa3 }, + { 0x100076b9, 0x08 }, + { 0x100076ba, 0xf4 }, + { 0x100076bb, 0x46 }, + { 0x100076bc, 0x03 }, + { 0x100076bd, 0xa7 }, + { 0x100076be, 0x01 }, + { 0x100076bf, 0x57 }, + { 0x100076c0, 0x93 }, + { 0x100076c1, 0x07 }, + { 0x100076c2, 0xa0 }, + { 0x100076c3, 0x05 }, + { 0x100076c4, 0x63 }, + { 0x100076c5, 0x14 }, + { 0x100076c6, 0xf7 }, + { 0x100076c7, 0x04 }, + { 0x100076c8, 0x37 }, + { 0x100076c9, 0x07 }, + { 0x100076ca, 0x00 }, + { 0x100076cb, 0x11 }, + { 0x100076cc, 0x83 }, + { 0x100076cd, 0x47 }, + { 0x100076ce, 0x07 }, + { 0x100076cf, 0x01 }, + { 0x100076d0, 0x13 }, + { 0x100076d1, 0x06 }, + { 0x100076d2, 0x30 }, + { 0x100076d3, 0x00 }, + { 0x100076d4, 0x93 }, + { 0x100076d5, 0xf7 }, + { 0x100076d6, 0xf7 }, + { 0x100076d7, 0x0f }, + { 0x100076d8, 0x63 }, + { 0x100076d9, 0x9a }, + { 0x100076da, 0xc7 }, + { 0x100076db, 0x02 }, + { 0x100076dc, 0x03 }, + { 0x100076dd, 0x47 }, + { 0x100076de, 0x87 }, + { 0x100076df, 0x01 }, + { 0x100076e0, 0x13 }, + { 0x100076e1, 0x77 }, + { 0x100076e2, 0xf7 }, + { 0x100076e3, 0x0f }, + { 0x100076e4, 0x63 }, + { 0x100076e5, 0x14 }, + { 0x100076e6, 0xf7 }, + { 0x100076e7, 0x02 }, + { 0x100076e8, 0x37 }, + { 0x100076e9, 0xd7 }, + { 0x100076ea, 0x00 }, + { 0x100076eb, 0x00 }, + { 0x100076ec, 0x83 }, + { 0x100076ed, 0x47 }, + { 0x100076ee, 0x37 }, + { 0x100076ef, 0x54 }, + { 0x100076f0, 0x93 }, + { 0x100076f1, 0xf7 }, + { 0x100076f2, 0xf7 }, + { 0x100076f3, 0x0f }, + { 0x100076f4, 0x93 }, + { 0x100076f5, 0xe7 }, + { 0x100076f6, 0x07 }, + { 0x100076f7, 0x02 }, + { 0x100076f8, 0xa3 }, + { 0x100076f9, 0x01 }, + { 0x100076fa, 0xf7 }, + { 0x100076fb, 0x54 }, + { 0x100076fc, 0x83 }, + { 0x100076fd, 0x47 }, + { 0x100076fe, 0x37 }, + { 0x100076ff, 0x54 }, + { 0x10007700, 0x93 }, + { 0x10007701, 0xf7 }, + { 0x10007702, 0xf7 }, + { 0x10007703, 0x0d }, + { 0x10007704, 0xa3 }, + { 0x10007705, 0x01 }, + { 0x10007706, 0xf7 }, + { 0x10007707, 0x54 }, + { 0x10007708, 0x23 }, + { 0x10007709, 0xa8 }, + { 0x1000770a, 0x01 }, + { 0x1000770b, 0x56 }, + { 0x1000770c, 0xb7 }, + { 0x1000770d, 0xd7 }, + { 0x1000770e, 0x00 }, + { 0x1000770f, 0x10 }, + { 0x10007710, 0x03 }, + { 0x10007711, 0xc7 }, + { 0x10007712, 0x07 }, + { 0x10007713, 0xd9 }, + { 0x10007714, 0x93 }, + { 0x10007715, 0x06 }, + { 0x10007716, 0x10 }, + { 0x10007717, 0x00 }, + { 0x10007718, 0x13 }, + { 0x10007719, 0x77 }, + { 0x1000771a, 0xf7 }, + { 0x1000771b, 0x0f }, + { 0x1000771c, 0x63 }, + { 0x1000771d, 0x1a }, + { 0x1000771e, 0xd7 }, + { 0x1000771f, 0x04 }, + { 0x10007720, 0x03 }, + { 0x10007721, 0xc7 }, + { 0x10007722, 0x27 }, + { 0x10007723, 0xd9 }, + { 0x10007724, 0x13 }, + { 0x10007725, 0x07 }, + { 0x10007726, 0x17 }, + { 0x10007727, 0x00 }, + { 0x10007728, 0x13 }, + { 0x10007729, 0x77 }, + { 0x1000772a, 0xf7 }, + { 0x1000772b, 0x0f }, + { 0x1000772c, 0x23 }, + { 0x1000772d, 0x89 }, + { 0x1000772e, 0xe7 }, + { 0x1000772f, 0xd8 }, + { 0x10007730, 0x83 }, + { 0x10007731, 0xc6 }, + { 0x10007732, 0x27 }, + { 0x10007733, 0xd9 }, + { 0x10007734, 0x03 }, + { 0x10007735, 0xc7 }, + { 0x10007736, 0x17 }, + { 0x10007737, 0xd9 }, + { 0x10007738, 0x93 }, + { 0x10007739, 0xf6 }, + { 0x1000773a, 0xf6 }, + { 0x1000773b, 0x0f }, + { 0x1000773c, 0x13 }, + { 0x1000773d, 0x77 }, + { 0x1000773e, 0xf7 }, + { 0x1000773f, 0x0f }, + { 0x10007740, 0x63 }, + { 0x10007741, 0xe8 }, + { 0x10007742, 0xe6 }, + { 0x10007743, 0x02 }, + { 0x10007744, 0xb7 }, + { 0x10007745, 0xd6 }, + { 0x10007746, 0x00 }, + { 0x10007747, 0x00 }, + { 0x10007748, 0x03 }, + { 0x10007749, 0xc7 }, + { 0x1000774a, 0xa6 }, + { 0x1000774b, 0xe1 }, + { 0x1000774c, 0x13 }, + { 0x1000774d, 0x67 }, + { 0x1000774e, 0x07 }, + { 0x1000774f, 0xf8 }, + { 0x10007750, 0x13 }, + { 0x10007751, 0x77 }, + { 0x10007752, 0xf7 }, + { 0x10007753, 0x0f }, + { 0x10007754, 0x23 }, + { 0x10007755, 0x8d }, + { 0x10007756, 0xe6 }, + { 0x10007757, 0xe0 }, + { 0x10007758, 0x03 }, + { 0x10007759, 0xc7 }, + { 0x1000775a, 0x37 }, + { 0x1000775b, 0xd9 }, + { 0x1000775c, 0x13 }, + { 0x1000775d, 0x07 }, + { 0x1000775e, 0x17 }, + { 0x1000775f, 0x00 }, + { 0x10007760, 0x13 }, + { 0x10007761, 0x77 }, + { 0x10007762, 0xf7 }, + { 0x10007763, 0x0f }, + { 0x10007764, 0xa3 }, + { 0x10007765, 0x89 }, + { 0x10007766, 0xe7 }, + { 0x10007767, 0xd8 }, + { 0x10007768, 0x13 }, + { 0x10007769, 0x07 }, + { 0x1000776a, 0x20 }, + { 0x1000776b, 0x00 }, + { 0x1000776c, 0x23 }, + { 0x1000776d, 0x88 }, + { 0x1000776e, 0xe7 }, + { 0x1000776f, 0xd8 }, + { 0x10007770, 0x83 }, + { 0x10007771, 0x20 }, + { 0x10007772, 0xc1 }, + { 0x10007773, 0x00 }, + { 0x10007774, 0x03 }, + { 0x10007775, 0x24 }, + { 0x10007776, 0x81 }, + { 0x10007777, 0x00 }, + { 0x10007778, 0x13 }, + { 0x10007779, 0x01 }, + { 0x1000777a, 0x01 }, + { 0x1000777b, 0x01 }, + { 0x1000777c, 0x67 }, + { 0x1000777d, 0x80 }, + { 0x1000777e, 0x00 }, + { 0x1000777f, 0x00 }, + { 0x10007780, 0x03 }, + { 0x10007781, 0xc7 }, + { 0x10007782, 0xa1 }, + { 0x10007783, 0x40 }, + { 0x10007784, 0x93 }, + { 0x10007785, 0x06 }, + { 0x10007786, 0x10 }, + { 0x10007787, 0x00 }, + { 0x10007788, 0x63 }, + { 0x10007789, 0x16 }, + { 0x1000778a, 0xd7 }, + { 0x1000778b, 0x00 }, + { 0x1000778c, 0xb7 }, + { 0x1000778d, 0xd6 }, + { 0x1000778e, 0x00 }, + { 0x1000778f, 0x10 }, + { 0x10007790, 0xa3 }, + { 0x10007791, 0x8a }, + { 0x10007792, 0xe6 }, + { 0x10007793, 0xd8 }, + { 0x10007794, 0x83 }, + { 0x10007795, 0xc7 }, + { 0x10007796, 0xa1 }, + { 0x10007797, 0x40 }, + { 0x10007798, 0x63 }, + { 0x10007799, 0x9c }, + { 0x1000779a, 0x07 }, + { 0x1000779b, 0x06 }, + { 0x1000779c, 0x13 }, + { 0x1000779d, 0x01 }, + { 0x1000779e, 0x01 }, + { 0x1000779f, 0xff }, + { 0x100077a0, 0x23 }, + { 0x100077a1, 0x22 }, + { 0x100077a2, 0x91 }, + { 0x100077a3, 0x00 }, + { 0x100077a4, 0x23 }, + { 0x100077a5, 0x26 }, + { 0x100077a6, 0x11 }, + { 0x100077a7, 0x00 }, + { 0x100077a8, 0x23 }, + { 0x100077a9, 0x24 }, + { 0x100077aa, 0x81 }, + { 0x100077ab, 0x00 }, + { 0x100077ac, 0xb7 }, + { 0x100077ad, 0xc4 }, + { 0x100077ae, 0x00 }, + { 0x100077af, 0x00 }, + { 0x100077b0, 0x83 }, + { 0x100077b1, 0xc7 }, + { 0x100077b2, 0x04 }, + { 0x100077b3, 0x56 }, + { 0x100077b4, 0x13 }, + { 0x100077b5, 0x07 }, + { 0x100077b6, 0x80 }, + { 0x100077b7, 0x01 }, + { 0x100077b8, 0x93 }, + { 0x100077b9, 0xf7 }, + { 0x100077ba, 0xf7 }, + { 0x100077bb, 0x0f }, + { 0x100077bc, 0x63 }, + { 0x100077bd, 0x70 }, + { 0x100077be, 0xf7 }, + { 0x100077bf, 0x04 }, + { 0x100077c0, 0x37 }, + { 0x100077c1, 0xd4 }, + { 0x100077c2, 0x00 }, + { 0x100077c3, 0x10 }, + { 0x100077c4, 0x83 }, + { 0x100077c5, 0x47 }, + { 0x100077c6, 0x54 }, + { 0x100077c7, 0xd9 }, + { 0x100077c8, 0x93 }, + { 0x100077c9, 0xf7 }, + { 0x100077ca, 0xf7 }, + { 0x100077cb, 0x0f }, + { 0x100077cc, 0x63 }, + { 0x100077cd, 0x88 }, + { 0x100077ce, 0x07 }, + { 0x100077cf, 0x02 }, + { 0x100077d0, 0x93 }, + { 0x100077d1, 0x07 }, + { 0x100077d2, 0x10 }, + { 0x100077d3, 0x00 }, + { 0x100077d4, 0x23 }, + { 0x100077d5, 0x82 }, + { 0x100077d6, 0xf4 }, + { 0x100077d7, 0x58 }, + { 0x100077d8, 0x03 }, + { 0x100077d9, 0x45 }, + { 0x100077da, 0x64 }, + { 0x100077db, 0xd9 }, + { 0x100077dc, 0xb7 }, + { 0x100077dd, 0x15 }, + { 0x100077de, 0x00 }, + { 0x100077df, 0x00 }, + { 0x100077e0, 0x93 }, + { 0x100077e1, 0x85 }, + { 0x100077e2, 0x85 }, + { 0x100077e3, 0x38 }, + { 0x100077e4, 0x13 }, + { 0x100077e5, 0x75 }, + { 0x100077e6, 0xf5 }, + { 0x100077e7, 0x0f }, + { 0x100077e8, 0xef }, + { 0x100077e9, 0xe0 }, + { 0x100077ea, 0x9f }, + { 0x100077eb, 0xd0 }, + { 0x100077ec, 0x93 }, + { 0x100077ed, 0x55 }, + { 0x100077ee, 0xf5 }, + { 0x100077ef, 0x41 }, + { 0x100077f0, 0xef }, + { 0x100077f1, 0xe0 }, + { 0x100077f2, 0x8f }, + { 0x100077f3, 0xa3 }, + { 0x100077f4, 0x23 }, + { 0x100077f5, 0x82 }, + { 0x100077f6, 0x04 }, + { 0x100077f7, 0x58 }, + { 0x100077f8, 0xa3 }, + { 0x100077f9, 0x0a }, + { 0x100077fa, 0x04 }, + { 0x100077fb, 0xd8 }, + { 0x100077fc, 0x83 }, + { 0x100077fd, 0x20 }, + { 0x100077fe, 0xc1 }, + { 0x100077ff, 0x00 }, + { 0x10007800, 0x03 }, + { 0x10007801, 0x24 }, + { 0x10007802, 0x81 }, + { 0x10007803, 0x00 }, + { 0x10007804, 0x83 }, + { 0x10007805, 0x24 }, + { 0x10007806, 0x41 }, + { 0x10007807, 0x00 }, + { 0x10007808, 0x13 }, + { 0x10007809, 0x01 }, + { 0x1000780a, 0x01 }, + { 0x1000780b, 0x01 }, + { 0x1000780c, 0x67 }, + { 0x1000780d, 0x80 }, + { 0x1000780e, 0x00 }, + { 0x1000780f, 0x00 }, + { 0x10007810, 0x67 }, + { 0x10007811, 0x80 }, + { 0x10007812, 0x00 }, + { 0x10007813, 0x00 }, + { 0x10007814, 0x13 }, + { 0x10007815, 0x01 }, + { 0x10007816, 0x01 }, + { 0x10007817, 0xff }, + { 0x10007818, 0x23 }, + { 0x10007819, 0x26 }, + { 0x1000781a, 0x11 }, + { 0x1000781b, 0x00 }, + { 0x1000781c, 0xef }, + { 0x1000781d, 0xd0 }, + { 0x1000781e, 0x8f }, + { 0x1000781f, 0x86 }, + { 0x10007820, 0x83 }, + { 0x10007821, 0xc7 }, + { 0x10007822, 0x11 }, + { 0x10007823, 0x42 }, + { 0x10007824, 0x63 }, + { 0x10007825, 0x86 }, + { 0x10007826, 0x07 }, + { 0x10007827, 0x00 }, + { 0x10007828, 0x03 }, + { 0x10007829, 0xc7 }, + { 0x1000782a, 0x01 }, + { 0x1000782b, 0x42 }, + { 0x1000782c, 0x63 }, + { 0x1000782d, 0x10 }, + { 0x1000782e, 0x07 }, + { 0x1000782f, 0x02 }, + { 0x10007830, 0x83 }, + { 0x10007831, 0xc6 }, + { 0x10007832, 0x21 }, + { 0x10007833, 0x41 }, + { 0x10007834, 0x13 }, + { 0x10007835, 0x07 }, + { 0x10007836, 0xf0 }, + { 0x10007837, 0x01 }, + { 0x10007838, 0x13 }, + { 0x10007839, 0x05 }, + { 0x1000783a, 0xf0 }, + { 0x1000783b, 0x01 }, + { 0x1000783c, 0x63 }, + { 0x1000783d, 0x98 }, + { 0x1000783e, 0xe6 }, + { 0x1000783f, 0x02 }, + { 0x10007840, 0x63 }, + { 0x10007841, 0x8a }, + { 0x10007842, 0x07 }, + { 0x10007843, 0x02 }, + { 0x10007844, 0x83 }, + { 0x10007845, 0xc7 }, + { 0x10007846, 0x01 }, + { 0x10007847, 0x42 }, + { 0x10007848, 0x63 }, + { 0x10007849, 0x86 }, + { 0x1000784a, 0x07 }, + { 0x1000784b, 0x02 }, + { 0x1000784c, 0x83 }, + { 0x1000784d, 0xc7 }, + { 0x1000784e, 0x31 }, + { 0x1000784f, 0x42 }, + { 0x10007850, 0x63 }, + { 0x10007851, 0x86 }, + { 0x10007852, 0x07 }, + { 0x10007853, 0x00 }, + { 0x10007854, 0x83 }, + { 0x10007855, 0xc7 }, + { 0x10007856, 0x21 }, + { 0x10007857, 0x42 }, + { 0x10007858, 0x63 }, + { 0x10007859, 0x9e }, + { 0x1000785a, 0x07 }, + { 0x1000785b, 0x00 }, + { 0x1000785c, 0x03 }, + { 0x1000785d, 0xc7 }, + { 0x1000785e, 0x21 }, + { 0x1000785f, 0x41 }, + { 0x10007860, 0x93 }, + { 0x10007861, 0x07 }, + { 0x10007862, 0xb0 }, + { 0x10007863, 0x01 }, + { 0x10007864, 0x63 }, + { 0x10007865, 0x08 }, + { 0x10007866, 0xf7 }, + { 0x10007867, 0x00 }, + { 0x10007868, 0x13 }, + { 0x10007869, 0x05 }, + { 0x1000786a, 0xb0 }, + { 0x1000786b, 0x01 }, + { 0x1000786c, 0xef }, + { 0x1000786d, 0xd0 }, + { 0x1000786e, 0x0f }, + { 0x1000786f, 0xcf }, + { 0x10007870, 0xef }, + { 0x10007871, 0xd0 }, + { 0x10007872, 0x8f }, + { 0x10007873, 0xa4 }, + { 0x10007874, 0x93 }, + { 0x10007875, 0x06 }, + { 0x10007876, 0x10 }, + { 0x10007877, 0x00 }, + { 0x10007878, 0xa3 }, + { 0x10007879, 0x89 }, + { 0x1000787a, 0xd1 }, + { 0x1000787b, 0x40 }, + { 0x1000787c, 0x37 }, + { 0x1000787d, 0xd7 }, + { 0x1000787e, 0x00 }, + { 0x1000787f, 0x10 }, + { 0x10007880, 0x83 }, + { 0x10007881, 0x47 }, + { 0x10007882, 0x07 }, + { 0x10007883, 0xd9 }, + { 0x10007884, 0x93 }, + { 0x10007885, 0xf7 }, + { 0x10007886, 0xf7 }, + { 0x10007887, 0x0f }, + { 0x10007888, 0x63 }, + { 0x10007889, 0x90 }, + { 0x1000788a, 0x07 }, + { 0x1000788b, 0x02 }, + { 0x1000788c, 0x37 }, + { 0x1000788d, 0xc6 }, + { 0x1000788e, 0x00 }, + { 0x1000788f, 0x00 }, + { 0x10007890, 0x83 }, + { 0x10007891, 0x47 }, + { 0x10007892, 0x26 }, + { 0x10007893, 0x04 }, + { 0x10007894, 0x93 }, + { 0x10007895, 0xe7 }, + { 0x10007896, 0x07 }, + { 0x10007897, 0xf8 }, + { 0x10007898, 0x93 }, + { 0x10007899, 0xf7 }, + { 0x1000789a, 0xf7 }, + { 0x1000789b, 0x0f }, + { 0x1000789c, 0x23 }, + { 0x1000789d, 0x01 }, + { 0x1000789e, 0xf6 }, + { 0x1000789f, 0x04 }, + { 0x100078a0, 0x23 }, + { 0x100078a1, 0x08 }, + { 0x100078a2, 0xd7 }, + { 0x100078a3, 0xd8 }, + { 0x100078a4, 0x23 }, + { 0x100078a5, 0x09 }, + { 0x100078a6, 0x07 }, + { 0x100078a7, 0xd8 }, + { 0x100078a8, 0x83 }, + { 0x100078a9, 0x20 }, + { 0x100078aa, 0xc1 }, + { 0x100078ab, 0x00 }, + { 0x100078ac, 0x13 }, + { 0x100078ad, 0x01 }, + { 0x100078ae, 0x01 }, + { 0x100078af, 0x01 }, + { 0x100078b0, 0x67 }, + { 0x100078b1, 0x80 }, + { 0x100078b2, 0x00 }, + { 0x100078b3, 0x00 }, + { 0x3fc2bfc7, 0x00 }, + { 0x3fc2bfc6, 0x00 }, + { 0x3fc2bfc5, 0x00 }, + { 0x3fc2bfc4, 0x01 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x02 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x11 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x82 }, + { 0x1000db06, 0x04 }, + { 0x1000db07, 0xf1 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0x40 }, + { 0x0000d540, 0x01 }, +}; + +static const struct reg_default rt1320_reg_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x0b }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +static const struct reg_default rt1320_mbq_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, +}; + +static bool rt1320_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000 ... 0xc086: + case 0xc400 ... 0xc409: + case 0xc480 ... 0xc48f: + case 0xc4c0 ... 0xc4c4: + case 0xc4e0 ... 0xc4e7: + case 0xc500: + case 0xc560 ... 0xc56b: + case 0xc570: + case 0xc580 ... 0xc59a: + case 0xc5b0 ... 0xc60f: + case 0xc640 ... 0xc64f: + case 0xc670: + case 0xc680 ... 0xc683: + case 0xc700 ... 0xc76f: + case 0xc800 ... 0xc801: + case 0xc820: + case 0xc900 ... 0xc901: + case 0xc920 ... 0xc921: + case 0xca00 ... 0xca07: + case 0xca20 ... 0xca27: + case 0xca40 ... 0xca4b: + case 0xca60 ... 0xca68: + case 0xca80 ... 0xca88: + case 0xcb00 ... 0xcb0c: + case 0xcc00 ... 0xcc12: + case 0xcc80 ... 0xcc81: + case 0xcd00: + case 0xcd80 ... 0xcd82: + case 0xce00 ... 0xce4d: + case 0xcf00 ... 0xcf25: + case 0xd000 ... 0xd0ff: + case 0xd100 ... 0xd1ff: + case 0xd200 ... 0xd2ff: + case 0xd300 ... 0xd3ff: + case 0xd400 ... 0xd403: + case 0xd410 ... 0xd417: + case 0xd470 ... 0xd497: + case 0xd4dc ... 0xd50f: + case 0xd520 ... 0xd543: + case 0xd560 ... 0xd5ef: + case 0xd600 ... 0xd663: + case 0xda00 ... 0xda6e: + case 0xda80 ... 0xda9e: + case 0xdb00 ... 0xdb7f: + case 0xdc00: + case 0xdc20 ... 0xdc21: + case 0xdd00 ... 0xdd17: + case 0xde00 ... 0xde09: + case 0xdf00 ... 0xdf1b: + case 0xe000 ... 0xe847: + case 0xf717 ... 0xf719: + case 0xf720 ... 0xf723: + case 0x1000f008: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static bool rt1320_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc402 ... 0xc406: + case 0xc48c ... 0xc48f: + case 0xc560: + case 0xc5b5 ... 0xc5b7: + case 0xc5fc ... 0xc5ff: + case 0xc820: + case 0xc900: + case 0xc920: + case 0xca42: + case 0xca62: + case 0xca82: + case 0xcd00: + case 0xce03: + case 0xce10: + case 0xce14 ... 0xce17: + case 0xce44 ... 0xce49: + case 0xce4c ... 0xce4d: + case 0xcf0c: + case 0xcf10 ... 0xcf25: + case 0xd486 ... 0xd487: + case 0xd4e5 ... 0xd4e6: + case 0xd4e8 ... 0xd4ff: + case 0xd530: + case 0xd540: + case 0xd543: + case 0xdb58 ... 0xdb5f: + case 0xdb60 ... 0xdb63: + case 0xdb68 ... 0xdb69: + case 0xdb6d: + case 0xdb70 ... 0xdb71: + case 0xdb76: + case 0xdb7a: + case 0xdb7c ... 0xdb7f: + case 0xdd0c ... 0xdd13: + case 0xde02: + case 0xdf14 ... 0xdf1b: + case 0xe83c ... 0xe847: + case 0xf717 ... 0xf719: + case 0xf720 ... 0xf723: + case 0x10000000 ... 0x10007fff: + case 0x1000c000 ... 0x1000dfff: + case 0x1000f008: + case 0x3fc2bfc4 ... 0x3fc2bfc7: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static bool rt1320_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02): + return true; + default: + return false; + } +} + +static const struct regmap_config rt1320_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1320_readable_register, + .volatile_reg = rt1320_volatile_register, + .max_register = 0x41081488, + .reg_defaults = rt1320_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1320_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt1320_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt1320_mbq_readable_register, + .max_register = 0x41000192, + .reg_defaults = rt1320_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1320_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1320_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + /* + * Due to support the multi-lane, we call 'sdw_slave_read_prop' to get the lane mapping + */ + sdw_slave_read_prop(slave); + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + prop->lane_control_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(4); + prop->sink_ports = BIT(1); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 64; + + return 0; +} + +static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + unsigned int amp_func_status, val, tmp; + + if (rt1320->hw_init) + return 0; + + regcache_cache_only(rt1320->regmap, false); + regcache_cache_only(rt1320->mbq_regmap, false); + if (rt1320->first_hw_init) { + regcache_cache_bypass(rt1320->regmap, true); + regcache_cache_bypass(rt1320->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + if (rt1320->version_id < 0) { + regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val); + rt1320->version_id = val; + } + + regmap_read(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), &_func_status); + dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status); + + /* initialization write */ + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt1320->first_hw_init)) { + regmap_multi_reg_write(rt1320->regmap, rt1320_blind_write, ARRAY_SIZE(rt1320_blind_write)); + regmap_multi_reg_write(rt1320->regmap, rt1320_patch_code_write, + ARRAY_SIZE(rt1320_patch_code_write)); + + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + if (!rt1320->first_hw_init) { + regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0); + regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val); + regmap_read(rt1320->regmap, RT1320_HIFI_VER_1, &tmp); + val = (tmp << 8) | val; + regmap_read(rt1320->regmap, RT1320_HIFI_VER_2, &tmp); + val = (tmp << 16) | val; + regmap_read(rt1320->regmap, RT1320_HIFI_VER_3, &tmp); + val = (tmp << 24) | val; + dev_dbg(dev, "%s ROM version=0x%x\n", __func__, val); + /* + * We call the version b which has the new DSP ROM code against version a. + * Therefore, we read the DSP address to check the ID. + */ + if (val == RT1320_VER_B_ID) + rt1320->version_id = RT1320_VB; + regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3); + } + dev_dbg(dev, "%s version_id=%d\n", __func__, rt1320->version_id); + + if (rt1320->first_hw_init) { + regcache_cache_bypass(rt1320->regmap, false); + regcache_cache_bypass(rt1320->mbq_regmap, false); + regcache_mark_dirty(rt1320->regmap); + regcache_mark_dirty(rt1320->mbq_regmap); + } + + /* Mark Slave initialization complete */ + rt1320->first_hw_init = true; + rt1320->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +static int rt1320_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1320->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1320->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1320_io_init(&slave->dev, slave); +} + +static int rt1320_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + default: + break; + } + + return 0; +} + +static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + + regmap_read(rt1320->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt1320->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + + if (lvalue == gain_l_val && rvalue == gain_r_val) + return 0; + + /* Lch*/ + regmap_write(rt1320->mbq_regmap, mc->reg, gain_l_val); + /* Rch */ + regmap_write(rt1320->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt1320->mbq_regmap, mc->reg, &read_l); + regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return 1; + + return -EIO; +} + +static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + const unsigned int interval_offset = 0xc0; + + regmap_read(rt1320->mbq_regmap, mc->reg, &read_l); + regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r); + + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + + if (read_l != read_r) + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + else + ctl_r = ctl_l; + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + return 0; +} + +static const char * const rt1320_rx_data_ch_select[] = { + "L,R", + "R,L", + "L,L", + "R,R", + "L,L+R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0, + rt1320_rx_data_ch_select); + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); + +static const struct snd_kcontrol_new rt1320_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), + 0, 0x57, 0, rt1320_set_gain_get, rt1320_set_gain_put, out_vol_tlv), + SOC_ENUM("RX Channel Select", rt1320_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1320_spk_l_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), + 0, 1, 1); +static const struct snd_kcontrol_new rt1320_spk_r_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_PGA("FU21", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt1320_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Output */ + SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt1320_spk_l_dac), + SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt1320_spk_r_dac), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + + /* Input */ + SND_SOC_DAPM_PGA("AEC Data", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("AEC Gen"), +}; + +static const struct snd_soc_dapm_route rt1320_dapm_routes[] = { + { "FU21", NULL, "DP1RX" }, + { "FU21", NULL, "PDE 23" }, + { "OT23 L", "Switch", "FU21" }, + { "OT23 R", "Switch", "FU21" }, + { "SPOL", NULL, "OT23 L" }, + { "SPOR", NULL, "OT23 R" }, + + { "AEC Data", NULL, "AEC Gen" }, + { "DP4TX", NULL, "AEC Data" }, +}; + +static int rt1320_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + return 0; +} + +static void rt1320_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1320->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (dai->id == RT1320_AIF1) + port_config.num = 1; + else + return -EINVAL; + } else { + if (dai->id == RT1320_AIF1) + port_config.num = 4; + else + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 16000: + sampling_rate = RT1320_SDCA_RATE_16000HZ; + break; + case 32000: + sampling_rate = RT1320_SDCA_RATE_32000HZ; + break; + case 44100: + sampling_rate = RT1320_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1320_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1320_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1320_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1320->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1320->sdw_slave, sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt1320_slave_ops = { + .read_prop = rt1320_read_prop, + .update_status = rt1320_update_status, +}; + +static int rt1320_sdw_component_probe(struct snd_soc_component *component) +{ + int ret; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + rt1320->component = component; + + if (!rt1320->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + dev_dbg(&rt1320->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_component_sdw_rt1320 = { + .probe = rt1320_sdw_component_probe, + .controls = rt1320_snd_controls, + .num_controls = ARRAY_SIZE(rt1320_snd_controls), + .dapm_widgets = rt1320_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1320_dapm_widgets), + .dapm_routes = rt1320_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1320_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt1320_aif_dai_ops = { + .hw_params = rt1320_sdw_hw_params, + .hw_free = rt1320_sdw_pcm_hw_free, + .set_stream = rt1320_set_sdw_stream, + .shutdown = rt1320_sdw_shutdown, +}; + +#define RT1320_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver rt1320_sdw_dai[] = { + { + .name = "rt1320-aif1", + .id = RT1320_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .ops = &rt1320_aif_dai_ops, + }, +}; + +static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt1320_sdw_priv *rt1320; + int ret; + + rt1320 = devm_kzalloc(dev, sizeof(*rt1320), GFP_KERNEL); + if (!rt1320) + return -ENOMEM; + + dev_set_drvdata(dev, rt1320); + rt1320->sdw_slave = slave; + rt1320->mbq_regmap = mbq_regmap; + rt1320->regmap = regmap; + + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1320->hw_init = false; + rt1320->first_hw_init = false; + rt1320->version_id = -1; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1320, + rt1320_sdw_dai, + ARRAY_SIZE(rt1320_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +static int rt1320_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt1320_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt1320_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt1320_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +/* + * Version A/B will use the class id 0 + * The newer version than A/B will use the class id 1, so add it in advance + */ +static const struct sdw_device_id rt1320_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1320_id); + +static int __maybe_unused rt1320_dev_suspend(struct device *dev) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + + if (!rt1320->hw_init) + return 0; + + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + return 0; +} + +#define RT1320_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1320_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1320->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1320_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1320->regmap, false); + regcache_sync(rt1320->regmap); + regcache_cache_only(rt1320->mbq_regmap, false); + regcache_sync(rt1320->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt1320_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume) + SET_RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL) +}; + +static struct sdw_driver rt1320_sdw_driver = { + .driver = { + .name = "rt1320-sdca", + .pm = &rt1320_pm, + }, + .probe = rt1320_sdw_probe, + .remove = rt1320_sdw_remove, + .ops = &rt1320_slave_ops, + .id_table = rt1320_id, +}; +module_sdw_driver(rt1320_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h new file mode 100644 index 000000000000..b23228e74568 --- /dev/null +++ b/sound/soc/codecs/rt1320-sdw.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1320-sdw.h -- RT1320 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + */ + +#ifndef __RT1320_SDW_H__ +#define __RT1320_SDW_H__ + +#include +#include +#include +#include +#include + +/* imp-defined registers */ +#define RT1320_DEV_VERSION_ID_1 0xc404 + +#define RT1320_KR0_STATUS_CNT 0x1000f008 +#define RT1320_HIFI_VER_0 0x3fe2e000 +#define RT1320_HIFI_VER_1 0x3fe2e001 +#define RT1320_HIFI_VER_2 0x3fe2e002 +#define RT1320_HIFI_VER_3 0x3fe2e003 + +/* RT1320 SDCA Control - function number */ +#define FUNC_NUM_AMP 0x04 + +/* RT1320 SDCA entity */ +#define RT1320_SDCA_ENT0 0x00 +#define RT1320_SDCA_ENT_PDE11 0x2a +#define RT1320_SDCA_ENT_PDE23 0x33 +#define RT1320_SDCA_ENT_PDE27 0x27 +#define RT1320_SDCA_ENT_FU14 0x32 +#define RT1320_SDCA_ENT_FU21 0x03 +#define RT1320_SDCA_ENT_FU113 0x30 +#define RT1320_SDCA_ENT_CS14 0x13 +#define RT1320_SDCA_ENT_CS21 0x21 +#define RT1320_SDCA_ENT_CS113 0x12 +#define RT1320_SDCA_ENT_SAPU 0x29 +#define RT1320_SDCA_ENT_PPU21 0x04 + +/* RT1320 SDCA control */ +#define RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT1320_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1320_SDCA_CTL_FU_MUTE 0x01 +#define RT1320_SDCA_CTL_FU_VOLUME 0x02 +#define RT1320_SDCA_CTL_SAPU_PROTECTION_MODE 0x10 +#define RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11 +#define RT1320_SDCA_CTL_POSTURE_NUMBER 0x10 +#define RT1320_SDCA_CTL_FUNC_STATUS 0x10 + +/* RT1320 SDCA channel */ +#define CH_01 0x01 +#define CH_02 0x02 + +/* Function_Status */ +#define FUNCTION_NEEDS_INITIALIZATION BIT(5) + +/* Sample Frequency Index */ +#define RT1320_SDCA_RATE_16000HZ 0x04 +#define RT1320_SDCA_RATE_32000HZ 0x07 +#define RT1320_SDCA_RATE_44100HZ 0x08 +#define RT1320_SDCA_RATE_48000HZ 0x09 +#define RT1320_SDCA_RATE_96000HZ 0x0b +#define RT1320_SDCA_RATE_192000HZ 0x0d + +enum { + RT1320_AIF1, +}; + +/* + * The version id will be useful to distinguish the capability between the different IC versions. + * Currently, VA and VB have different DSP FW versions. + */ +enum rt1320_version_id { + RT1320_VA, + RT1320_VB, +}; + +#define RT1320_VER_B_ID 0x07392238 + +struct rt1320_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regmap *mbq_regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + int version_id; +}; + +#endif /* __RT1320_SDW_H__ */ -- cgit v1.2.3 From ad72a1e7c0534b9d533ac967a880b8f8d0b83bc0 Mon Sep 17 00:00:00 2001 From: Xiaxi Shen Date: Thu, 16 May 2024 22:13:17 -0700 Subject: ASoC: dt-bindings: ak4104: convert to dt schema Convert ak4104 binding to DT schema Signed-off-by: Xiaxi Shen Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240517051317.466764-1-shenxiaxi26@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4104.txt | 25 ----------- .../bindings/sound/asahi-kasei,ak4104.yaml | 49 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 25 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/ak4104.txt create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt deleted file mode 100644 index ae5f7f057dc3..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4104.txt +++ /dev/null @@ -1,25 +0,0 @@ -AK4104 S/PDIF transmitter - -This device supports SPI mode only. - -Required properties: - - - compatible : "asahi-kasei,ak4104" - - - reg : The chip select number on the SPI bus - - - vdd-supply : A regulator node, providing 2.7V - 3.6V - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the device starts. - -Example: - -spdif: ak4104@0 { - compatible = "asahi-kasei,ak4104"; - reg = <0>; - spi-max-frequency = <5000000>; - vdd-supply = <&vdd_3v3_reg>; -}; diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml new file mode 100644 index 000000000000..86f6061d3c50 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4104.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4104 S/PDIF transmitter + +allOf: + - $ref: dai-common.yaml# + +maintainers: + - Daniel Mack + - Xiaxi Shen + +properties: + compatible: + const: asahi-kasei,ak4104 + + reg: + description: Chip select number on the SPI bus + maxItems: 1 + + vdd-supply: + description: A regulator node providing between 2.7V and 3.6V. + + reset-gpios: + maxItems: 1 + description: Optional GPIO spec for the reset pin, deasserted + before communication starts. + +required: + - compatible + - reg + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@0 { + compatible = "asahi-kasei,ak4104"; + reg = <0>; + vdd-supply = <&vdd_3v3_reg>; + }; + }; -- cgit v1.2.3 From f20847d92caa51c31556a64f0849903c55ed38c1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 12:42:25 +0200 Subject: ASoC: codecs: wcd938x: Drop unused duplicated MIC2 bias register defines All MIC1-4 bias registers are the same and the header already defines register fields for them. Drop unused, duplicated defines for MIC2 bias enable register. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523104228.36263-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 74b1498fec38..df07e92d9d97 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -75,9 +75,7 @@ #define WCD938X_MICB_PULL_UP 2 #define WCD938X_MICB_PULL_DOWN 3 #define WCD938X_ANA_MICB2 (0x3023) -#define WCD938X_ANA_MICB2_ENABLE BIT(6) #define WCD938X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) -#define WCD938X_ANA_MICB2_VOUT_MASK GENMASK(5, 0) #define WCD938X_ANA_MICB2_RAMP (0x3024) #define WCD938X_RAMP_EN_MASK BIT(7) #define WCD938X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) -- cgit v1.2.3 From 40b2ffaf594cdf9e4a86d4f28b392e58b9dafafd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 12:42:26 +0200 Subject: ASoC: codecs: wcd938x: Unify define used for MIC2 bias register There is a define for MIC bias registers enable mask called WCD938X_MICB_EN_MASK, which is already used to handle all four MIC1-4 biases in wcd938x_mbhc_micb_adjust_voltage(). Use the same define also in wcd938x_mbhc_micb_en_status() for MIC2 to have unified, simpler approach. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523104228.36263-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 2 +- sound/soc/codecs/wcd938x.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 6021aa5a5689..9e2070356b84 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -1982,7 +1982,7 @@ static bool wcd938x_mbhc_micb_en_status(struct snd_soc_component *component, int if (micb_num == MIC_BIAS_2) { val = snd_soc_component_read_field(component, WCD938X_ANA_MICB2, - WCD938X_ANA_MICB2_ENABLE_MASK); + WCD938X_MICB_EN_MASK); if (val == WCD938X_MICB_ENABLE) return true; } diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index df07e92d9d97..0d332cb555ac 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -75,7 +75,6 @@ #define WCD938X_MICB_PULL_UP 2 #define WCD938X_MICB_PULL_DOWN 3 #define WCD938X_ANA_MICB2 (0x3023) -#define WCD938X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) #define WCD938X_ANA_MICB2_RAMP (0x3024) #define WCD938X_RAMP_EN_MASK BIT(7) #define WCD938X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) -- cgit v1.2.3 From 42cef39b76ee705118b27b23a0117e382d4d4a35 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 12:42:27 +0200 Subject: ASoC: codecs: wcd939x: Unify define used for MIC bias registers All four microphone bias registers have similar layout: enable mask is exactly the same. Use one define, just like older driver wcd938x is doing, to make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523104228.36263-3-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 36 +++++++++++++++++------------------- sound/soc/codecs/wcd939x.h | 5 +---- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index c49894aad8a5..201592c728c3 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -1268,25 +1268,20 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, { struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); int micb_index = micb_num - 1; - u16 micb_field; u16 micb_reg; switch (micb_num) { case MIC_BIAS_1: micb_reg = WCD939X_ANA_MICB1; - micb_field = WCD939X_MICB1_ENABLE; break; case MIC_BIAS_2: micb_reg = WCD939X_ANA_MICB2; - micb_field = WCD939X_MICB2_ENABLE; break; case MIC_BIAS_3: micb_reg = WCD939X_ANA_MICB3; - micb_field = WCD939X_MICB3_ENABLE; break; case MIC_BIAS_4: micb_reg = WCD939X_ANA_MICB4; - micb_field = WCD939X_MICB4_ENABLE; break; default: dev_err(component->dev, "%s: Invalid micbias number: %d\n", @@ -1300,7 +1295,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, if (wcd939x->pullup_ref[micb_index] == 1 && wcd939x->micb_ref[micb_index] == 0) snd_soc_component_write_field(component, micb_reg, - micb_field, MICB_BIAS_PULL_UP); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_UP); break; case MICB_PULLUP_DISABLE: if (wcd939x->pullup_ref[micb_index] > 0) @@ -1308,7 +1304,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, if (wcd939x->pullup_ref[micb_index] == 0 && wcd939x->micb_ref[micb_index] == 0) snd_soc_component_write_field(component, micb_reg, - micb_field, MICB_BIAS_DISABLE); + WCD939X_MICB_ENABLE, + MICB_BIAS_DISABLE); break; case MICB_ENABLE: wcd939x->micb_ref[micb_index]++; @@ -1345,7 +1342,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_2, WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); - snd_soc_component_write_field(component, micb_reg, micb_field, + snd_soc_component_write_field(component, micb_reg, + WCD939X_MICB_ENABLE, MICB_BIAS_ENABLE); if (micb_num == MIC_BIAS_2) wcd_mbhc_event_notify(wcd939x->wcd_mbhc, @@ -1362,7 +1360,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, if (wcd939x->micb_ref[micb_index] == 0 && wcd939x->pullup_ref[micb_index] > 0) snd_soc_component_write_field(component, micb_reg, - micb_field, MICB_BIAS_PULL_UP); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_UP); else if (wcd939x->micb_ref[micb_index] == 0 && wcd939x->pullup_ref[micb_index] == 0) { if (micb_num == MIC_BIAS_2) @@ -1370,7 +1369,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, WCD_EVENT_PRE_MICBIAS_2_OFF); snd_soc_component_write_field(component, micb_reg, - micb_field, MICB_BIAS_DISABLE); + WCD939X_MICB_ENABLE, + MICB_BIAS_DISABLE); if (micb_num == MIC_BIAS_2) wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_POST_MICBIAS_2_OFF); @@ -1873,7 +1873,7 @@ static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int if (micb_num == MIC_BIAS_2) { u8 val; - val = FIELD_GET(WCD939X_MICB2_ENABLE, + val = FIELD_GET(WCD939X_MICB_ENABLE, snd_soc_component_read(component, WCD939X_ANA_MICB2)); if (val == MICB_BIAS_ENABLE) return true; @@ -1935,7 +1935,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); - unsigned int micb_en_field, micb_vout_ctl_field; + unsigned int micb_vout_ctl_field; unsigned int micb_reg, cur_vout_ctl, micb_en; int req_vout_ctl; int ret = 0; @@ -1943,22 +1943,18 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, switch (micb_num) { case MIC_BIAS_1: micb_reg = WCD939X_ANA_MICB1; - micb_en_field = WCD939X_MICB1_ENABLE; micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; break; case MIC_BIAS_2: micb_reg = WCD939X_ANA_MICB2; - micb_en_field = WCD939X_MICB2_ENABLE; micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; break; case MIC_BIAS_3: micb_reg = WCD939X_ANA_MICB3; - micb_en_field = WCD939X_MICB3_ENABLE; micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; break; case MIC_BIAS_4: micb_reg = WCD939X_ANA_MICB4; - micb_en_field = WCD939X_MICB4_ENABLE; micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; break; default: @@ -1975,7 +1971,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, * micbias. */ micb_en = snd_soc_component_read_field(component, micb_reg, - micb_en_field); + WCD939X_MICB_ENABLE); cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, micb_vout_ctl_field); @@ -1996,14 +1992,16 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, if (micb_en == MICB_BIAS_ENABLE) snd_soc_component_write_field(component, micb_reg, - micb_en_field, MICB_BIAS_PULL_DOWN); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_DOWN); snd_soc_component_write_field(component, micb_reg, micb_vout_ctl_field, req_vout_ctl); if (micb_en == MICB_BIAS_ENABLE) { snd_soc_component_write_field(component, micb_reg, - micb_en_field, MICB_BIAS_ENABLE); + WCD939X_MICB_ENABLE, + MICB_BIAS_ENABLE); /* * Add 2ms delay as per HW requirement after enabling * micbias diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 807cf3113d20..383a79316439 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -91,10 +91,9 @@ #define WCD939X_ANA_MBHC_BTN7 (0x3021) #define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2) #define WCD939X_ANA_MICB1 (0x3022) -#define WCD939X_MICB1_ENABLE GENMASK(7, 6) +#define WCD939X_MICB_ENABLE GENMASK(7, 6) #define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB2 (0x3023) -#define WCD939X_MICB2_ENABLE GENMASK(7, 6) #define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB2_RAMP (0x3024) #define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7) @@ -103,10 +102,8 @@ #define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2) #define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0) #define WCD939X_ANA_MICB3 (0x3025) -#define WCD939X_MICB3_ENABLE GENMASK(7, 6) #define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB4 (0x3026) -#define WCD939X_MICB4_ENABLE GENMASK(7, 6) #define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0) #define WCD939X_BIAS_CTL (0x3028) #define WCD939X_BIAS_VBG_FINE_ADJ (0x3029) -- cgit v1.2.3 From 6f80324bc1317f50f1c25c6d65a8dbef417aeba4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 12:42:28 +0200 Subject: ASoC: codecs: wcd939x: Minor white-space and define cleanup Correct white-space issues and drop unused WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED() define. No functional impact. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523104228.36263-4-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 201592c728c3..770bc2f35ba7 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -85,7 +85,6 @@ enum { #define WCD939X_MBHC_GET_X1(x) ((x) & 0x3FFF) /* Z value compared in milliOhm */ -#define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false #define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024) enum { @@ -525,7 +524,7 @@ static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, WCD939X_DIGITAL_CDC_COMP_CTL_0, WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, true); - /* 5msec compander delay as per HW requirement */ + /* 5msec compander delay as per HW requirement */ if (!wcd939x->comp2_enable || snd_soc_component_read_field(component, WCD939X_DIGITAL_CDC_COMP_CTL_0, @@ -1869,7 +1868,6 @@ static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component, static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) { - if (micb_num == MIC_BIAS_2) { u8 val; -- cgit v1.2.3 From d556f807bda0b260b392069b3c8ac9e6491f0025 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 16 May 2024 10:56:11 +0300 Subject: ASoC: SOF: ipc4-topology: Add support for NHLT with 16-bit only DMIC blob The ACPI NHLT table always had 32-bit DMIC blob even if 16-bit was also present and taken as a 'rule' which obviously got broken and there is at least one device on the market which ships with only 16-bit DMIC configuration blob. This corner case has never been supported and it is going to need topology updates for DMIC copier to support multiple formats. As for the kernel side: if the copier supports multiple formats and the preferred 32-bit DMIC blob is not found then we will try to get a 16-bit DMIC configuration and look for a 16-bit copier config. Fixes: f9209644ae76 ("ASoC: SOF: ipc4-topology: Correct DAI copier config and NHLT blob request") Link: https://github.com/thesofproject/linux/issues/4973 Signed-off-by: Peter Ujfalusi Reviewed-by: Seppo Ingalsuo Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240516075611.18018-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index beff10989324..521b4dcba601 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1483,6 +1483,8 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai dir, dev_type); if (!cfg) { + bool get_new_blob = false; + if (format_change) { /* * The 32-bit blob was not found in NHLT table, try to @@ -1490,7 +1492,20 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai */ bit_depth = params_width(params); format_change = false; + get_new_blob = true; + } else if (linktype == SOF_DAI_INTEL_DMIC && !single_format) { + /* + * The requested 32-bit blob (no format change for the + * blob request) was not found in NHLT table, try to + * look for 16-bit blob if the copier supports multiple + * formats + */ + bit_depth = 16; + format_change = true; + get_new_blob = true; + } + if (get_new_blob) { cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type, bit_depth, bit_depth, @@ -1513,8 +1528,8 @@ out: if (format_change) { /* - * Update the params to reflect that we have loaded 32-bit blob - * instead of the 16-bit. + * Update the params to reflect that different blob was loaded + * instead of the requested bit depth (16 -> 32 or 32 -> 16). * This information is going to be used by the caller to find * matching copier format on the dai side. */ @@ -1522,7 +1537,11 @@ out: m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_none(m); - snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE); + if (bit_depth == 16) + snd_mask_set_format(m, SNDRV_PCM_FORMAT_S16_LE); + else + snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE); + } return 0; -- cgit v1.2.3 From 734447685ecc7c6328e40cb1bd4aaeeac03c1413 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:20 +0200 Subject: ASoC: topology: Constify an argument of snd_soc_tplg_component_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snd_soc_tplg_component_load() does not modify its "*ops" argument. It only read some values and stores it in "soc_tplg.ops". This argument and the ops field in "struct soc_tplg" can be made const. Signed-off-by: Christophe JAILLET Reviewed-by: Amadeusz Sławiński Link: https://msgid.link/r/f2f983e791d7f941a95556bb147f426a345d84d4.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 2 +- sound/soc/soc-topology.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f055c6917f6c..1eedd203ac29 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -178,7 +178,7 @@ static inline const void *snd_soc_tplg_get_data(struct snd_soc_tplg_hdr *hdr) /* Dynamic Object loading and removal for component drivers */ int snd_soc_tplg_component_load(struct snd_soc_component *comp, - struct snd_soc_tplg_ops *ops, const struct firmware *fw); + const struct snd_soc_tplg_ops *ops, const struct firmware *fw); int snd_soc_tplg_component_remove(struct snd_soc_component *comp); /* Binds event handlers to dynamic widgets */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 90ca37e008b3..b00ec01361c2 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -73,7 +73,7 @@ struct soc_tplg { int bytes_ext_ops_count; /* optional fw loading callbacks to component drivers */ - struct snd_soc_tplg_ops *ops; + const struct snd_soc_tplg_ops *ops; }; /* check we dont overflow the data for this control chunk */ @@ -2334,7 +2334,7 @@ static int soc_tplg_load(struct soc_tplg *tplg) /* load audio component topology from "firmware" file */ int snd_soc_tplg_component_load(struct snd_soc_component *comp, - struct snd_soc_tplg_ops *ops, const struct firmware *fw) + const struct snd_soc_tplg_ops *ops, const struct firmware *fw) { struct soc_tplg tplg; int ret; -- cgit v1.2.3 From e51c001faa59fd751ef16bac6cf373fb7fc91e9c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:21 +0200 Subject: ASoC: Intel: avs: Constify struct snd_soc_tplg_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constifying "struct snd_soc_tplg_ops" moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 28046 794 0 28840 70a8 sound/soc/intel/avs/topology.o After: text data bss dec hex filename 28206 614 0 28820 7094 sound/soc/intel/avs/topology.o Signed-off-by: Christophe JAILLET Reviewed-by: Amadeusz Sławiński Link: https://msgid.link/r/01de0157088de0475970c79738284d6c86d702fe.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/intel/avs/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 02bae207f6ec..35381a835c93 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1889,7 +1889,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ return 0; } -static struct snd_soc_tplg_ops avs_tplg_ops = { +static const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count = ARRAY_SIZE(avs_control_ops), .control_load = avs_control_load, -- cgit v1.2.3 From 4a341101337fc158b993417a9d2085815b0201da Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:22 +0200 Subject: ASoC: qdsp6: audioreach: Constify struct snd_soc_tplg_ops Constifying "struct snd_soc_tplg_ops" moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 19942 832 0 20774 5126 sound/soc/qcom/qdsp6/topology.o After: text data bss dec hex filename 20102 652 0 20754 5112 sound/soc/qcom/qdsp6/topology.o Signed-off-by: Christophe JAILLET Link: https://msgid.link/r/a5ae843dd8aaacbc1148aea7a3b1d03f11495872.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 70572c83e101..c15d1a2b6dbf 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -1240,7 +1240,7 @@ static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = { audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw}, }; -static struct snd_soc_tplg_ops audioreach_tplg_ops = { +static const struct snd_soc_tplg_ops audioreach_tplg_ops = { .io_ops = audioreach_io_ops, .io_ops_count = ARRAY_SIZE(audioreach_io_ops), -- cgit v1.2.3 From 9ff6aaf0f7e99798afeda974c83e410e9e2c3026 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:23 +0200 Subject: ASoC: Intel: Skylake: Constify struct snd_soc_tplg_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constifying "struct snd_soc_tplg_ops" moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 58844 5282 56 64182 fab6 sound/soc/intel/skylake/skl-topology.o After: text data bss dec hex filename 59004 5122 56 64182 fab6 sound/soc/intel/skylake/skl-topology.o Signed-off-by: Christophe JAILLET Reviewed-by: Amadeusz Sławiński Link: https://msgid.link/r/48f096b6dc617ecf3ca53211c2a696a4df33b21a.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e27f0fc3d897..602ef4321122 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3470,7 +3470,7 @@ static int skl_tplg_complete(struct snd_soc_component *component) return 0; } -static struct snd_soc_tplg_ops skl_tplg_ops = { +static const struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, .bytes_ext_ops = skl_tlv_ops, -- cgit v1.2.3 From e9c33917932e940348833bf4a1ad3c5ffceb211e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:24 +0200 Subject: ASoC: SOF: topology: Constify struct snd_soc_tplg_ops Constifying "struct snd_soc_tplg_ops" moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 44519 2888 48 47455 b95f sound/soc/sof/topology.o After: text data bss dec hex filename 44839 2552 48 47439 b94f sound/soc/sof/topology.o Signed-off-by: Christophe JAILLET Link: https://msgid.link/r/2d9f5b75e979eb38b6f3baf85dfa1f0fdb3447ef.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index da182314aa87..b54382131991 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2278,7 +2278,7 @@ static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_bytes_ext_volatile_get}, }; -static struct snd_soc_tplg_ops sof_tplg_ops = { +static const struct snd_soc_tplg_ops sof_tplg_ops = { /* external kcontrol init - used for any driver specific init */ .control_load = sof_control_load, .control_unload = sof_control_unload, @@ -2433,7 +2433,7 @@ static int sof_dspless_link_load(struct snd_soc_component *scomp, int index, return 0; } -static struct snd_soc_tplg_ops sof_dspless_tplg_ops = { +static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { /* external widget init - used for any driver specific init */ .widget_ready = sof_dspless_widget_ready, .widget_unload = sof_dspless_widget_unload, -- cgit v1.2.3 From 8951ae0b2e096387154fa05887259b405cc05383 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:37:25 +0200 Subject: const_structs.checkpatch: add snd_soc_tplg_ops Now that the soc-topology can handle "const struct snd_soc_tplg_ops" make sure that new usages of the struct already enter the tree as const. Signed-off-by: Christophe JAILLET Link: https://msgid.link/r/6936369a414077265c11115c98364ae21e3cbdda.1715526069.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- scripts/const_structs.checkpatch | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index 52e5bfb61fd0..e9589c372968 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -79,6 +79,7 @@ snd_rawmidi_ops snd_soc_component_driver snd_soc_dai_ops snd_soc_ops +snd_soc_tplg_ops soc_pcmcia_socket_ops stacktrace_ops sysfs_ops -- cgit v1.2.3 From 560495c04d053b98f1349249a8d0545fb25d84ec Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 May 2024 14:37:33 -0300 Subject: ASoC: fsl: fsl_aud2htx: Switch to RUNTIME_PM_OPS() Replace SET_RUNTIME_PM_OPS() with its modern alternative RUNTIME_PM_OPS(). The combined usage of pm_ptr() and RUNTIME_PM_OPS() allows the compiler to evaluate if the runtime suspend/resume() functions are used at build time or are simply dead code. This allows removing the __maybe_unused notation from the runtime suspend/resume() functions. Signed-off-by: Fabio Estevam Link: https://msgid.link/r/20240507173735.3456334-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_aud2htx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index ee2f6ad1f800..a6cbaa6364c7 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -261,7 +261,7 @@ static void fsl_aud2htx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) +static int fsl_aud2htx_runtime_suspend(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); @@ -271,7 +271,7 @@ static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) +static int fsl_aud2htx_runtime_resume(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); int ret; @@ -288,9 +288,8 @@ static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) } static const struct dev_pm_ops fsl_aud2htx_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, - fsl_aud2htx_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume, + NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -300,7 +299,7 @@ static struct platform_driver fsl_aud2htx_driver = { .remove_new = fsl_aud2htx_remove, .driver = { .name = "fsl-aud2htx", - .pm = &fsl_aud2htx_pm_ops, + .pm = pm_ptr(&fsl_aud2htx_pm_ops), .of_match_table = fsl_aud2htx_dt_ids, }, }; -- cgit v1.2.3 From fa14065d71cb0cfe8eb445b23fd313922693a1fa Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 May 2024 14:37:34 -0300 Subject: ASoC: fsl: fsl_easrc: Switch to RUNTIME_PM_OPS() Replace SET_RUNTIME_PM_OPS() with its modern alternative RUNTIME_PM_OPS(). The combined usage of pm_ptr() and RUNTIME_PM_OPS() allows the compiler to evaluate if the runtime suspend/resume() functions are used at build time or are simply dead code. This allows removing the __maybe_unused notation from the runtime suspend/resume() functions. Signed-off-by: Fabio Estevam Link: https://msgid.link/r/20240507173735.3456334-2-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index ec53bda46a46..962f30912091 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1988,7 +1988,7 @@ static void fsl_easrc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev) +static int fsl_easrc_runtime_suspend(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; @@ -2005,7 +2005,7 @@ static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int fsl_easrc_runtime_resume(struct device *dev) +static int fsl_easrc_runtime_resume(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; @@ -2086,9 +2086,7 @@ disable_mem_clk: } static const struct dev_pm_ops fsl_easrc_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, - fsl_easrc_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -2098,7 +2096,7 @@ static struct platform_driver fsl_easrc_driver = { .remove_new = fsl_easrc_remove, .driver = { .name = "fsl-easrc", - .pm = &fsl_easrc_pm_ops, + .pm = pm_ptr(&fsl_easrc_pm_ops), .of_match_table = fsl_easrc_dt_ids, }, }; -- cgit v1.2.3 From 5dfd8f78b57641530bb674791b66b4ae8a8417f5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 7 May 2024 14:37:35 -0300 Subject: ASoC: fsl: fsl_xcvr: Switch to RUNTIME_PM_OPS() Replace SET_RUNTIME_PM_OPS() with its modern alternative RUNTIME_PM_OPS(). The combined usage of pm_ptr() and RUNTIME_PM_OPS() allows the compiler to evaluate if the runtime suspend/resume() functions are used at build time or are simply dead code. This allows removing the __maybe_unused notation from the runtime suspend/resume() functions. Signed-off-by: Fabio Estevam Link: https://msgid.link/r/20240507173735.3456334-3-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index c46f64557a7f..5472ace23d82 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -1364,7 +1364,7 @@ static void fsl_xcvr_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) +static int fsl_xcvr_runtime_suspend(struct device *dev) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; @@ -1398,7 +1398,7 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev) +static int fsl_xcvr_runtime_resume(struct device *dev) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; @@ -1483,9 +1483,7 @@ stop_ipg_clk: } static const struct dev_pm_ops fsl_xcvr_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, - fsl_xcvr_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -1494,7 +1492,7 @@ static struct platform_driver fsl_xcvr_driver = { .probe = fsl_xcvr_probe, .driver = { .name = "fsl,imx8mp-audio-xcvr", - .pm = &fsl_xcvr_pm_ops, + .pm = pm_ptr(&fsl_xcvr_pm_ops), .of_match_table = fsl_xcvr_dt_ids, }, .remove_new = fsl_xcvr_remove, -- cgit v1.2.3 From ed2581305360725381190bc442918e4e5b2b8545 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 13 May 2024 19:57:16 +0200 Subject: ASoC: intel: Constify struct snd_soc_ops Constifying "struct snd_soc_ops" moves some data to a read-only section, so increase overall security. This structure is also part of scripts/const_structs.checkpatch. As an example, on a x86_64, with allmodconfig: Before: text data bss dec hex filename 6315 3696 0 10011 271b sound/soc/intel/boards/ehl_rt5660.o After: text data bss dec hex filename 6379 3648 0 10027 272b sound/soc/intel/boards/ehl_rt5660.o Signed-off-by: Christophe JAILLET Link: https://msgid.link/r/2f0613bf4c6018569cdaac876d0589e49cf38a80.1715622793.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5650.c | 2 +- sound/soc/intel/boards/ehl_rt5660.c | 2 +- sound/soc/intel/boards/kbl_da7219_max98357a.c | 4 ++-- sound/soc/intel/boards/kbl_da7219_max98927.c | 6 +++--- sound/soc/intel/boards/kbl_rt5660.c | 2 +- sound/soc/intel/boards/kbl_rt5663_max98927.c | 8 ++++---- sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 6 +++--- sound/soc/intel/boards/sof_es8336.c | 2 +- sound/soc/intel/boards/sof_nau8825.c | 2 +- sound/soc/intel/boards/sof_realtek_common.c | 2 +- sound/soc/intel/boards/sof_rt5682.c | 2 +- sound/soc/intel/boards/sof_sdw_common.h | 2 +- sound/soc/intel/boards/sof_sdw_rt_amp.c | 2 +- sound/soc/intel/boards/sof_wm8804.c | 2 +- 14 files changed, 22 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index 3ae26f21458f..3c7cee03a02e 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -131,7 +131,7 @@ static int bdw_rt5650_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops bdw_rt5650_ops = { +static const struct snd_soc_ops bdw_rt5650_ops = { .hw_params = bdw_rt5650_hw_params, }; diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c index 686e60321224..26289e8fdd87 100644 --- a/sound/soc/intel/boards/ehl_rt5660.c +++ b/sound/soc/intel/boards/ehl_rt5660.c @@ -132,7 +132,7 @@ static int rt5660_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops rt5660_ops = { +static const struct snd_soc_ops rt5660_ops = { .hw_params = rt5660_hw_params, }; diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 9dbc15f9d1c9..154f6a74ed15 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -354,7 +354,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -388,7 +388,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index e662da5af83b..02ed77a07e23 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -288,7 +288,7 @@ static int kabylake_ssp0_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, .trigger = kabylake_ssp0_trigger, }; @@ -535,7 +535,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -569,7 +569,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index 894d127c482a..66885cb36f24 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -277,7 +277,7 @@ static int kabylake_rt5660_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5660_ops = { +static const struct snd_soc_ops kabylake_rt5660_ops = { .hw_params = kabylake_rt5660_hw_params, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index e16c42e81eca..9da89436a917 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -489,7 +489,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5663_ops = { +static const struct snd_soc_ops kabylake_rt5663_ops = { .hw_params = kabylake_rt5663_hw_params, }; @@ -539,7 +539,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, }; @@ -575,7 +575,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -609,7 +609,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index a9501cd106ff..a32ce8f972f3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -424,7 +424,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5663_ops = { +static const struct snd_soc_ops kabylake_rt5663_ops = { .hw_params = kabylake_rt5663_hw_params, }; @@ -469,7 +469,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, }; @@ -508,7 +508,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index c1fcc156a575..2a88efaa6d26 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -371,7 +371,7 @@ static int sof_es8336_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops sof_es8336_ops = { +static const struct snd_soc_ops sof_es8336_ops = { .hw_params = sof_es8336_hw_params, .trigger = sof_8336_trigger, }; diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index c08b4eef0bcb..bfe17acbc161 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -115,7 +115,7 @@ static int sof_nau8825_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops sof_nau8825_ops = { +static const struct snd_soc_ops sof_nau8825_ops = { .hw_params = sof_nau8825_hw_params, }; diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index dda346e0f737..f52e25083905 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -452,7 +452,7 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops rt1015_ops = { +static const struct snd_soc_ops rt1015_ops = { .hw_params = rt1015_hw_params, }; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 6fc6eb0c5172..23a40b913290 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -397,7 +397,7 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops sof_rt5682_ops = { +static const struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 3dfba6f6b95d..7dedac918065 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -169,7 +169,7 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* RT1308 I2S support */ -extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; +extern const struct snd_soc_ops sof_sdw_rt1308_i2s_ops; /* generic amp support */ int sof_sdw_rt_amp_init(struct snd_soc_card *card, diff --git a/sound/soc/intel/boards/sof_sdw_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index 797ea9ffa77a..d1c0f91ce589 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_amp.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -233,7 +233,7 @@ static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { +const struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { .hw_params = rt1308_i2s_hw_params, }; diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c index 4cb0d463bf40..b2d02cc92a6a 100644 --- a/sound/soc/intel/boards/sof_wm8804.c +++ b/sound/soc/intel/boards/sof_wm8804.c @@ -148,7 +148,7 @@ static int sof_wm8804_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops sof_wm8804_ops = { +static const struct snd_soc_ops sof_wm8804_ops = { .hw_params = sof_wm8804_hw_params, }; -- cgit v1.2.3 From 825fc49497957310e421454fe3fb8b8d8d8e2dd2 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Tue, 21 May 2024 18:22:54 +0200 Subject: media: mgb4: Fix double debugfs remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an error where debugfs_remove_recursive() is called first on a parent directory and then again on a child which causes a kernel panic. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil Fixes: 0ab13674a9bd ("media: pci: mgb4: Added Digiteq Automotive MGB4 driver") Cc: [hverkuil: added Fixes/Cc tags] --- drivers/media/pci/mgb4/mgb4_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index 60498a5abebf..ab4f07e2e560 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -642,9 +642,6 @@ static void mgb4_remove(struct pci_dev *pdev) struct mgb4_dev *mgbdev = pci_get_drvdata(pdev); int i; -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(mgbdev->debugfs); -#endif #if IS_REACHABLE(CONFIG_HWMON) hwmon_device_unregister(mgbdev->hwmon_dev); #endif @@ -659,6 +656,10 @@ static void mgb4_remove(struct pci_dev *pdev) if (mgbdev->vin[i]) mgb4_vin_free(mgbdev->vin[i]); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(mgbdev->debugfs); +#endif + device_remove_groups(&mgbdev->pdev->dev, mgb4_pci_groups); free_spi(mgbdev); free_i2c(mgbdev); -- cgit v1.2.3 From 7ff6c798eca05e4a9dcb80163cb454d7787a4bc3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 21 May 2024 15:46:32 -0700 Subject: ACPI: APEI: EINJ: Fix einj_dev release leak The platform driver conversion of EINJ mistakenly used platform_device_del() to unwind platform_device_register_full() at module exit. This leads to a small leak of one 'struct platform_device' instance per module load/unload cycle. Switch to platform_device_unregister() which performs both device_del() and final put_device(). Fixes: 5621fafaac00 ("EINJ: Migrate to a platform driver") Cc: 6.9+ # 6.9+ Signed-off-by: Dan Williams Reviewed-by: Ben Cheatham Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 9515bcfe5e97..73903a497d73 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -909,7 +909,7 @@ static void __exit einj_exit(void) if (einj_initialized) platform_driver_unregister(&einj_driver); - platform_device_del(einj_dev); + platform_device_unregister(einj_dev); } module_init(einj_init); -- cgit v1.2.3 From f6f172dc6a6d7775b2df6adfd1350700e9a847ec Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 22 May 2024 23:36:48 +0200 Subject: ACPI: EC: Abort address space access upon error When a multi-byte address space access is requested, acpi_ec_read()/ acpi_ec_write() is being called multiple times. Abort such operations if a single call to acpi_ec_read() / acpi_ec_write() fails, as the data read from / written to the EC might be incomplete. Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e7793ee9e649..a68dce2147a4 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1333,10 +1333,13 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (ec->busy_polling || bits > 8) acpi_ec_burst_enable(ec); - for (i = 0; i < bytes; ++i, ++address, ++value) + for (i = 0; i < bytes; ++i, ++address, ++value) { result = (function == ACPI_READ) ? acpi_ec_read(ec, address, value) : acpi_ec_write(ec, address, *value); + if (result < 0) + break; + } if (ec->busy_polling || bits > 8) acpi_ec_burst_disable(ec); -- cgit v1.2.3 From c4bd7f1d78340e63de4d073fd3dbe5391e2996e5 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 22 May 2024 23:36:49 +0200 Subject: ACPI: EC: Avoid returning AE_OK on errors in address space handler If an error code other than EINVAL, ENODEV or ETIME is returned by acpi_ec_read() / acpi_ec_write(), then AE_OK is incorrectly returned by acpi_ec_space_handler(). Fix this by only returning AE_OK on success, and return AE_ERROR otherwise. Signed-off-by: Armin Wolf [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a68dce2147a4..68dd17f96f63 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1351,8 +1351,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, return AE_NOT_FOUND; case -ETIME: return AE_TIME; - default: + case 0: return AE_OK; + default: + return AE_ERROR; } } -- cgit v1.2.3 From 9e69acc1de306b5243ca2ef8a54242e56d01b8e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 23 May 2024 17:57:21 +0200 Subject: thermal/debugfs: Print initial trip temperature and hysteresis in tze_seq_show() The temperature and hysteresis of a trip point may change during a mitigation episode it is involved in (it may even become invalid altogether), so in order to avoid possible confusion related to that, store the temperature and hysteresis of trip points at the time they are crossed on the way up and print those values instead of their current temperature and hysteresis. Fixes: 7ef01f228c9f ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_debugfs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 91f9c21235a8..e0e4eb6fad39 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -91,6 +91,8 @@ struct cdev_record { * * @timestamp: the trip crossing timestamp * @duration: total time when the zone temperature was above the trip point + * @trip_temp: trip temperature at mitigation start + * @trip_hyst: trip hysteresis at mitigation start * @count: the number of times the zone temperature was above the trip point * @max: maximum recorded temperature above the trip point * @min: minimum recorded temperature above the trip point @@ -99,6 +101,8 @@ struct cdev_record { struct trip_stats { ktime_t timestamp; ktime_t duration; + int trip_temp; + int trip_hyst; int count; int max; int min; @@ -574,6 +578,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, struct thermal_debugfs *thermal_dbg = tz->debugfs; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); + struct trip_stats *trip_stats; if (!thermal_dbg) return; @@ -639,7 +644,10 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id; tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - tze->trip_stats[trip_id].timestamp = now; + trip_stats = &tze->trip_stats[trip_id]; + trip_stats->trip_temp = trip->temperature; + trip_stats->trip_hyst = trip->hysteresis; + trip_stats->timestamp = now; unlock: mutex_unlock(&thermal_dbg->lock); @@ -836,8 +844,8 @@ static int tze_seq_show(struct seq_file *s, void *v) seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, - 9, trip->temperature, - 9, trip->hysteresis, + 9, trip_stats->trip_temp, + 9, trip_stats->trip_hyst, c, 10, duration_ms, 9, trip_stats->avg, 9, trip_stats->min, -- cgit v1.2.3 From 5a599e10e53d1914adf0032c730bc0f604a5d947 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 23 May 2024 17:58:42 +0200 Subject: thermal/debugfs: Allow tze_seq_show() to print statistics for invalid trips Commit a6258fde8de3 ("thermal/debugfs: Make tze_seq_show() skip invalid trips and trips with no stats") modified tze_seq_show() to skip invalid trips, but it overlooked the fact that a trip may become invalid during a mitigation eposide involving it, in which case its statistics should still be reported. For this reason, remove the invalid trip temperature check from the main loop in tze_seq_show(). The trips that have never been valid will still be skipped after this change because there are no statistics to report for them. Fixes: a6258fde8de3 ("thermal/debugfs: Make tze_seq_show() skip invalid trips and trips with no stats") Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_debugfs.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index e0e4eb6fad39..942447229157 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -802,10 +802,6 @@ static int tze_seq_show(struct seq_file *s, void *v) const struct thermal_trip *trip = &td->trip; struct trip_stats *trip_stats; - /* Skip invalid trips. */ - if (trip->temperature == THERMAL_TEMP_INVALID) - continue; - /* * There is no possible mitigation happening at the * critical trip point, so the stats will be always -- cgit v1.2.3 From cb573eec609c44f44b27c2fa07afd926eddc9f89 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 23 May 2024 18:00:14 +0200 Subject: thermal: core: Introduce thermal_trip_crossed() Add a helper function called thermal_trip_crossed() to be invoked by __thermal_zone_device_update() in order to notify user space, the thermal debug code and the zone governor about trip crossing. Subsequently, this will also be used in the case when a trip point becomes invalid after being crossed on the way up. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 54cce4e523bc..c119ef920793 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -467,6 +467,21 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, governor->trip_crossed(tz, trip, crossed_up); } +static void thermal_trip_crossed(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_governor *governor, + bool crossed_up) +{ + if (crossed_up) { + thermal_notify_tz_trip_up(tz, trip); + thermal_debug_tz_trip_up(tz, trip); + } else { + thermal_notify_tz_trip_down(tz, trip); + thermal_debug_tz_trip_down(tz, trip); + } + thermal_governor_trip_crossed(governor, tz, trip, crossed_up); +} + static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, const struct list_head *b) { @@ -506,18 +521,12 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, handle_thermal_trip(tz, td, &way_up_list, &way_down_list); list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp); - list_for_each_entry(td, &way_up_list, notify_list_node) { - thermal_notify_tz_trip_up(tz, &td->trip); - thermal_debug_tz_trip_up(tz, &td->trip); - thermal_governor_trip_crossed(governor, tz, &td->trip, true); - } + list_for_each_entry(td, &way_up_list, notify_list_node) + thermal_trip_crossed(tz, &td->trip, governor, true); list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); - list_for_each_entry(td, &way_down_list, notify_list_node) { - thermal_notify_tz_trip_down(tz, &td->trip); - thermal_debug_tz_trip_down(tz, &td->trip); - thermal_governor_trip_crossed(governor, tz, &td->trip, false); - } + list_for_each_entry(td, &way_down_list, notify_list_node) + thermal_trip_crossed(tz, &td->trip, governor, false); if (governor->manage) governor->manage(tz); -- cgit v1.2.3 From ae2170d6ea96e652c7fb5689f1980986bf48b7b8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 23 May 2024 18:05:03 +0200 Subject: thermal: trip: Trigger trip down notifications when trips involved in mitigation become invalid When a trip point becomes invalid after being crossed on the way up, it is involved in a mitigation episode that needs to be adjusted to compensate for the trip going away. For this reason, introduce thermal_zone_trip_down() as a wrapper around thermal_trip_crossed() and make thermal_zone_set_trip_temp() call it if the new temperature of the trip at hand is equal to THERMAL_TEMP_INVALID and it has been crossed on the way up to trigger all of the necessary adjustments in user space, the thermal debug code and the zone governor. Fixes: 8c69a777e480 ("thermal: core: Fix the handling of invalid trip points") Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 6 ++++++ drivers/thermal/thermal_core.h | 2 ++ drivers/thermal/thermal_trip.c | 20 ++++++++++++-------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c119ef920793..30567b499455 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -602,6 +602,12 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_zone_device_update); +void thermal_zone_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip) +{ + thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); +} + int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), void *data) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index d9785e5bbb08..20e7b45673d6 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -246,6 +246,8 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); +void thermal_zone_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip); /* sysfs I/F */ int thermal_zone_create_device_groups(struct thermal_zone_device *tz); diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index d6a6acc78ddb..49e63db68517 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -152,17 +152,23 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, if (trip->temperature == temp) return; + trip->temperature = temp; + thermal_notify_tz_trip_change(tz, trip); + if (temp == THERMAL_TEMP_INVALID) { struct thermal_trip_desc *td = trip_to_trip_desc(trip); - if (trip->type == THERMAL_TRIP_PASSIVE && - tz->temperature >= td->threshold) { + if (tz->temperature >= td->threshold) { /* - * The trip has been crossed, so the thermal zone's - * passive count needs to be adjusted. + * The trip has been crossed on the way up, so some + * adjustments are needed to compensate for the lack + * of it going forward. */ - tz->passive--; - WARN_ON_ONCE(tz->passive < 0); + if (trip->type == THERMAL_TRIP_PASSIVE) { + tz->passive--; + WARN_ON_ONCE(tz->passive < 0); + } + thermal_zone_trip_down(tz, trip); } /* * Invalidate the threshold to avoid triggering a spurious @@ -170,7 +176,5 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, */ td->threshold = INT_MAX; } - trip->temperature = temp; - thermal_notify_tz_trip_change(tz, trip); } EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); -- cgit v1.2.3 From 8cb3aeebcb86088e30232277c9bee9054837442b Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Mon, 27 May 2024 12:56:06 +0000 Subject: ASoC: simple-card-utils: Split simple_fixup_sample_fmt func Split the simple_fixup_sample_fmt() into two functions by adding one more function named simple_util_get_sample_fmt() to return the sample format value. This is useful for drivers that wish to simply get the sample format without setting the mask. Signed-off-by: Mohan Kumar Signed-off-by: Sameer Pujar Link: https://msgid.link/r/20240527125608.2461300-2-spujar@nvidia.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 ++ sound/soc/generic/simple-card-utils.c | 26 ++++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index ad67957b7b48..2c2279d082ec 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -174,6 +174,8 @@ void simple_util_parse_convert(struct device_node *np, char *prefix, struct simple_util_data *data); bool simple_util_is_convert_required(const struct simple_util_data *data); +int simple_util_get_sample_fmt(struct simple_util_data *data); + int simple_util_parse_routing(struct snd_soc_card *card, char *prefix); int simple_util_parse_widgets(struct snd_soc_card *card, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index b4876b4f259d..852989611362 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -13,12 +13,11 @@ #include #include -static void simple_fixup_sample_fmt(struct simple_util_data *data, - struct snd_pcm_hw_params *params) +int simple_util_get_sample_fmt(struct simple_util_data *data) { int i; - struct snd_mask *mask = hw_param_mask(params, - SNDRV_PCM_HW_PARAM_FORMAT); + int val = -EINVAL; + struct { char *fmt; u32 val; @@ -33,11 +32,26 @@ static void simple_fixup_sample_fmt(struct simple_util_data *data, for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) { if (!strcmp(data->convert_sample_format, of_sample_fmt_table[i].fmt)) { - snd_mask_none(mask); - snd_mask_set(mask, of_sample_fmt_table[i].val); + val = of_sample_fmt_table[i].val; break; } } + return val; +} +EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt); + +static void simple_fixup_sample_fmt(struct simple_util_data *data, + struct snd_pcm_hw_params *params) +{ + int val; + struct snd_mask *mask = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); + + val = simple_util_get_sample_fmt(data); + if (val >= 0) { + snd_mask_none(mask); + snd_mask_set(mask, val); + } } void simple_util_parse_convert(struct device_node *np, -- cgit v1.2.3 From 2502f8dd8c30edbca9253d5999294f58211039b1 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Mon, 27 May 2024 12:56:07 +0000 Subject: ASoC: tegra: I2S client convert formats handling The AHUB HW modules are interconnected with CIF which are capable of supporting Channel and Sample bit format conversion. Due to this, the I2S Client can have different Channel and Sample bit from the hw_params() and this config is passed from CIF port of I2S DT node which can help to perform this conversion. For e.g. HFP usecase consists of BT SCO with 1ch and 8k audio data which needs to be converted and mixed with external codec playback and capture path which is of 2ch and 48k format. For HFP Playback: The path includes mono to stereo and 8k to 48k conversion _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |1ch | | 1ch | |2ch | SFC | 2ch | | |BT SCO |---->| I2Sx |------>| CIF |----->| 8k->48k |---->| Codec I2Sy| |_ _ _ _| |_ _ __|client |_ _ _|audio |_ _ _ _ _| |_ _ _ _ _ _| For HFP Capture: The path includes stereo to mono and 48k to 8k conversion _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | 2ch | SFC | 2ch | | 1ch | | 1ch | | | codec I2Sy|---->| 48k->8k |----->| CIF |------>| I2Sx |---->| BT SCO| |_ _ _ _ _ _| |_ _ _ _ _| audio|_ _ _|client |_ _ _ _| |_ _ _ _| For above two path, I2S client channel uses existing DT binding to pass channels and format conversion in I2Sx CIF Port. Signed-off-by: Mohan Kumar Signed-off-by: Sameer Pujar Link: https://msgid.link/r/20240527125608.2461300-3-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_i2s.c | 71 +++++++++++++++++++++++++++++++++++++++--- sound/soc/tegra/tegra210_i2s.h | 2 ++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index ba7fdd7405ac..fe4fde844d86 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -8,11 +8,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "tegra210_i2s.h" #include "tegra_cif.h" @@ -603,6 +605,7 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream, struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int sample_size, channels, srate, val, reg, path; struct tegra_cif_conf cif_conf; + snd_pcm_format_t sample_format; memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); @@ -615,28 +618,51 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream, cif_conf.audio_ch = channels; cif_conf.client_ch = channels; + if (i2s->client_channels) + cif_conf.client_ch = i2s->client_channels; + /* AHUB CIF Audio bits configs */ switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + cif_conf.audio_bits = TEGRA_ACIF_BITS_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + dev_err(dev, "unsupported params audio bit format!\n"); + return -EOPNOTSUPP; + } + + sample_format = params_format(params); + if (i2s->client_sample_format >= 0) + sample_format = (snd_pcm_format_t)i2s->client_sample_format; + + /* + * Format of the I2S for sending/receiving the audio + * to/from external device. + */ + switch (sample_format) { case SNDRV_PCM_FORMAT_S8: val = I2S_BITS_8; sample_size = 8; - cif_conf.audio_bits = TEGRA_ACIF_BITS_8; cif_conf.client_bits = TEGRA_ACIF_BITS_8; break; case SNDRV_PCM_FORMAT_S16_LE: val = I2S_BITS_16; sample_size = 16; - cif_conf.audio_bits = TEGRA_ACIF_BITS_16; cif_conf.client_bits = TEGRA_ACIF_BITS_16; break; case SNDRV_PCM_FORMAT_S32_LE: val = I2S_BITS_32; sample_size = 32; - cif_conf.audio_bits = TEGRA_ACIF_BITS_32; cif_conf.client_bits = TEGRA_ACIF_BITS_32; break; default: - dev_err(dev, "unsupported format!\n"); + dev_err(dev, "unsupported client bit format!\n"); return -EOPNOTSUPP; } @@ -872,6 +898,40 @@ static const struct regmap_config tegra210_i2s_regmap_config = { .cache_type = REGCACHE_FLAT, }; +/* + * The AHUB HW modules are interconnected with CIF which are capable of + * supporting Channel and Sample bit format conversion. This needs different + * CIF Audio and client configuration. As one of the config comes from + * params_channels() or params_format(), the extra configuration is passed from + * CIF Port of DT I2S node which can help to perform this conversion. + * + * 4ch audio = 4ch client = 2ch 2ch + * -----> ADMAIF -----------> CIF -------------> I2S ----> + */ +static void tegra210_parse_client_convert(struct device *dev) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + struct device_node *ports, *ep; + struct simple_util_data data = {}; + int cif_port = 0; + + ports = of_get_child_by_name(dev->of_node, "ports"); + if (ports) { + ep = of_graph_get_endpoint_by_regs(ports, cif_port, -1); + if (ep) { + simple_util_parse_convert(ep, NULL, &data); + of_node_put(ep); + } + of_node_put(ports); + } + + if (data.convert_channels) + i2s->client_channels = data.convert_channels; + + if (data.convert_sample_format) + i2s->client_sample_format = simple_util_get_sample_fmt(&data); +} + static int tegra210_i2s_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -887,6 +947,7 @@ static int tegra210_i2s_probe(struct platform_device *pdev) i2s->tx_mask = DEFAULT_I2S_SLOT_MASK; i2s->rx_mask = DEFAULT_I2S_SLOT_MASK; i2s->loopback = false; + i2s->client_sample_format = -EINVAL; dev_set_drvdata(dev, i2s); @@ -916,6 +977,8 @@ static int tegra210_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->regmap); } + tegra210_parse_client_convert(dev); + regcache_cache_only(i2s->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt, diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h index 030d70c45e18..fe478f3d8435 100644 --- a/sound/soc/tegra/tegra210_i2s.h +++ b/sound/soc/tegra/tegra210_i2s.h @@ -112,6 +112,8 @@ struct tegra210_i2s { struct clk *clk_i2s; struct clk *clk_sync_input; struct regmap *regmap; + int client_sample_format; + unsigned int client_channels; unsigned int stereo_to_mono[I2S_PATHS]; unsigned int mono_to_stereo[I2S_PATHS]; unsigned int dai_fmt; -- cgit v1.2.3 From e5c7bd4e5ca0f549108a7013ba77885926c6a56b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 22 May 2024 13:40:19 -0300 Subject: tools include UAPI: Sync linux/stat.h with the kernel sources To get the changes in: 2a82bb02941fb53d ("statx: stx_subvol") To pick up this change and support it: $ tools/perf/trace/beauty/statx_mask.sh > before $ cp include/uapi/linux/stat.h tools/perf/trace/beauty/include/uapi/linux/stat.h $ tools/perf/trace/beauty/statx_mask.sh > after $ diff -u before after --- before 2024-05-22 13:39:49.742470571 -0300 +++ after 2024-05-22 13:39:59.157883101 -0300 @@ -14,4 +14,5 @@ [ilog2(0x00001000) + 1] = "MNT_ID", [ilog2(0x00002000) + 1] = "DIOALIGN", [ilog2(0x00004000) + 1] = "MNT_ID_UNIQUE", + [ilog2(0x00008000) + 1] = "SUBVOL", }; $ Now we'll see it like we see these: # perf trace -e statx 0.000 ( 0.015 ms): systemd-userwo/3982299 statx(dfd: 6, filename: ".", mask: TYPE|INO|MNT_ID, buffer: 0x7ffd8945e850) = 0 180.559 ( 0.007 ms): (ostnamed)/3982957 statx(dfd: 4, filename: "sys", flags: SYMLINK_NOFOLLOW|NO_AUTOMOUNT|STATX_DONT_SYNC, mask: TYPE, buffer: 0x7fff13161190) = 0 180.918 ( 0.011 ms): (ostnamed)/3982957 statx(dfd: CWD, filename: "/run/systemd/mount-rootfs/sys/kernel/security", flags: SYMLINK_NOFOLLOW|NO_AUTOMOUNT|STATX_DONT_SYNC, mask: MNT_ID, buffer: 0x7fff13161120) = 0 180.956 ( 0.010 ms): (ostnamed)/3982957 statx(dfd: CWD, filename: "/run/systemd/mount-rootfs/sys/fs/cgroup", flags: SYMLINK_NOFOLLOW|NO_AUTOMOUNT|STATX_DONT_SYNC, mask: MNT_ID, buffer: 0x7fff13161120) = 0 Cc: Adrian Hunter Cc: Christian Brauner Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Kent Overstreet Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/Zk5nO9yT0oPezUoo@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/uapi/linux/stat.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/include/uapi/linux/stat.h b/tools/perf/trace/beauty/include/uapi/linux/stat.h index 2f2ee82d5517..67626d535316 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/stat.h +++ b/tools/perf/trace/beauty/include/uapi/linux/stat.h @@ -126,8 +126,9 @@ struct statx { __u64 stx_mnt_id; __u32 stx_dio_mem_align; /* Memory buffer alignment for direct I/O */ __u32 stx_dio_offset_align; /* File offset alignment for direct I/O */ + __u64 stx_subvol; /* Subvolume identifier */ /* 0xa0 */ - __u64 __spare3[12]; /* Spare space for future expansion */ + __u64 __spare3[11]; /* Spare space for future expansion */ /* 0x100 */ }; @@ -155,6 +156,7 @@ struct statx { #define STATX_MNT_ID 0x00001000U /* Got stx_mnt_id */ #define STATX_DIOALIGN 0x00002000U /* Want/got direct I/O alignment info */ #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ +#define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */ #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ -- cgit v1.2.3 From 0efc88e444d9d74f220f60a3e37143c8decf1bea Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 27 May 2024 12:15:27 -0300 Subject: tools headers UAPI: Sync linux/prctl.h with the kernel sources To pick the changes in: 628d701f2de5b9a1 ("powerpc/dexcr: Add DEXCR prctl interface") 6b9391b581fddd85 ("riscv: Include riscv_set_icache_flush_ctx prctl") That adds some PowerPC and a RISC-V specific prctl options: $ tools/perf/trace/beauty/prctl_option.sh > before $ cp include/uapi/linux/prctl.h tools/perf/trace/beauty/include/uapi/linux/prctl.h $ tools/perf/trace/beauty/prctl_option.sh > after $ diff -u before after --- before 2024-05-27 12:14:21.358032781 -0300 +++ after 2024-05-27 12:14:32.364530185 -0300 @@ -65,6 +65,9 @@ [68] = "GET_MEMORY_MERGE", [69] = "RISCV_V_SET_CONTROL", [70] = "RISCV_V_GET_CONTROL", + [71] = "RISCV_SET_ICACHE_FLUSH_CTX", + [72] = "PPC_GET_DEXCR", + [73] = "PPC_SET_DEXCR", }; static const char *prctl_set_mm_options[] = { [1] = "START_CODE", $ That now will be used to decode the syscall option and also to compose filters, for instance: [root@five ~]# perf trace -e syscalls:sys_enter_prctl --filter option==SET_NAME 0.000 Isolated Servi/3474327 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23f13b7aee) 0.032 DOM Worker/3474327 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23deb25670) 7.920 :3474328/3474328 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23e24fbb10) 7.935 StreamT~s #374/3474328 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23e24fb970) 8.400 Isolated Servi/3474329 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23e24bab10) 8.418 StreamT~s #374/3474329 syscalls:sys_enter_prctl(option: SET_NAME, arg2: 0x7f23e24ba970) ^C[root@five ~]# This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/prctl.h include/uapi/linux/prctl.h Cc: Adrian Hunter Cc: Benjamin Gray Cc: Charlie Jenkins Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Michael Ellerman Cc: Namhyung Kim Cc: Palmer Dabbelt Link: https://lore.kernel.org/lkml/ZlSklGWp--v_Ije7@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/uapi/linux/prctl.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/perf/trace/beauty/include/uapi/linux/prctl.h b/tools/perf/trace/beauty/include/uapi/linux/prctl.h index 370ed14b1ae0..35791791a879 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/prctl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/prctl.h @@ -306,4 +306,26 @@ struct prctl_mm_map { # define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc # define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f +#define PR_RISCV_SET_ICACHE_FLUSH_CTX 71 +# define PR_RISCV_CTX_SW_FENCEI_ON 0 +# define PR_RISCV_CTX_SW_FENCEI_OFF 1 +# define PR_RISCV_SCOPE_PER_PROCESS 0 +# define PR_RISCV_SCOPE_PER_THREAD 1 + +/* PowerPC Dynamic Execution Control Register (DEXCR) controls */ +#define PR_PPC_GET_DEXCR 72 +#define PR_PPC_SET_DEXCR 73 +/* DEXCR aspect to act on */ +# define PR_PPC_DEXCR_SBHE 0 /* Speculative branch hint enable */ +# define PR_PPC_DEXCR_IBRTPD 1 /* Indirect branch recurrent target prediction disable */ +# define PR_PPC_DEXCR_SRAPD 2 /* Subroutine return address prediction disable */ +# define PR_PPC_DEXCR_NPHIE 3 /* Non-privileged hash instruction enable */ +/* Action to apply / return */ +# define PR_PPC_DEXCR_CTRL_EDITABLE 0x1 /* Aspect can be modified with PR_PPC_SET_DEXCR */ +# define PR_PPC_DEXCR_CTRL_SET 0x2 /* Set the aspect for this process */ +# define PR_PPC_DEXCR_CTRL_CLEAR 0x4 /* Clear the aspect for this process */ +# define PR_PPC_DEXCR_CTRL_SET_ONEXEC 0x8 /* Set the aspect on exec */ +# define PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC 0x10 /* Clear the aspect on exec */ +# define PR_PPC_DEXCR_CTRL_MASK 0x1f + #endif /* _LINUX_PRCTL_H */ -- cgit v1.2.3 From 1437a9f06f740cc2950d9b6ac23fad838ff023a3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 27 May 2024 12:34:20 -0300 Subject: tools headers UAPI: Sync fcntl.h with the kernel sources to pick F_DUPFD_QUERY There is no scrape script yet for those, but the warning pointed out we need to update the array with the F_LINUX_SPECIFIC_BASE entries, do it. Now 'perf trace' can decode that cmd and also use it in filter, as in: root@number:~# perf trace -e syscalls:*enter_fcntl --filter 'cmd != SETFL && cmd != GETFL' 0.000 sssd_kcm/303828 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLK, arg: 0x7fffdc6a8a50) 0.013 sssd_kcm/303828 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLKW, arg: 0x7fffdc6a8aa0) 0.090 sssd_kcm/303828 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLKW, arg: 0x7fffdc6a88e0) ^Croot@number:~# This picks up the changes in: c62b758bae6af16f ("fcntl: add F_DUPFD_QUERY fcntl()") Addressing this perf tools build warning: Warning: Kernel ABI header differences: diff -u tools/perf/trace/beauty/include/uapi/linux/fcntl.h include/uapi/linux/fcntl.h Cc: Adrian Hunter Cc: Christian Brauner Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZlSqNQH9mFw2bmjq@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 2 +- tools/perf/trace/beauty/include/uapi/linux/fcntl.h | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 51eca671c797..08a3a6effac1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -765,7 +765,7 @@ static const char *fcntl_cmds[] = { static DEFINE_STRARRAY(fcntl_cmds, "F_"); static const char *fcntl_linux_specific_cmds[] = { - "SETLEASE", "GETLEASE", "NOTIFY", [5] = "CANCELLK", "DUPFD_CLOEXEC", + "SETLEASE", "GETLEASE", "NOTIFY", "DUPFD_QUERY", [5] = "CANCELLK", "DUPFD_CLOEXEC", "SETPIPE_SZ", "GETPIPE_SZ", "ADD_SEALS", "GET_SEALS", "GET_RW_HINT", "SET_RW_HINT", "GET_FILE_RW_HINT", "SET_FILE_RW_HINT", }; diff --git a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h index 282e90aeb163..c0bcc185fa48 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h @@ -8,6 +8,14 @@ #define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0) #define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1) +/* + * Request nofications on a directory. + * See below for events that may be notified. + */ +#define F_NOTIFY (F_LINUX_SPECIFIC_BASE + 2) + +#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3) + /* * Cancel a blocking posix lock; internal use only until we expose an * asynchronous lock api to userspace: @@ -17,12 +25,6 @@ /* Create a file descriptor with FD_CLOEXEC set. */ #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) -/* - * Request nofications on a directory. - * See below for events that may be notified. - */ -#define F_NOTIFY (F_LINUX_SPECIFIC_BASE+2) - /* * Set and get of pipe page size array */ -- cgit v1.2.3 From a3eed53beec4a7bac8acd109abcb187df2577781 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 27 May 2024 12:45:33 -0300 Subject: perf beauty: Update copy of linux/socket.h with the kernel sources To pick up the fixes in: 0645fbe760afcc53 ("net: have do_accept() take a struct proto_accept_arg argument") That just changes a function prototype, not touching things used by the perf scrape scripts such as: $ tools/perf/trace/beauty/sockaddr.sh | head -5 static const char *socket_families[] = { [0] = "UNSPEC", [1] = "LOCAL", [2] = "INET", [3] = "AX25", $ This addresses this perf tools build warning: Warning: Kernel ABI header differences: diff -u tools/perf/trace/beauty/include/linux/socket.h include/linux/socket.h Cc: Adrian Hunter Cc: Ian Rogers Cc: Jens Axboe Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZlSrceExgjrUiDb5@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/linux/socket.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h index 139c330ccf2c..89d16b90370b 100644 --- a/tools/perf/trace/beauty/include/linux/socket.h +++ b/tools/perf/trace/beauty/include/linux/socket.h @@ -16,6 +16,7 @@ struct cred; struct socket; struct sock; struct sk_buff; +struct proto_accept_arg; #define __sockaddr_check_size(size) \ BUILD_BUG_ON(((size) > sizeof(struct __kernel_sockaddr_storage))) @@ -433,7 +434,7 @@ extern int __sys_recvfrom(int fd, void __user *ubuf, size_t size, extern int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, struct sockaddr __user *addr, int addr_len); -extern struct file *do_accept(struct file *file, unsigned file_flags, +extern struct file *do_accept(struct file *file, struct proto_accept_arg *arg, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, int flags); extern int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr, -- cgit v1.2.3 From 001821b0e79716c4e17c71d8e053a23599a7a508 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 27 May 2024 12:51:30 -0300 Subject: perf trace beauty: Update the arch/x86/include/asm/irq_vectors.h copy with the kernel sources to pick POSTED_MSI_NOTIFICATION To pick up the change in: f5a3562ec9dd29e6 ("x86/irq: Reserve a per CPU IDT vector for posted MSIs") That picks up this new vector: $ cp arch/x86/include/asm/irq_vectors.h tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h $ tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh > after $ diff -u before after --- before 2024-05-27 12:50:47.708863932 -0300 +++ after 2024-05-27 12:51:15.335113123 -0300 @@ -1,6 +1,7 @@ static const char *x86_irq_vectors[] = { [0x02] = "NMI", [0x80] = "IA32_SYSCALL", + [0xeb] = "POSTED_MSI_NOTIFICATION", [0xec] = "LOCAL_TIMER", [0xed] = "HYPERV_STIMER0", [0xee] = "HYPERV_REENLIGHTENMENT", $ Now those will be known when pretty printing the irq_vectors:* tracepoints. Cc: Adrian Hunter Cc: Ian Rogers Cc: Jacob Pan Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Thomas Gleixner Link: https://lore.kernel.org/lkml/ZlS34M0x30EFVhbg@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h index d18bfb238f66..13aea8fc3d45 100644 --- a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h +++ b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h @@ -97,10 +97,16 @@ #define LOCAL_TIMER_VECTOR 0xec +/* + * Posted interrupt notification vector for all device MSIs delivered to + * the host kernel. + */ +#define POSTED_MSI_NOTIFICATION_VECTOR 0xeb + #define NR_VECTORS 256 #ifdef CONFIG_X86_LOCAL_APIC -#define FIRST_SYSTEM_VECTOR LOCAL_TIMER_VECTOR +#define FIRST_SYSTEM_VECTOR POSTED_MSI_NOTIFICATION_VECTOR #else #define FIRST_SYSTEM_VECTOR NR_VECTORS #endif -- cgit v1.2.3 From 947051e361d551e0590777080ffc4926190f62f2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 24 May 2024 15:19:54 +0100 Subject: KVM: arm64: Fix AArch32 register narrowing on userspace write When userspace writes to one of the core registers, we make sure to narrow the corresponding GPRs if PSTATE indicates an AArch32 context. The code tries to check whether the context is EL0 or EL1 so that it narrows the correct registers. But it does so by checking the full PSTATE instead of PSTATE.M. As a consequence, and if we are restoring an AArch32 EL0 context in a 64bit guest, and that PSTATE has *any* bit set outside of PSTATE.M, we narrow *all* registers instead of only the first 15, destroying the 64bit state. Obviously, this is not something the guest is likely to enjoy. Correctly masking PSTATE to only evaluate PSTATE.M fixes it. Fixes: 90c1f934ed71 ("KVM: arm64: Get rid of the AArch32 register mapping code") Reported-by: Nina Schoetterl-Glausch Cc: stable@vger.kernel.org Reviewed-by: Nina Schoetterl-Glausch Acked-by: Oliver Upton Link: https://lore.kernel.org/r/20240524141956.1450304-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/guest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index e2f762d959bb..d9617b11f7a8 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -276,7 +276,7 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if (*vcpu_cpsr(vcpu) & PSR_MODE32_BIT) { int i, nr_reg; - switch (*vcpu_cpsr(vcpu)) { + switch (*vcpu_cpsr(vcpu) & PSR_AA32_MODE_MASK) { /* * Either we are dealing with user mode, and only the * first 15 registers (+ PC) must be narrowed to 32bit. -- cgit v1.2.3 From dfe6d190f38fc5df5ff2614b463a5195a399c885 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 24 May 2024 15:19:55 +0100 Subject: KVM: arm64: Allow AArch32 PSTATE.M to be restored as System mode It appears that we don't allow a vcpu to be restored in AArch32 System mode, as we *never* included it in the list of valid modes. Just add it to the list of allowed modes. Fixes: 0d854a60b1d7 ("arm64: KVM: enable initialization of a 32bit vcpu") Cc: stable@vger.kernel.org Acked-by: Oliver Upton Link: https://lore.kernel.org/r/20240524141956.1450304-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/guest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index d9617b11f7a8..11098eb7eb44 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -251,6 +251,7 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) case PSR_AA32_MODE_SVC: case PSR_AA32_MODE_ABT: case PSR_AA32_MODE_UND: + case PSR_AA32_MODE_SYS: if (!vcpu_el1_is_32bit(vcpu)) return -EINVAL; break; -- cgit v1.2.3 From c92e8b9eacebb4060634ebd9395bba1b29aadc68 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 24 May 2024 15:19:56 +0100 Subject: KVM: arm64: AArch32: Fix spurious trapping of conditional instructions We recently upgraded the view of ESR_EL2 to 64bit, in keeping with the requirements of the architecture. However, the AArch32 emulation code was left unaudited, and the (already dodgy) code that triages whether a trap is spurious or not (because the condition code failed) broke in a subtle way: If ESR_EL2.ISS2 is ever non-zero (unlikely, but hey, this is the ARM architecture we're talking about), the hack that tests the top bits of ESR_EL2.EC will break in an interesting way. Instead, use kvm_vcpu_trap_get_class() to obtain the EC, and list all the possible ECs that can fail a condition code check. While we're at it, add SMC32 to the list, as it is explicitly listed as being allowed to trap despite failing a condition code check (as described in the HCR_EL2.TSC documentation). Fixes: 0b12620fddb8 ("KVM: arm64: Treat ESR_EL2 as a 64-bit register") Cc: stable@vger.kernel.org Acked-by: Oliver Upton Link: https://lore.kernel.org/r/20240524141956.1450304-4-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/aarch32.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/aarch32.c b/arch/arm64/kvm/hyp/aarch32.c index 8d9670e6615d..449fa58cf3b6 100644 --- a/arch/arm64/kvm/hyp/aarch32.c +++ b/arch/arm64/kvm/hyp/aarch32.c @@ -50,9 +50,23 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu) u32 cpsr_cond; int cond; - /* Top two bits non-zero? Unconditional. */ - if (kvm_vcpu_get_esr(vcpu) >> 30) + /* + * These are the exception classes that could fire with a + * conditional instruction. + */ + switch (kvm_vcpu_trap_get_class(vcpu)) { + case ESR_ELx_EC_CP15_32: + case ESR_ELx_EC_CP15_64: + case ESR_ELx_EC_CP14_MR: + case ESR_ELx_EC_CP14_LS: + case ESR_ELx_EC_FP_ASIMD: + case ESR_ELx_EC_CP10_ID: + case ESR_ELx_EC_CP14_64: + case ESR_ELx_EC_SVC32: + break; + default: return true; + } /* Is condition field valid? */ cond = kvm_vcpu_get_condition(vcpu); -- cgit v1.2.3 From fd7ccfb112302c6941f03095544c10879ced98be Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 17 May 2024 09:42:04 +0300 Subject: media: Documentation: v4l: Fix ACTIVE route flag The documentation in one occasion mentions the VIDIOC_SUBDEV_STREAM_FL_ACTIVE flag. This was meant to be V4L2_SUBDEV_STREAM_FL_ACTIVE as it's a flag, not an IOCTL. Fix it. Fixes: cd2c75454d74 ("media: Documentation: Document S_ROUTING behaviour") Reported-by: Samuel Wein PhD Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/dev-subdev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index 0f9eda3187f3..161b43f1ce66 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -582,7 +582,7 @@ depending on the hardware. In all cases, however, only routes that have the Devices generating the streams may allow enabling and disabling some of the routes or have a fixed routing configuration. If the routes can be disabled, not declaring the routes (or declaring them without -``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag unset. -- cgit v1.2.3 From fe61b2906bd046535f4ef7dfcd69562f531ccd38 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 20 May 2024 11:55:54 +0200 Subject: media: intel/ipu6: Fix some redundant resources freeing in ipu6_pci_remove() pcim_iomap_regions() and pcim_enable_device() are used in the probe. So the corresponding managed resources don't need to be freed explicitly in the remove function. Remove the incorrect pci_release_regions() and pci_disable_device() calls. Fixes: 25fedc021985 ("media: intel/ipu6: add Intel IPU6 PCI device driver") Signed-off-by: Christophe JAILLET Reviewed-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index d2bebd208461..f587f609259d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -727,9 +727,6 @@ static void ipu6_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - release_firmware(isp->cpd_fw); ipu6_mmu_cleanup(psys_mmu); -- cgit v1.2.3 From 266b44ec9a26dd2ad5d1dfcbb2f7f4e2c10da3fb Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 26 May 2024 09:59:56 +0200 Subject: media: intel/ipu6: Move isys_remove() close to isys_probe() In preparation to fixing a leak in isys_probe(), move isys_remove(). The fix will introduce a new function that will also be called from isys_remove(). The code needs to be rearranged to avoid a forward declaration. Having the .remove function close to the .probe function is also more standard. Signed-off-by: Christophe JAILLET Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys.c | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 5992138c7290..7ce2047a09b5 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -925,41 +925,6 @@ static const struct dev_pm_ops isys_pm_ops = { .resume = isys_resume, }; -static void isys_remove(struct auxiliary_device *auxdev) -{ - struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); - struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); - struct ipu6_device *isp = adev->isp; - struct isys_fw_msgs *fwmsg, *safe; - unsigned int i; - - list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - - list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - - isys_unregister_devices(isys); - isys_notifier_cleanup(isys); - - cpu_latency_qos_remove_request(&isys->pm_qos); - - if (!isp->secure_mode) { - ipu6_cpd_free_pkg_dir(adev); - ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); - release_firmware(adev->fw); - } - - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); - - isys_iwake_watermark_cleanup(isys); - mutex_destroy(&isys->stream_mutex); - mutex_destroy(&isys->mutex); -} - static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount) { struct device *dev = &isys->adev->auxdev.dev; @@ -1167,6 +1132,41 @@ release_firmware: return ret; } +static void isys_remove(struct auxiliary_device *auxdev) +{ + struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); + struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); + struct ipu6_device *isp = adev->isp; + struct isys_fw_msgs *fwmsg, *safe; + unsigned int i; + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) + dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, 0); + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) + dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, 0); + + isys_unregister_devices(isys); + isys_notifier_cleanup(isys); + + cpu_latency_qos_remove_request(&isys->pm_qos); + + if (!isp->secure_mode) { + ipu6_cpd_free_pkg_dir(adev); + ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); + release_firmware(adev->fw); + } + + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + + isys_iwake_watermark_cleanup(isys); + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); +} + struct fwmsg { int type; char *msg; -- cgit v1.2.3 From ab0ed481012811fed052f609b364c118ce8a576e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 26 May 2024 09:59:57 +0200 Subject: media: intel/ipu6: Fix an error handling path in isys_probe() If an error occurs after a successful alloc_fw_msg_bufs() call, some resources should be released as already done in the remove function. Add a new free_fw_msg_bufs() function that releases what has been allocated by alloc_fw_msg_bufs(). Also use this new function in isys_remove() to avoid some code duplication. Fixes: f50c4ca0a820 ("media: intel/ipu6: add the main input system driver") Signed-off-by: Christophe JAILLET Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 7ce2047a09b5..1998b72ac07d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -925,6 +925,20 @@ static const struct dev_pm_ops isys_pm_ops = { .resume = isys_resume, }; +static void free_fw_msg_bufs(struct ipu6_isys *isys) +{ + struct device *dev = &isys->adev->auxdev.dev; + struct isys_fw_msgs *fwmsg, *safe; + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) + dma_free_attrs(dev, sizeof(struct isys_fw_msgs), fwmsg, + fwmsg->dma_addr, 0); + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) + dma_free_attrs(dev, sizeof(struct isys_fw_msgs), fwmsg, + fwmsg->dma_addr, 0); +} + static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount) { struct device *dev = &isys->adev->auxdev.dev; @@ -1105,12 +1119,14 @@ static int isys_probe(struct auxiliary_device *auxdev, ret = isys_register_devices(isys); if (ret) - goto out_remove_pkg_dir_shared_buffer; + goto free_fw_msg_bufs; ipu6_mmu_hw_cleanup(adev->mmu); return 0; +free_fw_msg_bufs: + free_fw_msg_bufs(isys); out_remove_pkg_dir_shared_buffer: if (!isp->secure_mode) ipu6_cpd_free_pkg_dir(adev); @@ -1137,16 +1153,9 @@ static void isys_remove(struct auxiliary_device *auxdev) struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); struct ipu6_device *isp = adev->isp; - struct isys_fw_msgs *fwmsg, *safe; unsigned int i; - list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - - list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); + free_fw_msg_bufs(isys); isys_unregister_devices(isys); isys_notifier_cleanup(isys); -- cgit v1.2.3 From c19fa08c1414644b0d9275d336bdaff90af57d0b Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Mon, 27 May 2024 18:48:57 +0800 Subject: media: intel/ipu6: fix the buffer flags caused by wrong parentheses The buffer flags is set by wrong due to wrong parentheses, the FL_INCOMING flag is never taken an account. Fix it by wrapping the ternary conditional operation with parentheses. Fixes: 3c1dfb5a69cf ("media: intel/ipu6: input system video nodes and buffer queues") Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 40a8ebfcfce2..4bd4e324abc9 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -301,10 +301,10 @@ static int ipu6_isys_stream_start(struct ipu6_isys_video *av, out_requeue: if (bl && bl->nbufs) ipu6_isys_buffer_list_queue(bl, - (IPU6_ISYS_BUFFER_LIST_FL_INCOMING | - error) ? + IPU6_ISYS_BUFFER_LIST_FL_INCOMING | + (error ? IPU6_ISYS_BUFFER_LIST_FL_SET_STATE : - 0, error ? VB2_BUF_STATE_ERROR : + 0), error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_QUEUED); flush_firmware_streamon_fail(stream); -- cgit v1.2.3 From da42b5229b27bb5c0eff3408c92f025e6041dad3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 28 May 2024 11:01:20 -0300 Subject: tools headers: Update the syscall tables and unistd.h, mostly to support the new 'mseal' syscall But also to wire up shadow stacks on 32-bit x86, picking up those changes from these csets: ff388fe5c481d39c ("mseal: wire up mseal syscall") 2883f01ec37dd866 ("x86/shstk: Enable shadow stacks for x32") This makes 'perf trace' support it, now its possible, for instance to do: # perf trace -e mseal --max-stack=16 Here is an example with the 'sendmmsg' syscall: root@x1:~# perf trace -e sendmmsg --max-stack 16 --max-events=1 0.000 ( 0.062 ms): dbus-broker/1012 sendmmsg(fd: 150, mmsg: 0x7ffef57cca50, vlen: 1, flags: DONTWAIT|NOSIGNAL) = 1 syscall_exit_to_user_mode_prepare ([kernel.kallsyms]) syscall_exit_to_user_mode_prepare ([kernel.kallsyms]) syscall_exit_to_user_mode ([kernel.kallsyms]) do_syscall_64 ([kernel.kallsyms]) entry_SYSCALL_64 ([kernel.kallsyms]) [0x117ce7] (/usr/lib64/libc.so.6 (deleted)) root@x1:~# To do a system wide tracing of the new 'mseal' syscall with a backtrace of at most 16 entries. This addresses these perf tools build warnings: Warning: Kernel ABI header differences: diff -u tools/include/uapi/asm-generic/unistd.h include/uapi/asm-generic/unistd.h diff -u tools/perf/arch/x86/entry/syscalls/syscall_64.tbl arch/x86/entry/syscalls/syscall_64.tbl diff -u tools/perf/arch/powerpc/entry/syscalls/syscall.tbl arch/powerpc/kernel/syscalls/syscall.tbl diff -u tools/perf/arch/s390/entry/syscalls/syscall.tbl arch/s390/kernel/syscalls/syscall.tbl diff -u tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl arch/mips/kernel/syscalls/syscall_n64.tbl Cc: Adrian Hunter Cc: Andrew Morton Cc: H J Lu Cc: Ian Rogers Cc: Ingo Molnar Cc: Jeff Xu Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZlXlo4TNcba4wnVZ@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/asm-generic/unistd.h | 5 ++++- tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl | 1 + tools/perf/arch/powerpc/entry/syscalls/syscall.tbl | 1 + tools/perf/arch/s390/entry/syscalls/syscall.tbl | 1 + tools/perf/arch/x86/entry/syscalls/syscall_64.tbl | 3 ++- 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h index 75f00965ab15..d983c48a3b6a 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -842,8 +842,11 @@ __SYSCALL(__NR_lsm_set_self_attr, sys_lsm_set_self_attr) #define __NR_lsm_list_modules 461 __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) +#define __NR_mseal 462 +__SYSCALL(__NR_mseal, sys_mseal) + #undef __NR_syscalls -#define __NR_syscalls 462 +#define __NR_syscalls 463 /* * 32 bit systems traditionally used different diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl index 532b855df589..1464c6be6eb3 100644 --- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl +++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl @@ -376,3 +376,4 @@ 459 n64 lsm_get_self_attr sys_lsm_get_self_attr 460 n64 lsm_set_self_attr sys_lsm_set_self_attr 461 n64 lsm_list_modules sys_lsm_list_modules +462 n64 mseal sys_mseal diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index 17173b82ca21..3656f1ca7a21 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -548,3 +548,4 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index 095bb86339a7..bd0fee24ad10 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -464,3 +464,4 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal sys_mseal diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index 7e8d46f4147f..a396f6e6ab5b 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -374,7 +374,7 @@ 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 -453 64 map_shadow_stack sys_map_shadow_stack +453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue @@ -383,6 +383,7 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal # # Due to a historical design error, certain syscalls are numbered differently -- cgit v1.2.3 From f13e01b89daf42330a4a722f451e48c3e2edfc8d Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Fri, 17 May 2024 12:48:25 +0100 Subject: btrfs: ensure fast fsync waits for ordered extents after a write failure If a write path in COW mode fails, either before submitting a bio for the new extents or an actual IO error happens, we can end up allowing a fast fsync to log file extent items that point to unwritten extents. This is because dropping the extent maps happens when completing ordered extents, at btrfs_finish_one_ordered(), and the completion of an ordered extent is executed in a work queue. This can result in a fast fsync to start logging file extent items based on existing extent maps before the ordered extents complete, therefore resulting in a log that has file extent items that point to unwritten extents, resulting in a corrupt file if a crash happens after and the log tree is replayed the next time the fs is mounted. This can happen for both direct IO writes and buffered writes. For example consider a direct IO write, in COW mode, that fails at btrfs_dio_submit_io() because btrfs_extract_ordered_extent() returned an error: 1) We call btrfs_finish_ordered_extent() with the 'uptodate' parameter set to false, meaning an error happened; 2) That results in marking the ordered extent with the BTRFS_ORDERED_IOERR flag; 3) btrfs_finish_ordered_extent() queues the completion of the ordered extent - so that btrfs_finish_one_ordered() will be executed later in a work queue. That function will drop extent maps in the range when it's executed, since the extent maps point to unwritten locations (signaled by the BTRFS_ORDERED_IOERR flag); 4) After calling btrfs_finish_ordered_extent() we keep going down the write path and unlock the inode; 5) After that a fast fsync starts and locks the inode; 6) Before the work queue executes btrfs_finish_one_ordered(), the fsync task sees the extent maps that point to the unwritten locations and logs file extent items based on them - it does not know they are unwritten, and the fast fsync path does not wait for ordered extents to complete, which is an intentional behaviour in order to reduce latency. For the buffered write case, here's one example: 1) A fast fsync begins, and it starts by flushing delalloc and waiting for the writeback to complete by calling filemap_fdatawait_range(); 2) Flushing the dellaloc created a new extent map X; 3) During the writeback some IO error happened, and at the end io callback (end_bbio_data_write()) we call btrfs_finish_ordered_extent(), which sets the BTRFS_ORDERED_IOERR flag in the ordered extent and queues its completion; 4) After queuing the ordered extent completion, the end io callback clears the writeback flag from all pages (or folios), and from that moment the fast fsync can proceed; 5) The fast fsync proceeds sees extent map X and logs a file extent item based on extent map X, resulting in a log that points to an unwritten data extent - because the ordered extent completion hasn't run yet, it happens only after the logging. To fix this make btrfs_finish_ordered_extent() set the inode flag BTRFS_INODE_NEEDS_FULL_SYNC in case an error happened for a COW write, so that a fast fsync will wait for ordered extent completion. Note that this issues of using extent maps that point to unwritten locations can not happen for reads, because in read paths we start by locking the extent range and wait for any ordered extents in the range to complete before looking for extent maps. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 10 ++++++++++ fs/btrfs/file.c | 16 ++++++++++++++++ fs/btrfs/ordered-data.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 91c994b569f3..6ed495ca7a31 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -89,6 +89,16 @@ enum { BTRFS_INODE_FREE_SPACE_INODE, /* Set when there are no capabilities in XATTs for the inode. */ BTRFS_INODE_NO_CAP_XATTR, + /* + * Set if an error happened when doing a COW write before submitting a + * bio or during writeback. Used for both buffered writes and direct IO + * writes. This is to signal a fast fsync that it has to wait for + * ordered extents to complete and therefore not log extent maps that + * point to unwritten extents (when an ordered extent completes and it + * has the BTRFS_ORDERED_IOERR flag set, it drops extent maps in its + * range). + */ + BTRFS_INODE_COW_WRITE_ERROR, }; /* in memory btrfs inode */ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0c7c1b42028e..00670596bf06 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1885,6 +1885,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) */ if (full_sync || btrfs_is_zoned(fs_info)) { ret = btrfs_wait_ordered_range(inode, start, len); + clear_bit(BTRFS_INODE_COW_WRITE_ERROR, &BTRFS_I(inode)->runtime_flags); } else { /* * Get our ordered extents as soon as possible to avoid doing @@ -1894,6 +1895,21 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) btrfs_get_ordered_extents_for_logging(BTRFS_I(inode), &ctx.ordered_extents); ret = filemap_fdatawait_range(inode->i_mapping, start, end); + if (ret) + goto out_release_extents; + + /* + * Check and clear the BTRFS_INODE_COW_WRITE_ERROR now after + * starting and waiting for writeback, because for buffered IO + * it may have been set during the end IO callback + * (end_bbio_data_write() -> btrfs_finish_ordered_extent()) in + * case an error happened and we need to wait for ordered + * extents to complete so that any extent maps that point to + * unwritten locations are dropped and we don't log them. + */ + if (test_and_clear_bit(BTRFS_INODE_COW_WRITE_ERROR, + &BTRFS_I(inode)->runtime_flags)) + ret = btrfs_wait_ordered_range(inode, start, len); } if (ret) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index c5bdd674f55c..35a413ce935d 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -388,6 +388,37 @@ bool btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, ret = can_finish_ordered_extent(ordered, page, file_offset, len, uptodate); spin_unlock_irqrestore(&inode->ordered_tree_lock, flags); + /* + * If this is a COW write it means we created new extent maps for the + * range and they point to unwritten locations if we got an error either + * before submitting a bio or during IO. + * + * We have marked the ordered extent with BTRFS_ORDERED_IOERR, and we + * are queuing its completion below. During completion, at + * btrfs_finish_one_ordered(), we will drop the extent maps for the + * unwritten extents. + * + * However because completion runs in a work queue we can end up having + * a fast fsync running before that. In the case of direct IO, once we + * unlock the inode the fsync might start, and we queue the completion + * before unlocking the inode. In the case of buffered IO when writeback + * finishes (end_bbio_data_write()) we queue the completion, so if the + * writeback was triggered by a fast fsync, the fsync might start + * logging before ordered extent completion runs in the work queue. + * + * The fast fsync will log file extent items based on the extent maps it + * finds, so if by the time it collects extent maps the ordered extent + * completion didn't happen yet, it will log file extent items that + * point to unwritten extents, resulting in a corruption if a crash + * happens and the log tree is replayed. Note that a fast fsync does not + * wait for completion of ordered extents in order to reduce latency. + * + * Set a flag in the inode so that the next fast fsync will wait for + * ordered extents to complete before starting to log. + */ + if (!uptodate && !test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags)) + set_bit(BTRFS_INODE_COW_WRITE_ERROR, &inode->runtime_flags); + if (ret) btrfs_queue_ordered_fn(ordered); return ret; -- cgit v1.2.3 From 43cad521c6d228ea0c51e248f8e5b3a6295a2849 Mon Sep 17 00:00:00 2001 From: Dhananjay Ugwekar Date: Tue, 30 Apr 2024 14:07:06 +0530 Subject: tools/power/cpupower: Fix Pstate frequency reporting on AMD Family 1Ah CPUs Update cpupower's P-State frequency calculation and reporting with AMD Family 1Ah+ processors, when using the acpi-cpufreq driver. This is due to a change in the PStateDef MSR layout in AMD Family 1Ah+. Tested on 4th and 5th Gen AMD EPYC system Signed-off-by: Ananth Narayan Signed-off-by: Dhananjay Ugwekar Reviewed-by: Mario Limonciello Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index c519cc89c97f..0a56e22240fc 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -41,6 +41,16 @@ union core_pstate { unsigned res1:31; unsigned en:1; } pstatedef; + /* since fam 1Ah: */ + struct { + unsigned fid:12; + unsigned res1:2; + unsigned vid:8; + unsigned iddval:8; + unsigned idddiv:2; + unsigned res2:31; + unsigned en:1; + } pstatedef2; unsigned long long val; }; @@ -48,6 +58,10 @@ static int get_did(union core_pstate pstate) { int t; + /* Fam 1Ah onward do not use did */ + if (cpupower_cpu_info.family >= 0x1A) + return 0; + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) t = pstate.pstatedef.did; else if (cpupower_cpu_info.family == 0x12) @@ -61,12 +75,18 @@ static int get_did(union core_pstate pstate) static int get_cof(union core_pstate pstate) { int t; - int fid, did, cof; + int fid, did, cof = 0; did = get_did(pstate); if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) { - fid = pstate.pstatedef.fid; - cof = 200 * fid / did; + if (cpupower_cpu_info.family >= 0x1A) { + fid = pstate.pstatedef2.fid; + if (fid > 0x0f) + cof = (fid * 5); + } else { + fid = pstate.pstatedef.fid; + cof = 200 * fid / did; + } } else { t = 0x10; fid = pstate.pstate.fid; -- cgit v1.2.3 From a0fc1a053b7a212244ff109e44469b8deff280c5 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 24 May 2024 16:58:26 -0700 Subject: of: of_test: add MODULE_DESCRIPTION() Fix the 'make W=1' warning: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/of/of_test.o Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240524-md-of-of_test-v1-1-6ebd078d620f@quicinc.com Signed-off-by: Rob Herring (Arm) --- drivers/of/of_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/of/of_test.c b/drivers/of/of_test.c index a9301d293f01..c85a258bc6ae 100644 --- a/drivers/of/of_test.c +++ b/drivers/of/of_test.c @@ -54,4 +54,5 @@ static struct kunit_suite of_dtb_suite = { kunit_test_suites( &of_dtb_suite, ); +MODULE_DESCRIPTION("KUnit tests for OF APIs"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4c7d2dc698dfdb16db180c142cd681c42e23b419 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 22 May 2024 11:08:24 +0800 Subject: ASoC: dt-bindings: fsl,mqs: Add i.MX95 platform support There are two MQS instances on the i.MX95 platform. The definition of bit positions in the control register are different. In order to support these MQS modules, define two compatible strings to distinguish them. As one instance is in the always-on domain, another is in the net controller domain, so the compatible strings are "fsl,imx95-aonmix-mqs", "fsl,imx95-netcmix-mqs". Signed-off-by: Shengjiu Wang Reviewed-by: Rob Herring (Arm) Link: https://msgid.link/r/1716347305-18457-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,mqs.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml index 8b33353a80ca..030ccc173130 100644 --- a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml @@ -23,6 +23,8 @@ properties: - fsl,imx8qm-mqs - fsl,imx8qxp-mqs - fsl,imx93-mqs + - fsl,imx95-aonmix-mqs + - fsl,imx95-netcmix-mqs clocks: minItems: 1 -- cgit v1.2.3 From 401a1f021bbc5a81eb63742f62005629c4f8d747 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 22 May 2024 11:08:25 +0800 Subject: ASoC: fsl_mqs: Add i.MX95 platform support There are two MQS instances on the i.MX95 platform. The definition of bit positions in the control register are different. In order to support these MQS modules, define two compatible strings to distinguish them. Define different soc data according to compatible strings On i.MX95 one instance in nect-mix is supported by this commit, another instance in always-on-mix is not supported, which depends on System Manager function readiness. Signed-off-by: Shengjiu Wang Link: https://msgid.link/r/1716347305-18457-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_mqs.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 60929c36a0e3..c95b84a54dc4 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -28,10 +28,16 @@ #define MQS_CLK_DIV_MASK (0xFF << 0) #define MQS_CLK_DIV_SHIFT (0) +enum reg_type { + TYPE_REG_OWN, /* module own register space */ + TYPE_REG_GPR, /* register in GPR space */ + TYPE_REG_SM, /* System Manager controls the register */ +}; + /** * struct fsl_mqs_soc_data - soc specific data * - * @use_gpr: control register is in General Purpose Register group + * @type: control register space type * @ctrl_off: control register offset * @en_mask: enable bit mask * @en_shift: enable bit shift @@ -43,7 +49,7 @@ * @div_shift: clock divider bit shift */ struct fsl_mqs_soc_data { - bool use_gpr; + enum reg_type type; int ctrl_off; int en_mask; int en_shift; @@ -200,7 +206,7 @@ static int fsl_mqs_probe(struct platform_device *pdev) */ mqs_priv->soc = of_device_get_match_data(&pdev->dev); - if (mqs_priv->soc->use_gpr) { + if (mqs_priv->soc->type == TYPE_REG_GPR) { gpr_np = of_parse_phandle(np, "gpr", 0); if (!gpr_np) { dev_err(&pdev->dev, "failed to get gpr node by phandle\n"); @@ -304,7 +310,7 @@ static const struct dev_pm_ops fsl_mqs_pm_ops = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = { - .use_gpr = false, + .type = TYPE_REG_OWN, .ctrl_off = REG_MQS_CTRL, .en_mask = MQS_EN_MASK, .en_shift = MQS_EN_SHIFT, @@ -317,7 +323,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = { - .use_gpr = true, + .type = TYPE_REG_GPR, .ctrl_off = IOMUXC_GPR2, .en_mask = IMX6SX_GPR2_MQS_EN_MASK, .en_shift = IMX6SX_GPR2_MQS_EN_SHIFT, @@ -330,7 +336,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { - .use_gpr = true, + .type = TYPE_REG_GPR, .ctrl_off = 0x20, .en_mask = BIT(1), .en_shift = 1, @@ -342,10 +348,38 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { .div_shift = 8, }; +static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = { + .type = TYPE_REG_SM, + .ctrl_off = 0x88, + .en_mask = BIT(1), + .en_shift = 1, + .rst_mask = BIT(2), + .rst_shift = 2, + .osr_mask = BIT(3), + .osr_shift = 3, + .div_mask = GENMASK(15, 8), + .div_shift = 8, +}; + +static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = { + .type = TYPE_REG_GPR, + .ctrl_off = 0x0, + .en_mask = BIT(2), + .en_shift = 2, + .rst_mask = BIT(3), + .rst_shift = 3, + .osr_mask = BIT(4), + .osr_shift = 4, + .div_mask = GENMASK(16, 9), + .div_shift = 9, +}; + static const struct of_device_id fsl_mqs_dt_ids[] = { { .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data }, { .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data }, { .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data }, + { .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data }, + { .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids); -- cgit v1.2.3 From ac4b069035783f7a54b3ab841119f4b6bf435f98 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 28 May 2024 15:10:28 -0300 Subject: tools arch x86: Sync the msr-index.h copy with the kernel sources To pick up the changes from these csets: 53bc516ade85a764 ("x86/msr: Move ARCH_CAP_XAPIC_DISABLE bit definition to its rightful place") That patch just move definitions around, so this just silences this perf build warning: Warning: Kernel ABI header differences: diff -u tools/arch/x86/include/asm/msr-index.h arch/x86/include/asm/msr-index. Cc: Adrian Hunter Cc: Borislav Petkov (AMD) Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Pawan Gupta Link: https://lore.kernel.org/lkml/ZlYe8jOzd1_DyA7X@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/msr-index.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index e72c2b872957..e022e6eb766c 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -170,6 +170,10 @@ * CPU is not affected by Branch * History Injection. */ +#define ARCH_CAP_XAPIC_DISABLE BIT(21) /* + * IA32_XAPIC_DISABLE_STATUS MSR + * supported + */ #define ARCH_CAP_PBRSB_NO BIT(24) /* * Not susceptible to Post-Barrier * Return Stack Buffer Predictions. @@ -192,11 +196,6 @@ * File. */ -#define ARCH_CAP_XAPIC_DISABLE BIT(21) /* - * IA32_XAPIC_DISABLE_STATUS MSR - * supported - */ - #define MSR_IA32_FLUSH_CMD 0x0000010b #define L1D_FLUSH BIT(0) /* * Writeback and invalidate the -- cgit v1.2.3 From 88e520512a68b4e724cb0f4281c79ae28673ccb1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 28 May 2024 16:24:15 -0300 Subject: tools headers UAPI: Sync kvm headers with the kernel sources To pick the changes in: 4af663c2f64a8d25 ("KVM: SEV: Allow per-guest configuration of GHCB protocol version") 4f5defae708992dd ("KVM: SEV: introduce KVM_SEV_INIT2 operation") 26c44aa9e076ed83 ("KVM: SEV: define VM types for SEV and SEV-ES") ac5c48027bacb1b5 ("KVM: SEV: publish supported VMSA features") 651d61bc8b7d8bb6 ("KVM: PPC: Fix documentation for ppc mmu caps") That don't change functionality in tools/perf, as no new ioctl is added for the 'perf trace' scripts to harvest. This addresses these perf build warnings: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/kvm.h include/uapi/linux/kvm.h diff -u tools/arch/x86/include/uapi/asm/kvm.h arch/x86/include/uapi/asm/kvm.h Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Joel Stanley Cc: Michael Ellerman Cc: Michael Roth Cc: Namhyung Kim Cc: Paolo Bonzini Link: https://lore.kernel.org/lkml/ZlYxAdHjyAkvGtMW@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/uapi/asm/kvm.h | 22 ++++++++++++++++++++-- tools/include/uapi/linux/kvm.h | 4 ++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index ef11aa4cab42..9fae1b73b529 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -457,8 +457,13 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 -/* attributes for system fd (group 0) */ -#define KVM_X86_XCOMP_GUEST_SUPP 0 +/* vendor-independent attributes for system fd (group 0) */ +#define KVM_X86_GRP_SYSTEM 0 +# define KVM_X86_XCOMP_GUEST_SUPP 0 + +/* vendor-specific groups and attributes for system fd */ +#define KVM_X86_GRP_SEV 1 +# define KVM_X86_SEV_VMSA_FEATURES 0 struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -689,6 +694,9 @@ enum sev_cmd_id { /* Guest Migration Extension */ KVM_SEV_SEND_CANCEL, + /* Second time is the charm; improved versions of the above ioctls. */ + KVM_SEV_INIT2, + KVM_SEV_NR_MAX, }; @@ -700,6 +708,14 @@ struct kvm_sev_cmd { __u32 sev_fd; }; +struct kvm_sev_init { + __u64 vmsa_features; + __u32 flags; + __u16 ghcb_version; + __u16 pad1; + __u32 pad2[8]; +}; + struct kvm_sev_launch_start { __u32 handle; __u32 policy; @@ -856,5 +872,7 @@ struct kvm_hyperv_eventfd { #define KVM_X86_DEFAULT_VM 0 #define KVM_X86_SW_PROTECTED_VM 1 +#define KVM_X86_SEV_VM 2 +#define KVM_X86_SEV_ES_VM 3 #endif /* _ASM_X86_KVM_H */ diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index ea32b101b999..d03842abae57 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1221,9 +1221,9 @@ struct kvm_vfio_spapr_tce { /* Available with KVM_CAP_SPAPR_RESIZE_HPT */ #define KVM_PPC_RESIZE_HPT_PREPARE _IOR(KVMIO, 0xad, struct kvm_ppc_resize_hpt) #define KVM_PPC_RESIZE_HPT_COMMIT _IOR(KVMIO, 0xae, struct kvm_ppc_resize_hpt) -/* Available with KVM_CAP_PPC_RADIX_MMU or KVM_CAP_PPC_MMU_HASH_V3 */ +/* Available with KVM_CAP_PPC_MMU_RADIX or KVM_CAP_PPC_MMU_HASH_V3 */ #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) -/* Available with KVM_CAP_PPC_RADIX_MMU */ +/* Available with KVM_CAP_PPC_MMU_RADIX */ #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) /* Available with KVM_CAP_PPC_GET_CPU_CHAR */ #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) -- cgit v1.2.3 From c7a5096781732e0f9784551309484f3e103f6750 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 May 2024 13:25:02 +0300 Subject: PNP: Make dev_is_pnp() to be a function and export it for modules Since we have a dev_is_pnp() macro that utilises the address of the pnp_bus_type variable, the users, which can be compiled as modules, will fail to build. Convert the macro to be a function and export it to the modules to prevent build breakage. Reported-by: Woody Suwalski Closes: https://lore.kernel.org/r/cc8a93b2-2504-9754-e26c-5d5c3bd1265c@gmail.com Fixes: 2a49b45cd0e7 ("PNP: Add dev_is_pnp() macro") Signed-off-by: Andy Shevchenko Reviewed-by: Christoph Hellwig Signed-off-by: Rafael J. Wysocki --- drivers/pnp/driver.c | 6 ++++++ include/linux/pnp.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 0a5d0d8befa8..3483e52e3a81 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -266,6 +266,12 @@ const struct bus_type pnp_bus_type = { .dev_groups = pnp_dev_groups, }; +bool dev_is_pnp(const struct device *dev) +{ + return dev->bus == &pnp_bus_type; +} +EXPORT_SYMBOL_GPL(dev_is_pnp); + int pnp_register_driver(struct pnp_driver *drv) { drv->driver.name = drv->name; diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 82561242cda4..a8def1cea32c 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -469,7 +469,7 @@ int compare_pnp_id(struct pnp_id *pos, const char *id); int pnp_register_driver(struct pnp_driver *drv); void pnp_unregister_driver(struct pnp_driver *drv); -#define dev_is_pnp(d) ((d)->bus == &pnp_bus_type) +bool dev_is_pnp(const struct device *dev); #else @@ -502,7 +502,7 @@ static inline int compare_pnp_id(struct pnp_id *pos, const char *id) { return -E static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; } static inline void pnp_unregister_driver(struct pnp_driver *drv) { } -#define dev_is_pnp(d) false +static inline bool dev_is_pnp(const struct device *dev) { return false; } #endif /* CONFIG_PNP */ -- cgit v1.2.3 From edcde848c01eb071a91d479a6b3101d9cf48e905 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 May 2024 13:25:03 +0300 Subject: PNP: Hide pnp_bus_type from the non-PNP code The pnp_bus_type is defined only when CONFIG_PNP=y, while being not guarded by ifdeffery in the header. Moreover, it's not used outside of the PNP code. Move it to the internal header to make sure no-one will try to (ab)use it. Signed-off-by: Andy Shevchenko Reviewed-by: Christoph Hellwig Signed-off-by: Rafael J. Wysocki --- drivers/pnp/base.h | 1 + include/linux/pnp.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index e74a0f6a3157..4e80273dfb1e 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -6,6 +6,7 @@ extern struct mutex pnp_lock; extern const struct attribute_group *pnp_dev_groups[]; +extern const struct bus_type pnp_bus_type; int pnp_register_protocol(struct pnp_protocol *protocol); void pnp_unregister_protocol(struct pnp_protocol *protocol); diff --git a/include/linux/pnp.h b/include/linux/pnp.h index a8def1cea32c..7f2ff95d2deb 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -435,8 +435,6 @@ struct pnp_protocol { #define protocol_for_each_dev(protocol, dev) \ list_for_each_entry(dev, &(protocol)->devices, protocol_list) -extern const struct bus_type pnp_bus_type; - #if defined(CONFIG_PNP) /* device management */ -- cgit v1.2.3 From 2f523f29d3b19a668b8d4ce6f768d8faff976b3a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 28 May 2024 16:54:31 -0300 Subject: tools headers UAPI: Update i915_drm.h with the kernel sources Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/drm/i915_drm.h | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/include/uapi/drm/i915_drm.h index 2ee338860b7e..d4d86e566e07 100644 --- a/tools/include/uapi/drm/i915_drm.h +++ b/tools/include/uapi/drm/i915_drm.h @@ -806,6 +806,12 @@ typedef struct drm_i915_irq_wait { */ #define I915_PARAM_PXP_STATUS 58 +/* + * Query if kernel allows marking a context to send a Freq hint to SLPC. This + * will enable use of the strategies allowed by the SLPC algorithm. + */ +#define I915_PARAM_HAS_CONTEXT_FREQ_HINT 59 + /* Must be kept compact -- no holes and well documented */ /** @@ -2148,6 +2154,15 @@ struct drm_i915_gem_context_param { * -EIO: The firmware did not succeed in creating the protected context. */ #define I915_CONTEXT_PARAM_PROTECTED_CONTENT 0xd + +/* + * I915_CONTEXT_PARAM_LOW_LATENCY: + * + * Mark this context as a low latency workload which requires aggressive GT + * frequency scaling. Use I915_PARAM_HAS_CONTEXT_FREQ_HINT to check if the kernel + * supports this per context flag. + */ +#define I915_CONTEXT_PARAM_LOW_LATENCY 0xe /* Must be kept compact -- no holes and well documented */ /** @value: Context parameter value to be set or queried */ @@ -2623,19 +2638,29 @@ struct drm_i915_reg_read { * */ +/* + * struct drm_i915_reset_stats - Return global reset and other context stats + * + * Driver keeps few stats for each contexts and also global reset count. + * This struct can be used to query those stats. + */ struct drm_i915_reset_stats { + /** @ctx_id: ID of the requested context */ __u32 ctx_id; + + /** @flags: MBZ */ __u32 flags; - /* All resets since boot/module reload, for all contexts */ + /** @reset_count: All resets since boot/module reload, for all contexts */ __u32 reset_count; - /* Number of batches lost when active in GPU, for this context */ + /** @batch_active: Number of batches lost when active in GPU, for this context */ __u32 batch_active; - /* Number of batches lost pending for execution, for this context */ + /** @batch_pending: Number of batches lost pending for execution, for this context */ __u32 batch_pending; + /** @pad: MBZ */ __u32 pad; }; -- cgit v1.2.3 From ac62f52138f752d6c74adc6321e4996d84caf5bb Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Sun, 26 May 2024 23:40:01 +0200 Subject: ACPI: AC: Properly notify powermanagement core about changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The powermanagement core does various actions when a powersupply changes. It calls into notifiers, LED triggers, other power supplies and emits an uevent. To make sure that all these actions happen properly call power_supply_changed(). Reported-by: Rajas Paranjpe Closes: https://github.com/MrChromebox/firmware/issues/420#issuecomment-2132251318 Signed-off-by: Thomas Weißschuh Reviewed-by: Sebastian Reichel Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ac.c | 4 ++-- drivers/acpi/sbs.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 2d4a35e6dd18..09a87fa222c7 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -145,7 +145,7 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) dev_name(&adev->dev), event, (u32) ac->state); acpi_notifier_call_chain(adev, event, (u32) ac->state); - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(ac->charger); } } @@ -268,7 +268,7 @@ static int acpi_ac_resume(struct device *dev) if (acpi_ac_get_state(ac)) return 0; if (old_state != ac->state) - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(ac->charger); return 0; } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 94e3c000df2e..dc8164b182dc 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -610,7 +610,7 @@ static void acpi_sbs_callback(void *context) if (sbs->charger_exists) { acpi_ac_get_present(sbs); if (sbs->charger_present != saved_charger_state) - kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(sbs->charger); } if (sbs->manager_present) { @@ -622,7 +622,7 @@ static void acpi_sbs_callback(void *context) acpi_battery_read(bat); if (saved_battery_state == bat->present) continue; - kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE); + power_supply_changed(bat->bat); } } } -- cgit v1.2.3 From 779b8a14afde110dd3502566be907289eba72447 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 May 2024 14:09:23 +0200 Subject: cpufreq: amd-pstate: remove global header file When extra warnings are enabled, gcc points out a global variable definition in a header: In file included from drivers/cpufreq/amd-pstate-ut.c:29: include/linux/amd-pstate.h:123:27: error: 'amd_pstate_mode_string' defined but not used [-Werror=unused-const-variable=] 123 | static const char * const amd_pstate_mode_string[] = { | ^~~~~~~~~~~~~~~~~~~~~~ This header is only included from two files in the same directory, and one of them uses only a single definition from it, so clean it up by moving most of the contents into the driver that uses them, and making shared bits a local header file. Fixes: 36c5014e5460 ("cpufreq: amd-pstate: optimize driver working mode selection in amd_pstate_param()") Signed-off-by: Arnd Bergmann Acked-by: Mario Limonciello Signed-off-by: Rafael J. Wysocki --- MAINTAINERS | 1 - drivers/cpufreq/amd-pstate-ut.c | 3 +- drivers/cpufreq/amd-pstate.c | 34 +++++++++- drivers/cpufreq/amd-pstate.h | 104 ++++++++++++++++++++++++++++++ include/linux/amd-pstate.h | 137 ---------------------------------------- 5 files changed, 139 insertions(+), 140 deletions(-) create mode 100644 drivers/cpufreq/amd-pstate.h delete mode 100644 include/linux/amd-pstate.h diff --git a/MAINTAINERS b/MAINTAINERS index d6c90161c7bf..fc31870379f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1107,7 +1107,6 @@ L: linux-pm@vger.kernel.org S: Supported F: Documentation/admin-guide/pm/amd-pstate.rst F: drivers/cpufreq/amd-pstate* -F: include/linux/amd-pstate.h F: tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py AMD PTDMA DRIVER diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index f04ae67dda37..fc275d41d51e 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -26,10 +26,11 @@ #include #include #include -#include #include +#include "amd-pstate.h" + /* * Abbreviations: * amd_pstate_ut: used as a shortform for AMD P-State unit test. diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 1b7e82a0ad2e..91993647e09e 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include @@ -46,6 +45,8 @@ #include #include #include + +#include "amd-pstate.h" #include "amd-pstate-trace.h" #define AMD_PSTATE_TRANSITION_LATENCY 20000 @@ -53,6 +54,37 @@ #define CPPC_HIGHEST_PERF_PERFORMANCE 196 #define CPPC_HIGHEST_PERF_DEFAULT 166 +#define AMD_CPPC_EPP_PERFORMANCE 0x00 +#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80 +#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF +#define AMD_CPPC_EPP_POWERSAVE 0xFF + +/* + * enum amd_pstate_mode - driver working mode of amd pstate + */ +enum amd_pstate_mode { + AMD_PSTATE_UNDEFINED = 0, + AMD_PSTATE_DISABLE, + AMD_PSTATE_PASSIVE, + AMD_PSTATE_ACTIVE, + AMD_PSTATE_GUIDED, + AMD_PSTATE_MAX, +}; + +static const char * const amd_pstate_mode_string[] = { + [AMD_PSTATE_UNDEFINED] = "undefined", + [AMD_PSTATE_DISABLE] = "disable", + [AMD_PSTATE_PASSIVE] = "passive", + [AMD_PSTATE_ACTIVE] = "active", + [AMD_PSTATE_GUIDED] = "guided", + NULL, +}; + +struct quirk_entry { + u32 nominal_freq; + u32 lowest_freq; +}; + /* * TODO: We need more time to fine tune processors with shared memory solution * with community together. diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h new file mode 100644 index 000000000000..e6a28e7f4dbf --- /dev/null +++ b/drivers/cpufreq/amd-pstate.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Meng Li + */ + +#ifndef _LINUX_AMD_PSTATE_H +#define _LINUX_AMD_PSTATE_H + +#include + +/********************************************************************* + * AMD P-state INTERFACE * + *********************************************************************/ +/** + * struct amd_aperf_mperf + * @aperf: actual performance frequency clock count + * @mperf: maximum performance frequency clock count + * @tsc: time stamp counter + */ +struct amd_aperf_mperf { + u64 aperf; + u64 mperf; + u64 tsc; +}; + +/** + * struct amd_cpudata - private CPU data for AMD P-State + * @cpu: CPU number + * @req: constraint request to apply + * @cppc_req_cached: cached performance request hints + * @highest_perf: the maximum performance an individual processor may reach, + * assuming ideal conditions + * For platforms that do not support the preferred core feature, the + * highest_pef may be configured with 166 or 255, to avoid max frequency + * calculated wrongly. we take the fixed value as the highest_perf. + * @nominal_perf: the maximum sustained performance level of the processor, + * assuming ideal operating conditions + * @lowest_nonlinear_perf: the lowest performance level at which nonlinear power + * savings are achieved + * @lowest_perf: the absolute lowest performance level of the processor + * @prefcore_ranking: the preferred core ranking, the higher value indicates a higher + * priority. + * @min_limit_perf: Cached value of the performance corresponding to policy->min + * @max_limit_perf: Cached value of the performance corresponding to policy->max + * @min_limit_freq: Cached value of policy->min (in khz) + * @max_limit_freq: Cached value of policy->max (in khz) + * @max_freq: the frequency (in khz) that mapped to highest_perf + * @min_freq: the frequency (in khz) that mapped to lowest_perf + * @nominal_freq: the frequency (in khz) that mapped to nominal_perf + * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf + * @cur: Difference of Aperf/Mperf/tsc count between last and current sample + * @prev: Last Aperf/Mperf/tsc count value read from register + * @freq: current cpu frequency value (in khz) + * @boost_supported: check whether the Processor or SBIOS supports boost mode + * @hw_prefcore: check whether HW supports preferred core featue. + * Only when hw_prefcore and early prefcore param are true, + * AMD P-State driver supports preferred core featue. + * @epp_policy: Last saved policy used to set energy-performance preference + * @epp_cached: Cached CPPC energy-performance preference value + * @policy: Cpufreq policy value + * @cppc_cap1_cached Cached MSR_AMD_CPPC_CAP1 register value + * + * The amd_cpudata is key private data for each CPU thread in AMD P-State, and + * represents all the attributes and goals that AMD P-State requests at runtime. + */ +struct amd_cpudata { + int cpu; + + struct freq_qos_request req[2]; + u64 cppc_req_cached; + + u32 highest_perf; + u32 nominal_perf; + u32 lowest_nonlinear_perf; + u32 lowest_perf; + u32 prefcore_ranking; + u32 min_limit_perf; + u32 max_limit_perf; + u32 min_limit_freq; + u32 max_limit_freq; + + u32 max_freq; + u32 min_freq; + u32 nominal_freq; + u32 lowest_nonlinear_freq; + + struct amd_aperf_mperf cur; + struct amd_aperf_mperf prev; + + u64 freq; + bool boost_supported; + bool hw_prefcore; + + /* EPP feature related attributes*/ + s16 epp_policy; + s16 epp_cached; + u32 policy; + u64 cppc_cap1_cached; + bool suspended; +}; + +#endif /* _LINUX_AMD_PSTATE_H */ diff --git a/include/linux/amd-pstate.h b/include/linux/amd-pstate.h deleted file mode 100644 index d58fc022ec46..000000000000 --- a/include/linux/amd-pstate.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * linux/include/linux/amd-pstate.h - * - * Copyright (C) 2022 Advanced Micro Devices, Inc. - * - * Author: Meng Li - */ - -#ifndef _LINUX_AMD_PSTATE_H -#define _LINUX_AMD_PSTATE_H - -#include - -#define AMD_CPPC_EPP_PERFORMANCE 0x00 -#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80 -#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF -#define AMD_CPPC_EPP_POWERSAVE 0xFF - -/********************************************************************* - * AMD P-state INTERFACE * - *********************************************************************/ -/** - * struct amd_aperf_mperf - * @aperf: actual performance frequency clock count - * @mperf: maximum performance frequency clock count - * @tsc: time stamp counter - */ -struct amd_aperf_mperf { - u64 aperf; - u64 mperf; - u64 tsc; -}; - -/** - * struct amd_cpudata - private CPU data for AMD P-State - * @cpu: CPU number - * @req: constraint request to apply - * @cppc_req_cached: cached performance request hints - * @highest_perf: the maximum performance an individual processor may reach, - * assuming ideal conditions - * For platforms that do not support the preferred core feature, the - * highest_pef may be configured with 166 or 255, to avoid max frequency - * calculated wrongly. we take the fixed value as the highest_perf. - * @nominal_perf: the maximum sustained performance level of the processor, - * assuming ideal operating conditions - * @lowest_nonlinear_perf: the lowest performance level at which nonlinear power - * savings are achieved - * @lowest_perf: the absolute lowest performance level of the processor - * @prefcore_ranking: the preferred core ranking, the higher value indicates a higher - * priority. - * @min_limit_perf: Cached value of the performance corresponding to policy->min - * @max_limit_perf: Cached value of the performance corresponding to policy->max - * @min_limit_freq: Cached value of policy->min (in khz) - * @max_limit_freq: Cached value of policy->max (in khz) - * @max_freq: the frequency (in khz) that mapped to highest_perf - * @min_freq: the frequency (in khz) that mapped to lowest_perf - * @nominal_freq: the frequency (in khz) that mapped to nominal_perf - * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf - * @cur: Difference of Aperf/Mperf/tsc count between last and current sample - * @prev: Last Aperf/Mperf/tsc count value read from register - * @freq: current cpu frequency value (in khz) - * @boost_supported: check whether the Processor or SBIOS supports boost mode - * @hw_prefcore: check whether HW supports preferred core featue. - * Only when hw_prefcore and early prefcore param are true, - * AMD P-State driver supports preferred core featue. - * @epp_policy: Last saved policy used to set energy-performance preference - * @epp_cached: Cached CPPC energy-performance preference value - * @policy: Cpufreq policy value - * @cppc_cap1_cached Cached MSR_AMD_CPPC_CAP1 register value - * - * The amd_cpudata is key private data for each CPU thread in AMD P-State, and - * represents all the attributes and goals that AMD P-State requests at runtime. - */ -struct amd_cpudata { - int cpu; - - struct freq_qos_request req[2]; - u64 cppc_req_cached; - - u32 highest_perf; - u32 nominal_perf; - u32 lowest_nonlinear_perf; - u32 lowest_perf; - u32 prefcore_ranking; - u32 min_limit_perf; - u32 max_limit_perf; - u32 min_limit_freq; - u32 max_limit_freq; - - u32 max_freq; - u32 min_freq; - u32 nominal_freq; - u32 lowest_nonlinear_freq; - - struct amd_aperf_mperf cur; - struct amd_aperf_mperf prev; - - u64 freq; - bool boost_supported; - bool hw_prefcore; - - /* EPP feature related attributes*/ - s16 epp_policy; - s16 epp_cached; - u32 policy; - u64 cppc_cap1_cached; - bool suspended; -}; - -/* - * enum amd_pstate_mode - driver working mode of amd pstate - */ -enum amd_pstate_mode { - AMD_PSTATE_UNDEFINED = 0, - AMD_PSTATE_DISABLE, - AMD_PSTATE_PASSIVE, - AMD_PSTATE_ACTIVE, - AMD_PSTATE_GUIDED, - AMD_PSTATE_MAX, -}; - -static const char * const amd_pstate_mode_string[] = { - [AMD_PSTATE_UNDEFINED] = "undefined", - [AMD_PSTATE_DISABLE] = "disable", - [AMD_PSTATE_PASSIVE] = "passive", - [AMD_PSTATE_ACTIVE] = "active", - [AMD_PSTATE_GUIDED] = "guided", - NULL, -}; - -struct quirk_entry { - u32 nominal_freq; - u32 lowest_freq; -}; - -#endif /* _LINUX_AMD_PSTATE_H */ -- cgit v1.2.3 From e4731baaf29438508197d3a8a6d4f5a8c51663f8 Mon Sep 17 00:00:00 2001 From: Dhananjay Ugwekar Date: Mon, 27 May 2024 10:41:28 +0530 Subject: cpufreq: amd-pstate: Fix the inconsistency in max frequency units The nominal frequency in cpudata is maintained in MHz whereas all other frequencies are in KHz. This means we have to convert nominal frequency value to KHz before we do any interaction with other frequency values. In amd_pstate_set_boost(), this conversion from MHz to KHz is missed, fix that. Tested on a AMD Zen4 EPYC server Before: $ cat /sys/devices/system/cpu/cpufreq/policy*/scaling_max_freq | uniq 2151 $ cat /sys/devices/system/cpu/cpufreq/policy*/cpuinfo_min_freq | uniq 400000 $ cat /sys/devices/system/cpu/cpufreq/policy*/scaling_cur_freq | uniq 2151 409422 After: $ cat /sys/devices/system/cpu/cpufreq/policy*/scaling_max_freq | uniq 2151000 $ cat /sys/devices/system/cpu/cpufreq/policy*/cpuinfo_min_freq | uniq 400000 $ cat /sys/devices/system/cpu/cpufreq/policy*/scaling_cur_freq | uniq 2151000 1799527 Fixes: ec437d71db77 ("cpufreq: amd-pstate: Introduce a new AMD P-State driver to support future processors") Signed-off-by: Dhananjay Ugwekar Acked-by: Mario Limonciello Acked-by: Gautham R. Shenoy Tested-by: Peter Jung Cc: 5.17+ # 5.17+ Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/amd-pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 91993647e09e..9ad62dbe8bfb 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -701,7 +701,7 @@ static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) if (state) policy->cpuinfo.max_freq = cpudata->max_freq; else - policy->cpuinfo.max_freq = cpudata->nominal_freq; + policy->cpuinfo.max_freq = cpudata->nominal_freq * 1000; policy->max = policy->cpuinfo.max_freq; -- cgit v1.2.3 From d55510527153d17a3af8cc2df69c04f95ae1350d Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 28 May 2024 15:55:51 -0700 Subject: cxl/test: Add missing vmalloc.h for tools/testing/cxl/test/mem.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools/testing/cxl/test/mem.c uses vmalloc() and vfree() but does not include linux/vmalloc.h. Kernel v6.10 made changes that causes the currently included headers not depend on vmalloc.h and therefore mem.c can no longer compile. Add linux/vmalloc.h to fix compile issue. CC [M] tools/testing/cxl/test/mem.o tools/testing/cxl/test/mem.c: In function ‘label_area_release’: tools/testing/cxl/test/mem.c:1428:9: error: implicit declaration of function ‘vfree’; did you mean ‘kvfree’? [-Werror=implicit-function-declaration] 1428 | vfree(lsa); | ^~~~~ | kvfree tools/testing/cxl/test/mem.c: In function ‘cxl_mock_mem_probe’: tools/testing/cxl/test/mem.c:1466:22: error: implicit declaration of function ‘vmalloc’; did you mean ‘kmalloc’? [-Werror=implicit-function-declaration] 1466 | mdata->lsa = vmalloc(LSA_SIZE); | ^~~~~~~ | kmalloc Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Reviewed-by: Dan Williams Reviewed-by: Alison Schofield Link: https://lore.kernel.org/r/20240528225551.1025977-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 6584443144de..eaf091a3d331 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From 49ba7b515c4c0719b866d16f068e62d16a8a3dd1 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 7 May 2024 13:34:21 +0800 Subject: cxl/region: Fix memregion leaks in devm_cxl_add_region() Move the mode verification to __create_region() before allocating the memregion to avoid the memregion leaks. Fixes: 6e099264185d ("cxl/region: Add volatile region creation support") Signed-off-by: Li Zhijian Reviewed-by: Dan Williams Link: https://lore.kernel.org/r/20240507053421.456439-1-lizhijian@fujitsu.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 00a9f0eef8dd..3c2b6144be23 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2352,15 +2352,6 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, struct device *dev; int rc; - switch (mode) { - case CXL_DECODER_RAM: - case CXL_DECODER_PMEM: - break; - default: - dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode); - return ERR_PTR(-EINVAL); - } - cxlr = cxl_region_alloc(cxlrd, id); if (IS_ERR(cxlr)) return cxlr; @@ -2415,6 +2406,15 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, { int rc; + switch (mode) { + case CXL_DECODER_RAM: + case CXL_DECODER_PMEM: + break; + default: + dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode); + return ERR_PTR(-EINVAL); + } + rc = memregion_alloc(GFP_KERNEL); if (rc < 0) return ERR_PTR(rc); -- cgit v1.2.3 From 10b6ad2b2d8ed07637bd4930f91548c9668ec7df Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:35 -0500 Subject: ASoC: Intel: sof_sdw_cs42l42: use dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_cs42l42.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index fdb75fc71c26..fc18e4aa3dbe 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -36,24 +36,15 @@ static struct snd_soc_jack_pin cs42l42_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "cs42l42" -}; - int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l42", card->components); -- cgit v1.2.3 From 1bf95876e6e1a3c77637d951cf59fb5c02bd02fa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:36 -0500 Subject: ASoC: Intel: sof_sdw_rt711: use dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt711.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index d49e5aa786c3..60ff4d88e2dc 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -59,24 +59,15 @@ static struct snd_soc_jack_pin rt711_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt711" -}; - int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt711", card->components); -- cgit v1.2.3 From 044413afbcca5da3ac61f2fb829a1fbb0df8ac81 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:37 -0500 Subject: ASoC: Intel: sof_sdw_rt5682: use dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt5682.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 96f193798540..67737815d016 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -35,24 +35,15 @@ static struct snd_soc_jack_pin rt5682_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt5682" -}; - int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt5682", card->components); -- cgit v1.2.3 From b237afe50151e5041d0734c759d5f8dac5c1b694 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:38 -0500 Subject: ASoC: Intel: sof_sdw_rt700: use dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt700.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index f9575db9d99c..0db730071be2 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -33,24 +33,15 @@ static struct snd_soc_jack_pin rt700_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt700" -}; - int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt700", card->components); -- cgit v1.2.3 From 06868a46301f4bcb1c9dcd71a291775ff3627a7a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:39 -0500 Subject: ASoC: Intel: sof_sdw_rt_dmic: use from dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt_dmic.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_rt_dmic.c b/sound/soc/intel/boards/sof_sdw_rt_dmic.c index b8b493d5c6ec..ea7c1a4bc566 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_dmic.c +++ b/sound/soc/intel/boards/sof_sdw_rt_dmic.c @@ -12,25 +12,13 @@ #include "sof_board_helpers.h" #include "sof_sdw_common.h" -static const char * const dmics[] = { - "rt715", - "rt715-sdca", - "rt712-sdca-dmic", - "rt722-sdca", -}; - int rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct snd_soc_component *component; - struct snd_soc_dai *codec_dai; char *mic_name; - codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; /* * rt715-sdca (aka rt714) is a special case that uses different name in card->components -- cgit v1.2.3 From 3c3e35cec005467f4091907f5ff8c8cbfecc1bf1 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:40 -0500 Subject: ASoC: Intel: sof_sdw_rt_sdca_jack_common: use dai parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dai is passed from the parameter. We don't need to find the dai by name. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index 012195c50519..4254e30ee4c3 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -74,10 +74,6 @@ static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt711", "rt712", "rt713", "rt722" -}; - /* * The sdca suffix is required for rt711 since there are two generations of the same chip. * RT713 is an SDCA device but the sdca suffix is required for backwards-compatibility with @@ -91,17 +87,12 @@ int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *d { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; int i; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:%s", card->components, component->name_prefix); -- cgit v1.2.3 From a9a7e865b187eadac8f5c253f7f9befca80699f4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 May 2024 14:35:41 -0500 Subject: ASoC: Intel: sof_sdw: remove get_codec_dai_by_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_codec_dai_by_name() is not used anymore. Remove it. Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 18 ------------------ sound/soc/intel/boards/sof_sdw_common.h | 4 ---- 2 files changed, 22 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index e41b0d95e0ff..9dc50a6d5301 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -559,24 +559,6 @@ static const struct snd_kcontrol_new rt700_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, - const char * const dai_name[], - int num_dais) -{ - struct snd_soc_dai *dai; - int index; - int i; - - for (index = 0; index < num_dais; index++) - for_each_rtd_codec_dais(rtd, i, dai) - if (strstr(dai->name, dai_name[index])) { - dev_dbg(rtd->card->dev, "get dai %s\n", dai->name); - return dai; - } - - return NULL; -} - /* these wrappers are only needed to avoid typecast compilation errors */ int sdw_startup(struct snd_pcm_substream *substream) { diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 3dfba6f6b95d..4e92ff65b537 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -134,10 +134,6 @@ struct mc_private { extern unsigned long sof_sdw_quirk; -struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, - const char * const dai_name[], - int num_dais); - int sdw_startup(struct snd_pcm_substream *substream); int sdw_prepare(struct snd_pcm_substream *substream); int sdw_trigger(struct snd_pcm_substream *substream, int cmd); -- cgit v1.2.3 From 8a7f876a692c4c7dc7ae845ae400b94df5a6a7c4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 27 May 2024 14:35:42 -0500 Subject: ASoC: Intel: sof_sdw: Add missing controls for cs42l43/cs35l56 No reason for cs42l43 and cs35l56 not to offer pin switch controls for the headphones and speakers, add these controls. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 9dc50a6d5301..0516ae660824 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1059,6 +1059,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_cs_amp_init, .rtd_init = cs_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), }, @@ -1094,6 +1096,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .rtd_init = cs42l43_hs_rtd_init, + .controls = generic_jack_controls, + .num_controls = ARRAY_SIZE(generic_jack_controls), .widgets = generic_jack_widgets, .num_widgets = ARRAY_SIZE(generic_jack_widgets), }, @@ -1119,6 +1123,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_cs42l43_spk_init, .rtd_init = cs42l43_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), .quirk = SOF_CODEC_SPKR | SOF_SIDECAR_AMPS, -- cgit v1.2.3 From 65ab45b90656e9b7ed51bce27ab7d83618167e76 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 27 May 2024 14:35:43 -0500 Subject: ASoC: Intel: soc-acpi: Add match entries for some cs42l43 laptops Add some new match table entries on Arrowlake and Raptorlake for some coming cs42l43 laptops. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-arl-match.c | 50 +++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-rpl-match.c | 50 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 79d26e0f2c28..cc87c34e5a08 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -15,6 +15,42 @@ static const struct snd_soc_acpi_endpoint single_endpoint = { .group_id = 0, }; +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { + { + .adr = 0x00003001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { .adr = 0x000020025D071100ull, @@ -33,6 +69,14 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_link_adr arl_cs42l43_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, +}; + static const struct snd_soc_acpi_link_adr arl_rvp[] = { { .mask = BIT(0), @@ -58,6 +102,12 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { + { + .link_mask = BIT(0), + .links = arl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", + }, { .link_mask = 0x1, /* link0 required */ .links = arl_rvp, diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index b0a49e28ab09..bc8817633b81 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -30,6 +30,42 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { + { + .adr = 0x00003001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { .adr = 0x000020025D071100ull, @@ -156,6 +192,14 @@ static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { } }; +static const struct snd_soc_acpi_link_adr rpl_cs42l43_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, +}; + static const struct snd_soc_acpi_link_adr rpl_sdca_3_in_1[] = { { .mask = BIT(0), @@ -446,6 +490,12 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = { + { + .link_mask = BIT(0), + .links = rpl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-rpl-cs42l43-l0.tplg", + }, { .link_mask = 0xF, /* 4 active links required */ .links = rpl_sdca_3_in_1, -- cgit v1.2.3 From 91cdecaba791c74df6da0650e797fe1192cf2700 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 27 May 2024 14:35:44 -0500 Subject: ASoC: Intel: sof_sdw: Add quirks for some new Dell laptops Add quirks for some new Dell laptops using Cirrus amplifiers in a bridge configuration. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 0516ae660824..b646b32dd311 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -505,6 +505,22 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(RT711_JD2), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE3") + }, + .driver_data = (void *)(SOF_SIDECAR_AMPS), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE4") + }, + .driver_data = (void *)(SOF_SIDECAR_AMPS), + }, {} }; -- cgit v1.2.3 From 6073c477d11c1ea4b3f13d9f6e15ca54041af9a3 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:46 -0500 Subject: ASoC: SOF: sof-audio: rename dai clock setting query function We are going to extend the dai_get_clk() callback function to retrieve other dai-specific settings like tdm slot number, not just mclk/bclk frequencies. Revisit functions and macros related to the callback and replace the string 'clk' with 'param' to reflect the ongoing change. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 12 ++++++------ sound/soc/sof/ipc4-topology.c | 12 ++++++------ sound/soc/sof/sof-audio.c | 10 +++++----- sound/soc/sof/sof-audio.h | 8 ++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 32c7d1f3b528..b68dea41cd95 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2500,7 +2500,7 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif return 0; } -static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +static int sof_ipc3_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { struct sof_dai_private_data *private = dai->private; @@ -2509,15 +2509,15 @@ static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *da switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: + switch (param_type) { + case SOF_DAI_PARAM_INTEL_SSP_MCLK: return private->dai_config->ssp.mclk_rate; - case SOF_DAI_CLK_INTEL_SSP_BCLK: + case SOF_DAI_PARAM_INTEL_SSP_BCLK: return private->dai_config->ssp.bclk_rate; default: + dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; } - dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type); break; default: /* not yet implemented for platforms other than the above */ @@ -2692,7 +2692,7 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, - .dai_get_clk = sof_ipc3_dai_get_clk, + .dai_get_param = sof_ipc3_dai_get_param, .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, .parse_manifest = sof_ipc3_parse_manifest, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index beff10989324..67e34fd559f2 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3130,7 +3130,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, return 0; } -static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { struct sof_ipc4_copier *ipc4_copier = dai->private; struct snd_soc_tplg_hw_config *hw_config; @@ -3169,13 +3169,13 @@ static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *da switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: + switch (param_type) { + case SOF_DAI_PARAM_INTEL_SSP_MCLK: return le32_to_cpu(hw_config->mclk_rate); - case SOF_DAI_CLK_INTEL_SSP_BCLK: + case SOF_DAI_PARAM_INTEL_SSP_BCLK: return le32_to_cpu(hw_config->bclk_rate); default: - dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type); + dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; } break; @@ -3352,7 +3352,7 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .route_free = sof_ipc4_route_free, .dai_config = sof_ipc4_dai_config, .parse_manifest = sof_ipc4_parse_manifest, - .dai_get_clk = sof_ipc4_dai_get_clk, + .dai_get_param = sof_ipc4_dai_get_param, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .link_setup = sof_ipc4_link_setup, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b3ac040811e7..2421a115747e 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -978,7 +978,7 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, return NULL; } -static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) +static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); @@ -991,8 +991,8 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) if (!dai) return 0; - if (tplg_ops && tplg_ops->dai_get_clk) - return tplg_ops->dai_get_clk(sdev, dai, clk_type); + if (tplg_ops && tplg_ops->dai_get_param) + return tplg_ops->dai_get_param(sdev, dai, param_type); return 0; } @@ -1003,7 +1003,7 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) */ int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd) { - return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK); + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_MCLK); } EXPORT_SYMBOL(sof_dai_get_mclk); @@ -1013,6 +1013,6 @@ EXPORT_SYMBOL(sof_dai_get_mclk); */ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) { - return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK); + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_BCLK); } EXPORT_SYMBOL(sof_dai_get_bclk); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index ec2a3bb644d2..abb2a00c520d 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -44,8 +44,8 @@ #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer) -#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 -#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 +#define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 +#define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 enum sof_widget_op { SOF_WIDGET_PREPARE, @@ -208,7 +208,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP - * @dai_get_clk: Function pointer for getting the DAI clock setting + * @dai_get_param: Function pointer for getting the DAI parameter * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest @@ -229,7 +229,7 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); - int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type); + int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*parse_manifest)(struct snd_soc_component *scomp, int index, -- cgit v1.2.3 From 1deba6e24c221c61c6eab8656a53f8c17035932b Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:47 -0500 Subject: ASoC: SOF: sof-audio: add sof_dai_get_tdm_slots function An new interface, sof_dai_get_tdm_slots(), is added for machine driver to get tdm slot number from topology. The dai_get_param() callback needs to support new parameter type SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS by returning the tdm slot number of specific SSP port. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 1 + sound/soc/sof/sof-audio.c | 10 ++++++++++ sound/soc/sof/sof-audio.h | 5 +++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index ec6c30d54592..64fd5504cb2b 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -173,5 +173,6 @@ struct sof_dev_desc { int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd); int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd); +int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd); #endif diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 2421a115747e..881eec38c2e2 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1016,3 +1016,13 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_BCLK); } EXPORT_SYMBOL(sof_dai_get_bclk); + +/* + * Helper to get SSP TDM slot number from a pcm_runtime. + * Return 0 if not exist. + */ +int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS); +} +EXPORT_SYMBOL(sof_dai_get_tdm_slots); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index abb2a00c520d..49be02234fc3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -44,8 +44,9 @@ #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer) -#define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 -#define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 +#define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 +#define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 +#define SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS 2 enum sof_widget_op { SOF_WIDGET_PREPARE, -- cgit v1.2.3 From e495f3ebe967fcc487d84f80ef236e4c0e92c2b6 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:48 -0500 Subject: ASoC: SOF: ipc3-topology: support tdm slot number query Support the new parameter type SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS in sof_ipc3_dai_get_param() function to get the tdm slot number of specific SSP port. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index b68dea41cd95..be61e377e59e 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2514,6 +2514,8 @@ static int sof_ipc3_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * return private->dai_config->ssp.mclk_rate; case SOF_DAI_PARAM_INTEL_SSP_BCLK: return private->dai_config->ssp.bclk_rate; + case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS: + return private->dai_config->ssp.tdm_slots; default: dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; -- cgit v1.2.3 From 97a9e9915cbb43d0feedbe2c9cf117f3dabf1286 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:49 -0500 Subject: ASoC: SOF: ipc4-topology: support tdm slot number query Support the new parameter type SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS in sof_ipc4_dai_get_param() function to get the tdm slot number of specific SSP port. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-16-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 67e34fd559f2..df4344f7d547 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3174,6 +3174,8 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * return le32_to_cpu(hw_config->mclk_rate); case SOF_DAI_PARAM_INTEL_SSP_BCLK: return le32_to_cpu(hw_config->bclk_rate); + case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS: + return le32_to_cpu(hw_config->tdm_slots); default: dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; -- cgit v1.2.3 From 459d71f14771211ee19d10a97675f4f08871f58a Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:50 -0500 Subject: ASoC: Intel: maxim-common: rewrite max_98373_hw_params function Rewrite max_98373_hw_params() function to get tdm slot number from topology in runtime because earlier platforms use 4-slot setting instead of 8. Also check if the interface is configured in TDM mode before calling snd_soc_dai_set_tdm_slot() function. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_maxim_common.c | 84 ++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 6c40ecc04723..f965b172fa36 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "../common/soc-intel-quirks.h" #include "sof_maxim_common.h" @@ -72,26 +73,85 @@ static struct snd_soc_dai_link_component max_98373_components[] = { }, }; +/* + * According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask + * should choose two channels from TDM slots, the LSB of rx mask is left channel + * and the other one is right channel. + * + * For tx mask, each codec requires two channels: one for V-sense and the other + * one for I-sense. Must match the device property "maxim,vmon-slot-no" and + * "maxim,imon-slot-no" in ACPI table. + */ +static const struct { + unsigned int tx; + unsigned int rx; +} max_98373_tdm_mask[] = { + {.tx = 0x03, .rx = 0x3}, + {.tx = 0x0c, .rx = 0x3}, +}; + static int max_98373_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *codec_dai; + int i; + int tdm_slots; int ret = 0; - int j; - for_each_rtd_codec_dais(rtd, j, codec_dai) { - if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { - /* DEV0 tdm slot configuration */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); - } else if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { - /* DEV1 tdm slot configuration */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (i >= ARRAY_SIZE(max_98373_tdm_mask)) { + dev_err(codec_dai->dev, "only 2 amps are supported\n"); + return -EINVAL; } - if (ret < 0) { - dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", - ret); - return ret; + + switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* get the tplg configured tdm slot number */ + tdm_slots = sof_dai_get_tdm_slots(rtd); + if (tdm_slots <= 0) { + dev_err(rtd->dev, "invalid tdm slots %d\n", + tdm_slots); + return -EINVAL; + } + + /* + * check if tdm slot number is too small for channel + * allocation + */ + if (fls(max_98373_tdm_mask[i].tx) > tdm_slots) { + dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n", + fls(max_98373_tdm_mask[i].tx), tdm_slots); + return -EINVAL; + } + + if (fls(max_98373_tdm_mask[i].rx) > tdm_slots) { + dev_err(codec_dai->dev, "slot mismatch, rx %d slots %d\n", + fls(max_98373_tdm_mask[i].rx), tdm_slots); + return -EINVAL; + } + + dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n", + max_98373_tdm_mask[i].tx, + max_98373_tdm_mask[i].rx, + tdm_slots, params_width(params)); + + ret = snd_soc_dai_set_tdm_slot(codec_dai, + max_98373_tdm_mask[i].tx, + max_98373_tdm_mask[i].rx, + tdm_slots, + params_width(params)); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", + ret); + return ret; + } + break; + default: + dev_dbg(codec_dai->dev, "codec is in I2S mode\n"); + break; } } return 0; -- cgit v1.2.3 From 660f029afe9bc4217e600f5616c66b855615b666 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:51 -0500 Subject: ASoC: Intel: sof_da7219: remove local max98373 ops Since maxim-module could detect tdm slot number in runtime, we could remove local ops and use maxim-module to handle max98373. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-18-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_board_helpers.h | 2 -- sound/soc/intel/boards/sof_da7219.c | 44 ------------------------------ 2 files changed, 46 deletions(-) diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index dfcc2c5c25cc..faba847bb7c9 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -86,12 +86,10 @@ enum { /* * sof_da7219_private: private data for da7219 machine driver * - * @is_jsl_board: true for JSL boards * @mclk_en: true for mclk pin is connected * @pll_bypass: true for PLL bypass mode */ struct sof_da7219_private { - bool is_jsl_board; bool mclk_en; bool pll_bypass; }; diff --git a/sound/soc/intel/boards/sof_da7219.c b/sound/soc/intel/boards/sof_da7219.c index 886771e9b9d6..0e3c352a1672 100644 --- a/sound/soc/intel/boards/sof_da7219.c +++ b/sound/soc/intel/boards/sof_da7219.c @@ -178,40 +178,6 @@ static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd) snd_soc_component_set_jack(component, NULL, NULL); } -static int max98373_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream); - int ret, j; - - for (j = 0; j < runtime->dai_link->num_codecs; j++) { - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, j); - - if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { - /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 3, 4, 16); - if (ret < 0) { - dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); - return ret; - } - } - if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { - /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC, 3, 4, 16); - if (ret < 0) { - dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); - return ret; - } - } - } - - return 0; -} - -static const struct snd_soc_ops max98373_ops = { - .hw_params = max98373_hw_params, -}; - static int card_late_probe(struct snd_soc_card *card) { return sof_intel_board_card_late_probe(card); @@ -276,14 +242,6 @@ sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, break; case CODEC_MAX98373: max_98373_dai_link(dev, ctx->amp_link); - - if (ctx->da7219.is_jsl_board) { - ctx->amp_link->ops = &max98373_ops; /* use local ops */ - } else { - /* TBD: implement the amp for later platform */ - dev_err(dev, "max98373 not support yet\n"); - return -EINVAL; - } break; case CODEC_MAX98390: max_98390_dai_link(dev, ctx->amp_link); @@ -388,8 +346,6 @@ static int audio_probe(struct platform_device *pdev) break; } } else if (board_quirk & SOF_DA7219_JSL_BOARD) { - ctx->da7219.is_jsl_board = true; - /* overwrite the DAI link order for JSL boards */ ctx->link_order_overwrite = JSL_LINK_ORDER; -- cgit v1.2.3 From 1085350387056812d19abaf20c59674ce36f0b8c Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 May 2024 14:35:52 -0500 Subject: ASoC: Intel: sof_da7219: disable max98373 speaker pins in late_probe Commit 94d2d0897474 ("ASoC: Intel: Boards: tgl_max98373: add dai_trigger function") disables speaker pins in late_probe to allow DSP to enter low power state. Add same code to sof_da7219 driver. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240527193552.165567-19-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_da7219.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/sof_da7219.c b/sound/soc/intel/boards/sof_da7219.c index 0e3c352a1672..fa1f7d2d8278 100644 --- a/sound/soc/intel/boards/sof_da7219.c +++ b/sound/soc/intel/boards/sof_da7219.c @@ -180,6 +180,19 @@ static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd) static int card_late_probe(struct snd_soc_card *card) { + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = &card->dapm; + int err; + + if (ctx->amp_type == CODEC_MAX98373) { + /* Disable Left and Right Spk pin after boot */ + snd_soc_dapm_disable_pin(dapm, "Left Spk"); + snd_soc_dapm_disable_pin(dapm, "Right Spk"); + err = snd_soc_dapm_sync(dapm); + if (err < 0) + return err; + } + return sof_intel_board_card_late_probe(card); } -- cgit v1.2.3 From e46e55b8142c2b02972e4a73753fa330a4541315 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 27 May 2024 14:39:36 -0500 Subject: ASoC: SOF: Intel: hda: print PCI class info only once With the deferred probe mechanism used by the gfx/display subsystem, we see this message repeated for no good reason. Print the information only once. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://msgid.link/r/20240527193936.165702-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e6a38de0a0aa..541c6052d4ed 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -783,8 +783,8 @@ int hda_dsp_probe_early(struct snd_sof_dev *sdev) pci->class); return -ENODEV; } - dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", - pci->class); + dev_info_once(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", + pci->class); } chip = get_chip_info(sdev->pdata); -- cgit v1.2.3 From 278343bba366d23f8afc99f2d1eb0ab959c32001 Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:05 +0200 Subject: ASoC: dt-bindings: samsung,midas-audio: Add headset mic bias supply Some devices use a separate mic bias supply (also referred to as "ear mic bias") to enable/disable the headset mic. Add the DT property headset-mic-bias-supply to allow for specifying this supply. Signed-off-by: Artur Weber Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-1-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml index 6ec80f529d84..6ed53dd0bb53 100644 --- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml @@ -53,6 +53,9 @@ properties: submic-bias-supply: description: Supply for the micbias on the Sub microphone + headset-mic-bias-supply: + description: Supply for the micbias on the Headset microphone + fm-sel-gpios: maxItems: 1 description: GPIO pin for FM selection -- cgit v1.2.3 From 0a590ecc672ae3de56384bbed05a4de532034b8f Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:06 +0200 Subject: ASoC: dt-bindings: samsung,midas-audio: Add GPIO-based headset jack detection Some Samsung devices that share the midas-audio driver use a GPIO-based approach to headset jack detection, as opposed to using the built-in jack detection provided by the wm8994 driver. This setup uses two GPIOs (one for jack detection and another for key detection) and an ADC channel for determining the jack type or button pressed. Add DT configuration values that allow for describing these setups. Signed-off-by: Artur Weber Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-2-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- .../bindings/sound/samsung,midas-audio.yaml | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml index 6ed53dd0bb53..69ddfd4afdcd 100644 --- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml @@ -64,6 +64,36 @@ properties: maxItems: 1 description: GPIO pin for line out selection + headset-detect-gpios: + maxItems: 1 + description: GPIO for detection of headset insertion + + headset-key-gpios: + maxItems: 1 + description: GPIO for detection of headset key press + + io-channels: + maxItems: 1 + description: IO channel to read micbias voltage for headset detection + + io-channel-names: + const: headset-detect + + samsung,headset-4pole-threshold-microvolt: + minItems: 2 + maxItems: 2 + description: + Array containing minimum and maximum IO channel value for 4-pole + (with microphone/button) headsets. If the IO channel value is + outside of this range, a 3-pole headset is assumed. + + samsung,headset-button-threshold-microvolt: + minItems: 3 + maxItems: 3 + description: | + Array of minimum (inclusive) IO channel values for headset button + detection, in order: "Media", "Volume Up" and "Volume Down". + required: - compatible - cpu -- cgit v1.2.3 From c6aa3ade499d8ee3374be70c137bd4411fbfdbdd Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:07 +0200 Subject: ASoC: samsung: midas_wm1811: Use SND_SOC_DAPM_REGULATOR_SUPPLY for bias regulators Instead of using a custom function with the SND_SOC_DAPM_MIC widget, split out the main mic/sub mic bias regulators into a separate widget using SND_SOC_DAPM_REGULATOR_SUPPLY, and connect them via a routing. Signed-off-by: Artur Weber Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-3-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 62 +++++++++------------------------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 0841e2e6f8ce..072a4707e9db 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -27,8 +26,6 @@ #define DEFAULT_FLL1_RATE 11289600U struct midas_priv { - struct regulator *reg_mic_bias; - struct regulator *reg_submic_bias; struct gpio_desc *gpio_fm_sel; struct gpio_desc *gpio_lineout_sel; unsigned int fll1_rate; @@ -169,38 +166,6 @@ static int midas_ext_spkmode(struct snd_soc_dapm_widget *w, return ret; } -static int midas_mic_bias(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_card *card = w->dapm->card; - struct midas_priv *priv = snd_soc_card_get_drvdata(card); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - return regulator_enable(priv->reg_mic_bias); - case SND_SOC_DAPM_POST_PMD: - return regulator_disable(priv->reg_mic_bias); - } - - return 0; -} - -static int midas_submic_bias(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_card *card = w->dapm->card; - struct midas_priv *priv = snd_soc_card_get_drvdata(card); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - return regulator_enable(priv->reg_submic_bias); - case SND_SOC_DAPM_POST_PMD: - return regulator_disable(priv->reg_submic_bias); - } - - return 0; -} - static int midas_fm_set(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -272,8 +237,17 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias), - SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias), + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("mic-bias", 0, 0), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("submic-bias", 0, 0), +}; + +/* Default routing; supplemented by audio-routing DT property */ +static const struct snd_soc_dapm_route midas_dapm_routes[] = { + /* Bind microphones with their respective regulator supplies */ + {"Main Mic", NULL, "mic-bias"}, + {"Sub Mic", NULL, "submic-bias"}, }; static int midas_set_bias_level(struct snd_soc_card *card, @@ -421,6 +395,8 @@ static struct snd_soc_card midas_card = { .num_controls = ARRAY_SIZE(midas_controls), .dapm_widgets = midas_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(midas_dapm_widgets), + .dapm_routes = midas_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(midas_dapm_routes), .set_bias_level = midas_set_bias_level, .late_probe = midas_late_probe, @@ -443,18 +419,6 @@ static int midas_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); card->dev = dev; - priv->reg_mic_bias = devm_regulator_get(dev, "mic-bias"); - if (IS_ERR(priv->reg_mic_bias)) { - dev_err(dev, "Failed to get mic bias regulator\n"); - return PTR_ERR(priv->reg_mic_bias); - } - - priv->reg_submic_bias = devm_regulator_get(dev, "submic-bias"); - if (IS_ERR(priv->reg_submic_bias)) { - dev_err(dev, "Failed to get submic bias regulator\n"); - return PTR_ERR(priv->reg_submic_bias); - } - priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpio_fm_sel)) { dev_err(dev, "Failed to get FM selection GPIO\n"); -- cgit v1.2.3 From 9da93d4932aa17d5a1342af75872fbba776a3a8d Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:08 +0200 Subject: ASoC: samsung: midas_wm1811: Add headset mic bias supply support Some devices use a headset mic bias supply (sometimes referred to as "ear mic bias") to enable/disable the headset mic. Add a REGULATOR_SUPPLY widget for this supply which gets the headset- mic-bias supply and is routed to the Headset Mic switch similar to how it's done for the other mic bias regulators. Signed-off-by: Artur Weber Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-4-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 072a4707e9db..4226d9169775 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -237,6 +237,7 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("headset-mic-bias", 0, 0), SND_SOC_DAPM_MIC("Main Mic", NULL), SND_SOC_DAPM_REGULATOR_SUPPLY("mic-bias", 0, 0), SND_SOC_DAPM_MIC("Sub Mic", NULL), @@ -248,6 +249,7 @@ static const struct snd_soc_dapm_route midas_dapm_routes[] = { /* Bind microphones with their respective regulator supplies */ {"Main Mic", NULL, "mic-bias"}, {"Sub Mic", NULL, "submic-bias"}, + {"Headset Mic", NULL, "headset-mic-bias"}, }; static int midas_set_bias_level(struct snd_soc_card *card, -- cgit v1.2.3 From c91d0c2e198d4453e634fe43a72ccbc59811af17 Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:09 +0200 Subject: ASoC: samsung: midas_wm1811: Add GPIO-based headset jack detection Some Samsung devices that use the midas_wm1811 driver use a GPIO-based method for detecting whether the headset jack is plugged in, as well as detecting which headset buttons are pressed. There are two GPIOs: a "headset detect" GPIO responsible for detecting jack insertion, and a "headset key" GPIO which triggers when a button on the headset is pressed. The plug type and the button pressed are determined based on information from an ADC channel. The headset mic is enabled by a headset mic bias regulator. Add support for the GPIO-based headset jack detection mechanism, and make it configurable from the device tree. This implementation borrows somewhat from the aries_wm8994.c driver, though there are a few changes to make the code cleaner, and to add support for DT-based configuration. Notably, a dependency on IIO is introduced, to accommodate the ADC reading requirement. Signed-off-by: Artur Weber Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-5-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/Kconfig | 2 +- sound/soc/samsung/midas_wm1811.c | 272 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 263 insertions(+), 11 deletions(-) diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 93c2b1b08d0a..4b1ea7b2c796 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -140,7 +140,7 @@ config SND_SOC_SAMSUNG_ARIES_WM8994 config SND_SOC_SAMSUNG_MIDAS_WM1811 tristate "SoC I2S Audio support for Midas boards" - depends on SND_SOC_SAMSUNG + depends on SND_SOC_SAMSUNG && IIO select SND_SAMSUNG_I2S select SND_SOC_WM8994 help diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 4226d9169775..3a6d3fc8152a 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include #include #include @@ -28,6 +30,9 @@ struct midas_priv { struct gpio_desc *gpio_fm_sel; struct gpio_desc *gpio_lineout_sel; + struct gpio_desc *gpio_headset_detect; + struct gpio_desc *gpio_headset_key; + struct iio_channel *adc_headset_detect; unsigned int fll1_rate; struct snd_soc_jack headset_jack; @@ -44,6 +49,117 @@ static struct snd_soc_jack_pin headset_jack_pins[] = { }, }; +/* + * min_mv/max_mv values in this struct are set up based on DT values. + */ +static struct snd_soc_jack_zone headset_jack_zones[] = { + { .jack_type = SND_JACK_HEADPHONE, }, + { .jack_type = SND_JACK_HEADSET, }, + { .jack_type = SND_JACK_HEADPHONE, }, +}; + +/* + * This is used for manual detection in headset_key_check, we reuse the + * structure since it's convenient. + * + * min_mv/max_mv values in this struct are set up based on DT values. + */ +static struct snd_soc_jack_zone headset_key_zones[] = { + { .jack_type = SND_JACK_BTN_0, }, /* Media */ + { .jack_type = SND_JACK_BTN_1, }, /* Volume Up */ + { .jack_type = SND_JACK_BTN_2, }, /* Volume Down */ +}; + +static int headset_jack_check(void *data) +{ + struct snd_soc_component *codec = data; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card); + int adc, ret; + int jack_type = 0; + + if (!gpiod_get_value_cansleep(priv->gpio_headset_detect)) + return 0; + + /* Enable headset mic bias regulator so that the ADC reading works */ + ret = snd_soc_dapm_force_enable_pin(dapm, "headset-mic-bias"); + if (ret < 0) { + pr_err("%s: Failed to enable headset mic bias regulator (%d), assuming headphones\n", + __func__, ret); + return SND_JACK_HEADPHONE; + } + snd_soc_dapm_sync(dapm); + + /* Sleep for a small amount of time to get the value to stabilize */ + msleep(20); + + ret = iio_read_channel_processed(priv->adc_headset_detect, &adc); + if (ret) { + pr_err("%s: Failed to read ADC (%d), assuming headphones\n", + __func__, ret); + jack_type = SND_JACK_HEADPHONE; + goto out; + } + pr_debug("%s: ADC value is %d\n", __func__, adc); + + jack_type = snd_soc_jack_get_type(&priv->headset_jack, adc); + +out: + ret = snd_soc_dapm_disable_pin(dapm, "headset-mic-bias"); + if (ret < 0) + pr_err("%s: Failed to disable headset mic bias regulator (%d)\n", + __func__, ret); + snd_soc_dapm_sync(dapm); + + return jack_type; +} + +static int headset_key_check(void *data) +{ + struct snd_soc_component *codec = data; + struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card); + int adc, i, ret; + + if (!gpiod_get_value_cansleep(priv->gpio_headset_key)) + return 0; + + /* Filter out keypresses when 4 pole jack not detected */ + if (!(priv->headset_jack.status & SND_JACK_MICROPHONE)) + return 0; + + ret = iio_read_channel_processed(priv->adc_headset_detect, &adc); + if (ret) { + pr_err("%s: Failed to read ADC (%d), can't detect key type\n", + __func__, ret); + return 0; + } + pr_debug("%s: ADC value is %d\n", __func__, adc); + + for (i = 0; i < ARRAY_SIZE(headset_key_zones); i++) { + if (adc >= headset_key_zones[i].min_mv && + adc <= headset_key_zones[i].max_mv) { + return headset_key_zones[i].jack_type; + } + } + + return 0; +} + +static struct snd_soc_jack_gpio headset_gpio[] = { + { + .name = "Headset Jack", + .report = SND_JACK_HEADSET, + .debounce_time = 150, + .jack_status_check = headset_jack_check, + }, + { + .name = "Headset Key", + .report = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, + .debounce_time = 30, + .jack_status_check = headset_key_check, + }, +}; + static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) { struct snd_soc_card *card = rtd->card; @@ -291,18 +407,67 @@ static int midas_late_probe(struct snd_soc_card *card) return ret; } - ret = snd_soc_card_jack_new_pins(card, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &priv->headset_jack, - headset_jack_pins, - ARRAY_SIZE(headset_jack_pins)); - if (ret) + if (!priv->gpio_headset_detect) { + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); + if (ret) + return ret; + + wm8958_mic_detect(aif1_dai->component, &priv->headset_jack, + NULL, NULL, NULL, NULL); + } else { + /* Some devices (n8000, t310) use a GPIO to detect the jack. */ + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); + if (ret) { + dev_err(card->dev, + "Failed to set up headset pins: %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_zones(&priv->headset_jack, + ARRAY_SIZE(headset_jack_zones), + headset_jack_zones); + if (ret) { + dev_err(card->dev, + "Failed to set up headset zones: %d\n", ret); + return ret; + } + + headset_gpio[0].data = aif1_dai->component; + headset_gpio[0].desc = priv->gpio_headset_detect; + + headset_gpio[1].data = aif1_dai->component; + headset_gpio[1].desc = priv->gpio_headset_key; + + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_2, KEY_VOLUMEDOWN); + + ret = snd_soc_jack_add_gpios(&priv->headset_jack, + ARRAY_SIZE(headset_gpio), + headset_gpio); + if (ret) + dev_err(card->dev, + "Failed to set up headset jack GPIOs: %d\n", + ret); + return ret; + } - wm8958_mic_detect(aif1_dai->component, &priv->headset_jack, - NULL, NULL, NULL, NULL); return 0; } @@ -411,6 +576,9 @@ static int midas_probe(struct platform_device *pdev) struct snd_soc_card *card = &midas_card; struct device *dev = &pdev->dev; static struct snd_soc_dai_link *dai_link; + enum iio_chan_type channel_type; + u32 fourpole_threshold[2]; + u32 button_threshold[3]; struct midas_priv *priv; int ret, i; @@ -434,6 +602,90 @@ static int midas_probe(struct platform_device *pdev) return PTR_ERR(priv->gpio_lineout_sel); } + priv->gpio_headset_detect = devm_gpiod_get_optional(dev, + "headset-detect", GPIOD_IN); + if (IS_ERR(priv->gpio_headset_detect)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_headset_detect), + "Failed to get headset jack detect GPIO\n"); + + if (priv->gpio_headset_detect) { + priv->adc_headset_detect = devm_iio_channel_get(dev, + "headset-detect"); + if (IS_ERR(priv->adc_headset_detect)) + return dev_err_probe(dev, + PTR_ERR(priv->adc_headset_detect), + "Failed to get ADC channel\n"); + + ret = iio_get_channel_type(priv->adc_headset_detect, + &channel_type); + if (ret) { + dev_err(dev, "Failed to get ADC channel type\n"); + return ret; + } + + if (channel_type != IIO_VOLTAGE) { + dev_err(dev, "ADC channel is not voltage\n"); + return ret; + } + + priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key", + GPIOD_IN); + if (IS_ERR(priv->gpio_headset_key)) + return dev_err_probe(dev, + PTR_ERR(priv->gpio_headset_key), + "Failed to get headset key GPIO\n"); + + ret = of_property_read_u32_array(dev->of_node, + "samsung,headset-4pole-threshold-microvolt", + fourpole_threshold, + ARRAY_SIZE(fourpole_threshold)); + if (ret) { + dev_err(dev, "Failed to get 4-pole jack detection threshold\n"); + return ret; + } + + if (fourpole_threshold[0] > fourpole_threshold[1]) { + dev_err(dev, "Invalid 4-pole jack detection threshold value\n"); + return -EINVAL; + } + + headset_jack_zones[0].max_mv = (fourpole_threshold[0]); + headset_jack_zones[1].min_mv = (fourpole_threshold[0] + 1); + + headset_jack_zones[1].max_mv = (fourpole_threshold[1]); + headset_jack_zones[2].min_mv = (fourpole_threshold[1] + 1); + + ret = of_property_read_u32_array(dev->of_node, + "samsung,headset-button-threshold-microvolt", + button_threshold, + ARRAY_SIZE(button_threshold)); + if (ret) { + dev_err(dev, "Failed to get headset button detection threshold\n"); + return ret; + } + + if (button_threshold[0] > button_threshold[1] || + button_threshold[1] > button_threshold[2]) { + dev_err(dev, "Invalid headset button detection threshold value\n"); + return -EINVAL; + } + + for (i = 0; i < 3; i++) { + if (i != 0 && button_threshold[i] <= 0) { + dev_err(dev, "Invalid headset button detection threshold value\n"); + return -EINVAL; + } + + headset_key_zones[i].min_mv = button_threshold[i]; + + if (i == 2) + headset_key_zones[i].max_mv = UINT_MAX; + else + headset_key_zones[i].max_mv = \ + (button_threshold[i+1] - 1); + } + } + ret = snd_soc_of_parse_card_name(card, "model"); if (ret < 0) { dev_err(dev, "Card name is not specified\n"); -- cgit v1.2.3 From 255009d22c185623140de600a5fb54f0fd541bb8 Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 25 May 2024 15:28:10 +0200 Subject: ASoC: samsung: midas_wm1811: Use dev_err_probe where appropriate Since we're already using it in the newly-added GPIO requests for jack detection, extend it to the previous checks as well. Signed-off-by: Artur Weber Link: https://msgid.link/r/20240525-midas-wm1811-gpio-jack-v4-6-f488e03bd8c7@gmail.com Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 3a6d3fc8152a..3a269c7de169 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -590,17 +590,15 @@ static int midas_probe(struct platform_device *pdev) card->dev = dev; priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpio_fm_sel)) { - dev_err(dev, "Failed to get FM selection GPIO\n"); - return PTR_ERR(priv->gpio_fm_sel); - } + if (IS_ERR(priv->gpio_fm_sel)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_fm_sel), + "Failed to get FM selection GPIO\n"); priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpio_lineout_sel)) { - dev_err(dev, "Failed to get line out selection GPIO\n"); - return PTR_ERR(priv->gpio_lineout_sel); - } + if (IS_ERR(priv->gpio_lineout_sel)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_lineout_sel), + "Failed to get line out selection GPIO\n"); priv->gpio_headset_detect = devm_gpiod_get_optional(dev, "headset-detect", GPIOD_IN); -- cgit v1.2.3 From b7d7f11a291830fdf69d3301075dd0fb347ced84 Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Tue, 28 May 2024 16:26:05 +0200 Subject: wifi: mac80211: mesh: Fix leak of mesh_preq_queue objects The hwmp code use objects of type mesh_preq_queue, added to a list in ieee80211_if_mesh, to keep track of mpath we need to resolve. If the mpath gets deleted, ex mesh interface is removed, the entries in that list will never get cleaned. Fix this by flushing all corresponding items of the preq_queue in mesh_path_flush_pending(). This should take care of KASAN reports like this: unreferenced object 0xffff00000668d800 (size 128): comm "kworker/u8:4", pid 67, jiffies 4295419552 (age 1836.444s) hex dump (first 32 bytes): 00 1f 05 09 00 00 ff ff 00 d5 68 06 00 00 ff ff ..........h..... 8e 97 ea eb 3e b8 01 00 00 00 00 00 00 00 00 00 ....>........... backtrace: [<000000007302a0b6>] __kmem_cache_alloc_node+0x1e0/0x35c [<00000000049bd418>] kmalloc_trace+0x34/0x80 [<0000000000d792bb>] mesh_queue_preq+0x44/0x2a8 [<00000000c99c3696>] mesh_nexthop_resolve+0x198/0x19c [<00000000926bf598>] ieee80211_xmit+0x1d0/0x1f4 [<00000000fc8c2284>] __ieee80211_subif_start_xmit+0x30c/0x764 [<000000005926ee38>] ieee80211_subif_start_xmit+0x9c/0x7a4 [<000000004c86e916>] dev_hard_start_xmit+0x174/0x440 [<0000000023495647>] __dev_queue_xmit+0xe24/0x111c [<00000000cfe9ca78>] batadv_send_skb_packet+0x180/0x1e4 [<000000007bacc5d5>] batadv_v_elp_periodic_work+0x2f4/0x508 [<00000000adc3cd94>] process_one_work+0x4b8/0xa1c [<00000000b36425d1>] worker_thread+0x9c/0x634 [<0000000005852dd5>] kthread+0x1bc/0x1c4 [<000000005fccd770>] ret_from_fork+0x10/0x20 unreferenced object 0xffff000009051f00 (size 128): comm "kworker/u8:4", pid 67, jiffies 4295419553 (age 1836.440s) hex dump (first 32 bytes): 90 d6 92 0d 00 00 ff ff 00 d8 68 06 00 00 ff ff ..........h..... 36 27 92 e4 02 e0 01 00 00 58 79 06 00 00 ff ff 6'.......Xy..... backtrace: [<000000007302a0b6>] __kmem_cache_alloc_node+0x1e0/0x35c [<00000000049bd418>] kmalloc_trace+0x34/0x80 [<0000000000d792bb>] mesh_queue_preq+0x44/0x2a8 [<00000000c99c3696>] mesh_nexthop_resolve+0x198/0x19c [<00000000926bf598>] ieee80211_xmit+0x1d0/0x1f4 [<00000000fc8c2284>] __ieee80211_subif_start_xmit+0x30c/0x764 [<000000005926ee38>] ieee80211_subif_start_xmit+0x9c/0x7a4 [<000000004c86e916>] dev_hard_start_xmit+0x174/0x440 [<0000000023495647>] __dev_queue_xmit+0xe24/0x111c [<00000000cfe9ca78>] batadv_send_skb_packet+0x180/0x1e4 [<000000007bacc5d5>] batadv_v_elp_periodic_work+0x2f4/0x508 [<00000000adc3cd94>] process_one_work+0x4b8/0xa1c [<00000000b36425d1>] worker_thread+0x9c/0x634 [<0000000005852dd5>] kthread+0x1bc/0x1c4 [<000000005fccd770>] ret_from_fork+0x10/0x20 Fixes: 050ac52cbe1f ("mac80211: code for on-demand Hybrid Wireless Mesh Protocol") Signed-off-by: Nicolas Escande Link: https://msgid.link/20240528142605.1060566-1-nico.escande@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index a6b62169f084..c0a5c75cddcb 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -1017,10 +1017,23 @@ void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, */ void mesh_path_flush_pending(struct mesh_path *mpath) { + struct ieee80211_sub_if_data *sdata = mpath->sdata; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_preq_queue *preq, *tmp; struct sk_buff *skb; while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) mesh_path_discard_frame(mpath->sdata, skb); + + spin_lock_bh(&ifmsh->mesh_preq_queue_lock); + list_for_each_entry_safe(preq, tmp, &ifmsh->preq_queue.list, list) { + if (ether_addr_equal(mpath->dst, preq->dst)) { + list_del(&preq->list); + kfree(preq); + --ifmsh->preq_queue_len; + } + } + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); } /** -- cgit v1.2.3 From 6f6291f09a322c1c1578badac8072d049363f4e6 Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Mon, 27 May 2024 16:17:59 +0200 Subject: wifi: mac80211: mesh: init nonpeer_pm to active by default in mesh sdata With a ath9k device I can see that: iw phy phy0 interface add mesh0 type mp ip link set mesh0 up iw dev mesh0 scan Will start a scan with the Power Management bit set in the Frame Control Field. This is because we set this bit depending on the nonpeer_pm variable of the mesh iface sdata and when there are no active links on the interface it remains to NL80211_MESH_POWER_UNKNOWN. As soon as links starts to be established, it wil switch to NL80211_MESH_POWER_ACTIVE as it is the value set by befault on the per sta nonpeer_pm field. As we want no power save by default, (as expressed with the per sta ini values), lets init it to the expected default value of NL80211_MESH_POWER_ACTIVE. Also please note that we cannot change the default value from userspace prior to establishing a link as using NL80211_CMD_SET_MESH_CONFIG will not work before NL80211_CMD_JOIN_MESH has been issued. So too late for our initial scan. Signed-off-by: Nicolas Escande Link: https://msgid.link/20240527141759.299411-1-nico.escande@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index cbc9b5e40cb3..6d4510221c98 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1776,6 +1776,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) ifmsh->last_preq = jiffies; ifmsh->next_perr = jiffies; ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; + ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); -- cgit v1.2.3 From 44c06bbde6443de206b30f513100b5670b23fc5e Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Wed, 29 May 2024 08:57:53 +0200 Subject: wifi: mac80211: Fix deadlock in ieee80211_sta_ps_deliver_wakeup() The ieee80211_sta_ps_deliver_wakeup() function takes sta->ps_lock to synchronizes with ieee80211_tx_h_unicast_ps_buf() which is called from softirq context. However using only spin_lock() to get sta->ps_lock in ieee80211_sta_ps_deliver_wakeup() does not prevent softirq to execute on this same CPU, to run ieee80211_tx_h_unicast_ps_buf() and try to take this same lock ending in deadlock. Below is an example of rcu stall that arises in such situation. rcu: INFO: rcu_sched self-detected stall on CPU rcu: 2-....: (42413413 ticks this GP) idle=b154/1/0x4000000000000000 softirq=1763/1765 fqs=21206996 rcu: (t=42586894 jiffies g=2057 q=362405 ncpus=4) CPU: 2 PID: 719 Comm: wpa_supplicant Tainted: G W 6.4.0-02158-g1b062f552873 #742 Hardware name: RPT (r1) (DT) pstate: 00000005 (nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : queued_spin_lock_slowpath+0x58/0x2d0 lr : invoke_tx_handlers_early+0x5b4/0x5c0 sp : ffff00001ef64660 x29: ffff00001ef64660 x28: ffff000009bc1070 x27: ffff000009bc0ad8 x26: ffff000009bc0900 x25: ffff00001ef647a8 x24: 0000000000000000 x23: ffff000009bc0900 x22: ffff000009bc0900 x21: ffff00000ac0e000 x20: ffff00000a279e00 x19: ffff00001ef646e8 x18: 0000000000000000 x17: ffff800016468000 x16: ffff00001ef608c0 x15: 0010533c93f64f80 x14: 0010395c9faa3946 x13: 0000000000000000 x12: 00000000fa83b2da x11: 000000012edeceea x10: ffff0000010fbe00 x9 : 0000000000895440 x8 : 000000000010533c x7 : ffff00000ad8b740 x6 : ffff00000c350880 x5 : 0000000000000007 x4 : 0000000000000001 x3 : 0000000000000000 x2 : 0000000000000000 x1 : 0000000000000001 x0 : ffff00000ac0e0e8 Call trace: queued_spin_lock_slowpath+0x58/0x2d0 ieee80211_tx+0x80/0x12c ieee80211_tx_pending+0x110/0x278 tasklet_action_common.constprop.0+0x10c/0x144 tasklet_action+0x20/0x28 _stext+0x11c/0x284 ____do_softirq+0xc/0x14 call_on_irq_stack+0x24/0x34 do_softirq_own_stack+0x18/0x20 do_softirq+0x74/0x7c __local_bh_enable_ip+0xa0/0xa4 _ieee80211_wake_txqs+0x3b0/0x4b8 __ieee80211_wake_queue+0x12c/0x168 ieee80211_add_pending_skbs+0xec/0x138 ieee80211_sta_ps_deliver_wakeup+0x2a4/0x480 ieee80211_mps_sta_status_update.part.0+0xd8/0x11c ieee80211_mps_sta_status_update+0x18/0x24 sta_apply_parameters+0x3bc/0x4c0 ieee80211_change_station+0x1b8/0x2dc nl80211_set_station+0x444/0x49c genl_family_rcv_msg_doit.isra.0+0xa4/0xfc genl_rcv_msg+0x1b0/0x244 netlink_rcv_skb+0x38/0x10c genl_rcv+0x34/0x48 netlink_unicast+0x254/0x2bc netlink_sendmsg+0x190/0x3b4 ____sys_sendmsg+0x1e8/0x218 ___sys_sendmsg+0x68/0x8c __sys_sendmsg+0x44/0x84 __arm64_sys_sendmsg+0x20/0x28 do_el0_svc+0x6c/0xe8 el0_svc+0x14/0x48 el0t_64_sync_handler+0xb0/0xb4 el0t_64_sync+0x14c/0x150 Using spin_lock_bh()/spin_unlock_bh() instead prevents softirq to raise on the same CPU that is holding the lock. Fixes: 1d147bfa6429 ("mac80211: fix AP powersave TX vs. wakeup race") Signed-off-by: Remi Pommarel Link: https://msgid.link/8e36fe07d0fbc146f89196cd47a53c8a0afe84aa.1716910344.git.repk@triplefau.lt Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index da5fdd6f5c85..aa22f09e6d14 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1724,7 +1724,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) skb_queue_head_init(&pending); /* sync with ieee80211_tx_h_unicast_ps_buf */ - spin_lock(&sta->ps_lock); + spin_lock_bh(&sta->ps_lock); /* Send all buffered frames to the station */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { int count = skb_queue_len(&pending), tmp; @@ -1753,7 +1753,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) */ clear_sta_flag(sta, WLAN_STA_PSPOLL); clear_sta_flag(sta, WLAN_STA_UAPSD); - spin_unlock(&sta->ps_lock); + spin_unlock_bh(&sta->ps_lock); atomic_dec(&ps->num_sta_ps); -- cgit v1.2.3 From 4dc3a3893dae5a7f73e5809273aca0f1f3548d55 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 May 2024 12:05:33 +0200 Subject: wifi: cfg80211: validate HE operation element parsing Validate that the HE operation element has the correct length before parsing it. Cc: stable@vger.kernel.org Fixes: 645f3d85129d ("wifi: cfg80211: handle UHB AP and STA power type") Reviewed-by: Miriam Rachel Korenblit Link: https://msgid.link/20240523120533.677025eb4a92.I44c091029ef113c294e8fe8b9bf871bf5dbeeb27@changeid Signed-off-by: Johannes Berg --- net/wireless/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 127853877a0a..8daed8232b05 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2128,7 +2128,8 @@ static bool cfg80211_6ghz_power_type_valid(const u8 *ie, size_t ielen, struct ieee80211_he_operation *he_oper; tmp = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ie, ielen); - if (tmp && tmp->datalen >= sizeof(*he_oper) + 1) { + if (tmp && tmp->datalen >= sizeof(*he_oper) + 1 && + tmp->datalen >= ieee80211_he_oper_size(tmp->data + 1)) { const struct ieee80211_he_6ghz_oper *he_6ghz_oper; he_oper = (void *)&tmp->data[1]; -- cgit v1.2.3 From e296c95eac655008d5a709b8cf54d0018da1c916 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 22 May 2024 12:41:25 +0200 Subject: wifi: cfg80211: fully move wiphy work to unbound workqueue Previously I had moved the wiphy work to the unbound system workqueue, but missed that when it restarts and during resume it was still using the normal system workqueue. Fix that. Fixes: 91d20ab9d9ca ("wifi: cfg80211: use system_unbound_wq for wiphy work") Reviewed-by: Miriam Rachel Korenblit Link: https://msgid.link/20240522124126.7ca959f2cbd3.I3e2a71ef445d167b84000ccf934ea245aef8d395@changeid Signed-off-by: Johannes Berg --- net/wireless/core.c | 2 +- net/wireless/sysfs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 3fb1b637352a..4b1f45e3070e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -431,7 +431,7 @@ static void cfg80211_wiphy_work(struct work_struct *work) if (wk) { list_del_init(&wk->entry); if (!list_empty(&rdev->wiphy_work_list)) - schedule_work(work); + queue_work(system_unbound_wq, work); spin_unlock_irq(&rdev->wiphy_work_lock); wk->func(&rdev->wiphy, wk); diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 565511a3f461..62f26618f674 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -5,7 +5,7 @@ * * Copyright 2005-2006 Jiri Benc * Copyright 2006 Johannes Berg - * Copyright (C) 2020-2021, 2023 Intel Corporation + * Copyright (C) 2020-2021, 2023-2024 Intel Corporation */ #include @@ -137,7 +137,7 @@ static int wiphy_resume(struct device *dev) if (rdev->wiphy.registered && rdev->ops->resume) ret = rdev_resume(rdev); rdev->suspended = false; - schedule_work(&rdev->wiphy_work); + queue_work(system_unbound_wq, &rdev->wiphy_work); wiphy_unlock(&rdev->wiphy); if (ret) -- cgit v1.2.3 From 642f89daa34567d02f312d03e41523a894906dae Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Tue, 21 May 2024 21:47:26 +0200 Subject: wifi: cfg80211: Lock wiphy in cfg80211_get_station Wiphy should be locked before calling rdev_get_station() (see lockdep assert in ieee80211_get_station()). This fixes the following kernel NULL dereference: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000050 Mem abort info: ESR = 0x0000000096000006 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x06: level 2 translation fault Data abort info: ISV = 0, ISS = 0x00000006 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=0000000003001000 [0000000000000050] pgd=0800000002dca003, p4d=0800000002dca003, pud=08000000028e9003, pmd=0000000000000000 Internal error: Oops: 0000000096000006 [#1] SMP Modules linked in: netconsole dwc3_meson_g12a dwc3_of_simple dwc3 ip_gre gre ath10k_pci ath10k_core ath9k ath9k_common ath9k_hw ath CPU: 0 PID: 1091 Comm: kworker/u8:0 Not tainted 6.4.0-02144-g565f9a3a7911-dirty #705 Hardware name: RPT (r1) (DT) Workqueue: bat_events batadv_v_elp_throughput_metric_update pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : ath10k_sta_statistics+0x10/0x2dc [ath10k_core] lr : sta_set_sinfo+0xcc/0xbd4 sp : ffff000007b43ad0 x29: ffff000007b43ad0 x28: ffff0000071fa900 x27: ffff00000294ca98 x26: ffff000006830880 x25: ffff000006830880 x24: ffff00000294c000 x23: 0000000000000001 x22: ffff000007b43c90 x21: ffff800008898acc x20: ffff00000294c6e8 x19: ffff000007b43c90 x18: 0000000000000000 x17: 445946354d552d78 x16: 62661f7200000000 x15: 57464f445946354d x14: 0000000000000000 x13: 00000000000000e3 x12: d5f0acbcebea978e x11: 00000000000000e3 x10: 000000010048fe41 x9 : 0000000000000000 x8 : ffff000007b43d90 x7 : 000000007a1e2125 x6 : 0000000000000000 x5 : ffff0000024e0900 x4 : ffff800000a0250c x3 : ffff000007b43c90 x2 : ffff00000294ca98 x1 : ffff000006831920 x0 : 0000000000000000 Call trace: ath10k_sta_statistics+0x10/0x2dc [ath10k_core] sta_set_sinfo+0xcc/0xbd4 ieee80211_get_station+0x2c/0x44 cfg80211_get_station+0x80/0x154 batadv_v_elp_get_throughput+0x138/0x1fc batadv_v_elp_throughput_metric_update+0x1c/0xa4 process_one_work+0x1ec/0x414 worker_thread+0x70/0x46c kthread+0xdc/0xe0 ret_from_fork+0x10/0x20 Code: a9bb7bfd 910003fd a90153f3 f9411c40 (f9402814) This happens because STA has time to disconnect and reconnect before batadv_v_elp_throughput_metric_update() delayed work gets scheduled. In this situation, ath10k_sta_state() can be in the middle of resetting arsta data when the work queue get chance to be scheduled and ends up accessing it. Locking wiphy prevents that. Fixes: 7406353d43c8 ("cfg80211: implement cfg80211_get_station cfg80211 API") Signed-off-by: Remi Pommarel Reviewed-by: Nicolas Escande Acked-by: Antonio Quartulli Link: https://msgid.link/983b24a6a176e0800c01aedcd74480d9b551cb13.1716046653.git.repk@triplefau.lt Signed-off-by: Johannes Berg --- net/wireless/util.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 2bde8a354631..082c6f9c5416 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2549,6 +2549,7 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; + int ret; wdev = dev->ieee80211_ptr; if (!wdev) @@ -2560,7 +2561,11 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, memset(sinfo, 0, sizeof(*sinfo)); - return rdev_get_station(rdev, dev, mac_addr, sinfo); + wiphy_lock(&rdev->wiphy); + ret = rdev_get_station(rdev, dev, mac_addr, sinfo); + wiphy_unlock(&rdev->wiphy); + + return ret; } EXPORT_SYMBOL(cfg80211_get_station); -- cgit v1.2.3 From ab904521f4de52fef4f179d2dfc1877645ef5f5c Mon Sep 17 00:00:00 2001 From: Lin Ma Date: Tue, 21 May 2024 15:50:59 +0800 Subject: wifi: cfg80211: pmsr: use correct nla_get_uX functions The commit 9bb7e0f24e7e ("cfg80211: add peer measurement with FTM initiator API") defines four attributes NL80211_PMSR_FTM_REQ_ATTR_ {NUM_BURSTS_EXP}/{BURST_PERIOD}/{BURST_DURATION}/{FTMS_PER_BURST} in following ways. static const struct nla_policy nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { ... [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = NLA_POLICY_MAX(NLA_U8, 15), [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] = NLA_POLICY_MAX(NLA_U8, 15), [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] = NLA_POLICY_MAX(NLA_U8, 31), ... }; That is, those attributes are expected to be NLA_U8 and NLA_U16 types. However, the consumers of these attributes in `pmsr_parse_ftm` blindly all use `nla_get_u32`, which is incorrect and causes functionality issues on little-endian platforms. Hence, fix them with the correct `nla_get_u8` and `nla_get_u16` functions. Fixes: 9bb7e0f24e7e ("cfg80211: add peer measurement with FTM initiator API") Signed-off-by: Lin Ma Link: https://msgid.link/20240521075059.47999-1-linma@zju.edu.cn Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index e106dcea3977..c569c37da317 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -56,7 +56,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.burst_period = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) out->ftm.burst_period = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); + nla_get_u16(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; if (out->ftm.asap && !capa->ftm.asap) { @@ -75,7 +75,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.num_bursts_exp = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) out->ftm.num_bursts_exp = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); if (capa->ftm.max_bursts_exponent >= 0 && out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) { @@ -88,7 +88,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.burst_duration = 15; if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) out->ftm.burst_duration = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); out->ftm.ftms_per_burst = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) @@ -107,7 +107,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.ftmr_retries = 3; if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) out->ftm.ftmr_retries = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; if (out->ftm.request_lci && !capa->ftm.request_lci) { -- cgit v1.2.3 From 02c665f048a439c0d58cc45334c94634bd7c18e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2024 13:34:10 +0200 Subject: wifi: mac80211: apply mcast rate only if interface is up If the interface isn't enabled, don't apply multicast rate changes immediately. Reported-by: syzbot+de87c09cc7b964ea2e23@syzkaller.appspotmail.com Link: https://msgid.link/20240515133410.d6cffe5756cc.I47b624a317e62bdb4609ff7fa79403c0c444d32d@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b08e5d7687e3..99abbb9e8477 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2958,8 +2958,9 @@ static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(int) * NUM_NL80211_BANDS); - ieee80211_link_info_change_notify(sdata, &sdata->deflink, - BSS_CHANGED_MCAST_RATE); + if (ieee80211_sdata_running(sdata)) + ieee80211_link_info_change_notify(sdata, &sdata->deflink, + BSS_CHANGED_MCAST_RATE); return 0; } -- cgit v1.2.3 From 177c6ae9725d783f9e96f02593ce8fb2639be22f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2024 13:53:19 +0200 Subject: wifi: mac80211: handle tasklet frames before stopping The code itself doesn't want to handle frames from the driver if it's already stopped, but if the tasklet was queued before and runs after the stop, then all bets are off. Flush queues before actually stopping, RX should be off at this point since all the interfaces are removed already, etc. Reported-by: syzbot+8830db5d3593b5546d2e@syzkaller.appspotmail.com Link: https://msgid.link/20240515135318.b05f11385c9a.I41c1b33a2e1814c3a7ef352cd7f2951b91785617@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/main.c | 10 ++++++++-- net/mac80211/util.c | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index eb62b7d4b4f7..3cedfdc9099b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1845,6 +1845,8 @@ void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_configure_filter(struct ieee80211_local *local); u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); +void ieee80211_handle_queued_frames(struct ieee80211_local *local); + u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local); int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, u64 *cookie, gfp_t gfp); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 4eaea0a9975b..1132dea0e290 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -423,9 +423,8 @@ u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_ERP_SLOT; } -static void ieee80211_tasklet_handler(struct tasklet_struct *t) +void ieee80211_handle_queued_frames(struct ieee80211_local *local) { - struct ieee80211_local *local = from_tasklet(local, t, tasklet); struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -450,6 +449,13 @@ static void ieee80211_tasklet_handler(struct tasklet_struct *t) } } +static void ieee80211_tasklet_handler(struct tasklet_struct *t) +{ + struct ieee80211_local *local = from_tasklet(local, t, tasklet); + + ieee80211_handle_queued_frames(local); +} + static void ieee80211_restart_work(struct work_struct *work) { struct ieee80211_local *local = diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0b893e958959..283bfc99417e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1567,6 +1567,8 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, void ieee80211_stop_device(struct ieee80211_local *local) { + ieee80211_handle_queued_frames(local); + ieee80211_led_radio(local, false); ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); -- cgit v1.2.3 From f7a8b10bfd614d7a9a16fbe80d28ead4f063cb00 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2024 11:37:38 +0200 Subject: wifi: cfg80211: fix 6 GHz scan request building The 6 GHz scan request struct allocated by cfg80211_scan_6ghz() is meant to be formed this way: [base struct][channels][ssids][6ghz_params] It is allocated with [channels] as the maximum number of channels supported by the driver in the 6 GHz band, since allocation is before knowing how many there will be. However, the inner pointers are set incorrectly: initially, the 6 GHz scan parameters pointer is set: [base struct][channels] ^ scan_6ghz_params and later the SSID pointer is set to the end of the actually _used_ channels. [base struct][channels] ^ ssids If many APs were to be discovered, and many channels used, and there were many SSIDs, then the SSIDs could overlap the 6 GHz parameters. Additionally, the request->ssids for most of the function points to the original request still (given the struct copy) but is used normally, which is confusing. Clear this up, by actually using the allocated space for 6 GHz parameters _after_ the SSIDs, and set up the SSIDs initially so they are used more clearly. Just like in nl80211.c, set them only if there actually are SSIDs though. Finally, also copy the elements (ie/ie_len) so they're part of the same request, not pointing to the old request. Co-developed-by: Miri Korenblit Signed-off-by: Miri Korenblit Reviewed-by: Ilan Peer Signed-off-by: Johannes Berg Link: https://msgid.link/20240510113738.4190692ef4ee.I0cb19188be17a8abd029805e3373c0a7777c214c@changeid Signed-off-by: Johannes Berg --- net/wireless/rdev-ops.h | 6 +++++- net/wireless/scan.c | 47 +++++++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 43897a5269b6..755af47b88b9 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018, 2021-2023 Intel Corporation + * Copyright (C) 2018, 2021-2024 Intel Corporation */ #ifndef __CFG80211_RDEV_OPS #define __CFG80211_RDEV_OPS @@ -458,6 +458,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev, struct cfg80211_scan_request *request) { int ret; + + if (WARN_ON_ONCE(!request->n_ssids && request->ssids)) + return -EINVAL; + trace_rdev_scan(&rdev->wiphy, request); ret = rdev->ops->scan(&rdev->wiphy, request); trace_rdev_return_int(&rdev->wiphy, ret); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 8daed8232b05..2f2a3163968a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -812,6 +812,7 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) LIST_HEAD(coloc_ap_list); bool need_scan_psc = true; const struct ieee80211_sband_iftype_data *iftd; + size_t size, offs_ssids, offs_6ghz_params, offs_ies; rdev_req->scan_6ghz = true; @@ -877,10 +878,15 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) spin_unlock_bh(&rdev->bss_lock); } - request = kzalloc(struct_size(request, channels, n_channels) + - sizeof(*request->scan_6ghz_params) * count + - sizeof(*request->ssids) * rdev_req->n_ssids, - GFP_KERNEL); + size = struct_size(request, channels, n_channels); + offs_ssids = size; + size += sizeof(*request->ssids) * rdev_req->n_ssids; + offs_6ghz_params = size; + size += sizeof(*request->scan_6ghz_params) * count; + offs_ies = size; + size += rdev_req->ie_len; + + request = kzalloc(size, GFP_KERNEL); if (!request) { cfg80211_free_coloc_ap_list(&coloc_ap_list); return -ENOMEM; @@ -888,8 +894,26 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) *request = *rdev_req; request->n_channels = 0; - request->scan_6ghz_params = - (void *)&request->channels[n_channels]; + request->n_6ghz_params = 0; + if (rdev_req->n_ssids) { + /* + * Add the ssids from the parent scan request to the new + * scan request, so the driver would be able to use them + * in its probe requests to discover hidden APs on PSC + * channels. + */ + request->ssids = (void *)request + offs_ssids; + memcpy(request->ssids, rdev_req->ssids, + sizeof(*request->ssids) * request->n_ssids); + } + request->scan_6ghz_params = (void *)request + offs_6ghz_params; + + if (rdev_req->ie_len) { + void *ie = (void *)request + offs_ies; + + memcpy(ie, rdev_req->ie, rdev_req->ie_len); + request->ie = ie; + } /* * PSC channels should not be scanned in case of direct scan with 1 SSID @@ -978,17 +1002,8 @@ skip: if (request->n_channels) { struct cfg80211_scan_request *old = rdev->int_scan_req; - rdev->int_scan_req = request; - /* - * Add the ssids from the parent scan request to the new scan - * request, so the driver would be able to use them in its - * probe requests to discover hidden APs on PSC channels. - */ - request->ssids = (void *)&request->channels[request->n_channels]; - request->n_ssids = rdev_req->n_ssids; - memcpy(request->ssids, rdev_req->ssids, sizeof(*request->ssids) * - request->n_ssids); + rdev->int_scan_req = request; /* * If this scan follows a previous scan, save the scan start -- cgit v1.2.3 From 8ecc4d7a7cd3e9704b63b8e4f6cd8b6b7314210f Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Thu, 9 May 2024 08:55:55 +0530 Subject: wifi: mac80211: pass proper link id for channel switch started notification Original changes[1] posted is having proper changes. However, at the same time, there was chandef puncturing changes which had a conflict with this. While applying, two errors crept in - a) Whitespace error. b) Link ID being passed to channel switch started notifier function is 0. However proper link ID is present in the function. Fix these now. [1] https://lore.kernel.org/all/20240130140918.1172387-5-quic_adisi@quicinc.com/ Fixes: 1a96bb4e8a79 ("wifi: mac80211: start and finalize channel switch on link basis") Signed-off-by: Aditya Kumar Singh Link: https://msgid.link/20240509032555.263933-1-quic_adisi@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 99abbb9e8477..83ad6c9709fe 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4017,7 +4017,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } - link_data->csa_chanreq = chanreq; + link_data->csa_chanreq = chanreq; link_conf->csa_active = true; if (params->block_tx && @@ -4028,7 +4028,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, } cfg80211_ch_switch_started_notify(sdata->dev, - &link_data->csa_chanreq.oper, 0, + &link_data->csa_chanreq.oper, link_id, params->count, params->block_tx); if (changed) { -- cgit v1.2.3 From 92158790ce4391ce4c35d8dfbce759195e4724cb Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 12 May 2024 15:25:00 +0300 Subject: wifi: iwlwifi: mvm: don't initialize csa_work twice The initialization of this worker moved to iwl_mvm_mac_init_mvmvif but we removed only from the pre-MLD version of the add_interface callback. Remove it also from the MLD version. Fixes: 0bcc2155983e ("wifi: iwlwifi: mvm: init vif works only once") Signed-off-by: Miri Korenblit Reviewed-by: Johannes Berg Link: https://msgid.link/20240512152312.4f15b41604f0.Iec912158e5a706175531d3736d77d25adf02fba4@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 0a3b7284eedd..fcfd2dd7568e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -75,8 +75,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, goto out_free_bf; iwl_mvm_tcm_add_vif(mvm, vif); - INIT_DELAYED_WORK(&mvmvif->csa_work, - iwl_mvm_channel_switch_disconnect_wk); if (vif->type == NL80211_IFTYPE_MONITOR) { mvm->monitor_on = true; -- cgit v1.2.3 From 98b7017ddb914a32152ed9aec7468386fff85f8f Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Sun, 12 May 2024 15:25:04 +0300 Subject: wifi: iwlwifi: mvm: always set the TWT IE offset In beacon template version 14, make sure to always set the TWT IE offset before sending the beacon template command, also in the debugfs inject_beacon_ie path. If the TWT IE does not exist, the offset will be set to zero. Fixes: bf0212fd8faa ("wifi: iwlwifi: mvm: add beacon template version 14") Signed-off-by: Shaul Triebitz Signed-off-by: Miri Korenblit Link: https://msgid.link/20240512152312.eb27175c345a.If30ef24aba10fe47fd42a7a9703eb8903035e294@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 9 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 79f4ac8cbc72..8101ecbb478b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1617,6 +1617,15 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len) &beacon_cmd.tim_size, beacon->data, beacon->len); + if (iwl_fw_lookup_cmd_ver(mvm->fw, + BEACON_TEMPLATE_CMD, 0) >= 14) { + u32 offset = iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len); + + beacon_cmd.btwt_offset = cpu_to_le32(offset); + } + iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, sizeof(beacon_cmd)); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 5a06f887769a..5144fa0f96b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -873,7 +873,7 @@ void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, } } -static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) +u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) { struct ieee80211_mgmt *mgmt = (void *)beacon; const u8 *ie; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1f58c727fa63..0a1959bd4079 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1758,6 +1758,7 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type, u32 *gp2, u64 *boottime, ktime_t *realtime); u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); +u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size); /* Tx / Host Commands */ int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, -- cgit v1.2.3 From 788e4c75f831d06fcfbbec1d455fac429521e607 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 12 May 2024 07:31:06 +0300 Subject: wifi: iwlwifi: mvm: fix a crash on 7265 Since IWL_FW_CMD_VER_UNKNOWN = 99, then my change to consider cmd_ver >= 7 instead of cmd_ver = 7 included also firmwares that don't advertise the command version at all. This made us send a command with a bad size and because of that, the firmware hit a BAD_COMMAND immediately after handling the REDUCE_TX_POWER_CMD command. Fixes: 8f892e225f41 ("wifi: iwlwifi: mvm: support iwl_dev_tx_power_cmd_v8") Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240512072733.eb20ff5050d3.Ie4fc6f5496cd296fd6ff20d15e98676f28a3cccd@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e7f5978ef2d7..ca7fcae6995e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -895,8 +895,8 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) int ret; u16 len = 0; u32 n_subbands; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3); + if (cmd_ver >= 7) { len = sizeof(cmd.v7); n_subbands = IWL_NUM_SUB_BANDS_V2; -- cgit v1.2.3 From b7ffca99313d856f7d1cc89038d9061b128e8e97 Mon Sep 17 00:00:00 2001 From: Yedidya Benshimol Date: Fri, 10 May 2024 17:06:29 +0300 Subject: wifi: iwlwifi: mvm: d3: fix WoWLAN command version lookup After moving from commands to notificaitons in the d3 resume flow, removing the WOWLAN_GET_STATUSES and REPLY_OFFLOADS_QUERY_CMD causes the return of the default value when looking up their version. Returning zero here results in the driver sending the not supported NON_QOS_TX_COUNTER_CMD. Signed-off-by: Yedidya Benshimol Reviewed-by: Gregory Greenman Signed-off-by: Miri Korenblit Link: https://msgid.link/20240510170500.8cabfd580614.If3a0db9851f56041f8f5360959354abd5379224a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 71e6b06481a9..e37ae34acba6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2341,7 +2341,8 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, out: if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, - WOWLAN_GET_STATUSES, 0) < 10) { + WOWLAN_GET_STATUSES, + IWL_FW_CMD_VER_UNKNOWN) < 10) { mvmvif->seqno_valid = true; /* +0x10 because the set API expects next-to-use, not last-used */ mvmvif->seqno = status->non_qos_seq_ctr + 0x10; -- cgit v1.2.3 From 4a7aace2899711592327463c1a29ffee44fcc66e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2024 17:06:33 +0300 Subject: wifi: iwlwifi: mvm: revert gen2 TX A-MPDU size to 64 We don't actually support >64 even for HE devices, so revert back to 64. This fixes an issue where the session is refused because the queue is configured differently from the actual session later. Fixes: 514c30696fbc ("iwlwifi: add support for IEEE802.11ax") Signed-off-by: Johannes Berg Reviewed-by: Liad Kaufman Reviewed-by: Luciano Coelho Signed-off-by: Miri Korenblit Link: https://msgid.link/20240510170500.52f7b4cf83aa.If47e43adddf7fe250ed7f5571fbb35d8221c7c47@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index 376b23b409dc..6cd4ec4d8f34 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -122,13 +122,8 @@ enum { #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -/* - * FIXME - various places in firmware API still use u8, - * e.g. LQ command and SCD config command. - * This should be 256 instead. - */ -#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF (255) -#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_MAX (255) +#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF (64) +#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_MAX (64) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) #define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ -- cgit v1.2.3 From 0f2e9f6f21d1ff292363cdfb5bc4d492eeaff76e Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Fri, 10 May 2024 17:06:35 +0300 Subject: wifi: iwlwifi: mvm: set properly mac header In the driver we only use skb_put* for adding data to the skb, hence data never moves and skb_reset_mac_haeder would set mac_header to the first time data was added and not to mac80211 header, fix this my using the actual len of bytes added for setting the mac header. Fixes: 3f7a9d577d47 ("wifi: iwlwifi: mvm: simplify by using SKB MAC header pointer") Signed-off-by: Mordechay Goodstein Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240510170500.12f2de2909c3.I72a819b96f2fe55bde192a8fd31a4b96c301aa73@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d78af2928152..489cfb0a4ab1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2450,8 +2450,11 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, * * We mark it as mac header, for upper layers to know where * all radio tap header ends. + * + * Since data doesn't move data while putting data on skb and that is + * the only way we use, data + len is the next place that hdr would be put */ - skb_reset_mac_header(skb); + skb_set_mac_header(skb, skb->len); /* * Override the nss from the rx_vec since the rate_n_flags has -- cgit v1.2.3 From 87821b67dea87addbc4ab093ba752753b002176a Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Fri, 10 May 2024 17:06:39 +0300 Subject: wifi: iwlwifi: dbg_ini: move iwl_dbg_tlv_free outside of debugfs ifdef The driver should call iwl_dbg_tlv_free even if debugfs is not defined since ini mode does not depend on debugfs ifdef. Fixes: 68f6f492c4fa ("iwlwifi: trans: support loading ini TLVs from external file") Signed-off-by: Shahar S Matityahu Reviewed-by: Luciano Coelho Signed-off-by: Miri Korenblit Link: https://msgid.link/20240510170500.c8e3723f55b0.I5e805732b0be31ee6b83c642ec652a34e974ff10@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 33654f228ee8..d156a9c64194 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1815,8 +1815,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) err_fw: #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(drv->dbgfs_drv); - iwl_dbg_tlv_free(drv->trans); #endif + iwl_dbg_tlv_free(drv->trans); kfree(drv); err: return ERR_PTR(ret); -- cgit v1.2.3 From cc3ba78f202de9752aceb16342ab62bdfbffac7e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 13 May 2024 13:27:08 +0300 Subject: wifi: iwlwifi: mvm: remove stale STA link data during restart If pre-recovery mac80211 tried to disable a link but this disablement failed, then there might be a mismatch between mac80211 assuming the link has been disabled and the driver still having the data around. During recover itself, that is not a problem, but should the link be activated again at a later point, iwlwifi will refuse the activation as it detects the inconsistent state. Solve this corner-case by iterating the station in the restart cleanup handler. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.d2fd60338055.I840d4fdce5fd49fe69896d928b071067e3730259@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 37 +++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 10 +++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 5 +++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 486a6b8f3c97..628b50ee0244 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1128,6 +1128,39 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); } +static void iwl_mvm_cleanup_sta_iterator(void *data, struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = data; + struct iwl_mvm_sta *mvm_sta; + struct ieee80211_vif *vif; + int link_id; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + vif = mvm_sta->vif; + + if (!sta->valid_links) + return; + + for (link_id = 0; link_id < ARRAY_SIZE((sta)->link); link_id++) { + struct iwl_mvm_link_sta *mvm_link_sta; + + mvm_link_sta = + rcu_dereference_check(mvm_sta->link[link_id], + lockdep_is_held(&mvm->mutex)); + if (mvm_link_sta && !(vif->active_links & BIT(link_id))) { + /* + * We have a link STA but the link is inactive in + * mac80211. This will happen if we failed to + * deactivate the link but mac80211 roll back the + * deactivation of the link. + * Delete the stale data to avoid issues later on. + */ + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, + link_id, false); + } + } +} + static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { iwl_mvm_stop_device(mvm); @@ -1150,6 +1183,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) */ ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); + /* cleanup stations as links may be gone after restart */ + ieee80211_iterate_stations_atomic(mvm->hw, + iwl_mvm_cleanup_sta_iterator, mvm); + mvm->p2p_device_vif = NULL; iwl_mvm_reset_phy_ctxts(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index b7a461dba41e..ae3cde0e64f3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -515,11 +515,11 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return iwl_mvm_mld_send_sta_cmd(mvm, &cmd); } -static void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvm_sta, - struct iwl_mvm_link_sta *mvm_sta_link, - unsigned int link_id, - bool is_in_fw) +void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + struct iwl_mvm_link_sta *mvm_sta_link, + unsigned int link_id, + bool is_in_fw) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id], is_in_fw ? ERR_PTR(-EINVAL) : NULL); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 264f1f9394b6..754a05a8c189 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -662,6 +662,11 @@ int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + struct iwl_mvm_link_sta *mvm_sta_link, + unsigned int link_id, + bool is_in_fw); int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id); int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, -- cgit v1.2.3 From 08b16d1b5997dc378533318e2a9cd73c7a898284 Mon Sep 17 00:00:00 2001 From: Yedidya Benshimol Date: Mon, 13 May 2024 13:27:09 +0300 Subject: wifi: iwlwifi: mvm: Handle BIGTK cipher in kek_kck cmd The BIGTK cipher field was added to the kek_kck_material_cmd but wasn't assigned. Fix that by differentiating between the IGTK/BIGTK keys and assign the ciphers fields accordingly. Signed-off-by: Yedidya Benshimol Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.7fd0b22b7267.Ie9b581652b74bd7806980364d59e1b2e78e682c0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index e37ae34acba6..54f4acbbd05b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -595,6 +595,12 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, void *_data) { struct wowlan_key_gtk_type_iter *data = _data; + __le32 *cipher = NULL; + + if (key->keyidx == 4 || key->keyidx == 5) + cipher = &data->kek_kck_cmd->igtk_cipher; + if (key->keyidx == 6 || key->keyidx == 7) + cipher = &data->kek_kck_cmd->bigtk_cipher; switch (key->cipher) { default: @@ -606,10 +612,13 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, return; case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_GMAC_128: - data->kek_kck_cmd->igtk_cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + if (cipher) + *cipher = cpu_to_le32(STA_KEY_FLG_GCMP); return; case WLAN_CIPHER_SUITE_AES_CMAC: - data->kek_kck_cmd->igtk_cipher = cpu_to_le32(STA_KEY_FLG_CCM); + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + if (cipher) + *cipher = cpu_to_le32(STA_KEY_FLG_CCM); return; case WLAN_CIPHER_SUITE_CCMP: if (!sta) -- cgit v1.2.3 From 4d08c0b3357cba0aeffaf3abc62cae0c154f2816 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 May 2024 13:27:10 +0300 Subject: wifi: iwlwifi: mvm: handle BA session teardown in RF-kill When entering RF-kill, mac80211 tears down BA sessions, but due to RF-kill the commands aren't sent to the device. As a result, there can be frames pending on the reorder buffer or perhaps even received while doing so, leading to warnings. Avoid the warnings by doing the BA session teardown normally even in RF-kill, which also requires queue sync. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.0762cd80fb3d.I43c5877f3b546159b2db4f36d6d956b333c41cf0@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 3 ++- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 12 ++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 628b50ee0244..de9f0b446545 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6385,7 +6385,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, .len[0] = sizeof(cmd), .data[1] = data, .len[1] = size, - .flags = sync ? 0 : CMD_ASYNC, + .flags = CMD_SEND_IN_RFKILL | (sync ? 0 : CMD_ASYNC), }; int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index ae3cde0e64f3..9d139b56e152 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -1014,7 +1014,8 @@ static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm, cmd.modify.tid = cpu_to_le32(data->tid); - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL, + sizeof(cmd), &cmd); data->sta_mask = new_sta_mask; if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 20d4968d692a..cc79fe991c26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2848,7 +2848,12 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, .action = start ? cpu_to_le32(IWL_RX_BAID_ACTION_ADD) : cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE), }; - u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD), + .flags = CMD_SEND_IN_RFKILL, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; int ret; BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); @@ -2860,7 +2865,7 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, cmd.alloc.ssn = cpu_to_le16(ssn); cmd.alloc.win_size = cpu_to_le16(buf_size); baid = -EIO; - } else if (iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1) == 1) { + } else if (iwl_fw_lookup_cmd_ver(mvm->fw, hcmd.id, 1) == 1) { cmd.remove_v1.baid = cpu_to_le32(baid); BUILD_BUG_ON(sizeof(cmd.remove_v1) > sizeof(cmd.remove)); } else { @@ -2869,8 +2874,7 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, cmd.remove.tid = cpu_to_le32(tid); } - ret = iwl_mvm_send_cmd_pdu_status(mvm, cmd_id, sizeof(cmd), - &cmd, &baid); + ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &baid); if (ret) return ret; -- cgit v1.2.3 From 989830d1cf16bd149bf0690d889a9caef95fb5b1 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Mon, 13 May 2024 13:27:11 +0300 Subject: wifi: iwlwifi: mvm: properly set 6 GHz channel direct probe option Ensure that the 6 GHz channel is configured with a valid direct BSSID, avoiding any invalid or multicast BSSID addresses. Signed-off-by: Ayala Beker Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.91a631a0fe60.I2ea2616af9b8a2eaf959b156c69cf65a2f1204d4@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index a7ec172eeade..890d4d79b172 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1730,7 +1730,10 @@ iwl_mvm_umac_scan_fill_6g_chan_list(struct iwl_mvm *mvm, break; } - if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE) { + if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE && + !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid), + "scan: invalid BSSID at index %u, index_b=%u\n", + j, idex_b)) { memcpy(&pp->bssid_array[idex_b++], scan_6ghz_params[j].bssid, ETH_ALEN); } -- cgit v1.2.3 From 60d62757df30b74bf397a2847a6db7385c6ee281 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 13 May 2024 13:27:12 +0300 Subject: wifi: iwlwifi: mvm: check n_ssids before accessing the ssids In some versions of cfg80211, the ssids poinet might be a valid one even though n_ssids is 0. Accessing the pointer in this case will cuase an out-of-bound access. Fix this by checking n_ssids first. Fixes: c1a7515393e4 ("iwlwifi: mvm: add adaptive dwell support") Signed-off-by: Miri Korenblit Reviewed-by: Ilan Peer Reviewed-by: Johannes Berg Link: https://msgid.link/20240513132416.6e4d1762bf0d.I5a0e6cc8f02050a766db704d15594c61fe583d45@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 890d4d79b172..9739c38ef824 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1313,7 +1313,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, if (IWL_MVM_ADWELL_MAX_BUDGET) cmd->v7.adwell_max_budget = cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); - else if (params->ssids && params->ssids[0].ssid_len) + else if (params->n_ssids && params->ssids[0].ssid_len) cmd->v7.adwell_max_budget = cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); else @@ -1418,7 +1418,7 @@ iwl_mvm_scan_umac_dwell_v11(struct iwl_mvm *mvm, if (IWL_MVM_ADWELL_MAX_BUDGET) general_params->adwell_max_budget = cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); - else if (params->ssids && params->ssids[0].ssid_len) + else if (params->n_ssids && params->ssids[0].ssid_len) general_params->adwell_max_budget = cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); else -- cgit v1.2.3 From e6dd2936ce7ce94a1915b799f8af8193ec628e87 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 13 May 2024 13:27:13 +0300 Subject: wifi: iwlwifi: mvm: Fix scan abort handling with HW rfkill When HW rfkill is toggled to disable the RF, the flow to stop scan is called. When trying to send the command to abort the scan, since HW rfkill is toggled, the command is not sent due to rfkill being asserted, and -ERFKILL is returned from iwl_trans_send_cmd(), but this is silently ignored in iwl_mvm_send_cmd() and thus the scan abort flow continues to wait for scan complete notification and fails. Since it fails, the UID to type mapping is not cleared, and thus a warning is later fired when trying to stop the interface. To fix this, modify the UMAC scan abort flow to force sending the scan abort command even when in rfkill, so stop the FW from accessing the radio etc. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.8cbe2f8c1a97.Iffe235c12a919dafec88eef399eb1f7bae2c5bdb@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 9739c38ef824..b5f664ae5a17 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -3322,10 +3322,11 @@ static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_ABORT_UMAC), - 0, sizeof(cmd), &cmd); + CMD_SEND_IN_RFKILL, sizeof(cmd), &cmd); if (!ret) mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; + IWL_DEBUG_SCAN(mvm, "Scan abort: ret=%d\n", ret); return ret; } -- cgit v1.2.3 From 4bb95f4535489ed830cf9b34b0a891e384d1aee4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 13 May 2024 13:27:14 +0300 Subject: wifi: iwlwifi: mvm: don't read past the mfuart notifcation In case the firmware sends a notification that claims it has more data than it has, we will read past that was allocated for the notification. Remove the print of the buffer, we won't see it by default. If needed, we can see the content with tracing. This was reported by KFENCE. Fixes: bdccdb854f2f ("iwlwifi: mvm: support MFUART dump in case of MFUART assert") Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://msgid.link/20240513132416.ba82a01a559e.Ia91dd20f5e1ca1ad380b95e68aebf2794f553d9b@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ca7fcae6995e..f4937a100cbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -94,20 +94,10 @@ void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mfu_assert_dump_notif *mfu_dump_notif = (void *)pkt->data; - __le32 *dump_data = mfu_dump_notif->data; - int n_words = le32_to_cpu(mfu_dump_notif->data_size) / sizeof(__le32); - int i; if (mfu_dump_notif->index_num == 0) IWL_INFO(mvm, "MFUART assert id 0x%x occurred\n", le32_to_cpu(mfu_dump_notif->assert_id)); - - for (i = 0; i < n_words; i++) - IWL_DEBUG_INFO(mvm, - "MFUART assert dump, dword %u: 0x%08x\n", - le16_to_cpu(mfu_dump_notif->index_num) * - n_words + i, - le32_to_cpu(dump_data[i])); } static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, -- cgit v1.2.3 From 0c2fd18f7ec552796179c14f13a0e06942f09d16 Mon Sep 17 00:00:00 2001 From: Lingbo Kong Date: Thu, 16 May 2024 10:18:53 +0800 Subject: wifi: mac80211: fix Spatial Reuse element size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the way to check the size of Spatial Reuse IE data in the ieee80211_parse_extension_element() is incorrect. This is because the len variable in the ieee80211_parse_extension_element() function is equal to the size of Spatial Reuse IE data minus one and the value of returned by the ieee80211_he_spr_size() function is equal to the length of Spatial Reuse IE data. So the result of the len >= ieee80211_he_spr_size(data) statement always false. To address this issue and make it consistent with the logic used elsewhere with ieee80211_he_oper_size(), change the "len >= ieee80211_he_spr_size(data)" to “len >= ieee80211_he_spr_size(data) - 1”. Fixes: 9d0480a7c05b ("wifi: mac80211: move element parsing to a new file") Signed-off-by: Lingbo Kong Link: https://msgid.link/20240516021854.5682-2-quic_lingbok@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 55e5497f8978..055a60e90979 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -111,7 +111,7 @@ ieee80211_parse_extension_element(u32 *crc, if (params->mode < IEEE80211_CONN_MODE_HE) break; if (len >= sizeof(*elems->he_spr) && - len >= ieee80211_he_spr_size(data)) + len >= ieee80211_he_spr_size(data) - 1) elems->he_spr = data; break; case WLAN_EID_EXT_HE_6GHZ_CAPA: -- cgit v1.2.3 From a26d8dc5227f449a54518a8b40733a54c6600a8b Mon Sep 17 00:00:00 2001 From: Lingbo Kong Date: Thu, 16 May 2024 10:18:54 +0800 Subject: wifi: mac80211: correctly parse Spatial Reuse Parameter Set element Currently, the way of parsing Spatial Reuse Parameter Set element is incorrect and some members of struct ieee80211_he_obss_pd are not assigned. To address this issue, it must be parsed in the order of the elements of Spatial Reuse Parameter Set defined in the IEEE Std 802.11ax specification. The diagram of the Spatial Reuse Parameter Set element (IEEE Std 802.11ax -2021-9.4.2.252). ------------------------------------------------------------------------- | | | | |Non-SRG| SRG | SRG | SRG | SRG | |Element|Length| Element | SR |OBSS PD|OBSS PD|OBSS PD| BSS |Partial| | ID | | ID |Control| Max | Min | Max |Color | BSSID | | | |Extension| | Offset| Offset|Offset |Bitmap|Bitmap | ------------------------------------------------------------------------- Fixes: 1ced169cc1c2 ("mac80211: allow setting spatial reuse parameters from bss_conf") Signed-off-by: Lingbo Kong Link: https://msgid.link/20240516021854.5682-3-quic_lingbok@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/he.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/mac80211/he.c b/net/mac80211/he.c index 9f5ffdc9db28..ecbb042dd043 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -230,15 +230,21 @@ ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif, if (!he_spr_ie_elem) return; + + he_obss_pd->sr_ctrl = he_spr_ie_elem->he_sr_control; data = he_spr_ie_elem->optional; if (he_spr_ie_elem->he_sr_control & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) - data++; + he_obss_pd->non_srg_max_offset = *data++; + if (he_spr_ie_elem->he_sr_control & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) { - he_obss_pd->max_offset = *data++; he_obss_pd->min_offset = *data++; + he_obss_pd->max_offset = *data++; + memcpy(he_obss_pd->bss_color_bitmap, data, 8); + data += 8; + memcpy(he_obss_pd->partial_bssid_bitmap, data, 8); he_obss_pd->enable = true; } } -- cgit v1.2.3 From 92ecbb3ac6f3fe8ae9edf3226c76aa17b6800699 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 17 May 2024 18:33:32 +0300 Subject: wifi: mac80211: fix UBSAN noise in ieee80211_prep_hw_scan() When testing the previous patch with CONFIG_UBSAN_BOUNDS, I've noticed the following: UBSAN: array-index-out-of-bounds in net/mac80211/scan.c:372:4 index 0 is out of range for type 'struct ieee80211_channel *[]' CPU: 0 PID: 1435 Comm: wpa_supplicant Not tainted 6.9.0+ #1 Hardware name: LENOVO 20UN005QRT/20UN005QRT <...BIOS details...> Call Trace: dump_stack_lvl+0x2d/0x90 __ubsan_handle_out_of_bounds+0xe7/0x140 ? timerqueue_add+0x98/0xb0 ieee80211_prep_hw_scan+0x2db/0x480 [mac80211] ? __kmalloc+0xe1/0x470 __ieee80211_start_scan+0x541/0x760 [mac80211] rdev_scan+0x1f/0xe0 [cfg80211] nl80211_trigger_scan+0x9b6/0xae0 [cfg80211] ... Since '__ieee80211_start_scan()' leaves 'hw_scan_req->req.n_channels' uninitialized, actual boundaries of 'hw_scan_req->req.channels' can't be checked in 'ieee80211_prep_hw_scan()'. Although an initialization of 'hw_scan_req->req.n_channels' introduces some confusion around allocated vs. used VLA members, this shouldn't be a problem since everything is correctly adjusted soon in 'ieee80211_prep_hw_scan()'. Cleanup 'kmalloc()' math in '__ieee80211_start_scan()' by using the convenient 'struct_size()' as well. Signed-off-by: Dmitry Antipov Link: https://msgid.link/20240517153332.18271-2-dmantipov@yandex.ru [improve (imho) indentation a bit] Signed-off-by: Johannes Berg --- net/mac80211/scan.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 3da1c5c45035..8ecc4b710b0e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -744,15 +744,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->hw_scan_ies_bufsize *= n_bands; } - local->hw_scan_req = kmalloc( - sizeof(*local->hw_scan_req) + - req->n_channels * sizeof(req->channels[0]) + - local->hw_scan_ies_bufsize, GFP_KERNEL); + local->hw_scan_req = kmalloc(struct_size(local->hw_scan_req, + req.channels, + req->n_channels) + + local->hw_scan_ies_bufsize, + GFP_KERNEL); if (!local->hw_scan_req) return -ENOMEM; local->hw_scan_req->req.ssids = req->ssids; local->hw_scan_req->req.n_ssids = req->n_ssids; + /* None of the channels are actually set + * up but let UBSAN know the boundaries. + */ + local->hw_scan_req->req.n_channels = req->n_channels; + ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); -- cgit v1.2.3 From 84081a885394fc94055c24c727c99c321df6abac Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 3 May 2024 10:44:02 -0500 Subject: dt-bindings: arm: sunxi: Fix incorrect '-' usage Commit 6bc6bf8a940a ("dt-bindings: arm: sunxi: document Anbernic RG35XX handheld gaming device variants") mistakenly added '-' on each line which created empty (i.e. description only) schemas matching anything. This causes validation to fail on all the root node compatibles as there are multiple oneOf clauses passing. Fixes: 6bc6bf8a940a ("dt-bindings: arm: sunxi: document Anbernic RG35XX handheld gaming device variants") Reviewed-by: Ryan Walklin Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240503154402.967632-1-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- Documentation/devicetree/bindings/arm/sunxi.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml index c6d0d8d81ed4..c2a158b75e49 100644 --- a/Documentation/devicetree/bindings/arm/sunxi.yaml +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml @@ -57,17 +57,17 @@ properties: - const: allwinner,sun8i-v3s - description: Anbernic RG35XX (2024) - - items: + items: - const: anbernic,rg35xx-2024 - const: allwinner,sun50i-h700 - description: Anbernic RG35XX Plus - - items: + items: - const: anbernic,rg35xx-plus - const: allwinner,sun50i-h700 - description: Anbernic RG35XX H - - items: + items: - const: anbernic,rg35xx-h - const: allwinner,sun50i-h700 -- cgit v1.2.3 From 321e4fa68ce15660ec578bdec5cc9607635087cf Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Thu, 23 May 2024 10:42:07 -0500 Subject: dt-bindings: arm: stm32: st,mlahb: Drop spurious "reg" property from example "reg" is not documented nor used for st,mlahb, so drop it from the example to fix the warning: Documentation/devicetree/bindings/arm/stm32/st,mlahb.example.dtb: ahb@38000000: Unevaluated properties are not allowed ('reg' was unexpected) from schema $id: http://devicetree.org/schemas/arm/stm32/st,mlahb.yaml# Since "reg" is dropped, the unit-address must be as well. Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20240523154208.2457864-1-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml index d2dce238ff5d..3e996346b264 100644 --- a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml @@ -54,11 +54,10 @@ unevaluatedProperties: false examples: - | - mlahb: ahb@38000000 { + ahb { compatible = "st,mlahb", "simple-bus"; #address-cells = <1>; #size-cells = <1>; - reg = <0x10000000 0x40000>; ranges; dma-ranges = <0x00000000 0x38000000 0x10000>, <0x10000000 0x10000000 0x60000>, -- cgit v1.2.3 From 2032e61e24fe9fe55d6c7a34fb5506c911b3e280 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 16 May 2024 16:27:33 +0100 Subject: kselftest/alsa: Ensure _GNU_SOURCE is defined The pcmtest driver tests use the kselftest harness which requires that _GNU_SOURCE is defined but nothing causes it to be defined. Since the KHDR_INCLUDES Makefile variable has had the required define added let's use that, this should provide some futureproofing. Fixes: daef47b89efd ("selftests: Compile kselftest headers with -D_GNU_SOURCE") Signed-off-by: Mark Brown Reviewed-by: Muhammad Usama Anjum Signed-off-by: Shuah Khan --- tools/testing/selftests/alsa/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile index 5af9ba8a4645..c1ce39874e2b 100644 --- a/tools/testing/selftests/alsa/Makefile +++ b/tools/testing/selftests/alsa/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # -CFLAGS += $(shell pkg-config --cflags alsa) +CFLAGS += $(shell pkg-config --cflags alsa) $(KHDR_INCLUDES) LDLIBS += $(shell pkg-config --libs alsa) ifeq ($(LDLIBS),) LDLIBS += -lasound -- cgit v1.2.3 From f6c3c83db1d939ebdb8c8922748ae647d8126d91 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 21 May 2024 09:00:22 +0900 Subject: selftests/ftrace: Fix to check required event file The dynevent/test_duplicates.tc test case uses `syscalls/sys_enter_openat` event for defining eprobe on it. Since this `syscalls` events depend on CONFIG_FTRACE_SYSCALLS=y, if it is not set, the test will fail. Add the event file to `required` line so that the test will return `unsupported` result. Fixes: 297e1dcdca3d ("selftests/ftrace: Add selftest for testing duplicate eprobes and kprobes") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc index d3a79da215c8..5f72abe6fa79 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: Generic dynamic event - check if duplicate events are caught -# requires: dynamic_events "e[:[/][]] . []":README +# requires: dynamic_events "e[:[/][]] . []":README events/syscalls/sys_enter_openat echo 0 > events/enable -- cgit v1.2.3 From 7ea794604bf6a3be8bb4b0f1483eb1d3972eac93 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 21 May 2024 09:00:32 +0900 Subject: selftests/ftrace: Update required config Update required config options for running all tests. This also sorts the config entries alphabetically. Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/config | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index e59d985eeff0..048a312abf40 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -1,16 +1,28 @@ -CONFIG_KPROBES=y +CONFIG_BPF_SYSCALL=y +CONFIG_DEBUG_INFO_BTF=y +CONFIG_DEBUG_INFO_DWARF4=y +CONFIG_EPROBE_EVENTS=y +CONFIG_FPROBE=y +CONFIG_FPROBE_EVENTS=y CONFIG_FTRACE=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_FUNCTION_GRAPH_RETVAL=y CONFIG_FUNCTION_PROFILER=y -CONFIG_TRACER_SNAPSHOT=y -CONFIG_STACK_TRACER=y CONFIG_HIST_TRIGGERS=y -CONFIG_SCHED_TRACER=y -CONFIG_PREEMPT_TRACER=y CONFIG_IRQSOFF_TRACER=y -CONFIG_PREEMPTIRQ_DELAY_TEST=m +CONFIG_KALLSYMS_ALL=y +CONFIG_KPROBES=y +CONFIG_KPROBE_EVENTS=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y +CONFIG_PREEMPTIRQ_DELAY_TEST=m +CONFIG_PREEMPT_TRACER=y +CONFIG_PROBE_EVENTS_BTF_ARGS=y CONFIG_SAMPLES=y CONFIG_SAMPLE_FTRACE_DIRECT=m CONFIG_SAMPLE_TRACE_PRINTK=m -CONFIG_KALLSYMS_ALL=y +CONFIG_SCHED_TRACER=y +CONFIG_STACK_TRACER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_UPROBES=y +CONFIG_UPROBE_EVENTS=y -- cgit v1.2.3 From 23a4b108accc29a6125ed14de4a044689ffeda78 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Mon, 20 May 2024 20:57:37 -0400 Subject: tracing/selftests: Fix kprobe event name test for .isra. functions The kprobe_eventname.tc test checks if a function with .isra. can have a kprobe attached to it. It loops through the kallsyms file for all the functions that have the .isra. name, and checks if it exists in the available_filter_functions file, and if it does, it uses it to attach a kprobe to it. The issue is that kprobes can not attach to functions that are listed more than once in available_filter_functions. With the latest kernel, the function that is found is: rapl_event_update.isra.0 # grep rapl_event_update.isra.0 /sys/kernel/tracing/available_filter_functions rapl_event_update.isra.0 rapl_event_update.isra.0 It is listed twice. This causes the attached kprobe to it to fail which in turn fails the test. Instead of just picking the function function that is found in available_filter_functions, pick the first one that is listed only once in available_filter_functions. Cc: stable@vger.kernel.org Fixes: 604e3548236d ("selftests/ftrace: Select an existing function in kprobe_eventname test") Signed-off-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc index 1f6981ef7afa..ba19b81cef39 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc @@ -30,7 +30,8 @@ find_dot_func() { fi grep " [tT] .*\.isra\..*" /proc/kallsyms | cut -f 3 -d " " | while read f; do - if grep -s $f available_filter_functions; then + cnt=`grep -s $f available_filter_functions | wc -l`; + if [ $cnt -eq 1 ]; then echo $f break fi -- cgit v1.2.3 From bc4d5f5d2debf8bb65fba188313481549ead8576 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 21 May 2024 13:01:11 +1000 Subject: selftests: cachestat: Fix build warnings on ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix warnings like: test_cachestat.c: In function ‘print_cachestat’: test_cachestat.c:30:38: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘__u64’ {aka ‘long unsigned int’} [-Wformat=] By switching to unsigned long long for u64 for ppc64 builds. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/cachestat/test_cachestat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/cachestat/test_cachestat.c b/tools/testing/selftests/cachestat/test_cachestat.c index b171fd53b004..632ab44737ec 100644 --- a/tools/testing/selftests/cachestat/test_cachestat.c +++ b/tools/testing/selftests/cachestat/test_cachestat.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include #include -- cgit v1.2.3 From 84b6df4c49a1cc2854a16937acd5fd3e6315d083 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 21 May 2024 13:03:25 +1000 Subject: selftests/openat2: Fix build warnings on ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix warnings like: openat2_test.c: In function ‘test_openat2_flags’: openat2_test.c:303:73: warning: format ‘%llX’ expects argument of type ‘long long unsigned int’, but argument 5 has type ‘__u64’ {aka ‘long unsigned int’} [-Wformat=] By switching to unsigned long long for u64 for ppc64 builds. Signed-off-by: Michael Ellerman Reviewed-by: Muhammad Usama Anjum Signed-off-by: Shuah Khan --- tools/testing/selftests/openat2/openat2_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c index 9024754530b2..5790ab446527 100644 --- a/tools/testing/selftests/openat2/openat2_test.c +++ b/tools/testing/selftests/openat2/openat2_test.c @@ -5,6 +5,7 @@ */ #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include #include #include -- cgit v1.2.3 From e8b8c5264d4ebd248f60a5cef077fe615806e7a0 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 21 May 2024 12:26:16 +1000 Subject: selftests/overlayfs: Fix build error on ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix build error on ppc64: dev_in_maps.c: In function ‘get_file_dev_and_inode’: dev_in_maps.c:60:59: error: format ‘%llu’ expects argument of type ‘long long unsigned int *’, but argument 7 has type ‘__u64 *’ {aka ‘long unsigned int *’} [-Werror=format=] By switching to unsigned long long for u64 for ppc64 builds. Signed-off-by: Michael Ellerman Signed-off-by: Shuah Khan --- tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c index 759f86e7d263..2862aae58b79 100644 --- a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c +++ b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include #include -- cgit v1.2.3 From 34864c05a54d1bc544c8c3939aababbc481d99e3 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Wed, 29 May 2024 12:14:50 -0700 Subject: ASoC: Intel: avs: es8336: Switch to new Intel CPU model defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New CPU #defines encode vendor and family as well as model. Signed-off-by: Tony Luck Reviewed-by: Amadeusz Sławiński Acked-by: Mark Brown Link: https://msgid.link/r/20240529191450.63414-1-tony.luck@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/boards/es8336.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 3bf37a8fd6e6..c8522e2430f8 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include "../utils.h" #define ES8336_CODEC_DAI "ES8316 HiFi" @@ -153,9 +153,9 @@ static int avs_es8336_hw_params(struct snd_pcm_substream *substream, int clk_freq; int ret; - switch (boot_cpu_data.x86_model) { - case INTEL_FAM6_KABYLAKE_L: - case INTEL_FAM6_KABYLAKE: + switch (boot_cpu_data.x86_vfm) { + case INTEL_KABYLAKE_L: + case INTEL_KABYLAKE: clk_freq = 24000000; break; default: -- cgit v1.2.3 From 2607133196c35f31892ee199ce7ffa717bea4ad1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 27 May 2024 17:14:12 -0700 Subject: clk: sifive: Do not register clkdevs for PRCI clocks These clkdevs were unnecessary, because systems using this driver always look up clocks using the devicetree. And as Russell King points out[1], since the provided device name was truncated, lookups via clkdev would never match. Recently, commit 8d532528ff6a ("clkdev: report over-sized strings when creating clkdev entries") caused clkdev registration to fail due to the truncation, and this now prevents the driver from probing. Fix the driver by removing the clkdev registration. Link: https://lore.kernel.org/linux-clk/ZkfYqj+OcAxd9O2t@shell.armlinux.org.uk/ [1] Fixes: 30b8e27e3b58 ("clk: sifive: add a driver for the SiFive FU540 PRCI IP block") Fixes: 8d532528ff6a ("clkdev: report over-sized strings when creating clkdev entries") Reported-by: Guenter Roeck Closes: https://lore.kernel.org/linux-clk/7eda7621-0dde-4153-89e4-172e4c095d01@roeck-us.net/ Suggested-by: Russell King Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20240528001432.1200403-1-samuel.holland@sifive.com Signed-off-by: Stephen Boyd --- drivers/clk/sifive/sifive-prci.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c index 25b8e1a80ddc..b32a59fe55e7 100644 --- a/drivers/clk/sifive/sifive-prci.c +++ b/drivers/clk/sifive/sifive-prci.c @@ -4,7 +4,6 @@ * Copyright (C) 2020 Zong Li */ -#include #include #include #include @@ -537,13 +536,6 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd, return r; } - r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); - if (r) { - dev_warn(dev, "Failed to register clkdev for %s: %d\n", - init.name, r); - return r; - } - pd->hw_clks.hws[i] = &pic->hw; } -- cgit v1.2.3 From 328af04b1ac279f17c09ddd5958aa6ec47131c80 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 27 May 2024 23:43:38 +0300 Subject: media: mei: csi: Put the IPU device reference The mei csi's probe function obtains a reference to the IPU device but never puts that reference. Do that now. Fixes: 33116eb12c6b ("media: ivsc: csi: Use IPU bridge") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ivsc/mei_csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 89b582a221ab..53151d016188 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -677,6 +677,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, return -ENODEV; ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb); + put_device(&ipu->dev); if (ret < 0) return ret; if (WARN_ON(!dev_fwnode(dev))) -- cgit v1.2.3 From cc864821c7e8b921ebbfb21b17c92f8b3ea3d7ff Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 27 May 2024 23:13:27 +0300 Subject: media: mei: csi: Warn less verbosely of a missing device fwnode The check for having device fwnode was meant to be a sanity check but this also happens if the ACPI DSDT has graph port nodes on sensor device(s) but not on the IVSC device. Use a more meaningful warning message to tell about this. Fixes: 33116eb12c6b ("media: ivsc: csi: Use IPU bridge") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ivsc/mei_csi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 53151d016188..f04a89584334 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -680,8 +680,10 @@ static int mei_csi_probe(struct mei_cl_device *cldev, put_device(&ipu->dev); if (ret < 0) return ret; - if (WARN_ON(!dev_fwnode(dev))) + if (!dev_fwnode(dev)) { + dev_err(dev, "mei-csi probed without device fwnode!\n"); return -ENXIO; + } csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL); if (!csi) -- cgit v1.2.3 From 54880795b494e6557b47fe3cf3f21f2f54c2aa68 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Wed, 29 May 2024 11:44:32 +0800 Subject: media: intel/ipu6: update the maximum supported csi2 port number to 6 IPU6EP on Meteor Lake SoC supports maximum 6 csi2 ports instead of 4. Fixes: 25fedc021985 ("media: intel/ipu6: add Intel IPU6 PCI device driver") Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index f587f609259d..bbd646378ab3 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -285,7 +285,7 @@ EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); #define IPU6_ISYS_CSI2_NPORTS 4 #define IPU6SE_ISYS_CSI2_NPORTS 4 #define IPU6_TGL_ISYS_CSI2_NPORTS 8 -#define IPU6EP_MTL_ISYS_CSI2_NPORTS 4 +#define IPU6EP_MTL_ISYS_CSI2_NPORTS 6 static void ipu6_internal_pdata_init(struct ipu6_device *isp) { -- cgit v1.2.3 From ffb9072bce200a4d004006e8b40c366933cf517b Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Wed, 29 May 2024 14:43:21 +0800 Subject: media: intel/ipu6: add csi2 port sanity check in notifier bound Invalid csi2 port will break the isys notifier bound ops as it is trying to access an invalid csi2 sub-device instance based on the port. It will trigger a mc warning, and it will cause the sensor driver to unbound an inexistent isys csi2 and crash. Adding a csi2 port sanity check, return error to avoid such case. Fixes: f50c4ca0a820 ("media: intel/ipu6: add the main input system driver") Signed-off-by: Bingbu Cao [Sakari Ailus: Fix spelling of "nports" field.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 1998b72ac07d..8b9b77719bb1 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -678,6 +678,12 @@ static int isys_notifier_bound(struct v4l2_async_notifier *notifier, container_of(asc, struct sensor_async_sd, asc); int ret; + if (s_asd->csi2.port >= isys->pdata->ipdata->csi2.nports) { + dev_err(&isys->adev->auxdev.dev, "invalid csi2 port %u\n", + s_asd->csi2.port); + return -EINVAL; + } + ret = ipu_bridge_instantiate_vcm(sd->dev); if (ret) { dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n"); -- cgit v1.2.3 From 41011e2de3480f9adb6420b35b67628b2d903355 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 28 May 2024 11:06:31 +0100 Subject: KVM: arm64: nv: Fix relative priorities of exceptions generated by ERETAx ERETAx can fail in multiple ways: (1) ELR_EL2 points lalaland (2) we get a PAC failure (3) SPSR_EL2 has the wrong mode (1) is easy, as we just let the CPU do its thing and deliver an Instruction Abort. However, (2) and (3) are interesting, because the PAC failure priority is way below that of the Illegal Execution State exception. Which means that if we have detected a PAC failure (and that we have FPACCOMBINE), we must be careful to give priority to the Illegal Execution State exception, should one be pending. Solving this involves hoisting the SPSR calculation earlier and testing for the IL bit before injecting the FPAC exception. In the extreme case of a ERETAx returning to an invalid mode *and* failing its PAC check, we end up with an Instruction Abort (due to the new PC being mangled by the failed Auth) *and* PSTATE.IL being set. Which matches the requirements of the architecture. Whilst we're at it, remove a stale comment that states the obvious and only confuses the reader. Fixes: 213b3d1ea161 ("KVM: arm64: nv: Handle ERETA[AB] instructions") Reviewed-by: Joey Gouly Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/20240528100632.1831995-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/emulate-nested.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 72d733c74a38..54090967a335 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2181,16 +2181,23 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) if (forward_traps(vcpu, HCR_NV)) return; + spsr = vcpu_read_sys_reg(vcpu, SPSR_EL2); + spsr = kvm_check_illegal_exception_return(vcpu, spsr); + /* Check for an ERETAx */ esr = kvm_vcpu_get_esr(vcpu); if (esr_iss_is_eretax(esr) && !kvm_auth_eretax(vcpu, &elr)) { /* - * Oh no, ERETAx failed to authenticate. If we have - * FPACCOMBINE, deliver an exception right away. If we - * don't, then let the mangled ELR value trickle down the + * Oh no, ERETAx failed to authenticate. + * + * If we have FPACCOMBINE and we don't have a pending + * Illegal Execution State exception (which has priority + * over FPAC), deliver an exception right away. + * + * Otherwise, let the mangled ELR value trickle down the * ERET handling, and the guest will have a little surprise. */ - if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE)) { + if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE) && !(spsr & PSR_IL_BIT)) { esr &= ESR_ELx_ERET_ISS_ERETA; esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_FPAC); kvm_inject_nested_sync(vcpu, esr); @@ -2201,17 +2208,11 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) preempt_disable(); kvm_arch_vcpu_put(vcpu); - spsr = __vcpu_sys_reg(vcpu, SPSR_EL2); - spsr = kvm_check_illegal_exception_return(vcpu, spsr); if (!esr_iss_is_eretax(esr)) elr = __vcpu_sys_reg(vcpu, ELR_EL2); trace_kvm_nested_eret(vcpu, elr, spsr); - /* - * Note that the current exception level is always the virtual EL2, - * since we set HCR_EL2.NV bit only when entering the virtual EL2. - */ *vcpu_pc(vcpu) = elr; *vcpu_cpsr(vcpu) = spsr; -- cgit v1.2.3 From 47eb2d68d10208e6a9e89d10b66018e8d6ca0623 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 28 May 2024 11:06:32 +0100 Subject: KVM: arm64: nv: Expose BTI and CSV_frac to a guest hypervisor Now that we expose PAC to NV guests, we can also expose BTI (as the two as joined at the hip, due to some of the PAC instructions being landing pads). While we're at it, also propagate CSV_frac, which requires no particular emulation. Fixes: f4f6a95bac49 ("KVM: arm64: nv: Advertise support for PAuth") Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/20240528100632.1831995-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/nested.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 6813c7c7f00a..bae8536cbf00 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -58,8 +58,10 @@ static u64 limit_nv_id_reg(u32 id, u64 val) break; case SYS_ID_AA64PFR1_EL1: - /* Only support SSBS */ - val &= NV_FTR(PFR1, SSBS); + /* Only support BTI, SSBS, CSV2_frac */ + val &= (NV_FTR(PFR1, BT) | + NV_FTR(PFR1, SSBS) | + NV_FTR(PFR1, CSV2_frac)); break; case SYS_ID_AA64MMFR0_EL1: -- cgit v1.2.3 From cb708ab9f584f159798b60853edcf0c8b67ce295 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Tue, 28 May 2024 19:29:38 -0700 Subject: selftests/futex: pass _GNU_SOURCE without a value to the compiler It's slightly better to set _GNU_SOURCE in the source code, but if one must do it via the compiler invocation, then the best way to do so is this: $(CC) -D_GNU_SOURCE= ...because otherwise, if this form is used: $(CC) -D_GNU_SOURCE ...then that leads the compiler to set a value, as if you had passed in: $(CC) -D_GNU_SOURCE=1 That, in turn, leads to warnings under both gcc and clang, like this: futex_requeue_pi.c:20: warning: "_GNU_SOURCE" redefined Fix this by using the "-D_GNU_SOURCE=" form. Reviewed-by: Edward Liaw Reviewed-by: Davidlohr Bueso Signed-off-by: John Hubbard Signed-off-by: Shuah Khan --- tools/testing/selftests/futex/functional/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile index a392d0917b4e..994fa3468f17 100644 --- a/tools/testing/selftests/futex/functional/Makefile +++ b/tools/testing/selftests/futex/functional/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 INCLUDES := -I../include -I../../ $(KHDR_INCLUDES) -CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE -pthread $(INCLUDES) $(KHDR_INCLUDES) +CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE= -pthread $(INCLUDES) $(KHDR_INCLUDES) LDLIBS := -lpthread -lrt LOCAL_HDRS := \ -- cgit v1.2.3 From d53b681ce9ca7db5ef4ecb8d2cf465ae4a031264 Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Fri, 24 May 2024 10:59:04 +0900 Subject: scsi: ufs: mcq: Fix error output and clean up ufshcd_mcq_abort() An error unrelated to ufshcd_try_to_abort_task is being logged and can cause confusion. Modify ufshcd_mcq_abort() to print the result of the abort failure. For readability, return immediately instead of 'goto'. Fixes: f1304d442077 ("scsi: ufs: mcq: Added ufshcd_mcq_abort()") Signed-off-by: Chanwoo Lee Link: https://lore.kernel.org/r/20240524015904.1116005-1-cw9316.lee@samsung.com Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-mcq.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 005d63ab1f44..8944548c30fa 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -634,20 +634,20 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) struct ufshcd_lrb *lrbp = &hba->lrb[tag]; struct ufs_hw_queue *hwq; unsigned long flags; - int err = FAILED; + int err; if (!ufshcd_cmd_inflight(lrbp->cmd)) { dev_err(hba->dev, "%s: skip abort. cmd at tag %d already completed.\n", __func__, tag); - goto out; + return FAILED; } /* Skip task abort in case previous aborts failed and report failure */ if (lrbp->req_abort_skip) { dev_err(hba->dev, "%s: skip abort. tag %d failed earlier\n", __func__, tag); - goto out; + return FAILED; } hwq = ufshcd_mcq_req_to_hwq(hba, scsi_cmd_to_rq(cmd)); @@ -659,7 +659,7 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) */ dev_err(hba->dev, "%s: cmd found in sq. hwq=%d, tag=%d\n", __func__, hwq->id, tag); - goto out; + return FAILED; } /* @@ -667,18 +667,17 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) * in the completion queue either. Query the device to see if * the command is being processed in the device. */ - if (ufshcd_try_to_abort_task(hba, tag)) { + err = ufshcd_try_to_abort_task(hba, tag); + if (err) { dev_err(hba->dev, "%s: device abort failed %d\n", __func__, err); lrbp->req_abort_skip = true; - goto out; + return FAILED; } - err = SUCCESS; spin_lock_irqsave(&hwq->cq_lock, flags); if (ufshcd_cmd_inflight(lrbp->cmd)) ufshcd_release_scsi_cmd(hba, lrbp); spin_unlock_irqrestore(&hwq->cq_lock, flags); -out: - return err; + return SUCCESS; } -- cgit v1.2.3 From 935df1bd40d43c4ee91838c42a20e9af751885cc Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 29 May 2024 14:59:20 -0500 Subject: of/irq: Factor out parsing of interrupt-map parent phandle+args from of_irq_parse_raw() Factor out the parsing of interrupt-map interrupt parent phandle and its arg cells to a separate function, of_irq_parse_imap_parent(), so that it can be used in other parsing scenarios (e.g. fw_devlink). There was a refcount leak on non-matching entries when iterating thru "interrupt-map" which is fixed. Tested-by: Marc Zyngier Tested-by: Anup Patel Link: https://lore.kernel.org/r/20240529-dt-interrupt-map-fix-v2-1-ef86dc5bcd2a@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/of/irq.c | 125 ++++++++++++++++++++++++++++-------------------- drivers/of/of_private.h | 3 ++ 2 files changed, 77 insertions(+), 51 deletions(-) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 174900072c18..462375b293e4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -25,6 +25,8 @@ #include #include +#include "of_private.h" + /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @dev: Device node of the device whose interrupt is to be mapped @@ -96,6 +98,57 @@ static const char * const of_irq_imap_abusers[] = { NULL, }; +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_phandle_args *out_irq) +{ + u32 intsize, addrsize; + struct device_node *np; + + /* Get the interrupt parent */ + if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) + np = of_node_get(of_irq_dflt_pic); + else + np = of_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + + /* Check if not found */ + if (!np) { + pr_debug(" -> imap parent not found !\n"); + return NULL; + } + + /* Get #interrupt-cells and #address-cells of new parent */ + if (of_property_read_u32(np, "#interrupt-cells", + &intsize)) { + pr_debug(" -> parent lacks #interrupt-cells!\n"); + of_node_put(np); + return NULL; + } + if (of_property_read_u32(np, "#address-cells", + &addrsize)) + addrsize = 0; + + pr_debug(" -> intsize=%d, addrsize=%d\n", + intsize, addrsize); + + /* Check for malformed properties */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS) + || (len < (addrsize + intsize))) { + of_node_put(np); + return NULL; + } + + pr_debug(" -> imaplen=%d\n", len); + + imap += addrsize + intsize; + + out_irq->np = np; + for (int i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(imap - intsize + i); + out_irq->args_count = intsize; + + return imap; +} + /** * of_irq_parse_raw - Low level interrupt tree parsing * @addr: address specifier (start of "reg" property of the device) in be32 format @@ -112,12 +165,12 @@ static const char * const of_irq_imap_abusers[] = { */ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { - struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + struct device_node *ipar, *tnode, *old = NULL; __be32 initial_match_array[MAX_PHANDLE_ARGS]; const __be32 *match_array = initial_match_array; - const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i, rc = -EINVAL; + const __be32 *tmp, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; + u32 intsize = 1, addrsize; + int i, rc = -EINVAL; #ifdef DEBUG of_print_phandle_args("of_irq_parse_raw: ", out_irq); @@ -176,6 +229,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Now start the actual "proper" walk of the interrupt tree */ while (ipar != NULL) { + int imaplen, match; + const __be32 *imap, *oldimap, *imask; + struct device_node *newpar; /* * Now check if cursor is an interrupt-controller and * if it is then we are done, unless there is an @@ -216,7 +272,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Parse interrupt-map */ match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { + while (imaplen > (addrsize + intsize + 1)) { /* Compare specifiers */ match = 1; for (i = 0; i < (addrsize + intsize); i++, imaplen--) @@ -224,48 +280,17 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - else - newpar = of_find_node_by_phandle(be32_to_cpup(imap)); - imap++; - --imaplen; - - /* Check if not found */ - if (newpar == NULL) { - pr_debug(" -> imap parent not found !\n"); - goto fail; - } - - if (!of_device_is_available(newpar)) - match = 0; - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - if (of_property_read_u32(newpar, "#interrupt-cells", - &newintsize)) { - pr_debug(" -> parent lacks #interrupt-cells!\n"); - goto fail; - } - if (of_property_read_u32(newpar, "#address-cells", - &newaddrsize)) - newaddrsize = 0; - - pr_debug(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS) - || (imaplen < (newaddrsize + newintsize))) { - rc = -EFAULT; + oldimap = imap; + imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq); + if (!imap) goto fail; - } - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; + match &= of_device_is_available(out_irq->np); + if (match) + break; + of_node_put(out_irq->np); + imaplen -= imap - oldimap; pr_debug(" -> imaplen=%d\n", imaplen); } if (!match) { @@ -287,11 +312,11 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) * Successfully parsed an interrupt-map translation; copy new * interrupt specifier into the out_irq structure */ - match_array = imap - newaddrsize - newintsize; - for (i = 0; i < newintsize; i++) - out_irq->args[i] = be32_to_cpup(imap - newintsize + i); - out_irq->args_count = intsize = newintsize; - addrsize = newaddrsize; + match_array = oldimap + 1; + + newpar = out_irq->np; + intsize = out_irq->args_count; + addrsize = (imap - match_array) - intsize; if (ipar == newpar) { pr_debug("%pOF interrupt-map entry to self\n", ipar); @@ -300,7 +325,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) skiplevel: /* Iterate again with new parent */ - out_irq->np = newpar; pr_debug(" -> new parent: %pOF\n", newpar); of_node_put(ipar); ipar = newpar; @@ -310,7 +334,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) fail: of_node_put(ipar); - of_node_put(newpar); return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 94fc0aa07af9..04aa2a91f851 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -159,6 +159,9 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, extern int of_bus_n_addr_cells(struct device_node *np); extern int of_bus_n_size_cells(struct device_node *np); +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, + struct of_phandle_args *out_irq); + struct bus_dma_region; #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA) int of_dma_get_range(struct device_node *np, -- cgit v1.2.3 From e7985f43609c782132f8f5794ee6cc4cdb66ca75 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 29 May 2024 14:59:21 -0500 Subject: of: property: Fix fw_devlink handling of interrupt-map Commit d976c6f4b32c ("of: property: Add fw_devlink support for interrupt-map property") tried to do what it says on the tin, but failed on a couple of points: - it confuses bytes and cells. Not a huge deal, except when it comes to pointer arithmetic - it doesn't really handle anything but interrupt-maps that have their parent #address-cells set to 0 The combinations of the two leads to some serious fun on my M1 box, with plenty of WARN-ON() firing all over the shop, and amusing values being generated for interrupt specifiers. Having 2 versions of parsing code for "interrupt-map" was a bad idea. Now that the common parsing parts have been refactored into of_irq_parse_imap_parent(), rework the code here to use it instead and fix the pointer arithmetic. Note that the dependency will be a bit different than the original code when the interrupt-map points to another interrupt-map. In this case, the original code would resolve to the final interrupt controller. Now the dependency is the parent interrupt-map (which itself should have a dependency to the parent). It is possible that a node with an interrupt-map has no driver. Fixes: d976c6f4b32c ("of: property: Add fw_devlink support for interrupt-map property") Signed-off-by: Marc Zyngier Co-developed-by: Rob Herring (Arm) Cc: Saravana Kannan Tested-by: Marc Zyngier Tested-by: Anup Patel Link: https://lore.kernel.org/r/20240529-dt-interrupt-map-fix-v2-2-ef86dc5bcd2a@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/of/property.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index 1c83e68f805b..164d77cb9445 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1306,10 +1306,10 @@ static struct device_node *parse_interrupts(struct device_node *np, static struct device_node *parse_interrupt_map(struct device_node *np, const char *prop_name, int index) { - const __be32 *imap, *imap_end, *addr; + const __be32 *imap, *imap_end; struct of_phandle_args sup_args; u32 addrcells, intcells; - int i, imaplen; + int imaplen; if (!IS_ENABLED(CONFIG_OF_IRQ)) return NULL; @@ -1322,33 +1322,23 @@ static struct device_node *parse_interrupt_map(struct device_node *np, addrcells = of_bus_n_addr_cells(np); imap = of_get_property(np, "interrupt-map", &imaplen); - if (!imap || imaplen <= (addrcells + intcells)) + imaplen /= sizeof(*imap); + if (!imap) return NULL; - imap_end = imap + imaplen; - while (imap < imap_end) { - addr = imap; - imap += addrcells; + imap_end = imap + imaplen; - sup_args.np = np; - sup_args.args_count = intcells; - for (i = 0; i < intcells; i++) - sup_args.args[i] = be32_to_cpu(imap[i]); - imap += intcells; + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; - /* - * Upon success, the function of_irq_parse_raw() returns - * interrupt controller DT node pointer in sup_args.np. - */ - if (of_irq_parse_raw(addr, &sup_args)) + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) return NULL; - if (!index) + if (i == index) return sup_args.np; of_node_put(sup_args.np); - imap += sup_args.args_count + 1; - index--; } return NULL; -- cgit v1.2.3 From 2d707b4e37f9b0c37b8b2392f91b04c5b63ea538 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Mon, 15 Apr 2024 14:49:04 +0800 Subject: RISC-V: KVM: No need to use mask when hart-index-bit is 0 When the maximum hart number within groups is 1, hart-index-bit is set to 0. Consequently, there is no need to restore the hart ID from IMSIC addresses and hart-index-bit settings. Currently, QEMU and kvmtool do not pass correct hart-index-bit values when the maximum hart number is a power of 2, thereby avoiding this issue. Corresponding patches for QEMU and kvmtool will also be dispatched. Fixes: 89d01306e34d ("RISC-V: KVM: Implement device interface for AIA irqchip") Signed-off-by: Yong-Xuan Wang Reviewed-by: Andrew Jones Link: https://lore.kernel.org/r/20240415064905.25184-1-yongxuan.wang@sifive.com Signed-off-by: Anup Patel --- arch/riscv/kvm/aia_device.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c index 0eb689351b7d..5cd407c6a8e4 100644 --- a/arch/riscv/kvm/aia_device.c +++ b/arch/riscv/kvm/aia_device.c @@ -237,10 +237,11 @@ static gpa_t aia_imsic_ppn(struct kvm_aia *aia, gpa_t addr) static u32 aia_imsic_hart_index(struct kvm_aia *aia, gpa_t addr) { - u32 hart, group = 0; + u32 hart = 0, group = 0; - hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & - GENMASK_ULL(aia->nr_hart_bits - 1, 0); + if (aia->nr_hart_bits) + hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & + GENMASK_ULL(aia->nr_hart_bits - 1, 0); if (aia->nr_group_bits) group = (addr >> aia->nr_group_shift) & GENMASK_ULL(aia->nr_group_bits - 1, 0); -- cgit v1.2.3 From c66f3b40b17d3dfc4b6abb5efde8e71c46971821 Mon Sep 17 00:00:00 2001 From: Quan Zhou Date: Thu, 23 May 2024 10:13:34 +0800 Subject: RISC-V: KVM: Fix incorrect reg_subtype labels in kvm_riscv_vcpu_set_reg_isa_ext function In the function kvm_riscv_vcpu_set_reg_isa_ext, the original code used incorrect reg_subtype labels KVM_REG_RISCV_SBI_MULTI_EN/DIS. These have been corrected to KVM_REG_RISCV_ISA_MULTI_EN/DIS respectively. Although they are numerically equivalent, the actual processing will not result in errors, but it may lead to ambiguous code semantics. Fixes: 613029442a4b ("RISC-V: KVM: Extend ONE_REG to enable/disable multiple ISA extensions") Signed-off-by: Quan Zhou Reviewed-by: Andrew Jones Link: https://lore.kernel.org/r/ff1c6771a67d660db94372ac9aaa40f51e5e0090.1716429371.git.zhouquan@iscas.ac.cn Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_onereg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index c676275ea0a0..62874fbca29f 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -724,9 +724,9 @@ static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, switch (reg_subtype) { case KVM_REG_RISCV_ISA_SINGLE: return riscv_vcpu_set_isa_ext_single(vcpu, reg_num, reg_val); - case KVM_REG_RISCV_SBI_MULTI_EN: + case KVM_REG_RISCV_ISA_MULTI_EN: return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, true); - case KVM_REG_RISCV_SBI_MULTI_DIS: + case KVM_REG_RISCV_ISA_MULTI_DIS: return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, false); default: return -ENOENT; -- cgit v1.2.3 From 539d33b5783804f22a62bd62ff463dfd1cef4265 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 16 May 2024 14:37:24 +0100 Subject: drm/komeda: remove unused struct 'gamma_curve_segment' 'gamma_curve_segment' looks like it has never been used. Remove it. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Dmitry Baryshkov Acked-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20240516133724.251750-1-linux@treblig.org Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c b/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c index d8e449e6ebda..50cb8f7ee6b2 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c @@ -72,11 +72,6 @@ struct gamma_curve_sector { u32 segment_width; }; -struct gamma_curve_segment { - u32 start; - u32 end; -}; - static struct gamma_curve_sector sector_tbl[] = { { 0, 4, 4 }, { 16, 4, 4 }, -- cgit v1.2.3 From 0f42bdf59b4e428485aa922bef871bfa6cc505e0 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 31 May 2024 18:43:37 +0900 Subject: selftests/tracing: Fix event filter test to retry up to 10 times Commit eb50d0f250e9 ("selftests/ftrace: Choose target function for filter test from samples") choose the target function from samples, but sometimes this test failes randomly because the target function does not hit at the next time. So retry getting samples up to 10 times. Fixes: eb50d0f250e9 ("selftests/ftrace: Choose target function for filter test from samples") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../ftrace/test.d/filter/event-filter-function.tc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc index 3f74c09c56b6..118247b8dd84 100644 --- a/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc +++ b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc @@ -10,7 +10,6 @@ fail() { #msg } sample_events() { - echo > trace echo 1 > events/kmem/kmem_cache_free/enable echo 1 > tracing_on ls > /dev/null @@ -22,6 +21,7 @@ echo 0 > tracing_on echo 0 > events/enable echo "Get the most frequently calling function" +echo > trace sample_events target_func=`cat trace | grep -o 'call_site=\([^+]*\)' | sed 's/call_site=//' | sort | uniq -c | sort | tail -n 1 | sed 's/^[ 0-9]*//'` @@ -32,7 +32,16 @@ echo > trace echo "Test event filter function name" echo "call_site.function == $target_func" > events/kmem/kmem_cache_free/filter + +sample_events +max_retry=10 +while [ `grep kmem_cache_free trace| wc -l` -eq 0 ]; do sample_events +max_retry=$((max_retry - 1)) +if [ $max_retry -eq 0 ]; then + exit_fail +fi +done hitcnt=`grep kmem_cache_free trace| grep $target_func | wc -l` misscnt=`grep kmem_cache_free trace| grep -v $target_func | wc -l` @@ -49,7 +58,16 @@ address=`grep " ${target_func}\$" /proc/kallsyms | cut -d' ' -f1` echo "Test event filter function address" echo "call_site.function == 0x$address" > events/kmem/kmem_cache_free/filter +echo > trace +sample_events +max_retry=10 +while [ `grep kmem_cache_free trace| wc -l` -eq 0 ]; do sample_events +max_retry=$((max_retry - 1)) +if [ $max_retry -eq 0 ]; then + exit_fail +fi +done hitcnt=`grep kmem_cache_free trace| grep $target_func | wc -l` misscnt=`grep kmem_cache_free trace| grep -v $target_func | wc -l` -- cgit v1.2.3 From 7dc3bfcb4c9cc58970fff6aaa48172cb224d85aa Mon Sep 17 00:00:00 2001 From: Chunguang Xu Date: Fri, 31 May 2024 17:24:21 +0800 Subject: nvme-fabrics: use reserved tag for reg read/write command In some scenarios, if too many commands are issued by nvme command in the same time by user tasks, this may exhaust all tags of admin_q. If a reset (nvme reset or IO timeout) occurs before these commands finish, reconnect routine may fail to update nvme regs due to insufficient tags, which will cause kernel hang forever. In order to workaround this issue, maybe we can let reg_read32()/reg_read64()/reg_write32() use reserved tags. This maybe safe for nvmf: 1. For the disable ctrl path, we will not issue connect command 2. For the enable ctrl / fw activate path, since connect and reg_xx() are called serially. So the reserved tags may still be enough while reg_xx() use reserved tags. Signed-off-by: Chunguang Xu Reviewed-by: Sagi Grimberg Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch --- drivers/nvme/host/fabrics.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index c6ad2148c2e0..ceb9c0ed3120 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -180,7 +180,7 @@ int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -226,7 +226,7 @@ int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -271,7 +271,7 @@ int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) cmd.prop_set.value = cpu_to_le64(val); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, NULL, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (unlikely(ret)) dev_err(ctrl->device, "Property Set error: %d, offset %#x\n", -- cgit v1.2.3 From 32c75ad4a79259609ee19f749832bc2d99bbdd13 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Fri, 31 May 2024 13:07:53 -0700 Subject: selftests/futex: don't redefine .PHONY targets (all, clean) The .PHONY targets "all" and "clean" are both already defined in the file that is included in the very next line: ../lib.mk. Remove this duplicate code. Reviewed-by: Davidlohr Bueso Signed-off-by: John Hubbard Signed-off-by: Shuah Khan --- tools/testing/selftests/futex/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index 11e157d7533b..78ab2cd111f6 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -3,8 +3,6 @@ SUBDIRS := functional TEST_PROGS := run.sh -.PHONY: all clean - include ../lib.mk all: -- cgit v1.2.3 From 4bf15b1c657d22d1d70173e43264e4606dfe75ff Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Fri, 31 May 2024 13:07:54 -0700 Subject: selftests/futex: don't pass a const char* to asprintf(3) When building with clang, via: make LLVM=1 -C tools/testing/selftests ...clang issues this warning: futex_requeue_pi.c:403:17: warning: passing 'const char **' to parameter of type 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers] This warning fires because test_name is passed into asprintf(3), which then changes it. Fix this by simply removing the const qualifier. This is a local automatic variable in a very short function, so there is not much need to use the compiler to enforce const-ness at this scope. [1] https://lore.kernel.org/all/20240329-selftests-libmk-llvm-rfc-v1-1-2f9ed7d1c49f@valentinobst.de/ Fixes: f17d8a87ecb5 ("selftests: fuxex: Report a unique test name per run of futex_requeue_pi") Reviewed-by: Davidlohr Bueso Signed-off-by: John Hubbard Signed-off-by: Shuah Khan --- tools/testing/selftests/futex/functional/futex_requeue_pi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index 7f3ca5c78df1..215c6cb539b4 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -360,7 +360,7 @@ out: int main(int argc, char *argv[]) { - const char *test_name; + char *test_name; int c, ret; while ((c = getopt(argc, argv, "bchlot:v:")) != -1) { -- cgit v1.2.3 From b1a1fdd7096dd2d67911b07f8118ff113d815db4 Mon Sep 17 00:00:00 2001 From: Weiwen Hu Date: Thu, 30 May 2024 14:16:46 +0800 Subject: nvme: fix nvme_pr_* status code parsing Fix the parsing if extra status bits (e.g. MORE) is present. Fixes: 7fb42780d06c ("nvme: Convert NVMe errors to PR errors") Signed-off-by: Weiwen Hu Signed-off-by: Keith Busch --- drivers/nvme/host/pr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index e05571b2a1b0..8fa1ffcdaed4 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -77,7 +77,7 @@ static int nvme_sc_to_pr_err(int nvme_sc) if (nvme_is_path_error(nvme_sc)) return PR_STS_PATH_FAILED; - switch (nvme_sc) { + switch (nvme_sc & 0x7ff) { case NVME_SC_SUCCESS: return PR_STS_SUCCESS; case NVME_SC_RESERVATION_CONFLICT: -- cgit v1.2.3 From 62da3acd28955e7299babebdfcb14243b789e773 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 28 May 2024 15:32:18 -0700 Subject: selftests/bpf: fix inet_csk_accept prototype in test_sk_storage_tracing.c Recent kernel change ([0]) changed inet_csk_accept() prototype. Adapt progs/test_sk_storage_tracing.c to take that into account. [0] 92ef0fd55ac8 ("net: change proto and proto_ops accept type") Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20240528223218.3445297-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c b/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c index 02e718f06e0f..40531e56776e 100644 --- a/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c +++ b/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c @@ -84,7 +84,7 @@ int BPF_PROG(trace_tcp_connect, struct sock *sk) } SEC("fexit/inet_csk_accept") -int BPF_PROG(inet_csk_accept, struct sock *sk, int flags, int *err, bool kern, +int BPF_PROG(inet_csk_accept, struct sock *sk, struct proto_accept_arg *arg, struct sock *accepted_sk) { set_task_info(accepted_sk); -- cgit v1.2.3 From aeb8fe0283d4d3b0f27a87c5f5c938e7324f7d8f Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 31 May 2024 21:45:00 +0200 Subject: bpf: Fix bpf_session_cookie BTF_ID in special_kfunc_set list The bpf_session_cookie is unavailable for !CONFIG_FPROBE as reported by Sebastian [1]. To fix that we remove CONFIG_FPROBE ifdef for session kfuncs, which is fine, because there's filter for session programs. Then based on bpf_trace.o dependency: obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o we add bpf_session_cookie BTF_ID in special_kfunc_set list dependency on CONFIG_BPF_EVENTS. [1] https://lore.kernel.org/bpf/20240531071557.MvfIqkn7@linutronix.de/T/#m71c6d5ec71db2967288cb79acedc15cc5dbfeec5 Reported-by: Sebastian Andrzej Siewior Tested-by: Sebastian Andrzej Siewior Suggested-by: Alexei Starovoitov Fixes: 5c919acef8514 ("bpf: Add support for kprobe session cookie") Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20240531194500.2967187-1-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 4 ++++ kernel/trace/bpf_trace.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 48f3a9acdef3..36ef8e96787e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11128,7 +11128,11 @@ BTF_ID(func, bpf_iter_css_task_new) #else BTF_ID_UNUSED #endif +#ifdef CONFIG_BPF_EVENTS BTF_ID(func, bpf_session_cookie) +#else +BTF_ID_UNUSED +#endif static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) { diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 6249dac61701..d1daeab1bbc1 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -3517,7 +3517,6 @@ static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx) } #endif /* CONFIG_UPROBES */ -#ifdef CONFIG_FPROBE __bpf_kfunc_start_defs(); __bpf_kfunc bool bpf_session_is_return(void) @@ -3566,4 +3565,3 @@ static int __init bpf_kprobe_multi_kfuncs_init(void) } late_initcall(bpf_kprobe_multi_kfuncs_init); -#endif -- cgit v1.2.3 From 7d0b3953f6d832daec10a0d76e2d4db405768a8b Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 29 May 2024 16:12:12 -0700 Subject: libbpf: don't close(-1) in multi-uprobe feature detector Guard close(link_fd) with extra link_fd >= 0 check to prevent close(-1). Detected by Coverity static analysis. Fixes: 04d939a2ab22 ("libbpf: detect broken PID filtering logic for multi-uprobe") Signed-off-by: Andrii Nakryiko Acked-by: Jiri Olsa Link: https://lore.kernel.org/r/20240529231212.768828-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/features.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c index 3df0125ed5fa..50befe125ddc 100644 --- a/tools/lib/bpf/features.c +++ b/tools/lib/bpf/features.c @@ -393,7 +393,8 @@ static int probe_uprobe_multi_link(int token_fd) err = -errno; /* close() can clobber errno */ if (link_fd >= 0 || err != -EBADF) { - close(link_fd); + if (link_fd >= 0) + close(link_fd); close(prog_fd); return 0; } -- cgit v1.2.3 From ebfb5e8fc8b45040b979f4bf0012a01d0abac8d4 Mon Sep 17 00:00:00 2001 From: Alexis Lothoré Date: Tue, 28 May 2024 16:20:28 +0200 Subject: Revert "wifi: wilc1000: convert list management to RCU" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f236464f1db7bea80075e6e31ac70dc6eb80547f Commit f236464f1db7 ("wifi: wilc1000: convert list management to RCU") replaced SRCU with RCU, aiming to simplify RCU usage in the driver. No documentation or commit history hinted about why SRCU has been preferred in original design, so it has been assumed to be safe to do this conversion. Unfortunately, some static analyzers raised warnings, confirmed by runtime checker, not long after the merge. At least three different issues arose when switching to RCU: - wilc_wlan_txq_filter_dup_tcp_ack is executed in a RCU read critical section yet calls wait_for_completion_timeout - wilc_wfi_init_mon_interface calls kmalloc and register_netdevice while manipulating a vif retrieved from vif list - set_channel sends command to chip (and so, also waits for a completion) while holding a vif retrieved from vif list (so, in RCU read critical section) Some of those issues are not trivial to fix and would need bigger driver rework. Fix those issues by reverting the SRCU to RCU conversion commit Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-wireless/3b46ec7c-baee-49fd-b760-3bc12fb12eaf@moroto.mountain/ Fixes: f236464f1db7 ("wifi: wilc1000: convert list management to RCU") Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240528-wilc_revert_srcu_to_rcu-v1-1-bce096e0798c@bootlin.com --- drivers/net/wireless/microchip/wilc1000/cfg80211.c | 41 ++++++++++++--------- drivers/net/wireless/microchip/wilc1000/hif.c | 15 +++++--- drivers/net/wireless/microchip/wilc1000/netdev.c | 43 +++++++++++++--------- drivers/net/wireless/microchip/wilc1000/netdev.h | 5 ++- drivers/net/wireless/microchip/wilc1000/wlan.c | 5 ++- 5 files changed, 64 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index 7d9fb9f2d527..089102ed9ae5 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -237,11 +237,12 @@ static int set_channel(struct wiphy *wiphy, struct wilc_vif *vif; u32 channelnum; int result; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return PTR_ERR(vif); } @@ -252,7 +253,7 @@ static int set_channel(struct wiphy *wiphy, if (result) netdev_err(vif->ndev, "Error in setting channel\n"); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return result; } @@ -805,8 +806,9 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; struct wilc_priv *priv; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) goto out; @@ -861,7 +863,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n"); out: - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1537,19 +1539,20 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, if (type == NL80211_IFTYPE_MONITOR) { struct net_device *ndev; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_vif_from_type(wl, WILC_AP_MODE); if (!vif) { vif = wilc_get_vif_from_type(wl, WILC_GO_MODE); if (!vif) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; } } if (vif->monitor_flag) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; } @@ -1557,12 +1560,12 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, if (ndev) { vif->monitor_flag = 1; } else { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ERR_PTR(-EINVAL); } wdev = &vif->priv.wdev; - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return wdev; } @@ -1610,7 +1613,7 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) list_del_rcu(&vif->list); wl->vif_num--; mutex_unlock(&wl->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wl->srcu); return 0; } @@ -1635,23 +1638,25 @@ static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled) { struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return; } netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled); wilc_set_wowlan_trigger(vif, enabled); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); } static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { int ret; + int srcu_idx; s32 tx_power = MBM_TO_DBM(mbm); struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; @@ -1659,10 +1664,10 @@ static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, if (!wl->initialized) return -EIO; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return -EINVAL; } @@ -1674,7 +1679,7 @@ static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, ret = wilc_set_tx_power(vif, tx_power); if (ret) netdev_err(vif->ndev, "Failed to set tx power\n"); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1757,6 +1762,7 @@ static void wlan_init_locks(struct wilc *wl) init_completion(&wl->cfg_event); init_completion(&wl->sync_event); init_completion(&wl->txq_thread_started); + init_srcu_struct(&wl->srcu); } void wlan_deinit_locks(struct wilc *wilc) @@ -1767,6 +1773,7 @@ void wlan_deinit_locks(struct wilc *wilc) mutex_destroy(&wilc->txq_add_to_head_cs); mutex_destroy(&wilc->vif_mutex); mutex_destroy(&wilc->deinit_lock); + cleanup_srcu_struct(&wilc->srcu); } int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 919de6ffb821..3925ca653e80 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1570,11 +1570,12 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1606,7 +1607,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) @@ -1614,13 +1615,14 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; mutex_lock(&wilc->deinit_lock); id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1647,7 +1649,7 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); mutex_unlock(&wilc->deinit_lock); } @@ -1655,11 +1657,12 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) { struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1684,7 +1687,7 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) } } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan, diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 73f56f7b002b..710e29bea560 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -127,28 +127,30 @@ void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid, int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) { + int srcu_idx; u8 ret_val = 0; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { if (!is_zero_ether_addr(vif->bssid)) ret_val++; } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return ret_val; } static void wilc_wake_tx_queues(struct wilc *wl) { + int srcu_idx; struct wilc_vif *ifc; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); wilc_for_each_vif(wl, ifc) { if (ifc->mac_opened && netif_queue_stopped(ifc->ndev)) netif_wake_queue(ifc->ndev); } - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); } static int wilc_txq_task(void *vp) @@ -653,6 +655,7 @@ static int wilc_set_mac_addr(struct net_device *dev, void *p) struct sockaddr *addr = (struct sockaddr *)p; unsigned char mac_addr[ETH_ALEN]; struct wilc_vif *tmp_vif; + int srcu_idx; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; @@ -664,19 +667,19 @@ static int wilc_set_mac_addr(struct net_device *dev, void *p) /* Verify MAC Address is not already in use: */ - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, tmp_vif) { wilc_get_mac_address(tmp_vif, mac_addr); if (ether_addr_equal(addr->sa_data, mac_addr)) { if (vif != tmp_vif) { - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return -EADDRNOTAVAIL; } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return 0; } } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); result = wilc_set_mac_address(vif, (u8 *)addr->sa_data); if (result) @@ -764,14 +767,15 @@ netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) wilc_tx_complete); if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { + int srcu_idx; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { if (vif->mac_opened) netif_stop_queue(vif->ndev); } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } return NETDEV_TX_OK; @@ -815,12 +819,13 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, unsigned int frame_len = 0; struct wilc_vif *vif; struct sk_buff *skb; + int srcu_idx; int stats; if (!wilc) return; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_netdev = get_if_handler(wilc, buff); if (!wilc_netdev) goto out; @@ -848,14 +853,15 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) { + int srcu_idx; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff; u16 type = le16_to_cpup((__le16 *)buff); @@ -876,7 +882,7 @@ void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) if (vif->monitor_flag) wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } static const struct net_device_ops wilc_netdev_ops = { @@ -906,7 +912,7 @@ void wilc_netdev_cleanup(struct wilc *wilc) list_del_rcu(&vif->list); wilc->vif_num--; mutex_unlock(&wilc->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wilc->srcu); if (vif->ndev) unregister_netdev(vif->ndev); } @@ -925,15 +931,16 @@ static u8 wilc_get_available_idx(struct wilc *wl) { int idx = 0; struct wilc_vif *vif; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); wilc_for_each_vif(wl, vif) { if (vif->idx == 0) idx = 1; else idx = 0; } - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return idx; } @@ -983,7 +990,7 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, list_add_tail_rcu(&vif->list, &wl->vif_list); wl->vif_num += 1; mutex_unlock(&wl->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wl->srcu); return vif; diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.h b/drivers/net/wireless/microchip/wilc1000/netdev.h index eecee3973d6a..5937d6d45695 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.h +++ b/drivers/net/wireless/microchip/wilc1000/netdev.h @@ -32,8 +32,8 @@ #define wilc_for_each_vif(w, v) \ struct wilc *_w = w; \ - list_for_each_entry_rcu(v, &_w->vif_list, list, \ - rcu_read_lock_held()) + list_for_each_entry_srcu(v, &_w->vif_list, list, \ + srcu_read_lock_held(&_w->srcu)) struct wilc_wfi_stats { unsigned long rx_packets; @@ -220,6 +220,7 @@ struct wilc { /* protect vif list */ struct mutex vif_mutex; + struct srcu_struct srcu; u8 open_ifcs; /* protect head of transmit queue */ diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c index 37c32d17856e..a9e872a7b2c3 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.c +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c @@ -712,6 +712,7 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) u32 *vmm_table = wilc->vmm_table; u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0}; const struct wilc_hif_func *func; + int srcu_idx; u8 *txb = wilc->tx_buffer; struct wilc_vif *vif; @@ -723,10 +724,10 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) mutex_lock(&wilc->txq_add_to_head_cs); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) wilc_wlan_txq_filter_dup_tcp_ack(vif->ndev); - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); for (ac = 0; ac < NQUEUES; ac++) tqe_q[ac] = wilc_wlan_txq_get_first(wilc, ac); -- cgit v1.2.3 From 3596717a6fbd54c64e97e085a9f77ed511ff59f9 Mon Sep 17 00:00:00 2001 From: Alexis Lothoré Date: Tue, 28 May 2024 16:20:29 +0200 Subject: Revert "wifi: wilc1000: set atomic flag on kmemdup in srcu critical section" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 35aee01ff43d7eb6c2caa0b94e7cc6c45baeeab7 Commit 35aee01ff43d ("wifi: wilc1000: set atomic flag on kmemdup in srcu critical section") was preparatory to the SRCU to RCU conversion done by commit f236464f1db7 ("wifi: wilc1000: convert list management to RCU"). This conversion brought issues and so has been reverted, so the atomic flag is not needed anymore. Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240528-wilc_revert_srcu_to_rcu-v1-2-bce096e0798c@bootlin.com --- drivers/net/wireless/microchip/wilc1000/hif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 3925ca653e80..f1085ccb7eed 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1594,7 +1594,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) msg->body.net_info.rssi = buffer[8]; msg->body.net_info.mgmt = kmemdup(&buffer[9], msg->body.net_info.frame_len, - GFP_ATOMIC); + GFP_KERNEL); if (!msg->body.net_info.mgmt) { kfree(msg); goto out; -- cgit v1.2.3 From 596c195680dceb34e6b994ff5571331d5dba8299 Mon Sep 17 00:00:00 2001 From: Alexis Lothoré Date: Tue, 28 May 2024 16:20:30 +0200 Subject: wifi: wilc1000: document SRCU usage instead of SRCU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f236464f1db7 ("wifi: wilc1000: convert list management to RCU") attempted to convert SRCU to RCU usage, assuming it was not really needed. The runtime issues that arose after merging it showed that there are code paths involving sleeping functions, and removing those would need some heavier driver rework. Add some documentation about SRCU need to make sure that any future developer do not miss some use cases if tempted to convert back again to RCU. Signed-off-by: Alexis Lothoré Signed-off-by: Kalle Valo Link: https://msgid.link/20240528-wilc_revert_srcu_to_rcu-v1-3-bce096e0798c@bootlin.com --- drivers/net/wireless/microchip/wilc1000/netdev.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.h b/drivers/net/wireless/microchip/wilc1000/netdev.h index 5937d6d45695..fde8610a9c84 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.h +++ b/drivers/net/wireless/microchip/wilc1000/netdev.h @@ -220,6 +220,13 @@ struct wilc { /* protect vif list */ struct mutex vif_mutex; + /* Sleepable RCU struct to manipulate vif list. Sleepable version is + * needed over the classic RCU version because the driver's current + * design involves some sleeping code while manipulating a vif + * retrieved from vif list (so in a SRCU critical section), like: + * - sending commands to the chip, using info from retrieved vif + * - registering a new monitoring net device + */ struct srcu_struct srcu; u8 open_ifcs; -- cgit v1.2.3 From 40cecacabc460f5074398753feb9ed7d43e8dfa6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 May 2024 14:23:08 +0200 Subject: wifi: mt76: mt7615: add missing chanctx ops Here's another one I missed during the initial conversion, fix that. Cc: stable@vger.kernel.org Reported-by: Rene Petersen Fixes: 0a44dfc07074 ("wifi: mac80211: simplify non-chanctx drivers") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218895 Signed-off-by: Johannes Berg Signed-off-by: Kalle Valo Link: https://msgid.link/20240528142308.3f7db1821e68.I531135d7ad76331a50244d6d5288e14aa9668390@changeid --- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 0971c164b57e..c27acaf0eb1c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -1326,6 +1326,10 @@ static void mt7615_set_rekey_data(struct ieee80211_hw *hw, #endif /* CONFIG_PM */ const struct ieee80211_ops mt7615_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt7615_tx, .start = mt7615_start, .stop = mt7615_stop, -- cgit v1.2.3 From 819bda58e77bb67974f94dc1aa11b0556b6f6889 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 29 May 2024 20:19:47 +0300 Subject: wifi: rtlwifi: Ignore IEEE80211_CONF_CHANGE_RETRY_LIMITS Since commit 0a44dfc07074 ("wifi: mac80211: simplify non-chanctx drivers") ieee80211_hw_config() is no longer called with changed = ~0. rtlwifi relied on ~0 in order to ignore the default retry limits of 4/7, preferring 48/48 in station mode and 7/7 in AP/IBSS. RTL8192DU has a lot of packet loss with the default limits from mac80211. Fix it by ignoring IEEE80211_CONF_CHANGE_RETRY_LIMITS completely, because it's the simplest solution. Link: https://lore.kernel.org/linux-wireless/cedd13d7691f4692b2a2fa5a24d44a22@realtek.com/ Cc: stable@vger.kernel.org # 6.9.x Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://msgid.link/1fabb8e4-adf3-47ae-8462-8aea963bc2a5@gmail.com --- drivers/net/wireless/realtek/rtlwifi/core.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 2e60a6991ca1..42b7db12b1bd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -633,21 +633,6 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { - rtl_dbg(rtlpriv, COMP_MAC80211, DBG_LOUD, - "IEEE80211_CONF_CHANGE_RETRY_LIMITS %x\n", - hw->conf.long_frame_max_tx_count); - /* brought up everything changes (changed == ~0) indicates first - * open, so use our default value instead of that of wiphy. - */ - if (changed != ~0) { - mac->retry_long = hw->conf.long_frame_max_tx_count; - mac->retry_short = hw->conf.long_frame_max_tx_count; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, - (u8 *)(&hw->conf.long_frame_max_tx_count)); - } - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL && !rtlpriv->proximity.proxim_on) { struct ieee80211_channel *channel = hw->conf.chandef.chan; -- cgit v1.2.3 From 5fc16fa5f13b3c06fdb959ef262050bd810416a2 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 1 Jun 2024 12:25:35 -0600 Subject: io_uring: check for non-NULL file pointer in io_file_can_poll() In earlier kernels, it was possible to trigger a NULL pointer dereference off the forced async preparation path, if no file had been assigned. The trace leading to that looks as follows: BUG: kernel NULL pointer dereference, address: 00000000000000b0 PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP CPU: 67 PID: 1633 Comm: buf-ring-invali Not tainted 6.8.0-rc3+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS unknown 2/2/2022 RIP: 0010:io_buffer_select+0xc3/0x210 Code: 00 00 48 39 d1 0f 82 ae 00 00 00 48 81 4b 48 00 00 01 00 48 89 73 70 0f b7 50 0c 66 89 53 42 85 ed 0f 85 d2 00 00 00 48 8b 13 <48> 8b 92 b0 00 00 00 48 83 7a 40 00 0f 84 21 01 00 00 4c 8b 20 5b RSP: 0018:ffffb7bec38c7d88 EFLAGS: 00010246 RAX: ffff97af2be61000 RBX: ffff97af234f1700 RCX: 0000000000000040 RDX: 0000000000000000 RSI: ffff97aecfb04820 RDI: ffff97af234f1700 RBP: 0000000000000000 R08: 0000000000200030 R09: 0000000000000020 R10: ffffb7bec38c7dc8 R11: 000000000000c000 R12: ffffb7bec38c7db8 R13: ffff97aecfb05800 R14: ffff97aecfb05800 R15: ffff97af2be5e000 FS: 00007f852f74b740(0000) GS:ffff97b1eeec0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000000000b0 CR3: 000000016deab005 CR4: 0000000000370ef0 Call Trace: ? __die+0x1f/0x60 ? page_fault_oops+0x14d/0x420 ? do_user_addr_fault+0x61/0x6a0 ? exc_page_fault+0x6c/0x150 ? asm_exc_page_fault+0x22/0x30 ? io_buffer_select+0xc3/0x210 __io_import_iovec+0xb5/0x120 io_readv_prep_async+0x36/0x70 io_queue_sqe_fallback+0x20/0x260 io_submit_sqes+0x314/0x630 __do_sys_io_uring_enter+0x339/0xbc0 ? __do_sys_io_uring_register+0x11b/0xc50 ? vm_mmap_pgoff+0xce/0x160 do_syscall_64+0x5f/0x180 entry_SYSCALL_64_after_hwframe+0x46/0x4e RIP: 0033:0x55e0a110a67e Code: ba cc 00 00 00 45 31 c0 44 0f b6 92 d0 00 00 00 31 d2 41 b9 08 00 00 00 41 83 e2 01 41 c1 e2 04 41 09 c2 b8 aa 01 00 00 0f 05 90 89 30 eb a9 0f 1f 40 00 48 8b 42 20 8b 00 a8 06 75 af 85 f6 because the request is marked forced ASYNC and has a bad file fd, and hence takes the forced async prep path. Current kernels with the request async prep cleaned up can no longer hit this issue, but for ease of backporting, let's add this safety check in here too as it really doesn't hurt. For both cases, this will inevitably end with a CQE posted with -EBADF. Cc: stable@vger.kernel.org Fixes: a76c0b31eef5 ("io_uring: commit non-pollable provided mapped buffers upfront") Signed-off-by: Jens Axboe --- io_uring/io_uring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 624ca9076a50..726e6367af4d 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -433,7 +433,7 @@ static inline bool io_file_can_poll(struct io_kiocb *req) { if (req->flags & REQ_F_CAN_POLL) return true; - if (file_can_poll(req->file)) { + if (req->file && file_can_poll(req->file)) { req->flags |= REQ_F_CAN_POLL; return true; } -- cgit v1.2.3 From 30636258a7c9174be44ddb2318bae4f66d4beab0 Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Thu, 30 May 2024 11:41:43 +0800 Subject: virtio_net: fix missing lock protection on control_buf access Refactored the handling of control_buf to be within the cvq_lock critical section, mitigating race conditions between reading device responses and new command submissions. Fixes: 6f45ab3e0409 ("virtio_net: Add a lock for the command VQ.") Signed-off-by: Heng Qi Reviewed-by: Hariprasad Kelam Acked-by: Jason Wang Reviewed-by: Xuan Zhuo Link: https://lore.kernel.org/r/20240530034143.19579-1-hengqi@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4a802c0ea2cb..1ea8e6a24286 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2686,6 +2686,7 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd { struct scatterlist *sgs[5], hdr, stat; u32 out_num = 0, tmp, in_num = 0; + bool ok; int ret; /* Caller should know better */ @@ -2731,8 +2732,9 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd } unlock: + ok = vi->ctrl->status == VIRTIO_NET_OK; mutex_unlock(&vi->cvq_lock); - return vi->ctrl->status == VIRTIO_NET_OK; + return ok; } static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, -- cgit v1.2.3 From 7679935b8bdf8d2bc298b04ed579d1ed78e81907 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 29 May 2024 14:56:35 -0400 Subject: MAINTAINERS: remove Peter Geis The Motorcomm PHY driver is now maintained by the OEM. The driver has expanded far beyond my original purpose, and I do not have the hardware to test against the new portions of it. Therefore I am removing myself as a maintainer of the driver. Signed-off-by: Peter Geis Link: https://lore.kernel.org/r/20240529185635.538072-1-pgwipeout@gmail.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index ba231d2f3a4e..f7b8025e3c27 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15239,7 +15239,6 @@ F: drivers/staging/most/ F: include/linux/most.h MOTORCOMM PHY DRIVER -M: Peter Geis M: Frank L: netdev@vger.kernel.org S: Maintained -- cgit v1.2.3 From 89e281ebff72e6d37dce2df0e142b2909dafb267 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Wed, 29 May 2024 21:08:14 -0700 Subject: ethtool: init tsinfo stats if requested Statistic values should be set to ETHTOOL_STAT_NOT_SET even if the device doesn't support statistics. Otherwise zeros will be returned as if they are proper values: host# ethtool -I -T lo Time stamping parameters for lo: Capabilities: software-transmit software-receive software-system-clock PTP Hardware Clock: none Hardware Transmit Timestamp Modes: none Hardware Receive Filter Modes: none Statistics: tx_pkts: 0 tx_lost: 0 tx_err: 0 Fixes: 0e9c127729be ("ethtool: add interface to read Tx hardware timestamping statistics") Suggested-by: Jakub Kicinski Signed-off-by: Vadim Fedorenko Reviewed-by: Rahul Rameshbabu Link: https://lore.kernel.org/r/20240530040814.1014446-1-vadfed@meta.com Signed-off-by: Jakub Kicinski --- net/ethtool/tsinfo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index be2755c8d8fd..57d496287e52 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -38,11 +38,11 @@ static int tsinfo_prepare_data(const struct ethnl_req_info *req_base, ret = ethnl_ops_begin(dev); if (ret < 0) return ret; - if (req_base->flags & ETHTOOL_FLAG_STATS && - dev->ethtool_ops->get_ts_stats) { + if (req_base->flags & ETHTOOL_FLAG_STATS) { ethtool_stats_init((u64 *)&data->stats, sizeof(data->stats) / sizeof(u64)); - dev->ethtool_ops->get_ts_stats(dev, &data->stats); + if (dev->ethtool_ops->get_ts_stats) + dev->ethtool_ops->get_ts_stats(dev, &data->stats); } ret = __ethtool_get_ts_info(dev, &data->ts_info); ethnl_ops_complete(dev); -- cgit v1.2.3 From 9e0945b1901c9eed4fbee3b8a3870487b2bdc936 Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Tue, 28 May 2024 21:41:15 +0800 Subject: virtio_net: fix possible dim status unrecoverable When the dim worker is scheduled, if it no longer needs to issue commands, dim may not be able to return to the working state later. For example, the following single queue scenario: 1. The dim worker of rxq0 is scheduled, and the dim status is changed to DIM_APPLY_NEW_PROFILE; 2. dim is disabled or parameters have not been modified; 3. virtnet_rx_dim_work exits directly; Then, even if net_dim is invoked again, it cannot work because the state is not restored to DIM_START_MEASURE. Fixes: 6208799553a8 ("virtio-net: support rx netdim") Signed-off-by: Heng Qi Reviewed-by: Jiri Pirko Acked-by: Michael S. Tsirkin Reviewed-by: Xuan Zhuo Link: https://lore.kernel.org/r/20240528134116.117426-2-hengqi@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 1ea8e6a24286..774a292a6090 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -4419,9 +4419,9 @@ static void virtnet_rx_dim_work(struct work_struct *work) if (err) pr_debug("%s: Failed to send dim parameters on rxq%d\n", dev->name, qnum); - dim->state = DIM_START_MEASURE; } out: + dim->state = DIM_START_MEASURE; mutex_unlock(&rq->dim_lock); } -- cgit v1.2.3 From d1f0bd01bc58f35b5353ad9dbe5f7249a8f3368e Mon Sep 17 00:00:00 2001 From: Heng Qi Date: Tue, 28 May 2024 21:41:16 +0800 Subject: virtio_net: fix a spurious deadlock issue When the following snippet is run, lockdep will report a deadlock[1]. /* Acquire all queues dim_locks */ for (i = 0; i < vi->max_queue_pairs; i++) mutex_lock(&vi->rq[i].dim_lock); There's no deadlock here because the vq locks are always taken in the same order, but lockdep can not figure it out. So refactoring the code to alleviate the problem. [1] ======================================================== WARNING: possible recursive locking detected 6.9.0-rc7+ #319 Not tainted -------------------------------------------- ethtool/962 is trying to acquire lock: but task is already holding lock: other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&vi->rq[i].dim_lock); lock(&vi->rq[i].dim_lock); *** DEADLOCK *** May be due to missing lock nesting notation 3 locks held by ethtool/962: #0: ffffffff82dbaab0 (cb_lock){++++}-{3:3}, at: genl_rcv+0x19/0x40 #1: ffffffff82dad0a8 (rtnl_mutex){+.+.}-{3:3}, at: ethnl_default_set_doit+0xbe/0x1e0 stack backtrace: CPU: 6 PID: 962 Comm: ethtool Not tainted 6.9.0-rc7+ #319 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack_lvl+0x79/0xb0 check_deadlock+0x130/0x220 __lock_acquire+0x861/0x990 lock_acquire.part.0+0x72/0x1d0 ? lock_acquire+0xf8/0x130 __mutex_lock+0x71/0xd50 virtnet_set_coalesce+0x151/0x190 __ethnl_set_coalesce.isra.0+0x3f8/0x4d0 ethnl_set_coalesce+0x34/0x90 ethnl_default_set_doit+0xdd/0x1e0 genl_family_rcv_msg_doit+0xdc/0x130 genl_family_rcv_msg+0x154/0x230 ? __pfx_ethnl_default_set_doit+0x10/0x10 genl_rcv_msg+0x4b/0xa0 ? __pfx_genl_rcv_msg+0x10/0x10 netlink_rcv_skb+0x5a/0x110 genl_rcv+0x28/0x40 netlink_unicast+0x1af/0x280 netlink_sendmsg+0x20e/0x460 __sys_sendto+0x1fe/0x210 ? find_held_lock+0x2b/0x80 ? do_user_addr_fault+0x3a2/0x8a0 ? __lock_release+0x5e/0x160 ? do_user_addr_fault+0x3a2/0x8a0 ? lock_release+0x72/0x140 ? do_user_addr_fault+0x3a7/0x8a0 __x64_sys_sendto+0x29/0x30 do_syscall_64+0x78/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e Fixes: 4d4ac2ececd3 ("virtio_net: Add a lock for per queue RX coalesce") Signed-off-by: Heng Qi Acked-by: Michael S. Tsirkin Reviewed-by: Xuan Zhuo Acked-by: Jason Wang Link: https://lore.kernel.org/r/20240528134116.117426-3-hengqi@linux.alibaba.com Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 774a292a6090..61a57d134544 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -4259,7 +4259,6 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, struct virtio_net_ctrl_coal_rx *coal_rx __free(kfree) = NULL; bool rx_ctrl_dim_on = !!ec->use_adaptive_rx_coalesce; struct scatterlist sgs_rx; - int ret = 0; int i; if (rx_ctrl_dim_on && !virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) @@ -4269,27 +4268,27 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, ec->rx_max_coalesced_frames != vi->intr_coal_rx.max_packets)) return -EINVAL; - /* Acquire all queues dim_locks */ - for (i = 0; i < vi->max_queue_pairs; i++) - mutex_lock(&vi->rq[i].dim_lock); - if (rx_ctrl_dim_on && !vi->rx_dim_enabled) { vi->rx_dim_enabled = true; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].dim_enabled = true; - goto unlock; + mutex_unlock(&vi->rq[i].dim_lock); + } + return 0; } coal_rx = kzalloc(sizeof(*coal_rx), GFP_KERNEL); - if (!coal_rx) { - ret = -ENOMEM; - goto unlock; - } + if (!coal_rx) + return -ENOMEM; if (!rx_ctrl_dim_on && vi->rx_dim_enabled) { vi->rx_dim_enabled = false; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].dim_enabled = false; + mutex_unlock(&vi->rq[i].dim_lock); + } } /* Since the per-queue coalescing params can be set, @@ -4302,22 +4301,19 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, - &sgs_rx)) { - ret = -EINVAL; - goto unlock; - } + &sgs_rx)) + return -EINVAL; vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs; vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames; for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs; vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames; - } -unlock: - for (i = vi->max_queue_pairs - 1; i >= 0; i--) mutex_unlock(&vi->rq[i].dim_lock); + } - return ret; + return 0; } static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, -- cgit v1.2.3 From 3c34fb0bd4a4237592c5ecb5b2e2531900c55774 Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Wed, 29 May 2024 17:02:43 -0400 Subject: ax25: Fix refcount imbalance on inbound connections When releasing a socket in ax25_release(), we call netdev_put() to decrease the refcount on the associated ax.25 device. However, the execution path for accepting an incoming connection never calls netdev_hold(). This imbalance leads to refcount errors, and ultimately to kernel crashes. A typical call trace for the above situation will start with one of the following errors: refcount_t: decrement hit 0; leaking memory. refcount_t: underflow; use-after-free. And will then have a trace like: Call Trace: ? show_regs+0x64/0x70 ? __warn+0x83/0x120 ? refcount_warn_saturate+0xb2/0x100 ? report_bug+0x158/0x190 ? prb_read_valid+0x20/0x30 ? handle_bug+0x3e/0x70 ? exc_invalid_op+0x1c/0x70 ? asm_exc_invalid_op+0x1f/0x30 ? refcount_warn_saturate+0xb2/0x100 ? refcount_warn_saturate+0xb2/0x100 ax25_release+0x2ad/0x360 __sock_release+0x35/0xa0 sock_close+0x19/0x20 [...] On reboot (or any attempt to remove the interface), the kernel gets stuck in an infinite loop: unregister_netdevice: waiting for ax0 to become free. Usage count = 0 This patch corrects these issues by ensuring that we call netdev_hold() and ax25_dev_hold() for new connections in ax25_accept(). This makes the logic leading to ax25_accept() match the logic for ax25_bind(): in both cases we increment the refcount, which is ultimately decremented in ax25_release(). Fixes: 9fd75b66b8f6 ("ax25: Fix refcount leaks caused by ax25_cb_del()") Signed-off-by: Lars Kellogg-Stedman Tested-by: Duoming Zhou Tested-by: Dan Cross Tested-by: Chris Maness Reviewed-by: Dan Carpenter Link: https://lore.kernel.org/r/20240529210242.3346844-2-lars@oddbit.com Signed-off-by: Jakub Kicinski --- net/ax25/af_ax25.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 8077cf2ee448..d6f9fae06a9d 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1378,8 +1378,10 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, { struct sk_buff *skb; struct sock *newsk; + ax25_dev *ax25_dev; DEFINE_WAIT(wait); struct sock *sk; + ax25_cb *ax25; int err = 0; if (sock->state != SS_UNCONNECTED) @@ -1434,6 +1436,10 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, kfree_skb(skb); sk_acceptq_removed(sk); newsock->state = SS_CONNECTED; + ax25 = sk_to_ax25(newsk); + ax25_dev = ax25->ax25_dev; + netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); + ax25_dev_hold(ax25_dev); out: release_sock(sk); -- cgit v1.2.3 From 166fcf86cd34e15c7f383eda4642d7a212393008 Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Thu, 30 May 2024 13:17:33 +0800 Subject: ax25: Replace kfree() in ax25_dev_free() with ax25_dev_put() The object "ax25_dev" is managed by reference counting. Thus it should not be directly released by kfree(), replace with ax25_dev_put(). Fixes: d01ffb9eee4a ("ax25: add refcount in ax25_dev to avoid UAF bugs") Suggested-by: Dan Carpenter Signed-off-by: Duoming Zhou Reviewed-by: Dan Carpenter Link: https://lore.kernel.org/r/20240530051733.11416-1-duoming@zju.edu.cn Signed-off-by: Jakub Kicinski --- net/ax25/ax25_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index 742d7c68e7e7..9efd6690b344 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -196,7 +196,7 @@ void __exit ax25_dev_free(void) list_for_each_entry_safe(s, n, &ax25_dev_list, list) { netdev_put(s->dev, &s->dev_tracker); list_del(&s->list); - kfree(s); + ax25_dev_put(s); } spin_unlock_bh(&ax25_dev_lock); } -- cgit v1.2.3 From 8105378c0c02309221b63d15cfe8fe92e7fe1434 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Thu, 30 May 2024 11:27:17 +0800 Subject: net: rps: fix error when CONFIG_RFS_ACCEL is off John Sperbeck reported that if we turn off CONFIG_RFS_ACCEL, the 'head' is not defined, which will trigger compile error. So I move the 'head' out of the CONFIG_RFS_ACCEL scope. Fixes: 84b6823cd96b ("net: rps: protect last_qtail with rps_input_queue_tail_save() helper") Reported-by: John Sperbeck Closes: https://lore.kernel.org/all/20240529203421.2432481-1-jsperbeck@google.com/ Signed-off-by: Jason Xing Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20240530032717.57787-1-kerneljasonxing@gmail.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index e1bb6d7856d9..4d4de9008f6f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4516,12 +4516,13 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb, struct rps_dev_flow *rflow, u16 next_cpu) { if (next_cpu < nr_cpu_ids) { + u32 head; #ifdef CONFIG_RFS_ACCEL struct netdev_rx_queue *rxqueue; struct rps_dev_flow_table *flow_table; struct rps_dev_flow *old_rflow; - u32 flow_id, head; u16 rxq_index; + u32 flow_id; int rc; /* Should we steer this flow to a different hardware queue? */ -- cgit v1.2.3 From e85e271dec0270982afed84f70dc37703fcc1d52 Mon Sep 17 00:00:00 2001 From: DelphineCCChiu Date: Wed, 29 May 2024 14:58:55 +0800 Subject: net/ncsi: Fix the multi thread manner of NCSI driver Currently NCSI driver will send several NCSI commands back to back without waiting the response of previous NCSI command or timeout in some state when NIC have multi channel. This operation against the single thread manner defined by NCSI SPEC(section 6.3.2.3 in DSP0222_1.1.1) According to NCSI SPEC(section 6.2.13.1 in DSP0222_1.1.1), we should probe one channel at a time by sending NCSI commands (Clear initial state, Get version ID, Get capabilities...), than repeat this steps until the max number of channels which we got from NCSI command (Get capabilities) has been probed. Fixes: e6f44ed6d04d ("net/ncsi: Package and channel management") Signed-off-by: DelphineCCChiu Link: https://lore.kernel.org/r/20240529065856.825241-1-delphine_cc_chiu@wiwynn.com Signed-off-by: Jakub Kicinski --- net/ncsi/internal.h | 2 ++ net/ncsi/ncsi-manage.c | 73 +++++++++++++++++++++++++------------------------- net/ncsi/ncsi-rsp.c | 4 ++- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h index 374412ed780b..ef0f8f73826f 100644 --- a/net/ncsi/internal.h +++ b/net/ncsi/internal.h @@ -325,6 +325,7 @@ struct ncsi_dev_priv { spinlock_t lock; /* Protect the NCSI device */ unsigned int package_probe_id;/* Current ID during probe */ unsigned int package_num; /* Number of packages */ + unsigned int channel_probe_id;/* Current cahnnel ID during probe */ struct list_head packages; /* List of packages */ struct ncsi_channel *hot_channel; /* Channel was ever active */ struct ncsi_request requests[256]; /* Request table */ @@ -343,6 +344,7 @@ struct ncsi_dev_priv { bool multi_package; /* Enable multiple packages */ bool mlx_multi_host; /* Enable multi host Mellanox */ u32 package_whitelist; /* Packages to configure */ + unsigned char channel_count; /* Num of channels to probe */ }; struct ncsi_cmd_arg { diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 745c788f1d1d..5ecf611c8820 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -510,17 +510,19 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) break; case ncsi_dev_state_suspend_gls: - ndp->pending_req_num = np->channel_num; + ndp->pending_req_num = 1; nca.type = NCSI_PKT_CMD_GLS; nca.package = np->id; + nca.channel = ndp->channel_probe_id; + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + ndp->channel_probe_id++; - nd->state = ncsi_dev_state_suspend_dcnt; - NCSI_FOR_EACH_CHANNEL(np, nc) { - nca.channel = nc->id; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; + if (ndp->channel_probe_id == ndp->channel_count) { + ndp->channel_probe_id = 0; + nd->state = ncsi_dev_state_suspend_dcnt; } break; @@ -1345,7 +1347,6 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) { struct ncsi_dev *nd = &ndp->ndev; struct ncsi_package *np; - struct ncsi_channel *nc; struct ncsi_cmd_arg nca; unsigned char index; int ret; @@ -1423,23 +1424,6 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nd->state = ncsi_dev_state_probe_cis; break; - case ncsi_dev_state_probe_cis: - ndp->pending_req_num = NCSI_RESERVED_CHANNEL; - - /* Clear initial state */ - nca.type = NCSI_PKT_CMD_CIS; - nca.package = ndp->active_package->id; - for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) { - nca.channel = index; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; - } - - nd->state = ncsi_dev_state_probe_gvi; - if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_KEEP_PHY)) - nd->state = ncsi_dev_state_probe_keep_phy; - break; case ncsi_dev_state_probe_keep_phy: ndp->pending_req_num = 1; @@ -1452,14 +1436,17 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nd->state = ncsi_dev_state_probe_gvi; break; + case ncsi_dev_state_probe_cis: case ncsi_dev_state_probe_gvi: case ncsi_dev_state_probe_gc: case ncsi_dev_state_probe_gls: np = ndp->active_package; - ndp->pending_req_num = np->channel_num; + ndp->pending_req_num = 1; - /* Retrieve version, capability or link status */ - if (nd->state == ncsi_dev_state_probe_gvi) + /* Clear initial state Retrieve version, capability or link status */ + if (nd->state == ncsi_dev_state_probe_cis) + nca.type = NCSI_PKT_CMD_CIS; + else if (nd->state == ncsi_dev_state_probe_gvi) nca.type = NCSI_PKT_CMD_GVI; else if (nd->state == ncsi_dev_state_probe_gc) nca.type = NCSI_PKT_CMD_GC; @@ -1467,19 +1454,29 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nca.type = NCSI_PKT_CMD_GLS; nca.package = np->id; - NCSI_FOR_EACH_CHANNEL(np, nc) { - nca.channel = nc->id; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; - } + nca.channel = ndp->channel_probe_id; - if (nd->state == ncsi_dev_state_probe_gvi) + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + + if (nd->state == ncsi_dev_state_probe_cis) { + nd->state = ncsi_dev_state_probe_gvi; + if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_KEEP_PHY) && ndp->channel_probe_id == 0) + nd->state = ncsi_dev_state_probe_keep_phy; + } else if (nd->state == ncsi_dev_state_probe_gvi) { nd->state = ncsi_dev_state_probe_gc; - else if (nd->state == ncsi_dev_state_probe_gc) + } else if (nd->state == ncsi_dev_state_probe_gc) { nd->state = ncsi_dev_state_probe_gls; - else + } else { + nd->state = ncsi_dev_state_probe_cis; + ndp->channel_probe_id++; + } + + if (ndp->channel_probe_id == ndp->channel_count) { + ndp->channel_probe_id = 0; nd->state = ncsi_dev_state_probe_dp; + } break; case ncsi_dev_state_probe_dp: ndp->pending_req_num = 1; @@ -1780,6 +1777,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, ndp->requests[i].ndp = ndp; timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0); } + ndp->channel_count = NCSI_RESERVED_CHANNEL; spin_lock_irqsave(&ncsi_dev_lock, flags); list_add_tail_rcu(&ndp->node, &ncsi_dev_list); @@ -1813,6 +1811,7 @@ int ncsi_start_dev(struct ncsi_dev *nd) if (!(ndp->flags & NCSI_DEV_PROBED)) { ndp->package_probe_id = 0; + ndp->channel_probe_id = 0; nd->state = ncsi_dev_state_probe; schedule_work(&ndp->work); return 0; diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c index bee290d0f48b..e28be33bdf2c 100644 --- a/net/ncsi/ncsi-rsp.c +++ b/net/ncsi/ncsi-rsp.c @@ -795,12 +795,13 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr) struct ncsi_rsp_gc_pkt *rsp; struct ncsi_dev_priv *ndp = nr->ndp; struct ncsi_channel *nc; + struct ncsi_package *np; size_t size; /* Find the channel */ rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp); ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, - NULL, &nc); + &np, &nc); if (!nc) return -ENODEV; @@ -835,6 +836,7 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr) */ nc->vlan_filter.bitmap = U64_MAX; nc->vlan_filter.n_vids = rsp->vlan_cnt; + np->ndp->channel_count = rsp->channel_cnt; return 0; } -- cgit v1.2.3 From 33700a0c9b562700c28d31360a5f04508f459a45 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov <0x7f454c46@gmail.com> Date: Wed, 29 May 2024 18:29:32 +0100 Subject: net/tcp: Don't consider TCP_CLOSE in TCP_AO_ESTABLISHED TCP_CLOSE may or may not have current/rnext keys and should not be considered "established". The fast-path for TCP_CLOSE is SKB_DROP_REASON_TCP_CLOSE. This is what tcp_rcv_state_process() does anyways. Add an early drop path to not spend any time verifying segment signatures for sockets in TCP_CLOSE state. Cc: stable@vger.kernel.org # v6.7 Fixes: 0a3a809089eb ("net/tcp: Verify inbound TCP-AO signed segments") Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://lore.kernel.org/r/20240529-tcp_ao-sk_state-v1-1-d69b5d323c52@gmail.com Signed-off-by: Jakub Kicinski --- include/net/tcp_ao.h | 7 ++++--- net/ipv4/tcp_ao.c | 13 +++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 471e177362b4..5d8e9ed2c005 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -86,7 +86,8 @@ static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key) struct tcp_ao_info { /* List of tcp_ao_key's */ struct hlist_head head; - /* current_key and rnext_key aren't maintained on listen sockets. + /* current_key and rnext_key are maintained on sockets + * in TCP_AO_ESTABLISHED states. * Their purpose is to cache keys on established connections, * saving needless lookups. Never dereference any of them from * listen sockets. @@ -201,9 +202,9 @@ struct tcp6_ao_context { }; struct tcp_sigpool; +/* Established states are fast-path and there always is current_key/rnext_key */ #define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \ - TCPF_CLOSE | TCPF_CLOSE_WAIT | \ - TCPF_LAST_ACK | TCPF_CLOSING) + TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING) int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, struct tcp_ao_key *key, struct tcphdr *th, diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 781b67a52571..37c42b63ff99 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -933,6 +933,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, struct tcp_ao_key *key; __be32 sisn, disn; u8 *traffic_key; + int state; u32 sne = 0; info = rcu_dereference(tcp_sk(sk)->ao_info); @@ -948,8 +949,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, disn = 0; } + state = READ_ONCE(sk->sk_state); /* Fast-path */ - if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) { + if (likely((1 << state) & TCP_AO_ESTABLISHED)) { enum skb_drop_reason err; struct tcp_ao_key *current_key; @@ -988,6 +990,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, return SKB_NOT_DROPPED_YET; } + if (unlikely(state == TCP_CLOSE)) + return SKB_DROP_REASON_TCP_CLOSE; + /* Lookup key based on peer address and keyid. * current_key and rnext_key must not be used on tcp listen * sockets as otherwise: @@ -1001,7 +1006,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, if (th->syn && !th->ack) goto verify_hash; - if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { + if ((1 << state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { /* Make the initial syn the likely case here */ if (unlikely(req)) { sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, @@ -1018,14 +1023,14 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, /* no way to figure out initial sisn/disn - drop */ return SKB_DROP_REASON_TCP_FLAGS; } - } else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { + } else if ((1 << state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { disn = info->lisn; if (th->syn || th->rst) sisn = th->seq; else sisn = info->risn; } else { - WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); + WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", state); return SKB_DROP_REASON_TCP_AOFAILURE; } verify_hash: -- cgit v1.2.3 From 6c3ca6654a74dd396bc477839ba8d9792eced441 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Remove CONFIG_ACPI_TABLE_UPGRADE in platform_init() Both acpi_table_upgrade() and acpi_boot_table_init() are defined as empty functions under !CONFIG_ACPI_TABLE_UPGRADE and !CONFIG_ACPI in include/linux/acpi.h, there are no implicit declaration errors with various configs. #ifdef CONFIG_ACPI_TABLE_UPGRADE void acpi_table_upgrade(void); #else static inline void acpi_table_upgrade(void) { } #endif #ifdef CONFIG_ACPI ... void acpi_boot_table_init (void); ... #else /* !CONFIG_ACPI */ ... static inline void acpi_boot_table_init(void) { } ... #endif /* !CONFIG_ACPI */ As Huacai suggested, CONFIG_ACPI_TABLE_UPGRADE is ugly and not necessary here, just remove it. At the same time, just keep CONFIG_ACPI to prevent potential build errors in future, and give a signal to indicate the code is ACPI-specific. For the same reason, we also put acpi_table_upgrade() under CONFIG_ACPI. Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/setup.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 60e0fe97f61a..89ad756aeeed 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -351,10 +351,8 @@ void __init platform_init(void) arch_reserve_vmcore(); arch_reserve_crashkernel(); -#ifdef CONFIG_ACPI_TABLE_UPGRADE - acpi_table_upgrade(); -#endif #ifdef CONFIG_ACPI + acpi_table_upgrade(); acpi_gbl_use_default_register_widths = false; acpi_boot_table_init(); #endif -- cgit v1.2.3 From b56f67a6c748bb009f313f91651c8020d2338d63 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Fix built-in DTB detection fdt_check_header(__dtb_start) will always success because kernel provides a dummy dtb, and by coincidence __dtb_start clashed with entry of this dummy dtb. The consequence is fdt passed from firmware will never be taken. Fix by trying to utilise __dtb_start only when CONFIG_BUILTIN_DTB is enabled. Cc: stable@vger.kernel.org Fixes: 7b937cc243e5 ("of: Create of_root if no dtb provided by firmware") Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 89ad756aeeed..3d048f1be143 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -282,7 +282,7 @@ static void __init fdt_setup(void) return; /* Prefer to use built-in dtb, checking its legality first. */ - if (!fdt_check_header(__dtb_start)) + if (IS_ENABLED(CONFIG_BUILTIN_DTB) && !fdt_check_header(__dtb_start)) fdt_pointer = __dtb_start; else fdt_pointer = efi_fdt_pointer(); /* Fallback to firmware dtb */ -- cgit v1.2.3 From 3de9c42d02a79a5e09bbee7a4421ddc00cfd5c6d Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Add all CPUs enabled by fdt to NUMA node 0 NUMA enabled kernel on FDT based machine fails to boot because CPUs are all in NUMA_NO_NODE and mm subsystem won't accept that. Fix by adding them to default NUMA node at FDT parsing phase and move numa_add_cpu(0) to a later point. Cc: stable@vger.kernel.org Fixes: 88d4d957edc7 ("LoongArch: Add FDT booting support from efi system table") Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/numa.h | 1 + arch/loongarch/kernel/smp.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/numa.h b/arch/loongarch/include/asm/numa.h index 27f319b49862..b5f9de9f102e 100644 --- a/arch/loongarch/include/asm/numa.h +++ b/arch/loongarch/include/asm/numa.h @@ -56,6 +56,7 @@ extern int early_cpu_to_node(int cpu); static inline void early_numa_add_cpu(int cpuid, s16 node) { } static inline void numa_add_cpu(unsigned int cpu) { } static inline void numa_remove_cpu(unsigned int cpu) { } +static inline void set_cpuid_to_node(int cpuid, s16 node) { } static inline int early_cpu_to_node(int cpu) { diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 0dfe2388ef41..1436d2465939 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -273,7 +273,6 @@ static void __init fdt_smp_setup(void) if (cpuid == loongson_sysconf.boot_cpu_id) { cpu = 0; - numa_add_cpu(cpu); } else { cpu = cpumask_next_zero(-1, cpu_present_mask); } @@ -283,6 +282,9 @@ static void __init fdt_smp_setup(void) set_cpu_present(cpu, true); __cpu_number_map[cpuid] = cpu; __cpu_logical_map[cpu] = cpuid; + + early_numa_add_cpu(cpu, 0); + set_cpuid_to_node(cpuid, 0); } loongson_sysconf.nr_cpus = num_processors; @@ -468,6 +470,7 @@ void smp_prepare_boot_cpu(void) set_cpu_possible(0, true); set_cpu_online(0, true); set_my_cpu_offset(per_cpu_offset(0)); + numa_add_cpu(0); rr_node = first_node(node_online_map); for_each_possible_cpu(cpu) { -- cgit v1.2.3 From beb2800074c15362cf9f6c7301120910046d6556 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Fix entry point in kernel image header Currently kernel entry in head.S is in DMW address range, firmware is instructed to jump to this address after loading the kernel image. However kernel should not make any assumption on firmware's DMW setting, thus the entry point should be a physical address falls into direct translation region. Fix by converting entry address to physical and amend entry calculation logic in libstub accordingly. BTW, use ABSOLUTE() to calculate variables to make Clang/LLVM happy. Cc: stable@vger.kernel.org Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/head.S | 2 +- arch/loongarch/kernel/vmlinux.lds.S | 10 ++++++---- drivers/firmware/efi/libstub/loongarch.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index c4f7de2e2805..4677ea8fa8e9 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -22,7 +22,7 @@ _head: .word MZ_MAGIC /* "MZ", MS-DOS header */ .org 0x8 - .dword kernel_entry /* Kernel entry point */ + .dword _kernel_entry /* Kernel entry point (physical address) */ .dword _kernel_asize /* Kernel image effective size */ .quad PHYS_LINK_KADDR /* Kernel image load offset from start of RAM */ .org 0x38 /* 0x20 ~ 0x37 reserved */ diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index e8e97dbf9ca4..3c7595342730 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -6,6 +6,7 @@ #define PAGE_SIZE _PAGE_SIZE #define RO_EXCEPTION_TABLE_ALIGN 4 +#define PHYSADDR_MASK 0xffffffffffff /* 48-bit */ /* * Put .bss..swapper_pg_dir as the first thing in .bss. This will @@ -142,10 +143,11 @@ SECTIONS #ifdef CONFIG_EFI_STUB /* header symbols */ - _kernel_asize = _end - _text; - _kernel_fsize = _edata - _text; - _kernel_vsize = _end - __initdata_begin; - _kernel_rsize = _edata - __initdata_begin; + _kernel_entry = ABSOLUTE(kernel_entry & PHYSADDR_MASK); + _kernel_asize = ABSOLUTE(_end - _text); + _kernel_fsize = ABSOLUTE(_edata - _text); + _kernel_vsize = ABSOLUTE(_end - __initdata_begin); + _kernel_rsize = ABSOLUTE(_edata - __initdata_begin); #endif .gptab.sdata : { diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 684c9354637c..d0ef93551c44 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -41,7 +41,7 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) unsigned long __weak kernel_entry_address(unsigned long kernel_addr, efi_loaded_image_t *image) { - return *(unsigned long *)(kernel_addr + 8) - VMLINUX_LOAD_ADDRESS + kernel_addr; + return *(unsigned long *)(kernel_addr + 8) - PHYSADDR(VMLINUX_LOAD_ADDRESS) + kernel_addr; } efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, -- cgit v1.2.3 From 1098efd299ffe9c8af818425338c7f6c4f930a98 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Override higher address bits in JUMP_VIRT_ADDR In JUMP_VIRT_ADDR we are performing an or calculation on address value directly from pcaddi. This will only work if we are currently running from direct 1:1 mapping addresses or firmware's DMW is configured exactly same as kernel. Still, we should not rely on such assumption. Fix by overriding higher bits in address comes from pcaddi, so we can get rid of or operator. Cc: stable@vger.kernel.org Signed-off-by: Jiaxun Yang Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/stackframe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 45b507a7b06f..d9eafd3ee3d1 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -42,7 +42,7 @@ .macro JUMP_VIRT_ADDR temp1 temp2 li.d \temp1, CACHE_BASE pcaddi \temp2, 0 - or \temp1, \temp1, \temp2 + bstrins.d \temp1, \temp2, (DMW_PABITS - 1), 0 jirl zero, \temp1, 0xc .endm -- cgit v1.2.3 From eb36e520f4f1b690fd776f15cbac452f82ff7bfa Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 3 Jun 2024 15:45:53 +0800 Subject: LoongArch: Fix GMAC's phy-mode definitions in dts The GMAC of Loongson chips cannot insert the correct 1.5-2ns delay. So we need the PHY to insert internal delays for both transmit and receive data lines from/to the PHY device. Fix this by changing the "phy-mode" from "rgmii" to "rgmii-id" in dts. Signed-off-by: Huacai Chen --- arch/loongarch/boot/dts/loongson-2k0500-ref.dts | 4 ++-- arch/loongarch/boot/dts/loongson-2k1000-ref.dts | 4 ++-- arch/loongarch/boot/dts/loongson-2k2000-ref.dts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts index 8aefb0c12672..a34734a6c3ce 100644 --- a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts @@ -44,14 +44,14 @@ &gmac0 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; bus_id = <0x0>; }; &gmac1 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; bus_id = <0x1>; }; diff --git a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts index 8463fe035386..23cf26cc3e5f 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts @@ -43,7 +43,7 @@ &gmac0 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy0>; mdio { compatible = "snps,dwmac-mdio"; @@ -58,7 +58,7 @@ &gmac1 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy1>; mdio { compatible = "snps,dwmac-mdio"; diff --git a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts index 74b99bd234cc..ea9e6985d0e9 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts @@ -92,7 +92,7 @@ &gmac2 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy2>; mdio { compatible = "snps,dwmac-mdio"; -- cgit v1.2.3 From 629f2b4e05225e53125aaf7ff0b87d5d53897128 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 28 May 2024 11:08:32 +0800 Subject: drm/panel: sitronix-st7789v: Add check for of_drm_get_panel_orientation Add check for the return value of of_drm_get_panel_orientation() and return the error if it fails in order to catch the error. Fixes: b27c0f6d208d ("drm/panel: sitronix-st7789v: add panel orientation support") Signed-off-by: Chen Ni Reviewed-by: Michael Riesch Acked-by: Jessica Zhang Link: https://lore.kernel.org/r/20240528030832.2529471-1-nichen@iscas.ac.cn Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240528030832.2529471-1-nichen@iscas.ac.cn --- drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index e8f385b9c618..28bfc48a9127 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -643,7 +643,9 @@ static int st7789v_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation); + ret = of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n"); drm_panel_add(&ctx->panel); -- cgit v1.2.3 From 38a38f5a36da9820680d413972cb733349400532 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 May 2024 21:38:53 +0200 Subject: Input: silead - Always support 10 fingers When support for Silead touchscreens was orginal added some touchscreens with older firmware versions only supported 5 fingers and this was made the default requiring the setting of a "silead,max-fingers=10" uint32 device-property for all touchscreen models which do support 10 fingers. There are very few models with the old 5 finger fw, so in practice the setting of the "silead,max-fingers=10" is boilerplate which needs to be copy and pasted to every touchscreen config. Reporting that 10 fingers are supported on devices which only support 5 fingers doesn't cause any problems for userspace in practice, since at max 4 finger gestures are supported anyways. Drop the max_fingers configuration and simply always assume 10 fingers. Signed-off-by: Hans de Goede Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20240525193854.39130-2-hdegoede@redhat.com --- drivers/input/touchscreen/silead.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index bbd366dcb69a..6a42b27c4599 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -71,7 +71,6 @@ struct silead_ts_data { struct regulator_bulk_data regulators[2]; char fw_name[64]; struct touchscreen_properties prop; - u32 max_fingers; u32 chip_id; struct input_mt_pos pos[SILEAD_MAX_FINGERS]; int slots[SILEAD_MAX_FINGERS]; @@ -136,7 +135,7 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) touchscreen_parse_properties(data->input, true, &data->prop); silead_apply_efi_fw_min_max(data); - input_mt_init_slots(data->input, data->max_fingers, + input_mt_init_slots(data->input, SILEAD_MAX_FINGERS, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); @@ -256,10 +255,10 @@ static void silead_ts_read_data(struct i2c_client *client) return; } - if (buf[0] > data->max_fingers) { + if (buf[0] > SILEAD_MAX_FINGERS) { dev_warn(dev, "More touches reported then supported %d > %d\n", - buf[0], data->max_fingers); - buf[0] = data->max_fingers; + buf[0], SILEAD_MAX_FINGERS); + buf[0] = SILEAD_MAX_FINGERS; } if (silead_ts_handle_pen_data(data, buf)) @@ -315,7 +314,6 @@ sync: static int silead_ts_init(struct i2c_client *client) { - struct silead_ts_data *data = i2c_get_clientdata(client); int error; error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, @@ -325,7 +323,7 @@ static int silead_ts_init(struct i2c_client *client) usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); error = i2c_smbus_write_byte_data(client, SILEAD_REG_TOUCH_NR, - data->max_fingers); + SILEAD_MAX_FINGERS); if (error) goto i2c_write_err; usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); @@ -591,13 +589,6 @@ static void silead_ts_read_props(struct i2c_client *client) const char *str; int error; - error = device_property_read_u32(dev, "silead,max-fingers", - &data->max_fingers); - if (error) { - dev_dbg(dev, "Max fingers read error %d\n", error); - data->max_fingers = 5; /* Most devices handle up-to 5 fingers */ - } - error = device_property_read_string(dev, "firmware-name", &str); if (!error) snprintf(data->fw_name, sizeof(data->fw_name), -- cgit v1.2.3 From 84b26f509c1b9c8b3c3c63d75912701f151fd148 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 May 2024 21:38:54 +0200 Subject: platform/x86: touchscreen_dmi: Drop "silead,max-fingers" property The silead touchscreen driver now defaults to 10 fingers, so it is no longer necessary to have a "silead,max-fingers=10" property for each silead touchscreen model. Drop this property from all the configs. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240525193854.39130-3-hdegoede@redhat.com --- drivers/platform/x86/touchscreen_dmi.c | 57 ---------------------------------- 1 file changed, 57 deletions(-) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 2d9ca2292ea1..3f626584f2c3 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -34,7 +34,6 @@ static const struct property_entry archos_101_cesium_educ_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-archos-101-cesium-educ.fw"), { } @@ -49,7 +48,6 @@ static const struct property_entry bush_bush_windows_tablet_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1850), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-bush-bush-windows-tablet.fw"), { } @@ -79,7 +77,6 @@ static const struct property_entry chuwi_hi8_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-hi8-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -95,7 +92,6 @@ static const struct property_entry chuwi_hi8_pro_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -123,7 +119,6 @@ static const struct property_entry chuwi_hi10_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 5), PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 4), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -139,7 +134,6 @@ static const struct property_entry chuwi_hi10_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1908), PROPERTY_ENTRY_U32("touchscreen-size-y", 1270), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("silead,pen-supported"), PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8), @@ -171,7 +165,6 @@ static const struct property_entry chuwi_hi10_pro_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-pro.fw"), PROPERTY_ENTRY_U32_ARRAY("silead,efi-fw-min-max", chuwi_hi10_pro_efi_min_max), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("silead,pen-supported"), PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8), @@ -201,7 +194,6 @@ static const struct property_entry chuwi_hibook_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hibook.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -227,7 +219,6 @@ static const struct property_entry chuwi_vi8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-vi8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -255,7 +246,6 @@ static const struct property_entry chuwi_vi10_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1858), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-vi10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -271,7 +261,6 @@ static const struct property_entry chuwi_surbook_mini_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2040), PROPERTY_ENTRY_U32("touchscreen-size-y", 1524), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-surbook-mini.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), { } }; @@ -289,7 +278,6 @@ static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-connect-tablet9.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -306,7 +294,6 @@ static const struct property_entry csl_panther_tab_hd_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-csl-panther-tab-hd.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -322,7 +309,6 @@ static const struct property_entry cube_iwork8_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -346,7 +332,6 @@ static const struct property_entry cube_knote_i1101_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1961), PROPERTY_ENTRY_U32("touchscreen-size-y", 1513), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-cube-knote-i1101.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -360,7 +345,6 @@ static const struct property_entry dexp_ursus_7w_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 890), PROPERTY_ENTRY_U32("touchscreen-size-y", 630), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -376,7 +360,6 @@ static const struct property_entry dexp_ursus_kx210i_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1720), PROPERTY_ENTRY_U32("touchscreen-size-y", 1137), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-dexp-ursus-kx210i.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -391,7 +374,6 @@ static const struct property_entry digma_citi_e200_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-digma_citi_e200.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -450,7 +432,6 @@ static const struct property_entry irbis_tw90_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-irbis_tw90.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -466,7 +447,6 @@ static const struct property_entry irbis_tw118_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1960), PROPERTY_ENTRY_U32("touchscreen-size-y", 1510), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-irbis-tw118.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -483,7 +463,6 @@ static const struct property_entry itworks_tw891_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -496,7 +475,6 @@ static const struct property_entry jumper_ezpad_6_pro_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -511,7 +489,6 @@ static const struct property_entry jumper_ezpad_6_pro_b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro-b.fw"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -527,7 +504,6 @@ static const struct property_entry jumper_ezpad_6_m4_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1950), PROPERTY_ENTRY_U32("touchscreen-size-y", 1525), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-m4.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -544,7 +520,6 @@ static const struct property_entry jumper_ezpad_7_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1526), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-jumper-ezpad-7.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,stuck-controller-bug"), { } }; @@ -561,7 +536,6 @@ static const struct property_entry jumper_ezpad_mini3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -578,7 +552,6 @@ static const struct property_entry mpman_converter9_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-mpman-converter9.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -594,7 +567,6 @@ static const struct property_entry mpman_mpwin895cl_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -611,7 +583,6 @@ static const struct property_entry myria_my8307_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-myria-my8307.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -628,7 +599,6 @@ static const struct property_entry onda_obook_20_plus_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-obook-20-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -645,7 +615,6 @@ static const struct property_entry onda_v80_plus_v3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v80-plus-v3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -669,7 +638,6 @@ static const struct property_entry onda_v820w_32g_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-onda-v820w-32g.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -687,7 +655,6 @@ static const struct property_entry onda_v891_v5_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v891-v5.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -703,7 +670,6 @@ static const struct property_entry onda_v891w_v1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1676), PROPERTY_ENTRY_U32("touchscreen-size-y", 1130), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-onda-v891w-v1.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -720,7 +686,6 @@ static const struct property_entry onda_v891w_v3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1135), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v891w-v3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -759,7 +724,6 @@ static const struct property_entry pipo_w11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1984), PROPERTY_ENTRY_U32("touchscreen-size-y", 1532), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-pipo-w11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -775,7 +739,6 @@ static const struct property_entry positivo_c4128b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1915), PROPERTY_ENTRY_U32("touchscreen-size-y", 1269), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-positivo-c4128b.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -791,7 +754,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v20_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1146), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-pov-mobii-wintab-p800w-v20.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -808,7 +770,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -825,7 +786,6 @@ static const struct property_entry pov_mobii_wintab_p1006w_v10_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1520), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p1006w-v10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -842,7 +802,6 @@ static const struct property_entry predia_basic_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1144), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-predia-basic.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -859,7 +818,6 @@ static const struct property_entry rca_cambio_w101_v2_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 874), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rca-cambio-w101-v2.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -874,7 +832,6 @@ static const struct property_entry rwc_nanote_p8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-p8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -890,7 +847,6 @@ static const struct property_entry schneider_sct101ctm_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-schneider-sct101ctm.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -906,7 +862,6 @@ static const struct property_entry globalspace_solt_ivw116_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1723), PROPERTY_ENTRY_U32("touchscreen-size-y", 1077), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-globalspace-solt-ivw116.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -923,7 +878,6 @@ static const struct property_entry techbite_arc_11_6_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1270), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -939,7 +893,6 @@ static const struct property_entry teclast_tbook11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1264), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-tbook11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -965,7 +918,6 @@ static const struct property_entry teclast_x16_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1264), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-x16-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -988,7 +940,6 @@ static const struct property_entry teclast_x3_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-teclast-x3-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1004,7 +955,6 @@ static const struct property_entry teclast_x98plus2_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-teclast_x98plus2.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -1018,7 +968,6 @@ static const struct property_entry trekstor_primebook_c11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1530), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1032,7 +981,6 @@ static const struct property_entry trekstor_primebook_c13_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2624), PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c13.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1046,7 +994,6 @@ static const struct property_entry trekstor_primetab_t13b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2500), PROPERTY_ENTRY_U32("touchscreen-size-y", 1900), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primetab-t13b.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), { } @@ -1074,7 +1021,6 @@ static const struct property_entry trekstor_surftab_twin_10_1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1090,7 +1036,6 @@ static const struct property_entry trekstor_surftab_wintron70_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 884), PROPERTY_ENTRY_U32("touchscreen-size-y", 632), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-surftab-wintron70-st70416-6.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1107,7 +1052,6 @@ static const struct property_entry viglen_connect_10_props[] = { PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 6), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-viglen-connect-10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1121,7 +1065,6 @@ static const struct property_entry vinga_twizzle_j116_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1920), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; -- cgit v1.2.3 From 55624db051c8bdd39acbb1f35774c3f7b97c07b8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 2 Jun 2024 11:58:00 +0300 Subject: platform/x86: touchscreen_dmi: Use 2-argument strscpy() Use 2-argument strscpy(), which is not only shorter but also provides an additional check that destination buffer is an array. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240602090244.1666360-8-andy.shevchenko@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/touchscreen_dmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 3f626584f2c3..f74af0a689f2 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -1850,7 +1850,7 @@ static int __init ts_parse_props(char *str) u32 u32val; int i, ret; - strscpy(orig_str, str, sizeof(orig_str)); + strscpy(orig_str, str); /* * str is part of the static_command_line from init/main.c and poking -- cgit v1.2.3 From 078fc56f5c1787a9272373742f817b320c046d1a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 May 2024 13:59:33 +0200 Subject: platform/x86: yt2-1380: add CONFIG_EXTCON dependency This driver uses the extcon subsystem and fails to build when it cannot call into that subsystem: x86_64-linux-ld: vmlinux.o: in function `yt2_1380_fc_worker': lenovo-yoga-tab2-pro-1380-fastcharger.c:(.text+0xa9d819): undefined reference to `extcon_get_state' x86_64-linux-ld: lenovo-yoga-tab2-pro-1380-fastcharger.c:(.text+0xa9d853): undefined reference to `extcon_get_state' x86_64-linux-ld: vmlinux.o: in function `yt2_1380_fc_serdev_probe': lenovo-yoga-tab2-pro-1380-fastcharger.c:(.text+0xa9da22): undefined reference to `extcon_get_extcon_dev' x86_64-linux-ld: lenovo-yoga-tab2-pro-1380-fastcharger.c:(.text+0xa9dc0c): undefined reference to `devm_extcon_register_notifier_all' Add a Kconfig dependency to make it it always builds correctly. Fixes: b2ed33e8d486 ("platform/x86: Add lenovo-yoga-tab2-pro-1380-fastcharger driver") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240528115940.3169455-1-arnd@kernel.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1953317541ea..665fa9524986 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -136,6 +136,7 @@ config YOGABOOK config YT2_1380 tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" depends on SERIAL_DEV_BUS + depends on EXTCON depends on ACPI help Say Y here to enable support for the custom fast charging protocol -- cgit v1.2.3 From 1981b296f858010eae409548fd297659b2cc570e Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 28 May 2024 22:49:02 +0200 Subject: platform/x86: dell-smbios: Fix wrong token data in sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading token data from sysfs on my Inspiron 3505, the token locations and values are wrong. This happens because match_attribute() blindly assumes that all entries in da_tokens have an associated entry in token_attrs. This however is not true as soon as da_tokens[] contains zeroed token entries. Those entries are being skipped when initialising token_attrs, breaking the core assumption of match_attribute(). Fix this by defining an extra struct for each pair of token attributes and use container_of() to retrieve token information. Tested on a Dell Inspiron 3050. Fixes: 33b9ca1e53b4 ("platform/x86: dell-smbios: Add a sysfs interface for SMBIOS tokens") Signed-off-by: Armin Wolf Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240528204903.445546-1-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-smbios-base.c | 92 +++++++++++----------------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c index e61bfaf8b5c4..86b95206cb1b 100644 --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -25,11 +26,16 @@ static u32 da_supported_commands; static int da_num_tokens; static struct platform_device *platform_device; static struct calling_interface_token *da_tokens; -static struct device_attribute *token_location_attrs; -static struct device_attribute *token_value_attrs; +static struct token_sysfs_data *token_entries; static struct attribute **token_attrs; static DEFINE_MUTEX(smbios_mutex); +struct token_sysfs_data { + struct device_attribute location_attr; + struct device_attribute value_attr; + struct calling_interface_token *token; +}; + struct smbios_device { struct list_head list; struct device *device; @@ -416,47 +422,26 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) } } -static int match_attribute(struct device *dev, - struct device_attribute *attr) -{ - int i; - - for (i = 0; i < da_num_tokens * 2; i++) { - if (!token_attrs[i]) - continue; - if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) - return i/2; - } - dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); - return -EINVAL; -} - static ssize_t location_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i; + struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr); if (!capable(CAP_SYS_ADMIN)) return -EPERM; - i = match_attribute(dev, attr); - if (i > 0) - return sysfs_emit(buf, "%08x", da_tokens[i].location); - return 0; + return sysfs_emit(buf, "%08x", data->token->location); } static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i; + struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr); if (!capable(CAP_SYS_ADMIN)) return -EPERM; - i = match_attribute(dev, attr); - if (i > 0) - return sysfs_emit(buf, "%08x", da_tokens[i].value); - return 0; + return sysfs_emit(buf, "%08x", data->token->value); } static struct attribute_group smbios_attribute_group = { @@ -473,22 +458,15 @@ static int build_tokens_sysfs(struct platform_device *dev) { char *location_name; char *value_name; - size_t size; int ret; int i, j; - /* (number of tokens + 1 for null terminated */ - size = sizeof(struct device_attribute) * (da_num_tokens + 1); - token_location_attrs = kzalloc(size, GFP_KERNEL); - if (!token_location_attrs) + token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL); + if (!token_entries) return -ENOMEM; - token_value_attrs = kzalloc(size, GFP_KERNEL); - if (!token_value_attrs) - goto out_allocate_value; /* need to store both location and value + terminator*/ - size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); - token_attrs = kzalloc(size, GFP_KERNEL); + token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL); if (!token_attrs) goto out_allocate_attrs; @@ -496,27 +474,32 @@ static int build_tokens_sysfs(struct platform_device *dev) /* skip empty */ if (da_tokens[i].tokenID == 0) continue; + + token_entries[i].token = &da_tokens[i]; + /* add location */ location_name = kasprintf(GFP_KERNEL, "%04x_location", da_tokens[i].tokenID); if (location_name == NULL) goto out_unwind_strings; - sysfs_attr_init(&token_location_attrs[i].attr); - token_location_attrs[i].attr.name = location_name; - token_location_attrs[i].attr.mode = 0444; - token_location_attrs[i].show = location_show; - token_attrs[j++] = &token_location_attrs[i].attr; + + sysfs_attr_init(&token_entries[i].location_attr.attr); + token_entries[i].location_attr.attr.name = location_name; + token_entries[i].location_attr.attr.mode = 0444; + token_entries[i].location_attr.show = location_show; + token_attrs[j++] = &token_entries[i].location_attr.attr; /* add value */ value_name = kasprintf(GFP_KERNEL, "%04x_value", da_tokens[i].tokenID); if (value_name == NULL) goto loop_fail_create_value; - sysfs_attr_init(&token_value_attrs[i].attr); - token_value_attrs[i].attr.name = value_name; - token_value_attrs[i].attr.mode = 0444; - token_value_attrs[i].show = value_show; - token_attrs[j++] = &token_value_attrs[i].attr; + + sysfs_attr_init(&token_entries[i].value_attr.attr); + token_entries[i].value_attr.attr.name = value_name; + token_entries[i].value_attr.attr.mode = 0444; + token_entries[i].value_attr.show = value_show; + token_attrs[j++] = &token_entries[i].value_attr.attr; continue; loop_fail_create_value: @@ -532,14 +515,12 @@ loop_fail_create_value: out_unwind_strings: while (i--) { - kfree(token_location_attrs[i].attr.name); - kfree(token_value_attrs[i].attr.name); + kfree(token_entries[i].location_attr.attr.name); + kfree(token_entries[i].value_attr.attr.name); } kfree(token_attrs); out_allocate_attrs: - kfree(token_value_attrs); -out_allocate_value: - kfree(token_location_attrs); + kfree(token_entries); return -ENOMEM; } @@ -551,12 +532,11 @@ static void free_group(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &smbios_attribute_group); for (i = 0; i < da_num_tokens; i++) { - kfree(token_location_attrs[i].attr.name); - kfree(token_value_attrs[i].attr.name); + kfree(token_entries[i].location_attr.attr.name); + kfree(token_entries[i].value_attr.attr.name); } kfree(token_attrs); - kfree(token_value_attrs); - kfree(token_location_attrs); + kfree(token_entries); } static int __init dell_smbios_init(void) -- cgit v1.2.3 From 306aec7eea8c83e8212f4dd6e5b358c508e3e466 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 28 May 2024 22:49:03 +0200 Subject: platform/x86: dell-smbios: Simplify error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the allocation of value_name fails, the error handling code uses two gotos for error handling, which is not necessary. Simplify the error handling in this case by only using a single goto. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240528204903.445546-2-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-smbios-base.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c index 86b95206cb1b..b562ed99ec4e 100644 --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -492,19 +492,16 @@ static int build_tokens_sysfs(struct platform_device *dev) /* add value */ value_name = kasprintf(GFP_KERNEL, "%04x_value", da_tokens[i].tokenID); - if (value_name == NULL) - goto loop_fail_create_value; + if (!value_name) { + kfree(location_name); + goto out_unwind_strings; + } sysfs_attr_init(&token_entries[i].value_attr.attr); token_entries[i].value_attr.attr.name = value_name; token_entries[i].value_attr.attr.mode = 0444; token_entries[i].value_attr.show = value_show; token_attrs[j++] = &token_entries[i].value_attr.attr; - continue; - -loop_fail_create_value: - kfree(location_name); - goto out_unwind_strings; } smbios_attribute_group.attrs = token_attrs; -- cgit v1.2.3 From 77f1972bdcf7513293e8bbe376b9fe837310ee9c Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Mon, 3 Jun 2024 08:15:12 +0000 Subject: platform/x86/amd/hsmp: Check HSMP support on AMD family of processors HSMP interface is supported only on few x86 processors from AMD. Accessing HSMP registers on rest of the platforms might cause unexpected behaviour. So add a check. Also unavailability of this interface on rest of the processors is not an error. Hence, use pr_info() instead of the pr_err() to log the message. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20240603081512.142909-1-suma.hegde@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/hsmp.c | 50 +++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index d84ea66eecc6..8fcf38eed7f0 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -907,16 +907,44 @@ static int hsmp_plat_dev_register(void) return ret; } +/* + * This check is only needed for backward compatibility of previous platforms. + * All new platforms are expected to support ACPI based probing. + */ +static bool legacy_hsmp_support(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return false; + + switch (boot_cpu_data.x86) { + case 0x19: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + case 0x30 ... 0x3F: + case 0x90 ... 0x9F: + case 0xA0 ... 0xAF: + return true; + default: + return false; + } + case 0x1A: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + return true; + default: + return false; + } + default: + return false; + } + + return false; +} + static int __init hsmp_plt_init(void) { int ret = -ENODEV; - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { - pr_err("HSMP is not supported on Family:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - return ret; - } - /* * amd_nb_num() returns number of SMN/DF interfaces present in the system * if we have N SMN/DF interfaces that ideally means N sockets @@ -930,7 +958,15 @@ static int __init hsmp_plt_init(void) return ret; if (!plat_dev.is_acpi_device) { - ret = hsmp_plat_dev_register(); + if (legacy_hsmp_support()) { + /* Not ACPI device, but supports HSMP, register a plat_dev */ + ret = hsmp_plat_dev_register(); + } else { + /* Not ACPI, Does not support HSMP */ + pr_info("HSMP is not supported on Family:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + ret = -ENODEV; + } if (ret) platform_driver_unregister(&amd_hsmp_driver); } -- cgit v1.2.3 From 0110c4b110477bb1f19b0d02361846be7ab08300 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Mon, 27 May 2024 13:41:13 +0530 Subject: irqchip/riscv-intc: Prevent memory leak when riscv_intc_init_common() fails When riscv_intc_init_common() fails, the firmware node allocated is not freed. Add the missing free(). Fixes: 7023b9d83f03 ("irqchip/riscv-intc: Add ACPI support") Signed-off-by: Sunil V L Signed-off-by: Thomas Gleixner Reviewed-by: Anup Patel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240527081113.616189-1-sunilvl@ventanamicro.com --- drivers/irqchip/irq-riscv-intc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 9e71c4428814..4f3a12383a1e 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -253,8 +253,9 @@ IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init); static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, const unsigned long end) { - struct fwnode_handle *fn; struct acpi_madt_rintc *rintc; + struct fwnode_handle *fn; + int rc; rintc = (struct acpi_madt_rintc *)header; @@ -273,7 +274,11 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, return -ENOMEM; } - return riscv_intc_init_common(fn, &riscv_intc_chip); + rc = riscv_intc_init_common(fn, &riscv_intc_chip); + if (rc) + irq_domain_free_fwnode(fn); + + return rc; } IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL, -- cgit v1.2.3 From 6149db4997f582e958da675092f21c666e3b67b7 Mon Sep 17 00:00:00 2001 From: Tristram Ha Date: Tue, 28 May 2024 19:20:23 -0700 Subject: net: phy: micrel: fix KSZ9477 PHY issues after suspend/resume When the PHY is powered up after powered down most of the registers are reset, so the PHY setup code needs to be done again. In addition the interrupt register will need to be setup again so that link status indication works again. Fixes: 26dd2974c5b5 ("net: phy: micrel: Move KSZ9477 errata fixes to PHY driver") Signed-off-by: Tristram Ha Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 62 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2b8f8b7f1517..8c20cf937530 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -1939,7 +1939,7 @@ static const struct ksz9477_errata_write ksz9477_errata_writes[] = { {0x1c, 0x20, 0xeeee}, }; -static int ksz9477_config_init(struct phy_device *phydev) +static int ksz9477_phy_errata(struct phy_device *phydev) { int err; int i; @@ -1967,16 +1967,30 @@ static int ksz9477_config_init(struct phy_device *phydev) return err; } + err = genphy_restart_aneg(phydev); + if (err) + return err; + + return err; +} + +static int ksz9477_config_init(struct phy_device *phydev) +{ + int err; + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + err = ksz9477_phy_errata(phydev); + if (err) + return err; + } + /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes * in this switch shall be regarded as broken. */ if (phydev->dev_flags & MICREL_NO_EEE) phydev->eee_broken_modes = -1; - err = genphy_restart_aneg(phydev); - if (err) - return err; - return kszphy_config_init(phydev); } @@ -2085,6 +2099,42 @@ static int kszphy_resume(struct phy_device *phydev) return 0; } +static int ksz9477_resume(struct phy_device *phydev) +{ + int ret; + + /* No need to initialize registers if not powered down. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + + /* After switching from power-down to normal mode, an internal global + * reset is automatically generated. Wait a minimum of 1 ms before + * read/write access to the PHY registers. + */ + usleep_range(1000, 2000); + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + ret = ksz9477_phy_errata(phydev); + if (ret) + return ret; + } + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -5493,7 +5543,7 @@ static struct phy_driver ksphy_driver[] = { .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = genphy_suspend, - .resume = genphy_resume, + .resume = ksz9477_resume, .get_features = ksz9477_get_features, } }; -- cgit v1.2.3 From e306a894bd511804ba9db7c00ca9cc05b55df1f2 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 29 May 2024 14:54:56 -0700 Subject: irqchip/sifive-plic: Chain to parent IRQ after handlers are ready Now that the PLIC uses a platform driver, the driver is probed later in the boot process, where interrupts from peripherals might already be pending. As a result, plic_handle_irq() may be called as early as the call to irq_set_chained_handler() completes. But this call happens before the per-context handler is completely set up, so there is a window where plic_handle_irq() can see incomplete per-context state and crash. Avoid this by delaying the call to irq_set_chained_handler() until all handlers from all PLICs are initialized. Fixes: 8ec99b033147 ("irqchip/sifive-plic: Convert PLIC driver into a platform driver") Reported-by: Geert Uytterhoeven Signed-off-by: Samuel Holland Signed-off-by: Thomas Gleixner Tested-by: Geert Uytterhoeven Reviewed-by: Anup Patel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240529215458.937817-1-samuel.holland@sifive.com Closes: https://lore.kernel.org/r/CAMuHMdVYFFR7K5SbHBLY-JHhb7YpgGMS_hnRWm8H0KD-wBo+4A@mail.gmail.com/ --- drivers/irqchip/irq-sifive-plic.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 8fb183ced1e7..9e22f7e378f5 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -85,7 +85,7 @@ struct plic_handler { struct plic_priv *priv; }; static int plic_parent_irq __ro_after_init; -static bool plic_cpuhp_setup_done __ro_after_init; +static bool plic_global_setup_done __ro_after_init; static DEFINE_PER_CPU(struct plic_handler, plic_handlers); static int plic_irq_set_type(struct irq_data *d, unsigned int type); @@ -487,10 +487,8 @@ static int plic_probe(struct platform_device *pdev) unsigned long plic_quirks = 0; struct plic_handler *handler; u32 nr_irqs, parent_hwirq; - struct irq_domain *domain; struct plic_priv *priv; irq_hw_number_t hwirq; - bool cpuhp_setup; if (is_of_node(dev->fwnode)) { const struct of_device_id *id; @@ -549,14 +547,6 @@ static int plic_probe(struct platform_device *pdev) continue; } - /* Find parent domain and register chained handler */ - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); - if (!plic_parent_irq && domain) { - plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); - if (plic_parent_irq) - irq_set_chained_handler(plic_parent_irq, plic_handle_irq); - } - /* * When running in M-mode we need to ignore the S-mode handler. * Here we assume it always comes later, but that might be a @@ -597,25 +587,35 @@ done: goto fail_cleanup_contexts; /* - * We can have multiple PLIC instances so setup cpuhp state + * We can have multiple PLIC instances so setup global state * and register syscore operations only once after context * handlers of all online CPUs are initialized. */ - if (!plic_cpuhp_setup_done) { - cpuhp_setup = true; + if (!plic_global_setup_done) { + struct irq_domain *domain; + bool global_setup = true; + for_each_online_cpu(cpu) { handler = per_cpu_ptr(&plic_handlers, cpu); if (!handler->present) { - cpuhp_setup = false; + global_setup = false; break; } } - if (cpuhp_setup) { + + if (global_setup) { + /* Find parent domain and register chained handler */ + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); + if (domain) + plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); + if (plic_parent_irq) + irq_set_chained_handler(plic_parent_irq, plic_handle_irq); + cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, "irqchip/sifive/plic:starting", plic_starting_cpu, plic_dying_cpu); register_syscore_ops(&plic_irq_syscore_ops); - plic_cpuhp_setup_done = true; + plic_global_setup_done = true; } } -- cgit v1.2.3 From 8c219e52ca4d9a67cd6a7074e91bf29b55edc075 Mon Sep 17 00:00:00 2001 From: Gregor Herburger Date: Thu, 30 May 2024 12:19:59 +0200 Subject: gpio: tqmx86: fix typo in Kconfig label Fix description for GPIO_TQMX86 from QTMX86 to TQMx86. Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Signed-off-by: Gregor Herburger Signed-off-by: Matthias Schiffer Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/e0e38c9944ad6d281d9a662a45d289b88edc808e.1717063994.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3dbddec07028..1c28a48915bb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1576,7 +1576,7 @@ config GPIO_TPS68470 are "output only" GPIOs. config GPIO_TQMX86 - tristate "TQ-Systems QTMX86 GPIO" + tristate "TQ-Systems TQMx86 GPIO" depends on MFD_TQMX86 || COMPILE_TEST depends on HAS_IOPORT_MAP select GPIOLIB_IRQCHIP -- cgit v1.2.3 From 9d6a811b522ba558bcb4ec01d12e72a0af8e9f6e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 30 May 2024 12:20:00 +0200 Subject: gpio: tqmx86: introduce shadow register for GPIO output value The TQMx86 GPIO controller uses the same register address for input and output data. Reading the register will always return current inputs rather than the previously set outputs (regardless of the current direction setting). Therefore, using a RMW pattern does not make sense when setting output values. Instead, the previously set output register value needs to be stored as a shadow register. As there is no reliable way to get the current output values from the hardware, also initialize all channels to 0, to ensure that stored and actual output values match. This should usually not have any effect in practise, as the TQMx86 UEFI sets all outputs to 0 during boot. Also prepare for extension of the driver to more than 8 GPIOs by using DECLARE_BITMAP. Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Signed-off-by: Matthias Schiffer Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/d0555933becd45fa92a85675d26e4d59343ddc01.1717063994.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 3a28c1f273c3..b7e2dbbdc4eb 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -6,6 +6,7 @@ * Vadim V.Vlasov */ +#include #include #include #include @@ -38,6 +39,7 @@ struct tqmx86_gpio_data { void __iomem *io_base; int irq; raw_spinlock_t spinlock; + DECLARE_BITMAP(output, TQMX86_NGPIO); u8 irq_type[TQMX86_NGPI]; }; @@ -64,15 +66,10 @@ static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); unsigned long flags; - u8 val; raw_spin_lock_irqsave(&gpio->spinlock, flags); - val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); - if (value) - val |= BIT(offset); - else - val &= ~BIT(offset); - tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); + __assign_bit(offset, gpio->output, value); + tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } @@ -277,6 +274,13 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + /* + * Reading the previous output state is not possible with TQMx86 hardware. + * Initialize all outputs to 0 to have a defined state that matches the + * shadow register. + */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIOD); + chip = &gpio->chip; chip->label = "gpio-tqmx86"; chip->owner = THIS_MODULE; -- cgit v1.2.3 From 08af509efdf8dad08e972b48de0e2c2a7919ea8b Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 30 May 2024 12:20:01 +0200 Subject: gpio: tqmx86: store IRQ trigger type and unmask status separately irq_set_type() should not implicitly unmask the IRQ. All accesses to the interrupt configuration register are moved to a new helper tqmx86_gpio_irq_config(). We also introduce the new rule that accessing irq_type must happen while locked, which will become significant for fixing EDGE_BOTH handling. Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/6aa4f207f77cb58ef64ffb947e91949b0f753ccd.1717063994.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 48 +++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index b7e2dbbdc4eb..7e428c872a25 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -29,15 +29,19 @@ #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ +#define TQMX86_GPII_NONE 0 #define TQMX86_GPII_FALLING BIT(0) #define TQMX86_GPII_RISING BIT(1) #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) #define TQMX86_GPII_BITS 2 +/* Stored in irq_type with GPII bits */ +#define TQMX86_INT_UNMASKED BIT(2) struct tqmx86_gpio_data { struct gpio_chip chip; void __iomem *io_base; int irq; + /* Lock must be held for accessing output and irq_type fields */ raw_spinlock_t spinlock; DECLARE_BITMAP(output, TQMX86_NGPIO); u8 irq_type[TQMX86_NGPI]; @@ -104,21 +108,32 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_OUT; } +static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) + __must_hold(&gpio->spinlock) +{ + u8 type = TQMX86_GPII_NONE, gpiic; + + if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) + type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); + gpiic |= type << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); +} + static void tqmx86_gpio_irq_mask(struct irq_data *data) { unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; - u8 gpiic, mask; - - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); } @@ -128,16 +143,12 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; - u8 gpiic, mask; - - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); + raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] |= TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } @@ -148,7 +159,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) unsigned int offset = (data->hwirq - TQMX86_NGPO); unsigned int edge_type = type & IRQF_TRIGGER_MASK; unsigned long flags; - u8 new_type, gpiic; + u8 new_type; switch (edge_type) { case IRQ_TYPE_EDGE_RISING: @@ -164,13 +175,10 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) return -EINVAL; /* not supported */ } - gpio->irq_type[offset] = new_type; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); - gpiic |= new_type << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] &= ~TQMX86_GPII_MASK; + gpio->irq_type[offset] |= new_type; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); return 0; -- cgit v1.2.3 From 90dd7de4ef7ba584823dfbeba834c2919a4bb55b Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 30 May 2024 12:20:02 +0200 Subject: gpio: tqmx86: fix broken IRQ_TYPE_EDGE_BOTH interrupt type The TQMx86 GPIO controller only supports falling and rising edge triggers, but not both. Fix this by implementing a software both-edge mode that toggles the edge type after every interrupt. Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Co-developed-by: Gregor Herburger Signed-off-by: Gregor Herburger Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/515324f0491c4d44f4ef49f170354aca002d81ef.1717063994.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 7e428c872a25..f2e7e8754d95 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -32,6 +32,10 @@ #define TQMX86_GPII_NONE 0 #define TQMX86_GPII_FALLING BIT(0) #define TQMX86_GPII_RISING BIT(1) +/* Stored in irq_type as a trigger type, but not actually valid as a register + * value, so the name doesn't use "GPII" + */ +#define TQMX86_INT_BOTH (BIT(0) | BIT(1)) #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) #define TQMX86_GPII_BITS 2 /* Stored in irq_type with GPII bits */ @@ -113,9 +117,15 @@ static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) { u8 type = TQMX86_GPII_NONE, gpiic; - if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) + if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + if (type == TQMX86_INT_BOTH) + type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) + ? TQMX86_GPII_FALLING + : TQMX86_GPII_RISING; + } + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); gpiic |= type << (offset * TQMX86_GPII_BITS); @@ -169,7 +179,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) new_type = TQMX86_GPII_FALLING; break; case IRQ_TYPE_EDGE_BOTH: - new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + new_type = TQMX86_INT_BOTH; break; default: return -EINVAL; /* not supported */ @@ -189,8 +199,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned long irq_bits; - int i = 0; + unsigned long irq_bits, flags; + int i; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -199,6 +209,34 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); irq_bits = irq_status; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + /* + * Edge-both triggers are implemented by flipping the edge + * trigger after each interrupt, as the controller only supports + * either rising or falling edge triggers, but not both. + * + * Internally, the TQMx86 GPIO controller has separate status + * registers for rising and falling edge interrupts. GPIIC + * configures which bits from which register are visible in the + * interrupt status register GPIIS and defines what triggers the + * parent IRQ line. Writing to GPIIS always clears both rising + * and falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the edge-both + * trigger in software by first clearing the interrupt and then + * setting the new trigger based on the current GPIO input in + * tqmx86_gpio_irq_config() - even if an edge arrives between + * reading the input and setting the trigger, we will have a new + * interrupt pending. + */ + if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) + tqmx86_gpio_irq_config(gpio, i); + } + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, i + TQMX86_NGPO); -- cgit v1.2.3 From 4a77c3dead97339478c7422eb07bf4bf63577008 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 2 Jun 2024 18:15:25 -0400 Subject: SUNRPC: Fix loop termination condition in gss_free_in_token_pages() The in_token->pages[] array is not NULL terminated. This results in the following KASAN splat: KASAN: maybe wild-memory-access in range [0x04a2013400000008-0x04a201340000000f] Fixes: bafa6b4d95d9 ("SUNRPC: Fix gss_free_in_token_pages()") Reviewed-by: Benjamin Coddington Signed-off-by: Chuck Lever --- net/sunrpc/auth_gss/svcauth_gss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 96ab50eda9c2..73a90ad873fb 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1069,7 +1069,7 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, goto out_denied_free; pages = DIV_ROUND_UP(inlen, PAGE_SIZE); - in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); + in_token->pages = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL); if (!in_token->pages) goto out_denied_free; in_token->page_base = 0; -- cgit v1.2.3 From c6144a21169fe7d0d70f1a0dae6f6301e5918d30 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 3 Jun 2024 22:43:11 +0900 Subject: tomoyo: update project links TOMOYO project has moved to SourceForge.net . Signed-off-by: Tetsuo Handa --- Documentation/admin-guide/LSM/tomoyo.rst | 35 ++++++++++---------------------- MAINTAINERS | 2 +- security/tomoyo/Kconfig | 2 +- security/tomoyo/common.c | 2 +- 4 files changed, 14 insertions(+), 27 deletions(-) diff --git a/Documentation/admin-guide/LSM/tomoyo.rst b/Documentation/admin-guide/LSM/tomoyo.rst index 4bc9c2b4da6f..bdb2c2e2a1b2 100644 --- a/Documentation/admin-guide/LSM/tomoyo.rst +++ b/Documentation/admin-guide/LSM/tomoyo.rst @@ -9,8 +9,8 @@ TOMOYO is a name-based MAC extension (LSM module) for the Linux kernel. LiveCD-based tutorials are available at -http://tomoyo.sourceforge.jp/1.8/ubuntu12.04-live.html -http://tomoyo.sourceforge.jp/1.8/centos6-live.html +https://tomoyo.sourceforge.net/1.8/ubuntu12.04-live.html +https://tomoyo.sourceforge.net/1.8/centos6-live.html Though these tutorials use non-LSM version of TOMOYO, they are useful for you to know what TOMOYO is. @@ -21,45 +21,32 @@ How to enable TOMOYO? Build the kernel with ``CONFIG_SECURITY_TOMOYO=y`` and pass ``security=tomoyo`` on kernel's command line. -Please see http://tomoyo.osdn.jp/2.5/ for details. +Please see https://tomoyo.sourceforge.net/2.6/ for details. Where is documentation? ======================= User <-> Kernel interface documentation is available at -https://tomoyo.osdn.jp/2.5/policy-specification/index.html . +https://tomoyo.sourceforge.net/2.6/policy-specification/index.html . Materials we prepared for seminars and symposiums are available at -https://osdn.jp/projects/tomoyo/docs/?category_id=532&language_id=1 . +https://sourceforge.net/projects/tomoyo/files/docs/ . Below lists are chosen from three aspects. What is TOMOYO? TOMOYO Linux Overview - https://osdn.jp/projects/tomoyo/docs/lca2009-takeda.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lca2009-takeda.pdf TOMOYO Linux: pragmatic and manageable security for Linux - https://osdn.jp/projects/tomoyo/docs/freedomhectaipei-tomoyo.pdf + https://sourceforge.net/projects/tomoyo/files/docs/freedomhectaipei-tomoyo.pdf TOMOYO Linux: A Practical Method to Understand and Protect Your Own Linux Box - https://osdn.jp/projects/tomoyo/docs/PacSec2007-en-no-demo.pdf + https://sourceforge.net/projects/tomoyo/files/docs/PacSec2007-en-no-demo.pdf What can TOMOYO do? Deep inside TOMOYO Linux - https://osdn.jp/projects/tomoyo/docs/lca2009-kumaneko.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lca2009-kumaneko.pdf The role of "pathname based access control" in security. - https://osdn.jp/projects/tomoyo/docs/lfj2008-bof.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lfj2008-bof.pdf History of TOMOYO? Realities of Mainlining - https://osdn.jp/projects/tomoyo/docs/lfj2008.pdf - -What is future plan? -==================== - -We believe that inode based security and name based security are complementary -and both should be used together. But unfortunately, so far, we cannot enable -multiple LSM modules at the same time. We feel sorry that you have to give up -SELinux/SMACK/AppArmor etc. when you want to use TOMOYO. - -We hope that LSM becomes stackable in future. Meanwhile, you can use non-LSM -version of TOMOYO, available at http://tomoyo.osdn.jp/1.8/ . -LSM version of TOMOYO is a subset of non-LSM version of TOMOYO. We are planning -to port non-LSM version's functionalities to LSM versions. + https://sourceforge.net/projects/tomoyo/files/docs/lfj2008.pdf diff --git a/MAINTAINERS b/MAINTAINERS index 8754ac2c259d..4154db83a172 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22679,7 +22679,7 @@ L: tomoyo-users-en@lists.osdn.me (subscribers-only, for users in English) L: tomoyo-dev@lists.osdn.me (subscribers-only, for developers in Japanese) L: tomoyo-users@lists.osdn.me (subscribers-only, for users in Japanese) S: Maintained -W: https://tomoyo.osdn.jp/ +W: https://tomoyo.sourceforge.net/ F: security/tomoyo/ TOPSTAR LAPTOP EXTRAS DRIVER diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index fad75be5f381..1e0dd1a6d0b0 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -10,7 +10,7 @@ config SECURITY_TOMOYO help This selects TOMOYO Linux, pathname-based access control. Required userspace tools and further information may be - found at . + found at . If you are unsure how to answer this question, answer N. config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ea3140d510ec..5c7b059a332a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2787,7 +2787,7 @@ void tomoyo_check_profile(void) else continue; pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n"); - pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n"); + pr_err("Please see https://tomoyo.sourceforge.net/2.6/ for more information.\n"); panic("STOP!"); } tomoyo_read_unlock(idx); -- cgit v1.2.3 From 994af1825a2aa286f4903ff64a1c7378b52defe6 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 25 Apr 2024 13:52:01 +0200 Subject: riscv: fix overlap of allocated page and PTR_ERR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On riscv32, it is possible for the last page in virtual address space (0xfffff000) to be allocated. This page overlaps with PTR_ERR, so that shouldn't happen. There is already some code to ensure memblock won't allocate the last page. However, buddy allocator is left unchecked. Fix this by reserving physical memory that would be mapped at virtual addresses greater than 0xfffff000. Reported-by: Björn Töpel Closes: https://lore.kernel.org/linux-riscv/878r1ibpdn.fsf@all.your.base.are.belong.to.us Fixes: 76d2a0493a17 ("RISC-V: Init and Halt Code") Signed-off-by: Nam Cao Cc: Tested-by: Björn Töpel Reviewed-by: Björn Töpel Reviewed-by: Mike Rapoport (IBM) Link: https://lore.kernel.org/r/20240425115201.3044202-1-namcao@linutronix.de Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/init.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index e3218d65f21d..e3405e4b99af 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -250,18 +250,19 @@ static void __init setup_bootmem(void) kernel_map.va_pa_offset = PAGE_OFFSET - phys_ram_base; /* - * memblock allocator is not aware of the fact that last 4K bytes of - * the addressable memory can not be mapped because of IS_ERR_VALUE - * macro. Make sure that last 4k bytes are not usable by memblock - * if end of dram is equal to maximum addressable memory. For 64-bit - * kernel, this problem can't happen here as the end of the virtual - * address space is occupied by the kernel mapping then this check must - * be done as soon as the kernel mapping base address is determined. + * Reserve physical address space that would be mapped to virtual + * addresses greater than (void *)(-PAGE_SIZE) because: + * - This memory would overlap with ERR_PTR + * - This memory belongs to high memory, which is not supported + * + * This is not applicable to 64-bit kernel, because virtual addresses + * after (void *)(-PAGE_SIZE) are not linearly mapped: they are + * occupied by kernel mapping. Also it is unrealistic for high memory + * to exist on 64-bit platforms. */ if (!IS_ENABLED(CONFIG_64BIT)) { - max_mapped_addr = __pa(~(ulong)0); - if (max_mapped_addr == (phys_ram_end - 1)) - memblock_set_current_limit(max_mapped_addr - 4096); + max_mapped_addr = __va_to_pa_nodebug(-PAGE_SIZE); + memblock_reserve(max_mapped_addr, (phys_addr_t)-max_mapped_addr); } min_low_pfn = PFN_UP(phys_ram_base); -- cgit v1.2.3 From e2c79b4c5c4d83520abb570ca633ded09621c0a6 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 30 May 2024 09:44:51 -0700 Subject: Revert "riscv: mm: accelerate pagefault when badaccess" I accidentally picked up an earlier version of this patch, which had already landed via mm. The patch I picked up contains a bug, which I kept as I thought it was a fix. So let's just revert it. This reverts commit 4c6c0020427a4547845a83f7e4d6085e16c3e24f. Fixes: 4c6c0020427a ("riscv: mm: accelerate pagefault when badaccess") Reviewed-by: Kefeng Wang Reviewed-by: Alexandre Ghiti Link: https://lore.kernel.org/r/20240530164451.21336-1-palmer@rivosinc.com Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/fault.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index b3fcf7d67efb..5224f3733802 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -293,8 +293,8 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(access_error(cause, vma))) { vma_end_read(vma); count_vm_vma_lock_event(VMA_LOCK_SUCCESS); - tsk->thread.bad_cause = SEGV_ACCERR; - bad_area_nosemaphore(regs, code, addr); + tsk->thread.bad_cause = cause; + bad_area_nosemaphore(regs, SEGV_ACCERR, addr); return; } -- cgit v1.2.3 From 44e55f9de9950dba091401898a931fc1a3a99146 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 1 Jun 2024 23:54:44 +0100 Subject: ASoC: codecs: lpass-rx-macro: remove unused struct 'rx_macro_reg_mask_val' 'rx_macro_reg_mask_val' is unused since the original commit af3d54b99764 ("ASoC: codecs: lpass-rx-macro: add support for lpass rx macro"). Remove it. Signed-off-by: Dr. David Alan Gilbert Link: https://msgid.link/r/20240601225446.183505-2-linux@treblig.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index f35187d69cac..dfb7e4c69683 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -463,12 +463,6 @@ static const struct comp_coeff_val comp_coeff_table[HPH_MODE_MAX][COMP_MAX_COEFF }, }; -struct rx_macro_reg_mask_val { - u16 reg; - u8 mask; - u8 val; -}; - enum { INTERP_HPHL, INTERP_HPHR, -- cgit v1.2.3 From 62ccbe8cbe2a1b6911ec47bea8b1510dc1f82dd5 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 1 Jun 2024 23:54:45 +0100 Subject: ASoC: codecs: wm0010: remove unused struct 'wm0010_spi_msg' 'wm0010_spi_msg' has been unused since the original commit e3523e01869d ("ASoC: wm0010: Add initial wm0010 DSP driver"). Remove it. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Charles Keepax Link: https://msgid.link/r/20240601225446.183505-3-linux@treblig.org Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 8f862729a2ca..edd2cb185c42 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -115,14 +115,6 @@ struct wm0010_priv { struct completion boot_completion; }; -struct wm0010_spi_msg { - struct spi_message m; - struct spi_transfer t; - u8 *tx_buf; - u8 *rx_buf; - size_t len; -}; - static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0), }; -- cgit v1.2.3 From 8080dde80a2d3657529c2172471c06cfcd9a228e Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 1 Jun 2024 23:54:46 +0100 Subject: ASoC: codecs: cx2072x: remove unused struct 'cx2072x_eq_ctrl' 'cx2072x_eq_ctrl' has been unused since the original commit a497a4363706 ("ASoC: Add support for Conexant CX2072X CODEC"). Remove it. Signed-off-by: Dr. David Alan Gilbert Link: https://msgid.link/r/20240601225446.183505-4-linux@treblig.org Signed-off-by: Mark Brown --- sound/soc/codecs/cx2072x.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index e8e22b1a1963..8cfec8dcf839 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -63,11 +63,6 @@ static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0); -struct cx2072x_eq_ctrl { - u8 ch; - u8 band; -}; - static const DECLARE_TLV_DB_RANGE(hpf_tlv, 0, 0, TLV_DB_SCALE_ITEM(120, 0, 0), 1, 63, TLV_DB_SCALE_ITEM(30, 30, 0) -- cgit v1.2.3 From 45919c28134519080a85a5fb66d0f65955ef7572 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:14 +0000 Subject: ASoC: simple-card-utils: remove both playback/capture_only check soc-pcm.c :: soc_get_playback_capture() will indicate error if both playback_only / capture_only were true. Thus, graph_util_parse_link_direction() which setup playback_only / capture_only don't need to check it. And, its return value is not used on existing driver. Let's remove it. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87a5kah6gm.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 +- sound/soc/generic/simple-card-utils.c | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 2c2279d082ec..0a6435ac5c5f 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -197,7 +197,7 @@ int graph_util_is_ports0(struct device_node *port); int graph_util_parse_dai(struct device *dev, struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link); -int graph_util_parse_link_direction(struct device_node *np, +void graph_util_parse_link_direction(struct device_node *np, bool *is_playback_only, bool *is_capture_only); #ifdef DEBUG diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 852989611362..bc4d518c35cb 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1140,7 +1140,7 @@ parse_dai_end: } EXPORT_SYMBOL_GPL(graph_util_parse_dai); -int graph_util_parse_link_direction(struct device_node *np, +void graph_util_parse_link_direction(struct device_node *np, bool *playback_only, bool *capture_only) { bool is_playback_only = false; @@ -1149,13 +1149,8 @@ int graph_util_parse_link_direction(struct device_node *np, is_playback_only = of_property_read_bool(np, "playback-only"); is_capture_only = of_property_read_bool(np, "capture-only"); - if (is_playback_only && is_capture_only) - return -EINVAL; - *playback_only = is_playback_only; *capture_only = is_capture_only; - - return 0; } EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); -- cgit v1.2.3 From 72999a1b6663f1ff604e79aea54f168f78e2441a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:20 +0000 Subject: ASoC: audio-graph-card2: add ep_to_port() / port_to_ports() Current audio-graph-card2 is using of_get_parent() to get "port" from "ep", or get "ports" from "port". But it is difficlut to understand, and "ports" might not exist. This patch adds ep_to_port() to get "port" from "ep", and port_to_ports() to get "ports" from "port". "ports" will be NULL if not exist. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/878qzuh6gf.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 46 ++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 81e84095107e..58123fbc2904 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -236,6 +236,18 @@ enum graph_type { #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") +#define ep_to_port(ep) of_get_parent(ep) +static struct device_node *port_to_ports(struct device_node *port) +{ + struct device_node *ports = of_get_parent(port); + + if (!of_node_name_eq(ports, "ports")) { + of_node_put(ports); + return NULL; + } + return ports; +} + static enum graph_type __graph_get_type(struct device_node *lnk) { struct device_node *np, *parent_np; @@ -320,7 +332,7 @@ static int graph_lnk_is_multi(struct device_node *lnk) static struct device_node *graph_get_next_multi_ep(struct device_node **port) { - struct device_node *ports = of_get_parent(*port); + struct device_node *ports = port_to_ports(*port); struct device_node *ep = NULL; struct device_node *rep = NULL; @@ -365,8 +377,8 @@ static const struct snd_soc_ops graph_ops = { static void graph_parse_convert(struct device_node *ep, struct simple_dai_props *props) { - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); struct simple_util_data *adata = &props->adata; if (of_node_name_eq(ports, "ports")) @@ -381,8 +393,8 @@ static void graph_parse_convert(struct device_node *ep, static void graph_parse_mclk_fs(struct device_node *ep, struct simple_dai_props *props) { - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); if (of_node_name_eq(ports, "ports")) of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); @@ -481,8 +493,8 @@ static int __graph_parse_node(struct simple_util_priv *priv, if (!is_cpu && gtype == GRAPH_DPCM) { struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx); struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); - struct device_node *rport = of_get_parent(ep); - struct device_node *rports = of_get_parent(rport); + struct device_node *rport = ep_to_port(ep); + struct device_node *rports = port_to_ports(rport); if (of_node_name_eq(rports, "ports")) snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); @@ -539,11 +551,11 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, */ struct device_node *mcpu_ep = port_to_endpoint(mcpu_port); struct device_node *mcpu_ep_n = mcpu_ep; - struct device_node *mcpu_port_top = of_get_next_child(of_get_parent(mcpu_port), NULL); + struct device_node *mcpu_port_top = of_get_next_child(port_to_ports(mcpu_port), NULL); struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top); struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top); - struct device_node *mcodec_port_top = of_get_parent(mcodec_ep_top); - struct device_node *mcodec_ports = of_get_parent(mcodec_port_top); + struct device_node *mcodec_port_top = ep_to_port(mcodec_ep_top); + struct device_node *mcodec_ports = port_to_ports(mcodec_port_top); int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); int ret = -EINVAL; @@ -566,9 +578,9 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, } mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n); - mcodec_port = of_get_parent(mcodec_ep_n); + mcodec_port = ep_to_port(mcodec_ep_n); - if (mcodec_ports != of_get_parent(mcodec_port)) + if (mcodec_ports != port_to_ports(mcodec_port)) goto mcpu_err; codec_idx = 0; @@ -765,12 +777,12 @@ static void graph_link_init(struct simple_util_priv *priv, if (graph_lnk_is_multi(port)) { of_node_get(port); ep = graph_get_next_multi_ep(&port); - port = of_get_parent(ep); + port = ep_to_port(ep); } else { ep = port_to_endpoint(port); } - ports = of_get_parent(port); + ports = port_to_ports(port); /* * ports { @@ -966,7 +978,7 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, */ of_node_get(lnk); port0 = lnk; - ports = of_get_parent(port0); + ports = port_to_ports(port0); port1 = of_get_next_child(ports, lnk); /* @@ -1098,7 +1110,7 @@ static int graph_counter(struct device_node *lnk) * ignore first lnk part */ if (graph_lnk_is_multi(lnk)) { - struct device_node *ports = of_get_parent(lnk); + struct device_node *ports = port_to_ports(lnk); struct device_node *port = NULL; int cnt = 0; @@ -1195,7 +1207,7 @@ static int graph_count_c2c(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { - struct device_node *ports = of_get_parent(lnk); + struct device_node *ports = port_to_ports(lnk); struct device_node *port0 = lnk; struct device_node *port1 = of_get_next_child(ports, of_node_get(lnk)); struct device_node *ep0 = port_to_endpoint(port0); -- cgit v1.2.3 From 33ae57277ce08b83c65c18a09bf09499de613c01 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:24 +0000 Subject: ASoC: audio-graph-card2: remove ports node name check Current audio-graph-card2 is checking if the node name was "ports" or not when parsing the property. if (of_node_name_eq(ports, "ports")) of_xxx(ports, ...); Now, it is using new port_to_ports() which will be NULL if the node doesn't have "ports", and each of_xxx functions will do nothing if node was NULL. Now we don't need to check ports node name. Let's remove and cleanup it. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/877cfeh6gb.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 15 +++++++-------- sound/soc/generic/simple-card-utils.c | 3 +++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 58123fbc2904..3029e8ebfa22 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -381,8 +381,7 @@ static void graph_parse_convert(struct device_node *ep, struct device_node *ports = port_to_ports(port); struct simple_util_data *adata = &props->adata; - if (of_node_name_eq(ports, "ports")) - simple_util_parse_convert(ports, NULL, adata); + simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); @@ -396,8 +395,7 @@ static void graph_parse_mclk_fs(struct device_node *ep, struct device_node *port = ep_to_port(ep); struct device_node *ports = port_to_ports(port); - if (of_node_name_eq(ports, "ports")) - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); + of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); of_property_read_u32(port, "mclk-fs", &props->mclk_fs); of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); @@ -496,8 +494,7 @@ static int __graph_parse_node(struct simple_util_priv *priv, struct device_node *rport = ep_to_port(ep); struct device_node *rports = port_to_ports(rport); - if (of_node_name_eq(rports, "ports")) - snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); of_node_put(rport); @@ -717,6 +714,9 @@ static void graph_parse_daifmt(struct device_node *node, { unsigned int fmt; + if (!node) + return; + /* * see also above "daifmt" explanation * and samples. @@ -798,8 +798,7 @@ static void graph_link_init(struct simple_util_priv *priv, */ graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */ graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */ - if (of_node_name_eq(ports, "ports")) - graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ + graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ /* * convert bit_frame diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index bc4d518c35cb..a655f20428af 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -60,6 +60,9 @@ void simple_util_parse_convert(struct device_node *np, { char prop[128]; + if (!np) + return; + if (!prefix) prefix = ""; -- cgit v1.2.3 From 844de7eebe97a1c277f8a408457712086c957195 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:28 +0000 Subject: ASoC: audio-graph-card2: expand dai_link property part Current dai_link related property are parsed and enabled only on CPU port node (A)(b)(c). OTOH, Audio Graph Card2 supports many connections like Multi-CPU, DPCM, Codec2Codec today. For example in Multi-CPU case, it will be checked via (X) -> (B) -> (b) process, but (X) / (B) part property is not parsed. >From dai_link related settings point of view, (B) (C) part and Codec port also enabled is more viscerally understandable, and useful. card2 { (X) links = <&snd-cpu (A) &snd-multi (B) &snd-dpcm (C) ...> multi { ports { (B) snd-multi: port { ... }; ... }; }; dpcm { ports { (C) snd-dpcm: port { ... }; ... }; }; codec2codec { ... }; }; cpu_device { ports { (A) snd-cpu: port { ... }; (b) mcpu: port { ... }; (c) dcpu: port { ... }; } }; One note here is that if it was Multi-CPU/Codec case, 1st port only enabled to have property it. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/875xuyh6g7.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 95 ++++++++++++++++++++++------------- sound/soc/generic/simple-card-utils.c | 13 +++-- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 3029e8ebfa22..bd714a6f74e6 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -763,42 +763,55 @@ static void graph_parse_daifmt(struct device_node *node, } static void graph_link_init(struct simple_util_priv *priv, - struct device_node *port, + struct device_node *lnk, + struct device_node *port_cpu, + struct device_node *port_codec, struct link_info *li, int is_cpu_node) { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); - struct device_node *ep; - struct device_node *ports; + struct device_node *ep_cpu, *ep_codec; + struct device_node *ports_cpu, *ports_codec; unsigned int daifmt = 0, daiclk = 0; bool playback_only = 0, capture_only = 0; unsigned int bit_frame = 0; - if (graph_lnk_is_multi(port)) { - of_node_get(port); - ep = graph_get_next_multi_ep(&port); - port = ep_to_port(ep); + of_node_get(port_cpu); + if (graph_lnk_is_multi(port_cpu)) { + ep_cpu = graph_get_next_multi_ep(&port_cpu); + of_node_put(port_cpu); + port_cpu = ep_to_port(ep_cpu); } else { - ep = port_to_endpoint(port); + ep_cpu = port_to_endpoint(port_cpu); } + ports_cpu = port_to_ports(port_cpu); - ports = port_to_ports(port); + of_node_get(port_codec); + if (graph_lnk_is_multi(port_codec)) { + ep_codec = graph_get_next_multi_ep(&port_codec); + of_node_put(port_cpu); + port_codec = ep_to_port(ep_codec); + } else { + ep_codec = port_to_endpoint(port_codec); + } + ports_codec = port_to_ports(port_codec); - /* - * ports { - * (A) - * port { - * (B) - * endpoint { - * (C) - * }; - * }; - * }; - * }; - */ - graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */ - graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */ - graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ + + graph_parse_daifmt(ep_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(ep_codec, &daifmt, &bit_frame); + graph_parse_daifmt(port_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(port_codec, &daifmt, &bit_frame); + graph_parse_daifmt(ports_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(ports_codec, &daifmt, &bit_frame); + graph_parse_daifmt(lnk, &daifmt, &bit_frame); + + graph_util_parse_link_direction(lnk, &playback_only, &capture_only); + graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ports_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(port_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); /* * convert bit_frame @@ -809,16 +822,21 @@ static void graph_link_init(struct simple_util_priv *priv, if (is_cpu_node) daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); - graph_util_parse_link_direction(port, &playback_only, &capture_only); - - dai_link->playback_only = playback_only; - dai_link->capture_only = capture_only; + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; dai_link->dai_fmt = daifmt | daiclk; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) dai_link->ops = priv->ops; + + of_node_put(ports_cpu); + of_node_put(ports_codec); + of_node_put(port_cpu); + of_node_put(port_codec); + of_node_put(ep_cpu); + of_node_put(ep_codec); } int audio_graph2_link_normal(struct simple_util_priv *priv, @@ -846,7 +864,7 @@ int audio_graph2_link_normal(struct simple_util_priv *priv, if (ret < 0) goto err; - graph_link_init(priv, cpu_port, li, 1); + graph_link_init(priv, lnk, cpu_port, codec_port, li, 1); err: of_node_put(codec_port); of_node_put(cpu_ep); @@ -861,13 +879,16 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, { struct device_node *ep = port_to_endpoint(lnk); struct device_node *rep = of_graph_get_remote_endpoint(ep); - struct device_node *rport = of_graph_get_remote_port(ep); + struct device_node *cpu_port = NULL; + struct device_node *codec_port = NULL; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); int is_cpu = graph_util_is_ports0(lnk); int ret; if (is_cpu) { + cpu_port = of_graph_get_remote_port(ep); /* rport */ + /* * dpcm { * // Front-End @@ -895,10 +916,13 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; - ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1); + ret = graph_parse_node(priv, GRAPH_DPCM, cpu_port, li, 1); if (ret) goto err; + } else { + codec_port = of_graph_get_remote_port(ep); /* rport */ + /* * dpcm { * // Front-End @@ -928,7 +952,7 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup; - ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0); + ret = graph_parse_node(priv, GRAPH_DPCM, codec_port, li, 0); if (ret < 0) goto err; } @@ -938,11 +962,12 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, snd_soc_dai_link_set_capabilities(dai_link); - graph_link_init(priv, rport, li, is_cpu); + graph_link_init(priv, lnk, cpu_port, codec_port, li, is_cpu); err: of_node_put(ep); of_node_put(rep); - of_node_put(rport); + of_node_put(cpu_port); + of_node_put(codec_port); return ret; } @@ -1030,7 +1055,7 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, if (ret < 0) goto err2; - graph_link_init(priv, codec0_port, li, 1); + graph_link_init(priv, lnk, codec0_port, codec1_port, li, 1); err2: of_node_put(ep0); of_node_put(ep1); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a655f20428af..dcd0569157ce 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1146,14 +1146,13 @@ EXPORT_SYMBOL_GPL(graph_util_parse_dai); void graph_util_parse_link_direction(struct device_node *np, bool *playback_only, bool *capture_only) { - bool is_playback_only = false; - bool is_capture_only = false; + bool is_playback_only = of_property_read_bool(np, "playback-only"); + bool is_capture_only = of_property_read_bool(np, "capture-only"); - is_playback_only = of_property_read_bool(np, "playback-only"); - is_capture_only = of_property_read_bool(np, "capture-only"); - - *playback_only = is_playback_only; - *capture_only = is_capture_only; + if (is_playback_only) + *playback_only = is_playback_only; + if (is_capture_only) + *capture_only = is_capture_only; } EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); -- cgit v1.2.3 From f2d7e85962baf410b1bbbb4cf23a1ca59261ef76 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:32 +0000 Subject: ASoC: audio-graph-card2: merge graph_parse_mclk_fs() into graph_link_init() Now graph_link_init() is parsing dai_link related property. Let's handle mclk_fs on it, too. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/874jaih6g3.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index bd714a6f74e6..8eea81888758 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -389,20 +389,6 @@ static void graph_parse_convert(struct device_node *ep, of_node_put(ports); } -static void graph_parse_mclk_fs(struct device_node *ep, - struct simple_dai_props *props) -{ - struct device_node *port = ep_to_port(ep); - struct device_node *ports = port_to_ports(port); - - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); - of_property_read_u32(port, "mclk-fs", &props->mclk_fs); - of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); - - of_node_put(port); - of_node_put(ports); -} - static int __graph_parse_node(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *ep, @@ -424,8 +410,6 @@ static int __graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, idx); } - graph_parse_mclk_fs(ep, dai_props); - ret = graph_util_parse_dai(dev, ep, dlc, &is_single_links); if (ret < 0) return ret; @@ -770,6 +754,7 @@ static void graph_link_init(struct simple_util_priv *priv, int is_cpu_node) { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *ep_cpu, *ep_codec; struct device_node *ports_cpu, *ports_codec; unsigned int daifmt = 0, daiclk = 0; @@ -813,6 +798,14 @@ static void graph_link_init(struct simple_util_priv *priv, graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); + of_property_read_u32(lnk, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + /* * convert bit_frame * We need to flip clock_provider if it was CPU node, -- cgit v1.2.3 From df23fcd56bb75ab522350bd8cb52bde9067aea45 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:36 +0000 Subject: ASoC: audio-graph-card: add ep_to_port() / port_to_ports() Current audio-graph-card is using of_get_parent() to get "port" from "ep", or get "ports" from "port". But it is difficlut to understand, and "ports" might not exist. This patch adds ep_to_port() to get "port" from "ep", and port_to_ports() to get "ports" from "port". "ports" will be NULL if not exist. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/8734q2h6fz.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 83e3ba773fbd..552b94908cf7 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -19,6 +19,18 @@ #define DPCM_SELECTABLE 1 +#define ep_to_port(ep) of_get_parent(ep) +static struct device_node *port_to_ports(struct device_node *port) +{ + struct device_node *ports = of_get_parent(port); + + if (!of_node_name_eq(ports, "ports")) { + of_node_put(ports); + return NULL; + } + return ports; +} + static int graph_outdrv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -68,8 +80,8 @@ static void graph_parse_convert(struct device *dev, struct simple_util_data *adata) { struct device_node *top = dev->of_node; - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); struct device_node *node = of_graph_get_port_parent(ep); simple_util_parse_convert(top, NULL, adata); @@ -87,8 +99,8 @@ static void graph_parse_mclk_fs(struct device_node *top, struct device_node *ep, struct simple_dai_props *props) { - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); of_property_read_u32(top, "mclk-fs", &props->mclk_fs); if (of_node_name_eq(ports, "ports")) @@ -231,8 +243,8 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, "be.%pOFP.%s", codecs->of_node, codecs->dai_name); /* check "prefix" from top node */ - port = of_get_parent(ep); - ports = of_get_parent(port); + port = ep_to_port(ep); + ports = port_to_ports(port); snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix"); if (of_node_name_eq(ports, "ports")) @@ -350,7 +362,7 @@ static int __graph_for_each_link(struct simple_util_priv *priv, /* get codec */ codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_get_parent(codec_ep); + codec_port = ep_to_port(codec_ep); /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); -- cgit v1.2.3 From 84c9601a92b755f869ac811607402e5b2162c225 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:40 +0000 Subject: ASoC: audio-graph-card: remove ports node name check Current audio-graph-card is checking if the node name was "ports" or not when parsing the property. if (of_node_name_eq(ports, "ports")) of_xxx(ports, ...); Now, it is using new port_to_ports() which will be NULL if the node doesn't have "ports", and each of_xxx functions will do nothing if node was NULL. Now we don't need to check ports node name. Let's remove and cleanup it. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/871q5mh6fv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 552b94908cf7..c60f00146859 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -85,8 +85,7 @@ static void graph_parse_convert(struct device *dev, struct device_node *node = of_graph_get_port_parent(ep); simple_util_parse_convert(top, NULL, adata); - if (of_node_name_eq(ports, "ports")) - simple_util_parse_convert(ports, NULL, adata); + simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); @@ -103,8 +102,7 @@ static void graph_parse_mclk_fs(struct device_node *top, struct device_node *ports = port_to_ports(port); of_property_read_u32(top, "mclk-fs", &props->mclk_fs); - if (of_node_name_eq(ports, "ports")) - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); + of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); of_property_read_u32(port, "mclk-fs", &props->mclk_fs); of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); @@ -245,12 +243,9 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, /* check "prefix" from top node */ port = ep_to_port(ep); ports = port_to_ports(port); - snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, - "prefix"); - if (of_node_name_eq(ports, "ports")) - snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix"); - snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, - "prefix"); + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, "prefix"); of_node_put(ports); of_node_put(port); -- cgit v1.2.3 From f23bac6e6913eed9eb831b4893255ea862d40ea5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:44 +0000 Subject: ASoC: audio-graph-card: enable playback/capture_only property Audio Graph Card2 is supporting playback/capture_only property, let's follow Audio Graph Card, too. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87zfsafrvb.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index c60f00146859..6a19e40d2c4a 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -149,26 +149,44 @@ static int graph_parse_node(struct simple_util_priv *priv, } static int graph_link_init(struct simple_util_priv *priv, - struct device_node *cpu_ep, - struct device_node *codec_ep, + struct device_node *ep_cpu, + struct device_node *ep_codec, struct link_info *li, char *name) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct device_node *port_cpu = ep_to_port(ep_cpu); + struct device_node *port_codec = ep_to_port(ep_codec); + bool playback_only = 0, capture_only = 0; int ret; - ret = simple_util_parse_daifmt(dev, cpu_ep, codec_ep, + ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec, NULL, &dai_link->dai_fmt); if (ret < 0) - return ret; + goto init_end; + + graph_util_parse_link_direction(top, &playback_only, &capture_only); + graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(port_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); + + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) dai_link->ops = priv->ops; - return simple_util_set_dailink_name(dev, dai_link, name); + ret = simple_util_set_dailink_name(dev, dai_link, name); +init_end: + of_node_put(port_cpu); + of_node_put(port_codec); + + return ret; } static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, -- cgit v1.2.3 From a0174c88386b48bea7c35bc5a927f7057cb45d38 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:48 +0000 Subject: ASoC: audio-graph-card: merge graph_parse_mclk_fs() into graph_link_init() Now graph_link_init() is parsing dai_link related property. Let's handle mclk_fs on it, too. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87y17ufrv8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 6a19e40d2c4a..7b981aa8690a 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -94,29 +94,12 @@ static void graph_parse_convert(struct device *dev, of_node_put(node); } -static void graph_parse_mclk_fs(struct device_node *top, - struct device_node *ep, - struct simple_dai_props *props) -{ - struct device_node *port = ep_to_port(ep); - struct device_node *ports = port_to_ports(port); - - of_property_read_u32(top, "mclk-fs", &props->mclk_fs); - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); - of_property_read_u32(port, "mclk-fs", &props->mclk_fs); - of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); - - of_node_put(port); - of_node_put(ports); -} - static int graph_parse_node(struct simple_util_priv *priv, struct device_node *ep, struct link_info *li, int *cpu) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct snd_soc_dai_link_component *dlc; @@ -131,8 +114,6 @@ static int graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - graph_parse_mclk_fs(top, ep, dai_props); - ret = graph_util_parse_dai(dev, ep, dlc, cpu); if (ret < 0) return ret; @@ -157,8 +138,11 @@ static int graph_link_init(struct simple_util_priv *priv, struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *port_cpu = ep_to_port(ep_cpu); struct device_node *port_codec = ep_to_port(ep_codec); + struct device_node *ports_cpu = port_to_ports(port_cpu); + struct device_node *ports_codec = port_to_ports(port_codec); bool playback_only = 0, capture_only = 0; int ret; @@ -173,6 +157,14 @@ static int graph_link_init(struct simple_util_priv *priv, graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; @@ -183,6 +175,8 @@ static int graph_link_init(struct simple_util_priv *priv, ret = simple_util_set_dailink_name(dev, dai_link, name); init_end: + of_node_put(ports_cpu); + of_node_put(ports_codec); of_node_put(port_cpu); of_node_put(port_codec); -- cgit v1.2.3 From 42d37e8de8f2d121481a65e6a3e10f6387c0ad4c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:51 +0000 Subject: ASoC: simple-audio-card: enable playback/capture_only property Audio Graph Card2 is supporting playback/capture_only property, let's follow Simple Audio Card, too. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87wmnefrv4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 9c79ff6a568f..e436e8fcfa0f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -187,24 +187,39 @@ static int simple_parse_node(struct simple_util_priv *priv, } static int simple_link_init(struct simple_util_priv *priv, - struct device_node *node, + struct device_node *cpu, struct device_node *codec, struct link_info *li, char *prefix, char *name) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct device_node *node = of_get_parent(cpu); + bool playback_only = 0, capture_only = 0; int ret; ret = simple_util_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) - return 0; + goto init_end; + + graph_util_parse_link_direction(top, &playback_only, &capture_only); + graph_util_parse_link_direction(node, &playback_only, &capture_only); + graph_util_parse_link_direction(cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(codec, &playback_only, &capture_only); + + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; dai_link->init = simple_util_dai_init; dai_link->ops = &simple_ops; - return simple_util_set_dailink_name(dev, dai_link, name); + ret = simple_util_set_dailink_name(dev, dai_link, name); +init_end: + of_node_put(node); + + return ret; } static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, @@ -278,7 +293,7 @@ static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, snd_soc_dai_link_set_capabilities(dai_link); - ret = simple_link_init(priv, node, codec, li, prefix, dai_name); + ret = simple_link_init(priv, np, codec, li, prefix, dai_name); out_put_node: li->link++; @@ -336,7 +351,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv, simple_util_canonicalize_cpu(cpus, single_cpu); simple_util_canonicalize_platform(platforms, cpus); - ret = simple_link_init(priv, node, codec, li, prefix, dai_name); + ret = simple_link_init(priv, cpu, codec, li, prefix, dai_name); dai_link_of_err: of_node_put(plat); -- cgit v1.2.3 From c4cfe1136d6edf8970ccdd944b7f86f7aa3edb77 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2024 05:05:55 +0000 Subject: ASoC: simple-audio-card: merge simple_parse_mclk_fs() into simple_link_init() Now graph_link_init() is parsing dai_link related property. Let's handle mclk_fs on it, too. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87v82yfrv0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index e436e8fcfa0f..2de5e6efe947 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -129,24 +129,6 @@ static void simple_parse_convert(struct device *dev, of_node_put(node); } -static void simple_parse_mclk_fs(struct device_node *top, - struct device_node *np, - struct simple_dai_props *props, - char *prefix) -{ - struct device_node *node = of_get_parent(np); - char prop[128]; - - snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX); - of_property_read_u32(top, prop, &props->mclk_fs); - - snprintf(prop, sizeof(prop), "%smclk-fs", prefix); - of_property_read_u32(node, prop, &props->mclk_fs); - of_property_read_u32(np, prop, &props->mclk_fs); - - of_node_put(node); -} - static int simple_parse_node(struct simple_util_priv *priv, struct device_node *np, struct link_info *li, @@ -154,7 +136,6 @@ static int simple_parse_node(struct simple_util_priv *priv, int *cpu) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct snd_soc_dai_link_component *dlc; @@ -169,8 +150,6 @@ static int simple_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - simple_parse_mclk_fs(top, np, dai_props, prefix); - ret = simple_parse_dai(dev, np, dlc, cpu); if (ret) return ret; @@ -195,6 +174,7 @@ static int simple_link_init(struct simple_util_priv *priv, struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *node = of_get_parent(cpu); bool playback_only = 0, capture_only = 0; int ret; @@ -209,6 +189,15 @@ static int simple_link_init(struct simple_util_priv *priv, graph_util_parse_link_direction(cpu, &playback_only, &capture_only); graph_util_parse_link_direction(codec, &playback_only, &capture_only); + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec, PREFIX "mclk-fs", &dai_props->mclk_fs); + dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; -- cgit v1.2.3 From fe243a546bcbc405abb7cc5cf26852609cf4e2cc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 14:50:49 +0200 Subject: ASoC: codecs: wcd939x: Unify define used for MIC bias VOUT registers All four microphone bias registers have similar layout: VOUT control mask is exactly the same. Use one define, just like older driver wcd938x is doing, to make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523125049.43713-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 17 ++++++----------- sound/soc/codecs/wcd939x.h | 5 +---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 770bc2f35ba7..72d8a6a35052 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -1933,7 +1933,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, int req_volt, int micb_num) { struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); - unsigned int micb_vout_ctl_field; unsigned int micb_reg, cur_vout_ctl, micb_en; int req_vout_ctl; int ret = 0; @@ -1941,19 +1940,15 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, switch (micb_num) { case MIC_BIAS_1: micb_reg = WCD939X_ANA_MICB1; - micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; break; case MIC_BIAS_2: micb_reg = WCD939X_ANA_MICB2; - micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; break; case MIC_BIAS_3: micb_reg = WCD939X_ANA_MICB3; - micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; break; case MIC_BIAS_4: micb_reg = WCD939X_ANA_MICB4; - micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; break; default: return -EINVAL; @@ -1971,7 +1966,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, micb_en = snd_soc_component_read_field(component, micb_reg, WCD939X_MICB_ENABLE); cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, - micb_vout_ctl_field); + WCD939X_MICB_VOUT_CTL); req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt); if (req_vout_ctl < 0) { @@ -1994,7 +1989,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, MICB_BIAS_PULL_DOWN); snd_soc_component_write_field(component, micb_reg, - micb_vout_ctl_field, req_vout_ctl); + WCD939X_MICB_VOUT_CTL, req_vout_ctl); if (micb_en == MICB_BIAS_ENABLE) { snd_soc_component_write_field(component, micb_reg, @@ -2912,13 +2907,13 @@ static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x) return -EINVAL; regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1, - WCD939X_MICB1_VOUT_CTL, vout_ctl_1); + WCD939X_MICB_VOUT_CTL, vout_ctl_1); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2, - WCD939X_MICB2_VOUT_CTL, vout_ctl_2); + WCD939X_MICB_VOUT_CTL, vout_ctl_2); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3, - WCD939X_MICB3_VOUT_CTL, vout_ctl_3); + WCD939X_MICB_VOUT_CTL, vout_ctl_3); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4, - WCD939X_MICB4_VOUT_CTL, vout_ctl_4); + WCD939X_MICB_VOUT_CTL, vout_ctl_4); return 0; } diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 383a79316439..756f497a337c 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -92,9 +92,8 @@ #define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2) #define WCD939X_ANA_MICB1 (0x3022) #define WCD939X_MICB_ENABLE GENMASK(7, 6) -#define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0) +#define WCD939X_MICB_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB2 (0x3023) -#define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB2_RAMP (0x3024) #define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7) #define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE BIT(6) @@ -102,9 +101,7 @@ #define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2) #define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0) #define WCD939X_ANA_MICB3 (0x3025) -#define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0) #define WCD939X_ANA_MICB4 (0x3026) -#define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0) #define WCD939X_BIAS_CTL (0x3028) #define WCD939X_BIAS_VBG_FINE_ADJ (0x3029) #define WCD939X_LDOL_VDDCX_ADJUST (0x3040) -- cgit v1.2.3 From d8e5fa784867c4cd400afecc43aec57c51c10dc7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 May 2024 14:48:08 +0200 Subject: ASoC: codecs: wcd938x: Drop no-op ADC2_BCS Disable Switch The "ADC2_BCS Disable Switch" control does basically nothing: sets field in driver's private data structure which is never read again to configure hardware. Drop the control as it has no effect. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240523124808.42923-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 9e2070356b84..73387152ce02 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -206,7 +206,6 @@ struct wcd938x_priv { bool comp1_enable; bool comp2_enable; bool ldoh; - bool bcs_dis; }; static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); @@ -1650,31 +1649,6 @@ static int wcd938x_ldoh_put(struct snd_kcontrol *kcontrol, return 1; } -static int wcd938x_bcs_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - - ucontrol->value.integer.value[0] = wcd938x->bcs_dis; - - return 0; -} - -static int wcd938x_bcs_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - - if (wcd938x->bcs_dis == ucontrol->value.integer.value[0]) - return 0; - - wcd938x->bcs_dis = ucontrol->value.integer.value[0]; - - return 1; -} - static const char * const tx_mode_mux_text_wcd9380[] = { "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", }; @@ -2695,8 +2669,6 @@ static const struct snd_kcontrol_new wcd938x_snd_controls[] = { wcd938x_get_swr_port, wcd938x_set_swr_port), SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, wcd938x_ldoh_get, wcd938x_ldoh_put), - SOC_SINGLE_EXT("ADC2_BCS Disable Switch", SND_SOC_NOPM, 0, 1, 0, - wcd938x_bcs_get, wcd938x_bcs_put), SOC_SINGLE_TLV("ADC1 Volume", WCD938X_ANA_TX_CH1, 0, 20, 0, analog_gain), SOC_SINGLE_TLV("ADC2 Volume", WCD938X_ANA_TX_CH2, 0, 20, 0, analog_gain), -- cgit v1.2.3 From ccdc13ab26ca248fdc53179a0c7f588ab2e1c8d8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Jun 2024 10:25:44 +0300 Subject: ASoC: SOF: Intel: pci-tgl: Align ADL-N sof_dev_desc struct name to convention Follow the convention already in use by other platforms on naming the sof_dev_desc struct by dropping the underscore between adl and n Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240603072544.5215-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-tgl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index ebe1a7d16689..01db2e720b44 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -183,7 +183,7 @@ static const struct sof_dev_desc adl_desc = { .ops_free = hda_ops_free, }; -static const struct sof_dev_desc adl_n_desc = { +static const struct sof_dev_desc adln_desc = { .machines = snd_soc_acpi_intel_adl_machines, .alt_machines = snd_soc_acpi_intel_adl_sdw_machines, .use_acpi_target_states = true, @@ -298,7 +298,7 @@ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &rpl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &rpl_desc) }, - { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adln_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); -- cgit v1.2.3 From 2317dc2c22cc353b699c7d1db47b2fe91f54055c Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 29 May 2024 12:19:01 +0200 Subject: bpf, devmap: Remove unnecessary if check in for loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iterator variable dst cannot be NULL and the if check can be removed. Remove it and fix the following Coccinelle/coccicheck warning reported by itnull.cocci: ERROR: iterator variable bound on line 762 cannot be NULL Signed-off-by: Thorsten Blum Signed-off-by: Daniel Borkmann Reviewed-by: Toke Høiland-Jørgensen Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/20240529101900.103913-2-thorsten.blum@toblux.com --- kernel/bpf/devmap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 4e2cdbb5629f..7f3b34452243 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -760,9 +760,6 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, for (i = 0; i < dtab->n_buckets; i++) { head = dev_map_index_hash(dtab, i); hlist_for_each_entry_safe(dst, next, head, index_hlist) { - if (!dst) - continue; - if (is_ifindex_excluded(excluded_devices, num_excluded, dst->dev->ifindex)) continue; -- cgit v1.2.3 From 93c1800b3799f17375989b0daf76497dd3e80922 Mon Sep 17 00:00:00 2001 From: David Kaplan Date: Sun, 2 Jun 2024 13:19:09 -0500 Subject: x86/kexec: Fix bug with call depth tracking The call to cc_platform_has() triggers a fault and system crash if call depth tracking is active because the GS segment has been reset by load_segments() and GS_BASE is now 0 but call depth tracking uses per-CPU variables to operate. Call cc_platform_has() earlier in the function when GS is still valid. [ bp: Massage. ] Fixes: 5d8213864ade ("x86/retbleed: Add SKL return thunk") Signed-off-by: David Kaplan Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tom Lendacky Cc: Link: https://lore.kernel.org/r/20240603083036.637-1-bp@kernel.org --- arch/x86/kernel/machine_kexec_64.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index b180d8e497c3..cc0f7f70b17b 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -295,8 +295,15 @@ void machine_kexec_cleanup(struct kimage *image) void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; - void *control_page; + unsigned int host_mem_enc_active; int save_ftrace_enabled; + void *control_page; + + /* + * This must be done before load_segments() since if call depth tracking + * is used then GS must be valid to make any function calls. + */ + host_mem_enc_active = cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT); #ifdef CONFIG_KEXEC_JUMP if (image->preserve_context) @@ -358,7 +365,7 @@ void machine_kexec(struct kimage *image) (unsigned long)page_list, image->start, image->preserve_context, - cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)); + host_mem_enc_active); #ifdef CONFIG_KEXEC_JUMP if (image->preserve_context) -- cgit v1.2.3 From 1e24c31351787e24b7eebe84866bd55fd62a0aef Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 31 May 2024 16:00:04 -0700 Subject: cpufreq: intel_pstate: Fix unchecked HWP MSR access Fix unchecked MSR access error for processors with no HWP support. On such processors, maximum frequency can be changed by the system firmware using ACPI event ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED. This results in accessing HWP MSR 0x771. Call Trace: generic_exec_single+0x58/0x120 smp_call_function_single+0xbf/0x110 rdmsrl_on_cpu+0x46/0x60 intel_pstate_get_hwp_cap+0x1b/0x70 intel_pstate_update_limits+0x2a/0x60 acpi_processor_notify+0xb7/0x140 acpi_ev_notify_dispatch+0x3b/0x60 HWP MSR 0x771 can be only read on a CPU which supports HWP and enabled. Hence intel_pstate_get_hwp_cap() can only be called when hwp_active is true. Reported-by: Sebastian Andrzej Siewior Closes: https://lore.kernel.org/linux-pm/20240529155740.Hq2Hw7be@linutronix.de/ Fixes: e8217b4bece3 ("cpufreq: intel_pstate: Update the maximum CPU frequency consistently") Tested-by: Sebastian Andrzej Siewior Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 4b986c044741..65d3f79104bd 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1153,7 +1153,8 @@ static void intel_pstate_update_policies(void) static void __intel_pstate_update_max_freq(struct cpudata *cpudata, struct cpufreq_policy *policy) { - intel_pstate_get_hwp_cap(cpudata); + if (hwp_active) + intel_pstate_get_hwp_cap(cpudata); policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; -- cgit v1.2.3 From fc1277335ffa0d180c76ddccf5fe27fc75674e67 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 29 May 2024 16:40:01 +0800 Subject: ASoC: dt-bindings: fsl,xcvr: Add compatible string for i.MX95 Add compatible string "fsl,imx95-xcvr" for i.MX95 platform. The difference between each platform is in below table. +---------+--------+----------+--------+ | SOC | PHY | eARC/ARC | SPDIF | +---------+--------+----------+--------+ | i.MX8MP | V1 | Yes | Yes | +---------+--------+----------+--------+ | i.MX93 | N/A | N/A | Yes | +---------+--------+----------+--------+ | i.MX95 | V2 | N/A | Yes | +---------+--------+----------+--------+ On i.MX95, there are two PLL clock sources, they are the parent clocks of the XCVR root clock. one is for 8kHz series rates, named as 'pll8k', another one is for 11kHz series rates, named as 'pll11k'. They are optional clocks, if there are such clocks, then the driver can switch between them to support more accurate sample rates. As 'pll8k' and 'pll11k' are optional, then add 'minItems: 4' for clocks and clock-names properties. On i.MX95, the 'interrupts' configuration has the same constraint as i.MX93. Only on i.MX8MP, the 'resets' is required, but for i.MX95 and i.MX93 there is no such hardware setting. Signed-off-by: Shengjiu Wang Reviewed-by: Rob Herring (Arm) Link: https://msgid.link/r/1716972002-2315-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,xcvr.yaml | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml index 0eb0c1ba8710..197ae8ba9c30 100644 --- a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml @@ -22,6 +22,7 @@ properties: enum: - fsl,imx8mp-xcvr - fsl,imx93-xcvr + - fsl,imx95-xcvr reg: items: @@ -49,6 +50,9 @@ properties: - description: PHY clock - description: SPBA clock - description: PLL clock + - description: PLL clock source for 8kHz series + - description: PLL clock source for 11kHz series + minItems: 4 clock-names: items: @@ -56,6 +60,9 @@ properties: - const: phy - const: spba - const: pll_ipg + - const: pll8k + - const: pll11k + minItems: 4 dmas: items: @@ -79,15 +86,24 @@ required: - clock-names - dmas - dma-names - - resets allOf: + - if: + properties: + compatible: + contains: + const: fsl,imx8mp-xcvr + then: + required: + - resets + - if: properties: compatible: contains: enum: - fsl,imx93-xcvr + - fsl,imx95-xcvr then: properties: interrupts: @@ -98,6 +114,20 @@ allOf: interrupts: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8mp-xcvr + - fsl,imx93-xcvr + then: + properties: + clocks: + maxItems: 4 + clock-names: + maxItems: 4 + additionalProperties: false examples: -- cgit v1.2.3 From f13b349e3c70320ef5a86edfc888a6feb612abb0 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 29 May 2024 16:40:02 +0800 Subject: ASoC: fsl_xcvr: Add support for i.MX95 platform On i.MX95, the XCVR uses a new PLL in the PHY, which is General Purpose (GP) PLL. Add GP PLL configuration support in the driver and add the 'pll_ver' flag to distinguish different PLL on different platforms. The XCVR also use PHY but limited for SPDIF only case Add 'use_phy' flag to distinguish these platforms. When there are 'pll8k' and 'pll11k' clock existing, the clock source of 'phy_clk' can be changed for different sample rate requirement. Signed-off-by: Shengjiu Wang Reviewed-by: Chancel Liu Link: https://msgid.link/r/1716972002-2315-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 1 + sound/soc/fsl/fsl_xcvr.c | 128 +++++++++++++++++++++++++++++++---------------- sound/soc/fsl/fsl_xcvr.h | 91 +++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 43 deletions(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 270726c134b3..9a371d4496c2 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -103,6 +103,7 @@ config SND_SOC_FSL_XCVR select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_FSL_UTILS help Say Y if you want to add Audio Transceiver (XCVR) support for NXP iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC, diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 5472ace23d82..337da46a2f90 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -15,14 +15,22 @@ #include #include "fsl_xcvr.h" +#include "fsl_utils.h" #include "imx-pcm.h" #define FSL_XCVR_CAPDS_SIZE 256 +enum fsl_xcvr_pll_verison { + PLL_MX8MP, + PLL_MX95, +}; + struct fsl_xcvr_soc_data { const char *fw_name; bool spdif_only; bool use_edma; + bool use_phy; + enum fsl_xcvr_pll_verison pll_ver; }; struct fsl_xcvr { @@ -33,6 +41,8 @@ struct fsl_xcvr { struct clk *pll_ipg_clk; struct clk *phy_clk; struct clk *spba_clk; + struct clk *pll8k_clk; + struct clk *pll11k_clk; struct reset_control *reset; u8 streams; u32 mode; @@ -262,10 +272,10 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) { struct device *dev = &xcvr->pdev->dev; - u32 i, div = 0, log2; + u32 i, div = 0, log2, val; int ret; - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) return 0; for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) { @@ -288,45 +298,62 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) return ret; } - /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET, - FSL_XCVR_PLL_BANDGAP_EN_VBG, 0); - - /* PLL: CTRL0: DIV_INTEGER */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0); - /* PLL: NUMERATOR: MFN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0); - /* PLL: DENOMINATOR: MFD */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0); - /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0); - udelay(25); - /* PLL: CTRL0: Clear Hold Ring Off */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR, - FSL_XCVR_PLL_CTRL0_HROFF, 0); - udelay(100); - if (tx) { /* TX is enabled for SPDIF only */ - /* PLL: POSTDIV: PDIV0 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 0), 0); - /* PLL: CTRL_SET: CLKMUX0_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM0_EN, 0); - } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ - /* PLL: POSTDIV: PDIV1 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 1), 0); - /* PLL: CTRL_SET: CLKMUX1_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM1_EN, 0); - } else { /* SPDIF / ARC RX */ - /* PLL: POSTDIV: PDIV2 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 2), 0); - /* PLL: CTRL_SET: CLKMUX2_EN */ + switch (xcvr->soc_data->pll_ver) { + case PLL_MX8MP: + /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET, + FSL_XCVR_PLL_BANDGAP_EN_VBG, 0); + + /* PLL: CTRL0: DIV_INTEGER */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0); + /* PLL: NUMERATOR: MFN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0); + /* PLL: DENOMINATOR: MFD */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0); + /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0); + udelay(25); + /* PLL: CTRL0: Clear Hold Ring Off */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR, + FSL_XCVR_PLL_CTRL0_HROFF, 0); + udelay(100); + if (tx) { /* TX is enabled for SPDIF only */ + /* PLL: POSTDIV: PDIV0 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 0), 0); + /* PLL: CTRL_SET: CLKMUX0_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM0_EN, 0); + } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ + /* PLL: POSTDIV: PDIV1 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 1), 0); + /* PLL: CTRL_SET: CLKMUX1_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM1_EN, 0); + } else { /* SPDIF / ARC RX */ + /* PLL: POSTDIV: PDIV2 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 2), 0); + /* PLL: CTRL_SET: CLKMUX2_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + } + break; + case PLL_MX95: + val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DIV, val, 0); + val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_NUMERATOR, val, 0); + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DENOMINATOR, + fsl_xcvr_pll_cfg[i].mfd, 0); + val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_CTRL, val, 0); + break; + default: + dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver); + return -EINVAL; } if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */ @@ -362,6 +389,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) freq = xcvr->soc_data->spdif_only ? freq / 5 : freq; clk_disable_unprepare(xcvr->phy_clk); + fsl_asoc_reparent_pll_clocks(dev, xcvr->phy_clk, + xcvr->pll8k_clk, xcvr->pll11k_clk, freq); ret = clk_set_rate(xcvr->phy_clk, freq); if (ret < 0) { dev_err(dev, "Error while setting AUD PLL rate: %d\n", ret); @@ -373,7 +402,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) return ret; } - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) return 0; /* Release AI interface from reset */ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, @@ -1017,7 +1046,7 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) || reg > FSL_XCVR_TX_DPTH_BCRR) return false; @@ -1090,7 +1119,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) return false; switch (reg) { @@ -1234,6 +1263,8 @@ static irqreturn_t irq0_isr(int irq, void *devid) static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = { .fw_name = "imx/xcvr/xcvr-imx8mp.bin", + .use_phy = true, + .pll_ver = PLL_MX8MP, }; static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { @@ -1241,9 +1272,17 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { .use_edma = true, }; +static const struct fsl_xcvr_soc_data fsl_xcvr_imx95_data = { + .spdif_only = true, + .use_phy = true, + .use_edma = true, + .pll_ver = PLL_MX95, +}; + static const struct of_device_id fsl_xcvr_dt_ids[] = { { .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data }, { .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data}, + { .compatible = "fsl,imx95-xcvr", .data = &fsl_xcvr_imx95_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids); @@ -1287,6 +1326,9 @@ static int fsl_xcvr_probe(struct platform_device *pdev) return PTR_ERR(xcvr->pll_ipg_clk); } + fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk, + &xcvr->pll11k_clk); + xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram"); if (IS_ERR(xcvr->ram_addr)) return PTR_ERR(xcvr->ram_addr); diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h index 044058fc6aa2..882428592e1a 100644 --- a/sound/soc/fsl/fsl_xcvr.h +++ b/sound/soc/fsl/fsl_xcvr.h @@ -291,4 +291,95 @@ #define FSL_XCVR_RX_CS_BUFF_1 0xA0 /* Second RX CS buffer */ #define FSL_XCVR_CAP_DATA_STR 0x300 /* Capabilities data structure */ +/* GP PLL Registers */ +#define FSL_XCVR_GP_PLL_CTRL 0x00 +#define FSL_XCVR_GP_PLL_CTRL_SET 0x04 +#define FSL_XCVR_GP_PLL_CTRL_CLR 0x08 +#define FSL_XCVR_GP_PLL_CTRL_TOG 0x0C +#define FSL_XCVR_GP_PLL_ANA_PRG 0x10 +#define FSL_XCVR_GP_PLL_ANA_PRG_SET 0x14 +#define FSL_XCVR_GP_PLL_ANA_PRG_CLR 0x18 +#define FSL_XCVR_GP_PLL_ANA_PRG_TOG 0x1C +#define FSL_XCVR_GP_PLL_TEST 0x20 +#define FSL_XCVR_GP_PLL_TEST_SET 0x24 +#define FSL_XCVR_GP_PLL_TEST_CLR 0x28 +#define FSL_XCVR_GP_PLL_TEST_TOG 0x2C +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM 0x30 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_SET 0x34 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_CLR 0x38 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_TOG 0x3C +#define FSL_XCVR_GP_PLL_NUMERATOR 0x40 +#define FSL_XCVR_GP_PLL_NUMERATOR_SET 0x44 +#define FSL_XCVR_GP_PLL_NUMERATOR_CLR 0x48 +#define FSL_XCVR_GP_PLL_NUMERATOR_TOG 0x4C +#define FSL_XCVR_GP_PLL_DENOMINATOR 0x50 +#define FSL_XCVR_GP_PLL_DENOMINATOR_SET 0x54 +#define FSL_XCVR_GP_PLL_DENOMINATOR_CLR 0x58 +#define FSL_XCVR_GP_PLL_DENOMINATOR_TOG 0x5C +#define FSL_XCVR_GP_PLL_DIV 0x60 +#define FSL_XCVR_GP_PLL_DIV_SET 0x64 +#define FSL_XCVR_GP_PLL_DIV_CLR 0x68 +#define FSL_XCVR_GP_PLL_DIV_TOG 0x6C +#define FSL_XCVR_GP_PLL_DFS_CTRL0 0x70 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_SET 0x74 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_CLR 0x78 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_TOG 0x7C +#define FSL_XCVR_GP_PLL_DFS_DIV0 0x80 +#define FSL_XCVR_GP_PLL_DFS_DIV0_SET 0x84 +#define FSL_XCVR_GP_PLL_DFS_DIV0_CLR 0x88 +#define FSL_XCVR_GP_PLL_DFS_DIV0_TOG 0x8C +#define FSL_XCVR_GP_PLL_DFS_CTRL1 0x90 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_SET 0x94 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_CLR 0x98 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_TOG 0x9C +#define FSL_XCVR_GP_PLL_DFS_DIV1 0xA0 +#define FSL_XCVR_GP_PLL_DFS_DIV1_SET 0xA4 +#define FSL_XCVR_GP_PLL_DFS_DIV1_CLR 0xA8 +#define FSL_XCVR_GP_PLL_DFS_DIV1_TOG 0xAC +#define FSL_XCVR_GP_PLL_DFS_CTRL2 0xB0 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_SET 0xB4 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_CLR 0xB8 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_TOG 0xBC +#define FSL_XCVR_GP_PLL_DFS_DIV2 0xC0 +#define FSL_XCVR_GP_PLL_DFS_DIV2_SET 0xC4 +#define FSL_XCVR_GP_PLL_DFS_DIV2_CLR 0xC8 +#define FSL_XCVR_GP_PLL_DFS_DIV2_TOG 0xCC +#define FSL_XCVR_GP_PLL_DFS_CTRL3 0xD0 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_SET 0xD4 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_CLR 0xD8 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_TOG 0xDC +#define FSL_XCVR_GP_PLL_DFS_DIV3 0xE0 +#define FSL_XCVR_GP_PLL_DFS_DIV3_SET 0xE4 +#define FSL_XCVR_GP_PLL_DFS_DIV3_CLR 0xE8 +#define FSL_XCVR_GP_PLL_DFS_DIV3_TOG 0xEC +#define FSL_XCVR_GP_PLL_STATUS 0xF0 +#define FSL_XCVR_GP_PLL_STATUS_SET 0xF4 +#define FSL_XCVR_GP_PLL_STATUS_CLR 0xF8 +#define FSL_XCVR_GP_PLL_STATUS_TOG 0xFC + +/* GP PLL Control Register */ +#define FSL_XCVR_GP_PLL_CTRL_LBYPASS BIT(31) +#define FSL_XCVR_GP_PLL_CTRL_HCS BIT(16) +#define FSL_XCVR_GP_PLL_CTRL_MSD BIT(12) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN3 BIT(11) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN2 BIT(10) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN1 BIT(9) +#define FSL_XCVR_GP_PLL_CTRL_SPREADCTL BIT(8) +#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_BYPASS BIT(2) +#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN BIT(1) +#define FSL_XCVR_GP_PLL_CTRL_POWERUP BIT(0) + +/* GP PLL Numerator Register */ +#define FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT 2 +#define FSL_XCVR_GP_PLL_NUMERATOR_MFN GENMASK(31, 2) + +/* GP PLL Denominator Register */ +#define FSL_XCVR_GP_PLL_DENOMINATOR_MFD GENMASK(29, 0) + +/* GP PLL Dividers Register */ +#define FSL_XCVR_GP_PLL_DIV_MFI_SHIFT 16 +#define FSL_XCVR_GP_PLL_DIV_MFI GENMASK(24, 16) +#define FSL_XCVR_GP_PLL_DIV_RDIV GENMASK(15, 13) +#define FSL_XCVR_GP_PLL_DIV_ODIV GENMASK(7, 0) + #endif /* __FSL_XCVR_H */ -- cgit v1.2.3 From 2884dc7d08d98a89d8d65121524bb7533183a63a Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sun, 2 Jun 2024 11:27:03 -0700 Subject: bpf: Fix a potential use-after-free in bpf_link_free() After commit 1a80dbcb2dba, bpf_link can be freed by link->ops->dealloc_deferred, but the code still tests and uses link->ops->dealloc afterward, which leads to a use-after-free as reported by syzbot. Actually, one of them should be sufficient, so just call one of them instead of both. Also add a WARN_ON() in case of any problematic implementation. Fixes: 1a80dbcb2dba ("bpf: support deferring bpf_link dealloc to after RCU grace period") Reported-by: syzbot+1989ee16d94720836244@syzkaller.appspotmail.com Signed-off-by: Cong Wang Signed-off-by: Daniel Borkmann Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/20240602182703.207276-1-xiyou.wangcong@gmail.com --- kernel/bpf/syscall.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2222c3ff88e7..f45ed6adc092 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2998,6 +2998,7 @@ static int bpf_obj_get(const union bpf_attr *attr) void bpf_link_init(struct bpf_link *link, enum bpf_link_type type, const struct bpf_link_ops *ops, struct bpf_prog *prog) { + WARN_ON(ops->dealloc && ops->dealloc_deferred); atomic64_set(&link->refcnt, 1); link->type = type; link->id = 0; @@ -3056,16 +3057,17 @@ static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu) /* bpf_link_free is guaranteed to be called from process context */ static void bpf_link_free(struct bpf_link *link) { + const struct bpf_link_ops *ops = link->ops; bool sleepable = false; bpf_link_free_id(link->id); if (link->prog) { sleepable = link->prog->sleepable; /* detach BPF program, clean up used resources */ - link->ops->release(link); + ops->release(link); bpf_prog_put(link->prog); } - if (link->ops->dealloc_deferred) { + if (ops->dealloc_deferred) { /* schedule BPF link deallocation; if underlying BPF program * is sleepable, we need to first wait for RCU tasks trace * sync, then go through "classic" RCU grace period @@ -3074,9 +3076,8 @@ static void bpf_link_free(struct bpf_link *link) call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp); else call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp); - } - if (link->ops->dealloc) - link->ops->dealloc(link); + } else if (ops->dealloc) + ops->dealloc(link); } static void bpf_link_put_deferred(struct work_struct *work) -- cgit v1.2.3 From b97e8a2f7130a4b30d1502003095833d16c028b3 Mon Sep 17 00:00:00 2001 From: Hagar Hemdan Date: Fri, 31 May 2024 16:21:44 +0000 Subject: irqchip/gic-v3-its: Fix potential race condition in its_vlpi_prop_update() its_vlpi_prop_update() calls lpi_write_config() which obtains the mapping information for a VLPI without lock held. So it could race with its_vlpi_unmap(). Since all calls from its_irq_set_vcpu_affinity() require the same lock to be held, hoist the locking there instead of sprinkling the locking all over the place. This bug was discovered using Coverity Static Analysis Security Testing (SAST) by Synopsys, Inc. [ tglx: Use guard() instead of goto ] Fixes: 015ec0386ab6 ("irqchip/gic-v3-its: Add VLPI configuration handling") Suggested-by: Marc Zyngier Signed-off-by: Hagar Hemdan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Reviewed-by: Marc Zyngier Link: https://lore.kernel.org/r/20240531162144.28650-1-hagarhem@amazon.com --- drivers/irqchip/irq-gic-v3-its.c | 44 +++++++++++----------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 40ebf1726393..3c755d5dad6e 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1846,28 +1846,22 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); - int ret = 0; if (!info->map) return -EINVAL; - raw_spin_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm) { struct its_vlpi_map *maps; maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps), GFP_ATOMIC); - if (!maps) { - ret = -ENOMEM; - goto out; - } + if (!maps) + return -ENOMEM; its_dev->event_map.vm = info->map->vm; its_dev->event_map.vlpi_maps = maps; } else if (its_dev->event_map.vm != info->map->vm) { - ret = -EINVAL; - goto out; + return -EINVAL; } /* Get our private copy of the mapping information */ @@ -1899,46 +1893,32 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) its_dev->event_map.nr_vlpis++; } -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); struct its_vlpi_map *map; - int ret = 0; - - raw_spin_lock(&its_dev->event_map.vlpi_lock); map = get_vlpi_map(d); - if (!its_dev->event_map.vm || !map) { - ret = -EINVAL; - goto out; - } + if (!its_dev->event_map.vm || !map) + return -EINVAL; /* Copy our mapping information to the incoming request */ *info->map = *map; -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_unmap(struct irq_data *d) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); - int ret = 0; - - raw_spin_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) { - ret = -EINVAL; - goto out; - } + if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) + return -EINVAL; /* Drop the virtual mapping */ its_send_discard(its_dev, event); @@ -1962,9 +1942,7 @@ static int its_vlpi_unmap(struct irq_data *d) kfree(its_dev->event_map.vlpi_maps); } -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info) @@ -1992,6 +1970,8 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) if (!is_v4(its_dev->its)) return -EINVAL; + guard(raw_spinlock_irq)(&its_dev->event_map.vlpi_lock); + /* Unmap request? */ if (!info) return its_vlpi_unmap(d); -- cgit v1.2.3 From 27bd5fdc24c0d5d1306f968ef24105c4577242b0 Mon Sep 17 00:00:00 2001 From: Nikunj A Dadhania Date: Fri, 31 May 2024 04:46:42 +0000 Subject: KVM: SEV-ES: Prevent MSR access post VMSA encryption KVM currently allows userspace to read/write MSRs even after the VMSA is encrypted. This can cause unintentional issues if MSR access has side- effects. For ex, while migrating a guest, userspace could attempt to migrate MSR_IA32_DEBUGCTLMSR and end up unintentionally disabling LBRV on the target. Fix this by preventing access to those MSRs which are context switched via the VMSA, once the VMSA is encrypted. Suggested-by: Sean Christopherson Signed-off-by: Nikunj A Dadhania Signed-off-by: Ravi Bangoria Message-ID: <20240531044644.768-2-ravi.bangoria@amd.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/svm.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 1d3c8be39479..f265361f4518 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2822,10 +2822,24 @@ static int svm_get_msr_feature(struct kvm_msr_entry *msr) return 0; } +static bool +sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, struct msr_data *msr_info) +{ + return sev_es_guest(vcpu->kvm) && + vcpu->arch.guest_state_protected && + svm_msrpm_offset(msr_info->index) != MSR_INVALID && + !msr_write_intercepted(vcpu, msr_info->index); +} + static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { struct vcpu_svm *svm = to_svm(vcpu); + if (sev_es_prevent_msr_access(vcpu, msr_info)) { + msr_info->data = 0; + return -EINVAL; + } + switch (msr_info->index) { case MSR_AMD64_TSC_RATIO: if (!msr_info->host_initiated && @@ -2976,6 +2990,10 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) u32 ecx = msr->index; u64 data = msr->data; + + if (sev_es_prevent_msr_access(vcpu, msr)) + return -EINVAL; + switch (ecx) { case MSR_AMD64_TSC_RATIO: -- cgit v1.2.3 From d922056215617eedfbdbc29fe49953423686fe5e Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 31 May 2024 04:46:43 +0000 Subject: KVM: SEV-ES: Disallow SEV-ES guests when X86_FEATURE_LBRV is absent As documented in APM[1], LBR Virtualization must be enabled for SEV-ES guests. So, prevent SEV-ES guests when LBRV support is missing. [1]: AMD64 Architecture Programmer's Manual Pub. 40332, Rev. 4.07 - June 2023, Vol 2, 15.35.2 Enabling SEV-ES. https://bugzilla.kernel.org/attachment.cgi?id=304653 Fixes: 376c6d285017 ("KVM: SVM: Provide support for SEV-ES vCPU creation/loading") Signed-off-by: Ravi Bangoria Message-ID: <20240531044644.768-3-ravi.bangoria@amd.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 6 ++++++ arch/x86/kvm/svm/svm.c | 16 +++++++--------- arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0623cfaa7bb0..8b52bbba02c0 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2406,6 +2406,12 @@ void __init sev_hardware_setup(void) if (!boot_cpu_has(X86_FEATURE_SEV_ES)) goto out; + if (!lbrv) { + WARN_ONCE(!boot_cpu_has(X86_FEATURE_LBRV), + "LBRV must be present for SEV-ES support"); + goto out; + } + /* Has the system been allocated ASIDs for SEV-ES? */ if (min_sev_asid == 1) goto out; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index f265361f4518..223a551bf44e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -215,7 +215,7 @@ int vgif = true; module_param(vgif, int, 0444); /* enable/disable LBR virtualization */ -static int lbrv = true; +int lbrv = true; module_param(lbrv, int, 0444); static int tsc_scaling = true; @@ -5294,6 +5294,12 @@ static __init int svm_hardware_setup(void) nrips = nrips && boot_cpu_has(X86_FEATURE_NRIPS); + if (lbrv) { + if (!boot_cpu_has(X86_FEATURE_LBRV)) + lbrv = false; + else + pr_info("LBR virtualization supported\n"); + } /* * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which * may be modified by svm_adjust_mmio_mask()), as well as nrips. @@ -5347,14 +5353,6 @@ static __init int svm_hardware_setup(void) svm_x86_ops.set_vnmi_pending = NULL; } - - if (lbrv) { - if (!boot_cpu_has(X86_FEATURE_LBRV)) - lbrv = false; - else - pr_info("LBR virtualization supported\n"); - } - if (!enable_pmu) pr_info("PMU virtualization is disabled\n"); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index be57213cd295..59bbb8122b75 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -39,6 +39,7 @@ extern int vgif; extern bool intercept_smi; extern bool x2avic_enabled; extern bool vnmi; +extern int lbrv; /* * Clean bits in VMCB. -- cgit v1.2.3 From b7e4be0a224fe5c6be30c1c8bdda8d2317ad6ba4 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 31 May 2024 04:46:44 +0000 Subject: KVM: SEV-ES: Delegate LBR virtualization to the processor As documented in APM[1], LBR Virtualization must be enabled for SEV-ES guests. Although KVM currently enforces LBRV for SEV-ES guests, there are multiple issues with it: o MSR_IA32_DEBUGCTLMSR is still intercepted. Since MSR_IA32_DEBUGCTLMSR interception is used to dynamically toggle LBRV for performance reasons, this can be fatal for SEV-ES guests. For ex SEV-ES guest on Zen3: [guest ~]# wrmsr 0x1d9 0x4 KVM: entry failed, hardware error 0xffffffff EAX=00000004 EBX=00000000 ECX=000001d9 EDX=00000000 Fix this by never intercepting MSR_IA32_DEBUGCTLMSR for SEV-ES guests. No additional save/restore logic is required since MSR_IA32_DEBUGCTLMSR is of swap type A. o KVM will disable LBRV if userspace sets MSR_IA32_DEBUGCTLMSR before the VMSA is encrypted. Fix this by moving LBRV enablement code post VMSA encryption. [1]: AMD64 Architecture Programmer's Manual Pub. 40332, Rev. 4.07 - June 2023, Vol 2, 15.35.2 Enabling SEV-ES. https://bugzilla.kernel.org/attachment.cgi?id=304653 Fixes: 376c6d285017 ("KVM: SVM: Provide support for SEV-ES vCPU creation/loading") Co-developed-by: Nikunj A Dadhania Signed-off-by: Nikunj A Dadhania Signed-off-by: Ravi Bangoria Message-ID: <20240531044644.768-4-ravi.bangoria@amd.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 13 ++++++++----- arch/x86/kvm/svm/svm.c | 8 +++++++- arch/x86/kvm/svm/svm.h | 3 ++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 8b52bbba02c0..95095a233a45 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -779,6 +779,14 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, */ fpstate_set_confidential(&vcpu->arch.guest_fpu); vcpu->arch.guest_state_protected = true; + + /* + * SEV-ES guest mandates LBR Virtualization to be _always_ ON. Enable it + * only after setting guest_state_protected because KVM_SET_MSRS allows + * dynamic toggling of LBRV (for performance reason) on write access to + * MSR_IA32_DEBUGCTLMSR when guest_state_protected is not set. + */ + svm_enable_lbrv(vcpu); return 0; } @@ -3222,7 +3230,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) struct kvm_vcpu *vcpu = &svm->vcpu; svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; - svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; /* * An SEV-ES guest requires a VMSA area that is a separate from the @@ -3274,10 +3281,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) /* Clear intercepts on selected MSRs */ set_msr_interception(vcpu, svm->msrpm, MSR_EFER, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_CR_PAT, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); } void sev_init_vmcb(struct vcpu_svm *svm) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 223a551bf44e..296c524988f9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -99,6 +99,7 @@ static const struct svm_direct_access_msrs { { .index = MSR_IA32_SPEC_CTRL, .always = false }, { .index = MSR_IA32_PRED_CMD, .always = false }, { .index = MSR_IA32_FLUSH_CMD, .always = false }, + { .index = MSR_IA32_DEBUGCTLMSR, .always = false }, { .index = MSR_IA32_LASTBRANCHFROMIP, .always = false }, { .index = MSR_IA32_LASTBRANCHTOIP, .always = false }, { .index = MSR_IA32_LASTINTFROMIP, .always = false }, @@ -990,7 +991,7 @@ void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb) vmcb_mark_dirty(to_vmcb, VMCB_LBR); } -static void svm_enable_lbrv(struct kvm_vcpu *vcpu) +void svm_enable_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1000,6 +1001,9 @@ static void svm_enable_lbrv(struct kvm_vcpu *vcpu) set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); + if (sev_es_guest(vcpu->kvm)) + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_DEBUGCTLMSR, 1, 1); + /* Move the LBR msrs to the vmcb02 so that the guest can see them. */ if (is_guest_mode(vcpu)) svm_copy_lbrs(svm->vmcb, svm->vmcb01.ptr); @@ -1009,6 +1013,8 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm); + svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 0, 0); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 59bbb8122b75..0f1472690b59 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -30,7 +30,7 @@ #define IOPM_SIZE PAGE_SIZE * 3 #define MSRPM_SIZE PAGE_SIZE * 2 -#define MAX_DIRECT_ACCESS_MSRS 47 +#define MAX_DIRECT_ACCESS_MSRS 48 #define MSRPM_OFFSETS 32 extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; extern bool npt_enabled; @@ -553,6 +553,7 @@ u32 *svm_vcpu_alloc_msrpm(void); void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm); void svm_vcpu_free_msrpm(u32 *msrpm); void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb); +void svm_enable_lbrv(struct kvm_vcpu *vcpu); void svm_update_lbrv(struct kvm_vcpu *vcpu); int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer); -- cgit v1.2.3 From 89a58812c47f1823191e9d0b08b53df2dd304ae2 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 21 May 2024 18:03:04 -0700 Subject: KVM: x86: Drop support for hand tuning APIC timer advancement from userspace Remove support for specifying a static local APIC timer advancement value, and instead present a read-only boolean parameter to let userspace enable or disable KVM's dynamic APIC timer advancement. Realistically, it's all but impossible for userspace to specify an advancement that is more precise than what KVM's adaptive tuning can provide. E.g. a static value needs to be tuned for the exact hardware and kernel, and if KVM is using hrtimers, likely requires additional tuning for the exact configuration of the entire system. Dropping support for a userspace provided value also fixes several flaws in the interface. E.g. KVM interprets a negative value other than -1 as a large advancement, toggling between a negative and positive value yields unpredictable behavior as vCPUs will switch from dynamic to static advancement, changing the advancement in the middle of VM creation can result in different values for vCPUs within a VM, etc. Those flaws are mostly fixable, but there's almost no justification for taking on yet more complexity (it's minimal complexity, but still non-zero). The only arguments against using KVM's adaptive tuning is if a setup needs a higher maximum, or if the adjustments are too reactive, but those are arguments for letting userspace control the absolute max advancement and the granularity of each adjustment, e.g. similar to how KVM provides knobs for halt polling. Link: https://lore.kernel.org/all/20240520115334.852510-1-zhoushuling@huawei.com Cc: Shuling Zhou Cc: Marcelo Tosatti Signed-off-by: Sean Christopherson Message-ID: <20240522010304.1650603-1-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/lapic.c | 39 +++++++++++++++++++++------------------ arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/x86.c | 11 +---------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index ebf41023be38..acd7d48100a1 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -59,7 +59,17 @@ #define MAX_APIC_VECTOR 256 #define APIC_VECTORS_PER_REG 32 -static bool lapic_timer_advance_dynamic __read_mostly; +/* + * Enable local APIC timer advancement (tscdeadline mode only) with adaptive + * tuning. When enabled, KVM programs the host timer event to fire early, i.e. + * before the deadline expires, to account for the delay between taking the + * VM-Exit (to inject the guest event) and the subsequent VM-Enter to resume + * the guest, i.e. so that the interrupt arrives in the guest with minimal + * latency relative to the deadline programmed by the guest. + */ +static bool lapic_timer_advance __read_mostly = true; +module_param(lapic_timer_advance, bool, 0444); + #define LAPIC_TIMER_ADVANCE_ADJUST_MIN 100 /* clock cycles */ #define LAPIC_TIMER_ADVANCE_ADJUST_MAX 10000 /* clock cycles */ #define LAPIC_TIMER_ADVANCE_NS_INIT 1000 @@ -1854,16 +1864,14 @@ static void __kvm_wait_lapic_expire(struct kvm_vcpu *vcpu) guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); trace_kvm_wait_lapic_expire(vcpu->vcpu_id, guest_tsc - tsc_deadline); - if (lapic_timer_advance_dynamic) { - adjust_lapic_timer_advance(vcpu, guest_tsc - tsc_deadline); - /* - * If the timer fired early, reread the TSC to account for the - * overhead of the above adjustment to avoid waiting longer - * than is necessary. - */ - if (guest_tsc < tsc_deadline) - guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); - } + adjust_lapic_timer_advance(vcpu, guest_tsc - tsc_deadline); + + /* + * If the timer fired early, reread the TSC to account for the overhead + * of the above adjustment to avoid waiting longer than is necessary. + */ + if (guest_tsc < tsc_deadline) + guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); if (guest_tsc < tsc_deadline) __wait_lapic_expire(vcpu, tsc_deadline - guest_tsc); @@ -2812,7 +2820,7 @@ static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) return HRTIMER_NORESTART; } -int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) +int kvm_create_lapic(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic; @@ -2845,13 +2853,8 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD); apic->lapic_timer.timer.function = apic_timer_fn; - if (timer_advance_ns == -1) { + if (lapic_timer_advance) apic->lapic_timer.timer_advance_ns = LAPIC_TIMER_ADVANCE_NS_INIT; - lapic_timer_advance_dynamic = true; - } else { - apic->lapic_timer.timer_advance_ns = timer_advance_ns; - lapic_timer_advance_dynamic = false; - } /* * Stuff the APIC ENABLE bit in lieu of temporarily incrementing diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 0a0ea4b5dd8c..a69e706b9080 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -85,7 +85,7 @@ struct kvm_lapic { struct dest_map; -int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns); +int kvm_create_lapic(struct kvm_vcpu *vcpu); void kvm_free_lapic(struct kvm_vcpu *vcpu); int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 082ac6d95a3a..8c9e4281d978 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -164,15 +164,6 @@ module_param(kvmclock_periodic_sync, bool, 0444); static u32 __read_mostly tsc_tolerance_ppm = 250; module_param(tsc_tolerance_ppm, uint, 0644); -/* - * lapic timer advance (tscdeadline mode only) in nanoseconds. '-1' enables - * adaptive tuning starting from default advancement of 1000ns. '0' disables - * advancement entirely. Any other value is used as-is and disables adaptive - * tuning, i.e. allows privileged userspace to set an exact advancement time. - */ -static int __read_mostly lapic_timer_advance_ns = -1; -module_param(lapic_timer_advance_ns, int, 0644); - static bool __read_mostly vector_hashing = true; module_param(vector_hashing, bool, 0444); @@ -12169,7 +12160,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) if (r < 0) return r; - r = kvm_create_lapic(vcpu, lapic_timer_advance_ns); + r = kvm_create_lapic(vcpu); if (r < 0) goto fail_mmu_destroy; -- cgit v1.2.3 From d6283b160a12010b2113cc64726a3c9eda13dc5f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 29 May 2024 10:38:04 -0300 Subject: tools headers uapi: Sync linux/stat.h with the kernel sources to pick STATX_SUBVOL To pick the changes from: 2a82bb02941fb53d ("statx: stx_subvol") This silences this perf build warning: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/stat.h include/uapi/linux/stat.h Cc: Adrian Hunter Cc: Christian Brauner Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Kent Overstreet Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZlnK2Fmx_gahzwZI@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/stat.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/include/uapi/linux/stat.h b/tools/include/uapi/linux/stat.h index 2f2ee82d5517..67626d535316 100644 --- a/tools/include/uapi/linux/stat.h +++ b/tools/include/uapi/linux/stat.h @@ -126,8 +126,9 @@ struct statx { __u64 stx_mnt_id; __u32 stx_dio_mem_align; /* Memory buffer alignment for direct I/O */ __u32 stx_dio_offset_align; /* File offset alignment for direct I/O */ + __u64 stx_subvol; /* Subvolume identifier */ /* 0xa0 */ - __u64 __spare3[12]; /* Spare space for future expansion */ + __u64 __spare3[11]; /* Spare space for future expansion */ /* 0x100 */ }; @@ -155,6 +156,7 @@ struct statx { #define STATX_MNT_ID 0x00001000U /* Got stx_mnt_id */ #define STATX_DIOALIGN 0x00002000U /* Want/got direct I/O alignment info */ #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ +#define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */ #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ -- cgit v1.2.3 From ffbe335b8d471f79b259e950cb20999700670456 Mon Sep 17 00:00:00 2001 From: Matthias Stocker Date: Fri, 31 May 2024 12:37:11 +0200 Subject: vmxnet3: disable rx data ring on dma allocation failure When vmxnet3_rq_create() fails to allocate memory for rq->data_ring.base, the subsequent call to vmxnet3_rq_destroy_all_rxdataring does not reset rq->data_ring.desc_size for the data ring that failed, which presumably causes the hypervisor to reference it on packet reception. To fix this bug, rq->data_ring.desc_size needs to be set to 0 to tell the hypervisor to disable this feature. [ 95.436876] kernel BUG at net/core/skbuff.c:207! [ 95.439074] invalid opcode: 0000 [#1] PREEMPT SMP NOPTI [ 95.440411] CPU: 7 PID: 0 Comm: swapper/7 Not tainted 6.9.3-dirty #1 [ 95.441558] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 12/12/2018 [ 95.443481] RIP: 0010:skb_panic+0x4d/0x4f [ 95.444404] Code: 4f 70 50 8b 87 c0 00 00 00 50 8b 87 bc 00 00 00 50 ff b7 d0 00 00 00 4c 8b 8f c8 00 00 00 48 c7 c7 68 e8 be 9f e8 63 58 f9 ff <0f> 0b 48 8b 14 24 48 c7 c1 d0 73 65 9f e8 a1 ff ff ff 48 8b 14 24 [ 95.447684] RSP: 0018:ffffa13340274dd0 EFLAGS: 00010246 [ 95.448762] RAX: 0000000000000089 RBX: ffff8fbbc72b02d0 RCX: 000000000000083f [ 95.450148] RDX: 0000000000000000 RSI: 00000000000000f6 RDI: 000000000000083f [ 95.451520] RBP: 000000000000002d R08: 0000000000000000 R09: ffffa13340274c60 [ 95.452886] R10: ffffffffa04ed468 R11: 0000000000000002 R12: 0000000000000000 [ 95.454293] R13: ffff8fbbdab3c2d0 R14: ffff8fbbdbd829e0 R15: ffff8fbbdbd809e0 [ 95.455682] FS: 0000000000000000(0000) GS:ffff8fbeefd80000(0000) knlGS:0000000000000000 [ 95.457178] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 95.458340] CR2: 00007fd0d1f650c8 CR3: 0000000115f28000 CR4: 00000000000406f0 [ 95.459791] Call Trace: [ 95.460515] [ 95.461180] ? __die_body.cold+0x19/0x27 [ 95.462150] ? die+0x2e/0x50 [ 95.462976] ? do_trap+0xca/0x110 [ 95.463973] ? do_error_trap+0x6a/0x90 [ 95.464966] ? skb_panic+0x4d/0x4f [ 95.465901] ? exc_invalid_op+0x50/0x70 [ 95.466849] ? skb_panic+0x4d/0x4f [ 95.467718] ? asm_exc_invalid_op+0x1a/0x20 [ 95.468758] ? skb_panic+0x4d/0x4f [ 95.469655] skb_put.cold+0x10/0x10 [ 95.470573] vmxnet3_rq_rx_complete+0x862/0x11e0 [vmxnet3] [ 95.471853] vmxnet3_poll_rx_only+0x36/0xb0 [vmxnet3] [ 95.473185] __napi_poll+0x2b/0x160 [ 95.474145] net_rx_action+0x2c6/0x3b0 [ 95.475115] handle_softirqs+0xe7/0x2a0 [ 95.476122] __irq_exit_rcu+0x97/0xb0 [ 95.477109] common_interrupt+0x85/0xa0 [ 95.478102] [ 95.478846] [ 95.479603] asm_common_interrupt+0x26/0x40 [ 95.480657] RIP: 0010:pv_native_safe_halt+0xf/0x20 [ 95.481801] Code: 22 d7 e9 54 87 01 00 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 93 ba 3b 00 fb f4 2c 87 01 00 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 [ 95.485563] RSP: 0018:ffffa133400ffe58 EFLAGS: 00000246 [ 95.486882] RAX: 0000000000004000 RBX: ffff8fbbc1d14064 RCX: 0000000000000000 [ 95.488477] RDX: ffff8fbeefd80000 RSI: ffff8fbbc1d14000 RDI: 0000000000000001 [ 95.490067] RBP: ffff8fbbc1d14064 R08: ffffffffa0652260 R09: 00000000000010d3 [ 95.491683] R10: 0000000000000018 R11: ffff8fbeefdb4764 R12: ffffffffa0652260 [ 95.493389] R13: ffffffffa06522e0 R14: 0000000000000001 R15: 0000000000000000 [ 95.495035] acpi_safe_halt+0x14/0x20 [ 95.496127] acpi_idle_do_entry+0x2f/0x50 [ 95.497221] acpi_idle_enter+0x7f/0xd0 [ 95.498272] cpuidle_enter_state+0x81/0x420 [ 95.499375] cpuidle_enter+0x2d/0x40 [ 95.500400] do_idle+0x1e5/0x240 [ 95.501385] cpu_startup_entry+0x29/0x30 [ 95.502422] start_secondary+0x11c/0x140 [ 95.503454] common_startup_64+0x13e/0x141 [ 95.504466] [ 95.505197] Modules linked in: nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 rfkill ip_set nf_tables vsock_loopback vmw_vsock_virtio_transport_common qrtr vmw_vsock_vmci_transport vsock sunrpc binfmt_misc pktcdvd vmw_balloon pcspkr vmw_vmci i2c_piix4 joydev loop dm_multipath nfnetlink zram crct10dif_pclmul crc32_pclmul vmwgfx crc32c_intel polyval_clmulni polyval_generic ghash_clmulni_intel sha512_ssse3 sha256_ssse3 vmxnet3 sha1_ssse3 drm_ttm_helper vmw_pvscsi ttm ata_generic pata_acpi serio_raw scsi_dh_rdac scsi_dh_emc scsi_dh_alua ip6_tables ip_tables fuse [ 95.516536] ---[ end trace 0000000000000000 ]--- Fixes: 6f4833383e85 ("net: vmxnet3: Fix NULL pointer dereference in vmxnet3_rq_rx_complete()") Signed-off-by: Matthias Stocker Reviewed-by: Subbaraya Sundeep Reviewed-by: Ronak Doshi Link: https://lore.kernel.org/r/20240531103711.101961-1-mstocker@barracuda.com Signed-off-by: Jakub Kicinski --- drivers/net/vmxnet3/vmxnet3_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 89ca6e75fcc6..63822d454c00 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2034,8 +2034,8 @@ vmxnet3_rq_destroy_all_rxdataring(struct vmxnet3_adapter *adapter) rq->data_ring.base, rq->data_ring.basePA); rq->data_ring.base = NULL; - rq->data_ring.desc_size = 0; } + rq->data_ring.desc_size = 0; } } -- cgit v1.2.3 From 2fe40483ec257de2a0d819ef88e3e76c7e261319 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 May 2024 13:26:32 +0000 Subject: ipv6: ioam: block BH from ioam6_output() As explained in commit 1378817486d6 ("tipc: block BH before using dst_cache"), net/core/dst_cache.c helpers need to be called with BH disabled. Disabling preemption in ioam6_output() is not good enough, because ioam6_output() is called from process context, lwtunnel_output() only uses rcu_read_lock(). We might be interrupted by a softirq, re-enter ioam6_output() and corrupt dst_cache data structures. Fix the race by using local_bh_disable() instead of preempt_disable(). Fixes: 8cb3bf8bff3c ("ipv6: ioam: Add support for the ip6ip6 encapsulation") Signed-off-by: Eric Dumazet Cc: Justin Iurman Acked-by: Paolo Abeni Link: https://lore.kernel.org/r/20240531132636.2637995-2-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ioam6_iptunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index 7563f8c6aa87..bf7120ecea1e 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -351,9 +351,9 @@ do_encap: goto drop; if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) { - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&ilwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -373,9 +373,9 @@ do_encap: goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); -- cgit v1.2.3 From db0090c6eb12c31246438b7fe2a8f1b833e7a653 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 May 2024 13:26:33 +0000 Subject: net: ipv6: rpl_iptunnel: block BH in rpl_output() and rpl_input() As explained in commit 1378817486d6 ("tipc: block BH before using dst_cache"), net/core/dst_cache.c helpers need to be called with BH disabled. Disabling preemption in rpl_output() is not good enough, because rpl_output() is called from process context, lwtunnel_output() only uses rcu_read_lock(). We might be interrupted by a softirq, re-enter rpl_output() and corrupt dst_cache data structures. Fix the race by using local_bh_disable() instead of preempt_disable(). Apply a similar change in rpl_input(). Signed-off-by: Eric Dumazet Cc: Alexander Aring Acked-by: Paolo Abeni Link: https://lore.kernel.org/r/20240531132636.2637995-3-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/rpl_iptunnel.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c index a013b92cbb86..2c83b7586422 100644 --- a/net/ipv6/rpl_iptunnel.c +++ b/net/ipv6/rpl_iptunnel.c @@ -212,9 +212,9 @@ static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (unlikely(err)) goto drop; - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&rlwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -234,9 +234,9 @@ static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&rlwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); @@ -268,23 +268,21 @@ static int rpl_input(struct sk_buff *skb) return err; } - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&rlwt->cache); - preempt_enable(); if (!dst) { ip6_route_input(skb); dst = skb_dst(skb); if (!dst->error) { - preempt_disable(); dst_cache_set_ip6(&rlwt->cache, dst, &ipv6_hdr(skb)->saddr); - preempt_enable(); } } else { skb_dst_drop(skb); skb_dst_set(skb, dst); } + local_bh_enable(); err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); if (unlikely(err)) -- cgit v1.2.3 From c0b98ac1cc104f48763cdb27b1e9ac25fd81fc90 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 May 2024 13:26:34 +0000 Subject: ipv6: sr: block BH in seg6_output_core() and seg6_input_core() As explained in commit 1378817486d6 ("tipc: block BH before using dst_cache"), net/core/dst_cache.c helpers need to be called with BH disabled. Disabling preemption in seg6_output_core() is not good enough, because seg6_output_core() is called from process context, lwtunnel_output() only uses rcu_read_lock(). We might be interrupted by a softirq, re-enter seg6_output_core() and corrupt dst_cache data structures. Fix the race by using local_bh_disable() instead of preempt_disable(). Apply a similar change in seg6_input_core(). Fixes: fa79581ea66c ("ipv6: sr: fix several BUGs when preemption is enabled") Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels") Signed-off-by: Eric Dumazet Cc: David Lebrun Acked-by: Paolo Abeni Link: https://lore.kernel.org/r/20240531132636.2637995-4-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/seg6_iptunnel.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index a75df2ec8db0..098632adc9b5 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -464,23 +464,21 @@ static int seg6_input_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&slwt->cache); - preempt_enable(); if (!dst) { ip6_route_input(skb); dst = skb_dst(skb); if (!dst->error) { - preempt_disable(); dst_cache_set_ip6(&slwt->cache, dst, &ipv6_hdr(skb)->saddr); - preempt_enable(); } } else { skb_dst_drop(skb); skb_dst_set(skb, dst); } + local_bh_enable(); err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); if (unlikely(err)) @@ -536,9 +534,9 @@ static int seg6_output_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&slwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -558,9 +556,9 @@ static int seg6_output_core(struct net *net, struct sock *sk, goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); -- cgit v1.2.3 From cf28ff8e4c02e1ffa850755288ac954b6ff0db8c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 May 2024 13:26:35 +0000 Subject: ila: block BH in ila_output() As explained in commit 1378817486d6 ("tipc: block BH before using dst_cache"), net/core/dst_cache.c helpers need to be called with BH disabled. ila_output() is called from lwtunnel_output() possibly from process context, and under rcu_read_lock(). We might be interrupted by a softirq, re-enter ila_output() and corrupt dst_cache data structures. Fix the race by using local_bh_disable(). Signed-off-by: Eric Dumazet Acked-by: Paolo Abeni Link: https://lore.kernel.org/r/20240531132636.2637995-5-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv6/ila/ila_lwt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 0601bad79822..ff7e734e335b 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -58,7 +58,9 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) return orig_dst->lwtstate->orig_output(net, sk, skb); } + local_bh_disable(); dst = dst_cache_get(&ilwt->dst_cache); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *ip6h = ipv6_hdr(skb); struct flowi6 fl6; @@ -86,8 +88,11 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } - if (ilwt->connected) + if (ilwt->connected) { + local_bh_disable(); dst_cache_set_ip6(&ilwt->dst_cache, dst, &fl6.saddr); + local_bh_enable(); + } } skb_dst_set(skb, dst); -- cgit v1.2.3 From 2fe6fb36c781b50482b1c3323fb526bc07d1af59 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 May 2024 13:26:36 +0000 Subject: net: dst_cache: add two DEBUG_NET warnings After fixing four different bugs involving dst_cache users, it might be worth adding a check about BH being blocked by dst_cache callers. DEBUG_NET_WARN_ON_ONCE(!in_softirq()); It is not fatal, if we missed valid case where no BH deadlock is to be feared, we might change this. Signed-off-by: Eric Dumazet Acked-by: Paolo Abeni Link: https://lore.kernel.org/r/20240531132636.2637995-6-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/dst_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/dst_cache.c b/net/core/dst_cache.c index 6a0482e676d3..70c634b9e7b0 100644 --- a/net/core/dst_cache.c +++ b/net/core/dst_cache.c @@ -27,6 +27,7 @@ struct dst_cache_pcpu { static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache, struct dst_entry *dst, u32 cookie) { + DEBUG_NET_WARN_ON_ONCE(!in_softirq()); dst_release(dst_cache->dst); if (dst) dst_hold(dst); @@ -40,6 +41,7 @@ static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache, { struct dst_entry *dst; + DEBUG_NET_WARN_ON_ONCE(!in_softirq()); dst = idst->dst; if (!dst) goto fail; -- cgit v1.2.3 From c6cab01d7e20a028ffcee1e0a0b782332a16b5e6 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 31 May 2024 18:35:43 -0700 Subject: lib/test_rhashtable: add missing MODULE_DESCRIPTION() macro make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in lib/test_rhashtable.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240531-md-lib-test_rhashtable-v1-1-cd6d4138f1b6@quicinc.com Signed-off-by: Jakub Kicinski --- lib/test_rhashtable.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 42b585208249..c63db03ebb9d 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -811,4 +811,5 @@ static void __exit test_rht_exit(void) module_init(test_rht_init); module_exit(test_rht_exit); +MODULE_DESCRIPTION("Resizable, Scalable, Concurrent Hash Table test module"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4aa2dcfbad538adf7becd0034a3754e1bd01b2b5 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 17 May 2024 07:19:14 -0700 Subject: HID: core: remove unnecessary WARN_ON() in implement() Syzkaller hit a warning [1] in a call to implement() when trying to write a value into a field of smaller size in an output report. Since implement() already has a warn message printed out with the help of hid_warn() and value in question gets trimmed with: ... value &= m; ... WARN_ON may be considered superfluous. Remove it to suppress future syzkaller triggers. [1] WARNING: CPU: 0 PID: 5084 at drivers/hid/hid-core.c:1451 implement drivers/hid/hid-core.c:1451 [inline] WARNING: CPU: 0 PID: 5084 at drivers/hid/hid-core.c:1451 hid_output_report+0x548/0x760 drivers/hid/hid-core.c:1863 Modules linked in: CPU: 0 PID: 5084 Comm: syz-executor424 Not tainted 6.9.0-rc7-syzkaller-00183-gcf87f46fd34d #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/02/2024 RIP: 0010:implement drivers/hid/hid-core.c:1451 [inline] RIP: 0010:hid_output_report+0x548/0x760 drivers/hid/hid-core.c:1863 ... Call Trace: __usbhid_submit_report drivers/hid/usbhid/hid-core.c:591 [inline] usbhid_submit_report+0x43d/0x9e0 drivers/hid/usbhid/hid-core.c:636 hiddev_ioctl+0x138b/0x1f00 drivers/hid/usbhid/hiddev.c:726 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:904 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:890 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xf5/0x240 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f ... Fixes: 95d1c8951e5b ("HID: simplify implement() a bit") Reported-by: Suggested-by: Alan Stern Signed-off-by: Nikita Zhandarovich Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b1fa0378e8f4..74efda212c55 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1448,7 +1448,6 @@ static void implement(const struct hid_device *hid, u8 *report, hid_warn(hid, "%s() called with too large value %d (n: %d)! (%s)\n", __func__, value, n, current->comm); - WARN_ON(1); value &= m; } } -- cgit v1.2.3 From 3ec8ebd8a5b782d56347ae884de880af26f93996 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 27 May 2024 16:22:34 +0300 Subject: EDAC/amd64: Convert PCIBIOS_* return codes to errnos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpu_get_node_map() uses pci_read_config_dword() that returns PCIBIOS_* codes. The return code is then returned all the way into the module init function amd64_edac_init() that returns it as is. The module init functions, however, should return normal errnos. Convert PCIBIOS_* returns code using pcibios_err_to_errno() into normal errno before returning it from gpu_get_node_map(). For consistency, convert also the other similar cases which return PCIBIOS_* codes even if they do not have any bugs at the moment. Fixes: 4251566ebc1c ("EDAC/amd64: Cache and use GPU node map") Signed-off-by: Ilpo Järvinen Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240527132236.13875-1-ilpo.jarvinen@linux.intel.com --- drivers/edac/amd64_edac.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 1f3520d76861..a17f3c0cdfa6 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -81,7 +81,7 @@ int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, amd64_warn("%s: error reading F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); - return err; + return pcibios_err_to_errno(err); } int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, @@ -94,7 +94,7 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, amd64_warn("%s: error writing to F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); - return err; + return pcibios_err_to_errno(err); } /* @@ -1025,8 +1025,10 @@ static int gpu_get_node_map(struct amd64_pvt *pvt) } ret = pci_read_config_dword(pdev, REG_LOCAL_NODE_TYPE_MAP, &tmp); - if (ret) + if (ret) { + ret = pcibios_err_to_errno(ret); goto out; + } gpu_node_map.node_count = FIELD_GET(LNTM_NODE_COUNT, tmp); gpu_node_map.base_node_id = FIELD_GET(LNTM_BASE_NODE_ID, tmp); -- cgit v1.2.3 From f8367a74aebf88dc8b58a0db6a6c90b4cb8fc9d3 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 27 May 2024 16:22:35 +0300 Subject: EDAC/igen6: Convert PCIBIOS_* return codes to errnos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit errcmd_enable_error_reporting() uses pci_{read,write}_config_word() that return PCIBIOS_* codes. The return code is then returned all the way into the probe function igen6_probe() that returns it as is. The probe functions, however, should return normal errnos. Convert PCIBIOS_* returns code using pcibios_err_to_errno() into normal errno before returning it from errcmd_enable_error_reporting(). Fixes: 10590a9d4f23 ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC") Signed-off-by: Ilpo Järvinen Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Qiuxu Zhuo Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240527132236.13875-2-ilpo.jarvinen@linux.intel.com --- drivers/edac/igen6_edac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index cdd8480e7368..dbe9fe5f2ca6 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -800,7 +800,7 @@ static int errcmd_enable_error_reporting(bool enable) rc = pci_read_config_word(imc->pdev, ERRCMD_OFFSET, &errcmd); if (rc) - return rc; + return pcibios_err_to_errno(rc); if (enable) errcmd |= ERRCMD_CE | ERRSTS_UE; @@ -809,7 +809,7 @@ static int errcmd_enable_error_reporting(bool enable) rc = pci_write_config_word(imc->pdev, ERRCMD_OFFSET, errcmd); if (rc) - return rc; + return pcibios_err_to_errno(rc); return 0; } -- cgit v1.2.3 From a535d59432370343058755100ee75ab03c0e3f91 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 30 May 2024 16:26:07 -0700 Subject: net: tls: fix marking packets as decrypted For TLS offload we mark packets with skb->decrypted to make sure they don't escape the host without getting encrypted first. The crypto state lives in the socket, so it may get detached by a call to skb_orphan(). As a safety check - the egress path drops all packets with skb->decrypted and no "crypto-safe" socket. The skb marking was added to sendpage only (and not sendmsg), because tls_device injected data into the TCP stack using sendpage. This special case was missed when sendpage got folded into sendmsg. Fixes: c5c37af6ecad ("tcp: Convert do_tcp_sendpages() to use MSG_SPLICE_PAGES") Signed-off-by: Jakub Kicinski Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/r/20240530232607.82686-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- net/ipv4/tcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 681b54e1f3a6..4d8cc2ebb64c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1165,6 +1165,9 @@ new_segment: process_backlog++; +#ifdef CONFIG_SKB_DECRYPTED + skb->decrypted = !!(flags & MSG_SENDPAGE_DECRYPTED); +#endif tcp_skb_entail(sk, skb); copy = size_goal; -- cgit v1.2.3 From a295ec52c8624883885396fde7b4df1a179627c3 Mon Sep 17 00:00:00 2001 From: "Kun(llfl)" Date: Thu, 9 May 2024 08:42:20 +0800 Subject: iommu/amd: Fix sysfs leak in iommu init During the iommu initialization, iommu_init_pci() adds sysfs nodes. However, these nodes aren't remove in free_iommu_resources() subsequently. Fixes: 39ab9555c241 ("iommu: Add sysfs bindings for struct iommu_device") Signed-off-by: Kun(llfl) Reviewed-by: Suravee Suthikulpanit Link: https://lore.kernel.org/r/c8e0d11c6ab1ee48299c288009cf9c5dae07b42d.1715215003.git.llfl@linux.alibaba.com Signed-off-by: Joerg Roedel --- drivers/iommu/amd/init.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index a18e74878f68..27e293727095 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -1626,8 +1626,17 @@ static void __init free_pci_segments(void) } } +static void __init free_sysfs(struct amd_iommu *iommu) +{ + if (iommu->iommu.dev) { + iommu_device_unregister(&iommu->iommu); + iommu_device_sysfs_remove(&iommu->iommu); + } +} + static void __init free_iommu_one(struct amd_iommu *iommu) { + free_sysfs(iommu); free_cwwb_sem(iommu); free_command_buffer(iommu); free_event_buffer(iommu); -- cgit v1.2.3 From cc8d89d0637990c66440a226f443d95340979a04 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 20 May 2024 20:14:44 +0100 Subject: iommu/dma: Fix domain init Despite carefully rewording the kerneldoc to describe the new direct interaction with dma_range_map, it seems I managed to confuse myself in removing the redundant force_aperture check and ended up making the code not do that at all. This led to dma_range_maps inadvertently being able to set iovad->start_pfn = 0, and all the nonsensical chaos which ensues from there. Restore the correct behaviour of constraining base_pfn to the domain aperture regardless of dma_range_map, and not trying to apply dma_range_map constraints to the basic IOVA domain since they will be properly handled with reserved regions later. Reported-by: Jon Hunter Reported-by: Jerry Snitselaar Fixes: ad4750b07d34 ("iommu/dma: Make limit checks self-contained") Signed-off-by: Robin Murphy Tested-by: Jerry Snitselaar Reviewed-by: Jerry Snitselaar Link: https://lore.kernel.org/r/721fa6baebb0924aa40db0b8fb86bcb4538434af.1716232484.git.robin.murphy@arm.com Signed-off-by: Joerg Roedel --- drivers/iommu/dma-iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index f731e4b2a417..43520e7275cc 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -686,15 +686,15 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, struct device *dev /* Check the domain allows at least some access to the device... */ if (map) { - dma_addr_t base = dma_range_map_min(map); - if (base > domain->geometry.aperture_end || + if (dma_range_map_min(map) > domain->geometry.aperture_end || dma_range_map_max(map) < domain->geometry.aperture_start) { pr_warn("specified DMA range outside IOMMU capability\n"); return -EFAULT; } - /* ...then finally give it a kicking to make sure it fits */ - base_pfn = max(base, domain->geometry.aperture_start) >> order; } + /* ...then finally give it a kicking to make sure it fits */ + base_pfn = max_t(unsigned long, base_pfn, + domain->geometry.aperture_start >> order); /* start_pfn is always nonzero for an already-initialised domain */ mutex_lock(&cookie->mutex); -- cgit v1.2.3 From 89e8a2366e3bce584b6c01549d5019c5cda1205e Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Tue, 28 May 2024 12:25:28 +0800 Subject: iommu: Return right value in iommu_sva_bind_device() iommu_sva_bind_device() should return either a sva bond handle or an ERR_PTR value in error cases. Existing drivers (idxd and uacce) only check the return value with IS_ERR(). This could potentially lead to a kernel NULL pointer dereference issue if the function returns NULL instead of an error pointer. In reality, this doesn't cause any problems because iommu_sva_bind_device() only returns NULL when the kernel is not configured with CONFIG_IOMMU_SVA. In this case, iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA) will return an error, and the device drivers won't call iommu_sva_bind_device() at all. Fixes: 26b25a2b98e4 ("iommu: Bind process address spaces to devices") Signed-off-by: Lu Baolu Reviewed-by: Jean-Philippe Brucker Reviewed-by: Kevin Tian Reviewed-by: Vasant Hegde Link: https://lore.kernel.org/r/20240528042528.71396-1-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 7bc8dff7cf6d..17b3f36ad843 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1533,7 +1533,7 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, static inline struct iommu_sva * iommu_sva_bind_device(struct device *dev, struct mm_struct *mm) { - return NULL; + return ERR_PTR(-ENODEV); } static inline void iommu_sva_unbind_device(struct iommu_sva *handle) -- cgit v1.2.3 From 39d762edd1f353c4446dbce83a18da4e491cc48e Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Tue, 28 May 2024 17:40:04 +0200 Subject: ASoC: dt-bindings: tlv320adc3xxx: Fix incorrect GPIO description Fix the description for the ti,dmdin-gpio1 and ti,dmclk-gpio2 properties to correctly describe that when configured as general purpose outputs (ADC3XXX_GPIO_GPO), the pins are available via the GPIO framework. Signed-off-by: Ricard Wanderlof Reviewed-by: Rob Herring (Arm) Link: https://msgid.link/r/20240528-tlv320adc3xxx-dt-gpio-fix-v1-1-209fb2c2f86f@axis.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml index ede14ca2c07a..8ac741f4cd56 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -58,8 +58,8 @@ properties: description: | Configuration for DMDIN/GPIO1 pin. - When ADC3XXX_GPIO_GPO is configured, this causes corresponding the - ALSA control "GPIOx Output" to appear, as a switch control. + When ADC3XXX_GPIO_GPO is selected, the pin may be controlled via the + GPIO framework, as pin number 0 on the device. ti,dmclk-gpio2: $ref: /schemas/types.yaml#/definitions/uint32 @@ -76,8 +76,8 @@ properties: description: | Configuration for DMCLK/GPIO2 pin. - When ADC3XXX_GPIO_GPO is configured, this causes corresponding the - ALSA control "GPIOx Output" to appear, as a switch control. + When ADC3XXX_GPIO_GPO is selected, the pin may be controlled via the + GPIO framework, as pin number 1 on the device. Note that there is currently no support for reading the GPIO pins as inputs. -- cgit v1.2.3 From 998a0a362b0b4cf501161c3319e6994d37c45a8c Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Wed, 29 May 2024 11:39:00 +0000 Subject: iommu/amd: Fix workqueue name Workqueue name length is crossing WQ_NAME_LEN limit. Fix it by changing name format. New format : "iopf_queue/amdvi-" kernel warning: [ 11.146912] workqueue: name exceeds WQ_NAME_LEN. Truncating to: iopf_queue/amdiommu-0xc002-iopf Reported-by: Borislav Petkov Fixes: 61928bab9d26 ("iommu/amd: Define per-IOMMU iopf_queue") Signed-off-by: Vasant Hegde Acked-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/r/20240529113900.5798-1-vasant.hegde@amd.com Signed-off-by: Joerg Roedel --- drivers/iommu/amd/ppr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 091423bb8aac..31d98a2a11a6 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -222,8 +222,7 @@ int amd_iommu_iopf_init(struct amd_iommu *iommu) if (iommu->iopf_queue) return ret; - snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), - "amdiommu-%#x-iopfq", + snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), "amdvi-%#x", PCI_SEG_DEVID_TO_SBDF(iommu->pci_seg->id, iommu->devid)); iommu->iopf_queue = iopf_queue_alloc(iommu->iopfq_name); -- cgit v1.2.3 From 48dc345a23b984c457d1c5878168d026c500618f Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Thu, 30 May 2024 07:11:18 +0000 Subject: iommu/amd: Check EFR[EPHSup] bit before enabling PPR Check for EFR[EPHSup] bit before enabling PPR. This bit must be set to enable PPR. Reported-by: Borislav Petkov Fixes: c4cb23111103 ("iommu/amd: Add support for enable/disable IOPF") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218900 Tested-by: Borislav Petkov Tested-by: Jean-Christophe Guillain Signed-off-by: Vasant Hegde Reviewed-by: Suravee Suthikulpanit Link: https://lore.kernel.org/r/20240530071118.10297-1-vasant.hegde@amd.com Signed-off-by: Joerg Roedel --- drivers/iommu/amd/amd_iommu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 2fde1302a584..2d5945c982bd 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -129,7 +129,8 @@ static inline int check_feature_gpt_level(void) static inline bool amd_iommu_gt_ppr_supported(void) { return (check_feature(FEATURE_GT) && - check_feature(FEATURE_PPR)); + check_feature(FEATURE_PPR) && + check_feature(FEATURE_EPHSUP)); } static inline u64 iommu_virt_to_phys(void *vaddr) -- cgit v1.2.3 From 526606b0a1998b0791b42c199d53550c3ba724b5 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Thu, 30 May 2024 08:48:01 +0000 Subject: iommu/amd: Fix Invalid wait context issue With commit c4cb23111103 ("iommu/amd: Add support for enable/disable IOPF") we are hitting below issue. This happens because in IOPF enablement path it holds spin lock with irq disable and then tries to take mutex lock. dmesg: ----- [ 0.938739] ============================= [ 0.938740] [ BUG: Invalid wait context ] [ 0.938742] 6.10.0-rc1+ #1 Not tainted [ 0.938745] ----------------------------- [ 0.938746] swapper/0/1 is trying to lock: [ 0.938748] ffffffff8c9f01d8 (&port_lock_key){....}-{3:3}, at: serial8250_console_write+0x78/0x4a0 [ 0.938767] other info that might help us debug this: [ 0.938768] context-{5:5} [ 0.938769] 7 locks held by swapper/0/1: [ 0.938772] #0: ffff888101a91310 (&group->mutex){+.+.}-{4:4}, at: bus_iommu_probe+0x70/0x160 [ 0.938790] #1: ffff888101d1f1b8 (&domain->lock){....}-{3:3}, at: amd_iommu_attach_device+0xa5/0x700 [ 0.938799] #2: ffff888101cc3d18 (&dev_data->lock){....}-{3:3}, at: amd_iommu_attach_device+0xc5/0x700 [ 0.938806] #3: ffff888100052830 (&iommu->lock){....}-{2:2}, at: amd_iommu_iopf_add_device+0x3f/0xa0 [ 0.938813] #4: ffffffff8945a340 (console_lock){+.+.}-{0:0}, at: _printk+0x48/0x50 [ 0.938822] #5: ffffffff8945a390 (console_srcu){....}-{0:0}, at: console_flush_all+0x58/0x4e0 [ 0.938867] #6: ffffffff82459f80 (console_owner){....}-{0:0}, at: console_flush_all+0x1f0/0x4e0 [ 0.938872] stack backtrace: [ 0.938874] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 6.10.0-rc1+ #1 [ 0.938877] Hardware name: HP HP EliteBook 745 G3/807E, BIOS N73 Ver. 01.39 04/16/2019 Fix above issue by re-arranging code in attach device path: - move device PASID/IOPF enablement outside lock in AMD IOMMU driver. This is safe as core layer holds group->mutex lock before calling iommu_ops->attach_dev. Reported-by: Borislav Petkov Reported-by: Mikhail Gavrilov Reported-by: Chris Bainbridge Fixes: c4cb23111103 ("iommu/amd: Add support for enable/disable IOPF") Tested-by: Borislav Petkov Tested-by: Chris Bainbridge Tested-by: Mikhail Gavrilov Signed-off-by: Vasant Hegde Link: https://lore.kernel.org/r/20240530084801.10758-1-vasant.hegde@amd.com Signed-off-by: Joerg Roedel --- drivers/iommu/amd/iommu.c | 48 +++++++++++++++++++++++------------------------ drivers/iommu/amd/ppr.c | 22 ++++------------------ 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 52d83730a22a..c2703599bb16 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2032,7 +2032,6 @@ static int do_attach(struct iommu_dev_data *dev_data, struct protection_domain *domain) { struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data); - struct pci_dev *pdev; int ret = 0; /* Update data structures */ @@ -2047,30 +2046,13 @@ static int do_attach(struct iommu_dev_data *dev_data, domain->dev_iommu[iommu->index] += 1; domain->dev_cnt += 1; - pdev = dev_is_pci(dev_data->dev) ? to_pci_dev(dev_data->dev) : NULL; + /* Setup GCR3 table */ if (pdom_is_sva_capable(domain)) { ret = init_gcr3_table(dev_data, domain); if (ret) return ret; - - if (pdev) { - pdev_enable_caps(pdev); - - /* - * Device can continue to function even if IOPF - * enablement failed. Hence in error path just - * disable device PRI support. - */ - if (amd_iommu_iopf_add_device(iommu, dev_data)) - pdev_disable_cap_pri(pdev); - } - } else if (pdev) { - pdev_enable_cap_ats(pdev); } - /* Update device table */ - amd_iommu_dev_update_dte(dev_data, true); - return ret; } @@ -2163,6 +2145,11 @@ static void detach_device(struct device *dev) do_detach(dev_data); +out: + spin_unlock(&dev_data->lock); + + spin_unlock_irqrestore(&domain->lock, flags); + /* Remove IOPF handler */ if (ppr) amd_iommu_iopf_remove_device(iommu, dev_data); @@ -2170,10 +2157,6 @@ static void detach_device(struct device *dev) if (dev_is_pci(dev)) pdev_disable_caps(to_pci_dev(dev)); -out: - spin_unlock(&dev_data->lock); - - spin_unlock_irqrestore(&domain->lock, flags); } static struct iommu_device *amd_iommu_probe_device(struct device *dev) @@ -2485,6 +2468,7 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev); struct protection_domain *domain = to_pdomain(dom); struct amd_iommu *iommu = get_amd_iommu_from_dev(dev); + struct pci_dev *pdev; int ret; /* @@ -2517,7 +2501,23 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, } #endif - iommu_completion_wait(iommu); + pdev = dev_is_pci(dev_data->dev) ? to_pci_dev(dev_data->dev) : NULL; + if (pdev && pdom_is_sva_capable(domain)) { + pdev_enable_caps(pdev); + + /* + * Device can continue to function even if IOPF + * enablement failed. Hence in error path just + * disable device PRI support. + */ + if (amd_iommu_iopf_add_device(iommu, dev_data)) + pdev_disable_cap_pri(pdev); + } else if (pdev) { + pdev_enable_cap_ats(pdev); + } + + /* Update device table */ + amd_iommu_dev_update_dte(dev_data, true); return ret; } diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 31d98a2a11a6..7c67d69f0b8c 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -248,40 +248,26 @@ void amd_iommu_page_response(struct device *dev, struct iopf_fault *evt, int amd_iommu_iopf_add_device(struct amd_iommu *iommu, struct iommu_dev_data *dev_data) { - unsigned long flags; int ret = 0; if (!dev_data->pri_enabled) return ret; - raw_spin_lock_irqsave(&iommu->lock, flags); - - if (!iommu->iopf_queue) { - ret = -EINVAL; - goto out_unlock; - } + if (!iommu->iopf_queue) + return -EINVAL; ret = iopf_queue_add_device(iommu->iopf_queue, dev_data->dev); if (ret) - goto out_unlock; + return ret; dev_data->ppr = true; - -out_unlock: - raw_spin_unlock_irqrestore(&iommu->lock, flags); - return ret; + return 0; } /* Its assumed that caller has verified that device was added to iopf queue */ void amd_iommu_iopf_remove_device(struct amd_iommu *iommu, struct iommu_dev_data *dev_data) { - unsigned long flags; - - raw_spin_lock_irqsave(&iommu->lock, flags); - iopf_queue_remove_device(iommu->iopf_queue, dev_data->dev); dev_data->ppr = false; - - raw_spin_unlock_irqrestore(&iommu->lock, flags); } -- cgit v1.2.3 From 415ce0ea55c5a3afea501a773e002be9ed7149f5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 3 Jun 2024 13:56:53 -0600 Subject: io_uring/napi: fix timeout calculation Not quite sure what __io_napi_adjust_timeout() was attemping to do, it's adjusting both the NAPI timeout and the general overall timeout, and calculating a value that is never used. The overall timeout is a super set of the NAPI timeout, and doesn't need adjusting. The only thing we really need to care about is that the NAPI timeout doesn't exceed the overall timeout. If a user asked for a timeout of eg 5 usec and NAPI timeout is 10 usec, then we should not spin for 10 usec. While in there, sanitize the time checking a bit. If we have a negative value in the passed in timeout, discard it. Round up the value as well, so we don't end up with a NAPI timeout for the majority of the wait, with only a tiny sleep value at the end. Hence the only case we need to care about is if the NAPI timeout is larger than the overall timeout. If it is, cap the NAPI timeout at what the overall timeout is. Cc: stable@vger.kernel.org Fixes: 8d0c12a80cde ("io-uring: add napi busy poll support") Reported-by: Lewis Baker Signed-off-by: Jens Axboe --- io_uring/napi.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/io_uring/napi.c b/io_uring/napi.c index 883a1a665907..8c18ede595c4 100644 --- a/io_uring/napi.c +++ b/io_uring/napi.c @@ -261,12 +261,14 @@ int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg) } /* - * __io_napi_adjust_timeout() - Add napi id to the busy poll list + * __io_napi_adjust_timeout() - adjust busy loop timeout * @ctx: pointer to io-uring context structure * @iowq: pointer to io wait queue * @ts: pointer to timespec or NULL * * Adjust the busy loop timeout according to timespec and busy poll timeout. + * If the specified NAPI timeout is bigger than the wait timeout, then adjust + * the NAPI timeout accordingly. */ void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iowq, struct timespec64 *ts) @@ -274,16 +276,16 @@ void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iow unsigned int poll_to = READ_ONCE(ctx->napi_busy_poll_to); if (ts) { - struct timespec64 poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to); - - if (timespec64_compare(ts, &poll_to_ts) > 0) { - *ts = timespec64_sub(*ts, poll_to_ts); - } else { - u64 to = timespec64_to_ns(ts); - - do_div(to, 1000); - ts->tv_sec = 0; - ts->tv_nsec = 0; + struct timespec64 poll_to_ts; + + poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to); + if (timespec64_compare(ts, &poll_to_ts) < 0) { + s64 poll_to_ns = timespec64_to_ns(ts); + if (poll_to_ns > 0) { + u64 val = poll_to_ns + 999; + do_div(val, (s64) 1000); + poll_to = val; + } } } -- cgit v1.2.3 From 91215f70ea8541e9011c0b48f8b59b9e0ce6953b Mon Sep 17 00:00:00 2001 From: Su Hui Date: Tue, 4 Jun 2024 20:12:43 +0800 Subject: io_uring/io-wq: avoid garbage value of 'match' in io_wq_enqueue() Clang static checker (scan-build) warning: o_uring/io-wq.c:line 1051, column 3 The expression is an uninitialized value. The computed value will also be garbage. 'match.nr_pending' is used in io_acct_cancel_pending_work(), but it is not fully initialized. Change the order of assignment for 'match' to fix this problem. Fixes: 42abc95f05bf ("io-wq: decouple work_list protection from the big wqe->lock") Signed-off-by: Su Hui Link: https://lore.kernel.org/r/20240604121242.2661244-1-suhui@nfschina.com Signed-off-by: Jens Axboe --- io_uring/io-wq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index d1c47a9d9215..7d3316fe9bfc 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -927,7 +927,11 @@ void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work) { struct io_wq_acct *acct = io_work_get_acct(wq, work); unsigned long work_flags = work->flags; - struct io_cb_cancel_data match; + struct io_cb_cancel_data match = { + .fn = io_wq_work_match_item, + .data = work, + .cancel_all = false, + }; bool do_create; /* @@ -965,10 +969,6 @@ void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work) raw_spin_unlock(&wq->lock); /* fatal condition, failed to create the first worker */ - match.fn = io_wq_work_match_item, - match.data = work, - match.cancel_all = false, - io_acct_cancel_pending_work(wq, acct, &match); } } -- cgit v1.2.3 From 73254a297c2dd094abec7c9efee32455ae875bdf Mon Sep 17 00:00:00 2001 From: Hagar Hemdan Date: Tue, 4 Jun 2024 13:05:27 +0000 Subject: io_uring: fix possible deadlock in io_register_iowq_max_workers() The io_register_iowq_max_workers() function calls io_put_sq_data(), which acquires the sqd->lock without releasing the uring_lock. Similar to the commit 009ad9f0c6ee ("io_uring: drop ctx->uring_lock before acquiring sqd->lock"), this can lead to a potential deadlock situation. To resolve this issue, the uring_lock is released before calling io_put_sq_data(), and then it is re-acquired after the function call. This change ensures that the locks are acquired in the correct order, preventing the possibility of a deadlock. Suggested-by: Maximilian Heyne Signed-off-by: Hagar Hemdan Link: https://lore.kernel.org/r/20240604130527.3597-1-hagarhem@amazon.com Signed-off-by: Jens Axboe --- io_uring/register.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io_uring/register.c b/io_uring/register.c index ef8c908346a4..c0010a66a6f2 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -355,8 +355,10 @@ static __cold int io_register_iowq_max_workers(struct io_ring_ctx *ctx, } if (sqd) { + mutex_unlock(&ctx->uring_lock); mutex_unlock(&sqd->lock); io_put_sq_data(sqd); + mutex_lock(&ctx->uring_lock); } if (copy_to_user(arg, new_count, sizeof(new_count))) @@ -380,8 +382,10 @@ static __cold int io_register_iowq_max_workers(struct io_ring_ctx *ctx, return 0; err: if (sqd) { + mutex_unlock(&ctx->uring_lock); mutex_unlock(&sqd->lock); io_put_sq_data(sqd); + mutex_lock(&ctx->uring_lock); } return ret; } -- cgit v1.2.3 From 87bb39ed40bdf1596b8820e800226e24eb642677 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:43 +0100 Subject: KVM: arm64: Reintroduce __sve_save_state Now that the hypervisor is handling the host sve state in protected mode, it needs to be able to save it. This reverts commit e66425fc9ba3 ("KVM: arm64: Remove unused __sve_save_state"). Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-2-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kvm/hyp/fpsimd.S | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 3e80464f8953..2ab23589339a 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -111,6 +111,7 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu); void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); +void __sve_save_state(void *sve_pffr, u32 *fpsr); void __sve_restore_state(void *sve_pffr, u32 *fpsr); u64 __guest_enter(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/kvm/hyp/fpsimd.S b/arch/arm64/kvm/hyp/fpsimd.S index 61e6f3ba7b7d..e950875e31ce 100644 --- a/arch/arm64/kvm/hyp/fpsimd.S +++ b/arch/arm64/kvm/hyp/fpsimd.S @@ -25,3 +25,9 @@ SYM_FUNC_START(__sve_restore_state) sve_load 0, x1, x2, 3 ret SYM_FUNC_END(__sve_restore_state) + +SYM_FUNC_START(__sve_save_state) + mov x2, #1 + sve_save 0, x1, x2, 3 + ret +SYM_FUNC_END(__sve_save_state) -- cgit v1.2.3 From 45f4ea9bcfe909b3461059990b1e232e55dde809 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:44 +0100 Subject: KVM: arm64: Fix prototype for __sve_save_state/__sve_restore_state Since the prototypes for __sve_save_state/__sve_restore_state at hyp were added, the underlying macro has acquired a third parameter for saving/restoring ffr. Fix the prototypes to account for the third parameter, and restore the ffr for the guest since it is saved. Suggested-by: Mark Brown Signed-off-by: Fuad Tabba Reviewed-by: Mark Brown Link: https://lore.kernel.org/r/20240603122852.3923848-3-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_hyp.h | 4 ++-- arch/arm64/kvm/hyp/include/hyp/switch.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 2ab23589339a..686cce7e4e96 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -111,8 +111,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu); void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); -void __sve_save_state(void *sve_pffr, u32 *fpsr); -void __sve_restore_state(void *sve_pffr, u32 *fpsr); +void __sve_save_state(void *sve_pffr, u32 *fpsr, int save_ffr); +void __sve_restore_state(void *sve_pffr, u32 *fpsr, int restore_ffr); u64 __guest_enter(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index a92566f36022..d58933ae8fd5 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -316,7 +316,8 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) { sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); __sve_restore_state(vcpu_sve_pffr(vcpu), - &vcpu->arch.ctxt.fp_regs.fpsr); + &vcpu->arch.ctxt.fp_regs.fpsr, + true); write_sysreg_el1(__vcpu_sys_reg(vcpu, ZCR_EL1), SYS_ZCR); } -- cgit v1.2.3 From 6d8fb3cbf7e06431a607c30c1bc4cd53a62c220a Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:45 +0100 Subject: KVM: arm64: Abstract set/clear of CPTR_EL2 bits behind helper The same traps controlled by CPTR_EL2 or CPACR_EL1 need to be toggled in different parts of the code, but the exact bits and their polarity differ between these two formats and the mode (vhe/nvhe/hvhe). To reduce the amount of duplicated code and the chance of getting the wrong bit/polarity or missing a field, abstract the set/clear of CPTR_EL2 bits behind a helper. Since (h)VHE is the way of the future, use the CPACR_EL1 format, which is a subset of the VHE CPTR_EL2, as a reference. No functional change intended. Suggested-by: Oliver Upton Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-4-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_arm.h | 6 ++++ arch/arm64/include/asm/kvm_emulate.h | 62 +++++++++++++++++++++++++++++++++ arch/arm64/kvm/hyp/include/hyp/switch.h | 18 +++------- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 6 +--- 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index e01bb5ca13b7..b2adc2c6c82a 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -305,6 +305,12 @@ GENMASK(19, 14) | \ BIT(11)) +#define CPTR_VHE_EL2_RES0 (GENMASK(63, 32) | \ + GENMASK(27, 26) | \ + GENMASK(23, 22) | \ + GENMASK(19, 18) | \ + GENMASK(15, 0)) + /* Hyp Debug Configuration Register bits */ #define MDCR_EL2_E2TB_MASK (UL(0x3)) #define MDCR_EL2_E2TB_SHIFT (UL(24)) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 501e3e019c93..2d7a0bdf9d03 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -557,6 +557,68 @@ static __always_inline void kvm_incr_pc(struct kvm_vcpu *vcpu) vcpu_set_flag((v), e); \ } while (0) +#define __build_check_all_or_none(r, bits) \ + BUILD_BUG_ON(((r) & (bits)) && ((r) & (bits)) != (bits)) + +#define __cpacr_to_cptr_clr(clr, set) \ + ({ \ + u64 cptr = 0; \ + \ + if ((set) & CPACR_ELx_FPEN) \ + cptr |= CPTR_EL2_TFP; \ + if ((set) & CPACR_ELx_ZEN) \ + cptr |= CPTR_EL2_TZ; \ + if ((set) & CPACR_ELx_SMEN) \ + cptr |= CPTR_EL2_TSM; \ + if ((clr) & CPACR_ELx_TTA) \ + cptr |= CPTR_EL2_TTA; \ + if ((clr) & CPTR_EL2_TAM) \ + cptr |= CPTR_EL2_TAM; \ + if ((clr) & CPTR_EL2_TCPAC) \ + cptr |= CPTR_EL2_TCPAC; \ + \ + cptr; \ + }) + +#define __cpacr_to_cptr_set(clr, set) \ + ({ \ + u64 cptr = 0; \ + \ + if ((clr) & CPACR_ELx_FPEN) \ + cptr |= CPTR_EL2_TFP; \ + if ((clr) & CPACR_ELx_ZEN) \ + cptr |= CPTR_EL2_TZ; \ + if ((clr) & CPACR_ELx_SMEN) \ + cptr |= CPTR_EL2_TSM; \ + if ((set) & CPACR_ELx_TTA) \ + cptr |= CPTR_EL2_TTA; \ + if ((set) & CPTR_EL2_TAM) \ + cptr |= CPTR_EL2_TAM; \ + if ((set) & CPTR_EL2_TCPAC) \ + cptr |= CPTR_EL2_TCPAC; \ + \ + cptr; \ + }) + +#define cpacr_clear_set(clr, set) \ + do { \ + BUILD_BUG_ON((set) & CPTR_VHE_EL2_RES0); \ + BUILD_BUG_ON((clr) & CPACR_ELx_E0POE); \ + __build_check_all_or_none((clr), CPACR_ELx_FPEN); \ + __build_check_all_or_none((set), CPACR_ELx_FPEN); \ + __build_check_all_or_none((clr), CPACR_ELx_ZEN); \ + __build_check_all_or_none((set), CPACR_ELx_ZEN); \ + __build_check_all_or_none((clr), CPACR_ELx_SMEN); \ + __build_check_all_or_none((set), CPACR_ELx_SMEN); \ + \ + if (has_vhe() || has_hvhe()) \ + sysreg_clear_set(cpacr_el1, clr, set); \ + else \ + sysreg_clear_set(cptr_el2, \ + __cpacr_to_cptr_clr(clr, set), \ + __cpacr_to_cptr_set(clr, set));\ + } while (0) + static __always_inline void kvm_write_cptr_el2(u64 val) { if (has_vhe() || has_hvhe()) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index d58933ae8fd5..055d2ca7264e 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -331,7 +331,6 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) { bool sve_guest; u8 esr_ec; - u64 reg; if (!system_supports_fpsimd()) return false; @@ -354,19 +353,10 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) /* Valid trap. Switch the context: */ /* First disable enough traps to allow us to update the registers */ - if (has_vhe() || has_hvhe()) { - reg = CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN; - if (sve_guest) - reg |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; - - sysreg_clear_set(cpacr_el1, 0, reg); - } else { - reg = CPTR_EL2_TFP; - if (sve_guest) - reg |= CPTR_EL2_TZ; - - sysreg_clear_set(cptr_el2, reg, 0); - } + if (sve_guest) + cpacr_clear_set(0, CPACR_ELx_FPEN | CPACR_ELx_ZEN); + else + cpacr_clear_set(0, CPACR_ELx_FPEN); isb(); /* Write out the host state if it's in the registers */ diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index d5c48dc98f67..f71394d0e32a 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -405,11 +405,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) handle_host_smc(host_ctxt); break; case ESR_ELx_EC_SVE: - if (has_hvhe()) - sysreg_clear_set(cpacr_el1, 0, (CPACR_EL1_ZEN_EL1EN | - CPACR_EL1_ZEN_EL0EN)); - else - sysreg_clear_set(cptr_el2, CPTR_EL2_TZ, 0); + cpacr_clear_set(0, CPACR_ELx_ZEN); isb(); sve_cond_update_zcr_vq(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); break; -- cgit v1.2.3 From e511e08a9f496948b13aac50610f2d17335f56c3 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:46 +0100 Subject: KVM: arm64: Specialize handling of host fpsimd state on trap In subsequent patches, n/vhe will diverge on saving the host fpsimd/sve state when taking a guest fpsimd/sve trap. Add a specialized helper to handle it. No functional change intended. Reviewed-by: Mark Brown Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-5-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 4 +++- arch/arm64/kvm/hyp/nvhe/switch.c | 5 +++++ arch/arm64/kvm/hyp/vhe/switch.c | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 055d2ca7264e..9b904c858df0 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -321,6 +321,8 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) write_sysreg_el1(__vcpu_sys_reg(vcpu, ZCR_EL1), SYS_ZCR); } +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu); + /* * We trap the first access to the FP/SIMD to save the host context and * restore the guest context lazily. @@ -361,7 +363,7 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) /* Write out the host state if it's in the registers */ if (host_owns_fp_regs()) - __fpsimd_save_state(*host_data_ptr(fpsimd_state)); + kvm_hyp_save_fpsimd_host(vcpu); /* Restore the guest state */ if (sve_guest) diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 6758cd905570..019f863922fa 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -182,6 +182,11 @@ static bool kvm_handle_pvm_sys64(struct kvm_vcpu *vcpu, u64 *exit_code) kvm_handle_pvm_sysreg(vcpu, exit_code)); } +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) +{ + __fpsimd_save_state(*host_data_ptr(fpsimd_state)); +} + static const exit_handler_fn hyp_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = NULL, [ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32, diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index d7af5f46f22a..20073579e9f5 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -262,6 +262,11 @@ static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code) return true; } +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) +{ + __fpsimd_save_state(*host_data_ptr(fpsimd_state)); +} + static const exit_handler_fn hyp_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = NULL, [ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32, -- cgit v1.2.3 From 66d5b53e20a6e00b7ce3b652a3e2db967f7b33d0 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:47 +0100 Subject: KVM: arm64: Allocate memory mapped at hyp for host sve state in pKVM Protected mode needs to maintain (save/restore) the host's sve state, rather than relying on the host kernel to do that. This is to avoid leaking information to the host about guests and the type of operations they are performing. As a first step towards that, allocate memory mapped at hyp, per cpu, for the host sve state. The following patch will use this memory to save/restore the host state. Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-6-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 17 ++++++++++ arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/include/asm/kvm_pkvm.h | 9 ++++++ arch/arm64/kvm/arm.c | 68 +++++++++++++++++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 ++ arch/arm64/kvm/hyp/nvhe/setup.c | 24 ++++++++++++++ arch/arm64/kvm/reset.c | 3 ++ 7 files changed, 124 insertions(+) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 8170c04fde91..90df7ccec5f4 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -76,6 +76,7 @@ static inline enum kvm_mode kvm_get_mode(void) { return KVM_MODE_NONE; }; DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use); extern unsigned int __ro_after_init kvm_sve_max_vl; +extern unsigned int __ro_after_init kvm_host_sve_max_vl; int __init kvm_arm_init_sve(void); u32 __attribute_const__ kvm_target_cpu(void); @@ -521,6 +522,20 @@ struct kvm_cpu_context { u64 *vncr_array; }; +struct cpu_sve_state { + __u64 zcr_el1; + + /* + * Ordering is important since __sve_save_state/__sve_restore_state + * relies on it. + */ + __u32 fpsr; + __u32 fpcr; + + /* Must be SVE_VQ_BYTES (128 bit) aligned. */ + __u8 sve_regs[]; +}; + /* * This structure is instantiated on a per-CPU basis, and contains * data that is: @@ -534,7 +549,9 @@ struct kvm_cpu_context { */ struct kvm_host_data { struct kvm_cpu_context host_ctxt; + struct user_fpsimd_state *fpsimd_state; /* hyp VA */ + struct cpu_sve_state *sve_state; /* hyp VA */ /* Ownership of the FP regs */ enum { diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 686cce7e4e96..b05bceca3385 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -143,5 +143,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val); extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); +extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index ad9cfb5c1ff4..cd56acd9a842 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -128,4 +128,13 @@ static inline unsigned long hyp_ffa_proxy_pages(void) return (2 * KVM_FFA_MBOX_NR_PAGES) + DIV_ROUND_UP(desc_max, PAGE_SIZE); } +static inline size_t pkvm_host_sve_state_size(void) +{ + if (!system_supports_sve()) + return 0; + + return size_add(sizeof(struct cpu_sve_state), + SVE_SIG_REGS_SIZE(sve_vq_from_vl(kvm_host_sve_max_vl))); +} + #endif /* __ARM64_KVM_PKVM_H__ */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 9996a989b52e..1acf7415e831 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1931,6 +1931,11 @@ static unsigned long nvhe_percpu_order(void) return size ? get_order(size) : 0; } +static size_t pkvm_host_sve_state_order(void) +{ + return get_order(pkvm_host_sve_state_size()); +} + /* A lookup table holding the hypervisor VA for each vector slot */ static void *hyp_spectre_vector_selector[BP_HARDEN_EL2_SLOTS]; @@ -2310,12 +2315,20 @@ static void __init teardown_subsystems(void) static void __init teardown_hyp_mode(void) { + bool free_sve = system_supports_sve() && is_protected_kvm_enabled(); int cpu; free_hyp_pgds(); for_each_possible_cpu(cpu) { free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); free_pages(kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu], nvhe_percpu_order()); + + if (free_sve) { + struct cpu_sve_state *sve_state; + + sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; + free_pages((unsigned long) sve_state, pkvm_host_sve_state_order()); + } } } @@ -2398,6 +2411,50 @@ static int __init kvm_hyp_init_protection(u32 hyp_va_bits) return 0; } +static int init_pkvm_host_sve_state(void) +{ + int cpu; + + if (!system_supports_sve()) + return 0; + + /* Allocate pages for host sve state in protected mode. */ + for_each_possible_cpu(cpu) { + struct page *page = alloc_pages(GFP_KERNEL, pkvm_host_sve_state_order()); + + if (!page) + return -ENOMEM; + + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = page_address(page); + } + + /* + * Don't map the pages in hyp since these are only used in protected + * mode, which will (re)create its own mapping when initialized. + */ + + return 0; +} + +/* + * Finalizes the initialization of hyp mode, once everything else is initialized + * and the initialziation process cannot fail. + */ +static void finalize_init_hyp_mode(void) +{ + int cpu; + + if (!is_protected_kvm_enabled() || !system_supports_sve()) + return; + + for_each_possible_cpu(cpu) { + struct cpu_sve_state *sve_state; + + sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = kern_hyp_va(sve_state); + } +} + static void pkvm_hyp_init_ptrauth(void) { struct kvm_cpu_context *hyp_ctxt; @@ -2566,6 +2623,10 @@ static int __init init_hyp_mode(void) goto out_err; } + err = init_pkvm_host_sve_state(); + if (err) + goto out_err; + err = kvm_hyp_init_protection(hyp_va_bits); if (err) { kvm_err("Failed to init hyp memory protection\n"); @@ -2730,6 +2791,13 @@ static __init int kvm_arm_init(void) if (err) goto out_subs; + /* + * This should be called after initialization is done and failure isn't + * possible anymore. + */ + if (!in_hyp_mode) + finalize_init_hyp_mode(); + kvm_arm_initialised = true; return 0; diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 16aa4875ddb8..25e9a94f6d76 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -18,6 +18,8 @@ unsigned long __icache_flags; /* Used by kvm_get_vttbr(). */ unsigned int kvm_arm_vmid_bits; +unsigned int kvm_host_sve_max_vl; + /* * Set trap register values based on features in ID_AA64PFR0. */ diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 859f22f754d3..3fae42479598 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -67,6 +67,28 @@ static int divide_memory_pool(void *virt, unsigned long size) return 0; } +static int pkvm_create_host_sve_mappings(void) +{ + void *start, *end; + int ret, i; + + if (!system_supports_sve()) + return 0; + + for (i = 0; i < hyp_nr_cpus; i++) { + struct kvm_host_data *host_data = per_cpu_ptr(&kvm_host_data, i); + struct cpu_sve_state *sve_state = host_data->sve_state; + + start = kern_hyp_va(sve_state); + end = start + PAGE_ALIGN(pkvm_host_sve_state_size()); + ret = pkvm_create_mappings(start, end, PAGE_HYP); + if (ret) + return ret; + } + + return 0; +} + static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, unsigned long *per_cpu_base, u32 hyp_va_bits) @@ -125,6 +147,8 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, return ret; } + pkvm_create_host_sve_mappings(); + /* * Map the host sections RO in the hypervisor, but transfer the * ownership from the host to the hypervisor itself to make sure they diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index 1b7b58cb121f..3fc8ca164dbe 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -32,6 +32,7 @@ /* Maximum phys_shift supported for any VM on this host */ static u32 __ro_after_init kvm_ipa_limit; +unsigned int __ro_after_init kvm_host_sve_max_vl; /* * ARMv8 Reset Values @@ -51,6 +52,8 @@ int __init kvm_arm_init_sve(void) { if (system_supports_sve()) { kvm_sve_max_vl = sve_max_virtualisable_vl(); + kvm_host_sve_max_vl = sve_max_vl(); + kvm_nvhe_sym(kvm_host_sve_max_vl) = kvm_host_sve_max_vl; /* * The get_sve_reg()/set_sve_reg() ioctl interface will need -- cgit v1.2.3 From b5b9955617bc0b41546f2fa7c3dbcc048b43dc82 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:48 +0100 Subject: KVM: arm64: Eagerly restore host fpsimd/sve state in pKVM When running in protected mode we don't want to leak protected guest state to the host, including whether a guest has used fpsimd/sve. Therefore, eagerly restore the host state on guest exit when running in protected mode, which happens only if the guest has used fpsimd/sve. Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-7-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/include/hyp/switch.h | 13 ++++++- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 67 +++++++++++++++++++++++++++++++-- arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 + arch/arm64/kvm/hyp/nvhe/switch.c | 16 +++++++- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 9b904c858df0..0c4de44534b7 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -321,6 +321,17 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) write_sysreg_el1(__vcpu_sys_reg(vcpu, ZCR_EL1), SYS_ZCR); } +static inline void __hyp_sve_save_host(void) +{ + struct cpu_sve_state *sve_state = *host_data_ptr(sve_state); + + sve_state->zcr_el1 = read_sysreg_el1(SYS_ZCR); + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); + __sve_save_state(sve_state->sve_regs + sve_ffr_offset(kvm_host_sve_max_vl), + &sve_state->fpsr, + true); +} + static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu); /* @@ -355,7 +366,7 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) /* Valid trap. Switch the context: */ /* First disable enough traps to allow us to update the registers */ - if (sve_guest) + if (sve_guest || (is_protected_kvm_enabled() && system_supports_sve())) cpacr_clear_set(0, CPACR_ELx_FPEN | CPACR_ELx_ZEN); else cpacr_clear_set(0, CPACR_ELx_FPEN); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index f71394d0e32a..bd93b8a9e172 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -23,20 +23,80 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); +static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) +{ + __vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_el1(SYS_ZCR); + /* + * On saving/restoring guest sve state, always use the maximum VL for + * the guest. The layout of the data when saving the sve state depends + * on the VL, so use a consistent (i.e., the maximum) guest VL. + */ + sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); + __sve_save_state(vcpu_sve_pffr(vcpu), &vcpu->arch.ctxt.fp_regs.fpsr, true); + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); +} + +static void __hyp_sve_restore_host(void) +{ + struct cpu_sve_state *sve_state = *host_data_ptr(sve_state); + + /* + * On saving/restoring host sve state, always use the maximum VL for + * the host. The layout of the data when saving the sve state depends + * on the VL, so use a consistent (i.e., the maximum) host VL. + * + * Setting ZCR_EL2 to ZCR_ELx_LEN_MASK sets the effective length + * supported by the system (or limited at EL3). + */ + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); + __sve_restore_state(sve_state->sve_regs + sve_ffr_offset(kvm_host_sve_max_vl), + &sve_state->fpsr, + true); + write_sysreg_el1(sve_state->zcr_el1, SYS_ZCR); +} + +static void fpsimd_sve_flush(void) +{ + *host_data_ptr(fp_owner) = FP_STATE_HOST_OWNED; +} + +static void fpsimd_sve_sync(struct kvm_vcpu *vcpu) +{ + if (!guest_owns_fp_regs()) + return; + + cpacr_clear_set(0, CPACR_ELx_FPEN | CPACR_ELx_ZEN); + isb(); + + if (vcpu_has_sve(vcpu)) + __hyp_sve_save_guest(vcpu); + else + __fpsimd_save_state(&vcpu->arch.ctxt.fp_regs); + + if (system_supports_sve()) + __hyp_sve_restore_host(); + else + __fpsimd_restore_state(*host_data_ptr(fpsimd_state)); + + *host_data_ptr(fp_owner) = FP_STATE_HOST_OWNED; +} + static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) { struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu; + fpsimd_sve_flush(); + hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state); - hyp_vcpu->vcpu.arch.sve_max_vl = host_vcpu->arch.sve_max_vl; + /* Limit guest vector length to the maximum supported by the host. */ + hyp_vcpu->vcpu.arch.sve_max_vl = min(host_vcpu->arch.sve_max_vl, kvm_host_sve_max_vl); hyp_vcpu->vcpu.arch.hw_mmu = host_vcpu->arch.hw_mmu; hyp_vcpu->vcpu.arch.hcr_el2 = host_vcpu->arch.hcr_el2; hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; - hyp_vcpu->vcpu.arch.cptr_el2 = host_vcpu->arch.cptr_el2; hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags; @@ -54,10 +114,11 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) struct vgic_v3_cpu_if *host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3; unsigned int i; + fpsimd_sve_sync(&hyp_vcpu->vcpu); + host_vcpu->arch.ctxt = hyp_vcpu->vcpu.arch.ctxt; host_vcpu->arch.hcr_el2 = hyp_vcpu->vcpu.arch.hcr_el2; - host_vcpu->arch.cptr_el2 = hyp_vcpu->vcpu.arch.cptr_el2; host_vcpu->arch.fault = hyp_vcpu->vcpu.arch.fault; diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 25e9a94f6d76..feb27b4ce459 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -588,6 +588,8 @@ unlock: if (ret) unmap_donated_memory(hyp_vcpu, sizeof(*hyp_vcpu)); + hyp_vcpu->vcpu.arch.cptr_el2 = kvm_get_reset_cptr_el2(&hyp_vcpu->vcpu); + return ret; } diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 019f863922fa..bef74de7065b 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -184,7 +184,21 @@ static bool kvm_handle_pvm_sys64(struct kvm_vcpu *vcpu, u64 *exit_code) static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) { - __fpsimd_save_state(*host_data_ptr(fpsimd_state)); + /* + * Non-protected kvm relies on the host restoring its sve state. + * Protected kvm restores the host's sve state as not to reveal that + * fpsimd was used by a guest nor leak upper sve bits. + */ + if (unlikely(is_protected_kvm_enabled() && system_supports_sve())) { + __hyp_sve_save_host(); + + /* Re-enable SVE traps if not supported for the guest vcpu. */ + if (!vcpu_has_sve(vcpu)) + cpacr_clear_set(CPACR_ELx_ZEN, 0); + + } else { + __fpsimd_save_state(*host_data_ptr(fpsimd_state)); + } } static const exit_handler_fn hyp_exit_handlers[] = { -- cgit v1.2.3 From 1696fc2174dbab12228ea9ec4c213d6aeea348f8 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:49 +0100 Subject: KVM: arm64: Consolidate initializing the host data's fpsimd_state/sve in pKVM Now that we have introduced finalize_init_hyp_mode(), lets consolidate the initializing of the host_data fpsimd_state and sve state. Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Reviewed-by: Mark Brown Link: https://lore.kernel.org/r/20240603122852.3923848-8-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 10 ++++++++-- arch/arm64/kvm/arm.c | 20 ++++++++++++++------ arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 1 - arch/arm64/kvm/hyp/nvhe/pkvm.c | 11 ----------- arch/arm64/kvm/hyp/nvhe/setup.c | 1 - 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 90df7ccec5f4..36b8e97bf49e 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -550,8 +550,14 @@ struct cpu_sve_state { struct kvm_host_data { struct kvm_cpu_context host_ctxt; - struct user_fpsimd_state *fpsimd_state; /* hyp VA */ - struct cpu_sve_state *sve_state; /* hyp VA */ + /* + * All pointers in this union are hyp VA. + * sve_state is only used in pKVM and if system_supports_sve(). + */ + union { + struct user_fpsimd_state *fpsimd_state; + struct cpu_sve_state *sve_state; + }; /* Ownership of the FP regs */ enum { diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 1acf7415e831..59716789fe0f 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -2444,14 +2444,22 @@ static void finalize_init_hyp_mode(void) { int cpu; - if (!is_protected_kvm_enabled() || !system_supports_sve()) - return; + if (system_supports_sve() && is_protected_kvm_enabled()) { + for_each_possible_cpu(cpu) { + struct cpu_sve_state *sve_state; - for_each_possible_cpu(cpu) { - struct cpu_sve_state *sve_state; + sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = + kern_hyp_va(sve_state); + } + } else { + for_each_possible_cpu(cpu) { + struct user_fpsimd_state *fpsimd_state; - sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; - per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = kern_hyp_va(sve_state); + fpsimd_state = &per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->host_ctxt.fp_regs; + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->fpsimd_state = + kern_hyp_va(fpsimd_state); + } } } diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 22f374e9f532..24a9a8330d19 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -59,7 +59,6 @@ static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu) } void pkvm_hyp_vm_table_init(void *tbl); -void pkvm_host_fpsimd_state_init(void); int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva, unsigned long pgd_hva); diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index feb27b4ce459..ea67fcbf8376 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -249,17 +249,6 @@ void pkvm_hyp_vm_table_init(void *tbl) vm_table = tbl; } -void pkvm_host_fpsimd_state_init(void) -{ - unsigned long i; - - for (i = 0; i < hyp_nr_cpus; i++) { - struct kvm_host_data *host_data = per_cpu_ptr(&kvm_host_data, i); - - host_data->fpsimd_state = &host_data->host_ctxt.fp_regs; - } -} - /* * Return the hyp vm structure corresponding to the handle. */ diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 3fae42479598..f4350ba07b0b 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -324,7 +324,6 @@ void __noreturn __pkvm_init_finalise(void) goto out; pkvm_hyp_vm_table_init(vm_table_base); - pkvm_host_fpsimd_state_init(); out: /* * We tail-called to here from handle___pkvm_init() and will not return, -- cgit v1.2.3 From a69283ae1db8dd416870d931caa9e2d3d2c1cd8b Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:50 +0100 Subject: KVM: arm64: Refactor CPACR trap bit setting/clearing to use ELx format When setting/clearing CPACR bits for EL0 and EL1, use the ELx format of the bits, which covers both. This makes the code clearer, and reduces the chances of accidentally missing a bit. No functional change intended. Reviewed-by: Oliver Upton Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-9-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/el2_setup.h | 6 +++--- arch/arm64/include/asm/kvm_emulate.h | 9 ++++----- arch/arm64/kvm/fpsimd.c | 4 +--- arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 +- arch/arm64/kvm/hyp/nvhe/switch.c | 5 ++--- arch/arm64/kvm/hyp/vhe/switch.c | 7 +++---- 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index e4546b29dd0c..fd87c4b8f984 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -146,7 +146,7 @@ /* Coprocessor traps */ .macro __init_el2_cptr __check_hvhe .LnVHE_\@, x1 - mov x0, #(CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN) + mov x0, #CPACR_ELx_FPEN msr cpacr_el1, x0 b .Lskip_set_cptr_\@ .LnVHE_\@: @@ -277,7 +277,7 @@ // (h)VHE case mrs x0, cpacr_el1 // Disable SVE traps - orr x0, x0, #(CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN) + orr x0, x0, #CPACR_ELx_ZEN msr cpacr_el1, x0 b .Lskip_set_cptr_\@ @@ -298,7 +298,7 @@ // (h)VHE case mrs x0, cpacr_el1 // Disable SME traps - orr x0, x0, #(CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN) + orr x0, x0, #CPACR_ELx_SMEN msr cpacr_el1, x0 b .Lskip_set_cptr_sme_\@ diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 2d7a0bdf9d03..21650e7924d4 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -632,17 +632,16 @@ static __always_inline u64 kvm_get_reset_cptr_el2(struct kvm_vcpu *vcpu) u64 val; if (has_vhe()) { - val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN | - CPACR_EL1_ZEN_EL1EN); + val = (CPACR_ELx_FPEN | CPACR_EL1_ZEN_EL1EN); if (cpus_have_final_cap(ARM64_SME)) val |= CPACR_EL1_SMEN_EL1EN; } else if (has_hvhe()) { - val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN); + val = CPACR_ELx_FPEN; if (!vcpu_has_sve(vcpu) || !guest_owns_fp_regs()) - val |= CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN; + val |= CPACR_ELx_ZEN; if (cpus_have_final_cap(ARM64_SME)) - val |= CPACR_EL1_SMEN_EL1EN | CPACR_EL1_SMEN_EL0EN; + val |= CPACR_ELx_SMEN; } else { val = CPTR_NVHE_EL2_RES1; diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index 1807d3a79a8a..eb21f29d91fc 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -161,9 +161,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) if (has_vhe() && system_supports_sme()) { /* Also restore EL0 state seen on entry */ if (vcpu_get_flag(vcpu, HOST_SME_ENABLED)) - sysreg_clear_set(CPACR_EL1, 0, - CPACR_EL1_SMEN_EL0EN | - CPACR_EL1_SMEN_EL1EN); + sysreg_clear_set(CPACR_EL1, 0, CPACR_ELx_SMEN); else sysreg_clear_set(CPACR_EL1, CPACR_EL1_SMEN_EL0EN, diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index ea67fcbf8376..95cf18574251 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -65,7 +65,7 @@ static void pvm_init_traps_aa64pfr0(struct kvm_vcpu *vcpu) /* Trap SVE */ if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE), feature_ids)) { if (has_hvhe()) - cptr_clear |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; + cptr_clear |= CPACR_ELx_ZEN; else cptr_set |= CPTR_EL2_TZ; } diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index bef74de7065b..6af179c6356d 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -48,15 +48,14 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val |= has_hvhe() ? CPACR_EL1_TTA : CPTR_EL2_TTA; if (cpus_have_final_cap(ARM64_SME)) { if (has_hvhe()) - val &= ~(CPACR_EL1_SMEN_EL1EN | CPACR_EL1_SMEN_EL0EN); + val &= ~CPACR_ELx_SMEN; else val |= CPTR_EL2_TSM; } if (!guest_owns_fp_regs()) { if (has_hvhe()) - val &= ~(CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN | - CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN); + val &= ~(CPACR_ELx_FPEN | CPACR_ELx_ZEN); else val |= CPTR_EL2_TFP | CPTR_EL2_TZ; diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 20073579e9f5..8fbb6a2e0559 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -93,8 +93,7 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val = read_sysreg(cpacr_el1); val |= CPACR_ELx_TTA; - val &= ~(CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN | - CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN); + val &= ~(CPACR_ELx_ZEN | CPACR_ELx_SMEN); /* * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to @@ -109,9 +108,9 @@ static void __activate_traps(struct kvm_vcpu *vcpu) if (guest_owns_fp_regs()) { if (vcpu_has_sve(vcpu)) - val |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; + val |= CPACR_ELx_ZEN; } else { - val &= ~(CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN); + val &= ~CPACR_ELx_FPEN; __activate_traps_fpsimd32(vcpu); } -- cgit v1.2.3 From afb91f5f8ad7af172d993a34fde1947892408f53 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 3 Jun 2024 13:28:51 +0100 Subject: KVM: arm64: Ensure that SME controls are disabled in protected mode KVM (and pKVM) do not support SME guests. Therefore KVM ensures that the host's SME state is flushed and that SME controls for enabling access to ZA storage and for streaming are disabled. pKVM needs to protect against a buggy/malicious host. Ensure that it wouldn't run a guest when protected mode is enabled should any of the SME controls be enabled. Signed-off-by: Fuad Tabba Link: https://lore.kernel.org/r/20240603122852.3923848-10-tabba@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/fpsimd.c | 7 +++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index eb21f29d91fc..521b32868d0d 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -90,6 +90,13 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) fpsimd_save_and_flush_cpu_state(); } } + + /* + * If normal guests gain SME support, maintain this behavior for pKVM + * guests, which don't support SME. + */ + WARN_ON(is_protected_kvm_enabled() && system_supports_sme() && + read_sysreg_s(SYS_SVCR)); } /* diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index bd93b8a9e172..f43d845f3c4e 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -140,6 +140,17 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) struct pkvm_hyp_vcpu *hyp_vcpu; struct kvm *host_kvm; + /* + * KVM (and pKVM) doesn't support SME guests for now, and + * ensures that SME features aren't enabled in pstate when + * loading a vcpu. Therefore, if SME features enabled the host + * is misbehaving. + */ + if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR))) { + ret = -EINVAL; + goto out; + } + host_kvm = kern_hyp_va(host_vcpu->kvm); hyp_vcpu = pkvm_load_hyp_vcpu(host_kvm->arch.pkvm.handle, host_vcpu->vcpu_idx); -- cgit v1.2.3 From ce3af2ee95170b7d9e15fff6e500d67deab1e7b3 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Fri, 24 May 2024 15:05:39 +0200 Subject: HID: logitech-dj: Fix memory leak in logi_dj_recv_switch_to_dj_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a memory leak on logi_dj_recv_send_report() error path. Fixes: 6f20d3261265 ("HID: logitech-dj: Fix error handling in logi_dj_recv_switch_to_dj_mode()") Signed-off-by: José Expósito Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 3c3c497b6b91..37958edec55f 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1284,8 +1284,10 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, */ msleep(50); - if (retval) + if (retval) { + kfree(dj_report); return retval; + } } /* -- cgit v1.2.3 From 655a8a7684b897721f87c59798fd04d8b79f1b69 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 26 May 2024 17:05:59 +0200 Subject: HID: nintendo: Fix an error handling path in nintendo_hid_probe() joycon_leds_create() has a ida_alloc() call. So if an error occurs after it, a corresponding ida_free() call is needed, as already done in the .remove function. This is not 100% perfect, because if ida_alloc() fails, then 'ctlr->player_id' will forced to be U32_MAX, and an error will be logged when ida_free() is called. Considering that this can't happen in real life, no special handling is done to handle it. Fixes: 5307de63d71d ("HID: nintendo: use ida for LED player id") Signed-off-by: Christophe JAILLET Reviewed-by: Silvan Jegen Signed-off-by: Jiri Kosina --- drivers/hid/hid-nintendo.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index b4a97803eca3..3062daf68d31 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -2725,13 +2725,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, ret = joycon_power_supply_create(ctlr); if (ret) { hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); - goto err_close; + goto err_ida; } ret = joycon_input_create(ctlr); if (ret) { hid_err(hdev, "Failed to create input device; ret=%d\n", ret); - goto err_close; + goto err_ida; } ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; @@ -2739,6 +2739,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_dbg(hdev, "probe - success\n"); return 0; +err_ida: + ida_free(&nintendo_player_id_allocator, ctlr->player_id); err_close: hid_hw_close(hdev); err_stop: -- cgit v1.2.3 From 9e438fe31e96b75c2ec599f6a9239950e6712358 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 3 Jun 2024 09:41:03 +0200 Subject: HID: intel-ish-hid: fix endian-conversion The newly added file causes a ton of sparse warnings about the incorrect use of __le32 and similar types: drivers/hid/intel-ish-hid/ishtp/loader.h:41:23: error: invalid bitfield specifier for type restricted __le32. drivers/hid/intel-ish-hid/ishtp/loader.h:42:27: error: invalid bitfield specifier for type restricted __le32. drivers/hid/intel-ish-hid/ishtp/loader.h:43:24: error: invalid bitfield specifier for type restricted __le32. drivers/hid/intel-ish-hid/ishtp/loader.h:44:24: error: invalid bitfield specifier for type restricted __le32. drivers/hid/intel-ish-hid/ishtp/loader.h:45:22: error: invalid bitfield specifier for type restricted __le32. drivers/hid/intel-ish-hid/ishtp/loader.c:172:33: warning: restricted __le32 degrades to integer drivers/hid/intel-ish-hid/ishtp/loader.c:178:50: warning: incorrect type in assignment (different base types) drivers/hid/intel-ish-hid/ishtp/loader.c:178:50: expected restricted __le32 [usertype] length drivers/hid/intel-ish-hid/ishtp/loader.c:178:50: got unsigned long drivers/hid/intel-ish-hid/ishtp/loader.c:179:50: warning: incorrect type in assignment (different base types) drivers/hid/intel-ish-hid/ishtp/loader.c:179:50: expected restricted __le32 [usertype] fw_off drivers/hid/intel-ish-hid/ishtp/loader.c:179:50: got unsigned int [usertype] offset drivers/hid/intel-ish-hid/ishtp/loader.c:180:17: warning: cast from restricted __le32 drivers/hid/intel-ish-hid/ishtp/loader.c:183:24: warning: invalid assignment: += drivers/hid/intel-ish-hid/ishtp/loader.c:183:24: left side has type unsigned int drivers/hid/intel-ish-hid/ishtp/loader.c:183:24: right side has type restricted __le32 Add the necessary conversions and use temporary variables where appropriate to avoid converting back. Fixes: 579a267e4617 ("HID: intel-ish-hid: Implement loading firmware from host feature") Signed-off-by: Arnd Bergmann Reviewed-by: Zhang Lixu Tested-by: Zhang Lixu Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/loader.c | 69 ++++++++++++++++++-------------- drivers/hid/intel-ish-hid/ishtp/loader.h | 33 ++++++++------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-hid/ishtp/loader.c index 2785b04a2f5a..fcca070bdecb 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.c +++ b/drivers/hid/intel-ish-hid/ishtp/loader.c @@ -84,8 +84,8 @@ static int loader_write_message(struct ishtp_device *dev, void *buf, int len) static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, void *resp, int resp_len) { - struct loader_msg_header *req_hdr = req; - struct loader_msg_header *resp_hdr = resp; + union loader_msg_header req_hdr; + union loader_msg_header resp_hdr; struct device *devc = dev->devc; int rv; @@ -93,34 +93,37 @@ static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, dev->fw_loader_rx_size = resp_len; rv = loader_write_message(dev, req, req_len); + req_hdr.val32 = le32_to_cpup(req); + if (rv < 0) { - dev_err(devc, "write cmd %u failed:%d\n", req_hdr->command, rv); + dev_err(devc, "write cmd %u failed:%d\n", req_hdr.command, rv); return rv; } /* Wait the ACK */ wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received, ISHTP_LOADER_TIMEOUT); + resp_hdr.val32 = le32_to_cpup(resp); dev->fw_loader_rx_size = 0; dev->fw_loader_rx_buf = NULL; if (!dev->fw_loader_received) { - dev_err(devc, "wait response of cmd %u timeout\n", req_hdr->command); + dev_err(devc, "wait response of cmd %u timeout\n", req_hdr.command); return -ETIMEDOUT; } - if (!resp_hdr->is_response) { - dev_err(devc, "not a response for %u\n", req_hdr->command); + if (!resp_hdr.is_response) { + dev_err(devc, "not a response for %u\n", req_hdr.command); return -EBADMSG; } - if (req_hdr->command != resp_hdr->command) { - dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr->command, - resp_hdr->command); + if (req_hdr.command != resp_hdr.command) { + dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr.command, + resp_hdr.command); return -EBADMSG; } - if (resp_hdr->status) { - dev_err(devc, "cmd %u failed %u\n", req_hdr->command, resp_hdr->status); + if (resp_hdr.status) { + dev_err(devc, "cmd %u failed %u\n", req_hdr.command, resp_hdr.status); return -EIO; } @@ -157,30 +160,33 @@ static void release_dma_bufs(struct ishtp_device *dev, * @fragment: The ISHTP firmware fragment descriptor * @dma_bufs: The array of DMA fragment buffers * @fragment_size: The size of a single DMA fragment + * @fragment_count: Number of fragments * * Return: 0 on success, negative error code on failure */ static int prepare_dma_bufs(struct ishtp_device *dev, const struct firmware *ish_fw, struct loader_xfer_dma_fragment *fragment, - void **dma_bufs, u32 fragment_size) + void **dma_bufs, u32 fragment_size, u32 fragment_count) { dma_addr_t dma_addr; u32 offset = 0; + u32 length; int i; - for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) { + for (i = 0; i < fragment_count && offset < ish_fw->size; i++) { dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, &dma_addr, GFP_KERNEL); if (!dma_bufs[i]) return -ENOMEM; fragment->fragment_tbl[i].ddr_adrs = cpu_to_le64(dma_addr); - fragment->fragment_tbl[i].length = clamp(ish_fw->size - offset, 0, fragment_size); - fragment->fragment_tbl[i].fw_off = offset; - memcpy(dma_bufs[i], ish_fw->data + offset, fragment->fragment_tbl[i].length); + length = clamp(ish_fw->size - offset, 0, fragment_size); + fragment->fragment_tbl[i].length = cpu_to_le32(length); + fragment->fragment_tbl[i].fw_off = cpu_to_le32(offset); + memcpy(dma_bufs[i], ish_fw->data + offset, length); clflush_cache_range(dma_bufs[i], fragment_size); - offset += fragment->fragment_tbl[i].length; + offset += length; } return 0; @@ -208,17 +214,17 @@ void ishtp_loader_work(struct work_struct *work) { DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM); struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader); - struct loader_xfer_query query = { - .header.command = LOADER_CMD_XFER_QUERY, - }; - struct loader_start start = { - .header.command = LOADER_CMD_START, - }; + union loader_msg_header query_hdr = { .command = LOADER_CMD_XFER_QUERY, }; + union loader_msg_header start_hdr = { .command = LOADER_CMD_START, }; + union loader_msg_header fragment_hdr = { .command = LOADER_CMD_XFER_FRAGMENT, }; + struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), }; + struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), }; union loader_recv_message recv_msg; char *filename = dev->driver_data->fw_filename; const struct firmware *ish_fw; void *dma_bufs[FRAGMENT_MAX_NUM] = {}; u32 fragment_size; + u32 fragment_count; int retry = ISHTP_LOADER_RETRY_TIMES; int rv; @@ -228,23 +234,24 @@ void ishtp_loader_work(struct work_struct *work) return; } - fragment->fragment.header.command = LOADER_CMD_XFER_FRAGMENT; - fragment->fragment.xfer_mode = LOADER_XFER_MODE_DMA; - fragment->fragment.is_last = 1; - fragment->fragment.size = ish_fw->size; + fragment->fragment.header = cpu_to_le32(fragment_hdr.val32); + fragment->fragment.xfer_mode = cpu_to_le32(LOADER_XFER_MODE_DMA); + fragment->fragment.is_last = cpu_to_le32(1); + fragment->fragment.size = cpu_to_le32(ish_fw->size); /* Calculate the size of a single DMA fragment */ fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM)); /* Calculate the count of DMA fragments */ - fragment->fragment_cnt = DIV_ROUND_UP(ish_fw->size, fragment_size); + fragment_count = DIV_ROUND_UP(ish_fw->size, fragment_size); + fragment->fragment_cnt = cpu_to_le32(fragment_count); - rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size); + rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size, fragment_count); if (rv) { dev_err(dev->devc, "prepare DMA buffer failed.\n"); goto out; } do { - query.image_size = ish_fw->size; + query.image_size = cpu_to_le32(ish_fw->size); rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data, sizeof(struct loader_xfer_query_ack)); if (rv) @@ -257,7 +264,7 @@ void ishtp_loader_work(struct work_struct *work) recv_msg.query_ack.version_build); rv = loader_xfer_cmd(dev, fragment, - struct_size(fragment, fragment_tbl, fragment->fragment_cnt), + struct_size(fragment, fragment_tbl, fragment_count), recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack)); if (rv) continue; /* try again if failed */ diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.h b/drivers/hid/intel-ish-hid/ishtp/loader.h index 7aa45ebc3f7b..308b96085a4d 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.h +++ b/drivers/hid/intel-ish-hid/ishtp/loader.h @@ -30,19 +30,23 @@ struct work_struct; #define LOADER_XFER_MODE_DMA BIT(0) /** - * struct loader_msg_header - ISHTP firmware loader message header + * union loader_msg_header - ISHTP firmware loader message header * @command: Command type * @is_response: Indicates if the message is a response * @has_next: Indicates if there is a next message * @reserved: Reserved for future use * @status: Status of the message - */ -struct loader_msg_header { - __le32 command:7; - __le32 is_response:1; - __le32 has_next:1; - __le32 reserved:15; - __le32 status:8; + * @val32: entire header as a 32-bit value + */ +union loader_msg_header { + struct { + __u32 command:7; + __u32 is_response:1; + __u32 has_next:1; + __u32 reserved:15; + __u32 status:8; + }; + __u32 val32; }; /** @@ -51,7 +55,7 @@ struct loader_msg_header { * @image_size: Size of the image */ struct loader_xfer_query { - struct loader_msg_header header; + __le32 header; __le32 image_size; }; @@ -103,7 +107,7 @@ struct loader_capability { * @capability: Loader capability */ struct loader_xfer_query_ack { - struct loader_msg_header header; + __le32 header; __le16 version_major; __le16 version_minor; __le16 version_hotfix; @@ -122,7 +126,7 @@ struct loader_xfer_query_ack { * @is_last: Is last */ struct loader_xfer_fragment { - struct loader_msg_header header; + __le32 header; __le32 xfer_mode; __le32 offset; __le32 size; @@ -134,7 +138,7 @@ struct loader_xfer_fragment { * @header: Header of the message */ struct loader_xfer_fragment_ack { - struct loader_msg_header header; + __le32 header; }; /** @@ -170,7 +174,7 @@ struct loader_xfer_dma_fragment { * @header: Header of the message */ struct loader_start { - struct loader_msg_header header; + __le32 header; }; /** @@ -178,10 +182,11 @@ struct loader_start { * @header: Header of the message */ struct loader_start_ack { - struct loader_msg_header header; + __le32 header; }; union loader_recv_message { + __le32 header; struct loader_xfer_query_ack query_ack; struct loader_xfer_fragment_ack fragment_ack; struct loader_start_ack start_ack; -- cgit v1.2.3 From 9c8f05cf1d7abd1bfb53cebe691bf3acb7baee99 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 3 Jun 2024 23:00:59 -0700 Subject: HID: logitech-hidpp: add missing MODULE_DESCRIPTION() macro make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/hid/hid-logitech-hidpp.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-hidpp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index b81d5bcc76a7..400d70e6dbe2 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -27,6 +27,7 @@ #include "usbhid/usbhid.h" #include "hid-ids.h" +MODULE_DESCRIPTION("Support for Logitech devices relying on the HID++ specification"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Tissoires "); MODULE_AUTHOR("Nestor Lopez Casado "); -- cgit v1.2.3 From 0698ff57bf327d9a5735a898f78161b8dada160b Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Mon, 27 May 2024 13:54:08 +0200 Subject: drm/xe/pf: Update the LMTT when freeing VF GT config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LMTT must be updated whenever we change the VF LMEM configuration. We missed that step when freeing the whole VF GT config, which could result in stale PTE in LMTT or LMTT PT object leaks. Fix that. Fixes: ac6598aed1b3 ("drm/xe/pf: Add support to configure SR-IOV VFs") Signed-off-by: Michal Wajdeczko Reviewed-by: Piotr Piórkowski Link: https://patchwork.freedesktop.org/patch/msgid/20240527115408.1064-1-michal.wajdeczko@intel.com (cherry picked from commit c063cce7df3a765539e2a2d75ab943f334446cce) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c index 79116ad58620..476d613333a9 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c @@ -1749,6 +1749,7 @@ static void pf_release_vf_config(struct xe_gt *gt, unsigned int vfid) if (!xe_gt_is_media_type(gt)) { pf_release_vf_config_ggtt(gt, config); pf_release_vf_config_lmem(gt, config); + pf_update_vf_lmtt(gt_to_xe(gt), vfid); } pf_release_config_ctxs(gt, config); pf_release_config_dbs(gt, config); -- cgit v1.2.3 From c9d52fb313d3719d69a040f4ca78a3e2e95fba21 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 30 May 2024 18:04:24 -0700 Subject: PCI: Revert the cfg_access_lock lockdep mechanism While the experiment did reveal that there are additional places that are missing the lock during secondary bus reset, one of the places that needs to take cfg_access_lock (pci_bus_lock()) is not prepared for lockdep annotation. Specifically, pci_bus_lock() takes pci_dev_lock() recursively and is currently dependent on the fact that the device_lock() is marked lockdep_set_novalidate_class(&dev->mutex). Otherwise, without that annotation, pci_bus_lock() would need to use something like a new pci_dev_lock_nested() helper, a scheme to track a PCI device's depth in the topology, and a hope that the depth of a PCI tree never exceeds the max value for a lockdep subclass. The alternative to ripping out the lockdep coverage would be to deploy a dynamic lock key for every PCI device. Unfortunately, there is evidence that increasing the number of keys that lockdep needs to track to be per-PCI-device is prohibitively expensive for something like the cfg_access_lock. The main motivation for adding the annotation in the first place was to catch unlocked secondary bus resets, not necessarily catch lock ordering problems between cfg_access_lock and other locks. Solve that narrower problem with follow-on patches, and just due to targeted revert for now. Link: https://lore.kernel.org/r/171711746402.1628941.14575335981264103013.stgit@dwillia2-xfh.jf.intel.com Fixes: 7e89efc6e9e4 ("PCI: Lock upstream bridge for pci_reset_function()") Reported-by: Imre Deak Closes: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_134186v1/shard-dg2-1/igt@device_reset@unbind-reset-rebind.html Signed-off-by: Dan Williams Signed-off-by: Bjorn Helgaas Tested-by: Hans de Goede Tested-by: Kalle Valo Reviewed-by: Dave Jiang Cc: Jani Saarinen --- drivers/pci/access.c | 4 ---- drivers/pci/pci.c | 1 - drivers/pci/probe.c | 3 --- include/linux/lockdep.h | 5 ----- include/linux/pci.h | 2 -- 5 files changed, 15 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 30f031de9cfe..b123da16b63b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -289,8 +289,6 @@ void pci_cfg_access_lock(struct pci_dev *dev) { might_sleep(); - lock_map_acquire(&dev->cfg_access_lock); - raw_spin_lock_irq(&pci_lock); if (dev->block_cfg_access) pci_wait_cfg(dev); @@ -345,8 +343,6 @@ void pci_cfg_access_unlock(struct pci_dev *dev) raw_spin_unlock_irqrestore(&pci_lock, flags); wake_up_all(&pci_cfg_wait); - - lock_map_release(&dev->cfg_access_lock); } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 59e0949fb079..35fb1f17a589 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4883,7 +4883,6 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { - lock_map_assert_held(&dev->cfg_access_lock); pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8e696e547565..5fbabb4e3425 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2546,9 +2546,6 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; - lockdep_register_key(&dev->cfg_access_key); - lockdep_init_map(&dev->cfg_access_lock, dev_name(&dev->dev), - &dev->cfg_access_key, 0); dma_set_max_seg_size(&dev->dev, 65536); dma_set_seg_boundary(&dev->dev, 0xffffffff); diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 5e51b0de4c4b..08b0d1d9d78b 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -297,9 +297,6 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie); .wait_type_inner = _wait_type, \ .lock_type = LD_LOCK_WAIT_OVERRIDE, } -#define lock_map_assert_held(l) \ - lockdep_assert(lock_is_held(l) != LOCK_STATE_NOT_HELD) - #else /* !CONFIG_LOCKDEP */ static inline void lockdep_init_task(struct task_struct *task) @@ -391,8 +388,6 @@ extern int lockdep_is_held(const void *); #define DEFINE_WAIT_OVERRIDE_MAP(_name, _wait_type) \ struct lockdep_map __maybe_unused _name = {} -#define lock_map_assert_held(l) do { (void)(l); } while (0) - #endif /* !LOCKDEP */ #ifdef CONFIG_PROVE_LOCKING diff --git a/include/linux/pci.h b/include/linux/pci.h index fb004fd4e889..cafc5ab1cbcb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -413,8 +413,6 @@ struct pci_dev { struct resource driver_exclusive_resource; /* driver exclusive resource ranges */ bool match_driver; /* Skip attaching driver */ - struct lock_class_key cfg_access_key; - struct lockdep_map cfg_access_lock; unsigned int transparent:1; /* Subtractive decode bridge */ unsigned int io_window:1; /* Bridge has I/O window */ -- cgit v1.2.3 From dc6abbbde4b099e936cd5428e196d86a5e119aae Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jun 2024 15:25:23 -0300 Subject: tools headers arm64: Sync arm64's cputype.h with the kernel sources To get the changes in: 0ce85db6c2141b7f ("arm64: cputype: Add Neoverse-V3 definitions") 02a0a04676fa7796 ("arm64: cputype: Add Cortex-X4 definitions") f4d9d9dcc70b96b5 ("arm64: Add Neoverse-V2 part") That makes this perf source code to be rebuilt: CC /tmp/build/perf-tools/util/arm-spe.o The changes in the above patch add MIDR_NEOVERSE_V[23] and MIDR_NEOVERSE_V1 is used in arm-spe.c, so probably we need to add those and perhaps MIDR_CORTEX_X4 to that array? Or maybe we need to leave this for later when this is all tested on those machines? static const struct midr_range neoverse_spe[] = { MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), {}, }; Mark Rutland recommended about arm-spe.c: "I would not touch this for now -- someone would have to go audit the TRMs to check that those other cores have the same encoding, and I think it'd be better to do that as a follow-up." That addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/arch/arm64/include/asm/cputype.h arch/arm64/include/asm/cputype.h Acked-by: Mark Rutland Cc: Adrian Hunter Cc: Besar Wicaksono Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Will Deacon Link: https://lore.kernel.org/lkml/Zl8cYk0Tai2fs7aM@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/arm64/include/asm/cputype.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/arch/arm64/include/asm/cputype.h b/tools/arch/arm64/include/asm/cputype.h index 52f076afeb96..7b32b99023a2 100644 --- a/tools/arch/arm64/include/asm/cputype.h +++ b/tools/arch/arm64/include/asm/cputype.h @@ -86,6 +86,9 @@ #define ARM_CPU_PART_CORTEX_X2 0xD48 #define ARM_CPU_PART_NEOVERSE_N2 0xD49 #define ARM_CPU_PART_CORTEX_A78C 0xD4B +#define ARM_CPU_PART_NEOVERSE_V2 0xD4F +#define ARM_CPU_PART_CORTEX_X4 0xD82 +#define ARM_CPU_PART_NEOVERSE_V3 0xD84 #define APM_CPU_PART_XGENE 0x000 #define APM_CPU_VAR_POTENZA 0x00 @@ -159,6 +162,9 @@ #define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) #define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) #define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) +#define MIDR_NEOVERSE_V2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V2) +#define MIDR_CORTEX_X4 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X4) +#define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) -- cgit v1.2.3 From 0ea00e249ca992adee54dc71a526ee70ef109e40 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 29 May 2024 15:23:25 +0300 Subject: tpm_tis: Do *not* flush uninitialized work tpm_tis_core_init() may fail before tpm_tis_probe_irq_single() is called, in which case tpm_tis_remove() unconditionally calling flush_work() is triggering a warning for .func still being NULL. Cc: stable@vger.kernel.org # v6.5+ Fixes: 481c2d14627d ("tpm,tpm_tis: Disable interrupts after 1000 unhandled IRQs") Signed-off-by: Jan Beulich Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 176cd8dbf1db..fdef214b9f6b 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1020,7 +1020,8 @@ void tpm_tis_remove(struct tpm_chip *chip) interrupt = 0; tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); - flush_work(&priv->free_irq_work); + if (priv->free_irq_work.func) + flush_work(&priv->free_irq_work); tpm_tis_clkrun_enable(chip, false); -- cgit v1.2.3 From f071d02ecad4cfbf3ab41807c90bd1fef1cbfd3f Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Wed, 5 Jun 2024 01:28:42 +0300 Subject: tpm: Switch to new Intel CPU model defines New CPU #defines encode vendor and family as well as model. Link: https://lore.kernel.org/all/20240520224620.9480-4-tony.luck@intel.com/ Signed-off-by: Tony Luck Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm.h | 2 +- drivers/char/tpm/tpm_tis_core.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 6b8b9956ba69..7bb87fa5f7a1 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -28,7 +28,7 @@ #include #ifdef CONFIG_X86 -#include +#include #endif #define TPM_MINOR 224 /* officially assigned */ diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 13e99cf65efe..690ad8e9b731 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -210,7 +210,7 @@ static inline int tpm_tis_verify_crc(struct tpm_tis_data *data, size_t len, static inline bool is_bsw(void) { #ifdef CONFIG_X86 - return ((boot_cpu_data.x86_model == INTEL_FAM6_ATOM_AIRMONT) ? 1 : 0); + return (boot_cpu_data.x86_vfm == INTEL_ATOM_AIRMONT) ? 1 : 0; #else return false; #endif -- cgit v1.2.3 From d0d1df8ba18abc57f28fb3bc053b2bf319367f2c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 4 Jun 2024 17:00:24 +0200 Subject: bpf: Set run context for rawtp test_run callback syzbot reported crash when rawtp program executed through the test_run interface calls bpf_get_attach_cookie helper or any other helper that touches task->bpf_ctx pointer. Setting the run context (task->bpf_ctx pointer) for test_run callback. Fixes: 7adfc6c9b315 ("bpf: Add bpf_get_attach_cookie() BPF helper to access bpf_cookie value") Reported-by: syzbot+3ab78ff125b7979e45f9@syzkaller.appspotmail.com Signed-off-by: Jiri Olsa Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Closes: https://syzkaller.appspot.com/bug?extid=3ab78ff125b7979e45f9 Link: https://lore.kernel.org/bpf/20240604150024.359247-1-jolsa@kernel.org --- net/bpf/test_run.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index f6aad4ed2ab2..36ae54f57bf5 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -727,10 +727,16 @@ static void __bpf_prog_test_run_raw_tp(void *data) { struct bpf_raw_tp_test_run_info *info = data; + struct bpf_trace_run_ctx run_ctx = {}; + struct bpf_run_ctx *old_run_ctx; + + old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); rcu_read_lock(); info->retval = bpf_prog_run(info->prog, info->ctx); rcu_read_unlock(); + + bpf_reset_run_ctx(old_run_ctx); } int bpf_prog_test_run_raw_tp(struct bpf_prog *prog, -- cgit v1.2.3 From 7fcf26b315bbb728036da0862de6b335da83dff2 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Tue, 4 Jun 2024 14:29:25 +0200 Subject: Revert "xsk: Support redirect to any socket bound to the same umem" This reverts commit 2863d665ea41282379f108e4da6c8a2366ba66db. This patch introduced a potential kernel crash when multiple napi instances redirect to the same AF_XDP socket. By removing the queue_index check, it is possible for multiple napi instances to access the Rx ring at the same time, which will result in a corrupted ring state which can lead to a crash when flushing the rings in __xsk_flush(). This can happen when the linked list of sockets to flush gets corrupted by concurrent accesses. A quick and small fix is not possible, so let us revert this for now. Reported-by: Yuval El-Hanany Signed-off-by: Magnus Karlsson Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/xdp-newbies/8100DBDC-0B7C-49DB-9995-6027F6E63147@radware.com Link: https://lore.kernel.org/bpf/20240604122927.29080-2-magnus.karlsson@gmail.com --- net/xdp/xsk.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 727aa20be4bd..7d1c0986f9bb 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -313,13 +313,10 @@ static bool xsk_is_bound(struct xdp_sock *xs) static int xsk_rcv_check(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) { - struct net_device *dev = xdp->rxq->dev; - u32 qid = xdp->rxq->queue_index; - if (!xsk_is_bound(xs)) return -ENXIO; - if (!dev->_rx[qid].pool || xs->umem != dev->_rx[qid].pool->umem) + if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index) return -EINVAL; if (len > xsk_pool_get_rx_frame_size(xs->pool) && !xs->sg) { -- cgit v1.2.3 From 03e38d315f3c5258270ad50f2ae784b6372e87c3 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Tue, 4 Jun 2024 14:29:26 +0200 Subject: Revert "xsk: Document ability to redirect to any socket bound to the same umem" This reverts commit 968595a93669b6b4f6d1fcf80cf2d97956b6868f. Reported-by: Yuval El-Hanany Signed-off-by: Magnus Karlsson Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/xdp-newbies/8100DBDC-0B7C-49DB-9995-6027F6E63147@radware.com Link: https://lore.kernel.org/bpf/20240604122927.29080-3-magnus.karlsson@gmail.com --- Documentation/networking/af_xdp.rst | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst index 72da7057e4cf..dceeb0d763aa 100644 --- a/Documentation/networking/af_xdp.rst +++ b/Documentation/networking/af_xdp.rst @@ -329,24 +329,23 @@ XDP_SHARED_UMEM option and provide the initial socket's fd in the sxdp_shared_umem_fd field as you registered the UMEM on that socket. These two sockets will now share one and the same UMEM. -In this case, it is possible to use the NIC's packet steering -capabilities to steer the packets to the right queue. This is not -possible in the previous example as there is only one queue shared -among sockets, so the NIC cannot do this steering as it can only steer -between queues. - -In libxdp (or libbpf prior to version 1.0), you need to use the -xsk_socket__create_shared() API as it takes a reference to a FILL ring -and a COMPLETION ring that will be created for you and bound to the -shared UMEM. You can use this function for all the sockets you create, -or you can use it for the second and following ones and use -xsk_socket__create() for the first one. Both methods yield the same -result. +There is no need to supply an XDP program like the one in the previous +case where sockets were bound to the same queue id and +device. Instead, use the NIC's packet steering capabilities to steer +the packets to the right queue. In the previous example, there is only +one queue shared among sockets, so the NIC cannot do this steering. It +can only steer between queues. + +In libbpf, you need to use the xsk_socket__create_shared() API as it +takes a reference to a FILL ring and a COMPLETION ring that will be +created for you and bound to the shared UMEM. You can use this +function for all the sockets you create, or you can use it for the +second and following ones and use xsk_socket__create() for the first +one. Both methods yield the same result. Note that a UMEM can be shared between sockets on the same queue id and device, as well as between queues on the same device and between -devices at the same time. It is also possible to redirect to any -socket as long as it is bound to the same umem with XDP_SHARED_UMEM. +devices at the same time. XDP_USE_NEED_WAKEUP bind flag ----------------------------- @@ -823,10 +822,6 @@ A: The short answer is no, that is not supported at the moment. The switch, or other distribution mechanism, in your NIC to direct traffic to the correct queue id and socket. - Note that if you are using the XDP_SHARED_UMEM option, it is - possible to switch traffic between any socket bound to the same - umem. - Q: My packets are sometimes corrupted. What is wrong? A: Care has to be taken not to feed the same buffer in the UMEM into -- cgit v1.2.3 From 99280413a5b785f22d91e8a8a66dc38f4a214495 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 4 Jun 2024 17:45:20 +0200 Subject: efi: Add missing __nocfi annotations to runtime wrappers The EFI runtime wrappers are a sandbox for calling into EFI runtime services, which are invoked using indirect calls. When running with kCFI enabled, the compiler will require the target of any indirect call to be type annotated. Given that the EFI runtime services prototypes and calling convention are governed by the EFI spec, not the Linux kernel, adding such type annotations for firmware routines is infeasible, and so the compiler must be informed that prototype validation should be omitted. Add the __nocfi annotation at the appropriate places in the EFI runtime wrapper code to achieve this. Note that this currently only affects 32-bit ARM, given that other architectures that support both kCFI and EFI use an asm wrapper to call EFI runtime services, and this hides the indirect call from the compiler. Fixes: 1a4fec49efe5 ("ARM: 9392/2: Support CLANG CFI") Reviewed-by: Linus Walleij Tested-by: Nathan Chancellor Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/runtime-wrappers.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 5d56bc40a79d..708b777857d3 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -213,7 +213,7 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); * Calls the appropriate efi_runtime_service() with the appropriate * arguments. */ -static void efi_call_rts(struct work_struct *work) +static void __nocfi efi_call_rts(struct work_struct *work) { const union efi_rts_args *args = efi_rts_work.args; efi_status_t status = EFI_NOT_FOUND; @@ -435,7 +435,7 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, return status; } -static efi_status_t +static efi_status_t __nocfi virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data) { @@ -469,7 +469,7 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, return status; } -static efi_status_t +static efi_status_t __nocfi virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, u64 *remaining_space, u64 *max_variable_size) { @@ -499,10 +499,9 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) return status; } -static void virt_efi_reset_system(int reset_type, - efi_status_t status, - unsigned long data_size, - efi_char16_t *data) +static void __nocfi +virt_efi_reset_system(int reset_type, efi_status_t status, + unsigned long data_size, efi_char16_t *data) { if (down_trylock(&efi_runtime_lock)) { pr_warn("failed to invoke the reset_system() runtime service:\n" -- cgit v1.2.3 From 8b0f7410942cdc420c4557eda02bfcdf60ccec17 Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Wed, 29 May 2024 20:59:44 +0530 Subject: octeontx2-af: Always allocate PF entries from low prioriy zone PF mcam entries has to be at low priority always so that VF can install longest prefix match rules at higher priority. This was taken care currently but when priority allocation wrt reference entry is requested then entries are allocated from mid-zone instead of low priority zone. Fix this and always allocate entries from low priority zone for PFs. Fixes: 7df5b4b260dd ("octeontx2-af: Allocate low priority entries for PF") Signed-off-by: Subbaraya Sundeep Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- .../net/ethernet/marvell/octeontx2/af/rvu_npc.c | 33 ++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index e8b73b9d75e3..97722ce8c4cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -2519,7 +2519,17 @@ static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc, * - when available free entries are less. * Lower priority ones out of avaialble free entries are always * chosen when 'high vs low' question arises. + * + * For a VF base MCAM match rule is set by its PF. And all the + * further MCAM rules installed by VF on its own are + * concatenated with the base rule set by its PF. Hence PF entries + * should be at lower priority compared to VF entries. Otherwise + * base rule is hit always and rules installed by VF will be of + * no use. Hence if the request is from PF then allocate low + * priority entries. */ + if (!(pcifunc & RVU_PFVF_FUNC_MASK)) + goto lprio_alloc; /* Get the search range for priority allocation request */ if (req->priority) { @@ -2528,17 +2538,6 @@ static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc, goto alloc; } - /* For a VF base MCAM match rule is set by its PF. And all the - * further MCAM rules installed by VF on its own are - * concatenated with the base rule set by its PF. Hence PF entries - * should be at lower priority compared to VF entries. Otherwise - * base rule is hit always and rules installed by VF will be of - * no use. Hence if the request is from PF and NOT a priority - * allocation request then allocate low priority entries. - */ - if (!(pcifunc & RVU_PFVF_FUNC_MASK)) - goto lprio_alloc; - /* Find out the search range for non-priority allocation request * * Get MCAM free entry count in middle zone. @@ -2568,6 +2567,18 @@ lprio_alloc: reverse = true; start = 0; end = mcam->bmap_entries; + /* Ensure PF requests are always at bottom and if PF requests + * for higher/lower priority entry wrt reference entry then + * honour that criteria and start search for entries from bottom + * and not in mid zone. + */ + if (!(pcifunc & RVU_PFVF_FUNC_MASK) && + req->priority == NPC_MCAM_HIGHER_PRIO) + end = req->ref_entry; + + if (!(pcifunc & RVU_PFVF_FUNC_MASK) && + req->priority == NPC_MCAM_LOWER_PRIO) + start = req->ref_entry; } alloc: -- cgit v1.2.3 From fb0aa0781a5f457e3864da68af52c3b1f4f7fd8f Mon Sep 17 00:00:00 2001 From: Wen Gu Date: Fri, 31 May 2024 16:54:17 +0800 Subject: net/smc: avoid overwriting when adjusting sock bufsizes When copying smc settings to clcsock, avoid setting clcsock's sk_sndbuf to sysctl_tcp_wmem[1], since this may overwrite the value set by tcp_sndbuf_expand() in TCP connection establishment. And the other setting sk_{snd|rcv}buf to sysctl value in smc_adjust_sock_bufsizes() can also be omitted since the initialization of smc sock and clcsock has set sk_{snd|rcv}buf to smc.sysctl_{w|r}mem or ipv4_sysctl_tcp_{w|r}mem[1]. Fixes: 30c3c4a4497c ("net/smc: Use correct buffer sizes when switching between TCP and SMC") Link: https://lore.kernel.org/r/5eaf3858-e7fd-4db8-83e8-3d7a3e0e9ae2@linux.alibaba.com Signed-off-by: Wen Gu Reviewed-by: Wenjia Zhang Reviewed-by: Gerd Bayer , too. Signed-off-by: David S. Miller --- net/smc/af_smc.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index e50a286fd0fb..c5f98c6b2561 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -459,29 +459,11 @@ out: static void smc_adjust_sock_bufsizes(struct sock *nsk, struct sock *osk, unsigned long mask) { - struct net *nnet = sock_net(nsk); - nsk->sk_userlocks = osk->sk_userlocks; - if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) { + if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) nsk->sk_sndbuf = osk->sk_sndbuf; - } else { - if (mask == SK_FLAGS_SMC_TO_CLC) - WRITE_ONCE(nsk->sk_sndbuf, - READ_ONCE(nnet->ipv4.sysctl_tcp_wmem[1])); - else - WRITE_ONCE(nsk->sk_sndbuf, - 2 * READ_ONCE(nnet->smc.sysctl_wmem)); - } - if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) { + if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) nsk->sk_rcvbuf = osk->sk_rcvbuf; - } else { - if (mask == SK_FLAGS_SMC_TO_CLC) - WRITE_ONCE(nsk->sk_rcvbuf, - READ_ONCE(nnet->ipv4.sysctl_tcp_rmem[1])); - else - WRITE_ONCE(nsk->sk_rcvbuf, - 2 * READ_ONCE(nnet->smc.sysctl_rmem)); - } } static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, -- cgit v1.2.3 From 0a8d3f2e3e8d8aea8af017e14227b91d5989b696 Mon Sep 17 00:00:00 2001 From: Tristram Ha Date: Thu, 30 May 2024 18:38:01 -0700 Subject: net: phy: Micrel KSZ8061: fix errata solution not taking effect problem KSZ8061 needs to write to a MMD register at driver initialization to fix an errata. This worked in 5.0 kernel but not in newer kernels. The issue is the main phylib code no longer resets PHY at the very beginning. Calling phy resuming code later will reset the chip if it is already powered down at the beginning. This wipes out the MMD register write. Solution is to implement a phy resume function for KSZ8061 to take care of this problem. Fixes: 232ba3a51cc2 ("net: phy: Micrel KSZ8061: link failure after cable connect") Signed-off-by: Tristram Ha Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 8c20cf937530..5aada7cf3da7 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -866,6 +866,17 @@ static int ksz8061_config_init(struct phy_device *phydev) { int ret; + /* Chip can be powered down by the bootstrap code. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (ret & BMCR_PDOWN) { + ret = phy_write(phydev, MII_BMCR, ret & ~BMCR_PDOWN); + if (ret < 0) + return ret; + usleep_range(1000, 2000); + } + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); if (ret) return ret; @@ -2135,6 +2146,35 @@ static int ksz9477_resume(struct phy_device *phydev) return 0; } +static int ksz8061_resume(struct phy_device *phydev) +{ + int ret; + + /* This function can be called twice when the Ethernet device is on. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + usleep_range(1000, 2000); + + /* Re-program the value after chip is reset. */ + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); + if (ret) + return ret; + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -5389,7 +5429,7 @@ static struct phy_driver ksphy_driver[] = { .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = kszphy_suspend, - .resume = kszphy_resume, + .resume = ksz8061_resume, }, { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, -- cgit v1.2.3 From 491aee894a08bc9b8bb52e7363b9d4bc6403f363 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Mon, 3 Jun 2024 04:57:55 +0000 Subject: ionic: fix kernel panic in XDP_TX action In the XDP_TX path, ionic driver sends a packet to the TX path with rx page and corresponding dma address. After tx is done, ionic_tx_clean() frees that page. But RX ring buffer isn't reset to NULL. So, it uses a freed page, which causes kernel panic. BUG: unable to handle page fault for address: ffff8881576c110c PGD 773801067 P4D 773801067 PUD 87f086067 PMD 87efca067 PTE 800ffffea893e060 Oops: Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN NOPTI CPU: 1 PID: 25 Comm: ksoftirqd/1 Not tainted 6.9.0+ #11 Hardware name: ASUS System Product Name/PRIME Z690-P D4, BIOS 0603 11/01/2021 RIP: 0010:bpf_prog_f0b8caeac1068a55_balancer_ingress+0x3b/0x44f Code: 00 53 41 55 41 56 41 57 b8 01 00 00 00 48 8b 5f 08 4c 8b 77 00 4c 89 f7 48 83 c7 0e 48 39 d8 RSP: 0018:ffff888104e6fa28 EFLAGS: 00010283 RAX: 0000000000000002 RBX: ffff8881576c1140 RCX: 0000000000000002 RDX: ffffffffc0051f64 RSI: ffffc90002d33048 RDI: ffff8881576c110e RBP: ffff888104e6fa88 R08: 0000000000000000 R09: ffffed1027a04a23 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8881b03a21a8 R13: ffff8881589f800f R14: ffff8881576c1100 R15: 00000001576c1100 FS: 0000000000000000(0000) GS:ffff88881ae00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffff8881576c110c CR3: 0000000767a90000 CR4: 00000000007506f0 PKRU: 55555554 Call Trace: ? __die+0x20/0x70 ? page_fault_oops+0x254/0x790 ? __pfx_page_fault_oops+0x10/0x10 ? __pfx_is_prefetch.constprop.0+0x10/0x10 ? search_bpf_extables+0x165/0x260 ? fixup_exception+0x4a/0x970 ? exc_page_fault+0xcb/0xe0 ? asm_exc_page_fault+0x22/0x30 ? 0xffffffffc0051f64 ? bpf_prog_f0b8caeac1068a55_balancer_ingress+0x3b/0x44f ? do_raw_spin_unlock+0x54/0x220 ionic_rx_service+0x11ab/0x3010 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ? ionic_tx_clean+0x29b/0xc60 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ? __pfx_ionic_tx_clean+0x10/0x10 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ? __pfx_ionic_rx_service+0x10/0x10 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ? ionic_tx_cq_service+0x25d/0xa00 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ? __pfx_ionic_rx_service+0x10/0x10 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ionic_cq_service+0x69/0x150 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] ionic_txrx_napi+0x11a/0x540 [ionic 9180c3001ab627d82bbc5f3ebe8a0decaf6bb864] __napi_poll.constprop.0+0xa0/0x440 net_rx_action+0x7e7/0xc30 ? __pfx_net_rx_action+0x10/0x10 Fixes: 8eeed8373e1c ("ionic: Add XDP_TX support") Signed-off-by: Taehee Yoo Reviewed-by: Shannon Nelson Reviewed-by: Brett Creeley Signed-off-by: David S. Miller --- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 5dba6d2d633c..2427610f4306 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -586,6 +586,7 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err); goto out_xdp_abort; } + buf_info->page = NULL; stats->xdp_tx++; /* the Tx completion will free the buffers */ -- cgit v1.2.3 From affc18fdc694190ca7575b9a86632a73b9fe043d Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Mon, 3 Jun 2024 15:13:03 +0800 Subject: net: sched: sch_multiq: fix possible OOB write in multiq_tune() q->bands will be assigned to qopt->bands to execute subsequent code logic after kmalloc. So the old q->bands should not be used in kmalloc. Otherwise, an out-of-bounds write will occur. Fixes: c2999f7fb05b ("net: sched: multiq: don't call qdisc_put() while holding tree lock") Signed-off-by: Hangyu Hua Acked-by: Cong Wang Signed-off-by: David S. Miller --- net/sched/sch_multiq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 79e93a19d5fa..06e03f5cd7ce 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -185,7 +185,7 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt, qopt->bands = qdisc_dev(sch)->real_num_tx_queues; - removed = kmalloc(sizeof(*removed) * (q->max_bands - q->bands), + removed = kmalloc(sizeof(*removed) * (q->max_bands - qopt->bands), GFP_KERNEL); if (!removed) return -ENOMEM; -- cgit v1.2.3 From 1cd4bc987abb2823836cbb8f887026011ccddc8a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 3 Jun 2024 10:59:26 +0200 Subject: vxlan: Fix regression when dropping packets due to invalid src addresses Commit f58f45c1e5b9 ("vxlan: drop packets from invalid src-address") has recently been added to vxlan mainly in the context of source address snooping/learning so that when it is enabled, an entry in the FDB is not being created for an invalid address for the corresponding tunnel endpoint. Before commit f58f45c1e5b9 vxlan was similarly behaving as geneve in that it passed through whichever macs were set in the L2 header. It turns out that this change in behavior breaks setups, for example, Cilium with netkit in L3 mode for Pods as well as tunnel mode has been passing before the change in f58f45c1e5b9 for both vxlan and geneve. After mentioned change it is only passing for geneve as in case of vxlan packets are dropped due to vxlan_set_mac() returning false as source and destination macs are zero which for E/W traffic via tunnel is totally fine. Fix it by only opting into the is_valid_ether_addr() check in vxlan_set_mac() when in fact source address snooping/learning is actually enabled in vxlan. This is done by moving the check into vxlan_snoop(). With this change, the Cilium connectivity test suite passes again for both tunnel flavors. Fixes: f58f45c1e5b9 ("vxlan: drop packets from invalid src-address") Signed-off-by: Daniel Borkmann Cc: David Bauer Cc: Ido Schimmel Cc: Nikolay Aleksandrov Cc: Martin KaFai Lau Reviewed-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Reviewed-by: David Bauer Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index f78dd0438843..567cb3faab70 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1446,6 +1446,10 @@ static bool vxlan_snoop(struct net_device *dev, struct vxlan_fdb *f; u32 ifindex = 0; + /* Ignore packets from invalid src-address */ + if (!is_valid_ether_addr(src_mac)) + return true; + #if IS_ENABLED(CONFIG_IPV6) if (src_ip->sa.sa_family == AF_INET6 && (ipv6_addr_type(&src_ip->sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)) @@ -1616,10 +1620,6 @@ static bool vxlan_set_mac(struct vxlan_dev *vxlan, if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr)) return false; - /* Ignore packets from invalid src-address */ - if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) - return false; - /* Get address from the outer IP header */ if (vxlan_get_sk_family(vs) == AF_INET) { saddr.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; -- cgit v1.2.3 From 712115a24b1a5318c10fc757d48d8f33815a6bfa Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 3 Jun 2024 17:30:19 +0800 Subject: selftests: hsr: add missing config for CONFIG_BRIDGE hsr_redbox.sh test need to create bridge for testing. Add the missing config CONFIG_BRIDGE in config file. Fixes: eafbf0574e05 ("test: hsr: Extend the hsr_redbox.sh to have more SAN devices connected") Signed-off-by: Hangbin Liu Tested-by: Simon Horman Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- tools/testing/selftests/net/hsr/config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/hsr/config b/tools/testing/selftests/net/hsr/config index 22061204fb69..241542441c51 100644 --- a/tools/testing/selftests/net/hsr/config +++ b/tools/testing/selftests/net/hsr/config @@ -2,3 +2,4 @@ CONFIG_IPV6=y CONFIG_NET_SCH_NETEM=m CONFIG_HSR=y CONFIG_VETH=y +CONFIG_BRIDGE=y -- cgit v1.2.3 From 14951beaec93696b092a906baa0f29322cf34004 Mon Sep 17 00:00:00 2001 From: Wei Li Date: Tue, 23 Apr 2024 17:35:01 +0800 Subject: arm64: armv8_deprecated: Fix warning in isndep cpuhp starting process The function run_all_insn_set_hw_mode() is registered as startup callback of 'CPUHP_AP_ARM64_ISNDEP_STARTING', it invokes set_hw_mode() methods of all emulated instructions. As the STARTING callbacks are not expected to fail, if one of the set_hw_mode() fails, e.g. due to el0 mixed-endian is not supported for 'setend', it will report a warning: ``` CPU[2] cannot support the emulation of setend CPU 2 UP state arm64/isndep:starting (136) failed (-22) CPU2: Booted secondary processor 0x0000000002 [0x414fd0c1] ``` To fix it, add a check for INSN_UNAVAILABLE status and skip the process. Signed-off-by: Wei Li Tested-by: Huisong Li Link: https://lore.kernel.org/r/20240423093501.3460764-1-liwei391@huawei.com Signed-off-by: Will Deacon --- arch/arm64/kernel/armv8_deprecated.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index dd6ce86d4332..b776e7424fe9 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -462,6 +462,9 @@ static int run_all_insn_set_hw_mode(unsigned int cpu) for (int i = 0; i < ARRAY_SIZE(insn_emulations); i++) { struct insn_emulation *insn = insn_emulations[i]; bool enable = READ_ONCE(insn->current_mode) == INSN_HW; + if (insn->status == INSN_UNAVAILABLE) + continue; + if (insn->set_hw_mode && insn->set_hw_mode(enable)) { pr_warn("CPU[%u] cannot support the emulation of %s", cpu, insn->name); -- cgit v1.2.3 From db574f2f96d0c9a245a9e787e3d9ec288fb2b445 Mon Sep 17 00:00:00 2001 From: Tao Su Date: Tue, 28 May 2024 18:22:34 +0800 Subject: KVM: x86/mmu: Don't save mmu_invalidate_seq after checking private attr Drop the second snapshot of mmu_invalidate_seq in kvm_faultin_pfn(). Before checking the mismatch of private vs. shared, mmu_invalidate_seq is saved to fault->mmu_seq, which can be used to detect an invalidation related to the gfn occurred, i.e. KVM will not install a mapping in page table if fault->mmu_seq != mmu_invalidate_seq. Currently there is a second snapshot of mmu_invalidate_seq, which may not be same as the first snapshot in kvm_faultin_pfn(), i.e. the gfn attribute may be changed between the two snapshots, but the gfn may be mapped in page table without hindrance. Therefore, drop the second snapshot as it has no obvious benefits. Fixes: f6adeae81f35 ("KVM: x86/mmu: Handle no-slot faults at the beginning of kvm_faultin_pfn()") Signed-off-by: Tao Su Message-ID: <20240528102234.2162763-1-tao1.su@linux.intel.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index b7b4426a7221..8d74bdef68c1 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4411,9 +4411,6 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, return RET_PF_EMULATE; } - fault->mmu_seq = vcpu->kvm->mmu_invalidate_seq; - smp_rmb(); - /* * Check for a relevant mmu_notifier invalidation event before getting * the pfn from the primary MMU, and before acquiring mmu_lock. -- cgit v1.2.3 From a46d0ea5c94205f40ecf912d1bb7806a8a64704f Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Tue, 4 Jun 2024 01:02:16 +0800 Subject: tcp: count CLOSE-WAIT sockets for TCP_MIB_CURRESTAB According to RFC 1213, we should also take CLOSE-WAIT sockets into consideration: "tcpCurrEstab OBJECT-TYPE ... The number of TCP connections for which the current state is either ESTABLISHED or CLOSE- WAIT." After this, CurrEstab counter will display the total number of ESTABLISHED and CLOSE-WAIT sockets. The logic of counting When we increment the counter? a) if we change the state to ESTABLISHED. b) if we change the state from SYN-RECEIVED to CLOSE-WAIT. When we decrement the counter? a) if the socket leaves ESTABLISHED and will never go into CLOSE-WAIT, say, on the client side, changing from ESTABLISHED to FIN-WAIT-1. b) if the socket leaves CLOSE-WAIT, say, on the server side, changing from CLOSE-WAIT to LAST-ACK. Please note: there are two chances that old state of socket can be changed to CLOSE-WAIT in tcp_fin(). One is SYN-RECV, the other is ESTABLISHED. So we have to take care of the former case. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jason Xing Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 4d8cc2ebb64c..e6790ea74877 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2649,6 +2649,10 @@ void tcp_set_state(struct sock *sk, int state) if (oldstate != TCP_ESTABLISHED) TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); break; + case TCP_CLOSE_WAIT: + if (oldstate == TCP_SYN_RECV) + TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); + break; case TCP_CLOSE: if (oldstate == TCP_CLOSE_WAIT || oldstate == TCP_ESTABLISHED) @@ -2660,7 +2664,7 @@ void tcp_set_state(struct sock *sk, int state) inet_put_port(sk); fallthrough; default: - if (oldstate == TCP_ESTABLISHED) + if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT) TCP_DEC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); } -- cgit v1.2.3 From 9633e9377e6af0244f7381e86b9aac5276f5be97 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Tue, 4 Jun 2024 01:02:17 +0800 Subject: mptcp: count CLOSE-WAIT sockets for MPTCP_MIB_CURRESTAB Like previous patch does in TCP, we need to adhere to RFC 1213: "tcpCurrEstab OBJECT-TYPE ... The number of TCP connections for which the current state is either ESTABLISHED or CLOSE- WAIT." So let's consider CLOSE-WAIT sockets. The logic of counting When we increment the counter? a) Only if we change the state to ESTABLISHED. When we decrement the counter? a) if the socket leaves ESTABLISHED and will never go into CLOSE-WAIT, say, on the client side, changing from ESTABLISHED to FIN-WAIT-1. b) if the socket leaves CLOSE-WAIT, say, on the server side, changing from CLOSE-WAIT to LAST-ACK. Fixes: d9cd27b8cd19 ("mptcp: add CurrEstab MIB counter support") Signed-off-by: Jason Xing Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: David S. Miller --- net/mptcp/protocol.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 7d44196ec5b6..96b113854bd3 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2916,9 +2916,14 @@ void mptcp_set_state(struct sock *sk, int state) if (oldstate != TCP_ESTABLISHED) MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB); break; - + case TCP_CLOSE_WAIT: + /* Unlike TCP, MPTCP sk would not have the TCP_SYN_RECV state: + * MPTCP "accepted" sockets will be created later on. So no + * transition from TCP_SYN_RECV to TCP_CLOSE_WAIT. + */ + break; default: - if (oldstate == TCP_ESTABLISHED) + if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT) MPTCP_DEC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB); } -- cgit v1.2.3 From 5b4b62a169e10401cca34a6e7ac39161986f5605 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Jun 2024 11:48:26 -0700 Subject: rtnetlink: make the "split" NLM_DONE handling generic Jaroslav reports Dell's OMSA Systems Management Data Engine expects NLM_DONE in a separate recvmsg(), both for rtnl_dump_ifinfo() and inet_dump_ifaddr(). We already added a similar fix previously in commit 460b0d33cf10 ("inet: bring NLM_DONE out to a separate recv() again") Instead of modifying all the dump handlers, and making them look different than modern for_each_netdev_dump()-based dump handlers - put the workaround in rtnetlink code. This will also help us move the custom rtnl-locking from af_netlink in the future (in net-next). Note that this change is not touching rtnl_dump_all(). rtnl_dump_all() is different kettle of fish and a potential problem. We now mix families in a single recvmsg(), but NLM_DONE is not coalesced. Tested: ./cli.py --dbg-small-recv 4096 --spec netlink/specs/rt_addr.yaml \ --dump getaddr --json '{"ifa-family": 2}' ./cli.py --dbg-small-recv 4096 --spec netlink/specs/rt_route.yaml \ --dump getroute --json '{"rtm-family": 2}' ./cli.py --dbg-small-recv 4096 --spec netlink/specs/rt_link.yaml \ --dump getlink Fixes: 3e41af90767d ("rtnetlink: use xarray iterator to implement rtnl_dump_ifinfo()") Fixes: cdb2f80f1c10 ("inet: use xa_array iterator to implement inet_dump_ifaddr()") Reported-by: Jaroslav Pulchart Link: https://lore.kernel.org/all/CAK8fFZ7MKoFSEzMBDAOjoUt+vTZRRQgLDNXEOfdCCXSoXXKE0g@mail.gmail.com Signed-off-by: Jakub Kicinski Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 1 + net/core/rtnetlink.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- net/ipv4/devinet.c | 2 +- net/ipv4/fib_frontend.c | 7 +------ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 3bfb80bad173..b45d57b5968a 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -13,6 +13,7 @@ enum rtnl_link_flags { RTNL_FLAG_DOIT_UNLOCKED = BIT(0), RTNL_FLAG_BULK_DEL_SUPPORTED = BIT(1), RTNL_FLAG_DUMP_UNLOCKED = BIT(2), + RTNL_FLAG_DUMP_SPLIT_NLM_DONE = BIT(3), /* legacy behavior */ }; enum rtnl_kinds { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b86b0a87367d..4668d6718040 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -6484,6 +6484,46 @@ static int rtnl_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, /* Process one rtnetlink message. */ +static int rtnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb) +{ + rtnl_dumpit_func dumpit = cb->data; + int err; + + /* Previous iteration have already finished, avoid calling->dumpit() + * again, it may not expect to be called after it reached the end. + */ + if (!dumpit) + return 0; + + err = dumpit(skb, cb); + + /* Old dump handlers used to send NLM_DONE as in a separate recvmsg(). + * Some applications which parse netlink manually depend on this. + */ + if (cb->flags & RTNL_FLAG_DUMP_SPLIT_NLM_DONE) { + if (err < 0 && err != -EMSGSIZE) + return err; + if (!err) + cb->data = NULL; + + return skb->len; + } + return err; +} + +static int rtnetlink_dump_start(struct sock *ssk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + struct netlink_dump_control *control) +{ + if (control->flags & RTNL_FLAG_DUMP_SPLIT_NLM_DONE) { + WARN_ON(control->data); + control->data = control->dump; + control->dump = rtnl_dumpit; + } + + return netlink_dump_start(ssk, skb, nlh, control); +} + static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -6548,7 +6588,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, .module = owner, .flags = flags, }; - err = netlink_dump_start(rtnl, skb, nlh, &c); + err = rtnetlink_dump_start(rtnl, skb, nlh, &c); /* netlink_dump_start() will keep a reference on * module if dump is still in progress. */ @@ -6694,7 +6734,7 @@ void __init rtnetlink_init(void) register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, - rtnl_dump_ifinfo, 0); + rtnl_dump_ifinfo, RTNL_FLAG_DUMP_SPLIT_NLM_DONE); rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, 0); rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0); rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, 0); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f3892ee9dfb3..d09f557eaa77 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2805,7 +2805,7 @@ void __init devinet_init(void) rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, - RTNL_FLAG_DUMP_UNLOCKED); + RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE); rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, inet_netconf_dump_devconf, RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c484b1c0fc00..7ad2cafb9276 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1050,11 +1050,6 @@ next: e++; } } - - /* Don't let NLM_DONE coalesce into a message, even if it could. - * Some user space expects NLM_DONE in a separate recv(). - */ - err = skb->len; out: cb->args[1] = e; @@ -1665,5 +1660,5 @@ void __init ip_fib_init(void) rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0); rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0); rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, - RTNL_FLAG_DUMP_UNLOCKED); + RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE); } -- cgit v1.2.3 From 5c40e428aea644c9d924e491b1bc22fa9f272bcc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 4 Jun 2024 22:59:57 +0200 Subject: arm64/io: add constant-argument check In some configurations __const_iowrite32_copy() does not get inlined and gcc runs into the BUILD_BUG(): In file included from : In function '__const_memcpy_toio_aligned32', inlined from '__const_iowrite32_copy' at arch/arm64/include/asm/io.h:203:3, inlined from '__const_iowrite32_copy' at arch/arm64/include/asm/io.h:199:20: include/linux/compiler_types.h:487:45: error: call to '__compiletime_assert_538' declared with attribute error: BUILD_BUG failed 487 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^ include/linux/compiler_types.h:468:25: note: in definition of macro '__compiletime_assert' 468 | prefix ## suffix(); \ | ^~~~~~ include/linux/compiler_types.h:487:9: note: in expansion of macro '_compiletime_assert' 487 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert' 39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) | ^~~~~~~~~~~~~~~~~~ include/linux/build_bug.h:59:21: note: in expansion of macro 'BUILD_BUG_ON_MSG' 59 | #define BUILD_BUG() BUILD_BUG_ON_MSG(1, "BUILD_BUG failed") | ^~~~~~~~~~~~~~~~ arch/arm64/include/asm/io.h:193:17: note: in expansion of macro 'BUILD_BUG' 193 | BUILD_BUG(); | ^~~~~~~~~ Move the check for constant arguments into the inline function to ensure it is still constant if the compiler decides against inlining it, and mark them as __always_inline to override the logic that sometimes leads to the compiler not producing the simplified output. Note that either the __always_inline annotation or the check for a constant value are sufficient here, but combining the two looks cleaner as it also avoids the macro. With clang-8 and older, the macro was still needed, but all versions of gcc and clang can reliably perform constant folding here. Fixes: ead79118dae6 ("arm64/io: Provide a WC friendly __iowriteXX_copy()") Signed-off-by: Arnd Bergmann Reviewed-by: Mark Rutland Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20240604210006.668912-1-arnd@kernel.org Signed-off-by: Will Deacon --- arch/arm64/include/asm/io.h | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 4ff0ae3f6d66..41fd90895dfc 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -153,8 +153,9 @@ extern void __memset_io(volatile void __iomem *, int, size_t); * emit the large TLP from the CPU. */ -static inline void __const_memcpy_toio_aligned32(volatile u32 __iomem *to, - const u32 *from, size_t count) +static __always_inline void +__const_memcpy_toio_aligned32(volatile u32 __iomem *to, const u32 *from, + size_t count) { switch (count) { case 8: @@ -196,24 +197,22 @@ static inline void __const_memcpy_toio_aligned32(volatile u32 __iomem *to, void __iowrite32_copy_full(void __iomem *to, const void *from, size_t count); -static inline void __const_iowrite32_copy(void __iomem *to, const void *from, - size_t count) +static __always_inline void +__iowrite32_copy(void __iomem *to, const void *from, size_t count) { - if (count == 8 || count == 4 || count == 2 || count == 1) { + if (__builtin_constant_p(count) && + (count == 8 || count == 4 || count == 2 || count == 1)) { __const_memcpy_toio_aligned32(to, from, count); dgh(); } else { __iowrite32_copy_full(to, from, count); } } +#define __iowrite32_copy __iowrite32_copy -#define __iowrite32_copy(to, from, count) \ - (__builtin_constant_p(count) ? \ - __const_iowrite32_copy(to, from, count) : \ - __iowrite32_copy_full(to, from, count)) - -static inline void __const_memcpy_toio_aligned64(volatile u64 __iomem *to, - const u64 *from, size_t count) +static __always_inline void +__const_memcpy_toio_aligned64(volatile u64 __iomem *to, const u64 *from, + size_t count) { switch (count) { case 8: @@ -255,21 +254,18 @@ static inline void __const_memcpy_toio_aligned64(volatile u64 __iomem *to, void __iowrite64_copy_full(void __iomem *to, const void *from, size_t count); -static inline void __const_iowrite64_copy(void __iomem *to, const void *from, - size_t count) +static __always_inline void +__iowrite64_copy(void __iomem *to, const void *from, size_t count) { - if (count == 8 || count == 4 || count == 2 || count == 1) { + if (__builtin_constant_p(count) && + (count == 8 || count == 4 || count == 2 || count == 1)) { __const_memcpy_toio_aligned64(to, from, count); dgh(); } else { __iowrite64_copy_full(to, from, count); } } - -#define __iowrite64_copy(to, from, count) \ - (__builtin_constant_p(count) ? \ - __const_iowrite64_copy(to, from, count) : \ - __iowrite64_copy_full(to, from, count)) +#define __iowrite64_copy __iowrite64_copy /* * I/O memory mapping functions. -- cgit v1.2.3 From c57e558194430d10d5e5f4acd8a8655b68dade13 Mon Sep 17 00:00:00 2001 From: Frank Wunderlich Date: Mon, 3 Jun 2024 21:25:05 +0200 Subject: net: ethernet: mtk_eth_soc: handle dma buffer size soc specific The mainline MTK ethernet driver suffers long time from rarly but annoying tx queue timeouts. We think that this is caused by fixed dma sizes hardcoded for all SoCs. We suspect this problem arises from a low level of free TX DMADs, the TX Ring alomost full. The transmit timeout is caused by the Tx queue not waking up. The Tx queue stops when the free counter is less than ring->thres, and it will wake up once the free counter is greater than ring->thres. If the CPU is too late to wake up the Tx queues, it may cause a transmit timeout. Therefore, we increased the TX and RX DMADs to improve this error situation. Use the dma-size implementation from SDK in a per SoC manner. In difference to SDK we have no RSS feature yet, so all RX/TX sizes should be raised from 512 to 2048 byte except fqdma on mt7988 to avoid the tx timeout issue. Fixes: 656e705243fd ("net-next: mediatek: add support for MT7623 ethernet") Suggested-by: Daniel Golle Signed-off-by: Frank Wunderlich Reviewed-by: Jacob Keller Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 104 +++++++++++++++++++--------- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 9 ++- 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index cae46290a7ae..c84ce54a84a0 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1131,9 +1131,9 @@ static int mtk_init_fq_dma(struct mtk_eth *eth) { const struct mtk_soc_data *soc = eth->soc; dma_addr_t phy_ring_tail; - int cnt = MTK_QDMA_RING_SIZE; + int cnt = soc->tx.fq_dma_size; dma_addr_t dma_addr; - int i; + int i, j, len; if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) eth->scratch_ring = eth->sram_base; @@ -1142,40 +1142,46 @@ static int mtk_init_fq_dma(struct mtk_eth *eth) cnt * soc->tx.desc_size, ð->phy_scratch_ring, GFP_KERNEL); + if (unlikely(!eth->scratch_ring)) return -ENOMEM; - eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); - if (unlikely(!eth->scratch_head)) - return -ENOMEM; + phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1); - dma_addr = dma_map_single(eth->dma_dev, - eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) - return -ENOMEM; + for (j = 0; j < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) { + len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH); + eth->scratch_head[j] = kcalloc(len, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); - phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1); + if (unlikely(!eth->scratch_head[j])) + return -ENOMEM; - for (i = 0; i < cnt; i++) { - dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE; - struct mtk_tx_dma_v2 *txd; + dma_addr = dma_map_single(eth->dma_dev, + eth->scratch_head[j], len * MTK_QDMA_PAGE_SIZE, + DMA_FROM_DEVICE); - txd = eth->scratch_ring + i * soc->tx.desc_size; - txd->txd1 = addr; - if (i < cnt - 1) - txd->txd2 = eth->phy_scratch_ring + - (i + 1) * soc->tx.desc_size; + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; - txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); - if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA)) - txd->txd3 |= TX_DMA_PREP_ADDR64(addr); - txd->txd4 = 0; - if (mtk_is_netsys_v2_or_greater(eth)) { - txd->txd5 = 0; - txd->txd6 = 0; - txd->txd7 = 0; - txd->txd8 = 0; + for (i = 0; i < cnt; i++) { + struct mtk_tx_dma_v2 *txd; + + txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size; + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (j * MTK_FQ_DMA_LENGTH + i < cnt) + txd->txd2 = eth->phy_scratch_ring + + (j * MTK_FQ_DMA_LENGTH + i + 1) * soc->tx.desc_size; + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA)) + txd->txd3 |= TX_DMA_PREP_ADDR64(dma_addr + i * MTK_QDMA_PAGE_SIZE); + + txd->txd4 = 0; + if (mtk_is_netsys_v2_or_greater(eth)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; + txd->txd8 = 0; + } } } @@ -2457,7 +2463,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth) if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ring_size = MTK_QDMA_RING_SIZE; else - ring_size = MTK_DMA_SIZE; + ring_size = soc->tx.dma_size; ring->buf = kcalloc(ring_size, sizeof(*ring->buf), GFP_KERNEL); @@ -2465,8 +2471,8 @@ static int mtk_tx_alloc(struct mtk_eth *eth) goto no_tx_mem; if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) { - ring->dma = eth->sram_base + ring_size * sz; - ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz; + ring->dma = eth->sram_base + soc->tx.fq_dma_size * sz; + ring->phys = eth->phy_scratch_ring + soc->tx.fq_dma_size * (dma_addr_t)sz; } else { ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz, &ring->phys, GFP_KERNEL); @@ -2588,6 +2594,7 @@ static void mtk_tx_clean(struct mtk_eth *eth) static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) { const struct mtk_reg_map *reg_map = eth->soc->reg_map; + const struct mtk_soc_data *soc = eth->soc; struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size, tx_ring_size; int i; @@ -2595,7 +2602,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) tx_ring_size = MTK_QDMA_RING_SIZE; else - tx_ring_size = MTK_DMA_SIZE; + tx_ring_size = soc->tx.dma_size; if (rx_flag == MTK_RX_FLAGS_QDMA) { if (ring_no) @@ -2610,7 +2617,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) rx_dma_size = MTK_HW_LRO_DMA_SIZE; } else { rx_data_len = ETH_DATA_LEN; - rx_dma_size = MTK_DMA_SIZE; + rx_dma_size = soc->rx.dma_size; } ring->frag_size = mtk_max_frag_size(rx_data_len); @@ -3139,7 +3146,10 @@ static void mtk_dma_free(struct mtk_eth *eth) mtk_rx_clean(eth, ð->rx_ring[i], false); } - kfree(eth->scratch_head); + for (i = 0; i < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); i++) { + kfree(eth->scratch_head[i]); + eth->scratch_head[i] = NULL; + } } static bool mtk_hw_reset_check(struct mtk_eth *eth) @@ -5052,11 +5062,14 @@ static const struct mtk_soc_data mt2701_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5076,11 +5089,14 @@ static const struct mtk_soc_data mt7621_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5102,11 +5118,14 @@ static const struct mtk_soc_data mt7622_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5127,11 +5146,14 @@ static const struct mtk_soc_data mt7623_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5150,11 +5172,14 @@ static const struct mtk_soc_data mt7629_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5176,6 +5201,8 @@ static const struct mtk_soc_data mt7981_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5183,6 +5210,7 @@ static const struct mtk_soc_data mt7981_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5202,6 +5230,8 @@ static const struct mtk_soc_data mt7986_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5209,6 +5239,7 @@ static const struct mtk_soc_data mt7986_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5228,6 +5259,8 @@ static const struct mtk_soc_data mt7988_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(4K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma_v2), @@ -5235,6 +5268,7 @@ static const struct mtk_soc_data mt7988_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5249,6 +5283,7 @@ static const struct mtk_soc_data rt5350_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5256,6 +5291,7 @@ static const struct mtk_soc_data rt5350_data = { .dma_l4_valid = RX_DMA_L4_VALID_PDMA, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 4eab30b44070..f5174f6cb1bb 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -32,7 +32,9 @@ #define MTK_TX_DMA_BUF_LEN 0x3fff #define MTK_TX_DMA_BUF_LEN_V2 0xffff #define MTK_QDMA_RING_SIZE 2048 -#define MTK_DMA_SIZE 512 +#define MTK_DMA_SIZE(x) (SZ_##x) +#define MTK_FQ_DMA_HEAD 32 +#define MTK_FQ_DMA_LENGTH 2048 #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) #define MTK_DMA_DUMMY_DESC 0xffffffff @@ -1176,6 +1178,8 @@ struct mtk_soc_data { u32 desc_size; u32 dma_max_len; u32 dma_len_offset; + u32 dma_size; + u32 fq_dma_size; } tx; struct { u32 desc_size; @@ -1183,6 +1187,7 @@ struct mtk_soc_data { u32 dma_l4_valid; u32 dma_max_len; u32 dma_len_offset; + u32 dma_size; } rx; }; @@ -1264,7 +1269,7 @@ struct mtk_eth { struct napi_struct rx_napi; void *scratch_ring; dma_addr_t phy_scratch_ring; - void *scratch_head; + void *scratch_head[MTK_FQ_DMA_HEAD]; struct clk *clks[MTK_CLK_MAX]; struct mii_bus *mii_bus; -- cgit v1.2.3 From 33afbfcc105a572159750f2ebee834a8a70fdd96 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Tue, 4 Jun 2024 00:04:42 +0300 Subject: net/mlx5: Stop waiting for PCI if pci channel is offline In case pci channel becomes offline the driver should not wait for PCI reads during health dump and recovery flow. The driver has timeout for each of these loops trying to read PCI, so it would fail anyway. However, in case of recovery waiting till timeout may cause the pci error_detected() callback fail to meet pci_dpc_recovered() wait timeout. Fixes: b3bd076f7501 ("net/mlx5: Report devlink health on FW fatal issues") Signed-off-by: Moshe Shemesh Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 4 ++++ drivers/net/ethernet/mellanox/mlx5/core/health.c | 8 ++++++++ drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 2d95a9b7b44e..b61b7d966114 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -373,6 +373,10 @@ int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev) do { if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for NIC IFC\n"); + return -EACCES; + } cond_resched(); } while (!time_after(jiffies, end)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index ad38e31822df..a6329ca2d9bf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -248,6 +248,10 @@ recover_from_sw_reset: do { if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for NIC IFC\n"); + goto unlock; + } msleep(20); } while (!time_after(jiffies, end)); @@ -317,6 +321,10 @@ int mlx5_health_wait_pci_up(struct mlx5_core_dev *dev) mlx5_core_warn(dev, "device is being removed, stop waiting for PCI\n"); return -ENODEV; } + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for PCI\n"); + return -EACCES; + } msleep(100); } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c index 6b774e0c2766..d0b595ba6110 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c @@ -74,6 +74,10 @@ int mlx5_vsc_gw_lock(struct mlx5_core_dev *dev) ret = -EBUSY; goto pci_unlock; } + if (pci_channel_offline(dev->pdev)) { + ret = -EACCES; + goto pci_unlock; + } /* Check if semaphore is already locked */ ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val); -- cgit v1.2.3 From c8b3f38d2dae0397944814d691a419c451f9906f Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 4 Jun 2024 00:04:43 +0300 Subject: net/mlx5: Always stop health timer during driver removal Currently, if teardown_hca fails to execute during driver removal, mlx5 does not stop the health timer. Afterwards, mlx5 continue with driver teardown. This may lead to a UAF bug, which results in page fault Oops[1], since the health timer invokes after resources were freed. Hence, stop the health monitor even if teardown_hca fails. [1] mlx5_core 0000:18:00.0: E-Switch: Unload vfs: mode(LEGACY), nvfs(0), necvfs(0), active vports(0) mlx5_core 0000:18:00.0: E-Switch: Disable: mode(LEGACY), nvfs(0), necvfs(0), active vports(0) mlx5_core 0000:18:00.0: E-Switch: Disable: mode(LEGACY), nvfs(0), necvfs(0), active vports(0) mlx5_core 0000:18:00.0: E-Switch: cleanup mlx5_core 0000:18:00.0: wait_func:1155:(pid 1967079): TEARDOWN_HCA(0x103) timeout. Will cause a leak of a command resource mlx5_core 0000:18:00.0: mlx5_function_close:1288:(pid 1967079): tear_down_hca failed, skip cleanup BUG: unable to handle page fault for address: ffffa26487064230 PGD 100c00067 P4D 100c00067 PUD 100e5a067 PMD 105ed7067 PTE 0 Oops: 0000 [#1] PREEMPT SMP PTI CPU: 0 PID: 0 Comm: swapper/0 Tainted: G OE ------- --- 6.7.0-68.fc38.x86_64 #1 Hardware name: Intel Corporation S2600WFT/S2600WFT, BIOS SE5C620.86B.02.01.0013.121520200651 12/15/2020 RIP: 0010:ioread32be+0x34/0x60 RSP: 0018:ffffa26480003e58 EFLAGS: 00010292 RAX: ffffa26487064200 RBX: ffff9042d08161a0 RCX: ffff904c108222c0 RDX: 000000010bbf1b80 RSI: ffffffffc055ddb0 RDI: ffffa26487064230 RBP: ffff9042d08161a0 R08: 0000000000000022 R09: ffff904c108222e8 R10: 0000000000000004 R11: 0000000000000441 R12: ffffffffc055ddb0 R13: ffffa26487064200 R14: ffffa26480003f00 R15: ffff904c108222c0 FS: 0000000000000000(0000) GS:ffff904c10800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffa26487064230 CR3: 00000002c4420006 CR4: 00000000007706f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? __die+0x23/0x70 ? page_fault_oops+0x171/0x4e0 ? exc_page_fault+0x175/0x180 ? asm_exc_page_fault+0x26/0x30 ? __pfx_poll_health+0x10/0x10 [mlx5_core] ? __pfx_poll_health+0x10/0x10 [mlx5_core] ? ioread32be+0x34/0x60 mlx5_health_check_fatal_sensors+0x20/0x100 [mlx5_core] ? __pfx_poll_health+0x10/0x10 [mlx5_core] poll_health+0x42/0x230 [mlx5_core] ? __next_timer_interrupt+0xbc/0x110 ? __pfx_poll_health+0x10/0x10 [mlx5_core] call_timer_fn+0x21/0x130 ? __pfx_poll_health+0x10/0x10 [mlx5_core] __run_timers+0x222/0x2c0 run_timer_softirq+0x1d/0x40 __do_softirq+0xc9/0x2c8 __irq_exit_rcu+0xa6/0xc0 sysvec_apic_timer_interrupt+0x72/0x90 asm_sysvec_apic_timer_interrupt+0x1a/0x20 RIP: 0010:cpuidle_enter_state+0xcc/0x440 ? cpuidle_enter_state+0xbd/0x440 cpuidle_enter+0x2d/0x40 do_idle+0x20d/0x270 cpu_startup_entry+0x2a/0x30 rest_init+0xd0/0xd0 arch_call_rest_init+0xe/0x30 start_kernel+0x709/0xa90 x86_64_start_reservations+0x18/0x30 x86_64_start_kernel+0x96/0xa0 secondary_startup_64_no_verify+0x18f/0x19b ---[ end trace 0000000000000000 ]--- Fixes: 9b98d395b85d ("net/mlx5: Start health poll at earlier stage of driver load") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6574c145dc1e..459a836a5d9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1298,6 +1298,9 @@ static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot) if (!err) mlx5_function_disable(dev, boot); + else + mlx5_stop_health_poll(dev, boot); + return err; } -- cgit v1.2.3 From 74751ef5c1912ebd3e65c3b65f45587e05ce5d36 Mon Sep 17 00:00:00 2001 From: Haifeng Xu Date: Mon, 13 May 2024 10:39:48 +0000 Subject: perf/core: Fix missing wakeup when waiting for context reference In our production environment, we found many hung tasks which are blocked for more than 18 hours. Their call traces are like this: [346278.191038] __schedule+0x2d8/0x890 [346278.191046] schedule+0x4e/0xb0 [346278.191049] perf_event_free_task+0x220/0x270 [346278.191056] ? init_wait_var_entry+0x50/0x50 [346278.191060] copy_process+0x663/0x18d0 [346278.191068] kernel_clone+0x9d/0x3d0 [346278.191072] __do_sys_clone+0x5d/0x80 [346278.191076] __x64_sys_clone+0x25/0x30 [346278.191079] do_syscall_64+0x5c/0xc0 [346278.191083] ? syscall_exit_to_user_mode+0x27/0x50 [346278.191086] ? do_syscall_64+0x69/0xc0 [346278.191088] ? irqentry_exit_to_user_mode+0x9/0x20 [346278.191092] ? irqentry_exit+0x19/0x30 [346278.191095] ? exc_page_fault+0x89/0x160 [346278.191097] ? asm_exc_page_fault+0x8/0x30 [346278.191102] entry_SYSCALL_64_after_hwframe+0x44/0xae The task was waiting for the refcount become to 1, but from the vmcore, we found the refcount has already been 1. It seems that the task didn't get woken up by perf_event_release_kernel() and got stuck forever. The below scenario may cause the problem. Thread A Thread B ... ... perf_event_free_task perf_event_release_kernel ... acquire event->child_mutex ... get_ctx ... release event->child_mutex acquire ctx->mutex ... perf_free_event (acquire/release event->child_mutex) ... release ctx->mutex wait_var_event acquire ctx->mutex acquire event->child_mutex # move existing events to free_list release event->child_mutex release ctx->mutex put_ctx ... ... In this case, all events of the ctx have been freed, so we couldn't find the ctx in free_list and Thread A will miss the wakeup. It's thus necessary to add a wakeup after dropping the reference. Fixes: 1cf8dfe8a661 ("perf/core: Fix race between close() and fork()") Signed-off-by: Haifeng Xu Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Frederic Weisbecker Acked-by: Mark Rutland Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20240513103948.33570-1-haifeng.xu@shopee.com --- kernel/events/core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index f0128c5ff278..8f908f077935 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5384,6 +5384,7 @@ int perf_event_release_kernel(struct perf_event *event) again: mutex_lock(&event->child_mutex); list_for_each_entry(child, &event->child_list, child_list) { + void *var = NULL; /* * Cannot change, child events are not migrated, see the @@ -5424,11 +5425,23 @@ again: * this can't be the last reference. */ put_event(event); + } else { + var = &ctx->refcount; } mutex_unlock(&event->child_mutex); mutex_unlock(&ctx->mutex); put_ctx(ctx); + + if (var) { + /* + * If perf_event_free_task() has deleted all events from the + * ctx while the child_mutex got released above, make sure to + * notify about the preceding put_ctx(). + */ + smp_mb(); /* pairs with wait_var_event() */ + wake_up_var(var); + } goto again; } mutex_unlock(&event->child_mutex); -- cgit v1.2.3 From f92a59f6d12e31ead999fee9585471b95a8ae8a3 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Wed, 15 May 2024 13:37:10 +0000 Subject: locking/atomic: scripts: fix ${atomic}_sub_and_test() kerneldoc For ${atomic}_sub_and_test() the @i parameter is the value to subtract, not add. Fix the typo in the kerneldoc template and generate the headers with this update. Fixes: ad8110706f38 ("locking/atomic: scripts: generate kerneldoc comments") Suggested-by: Mark Rutland Signed-off-by: Carlos Llamas Signed-off-by: Peter Zijlstra (Intel) Acked-by: Mark Rutland Reviewed-by: Kees Cook Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20240515133844.3502360-1-cmllamas@google.com --- include/linux/atomic/atomic-arch-fallback.h | 6 +++--- include/linux/atomic/atomic-instrumented.h | 8 ++++---- include/linux/atomic/atomic-long.h | 4 ++-- scripts/atomic/kerneldoc/sub_and_test | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/linux/atomic/atomic-arch-fallback.h b/include/linux/atomic/atomic-arch-fallback.h index 956bcba5dbf2..2f9d36b72bd8 100644 --- a/include/linux/atomic/atomic-arch-fallback.h +++ b/include/linux/atomic/atomic-arch-fallback.h @@ -2242,7 +2242,7 @@ raw_atomic_try_cmpxchg_relaxed(atomic_t *v, int *old, int new) /** * raw_atomic_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: int value to add + * @i: int value to subtract * @v: pointer to atomic_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4368,7 +4368,7 @@ raw_atomic64_try_cmpxchg_relaxed(atomic64_t *v, s64 *old, s64 new) /** * raw_atomic64_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: s64 value to add + * @i: s64 value to subtract * @v: pointer to atomic64_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4690,4 +4690,4 @@ raw_atomic64_dec_if_positive(atomic64_t *v) } #endif /* _LINUX_ATOMIC_FALLBACK_H */ -// 14850c0b0db20c62fdc78ccd1d42b98b88d76331 +// b565db590afeeff0d7c9485ccbca5bb6e155749f diff --git a/include/linux/atomic/atomic-instrumented.h b/include/linux/atomic/atomic-instrumented.h index debd487fe971..9409a6ddf3e0 100644 --- a/include/linux/atomic/atomic-instrumented.h +++ b/include/linux/atomic/atomic-instrumented.h @@ -1349,7 +1349,7 @@ atomic_try_cmpxchg_relaxed(atomic_t *v, int *old, int new) /** * atomic_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: int value to add + * @i: int value to subtract * @v: pointer to atomic_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -2927,7 +2927,7 @@ atomic64_try_cmpxchg_relaxed(atomic64_t *v, s64 *old, s64 new) /** * atomic64_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: s64 value to add + * @i: s64 value to subtract * @v: pointer to atomic64_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4505,7 +4505,7 @@ atomic_long_try_cmpxchg_relaxed(atomic_long_t *v, long *old, long new) /** * atomic_long_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: long value to add + * @i: long value to subtract * @v: pointer to atomic_long_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -5050,4 +5050,4 @@ atomic_long_dec_if_positive(atomic_long_t *v) #endif /* _LINUX_ATOMIC_INSTRUMENTED_H */ -// ce5b65e0f1f8a276268b667194581d24bed219d4 +// 8829b337928e9508259079d32581775ececd415b diff --git a/include/linux/atomic/atomic-long.h b/include/linux/atomic/atomic-long.h index 3ef844b3ab8a..f86b29d90877 100644 --- a/include/linux/atomic/atomic-long.h +++ b/include/linux/atomic/atomic-long.h @@ -1535,7 +1535,7 @@ raw_atomic_long_try_cmpxchg_relaxed(atomic_long_t *v, long *old, long new) /** * raw_atomic_long_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: long value to add + * @i: long value to subtract * @v: pointer to atomic_long_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -1809,4 +1809,4 @@ raw_atomic_long_dec_if_positive(atomic_long_t *v) } #endif /* _LINUX_ATOMIC_LONG_H */ -// 1c4a26fc77f345342953770ebe3c4d08e7ce2f9a +// eadf183c3600b8b92b91839dd3be6bcc560c752d diff --git a/scripts/atomic/kerneldoc/sub_and_test b/scripts/atomic/kerneldoc/sub_and_test index d3760f7749d4..96615e50836b 100644 --- a/scripts/atomic/kerneldoc/sub_and_test +++ b/scripts/atomic/kerneldoc/sub_and_test @@ -1,7 +1,7 @@ cat < Date: Tue, 4 Jun 2024 11:00:22 -0300 Subject: Revert "perf record: Reduce memory for recording PERF_RECORD_LOST_SAMPLES event" This reverts commit 7d1405c71df21f6c394b8a885aa8a133f749fa22. This causes segfaults in some cases, as reported by Milian: ``` sudo /usr/bin/perf record -z --call-graph dwarf -e cycles -e raw_syscalls:sys_enter ls ... [ perf record: Woken up 3 times to write data ] malloc(): invalid next size (unsorted) Aborted ``` Backtrace with GDB + debuginfod: ``` malloc(): invalid next size (unsorted) Thread 1 "perf" received signal SIGABRT, Aborted. __pthread_kill_implementation (threadid=, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44 Downloading source file /usr/src/debug/glibc/glibc/nptl/pthread_kill.c 44 return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; (gdb) bt #0 __pthread_kill_implementation (threadid=, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44 #1 0x00007ffff6ea8eb3 in __pthread_kill_internal (threadid=, signo=6) at pthread_kill.c:78 #2 0x00007ffff6e50a30 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/ raise.c:26 #3 0x00007ffff6e384c3 in __GI_abort () at abort.c:79 #4 0x00007ffff6e39354 in __libc_message_impl (fmt=fmt@entry=0x7ffff6fc22ea "%s\n") at ../sysdeps/posix/libc_fatal.c:132 #5 0x00007ffff6eb3085 in malloc_printerr (str=str@entry=0x7ffff6fc5850 "malloc(): invalid next size (unsorted)") at malloc.c:5772 #6 0x00007ffff6eb657c in _int_malloc (av=av@entry=0x7ffff6ff6ac0 , bytes=bytes@entry=368) at malloc.c:4081 #7 0x00007ffff6eb877e in __libc_calloc (n=, elem_size=) at malloc.c:3754 #8 0x000055555569bdb6 in perf_session.do_write_header () #9 0x00005555555a373a in __cmd_record.constprop.0 () #10 0x00005555555a6846 in cmd_record () #11 0x000055555564db7f in run_builtin () #12 0x000055555558ed77 in main () ``` Valgrind memcheck: ``` ==45136== Invalid write of size 8 ==45136== at 0x2B38A5: perf_event__synthesize_id_sample (in /usr/bin/perf) ==45136== by 0x157069: __cmd_record.constprop.0 (in /usr/bin/perf) ==45136== by 0x15A845: cmd_record (in /usr/bin/perf) ==45136== by 0x201B7E: run_builtin (in /usr/bin/perf) ==45136== by 0x142D76: main (in /usr/bin/perf) ==45136== Address 0x6a866a8 is 0 bytes after a block of size 40 alloc'd ==45136== at 0x4849BF3: calloc (vg_replace_malloc.c:1675) ==45136== by 0x3574AB: zalloc (in /usr/bin/perf) ==45136== by 0x1570E0: __cmd_record.constprop.0 (in /usr/bin/perf) ==45136== by 0x15A845: cmd_record (in /usr/bin/perf) ==45136== by 0x201B7E: run_builtin (in /usr/bin/perf) ==45136== by 0x142D76: main (in /usr/bin/perf) ==45136== ==45136== Syscall param write(buf) points to unaddressable byte(s) ==45136== at 0x575953D: __libc_write (write.c:26) ==45136== by 0x575953D: write (write.c:24) ==45136== by 0x35761F: ion (in /usr/bin/perf) ==45136== by 0x357778: writen (in /usr/bin/perf) ==45136== by 0x1548F7: record__write (in /usr/bin/perf) ==45136== by 0x15708A: __cmd_record.constprop.0 (in /usr/bin/perf) ==45136== by 0x15A845: cmd_record (in /usr/bin/perf) ==45136== by 0x201B7E: run_builtin (in /usr/bin/perf) ==45136== by 0x142D76: main (in /usr/bin/perf) ==45136== Address 0x6a866a8 is 0 bytes after a block of size 40 alloc'd ==45136== at 0x4849BF3: calloc (vg_replace_malloc.c:1675) ==45136== by 0x3574AB: zalloc (in /usr/bin/perf) ==45136== by 0x1570E0: __cmd_record.constprop.0 (in /usr/bin/perf) ==45136== by 0x15A845: cmd_record (in /usr/bin/perf) ==45136== by 0x201B7E: run_builtin (in /usr/bin/perf) ==45136== by 0x142D76: main (in /usr/bin/perf) ==45136== ----- Closes: https://lore.kernel.org/linux-perf-users/23879991.0LEYPuXRzz@milian-workstation/ Reported-by: Milian Wolff Tested-by: Milian Wolff Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: stable@kernel.org # 6.8+ Link: https://lore.kernel.org/lkml/Zl9ksOlHJHnKM70p@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 66a3de8ac661..0a8ba1323d64 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1956,8 +1956,7 @@ static void record__read_lost_samples(struct record *rec) if (count.lost) { if (!lost) { - lost = zalloc(sizeof(*lost) + - session->machines.host.id_hdr_size); + lost = zalloc(PERF_SAMPLE_MAX_SIZE); if (!lost) { pr_debug("Memory allocation failed\n"); return; @@ -1973,8 +1972,7 @@ static void record__read_lost_samples(struct record *rec) lost_count = perf_bpf_filter__lost_count(evsel); if (lost_count) { if (!lost) { - lost = zalloc(sizeof(*lost) + - session->machines.host.id_hdr_size); + lost = zalloc(PERF_SAMPLE_MAX_SIZE); if (!lost) { pr_debug("Memory allocation failed\n"); return; -- cgit v1.2.3 From ca9680821dfec73c9100860bda4fab1f1309722e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 21 May 2024 10:07:40 -0700 Subject: perf bpf: Fix handling of minimal vmlinux.h file when interrupting the build Ingo reported that he was seeing these when hitting Control+C during a perf tools build: Makefile.perf:1149: *** Missing bpftool input for generating vmlinux.h. Stop. The failure happens when you don't have vmlinux.h or vmlinux with BTF. ifeq ($(VMLINUX_H),) ifeq ($(VMLINUX_BTF),) $(error Missing bpftool input for generating vmlinux.h) endif endif VMLINUX_BTF can be empty if you didn't build a kernel or it doesn't have a BTF section and the current kernel also has no BTF. This is totally ok. But VMLINUX_H should be set to the minimal version in the source tree (unless you overwrite it manually) when you don't pass GEN_VMLINUX_H=1 (which requires VMLINUX_BTF should not be empty). The problem is that it's defined in Makefile.config which is not included for `make clean`. Reported-by: Ingo Molnar Signed-off-by: Namhyung Kim Tested-by: Ingo Molnar Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Link: http://lore.kernel.org/lkml/CAM9d7ch5HTr+k+_GpbMrX0HUo5BZ11byh1xq0Two7B7RQACuNw@mail.gmail.com Link: http://lore.kernel.org/lkml/ZjssGrj+abyC6mYP@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5c35c0d89306..e6d56b555369 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -214,6 +214,7 @@ NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help ifdef MAKECMDGOALS ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) + VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h config := 0 endif endif -- cgit v1.2.3 From 9a64e1bfd8a10c015c41fe0d289e89862486c50d Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 2 Jun 2024 22:52:24 -0400 Subject: bcachefs: Fix GFP_KERNEL allocation in break_cycle() Signed-off-by: Kent Overstreet --- fs/bcachefs/btree_locking.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/bcachefs/btree_locking.c b/fs/bcachefs/btree_locking.c index c3e9b0cc7bbd..d66fff22109a 100644 --- a/fs/bcachefs/btree_locking.c +++ b/fs/bcachefs/btree_locking.c @@ -215,6 +215,7 @@ static noinline int break_cycle(struct lock_graph *g, struct printbuf *cycle) if (unlikely(!best)) { struct printbuf buf = PRINTBUF; + buf.atomic++; prt_printf(&buf, bch2_fmt(g->g->trans->c, "cycle of nofail locks")); -- cgit v1.2.3 From fdccb24352e589bb59c9ba90f23c4e0994b90518 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 2 Jun 2024 22:25:18 -0400 Subject: bcachefs: Rereplicate now moves data off of durability=0 devices This fixes an issue where setting a device to durability=0 after it's been used makes it impossible to remove. Signed-off-by: Kent Overstreet --- fs/bcachefs/move.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c index 8171f947fac8..2706d8242345 100644 --- a/fs/bcachefs/move.c +++ b/fs/bcachefs/move.c @@ -920,7 +920,20 @@ static bool rereplicate_pred(struct bch_fs *c, void *arg, ? c->opts.metadata_replicas : io_opts->data_replicas; - if (!nr_good || nr_good >= replicas) + rcu_read_lock(); + struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); + unsigned i = 0; + bkey_for_each_ptr(ptrs, ptr) { + struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev); + if (!ptr->cached && + (!ca || !ca->mi.durability)) + data_opts->kill_ptrs |= BIT(i); + i++; + } + rcu_read_unlock(); + + if (!data_opts->kill_ptrs && + (!nr_good || nr_good >= replicas)) return false; data_opts->target = 0; -- cgit v1.2.3 From 319fef29e96524966bb8593117ce0c5867846eea Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 3 Jun 2024 18:00:48 -0400 Subject: bcachefs: Fix trans->locked assert in bch2_move_data_btree, we might start with the trans unlocked from a previous loop iteration - we need a trans_begin() before iter_init(). Signed-off-by: Kent Overstreet --- fs/bcachefs/move.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c index 2706d8242345..6e477fadaa2a 100644 --- a/fs/bcachefs/move.c +++ b/fs/bcachefs/move.c @@ -547,6 +547,7 @@ static int bch2_move_data_btree(struct moving_context *ctxt, ctxt->stats->pos = BBPOS(btree_id, start); } + bch2_trans_begin(trans); bch2_trans_iter_init(trans, &iter, btree_id, start, BTREE_ITER_prefetch| BTREE_ITER_all_snapshots); -- cgit v1.2.3 From d38e48563c1f70460503de7ffff9a7f46b54b67d Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 3 Jun 2024 21:03:42 +0200 Subject: s390/crash: Do not use VM info if os_info does not have it The virtual memory information stored in os_info area is required for creation of the kernel image PT_LOAD program header for kernels since commit a2ec5bec56dd ("s390/mm: uncouple physical vs virtual address spaces"). By contrast, if such information in os_info is absent the PT_LOAD program header should not be created. Currently the proper PT_LOAD program header is created for kernels that contain the virtual memory information, but for kernels without one an invalid header of zero size is created. That in turn leads to stand-alone dump failures. Use OS_INFO_KASLR_OFFSET variable to check whether os_info is present or not (same as crash and makedumpfile tools do) and based on that create or do not create the kernel image PT_LOAD program header. Fixes: f4cac27dc0d6 ("s390/crash: Use old os_info to create PT_LOAD headers") Tested-by: Mikhail Zaslonko Acked-by: Mikhail Zaslonko Acked-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/kernel/crash_dump.c | 54 ++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index 9863ebe75019..edae13416196 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -451,7 +451,7 @@ static void *nt_final(void *ptr) /* * Initialize ELF header (new kernel) */ -static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt) +static void *ehdr_init(Elf64_Ehdr *ehdr, int phdr_count) { memset(ehdr, 0, sizeof(*ehdr)); memcpy(ehdr->e_ident, ELFMAG, SELFMAG); @@ -465,11 +465,8 @@ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt) ehdr->e_phoff = sizeof(Elf64_Ehdr); ehdr->e_ehsize = sizeof(Elf64_Ehdr); ehdr->e_phentsize = sizeof(Elf64_Phdr); - /* - * Number of memory chunk PT_LOAD program headers plus one kernel - * image PT_LOAD program header plus one PT_NOTE program header. - */ - ehdr->e_phnum = mem_chunk_cnt + 1 + 1; + /* Number of PT_LOAD program headers plus PT_NOTE program header */ + ehdr->e_phnum = phdr_count + 1; return ehdr + 1; } @@ -503,12 +500,14 @@ static int get_mem_chunk_cnt(void) /* * Initialize ELF loads (new kernel) */ -static void loads_init(Elf64_Phdr *phdr) +static void loads_init(Elf64_Phdr *phdr, bool os_info_has_vm) { - unsigned long old_identity_base = os_info_old_value(OS_INFO_IDENTITY_BASE); + unsigned long old_identity_base = 0; phys_addr_t start, end; u64 idx; + if (os_info_has_vm) + old_identity_base = os_info_old_value(OS_INFO_IDENTITY_BASE); for_each_physmem_range(idx, &oldmem_type, &start, &end) { phdr->p_type = PT_LOAD; phdr->p_vaddr = old_identity_base + start; @@ -522,6 +521,11 @@ static void loads_init(Elf64_Phdr *phdr) } } +static bool os_info_has_vm(void) +{ + return os_info_old_value(OS_INFO_KASLR_OFFSET); +} + /* * Prepare PT_LOAD type program header for kernel image region */ @@ -566,7 +570,7 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) return ptr; } -static size_t get_elfcorehdr_size(int mem_chunk_cnt) +static size_t get_elfcorehdr_size(int phdr_count) { size_t size; @@ -581,10 +585,8 @@ static size_t get_elfcorehdr_size(int mem_chunk_cnt) size += nt_vmcoreinfo_size(); /* nt_final */ size += sizeof(Elf64_Nhdr); - /* PT_LOAD type program header for kernel text region */ - size += sizeof(Elf64_Phdr); /* PT_LOADS */ - size += mem_chunk_cnt * sizeof(Elf64_Phdr); + size += phdr_count * sizeof(Elf64_Phdr); return size; } @@ -595,8 +597,8 @@ static size_t get_elfcorehdr_size(int mem_chunk_cnt) int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) { Elf64_Phdr *phdr_notes, *phdr_loads, *phdr_text; + int mem_chunk_cnt, phdr_text_cnt; size_t alloc_size; - int mem_chunk_cnt; void *ptr, *hdr; u64 hdr_off; @@ -615,12 +617,14 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) } mem_chunk_cnt = get_mem_chunk_cnt(); + phdr_text_cnt = os_info_has_vm() ? 1 : 0; - alloc_size = get_elfcorehdr_size(mem_chunk_cnt); + alloc_size = get_elfcorehdr_size(mem_chunk_cnt + phdr_text_cnt); hdr = kzalloc(alloc_size, GFP_KERNEL); - /* Without elfcorehdr /proc/vmcore cannot be created. Thus creating + /* + * Without elfcorehdr /proc/vmcore cannot be created. Thus creating * a dump with this crash kernel will fail. Panic now to allow other * dump mechanisms to take over. */ @@ -628,21 +632,23 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) panic("s390 kdump allocating elfcorehdr failed"); /* Init elf header */ - ptr = ehdr_init(hdr, mem_chunk_cnt); + phdr_notes = ehdr_init(hdr, mem_chunk_cnt + phdr_text_cnt); /* Init program headers */ - phdr_notes = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr)); - phdr_text = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr)); - phdr_loads = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * mem_chunk_cnt); + if (phdr_text_cnt) { + phdr_text = phdr_notes + 1; + phdr_loads = phdr_text + 1; + } else { + phdr_loads = phdr_notes + 1; + } + ptr = PTR_ADD(phdr_loads, sizeof(Elf64_Phdr) * mem_chunk_cnt); /* Init notes */ hdr_off = PTR_DIFF(ptr, hdr); ptr = notes_init(phdr_notes, ptr, ((unsigned long) hdr) + hdr_off); /* Init kernel text program header */ - text_init(phdr_text); + if (phdr_text_cnt) + text_init(phdr_text); /* Init loads */ - loads_init(phdr_loads); + loads_init(phdr_loads, phdr_text_cnt); /* Finalize program headers */ hdr_off = PTR_DIFF(ptr, hdr); *addr = (unsigned long long) hdr; -- cgit v1.2.3 From 01c51a32dc18f128d2e55a7b2128b77fc01a2285 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 12 Apr 2024 10:43:29 +0200 Subject: KVM: s390x: selftests: Add shared zeropage test Let's test that we can have shared zeropages in our process as long as storage keys are not getting used, that shared zeropages are properly unshared (replaced by anonymous pages) once storage keys are enabled, and that no new shared zeropages are populated after storage keys were enabled. We require the new pagemap interface to detect the shared zeropage. On an old kernel (zeropages always disabled): # ./s390x/shared_zeropage_test TAP version 13 1..3 not ok 1 Shared zeropages should be enabled ok 2 Shared zeropage should be gone ok 3 Shared zeropages should be disabled # Totals: pass:2 fail:1 xfail:0 xpass:0 skip:0 error:0 On a fixed kernel: # ./s390x/shared_zeropage_test TAP version 13 1..3 ok 1 Shared zeropages should be enabled ok 2 Shared zeropage should be gone ok 3 Shared zeropages should be disabled # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Testing of UFFDIO_ZEROPAGE can be added later. [ agordeev: Fixed checkpatch complaint, added ucall_common.h include ] Cc: Christian Borntraeger Cc: Janosch Frank Cc: Claudio Imbrenda Cc: Thomas Huth Cc: Alexander Gordeev Cc: Paolo Bonzini Cc: Shuah Khan Signed-off-by: David Hildenbrand Acked-by: Christian Borntraeger Acked-by: Muhammad Usama Anjum Tested-by: Alexander Gordeev Link: https://lore.kernel.org/r/20240412084329.30315-1-david@redhat.com Signed-off-by: Alexander Gordeev --- tools/testing/selftests/kvm/Makefile | 1 + .../selftests/kvm/s390x/shared_zeropage_test.c | 111 +++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tools/testing/selftests/kvm/s390x/shared_zeropage_test.c diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index ce8ff8e8ce3a..ac280dcba996 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -183,6 +183,7 @@ TEST_GEN_PROGS_s390x += s390x/sync_regs_test TEST_GEN_PROGS_s390x += s390x/tprot TEST_GEN_PROGS_s390x += s390x/cmma_test TEST_GEN_PROGS_s390x += s390x/debug_test +TEST_GEN_PROGS_s390x += s390x/shared_zeropage_test TEST_GEN_PROGS_s390x += demand_paging_test TEST_GEN_PROGS_s390x += dirty_log_test TEST_GEN_PROGS_s390x += guest_print_test diff --git a/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c new file mode 100644 index 000000000000..bba0d9a6dcc8 --- /dev/null +++ b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test shared zeropage handling (with/without storage keys) + * + * Copyright (C) 2024, Red Hat, Inc. + */ +#include + +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "kselftest.h" +#include "ucall_common.h" + +static void set_storage_key(void *addr, uint8_t skey) +{ + asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); +} + +static void guest_code(void) +{ + /* Issue some storage key instruction. */ + set_storage_key((void *)0, 0x98); + GUEST_DONE(); +} + +/* + * Returns 1 if the shared zeropage is mapped, 0 if something else is mapped. + * Returns < 0 on error or if nothing is mapped. + */ +static int maps_shared_zeropage(int pagemap_fd, void *addr) +{ + struct page_region region; + struct pm_scan_arg arg = { + .start = (uintptr_t)addr, + .end = (uintptr_t)addr + 4096, + .vec = (uintptr_t)®ion, + .vec_len = 1, + .size = sizeof(struct pm_scan_arg), + .category_mask = PAGE_IS_PFNZERO, + .category_anyof_mask = PAGE_IS_PRESENT, + .return_mask = PAGE_IS_PFNZERO, + }; + return ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); +} + +int main(int argc, char *argv[]) +{ + char *mem, *page0, *page1, *page2, tmp; + const size_t pagesize = getpagesize(); + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + int pagemap_fd; + + ksft_print_header(); + ksft_set_plan(3); + + /* + * We'll use memory that is not mapped into the VM for simplicity. + * Shared zeropages are enabled/disabled per-process. + */ + mem = mmap(0, 3 * pagesize, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0); + TEST_ASSERT(mem != MAP_FAILED, "mmap() failed"); + + /* Disable THP. Ignore errors on older kernels. */ + madvise(mem, 3 * pagesize, MADV_NOHUGEPAGE); + + page0 = mem; + page1 = page0 + pagesize; + page2 = page1 + pagesize; + + /* Can we even detect shared zeropages? */ + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + TEST_REQUIRE(pagemap_fd >= 0); + + tmp = *page0; + asm volatile("" : "+r" (tmp)); + TEST_REQUIRE(maps_shared_zeropage(pagemap_fd, page0) == 1); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + /* Verify that we get the shared zeropage after VM creation. */ + tmp = *page1; + asm volatile("" : "+r" (tmp)); + ksft_test_result(maps_shared_zeropage(pagemap_fd, page1) == 1, + "Shared zeropages should be enabled\n"); + + /* + * Let our VM execute a storage key instruction that should + * unshare all shared zeropages. + */ + vcpu_run(vcpu); + get_ucall(vcpu, &uc); + TEST_ASSERT_EQ(uc.cmd, UCALL_DONE); + + /* Verify that we don't have a shared zeropage anymore. */ + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page1), + "Shared zeropage should be gone\n"); + + /* Verify that we don't get any new shared zeropages. */ + tmp = *page2; + asm volatile("" : "+r" (tmp)); + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page2), + "Shared zeropages should be disabled\n"); + + kvm_vm_free(vm); + + ksft_finished(); +} -- cgit v1.2.3 From 9d274c19a71b3a276949933859610721a453946b Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 24 May 2024 13:58:11 -0700 Subject: btrfs: fix crash on racing fsync and size-extending write into prealloc We have been seeing crashes on duplicate keys in btrfs_set_item_key_safe(): BTRFS critical (device vdb): slot 4 key (450 108 8192) new key (450 108 8192) ------------[ cut here ]------------ kernel BUG at fs/btrfs/ctree.c:2620! invalid opcode: 0000 [#1] PREEMPT SMP PTI CPU: 0 PID: 3139 Comm: xfs_io Kdump: loaded Not tainted 6.9.0 #6 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-2.fc40 04/01/2014 RIP: 0010:btrfs_set_item_key_safe+0x11f/0x290 [btrfs] With the following stack trace: #0 btrfs_set_item_key_safe (fs/btrfs/ctree.c:2620:4) #1 btrfs_drop_extents (fs/btrfs/file.c:411:4) #2 log_one_extent (fs/btrfs/tree-log.c:4732:9) #3 btrfs_log_changed_extents (fs/btrfs/tree-log.c:4955:9) #4 btrfs_log_inode (fs/btrfs/tree-log.c:6626:9) #5 btrfs_log_inode_parent (fs/btrfs/tree-log.c:7070:8) #6 btrfs_log_dentry_safe (fs/btrfs/tree-log.c:7171:8) #7 btrfs_sync_file (fs/btrfs/file.c:1933:8) #8 vfs_fsync_range (fs/sync.c:188:9) #9 vfs_fsync (fs/sync.c:202:9) #10 do_fsync (fs/sync.c:212:9) #11 __do_sys_fdatasync (fs/sync.c:225:9) #12 __se_sys_fdatasync (fs/sync.c:223:1) #13 __x64_sys_fdatasync (fs/sync.c:223:1) #14 do_syscall_x64 (arch/x86/entry/common.c:52:14) #15 do_syscall_64 (arch/x86/entry/common.c:83:7) #16 entry_SYSCALL_64+0xaf/0x14c (arch/x86/entry/entry_64.S:121) So we're logging a changed extent from fsync, which is splitting an extent in the log tree. But this split part already exists in the tree, triggering the BUG(). This is the state of the log tree at the time of the crash, dumped with drgn (https://github.com/osandov/drgn/blob/main/contrib/btrfs_tree.py) to get more details than btrfs_print_leaf() gives us: >>> print_extent_buffer(prog.crashed_thread().stack_trace()[0]["eb"]) leaf 33439744 level 0 items 72 generation 9 owner 18446744073709551610 leaf 33439744 flags 0x100000000000000 fs uuid e5bd3946-400c-4223-8923-190ef1f18677 chunk uuid d58cb17e-6d02-494a-829a-18b7d8a399da item 0 key (450 INODE_ITEM 0) itemoff 16123 itemsize 160 generation 7 transid 9 size 8192 nbytes 8473563889606862198 block group 0 mode 100600 links 1 uid 0 gid 0 rdev 0 sequence 204 flags 0x10(PREALLOC) atime 1716417703.220000000 (2024-05-22 15:41:43) ctime 1716417704.983333333 (2024-05-22 15:41:44) mtime 1716417704.983333333 (2024-05-22 15:41:44) otime 17592186044416.000000000 (559444-03-08 01:40:16) item 1 key (450 INODE_REF 256) itemoff 16110 itemsize 13 index 195 namelen 3 name: 193 item 2 key (450 XATTR_ITEM 1640047104) itemoff 16073 itemsize 37 location key (0 UNKNOWN.0 0) type XATTR transid 7 data_len 1 name_len 6 name: user.a data a item 3 key (450 EXTENT_DATA 0) itemoff 16020 itemsize 53 generation 9 type 1 (regular) extent data disk byte 303144960 nr 12288 extent data offset 0 nr 4096 ram 12288 extent compression 0 (none) item 4 key (450 EXTENT_DATA 4096) itemoff 15967 itemsize 53 generation 9 type 2 (prealloc) prealloc data disk byte 303144960 nr 12288 prealloc data offset 4096 nr 8192 item 5 key (450 EXTENT_DATA 8192) itemoff 15914 itemsize 53 generation 9 type 2 (prealloc) prealloc data disk byte 303144960 nr 12288 prealloc data offset 8192 nr 4096 ... So the real problem happened earlier: notice that items 4 (4k-12k) and 5 (8k-12k) overlap. Both are prealloc extents. Item 4 straddles i_size and item 5 starts at i_size. Here is the state of the filesystem tree at the time of the crash: >>> root = prog.crashed_thread().stack_trace()[2]["inode"].root >>> ret, nodes, slots = btrfs_search_slot(root, BtrfsKey(450, 0, 0)) >>> print_extent_buffer(nodes[0]) leaf 30425088 level 0 items 184 generation 9 owner 5 leaf 30425088 flags 0x100000000000000 fs uuid e5bd3946-400c-4223-8923-190ef1f18677 chunk uuid d58cb17e-6d02-494a-829a-18b7d8a399da ... item 179 key (450 INODE_ITEM 0) itemoff 4907 itemsize 160 generation 7 transid 7 size 4096 nbytes 12288 block group 0 mode 100600 links 1 uid 0 gid 0 rdev 0 sequence 6 flags 0x10(PREALLOC) atime 1716417703.220000000 (2024-05-22 15:41:43) ctime 1716417703.220000000 (2024-05-22 15:41:43) mtime 1716417703.220000000 (2024-05-22 15:41:43) otime 1716417703.220000000 (2024-05-22 15:41:43) item 180 key (450 INODE_REF 256) itemoff 4894 itemsize 13 index 195 namelen 3 name: 193 item 181 key (450 XATTR_ITEM 1640047104) itemoff 4857 itemsize 37 location key (0 UNKNOWN.0 0) type XATTR transid 7 data_len 1 name_len 6 name: user.a data a item 182 key (450 EXTENT_DATA 0) itemoff 4804 itemsize 53 generation 9 type 1 (regular) extent data disk byte 303144960 nr 12288 extent data offset 0 nr 8192 ram 12288 extent compression 0 (none) item 183 key (450 EXTENT_DATA 8192) itemoff 4751 itemsize 53 generation 9 type 2 (prealloc) prealloc data disk byte 303144960 nr 12288 prealloc data offset 8192 nr 4096 Item 5 in the log tree corresponds to item 183 in the filesystem tree, but nothing matches item 4. Furthermore, item 183 is the last item in the leaf. btrfs_log_prealloc_extents() is responsible for logging prealloc extents beyond i_size. It first truncates any previously logged prealloc extents that start beyond i_size. Then, it walks the filesystem tree and copies the prealloc extent items to the log tree. If it hits the end of a leaf, then it calls btrfs_next_leaf(), which unlocks the tree and does another search. However, while the filesystem tree is unlocked, an ordered extent completion may modify the tree. In particular, it may insert an extent item that overlaps with an extent item that was already copied to the log tree. This may manifest in several ways depending on the exact scenario, including an EEXIST error that is silently translated to a full sync, overlapping items in the log tree, or this crash. This particular crash is triggered by the following sequence of events: - Initially, the file has i_size=4k, a regular extent from 0-4k, and a prealloc extent beyond i_size from 4k-12k. The prealloc extent item is the last item in its B-tree leaf. - The file is fsync'd, which copies its inode item and both extent items to the log tree. - An xattr is set on the file, which sets the BTRFS_INODE_COPY_EVERYTHING flag. - The range 4k-8k in the file is written using direct I/O. i_size is extended to 8k, but the ordered extent is still in flight. - The file is fsync'd. Since BTRFS_INODE_COPY_EVERYTHING is set, this calls copy_inode_items_to_log(), which calls btrfs_log_prealloc_extents(). - btrfs_log_prealloc_extents() finds the 4k-12k prealloc extent in the filesystem tree. Since it starts before i_size, it skips it. Since it is the last item in its B-tree leaf, it calls btrfs_next_leaf(). - btrfs_next_leaf() unlocks the path. - The ordered extent completion runs, which converts the 4k-8k part of the prealloc extent to written and inserts the remaining prealloc part from 8k-12k. - btrfs_next_leaf() does a search and finds the new prealloc extent 8k-12k. - btrfs_log_prealloc_extents() copies the 8k-12k prealloc extent into the log tree. Note that it overlaps with the 4k-12k prealloc extent that was copied to the log tree by the first fsync. - fsync calls btrfs_log_changed_extents(), which tries to log the 4k-8k extent that was written. - This tries to drop the range 4k-8k in the log tree, which requires adjusting the start of the 4k-12k prealloc extent in the log tree to 8k. - btrfs_set_item_key_safe() sees that there is already an extent starting at 8k in the log tree and calls BUG(). Fix this by detecting when we're about to insert an overlapping file extent item in the log tree and truncating the part that would overlap. CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Filipe Manana Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 5146387b416b..26a2e5aa08e9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4860,18 +4860,23 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, path->slots[0]++; continue; } - if (!dropped_extents) { - /* - * Avoid logging extent items logged in past fsync calls - * and leading to duplicate keys in the log tree. - */ + /* + * Avoid overlapping items in the log tree. The first time we + * get here, get rid of everything from a past fsync. After + * that, if the current extent starts before the end of the last + * extent we copied, truncate the last one. This can happen if + * an ordered extent completion modifies the subvolume tree + * while btrfs_next_leaf() has the tree unlocked. + */ + if (!dropped_extents || key.offset < truncate_offset) { ret = truncate_inode_items(trans, root->log_root, inode, - truncate_offset, + min(key.offset, truncate_offset), BTRFS_EXTENT_DATA_KEY); if (ret) goto out; dropped_extents = true; } + truncate_offset = btrfs_file_extent_end(path); if (ins_nr == 0) start_slot = slot; ins_nr++; -- cgit v1.2.3 From fb33eb2ef0d88e75564983ef057b44c5b7e4fded Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 3 Jun 2024 12:49:08 +0100 Subject: btrfs: fix leak of qgroup extent records after transaction abort Qgroup extent records are created when delayed ref heads are created and then released after accounting extents at btrfs_qgroup_account_extents(), called during the transaction commit path. If a transaction is aborted we free the qgroup records by calling btrfs_qgroup_destroy_extent_records() at btrfs_destroy_delayed_refs(), unless we don't have delayed references. We are incorrectly assuming that no delayed references means we don't have qgroup extents records. We can currently have no delayed references because we ran them all during a transaction commit and the transaction was aborted after that due to some error in the commit path. So fix this by ensuring we btrfs_qgroup_destroy_extent_records() at btrfs_destroy_delayed_refs() even if we don't have any delayed references. Reported-by: syzbot+0fecc032fa134afd49df@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/0000000000004e7f980619f91835@google.com/ Fixes: 81f7eb00ff5b ("btrfs: destroy qgroup extent records on transaction abort") CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Josef Bacik Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a91a8056758a..242ada7e47b4 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4538,18 +4538,10 @@ static void btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, struct btrfs_fs_info *fs_info) { struct rb_node *node; - struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_delayed_ref_root *delayed_refs = &trans->delayed_refs; struct btrfs_delayed_ref_node *ref; - delayed_refs = &trans->delayed_refs; - spin_lock(&delayed_refs->lock); - if (atomic_read(&delayed_refs->num_entries) == 0) { - spin_unlock(&delayed_refs->lock); - btrfs_debug(fs_info, "delayed_refs has NO entry"); - return; - } - while ((node = rb_first_cached(&delayed_refs->href_root)) != NULL) { struct btrfs_delayed_ref_head *head; struct rb_node *n; -- cgit v1.2.3 From 267cace556e8a53d703119f7435ab556209e5b6a Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 26 May 2024 07:59:08 -0500 Subject: drm/amd: Fix shutdown (again) on some SMU v13.0.4/11 platforms commit cd94d1b182d2 ("dm/amd/pm: Fix problems with reboot/shutdown for some SMU 13.0.4/13.0.11 users") attempted to fix shutdown issues that were reported since commit 31729e8c21ec ("drm/amd/pm: fixes a random hang in S4 for SMU v13.0.4/11") but caused issues for some people. Adjust the workaround flow to properly only apply in the S4 case: -> For shutdown go through SMU_MSG_PrepareMp1ForUnload -> For S4 go through SMU_MSG_GfxDeviceDriverReset and SMU_MSG_PrepareMp1ForUnload Reported-and-tested-by: lectrode Closes: https://github.com/void-linux/void-packages/issues/50417 Cc: stable@vger.kernel.org Fixes: cd94d1b182d2 ("dm/amd/pm: Fix problems with reboot/shutdown for some SMU 13.0.4/13.0.11 users") Reviewed-by: Tim Huang Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index bc241b593db1..b6257f34a7c6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -226,15 +226,17 @@ static int smu_v13_0_4_system_features_control(struct smu_context *smu, bool en) struct amdgpu_device *adev = smu->adev; int ret = 0; - if (!en && adev->in_s4) { - /* Adds a GFX reset as workaround just before sending the - * MP1_UNLOAD message to prevent GC/RLC/PMFW from entering - * an invalid state. - */ - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, - SMU_RESET_MODE_2, NULL); - if (ret) - return ret; + if (!en && !adev->in_s0ix) { + if (adev->in_s4) { + /* Adds a GFX reset as workaround just before sending the + * MP1_UNLOAD message to prevent GC/RLC/PMFW from entering + * an invalid state. + */ + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, + SMU_RESET_MODE_2, NULL); + if (ret) + return ret; + } ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL); } -- cgit v1.2.3 From c6c4dd54012551cce5cde408b35468f2c62b0cce Mon Sep 17 00:00:00 2001 From: Tasos Sahanidis Date: Fri, 31 May 2024 19:23:30 +0300 Subject: drm/amdgpu/pptable: Fix UBSAN array-index-out-of-bounds Flexible arrays used [1] instead of []. Replace the former with the latter to resolve multiple UBSAN warnings observed on boot with a BONAIRE card. In addition, use the __counted_by attribute where possible to hint the length of the arrays to the compiler and any sanitizers. Signed-off-by: Tasos Sahanidis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/include/pptable.h | 91 +++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/amd/include/pptable.h b/drivers/gpu/drm/amd/include/pptable.h index 2e8e6c9875f6..f83ace2d7ec3 100644 --- a/drivers/gpu/drm/amd/include/pptable.h +++ b/drivers/gpu/drm/amd/include/pptable.h @@ -477,31 +477,30 @@ typedef struct _ATOM_PPLIB_STATE_V2 } ATOM_PPLIB_STATE_V2; typedef struct _StateArray{ - //how many states we have - UCHAR ucNumEntries; - - ATOM_PPLIB_STATE_V2 states[1]; + //how many states we have + UCHAR ucNumEntries; + + ATOM_PPLIB_STATE_V2 states[] /* __counted_by(ucNumEntries) */; }StateArray; typedef struct _ClockInfoArray{ - //how many clock levels we have - UCHAR ucNumEntries; - - //sizeof(ATOM_PPLIB_CLOCK_INFO) - UCHAR ucEntrySize; - - UCHAR clockInfo[]; + //how many clock levels we have + UCHAR ucNumEntries; + + //sizeof(ATOM_PPLIB_CLOCK_INFO) + UCHAR ucEntrySize; + + UCHAR clockInfo[]; }ClockInfoArray; typedef struct _NonClockInfoArray{ + //how many non-clock levels we have. normally should be same as number of states + UCHAR ucNumEntries; + //sizeof(ATOM_PPLIB_NONCLOCK_INFO) + UCHAR ucEntrySize; - //how many non-clock levels we have. normally should be same as number of states - UCHAR ucNumEntries; - //sizeof(ATOM_PPLIB_NONCLOCK_INFO) - UCHAR ucEntrySize; - - ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[]; + ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[] __counted_by(ucNumEntries); }NonClockInfoArray; typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record @@ -513,8 +512,10 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_Clock_Voltage_Dependency_Table; typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record @@ -529,8 +530,10 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_Clock_Voltage_Limit_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_Clock_Voltage_Limit_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_Clock_Voltage_Limit_Table; union _ATOM_PPLIB_CAC_Leakage_Record @@ -553,8 +556,10 @@ typedef union _ATOM_PPLIB_CAC_Leakage_Record ATOM_PPLIB_CAC_Leakage_Record; typedef struct _ATOM_PPLIB_CAC_Leakage_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_CAC_Leakage_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_CAC_Leakage_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_CAC_Leakage_Table; typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record @@ -568,8 +573,10 @@ typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_PhaseSheddingLimits_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_PhaseSheddingLimits_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_PhaseSheddingLimits_Table; typedef struct _VCEClockInfo{ @@ -580,8 +587,8 @@ typedef struct _VCEClockInfo{ }VCEClockInfo; typedef struct _VCEClockInfoArray{ - UCHAR ucNumEntries; - VCEClockInfo entries[1]; + UCHAR ucNumEntries; + VCEClockInfo entries[] __counted_by(ucNumEntries); }VCEClockInfoArray; typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record @@ -592,8 +599,8 @@ typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table { - UCHAR numEntries; - ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_VCE_State_Record @@ -604,8 +611,8 @@ typedef struct _ATOM_PPLIB_VCE_State_Record typedef struct _ATOM_PPLIB_VCE_State_Table { - UCHAR numEntries; - ATOM_PPLIB_VCE_State_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_VCE_State_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_VCE_State_Table; @@ -626,8 +633,8 @@ typedef struct _UVDClockInfo{ }UVDClockInfo; typedef struct _UVDClockInfoArray{ - UCHAR ucNumEntries; - UVDClockInfo entries[1]; + UCHAR ucNumEntries; + UVDClockInfo entries[] __counted_by(ucNumEntries); }UVDClockInfoArray; typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record @@ -638,8 +645,8 @@ typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table { - UCHAR numEntries; - ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_UVD_Table @@ -657,8 +664,8 @@ typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Record }ATOM_PPLIB_SAMClk_Voltage_Limit_Record; typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Table{ - UCHAR numEntries; - ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[]; + UCHAR numEntries; + ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_SAMClk_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_SAMU_Table @@ -675,8 +682,8 @@ typedef struct _ATOM_PPLIB_ACPClk_Voltage_Limit_Record }ATOM_PPLIB_ACPClk_Voltage_Limit_Record; typedef struct _ATOM_PPLIB_ACPClk_Voltage_Limit_Table{ - UCHAR numEntries; - ATOM_PPLIB_ACPClk_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_ACPClk_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_ACPClk_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_ACP_Table @@ -743,9 +750,9 @@ typedef struct ATOM_PPLIB_VQ_Budgeting_Record{ } ATOM_PPLIB_VQ_Budgeting_Record; typedef struct ATOM_PPLIB_VQ_Budgeting_Table { - UCHAR revid; - UCHAR numEntries; - ATOM_PPLIB_VQ_Budgeting_Record entries[1]; + UCHAR revid; + UCHAR numEntries; + ATOM_PPLIB_VQ_Budgeting_Record entries[] __counted_by(numEntries); } ATOM_PPLIB_VQ_Budgeting_Table; #pragma pack() -- cgit v1.2.3 From c462ecd659b5fce731f1d592285832fd6ad54053 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Mon, 3 Jun 2024 21:26:45 +0200 Subject: null_blk: fix validation of block size Block size should be between 512 and PAGE_SIZE and be a power of 2. The current check does not validate this, so update the check. Without this patch, null_blk would Oops due to a null pointer deref when loaded with bs=1536 [1]. Link: https://lore.kernel.org/all/87wmn8mocd.fsf@metaspace.dk/ Signed-off-by: Andreas Hindborg Reviewed-by: Ming Lei Link: https://lore.kernel.org/r/20240603192645.977968-1-nmi@metaspace.dk [axboe: remove unnecessary braces and != 0 check] Signed-off-by: Jens Axboe --- drivers/block/null_blk/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 631dca2e4e84..75f189e42f88 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1824,8 +1824,8 @@ static int null_validate_conf(struct nullb_device *dev) dev->queue_mode = NULL_Q_MQ; } - dev->blocksize = round_down(dev->blocksize, 512); - dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096); + if (blk_validate_block_size(dev->blocksize)) + return -EINVAL; if (dev->use_per_node_hctx) { if (dev->submit_queues != nr_online_nodes) -- cgit v1.2.3 From c625dabbf1c4a8e77e4734014f2fde7aa9071a1f Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Mon, 3 Apr 2023 16:42:44 +0000 Subject: x86/amd_nb: Check for invalid SMN reads AMD Zen-based systems use a System Management Network (SMN) that provides access to implementation-specific registers. SMN accesses are done indirectly through an index/data pair in PCI config space. The PCI config access may fail and return an error code. This would prevent the "read" value from being updated. However, the PCI config access may succeed, but the return value may be invalid. This is in similar fashion to PCI bad reads, i.e. return all bits set. Most systems will return 0 for SMN addresses that are not accessible. This is in line with AMD convention that unavailable registers are Read-as-Zero/Writes-Ignored. However, some systems will return a "PCI Error Response" instead. This value, along with an error code of 0 from the PCI config access, will confuse callers of the amd_smn_read() function. Check for this condition, clear the return value, and set a proper error code. Fixes: ddfe43cdc0da ("x86/amd_nb: Add SMN and Indirect Data Fabric access for AMD Fam17h") Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230403164244.471141-1-yazen.ghannam@amd.com --- arch/x86/kernel/amd_nb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 3cf156f70859..027a8c7a2c9e 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -215,7 +215,14 @@ out: int amd_smn_read(u16 node, u32 address, u32 *value) { - return __amd_smn_rw(node, address, value, false); + int err = __amd_smn_rw(node, address, value, false); + + if (PCI_POSSIBLE_ERROR(*value)) { + err = -ENODEV; + *value = 0; + } + + return err; } EXPORT_SYMBOL_GPL(amd_smn_read); -- cgit v1.2.3 From 229bedbf62b13af5aba6525ad10b62ad38d9ccb5 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Tue, 4 Jun 2024 13:05:52 +0300 Subject: net/mlx5: Fix tainted pointer delete is case of flow rules creation fail In case of flow rule creation fail in mlx5_lag_create_port_sel_table(), instead of previously created rules, the tainted pointer is deleted deveral times. Fix this bug by using correct flow rules pointers. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 352899f384d4 ("net/mlx5: Lag, use buckets in hash mode") Signed-off-by: Aleksandr Mishin Reviewed-by: Jacob Keller Reviewed-by: Tariq Toukan Link: https://lore.kernel.org/r/20240604100552.25201-1-amishin@t-argos.ru Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c index c16b462ddedf..ab2717012b79 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c @@ -88,9 +88,13 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev, &dest, 1); if (IS_ERR(lag_definer->rules[idx])) { err = PTR_ERR(lag_definer->rules[idx]); - while (i--) - while (j--) + do { + while (j--) { + idx = i * ldev->buckets + j; mlx5_del_flow_rules(lag_definer->rules[idx]); + } + j = ldev->buckets; + } while (i--); goto destroy_fg; } } -- cgit v1.2.3 From f921a58ae20852d188f70842431ce6519c4fdc36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Jun 2024 18:15:11 +0000 Subject: net/sched: taprio: always validate TCA_TAPRIO_ATTR_PRIOMAP If one TCA_TAPRIO_ATTR_PRIOMAP attribute has been provided, taprio_parse_mqprio_opt() must validate it, or userspace can inject arbitrary data to the kernel, the second time taprio_change() is called. First call (with valid attributes) sets dev->num_tc to a non zero value. Second call (with arbitrary mqprio attributes) returns early from taprio_parse_mqprio_opt() and bad things can happen. Fixes: a3d43c0d56f1 ("taprio: Add support adding an admin schedule") Reported-by: Noam Rathaus Signed-off-by: Eric Dumazet Acked-by: Vinicius Costa Gomes Reviewed-by: Vladimir Oltean Link: https://lore.kernel.org/r/20240604181511.769870-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/sched/sch_taprio.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 937a0c513c17..b284a06b5a75 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1176,16 +1176,13 @@ static int taprio_parse_mqprio_opt(struct net_device *dev, { bool allow_overlapping_txqs = TXTIME_ASSIST_IS_ENABLED(taprio_flags); - if (!qopt && !dev->num_tc) { - NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary"); - return -EINVAL; - } - - /* If num_tc is already set, it means that the user already - * configured the mqprio part - */ - if (dev->num_tc) + if (!qopt) { + if (!dev->num_tc) { + NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary"); + return -EINVAL; + } return 0; + } /* taprio imposes that traffic classes map 1:n to tx queues */ if (qopt->num_tc > dev->num_tx_queues) { -- cgit v1.2.3 From 323a359f9b077f382f4483023d096a4d316fd135 Mon Sep 17 00:00:00 2001 From: Karol Kolacinski Date: Tue, 4 Jun 2024 14:05:27 +0200 Subject: ptp: Fix error message on failed pin verification On failed verification of PTP clock pin, error message prints channel number instead of pin index after "pin", which is incorrect. Fix error message by adding channel number to the message and printing pin number instead of channel number. Fixes: 6092315dfdec ("ptp: introduce programmable pins.") Signed-off-by: Karol Kolacinski Acked-by: Richard Cochran Link: https://lore.kernel.org/r/20240604120555.16643-1-karol.kolacinski@intel.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_chardev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 7513018c9f9a..2067b0120d08 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -85,7 +85,8 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, } if (info->verify(info, pin, func, chan)) { - pr_err("driver cannot use function %u on pin %u\n", func, chan); + pr_err("driver cannot use function %u and channel %u on pin %u\n", + func, chan, pin); return -EOPNOTSUPP; } -- cgit v1.2.3 From 426826933109093503e7ef15d49348fc5ab505fe Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 21 May 2024 13:47:17 -0500 Subject: drm/vmwgfx: Filter modes which exceed graphics memory SVGA requires individual surfaces to fit within graphics memory (max_mob_pages) which means that modes with a final buffer size that would exceed graphics memory must be pruned otherwise creation will fail. Additionally llvmpipe requires its buffer height and width to be a multiple of its tile size which is 64. As a result we have to anticipate that llvmpipe will round up the mode size passed to it by the compositor when it creates buffers and filter modes where this rounding exceeds graphics memory. This fixes an issue where VMs with low graphics memory (< 64MiB) configured with high resolution mode boot to a black screen because surface creation fails. Fixes: d947d1b71deb ("drm/vmwgfx: Add and connect connector helper function") Signed-off-by: Ian Forbes Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-2-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 2041c4d48daa..52e8ec3b2357 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -43,7 +43,14 @@ #define vmw_connector_to_stdu(x) \ container_of(x, struct vmw_screen_target_display_unit, base.connector) - +/* + * Some renderers such as llvmpipe will align the width and height of their + * buffers to match their tile size. We need to keep this in mind when exposing + * modes to userspace so that this possible over-allocation will not exceed + * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the + * tile size of current renderers. + */ +#define GPU_TILE_SIZE 64 enum stdu_content_type { SAME_AS_DISPLAY = 0, @@ -829,7 +836,41 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector) vmw_stdu_destroy(vmw_connector_to_stdu(connector)); } +static enum drm_mode_status +vmw_stdu_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + enum drm_mode_status ret; + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + u64 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4; + /* Align width and height to account for GPU tile over-alignment */ + u64 required_mem = ALIGN(mode->hdisplay, GPU_TILE_SIZE) * + ALIGN(mode->vdisplay, GPU_TILE_SIZE) * + assumed_cpp; + required_mem = ALIGN(required_mem, PAGE_SIZE); + + ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width, + dev_priv->stdu_max_height); + if (ret != MODE_OK) + return ret; + ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, + dev_priv->texture_max_height); + if (ret != MODE_OK) + return ret; + + if (required_mem > dev_priv->max_primary_mem) + return MODE_MEM; + + if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE) + return MODE_MEM; + + if (required_mem > dev_priv->max_mob_size) + return MODE_MEM; + + return MODE_OK; +} static const struct drm_connector_funcs vmw_stdu_connector_funcs = { .dpms = vmw_du_connector_dpms, @@ -845,7 +886,7 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { static const struct drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { .get_modes = vmw_connector_get_modes, - .mode_valid = vmw_connector_mode_valid + .mode_valid = vmw_stdu_connector_mode_valid }; -- cgit v1.2.3 From fb5e19d2dd03eb995ccd468d599b2337f7f66555 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 21 May 2024 13:47:18 -0500 Subject: drm/vmwgfx: 3D disabled should not effect STDU memory limits This limit became a hard cap starting with the change referenced below. Surface creation on the device will fail if the requested size is larger than this limit so altering the value arbitrarily will expose modes that are too large for the device's hard limits. Fixes: 7ebb47c9f9ab ("drm/vmwgfx: Read new register for GB memory when available") Signed-off-by: Ian Forbes Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-3-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8f1730aeacc9..18a1f05c6fa8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -960,13 +960,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) vmw_read(dev_priv, SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); - /* - * Workaround for low memory 2D VMs to compensate for the - * allocation taken by fbdev - */ - if (!(dev_priv->capabilities & SVGA_CAP_3D)) - mem_size *= 3; - dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; dev_priv->max_primary_mem = vmw_read(dev_priv, SVGA_REG_MAX_PRIMARY_MEM); -- cgit v1.2.3 From dde1de06bd7248fd83c4ce5cf0dbe9e4e95bbb91 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 21 May 2024 13:47:19 -0500 Subject: drm/vmwgfx: Remove STDU logic from generic mode_valid function STDU has its own mode_valid function now so this logic can be removed from the generic version. Fixes: 935f795045a6 ("drm/vmwgfx: Refactor drm connector probing for display modes") Signed-off-by: Ian Forbes Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-4-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 3 --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 26 +++++++++----------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 4ecaea0026fc..a1ce41e1c468 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1043,9 +1043,6 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, unsigned bpp, unsigned depth); -bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height); int vmw_kms_present(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_framebuffer *vfb, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 13b2820cae51..9532258a0848 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2171,13 +2171,12 @@ int vmw_kms_write_svga(struct vmw_private *vmw_priv, return 0; } +static bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height) + u64 pitch, + u64 height) { - return ((u64) pitch * (u64) height) < (u64) - ((dev_priv->active_display_unit == vmw_du_screen_target) ? - dev_priv->max_primary_mem : dev_priv->vram_size); + return (pitch * height) < (u64)dev_priv->vram_size; } /** @@ -2873,25 +2872,18 @@ out_unref: enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + enum drm_mode_status ret; struct drm_device *dev = connector->dev; struct vmw_private *dev_priv = vmw_priv(dev); - u32 max_width = dev_priv->texture_max_width; - u32 max_height = dev_priv->texture_max_height; u32 assumed_cpp = 4; if (dev_priv->assume_16bpp) assumed_cpp = 2; - if (dev_priv->active_display_unit == vmw_du_screen_target) { - max_width = min(dev_priv->stdu_max_width, max_width); - max_height = min(dev_priv->stdu_max_height, max_height); - } - - if (max_width < mode->hdisplay) - return MODE_BAD_HVALUE; - - if (max_height < mode->vdisplay) - return MODE_BAD_VVALUE; + ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, + dev_priv->texture_max_height); + if (ret != MODE_OK) + return ret; if (!vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * assumed_cpp, -- cgit v1.2.3 From a54a200f3dc710db0572aba45c5c06b12b74489a Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 21 May 2024 13:47:20 -0500 Subject: drm/vmwgfx: Standardize use of kibibytes when logging Use the same standard abbreviation KiB instead of incorrect variants. Signed-off-by: Ian Forbes Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240521184720.767-5-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 12 ++++++------ drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 18a1f05c6fa8..823d8d2da17c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -746,7 +746,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, dev->vram_size = pci_resource_len(pdev, 2); drm_info(&dev->drm, - "Register MMIO at 0x%pa size is %llu kiB\n", + "Register MMIO at 0x%pa size is %llu KiB\n", &rmmio_start, (uint64_t)rmmio_size / 1024); dev->rmmio = devm_ioremap(dev->drm.dev, rmmio_start, @@ -765,7 +765,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, fifo_size = pci_resource_len(pdev, 2); drm_info(&dev->drm, - "FIFO at %pa size is %llu kiB\n", + "FIFO at %pa size is %llu KiB\n", &fifo_start, (uint64_t)fifo_size / 1024); dev->fifo_mem = devm_memremap(dev->drm.dev, fifo_start, @@ -790,7 +790,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, * SVGA_REG_VRAM_SIZE. */ drm_info(&dev->drm, - "VRAM at %pa size is %llu kiB\n", + "VRAM at %pa size is %llu KiB\n", &dev->vram_start, (uint64_t)dev->vram_size / 1024); return 0; @@ -984,13 +984,13 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) dev_priv->max_primary_mem = dev_priv->vram_size; } drm_info(&dev_priv->drm, - "Legacy memory limits: VRAM = %llu kB, FIFO = %llu kB, surface = %u kB\n", + "Legacy memory limits: VRAM = %llu KiB, FIFO = %llu KiB, surface = %u KiB\n", (u64)dev_priv->vram_size / 1024, (u64)dev_priv->fifo_mem_size / 1024, dev_priv->memory_size / 1024); drm_info(&dev_priv->drm, - "MOB limits: max mob size = %u kB, max mob pages = %u\n", + "MOB limits: max mob size = %u KiB, max mob pages = %u\n", dev_priv->max_mob_size / 1024, dev_priv->max_mob_pages); ret = vmw_dma_masks(dev_priv); @@ -1008,7 +1008,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) (unsigned)dev_priv->max_gmr_pages); } drm_info(&dev_priv->drm, - "Maximum display memory size is %llu kiB\n", + "Maximum display memory size is %llu KiB\n", (uint64_t)dev_priv->max_primary_mem / 1024); /* Need mmio memory to check for fifo pitchlock cap. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index a0b47c9b33f5..5bd967fbcf55 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -94,14 +94,14 @@ static int vmw_gmrid_man_get_node(struct ttm_resource_manager *man, } else new_max_pages = gman->max_gmr_pages * 2; if (new_max_pages > gman->max_gmr_pages && new_max_pages >= gman->used_gmr_pages) { - DRM_WARN("vmwgfx: increasing guest mob limits to %u kB.\n", + DRM_WARN("vmwgfx: increasing guest mob limits to %u KiB.\n", ((new_max_pages) << (PAGE_SHIFT - 10))); gman->max_gmr_pages = new_max_pages; } else { char buf[256]; snprintf(buf, sizeof(buf), - "vmwgfx, error: guest graphics is out of memory (mob limit at: %ukB).\n", + "vmwgfx, error: guest graphics is out of memory (mob limit at: %u KiB).\n", ((gman->max_gmr_pages) << (PAGE_SHIFT - 10))); vmw_host_printf(buf); DRM_WARN("%s", buf); -- cgit v1.2.3 From 7ef91dcba172441582962602ff6899bfec6078b7 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Fri, 31 May 2024 15:33:58 -0500 Subject: drm/vmwgfx: Don't destroy Screen Target when CRTC is enabled but inactive drm_crtc_helper_funcs::atomic_disable can be called even when the CRTC is still enabled. This can occur when the mode changes or the CRTC is set as inactive. In the case where the CRTC is being set as inactive we only want to blank the screen. The Screen Target should remain intact as long as the mode has not changed and CRTC is enabled. This fixes a bug with GDM where locking the screen results in a permanent black screen because the Screen Target is no longer defined. Fixes: 7b0062036c3b ("drm/vmwgfx: Implement virtual crc generation") Signed-off-by: Ian Forbes Reviewed-by: Martin Krastev Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240531203358.26677-1-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 52e8ec3b2357..afc0bc4ef4e1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -421,6 +421,7 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, { struct vmw_private *dev_priv; struct vmw_screen_target_display_unit *stdu; + struct drm_crtc_state *new_crtc_state; int ret; if (!crtc) { @@ -430,6 +431,7 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, stdu = vmw_crtc_to_stdu(crtc); dev_priv = vmw_priv(crtc->dev); + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (dev_priv->vkms_enabled) drm_crtc_vblank_off(crtc); @@ -441,6 +443,14 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, (void) vmw_stdu_update_st(dev_priv, stdu); + /* Don't destroy the Screen Target if we are only setting the + * display as inactive + */ + if (new_crtc_state->enable && + !new_crtc_state->active && + !new_crtc_state->mode_changed) + return; + ret = vmw_stdu_destroy_st(dev_priv, stdu); if (ret) DRM_ERROR("Failed to destroy Screen Target\n"); -- cgit v1.2.3 From 0d648dd5c899f33154b98a6aef6e3dab0f4de613 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Thu, 23 May 2024 10:36:39 +0800 Subject: mm: drop the 'anon_' prefix for swap-out mTHP counters The mTHP swap related counters: 'anon_swpout' and 'anon_swpout_fallback' are confusing with an 'anon_' prefix, since the shmem can swap out non-anonymous pages. So drop the 'anon_' prefix to keep consistent with the old swap counter names. This is needed in 6.10-rcX to avoid having an inconsistent ABI out in the field. Link: https://lkml.kernel.org/r/7a8989c13299920d7589007a30065c3e2c19f0e0.1716431702.git.baolin.wang@linux.alibaba.com Fixes: d0f048ac39f6 ("mm: add per-order mTHP anon_swpout and anon_swpout_fallback counters") Fixes: 42248b9d34ea ("mm: add docs for per-order mTHP counters and transhuge_page ABI") Signed-off-by: Baolin Wang Suggested-by: "Huang, Ying" Acked-by: Barry Song Cc: David Hildenbrand Cc: Lance Yang Cc: Matthew Wilcox (Oracle) Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- Documentation/admin-guide/mm/transhuge.rst | 4 ++-- include/linux/huge_mm.h | 4 ++-- mm/huge_memory.c | 8 ++++---- mm/page_io.c | 2 +- mm/vmscan.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index 076443cc10a6..d414d3f5592a 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -467,11 +467,11 @@ anon_fault_fallback_charge instead falls back to using huge pages with lower orders or small pages even though the allocation was successful. -anon_swpout +swpout is incremented every time a huge page is swapped out in one piece without splitting. -anon_swpout_fallback +swpout_fallback is incremented if a huge page has to be split before swapout. Usually because failed to allocate some continuous swap space for the huge page. diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index c8d3ec116e29..8c72d3786583 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -269,8 +269,8 @@ enum mthp_stat_item { MTHP_STAT_ANON_FAULT_ALLOC, MTHP_STAT_ANON_FAULT_FALLBACK, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE, - MTHP_STAT_ANON_SWPOUT, - MTHP_STAT_ANON_SWPOUT_FALLBACK, + MTHP_STAT_SWPOUT, + MTHP_STAT_SWPOUT_FALLBACK, __MTHP_STAT_COUNT }; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 317de2afd371..89932fd0f62e 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -558,15 +558,15 @@ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) DEFINE_MTHP_STAT_ATTR(anon_fault_alloc, MTHP_STAT_ANON_FAULT_ALLOC); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback, MTHP_STAT_ANON_FAULT_FALLBACK); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback_charge, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); -DEFINE_MTHP_STAT_ATTR(anon_swpout, MTHP_STAT_ANON_SWPOUT); -DEFINE_MTHP_STAT_ATTR(anon_swpout_fallback, MTHP_STAT_ANON_SWPOUT_FALLBACK); +DEFINE_MTHP_STAT_ATTR(swpout, MTHP_STAT_SWPOUT); +DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK); static struct attribute *stats_attrs[] = { &anon_fault_alloc_attr.attr, &anon_fault_fallback_attr.attr, &anon_fault_fallback_charge_attr.attr, - &anon_swpout_attr.attr, - &anon_swpout_fallback_attr.attr, + &swpout_attr.attr, + &swpout_fallback_attr.attr, NULL, }; diff --git a/mm/page_io.c b/mm/page_io.c index 46c603dddf04..0a150c240bf4 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -217,7 +217,7 @@ static inline void count_swpout_vm_event(struct folio *folio) count_memcg_folio_events(folio, THP_SWPOUT, 1); count_vm_event(THP_SWPOUT); } - count_mthp_stat(folio_order(folio), MTHP_STAT_ANON_SWPOUT); + count_mthp_stat(folio_order(folio), MTHP_STAT_SWPOUT); #endif count_vm_events(PSWPOUT, folio_nr_pages(folio)); } diff --git a/mm/vmscan.c b/mm/vmscan.c index d55e8d07ffc4..2e34de9cd0d4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1227,7 +1227,7 @@ retry: THP_SWPOUT_FALLBACK, 1); count_vm_event(THP_SWPOUT_FALLBACK); } - count_mthp_stat(order, MTHP_STAT_ANON_SWPOUT_FALLBACK); + count_mthp_stat(order, MTHP_STAT_SWPOUT_FALLBACK); #endif if (!add_to_swap(folio)) goto activate_locked_split; -- cgit v1.2.3 From 94d46bf17916965e918bd2f3d2eec057f7c5578d Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 24 May 2024 08:50:48 +1200 Subject: mm: huge_mm: fix undefined reference to `mthp_stats' for CONFIG_SYSFS=n if CONFIG_SYSFS is not enabled in config, we get the below error, All errors (new ones prefixed by >>): s390-linux-ld: mm/memory.o: in function `count_mthp_stat': >> include/linux/huge_mm.h:285:(.text+0x191c): undefined reference to `mthp_stats' s390-linux-ld: mm/huge_memory.o:(.rodata+0x10): undefined reference to `mthp_stats' vim +285 include/linux/huge_mm.h 279 280 static inline void count_mthp_stat(int order, enum mthp_stat_item item) 281 { 282 if (order <= 0 || order > PMD_ORDER) 283 return; 284 > 285 this_cpu_inc(mthp_stats.stats[order][item]); 286 } 287 Link: https://lkml.kernel.org/r/20240523210045.40444-1-21cnbao@gmail.com Fixes: ec33687c6749 ("mm: add per-order mTHP anon_fault_alloc and anon_fault_fallback counters") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202405231728.tCAogiSI-lkp@intel.com/ Signed-off-by: Barry Song Tested-by: Yujie Liu Signed-off-by: Andrew Morton --- include/linux/huge_mm.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 8c72d3786583..2aa986a5cd1b 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -278,6 +278,7 @@ struct mthp_stat { unsigned long stats[ilog2(MAX_PTRS_PER_PTE) + 1][__MTHP_STAT_COUNT]; }; +#ifdef CONFIG_SYSFS DECLARE_PER_CPU(struct mthp_stat, mthp_stats); static inline void count_mthp_stat(int order, enum mthp_stat_item item) @@ -287,6 +288,11 @@ static inline void count_mthp_stat(int order, enum mthp_stat_item item) this_cpu_inc(mthp_stats.stats[order][item]); } +#else +static inline void count_mthp_stat(int order, enum mthp_stat_item item) +{ +} +#endif #define transparent_hugepage_use_zero_page() \ (transparent_hugepage_flags & \ -- cgit v1.2.3 From 6434e69814b159608a23135ca2be36024f402717 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 24 May 2024 12:54:44 +1200 Subject: mm: arm64: fix the out-of-bounds issue in contpte_clear_young_dirty_ptes We are passing a huge nr to __clear_young_dirty_ptes() right now. While we should pass the number of pages, we are actually passing CONT_PTE_SIZE. This is causing lots of crashes of MADV_FREE, panic oops could vary everytime. Link: https://lkml.kernel.org/r/20240524005444.135417-1-21cnbao@gmail.com Fixes: 89e86854fb0a ("mm/arm64: override clear_young_dirty_ptes() batch helper") Signed-off-by: Barry Song Reviewed-by: Baolin Wang Acked-by: Lance Yang Acked-by: David Hildenbrand Acked-by: Chris Li Cc: Barry Song <21cnbao@gmail.com> Cc: Ryan Roberts Cc: Jeff Xie Cc: Kefeng Wang Cc: Michal Hocko Cc: Minchan Kim Cc: Muchun Song Cc: Peter Xu Cc: Yang Shi Cc: Yin Fengwei Cc: Zach O'Keefe Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Andrew Morton --- arch/arm64/mm/contpte.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c index 9f9486de0004..a3edced29ac1 100644 --- a/arch/arm64/mm/contpte.c +++ b/arch/arm64/mm/contpte.c @@ -376,7 +376,7 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma, * clearing access/dirty for the whole block. */ unsigned long start = addr; - unsigned long end = start + nr; + unsigned long end = start + nr * PAGE_SIZE; if (pte_cont(__ptep_get(ptep + nr - 1))) end = ALIGN(end, CONT_PTE_SIZE); @@ -386,7 +386,7 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma, ptep = contpte_align_down(ptep); } - __clear_young_dirty_ptes(vma, start, ptep, end - start, flags); + __clear_young_dirty_ptes(vma, start, ptep, (end - start) / PAGE_SIZE, flags); } EXPORT_SYMBOL_GPL(contpte_clear_young_dirty_ptes); -- cgit v1.2.3 From 36eef400c2d571d05b6e41a9a61f874d4a7b82c5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 28 May 2024 16:13:41 +0200 Subject: memcg: remove the lockdep assert from __mod_objcg_mlstate() The assert was introduced in the commit cited below as an insurance that the semantic is the same after the local_irq_save() has been removed and the function has been made static. The original requirement to disable interrupt was due the modification of per-CPU counters which require interrupts to be disabled because the counter update operation is not atomic and some of the counters are updated from interrupt context. All callers of __mod_objcg_mlstate() acquire a lock (memcg_stock.stock_lock) which disables interrupts on !PREEMPT_RT and the lockdep assert is satisfied. On PREEMPT_RT the interrupts are not disabled and the assert triggers. The safety of the counter update is already ensured by VM_WARN_ON_IRQS_ENABLED() which is part of __mod_memcg_lruvec_state() and does not require yet another check. Remove the lockdep assert from __mod_objcg_mlstate(). Link: https://lkml.kernel.org/r/20240528141341.rz_rytN_@linutronix.de Fixes: 91882c1617c1 ("memcg: simple cleanup of stats update functions") Signed-off-by: Sebastian Andrzej Siewior Acked-by: Vlastimil Babka Acked-by: Shakeel Butt Acked-by: Michal Hocko Reviewed-by: Vlastimil Babka Cc: Johannes Weiner Cc: Muchun Song Cc: Roman Gushchin Cc: Thomas Gleixner Signed-off-by: Andrew Morton --- mm/memcontrol.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7fad15b2290c..36793e509f47 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3147,8 +3147,6 @@ static inline void __mod_objcg_mlstate(struct obj_cgroup *objcg, struct mem_cgroup *memcg; struct lruvec *lruvec; - lockdep_assert_irqs_disabled(); - rcu_read_lock(); memcg = obj_cgroup_memcg(objcg); lruvec = mem_cgroup_lruvec(memcg, pgdat); -- cgit v1.2.3 From a4ca369ca221bb7e06c725792ac107f0e48e82e7 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 30 May 2024 23:15:56 +0900 Subject: nilfs2: fix potential kernel bug due to lack of writeback flag waiting Destructive writes to a block device on which nilfs2 is mounted can cause a kernel bug in the folio/page writeback start routine or writeback end routine (__folio_start_writeback in the log below): kernel BUG at mm/page-writeback.c:3070! Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN PTI ... RIP: 0010:__folio_start_writeback+0xbaa/0x10e0 Code: 25 ff 0f 00 00 0f 84 18 01 00 00 e8 40 ca c6 ff e9 17 f6 ff ff e8 36 ca c6 ff 4c 89 f7 48 c7 c6 80 c0 12 84 e8 e7 b3 0f 00 90 <0f> 0b e8 1f ca c6 ff 4c 89 f7 48 c7 c6 a0 c6 12 84 e8 d0 b3 0f 00 ... Call Trace: nilfs_segctor_do_construct+0x4654/0x69d0 [nilfs2] nilfs_segctor_construct+0x181/0x6b0 [nilfs2] nilfs_segctor_thread+0x548/0x11c0 [nilfs2] kthread+0x2f0/0x390 ret_from_fork+0x4b/0x80 ret_from_fork_asm+0x1a/0x30 This is because when the log writer starts a writeback for segment summary blocks or a super root block that use the backing device's page cache, it does not wait for the ongoing folio/page writeback, resulting in an inconsistent writeback state. Fix this issue by waiting for ongoing writebacks when putting folios/pages on the backing device into writeback state. Link: https://lkml.kernel.org/r/20240530141556.4411-1-konishi.ryusuke@gmail.com Fixes: 9ff05123e3bf ("nilfs2: segment constructor") Signed-off-by: Ryusuke Konishi Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton --- fs/nilfs2/segment.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 60d4f59f7665..6ea81f1d5094 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1652,6 +1652,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) if (bh->b_folio != bd_folio) { if (bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); @@ -1665,6 +1666,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) if (bh == segbuf->sb_super_root) { if (bh->b_folio != bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); @@ -1681,6 +1683,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) } if (bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); -- cgit v1.2.3 From 7cc5a5d65011983952a9c62f170f5b79e24b1239 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 29 May 2024 18:18:12 -0400 Subject: mm: page_alloc: fix highatomic typing in multi-block buddies Christoph reports a page allocator splat triggered by xfstests: generic/176 214s ... [ 1204.507931] run fstests generic/176 at 2024-05-27 12:52:30 XFS (nvme0n1): Mounting V5 Filesystem cd936307-415f-48a3-b99d-a2d52ae1f273 XFS (nvme0n1): Ending clean mount XFS (nvme1n1): Mounting V5 Filesystem ab3ee1a4-af62-4934-9a6a-6c2fde321850 XFS (nvme1n1): Ending clean mount XFS (nvme1n1): Unmounting Filesystem ab3ee1a4-af62-4934-9a6a-6c2fde321850 XFS (nvme1n1): Mounting V5 Filesystem 7099b02d-9c58-4d1d-be1d-2cc472d12cd9 XFS (nvme1n1): Ending clean mount ------------[ cut here ]------------ page type is 3, passed migratetype is 1 (nr=512) WARNING: CPU: 0 PID: 509870 at mm/page_alloc.c:645 expand+0x1c5/0x1f0 Modules linked in: i2c_i801 crc32_pclmul i2c_smbus [last unloaded: scsi_debug] CPU: 0 PID: 509870 Comm: xfs_io Not tainted 6.10.0-rc1+ #2437 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:expand+0x1c5/0x1f0 Code: 05 16 70 bf 02 01 e8 ca fc ff ff 8b 54 24 34 44 89 e1 48 c7 c7 80 a2 28 83 48 89 c6 b8 01 00 3 RSP: 0018:ffffc90003b2b968 EFLAGS: 00010082 RAX: 0000000000000000 RBX: ffffffff83fa9480 RCX: 0000000000000000 RDX: 0000000000000005 RSI: 0000000000000027 RDI: 00000000ffffffff RBP: 00000000001f2600 R08: 00000000fffeffff R09: 0000000000000001 R10: 0000000000000000 R11: ffffffff83676200 R12: 0000000000000009 R13: 0000000000000200 R14: 0000000000000001 R15: ffffea0007c98000 FS: 00007f72ca3d5780(0000) GS:ffff8881f9c00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f72ca1fff38 CR3: 00000001aa0c6002 CR4: 0000000000770ef0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff07f0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? __warn+0x7b/0x120 ? expand+0x1c5/0x1f0 ? report_bug+0x191/0x1c0 ? handle_bug+0x3c/0x80 ? exc_invalid_op+0x17/0x70 ? asm_exc_invalid_op+0x1a/0x20 ? expand+0x1c5/0x1f0 ? expand+0x1c5/0x1f0 __rmqueue_pcplist+0x3a9/0x730 get_page_from_freelist+0x7a0/0xf00 __alloc_pages_noprof+0x153/0x2e0 __folio_alloc_noprof+0x10/0xa0 __filemap_get_folio+0x16b/0x370 iomap_write_begin+0x496/0x680 While trying to service a movable allocation (page type 1), the page allocator runs into a two-pageblock buddy on the movable freelist whose second block is typed as highatomic (page type 3). This inconsistency is caused by the highatomic reservation system operating on single pageblocks, while MAX_ORDER can be bigger than that - in this configuration, pageblock_order is 9 while MAX_PAGE_ORDER is 10. The test case is observed to make several adjacent order-3 requests with __GFP_DIRECT_RECLAIM cleared, which marks the surrounding block as highatomic. Upon freeing, the blocks merge into an order-10 buddy. When the highatomic pool is drained later on, this order-10 buddy gets moved back to the movable list, but only the first pageblock is marked movable again. A subsequent expand() of this buddy warns about the tail being of a different type. This is a long-standing bug that's surfaced by the recent block type warnings added to the allocator. The consequences seem mostly benign, it just results in odd behavior: the highatomic tail blocks are not properly drained, instead they end up on the movable list first, then go back to the highatomic list after an alloc-free cycle. To fix this, make the highatomic reservation code aware that allocations/buddies can be larger than a pageblock. While it's an old quirk, the recently added type consistency warnings seem to be the most prominent consequence of it. Set the Fixes: tag accordingly to highlight this backporting dependency. Link: https://lkml.kernel.org/r/20240530114203.GA1222079@cmpxchg.org Fixes: e0932b6c1f94 ("mm: page_alloc: consolidate free page accounting") Signed-off-by: Johannes Weiner Reported-by: Christoph Hellwig Reviewed-by: Zi Yan Tested-by: Christoph Hellwig Cc: Andy Shevchenko Cc: Baolin Wang Cc: Mel Gorman Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- mm/page_alloc.c | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2e22ce5675ca..222299b5c0e6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1955,10 +1955,12 @@ int find_suitable_fallback(struct free_area *area, unsigned int order, } /* - * Reserve a pageblock for exclusive use of high-order atomic allocations if - * there are no empty page blocks that contain a page with a suitable order + * Reserve the pageblock(s) surrounding an allocation request for + * exclusive use of high-order atomic allocations if there are no + * empty page blocks that contain a page with a suitable order */ -static void reserve_highatomic_pageblock(struct page *page, struct zone *zone) +static void reserve_highatomic_pageblock(struct page *page, int order, + struct zone *zone) { int mt; unsigned long max_managed, flags; @@ -1984,10 +1986,17 @@ static void reserve_highatomic_pageblock(struct page *page, struct zone *zone) /* Yoink! */ mt = get_pageblock_migratetype(page); /* Only reserve normal pageblocks (i.e., they can merge with others) */ - if (migratetype_is_mergeable(mt)) - if (move_freepages_block(zone, page, mt, - MIGRATE_HIGHATOMIC) != -1) - zone->nr_reserved_highatomic += pageblock_nr_pages; + if (!migratetype_is_mergeable(mt)) + goto out_unlock; + + if (order < pageblock_order) { + if (move_freepages_block(zone, page, mt, MIGRATE_HIGHATOMIC) == -1) + goto out_unlock; + zone->nr_reserved_highatomic += pageblock_nr_pages; + } else { + change_pageblock_range(page, order, MIGRATE_HIGHATOMIC); + zone->nr_reserved_highatomic += 1 << order; + } out_unlock: spin_unlock_irqrestore(&zone->lock, flags); @@ -1999,7 +2008,7 @@ out_unlock: * intense memory pressure but failed atomic allocations should be easier * to recover from than an OOM. * - * If @force is true, try to unreserve a pageblock even though highatomic + * If @force is true, try to unreserve pageblocks even though highatomic * pageblock is exhausted. */ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, @@ -2041,6 +2050,7 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * adjust the count once. */ if (is_migrate_highatomic(mt)) { + unsigned long size; /* * It should never happen but changes to * locking could inadvertently allow a per-cpu @@ -2048,9 +2058,9 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * while unreserving so be safe and watch for * underflows. */ - zone->nr_reserved_highatomic -= min( - pageblock_nr_pages, - zone->nr_reserved_highatomic); + size = max(pageblock_nr_pages, 1UL << order); + size = min(size, zone->nr_reserved_highatomic); + zone->nr_reserved_highatomic -= size; } /* @@ -2062,11 +2072,19 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * of pageblocks that cannot be completely freed * may increase. */ - ret = move_freepages_block(zone, page, mt, - ac->migratetype); + if (order < pageblock_order) + ret = move_freepages_block(zone, page, mt, + ac->migratetype); + else { + move_to_free_list(page, zone, order, mt, + ac->migratetype); + change_pageblock_range(page, order, + ac->migratetype); + ret = 1; + } /* - * Reserving this block already succeeded, so this should - * not fail on zone boundaries. + * Reserving the block(s) already succeeded, + * so this should not fail on zone boundaries. */ WARN_ON_ONCE(ret == -1); if (ret > 0) { @@ -3406,7 +3424,7 @@ try_this_zone: * if the pageblock should be reserved for the future */ if (unlikely(alloc_flags & ALLOC_HIGHATOMIC)) - reserve_highatomic_pageblock(page, zone); + reserve_highatomic_pageblock(page, order, zone); return page; } else { -- cgit v1.2.3 From 0105eaabb27f31d9b8d340aca6fb6a3420cab30f Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 28 May 2024 09:08:38 -0700 Subject: vmalloc: check CONFIG_EXECMEM in is_vmalloc_or_module_addr() After commit 2c9e5d4a0082 ("bpf: remove CONFIG_BPF_JIT dependency on CONFIG_MODULES of") CONFIG_BPF_JIT does not depend on CONFIG_MODULES any more and bpf jit also uses the [MODULES_VADDR, MODULES_END] memory region. But is_vmalloc_or_module_addr() still checks CONFIG_MODULES, which then returns false for a bpf jit memory region when CONFIG_MODULES is not defined. It leads to the following kernel BUG: [ 1.567023] ------------[ cut here ]------------ [ 1.567883] kernel BUG at mm/vmalloc.c:745! [ 1.568477] Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI [ 1.569367] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.9.0+ #448 [ 1.570247] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 [ 1.570786] RIP: 0010:vmalloc_to_page+0x48/0x1ec [ 1.570786] Code: 0f 00 00 e8 eb 1a 05 00 b8 37 00 00 00 48 ba fe ff ff ff ff 1f 00 00 4c 03 25 76 49 c6 02 48 c1 e0 28 48 01 e8 48 39 d0 76 02 <0f> 0b 4c 89 e7 e8 bf 1a 05 00 49 8b 04 24 48 a9 9f ff ff ff 0f 84 [ 1.570786] RSP: 0018:ffff888007787960 EFLAGS: 00010212 [ 1.570786] RAX: 000036ffa0000000 RBX: 0000000000000640 RCX: ffffffff8147e93c [ 1.570786] RDX: 00001ffffffffffe RSI: dffffc0000000000 RDI: ffffffff840e32c8 [ 1.570786] RBP: ffffffffa0000000 R08: 0000000000000000 R09: 0000000000000000 [ 1.570786] R10: ffff888007787a88 R11: ffffffff8475d8e7 R12: ffffffff83e80ff8 [ 1.570786] R13: 0000000000000640 R14: 0000000000000640 R15: 0000000000000640 [ 1.570786] FS: 0000000000000000(0000) GS:ffff88806cc00000(0000) knlGS:0000000000000000 [ 1.570786] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1.570786] CR2: ffff888006a01000 CR3: 0000000003e80000 CR4: 0000000000350ef0 [ 1.570786] Call Trace: [ 1.570786] [ 1.570786] ? __die_body+0x1b/0x58 [ 1.570786] ? die+0x31/0x4b [ 1.570786] ? do_trap+0x9d/0x138 [ 1.570786] ? vmalloc_to_page+0x48/0x1ec [ 1.570786] ? do_error_trap+0xcd/0x102 [ 1.570786] ? vmalloc_to_page+0x48/0x1ec [ 1.570786] ? vmalloc_to_page+0x48/0x1ec [ 1.570786] ? handle_invalid_op+0x2f/0x38 [ 1.570786] ? vmalloc_to_page+0x48/0x1ec [ 1.570786] ? exc_invalid_op+0x2b/0x41 [ 1.570786] ? asm_exc_invalid_op+0x16/0x20 [ 1.570786] ? vmalloc_to_page+0x26/0x1ec [ 1.570786] ? vmalloc_to_page+0x48/0x1ec [ 1.570786] __text_poke+0xb6/0x458 [ 1.570786] ? __pfx_text_poke_memcpy+0x10/0x10 [ 1.570786] ? __pfx___mutex_lock+0x10/0x10 [ 1.570786] ? __pfx___text_poke+0x10/0x10 [ 1.570786] ? __pfx_get_random_u32+0x10/0x10 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] text_poke_copy_locked+0x70/0x84 [ 1.570786] text_poke_copy+0x32/0x4f [ 1.570786] bpf_arch_text_copy+0xf/0x27 [ 1.570786] bpf_jit_binary_pack_finalize+0x26/0x5a [ 1.570786] bpf_int_jit_compile+0x576/0x8ad [ 1.570786] ? __pfx_bpf_int_jit_compile+0x10/0x10 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] ? __kmalloc_node_track_caller+0x2b5/0x2e0 [ 1.570786] bpf_prog_select_runtime+0x7c/0x199 [ 1.570786] bpf_prepare_filter+0x1e9/0x25b [ 1.570786] ? __pfx_bpf_prepare_filter+0x10/0x10 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] ? _find_next_bit+0x29/0x7e [ 1.570786] bpf_prog_create+0xb8/0xe0 [ 1.570786] ptp_classifier_init+0x75/0xa1 [ 1.570786] ? __pfx_ptp_classifier_init+0x10/0x10 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] ? register_pernet_subsys+0x36/0x42 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] sock_init+0x99/0xa3 [ 1.570786] ? __pfx_sock_init+0x10/0x10 [ 1.570786] do_one_initcall+0x104/0x2c4 [ 1.570786] ? __pfx_do_one_initcall+0x10/0x10 [ 1.570786] ? parameq+0x25/0x2d [ 1.570786] ? rcu_is_watching+0x1c/0x3c [ 1.570786] ? trace_kmalloc+0x81/0xb2 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] ? __kmalloc+0x29c/0x2c7 [ 1.570786] ? srso_return_thunk+0x5/0x5f [ 1.570786] do_initcalls+0xf9/0x123 [ 1.570786] kernel_init_freeable+0x24f/0x289 [ 1.570786] ? __pfx_kernel_init+0x10/0x10 [ 1.570786] kernel_init+0x19/0x13a [ 1.570786] ret_from_fork+0x24/0x41 [ 1.570786] ? __pfx_kernel_init+0x10/0x10 [ 1.570786] ret_from_fork_asm+0x1a/0x30 [ 1.570786] [ 1.570819] ---[ end trace 0000000000000000 ]--- [ 1.571463] RIP: 0010:vmalloc_to_page+0x48/0x1ec [ 1.572111] Code: 0f 00 00 e8 eb 1a 05 00 b8 37 00 00 00 48 ba fe ff ff ff ff 1f 00 00 4c 03 25 76 49 c6 02 48 c1 e0 28 48 01 e8 48 39 d0 76 02 <0f> 0b 4c 89 e7 e8 bf 1a 05 00 49 8b 04 24 48 a9 9f ff ff ff 0f 84 [ 1.574632] RSP: 0018:ffff888007787960 EFLAGS: 00010212 [ 1.575129] RAX: 000036ffa0000000 RBX: 0000000000000640 RCX: ffffffff8147e93c [ 1.576097] RDX: 00001ffffffffffe RSI: dffffc0000000000 RDI: ffffffff840e32c8 [ 1.577084] RBP: ffffffffa0000000 R08: 0000000000000000 R09: 0000000000000000 [ 1.578077] R10: ffff888007787a88 R11: ffffffff8475d8e7 R12: ffffffff83e80ff8 [ 1.578810] R13: 0000000000000640 R14: 0000000000000640 R15: 0000000000000640 [ 1.579823] FS: 0000000000000000(0000) GS:ffff88806cc00000(0000) knlGS:0000000000000000 [ 1.580992] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1.581869] CR2: ffff888006a01000 CR3: 0000000003e80000 CR4: 0000000000350ef0 [ 1.582800] Kernel panic - not syncing: Fatal exception [ 1.583765] ---[ end Kernel panic - not syncing: Fatal exception ]--- Fix this by checking CONFIG_EXECMEM instead. Link: https://lkml.kernel.org/r/20240528160838.102223-1-xiyou.wangcong@gmail.com Fixes: 2c9e5d4a0082 ("bpf: remove CONFIG_BPF_JIT dependency on CONFIG_MODULES of") Signed-off-by: Cong Wang Acked-by: Mike Rapoport (IBM) Cc: Luis Chamberlain Cc: Daniel Borkmann Signed-off-by: Andrew Morton --- mm/vmalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5d3aa2dc88a8..45e1506d58c3 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -722,7 +722,7 @@ int is_vmalloc_or_module_addr(const void *x) * and fall back on vmalloc() if that fails. Others * just put it in the vmalloc space. */ -#if defined(CONFIG_MODULES) && defined(MODULES_VADDR) +#if defined(CONFIG_EXECMEM) && defined(MODULES_VADDR) unsigned long addr = (unsigned long)kasan_reset_tag(x); if (addr >= MODULES_VADDR && addr < MODULES_END) return 1; -- cgit v1.2.3 From 2ef3cec44c60ae171b287db7fc2aa341586d65ba Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Tue, 28 May 2024 12:48:06 +0200 Subject: kmsan: do not wipe out origin when doing partial unpoisoning As noticed by Brian, KMSAN should not be zeroing the origin when unpoisoning parts of a four-byte uninitialized value, e.g.: char a[4]; kmsan_unpoison_memory(a, 1); This led to false negatives, as certain poisoned values could receive zero origins, preventing those values from being reported. To fix the problem, check that kmsan_internal_set_shadow_origin() writes zero origins only to slots which have zero shadow. Link: https://lkml.kernel.org/r/20240528104807.738758-1-glider@google.com Fixes: f80be4571b19 ("kmsan: add KMSAN runtime core") Signed-off-by: Alexander Potapenko Reported-by: Brian Johannesmeyer Link: https://lore.kernel.org/lkml/20240524232804.1984355-1-bjohannesmeyer@gmail.com/T/ Reviewed-by: Marco Elver Tested-by: Brian Johannesmeyer Cc: Dmitry Vyukov Cc: Kees Cook Cc: Signed-off-by: Andrew Morton --- mm/kmsan/core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mm/kmsan/core.c b/mm/kmsan/core.c index cf2d70e9c9a5..95f859e38c53 100644 --- a/mm/kmsan/core.c +++ b/mm/kmsan/core.c @@ -196,8 +196,7 @@ void kmsan_internal_set_shadow_origin(void *addr, size_t size, int b, u32 origin, bool checked) { u64 address = (u64)addr; - void *shadow_start; - u32 *origin_start; + u32 *shadow_start, *origin_start; size_t pad = 0; KMSAN_WARN_ON(!kmsan_metadata_is_contiguous(addr, size)); @@ -225,8 +224,16 @@ void kmsan_internal_set_shadow_origin(void *addr, size_t size, int b, origin_start = (u32 *)kmsan_get_metadata((void *)address, KMSAN_META_ORIGIN); - for (int i = 0; i < size / KMSAN_ORIGIN_SIZE; i++) - origin_start[i] = origin; + /* + * If the new origin is non-zero, assume that the shadow byte is also non-zero, + * and unconditionally overwrite the old origin slot. + * If the new origin is zero, overwrite the old origin slot iff the + * corresponding shadow slot is zero. + */ + for (int i = 0; i < size / KMSAN_ORIGIN_SIZE; i++) { + if (origin || !shadow_start[i]) + origin_start[i] = origin; + } } struct page *kmsan_vmalloc_to_page_or_null(void *vaddr) -- cgit v1.2.3 From 730cdc2c72c6905a2eda2fccbbf67dcef1206590 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Tue, 28 May 2024 13:15:21 +0800 Subject: mm/ksm: fix ksm_pages_scanned accounting Patch series "mm/ksm: fix some accounting problems", v3. We encountered some abnormal ksm_pages_scanned and ksm_zero_pages during some random tests. 1. ksm_pages_scanned unchanged even ksmd scanning has progress. 2. ksm_zero_pages maybe -1 in some rare cases. This patch (of 2): During testing, I found ksm_pages_scanned is unchanged although the scan_get_next_rmap_item() did return valid rmap_item that is not NULL. The reason is the scan_get_next_rmap_item() will return NULL after a full scan, so ksm_do_scan() just return without accounting of the ksm_pages_scanned. Fix it by just putting ksm_pages_scanned accounting in that loop, and it will be accounted more timely if that loop would last for a long time. Link: https://lkml.kernel.org/r/20240528-b4-ksm-counters-v3-0-34bb358fdc13@linux.dev Link: https://lkml.kernel.org/r/20240528-b4-ksm-counters-v3-1-34bb358fdc13@linux.dev Fixes: b348b5fe2b5f ("mm/ksm: add pages scanned metric") Signed-off-by: Chengming Zhou Acked-by: David Hildenbrand Reviewed-by: xu xin Cc: Andrea Arcangeli Cc: Hugh Dickins Cc: Ran Xiaokai Cc: Stefan Roesch Cc: Yang Yang Cc: Signed-off-by: Andrew Morton --- mm/ksm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mm/ksm.c b/mm/ksm.c index 452ac8346e6e..9e99cb12d330 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2754,18 +2754,16 @@ static void ksm_do_scan(unsigned int scan_npages) { struct ksm_rmap_item *rmap_item; struct page *page; - unsigned int npages = scan_npages; - while (npages-- && likely(!freezing(current))) { + while (scan_npages-- && likely(!freezing(current))) { cond_resched(); rmap_item = scan_get_next_rmap_item(&page); if (!rmap_item) return; cmp_and_merge_page(page, rmap_item); put_page(page); + ksm_pages_scanned++; } - - ksm_pages_scanned += scan_npages - npages; } static int ksmd_should_run(void) -- cgit v1.2.3 From c2dc78b86e0821ecf9a9d0c35dba2618279a5bb6 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Tue, 28 May 2024 13:15:22 +0800 Subject: mm/ksm: fix ksm_zero_pages accounting We normally ksm_zero_pages++ in ksmd when page is merged with zero page, but ksm_zero_pages-- is done from page tables side, where there is no any accessing protection of ksm_zero_pages. So we can read very exceptional value of ksm_zero_pages in rare cases, such as -1, which is very confusing to users. Fix it by changing to use atomic_long_t, and the same case with the mm->ksm_zero_pages. Link: https://lkml.kernel.org/r/20240528-b4-ksm-counters-v3-2-34bb358fdc13@linux.dev Fixes: e2942062e01d ("ksm: count all zero pages placed by KSM") Fixes: 6080d19f0704 ("ksm: add ksm zero pages for each process") Signed-off-by: Chengming Zhou Acked-by: David Hildenbrand Cc: Andrea Arcangeli Cc: Hugh Dickins Cc: Ran Xiaokai Cc: Stefan Roesch Cc: xu xin Cc: Yang Yang Cc: Signed-off-by: Andrew Morton --- fs/proc/base.c | 2 +- include/linux/ksm.h | 17 ++++++++++++++--- include/linux/mm_types.h | 2 +- mm/ksm.c | 11 +++++------ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 18550c071d71..72a1acd03675 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3214,7 +3214,7 @@ static int proc_pid_ksm_stat(struct seq_file *m, struct pid_namespace *ns, mm = get_task_mm(task); if (mm) { seq_printf(m, "ksm_rmap_items %lu\n", mm->ksm_rmap_items); - seq_printf(m, "ksm_zero_pages %lu\n", mm->ksm_zero_pages); + seq_printf(m, "ksm_zero_pages %ld\n", mm_ksm_zero_pages(mm)); seq_printf(m, "ksm_merging_pages %lu\n", mm->ksm_merging_pages); seq_printf(m, "ksm_process_profit %ld\n", ksm_process_profit(mm)); mmput(mm); diff --git a/include/linux/ksm.h b/include/linux/ksm.h index 52c63a9c5a9c..11690dacd986 100644 --- a/include/linux/ksm.h +++ b/include/linux/ksm.h @@ -33,16 +33,27 @@ void __ksm_exit(struct mm_struct *mm); */ #define is_ksm_zero_pte(pte) (is_zero_pfn(pte_pfn(pte)) && pte_dirty(pte)) -extern unsigned long ksm_zero_pages; +extern atomic_long_t ksm_zero_pages; + +static inline void ksm_map_zero_page(struct mm_struct *mm) +{ + atomic_long_inc(&ksm_zero_pages); + atomic_long_inc(&mm->ksm_zero_pages); +} static inline void ksm_might_unmap_zero_page(struct mm_struct *mm, pte_t pte) { if (is_ksm_zero_pte(pte)) { - ksm_zero_pages--; - mm->ksm_zero_pages--; + atomic_long_dec(&ksm_zero_pages); + atomic_long_dec(&mm->ksm_zero_pages); } } +static inline long mm_ksm_zero_pages(struct mm_struct *mm) +{ + return atomic_long_read(&mm->ksm_zero_pages); +} + static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { if (test_bit(MMF_VM_MERGEABLE, &oldmm->flags)) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 24323c7d0bd4..af3a0256fa93 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -985,7 +985,7 @@ struct mm_struct { * Represent how many empty pages are merged with kernel zero * pages when enabling KSM use_zero_pages. */ - unsigned long ksm_zero_pages; + atomic_long_t ksm_zero_pages; #endif /* CONFIG_KSM */ #ifdef CONFIG_LRU_GEN_WALKS_MMU struct { diff --git a/mm/ksm.c b/mm/ksm.c index 9e99cb12d330..34c4820e0d3d 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -296,7 +296,7 @@ static bool ksm_use_zero_pages __read_mostly; static bool ksm_smart_scan = true; /* The number of zero pages which is placed by KSM */ -unsigned long ksm_zero_pages; +atomic_long_t ksm_zero_pages = ATOMIC_LONG_INIT(0); /* The number of pages that have been skipped due to "smart scanning" */ static unsigned long ksm_pages_skipped; @@ -1429,8 +1429,7 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, * the dirty bit in zero page's PTE is set. */ newpte = pte_mkdirty(pte_mkspecial(pfn_pte(page_to_pfn(kpage), vma->vm_page_prot))); - ksm_zero_pages++; - mm->ksm_zero_pages++; + ksm_map_zero_page(mm); /* * We're replacing an anonymous page with a zero page, which is * not anonymous. We need to do proper accounting otherwise we @@ -3374,7 +3373,7 @@ static void wait_while_offlining(void) #ifdef CONFIG_PROC_FS long ksm_process_profit(struct mm_struct *mm) { - return (long)(mm->ksm_merging_pages + mm->ksm_zero_pages) * PAGE_SIZE - + return (long)(mm->ksm_merging_pages + mm_ksm_zero_pages(mm)) * PAGE_SIZE - mm->ksm_rmap_items * sizeof(struct ksm_rmap_item); } #endif /* CONFIG_PROC_FS */ @@ -3663,7 +3662,7 @@ KSM_ATTR_RO(pages_skipped); static ssize_t ksm_zero_pages_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sysfs_emit(buf, "%ld\n", ksm_zero_pages); + return sysfs_emit(buf, "%ld\n", atomic_long_read(&ksm_zero_pages)); } KSM_ATTR_RO(ksm_zero_pages); @@ -3672,7 +3671,7 @@ static ssize_t general_profit_show(struct kobject *kobj, { long general_profit; - general_profit = (ksm_pages_sharing + ksm_zero_pages) * PAGE_SIZE - + general_profit = (ksm_pages_sharing + atomic_long_read(&ksm_zero_pages)) * PAGE_SIZE - ksm_rmap_items * sizeof(struct ksm_rmap_item); return sysfs_emit(buf, "%ld\n", general_profit); -- cgit v1.2.3 From 8daf9c702ee7f825f0de8600abff764acfedea13 Mon Sep 17 00:00:00 2001 From: Oscar Salvador Date: Tue, 28 May 2024 22:53:23 +0200 Subject: mm/hugetlb: do not call vma_add_reservation upon ENOMEM sysbot reported a splat [1] on __unmap_hugepage_range(). This is because vma_needs_reservation() can return -ENOMEM if allocate_file_region_entries() fails to allocate the file_region struct for the reservation. Check for that and do not call vma_add_reservation() if that is the case, otherwise region_abort() and region_del() will see that we do not have any file_regions. If we detect that vma_needs_reservation() returned -ENOMEM, we clear the hugetlb_restore_reserve flag as if this reservation was still consumed, so free_huge_folio() will not increment the resv count. [1] https://lore.kernel.org/linux-mm/0000000000004096100617c58d54@google.com/T/#ma5983bc1ab18a54910da83416b3f89f3c7ee43aa Link: https://lkml.kernel.org/r/20240528205323.20439-1-osalvador@suse.de Fixes: df7a6d1f6405 ("mm/hugetlb: restore the reservation if needed") Signed-off-by: Oscar Salvador Reported-and-tested-by: syzbot+d3fe2dc5ffe9380b714b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-mm/0000000000004096100617c58d54@google.com/ Cc: Breno Leitao Cc: Muchun Song Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 6be78e7d4f6e..f35abff8be60 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5768,8 +5768,20 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, * do_exit() will not see it, and will keep the reservation * forever. */ - if (adjust_reservation && vma_needs_reservation(h, vma, address)) - vma_add_reservation(h, vma, address); + if (adjust_reservation) { + int rc = vma_needs_reservation(h, vma, address); + + if (rc < 0) + /* Pressumably allocate_file_region_entries failed + * to allocate a file_region struct. Clear + * hugetlb_restore_reserve so that global reserve + * count will not be incremented by free_huge_folio. + * Act as if we consumed the reservation. + */ + folio_clear_hugetlb_restore_reserve(page_folio(page)); + else if (rc) + vma_add_reservation(h, vma, address); + } tlb_remove_page_size(tlb, page, huge_page_size(h)); /* -- cgit v1.2.3 From 3f0c44c8c21cfa3bb6b756b939491b7a60932cd1 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 27 May 2024 15:30:06 -0300 Subject: codetag: avoid race at alloc_slab_obj_exts When CONFIG_MEM_ALLOC_PROFILING_DEBUG is enabled, the following warning may be noticed: [ 48.299584] ------------[ cut here ]------------ [ 48.300092] alloc_tag was not set [ 48.300528] WARNING: CPU: 2 PID: 1361 at include/linux/alloc_tag.h:130 alloc_tagging_slab_free_hook+0x84/0xc7 [ 48.301305] Modules linked in: [ 48.301553] CPU: 2 PID: 1361 Comm: systemd-udevd Not tainted 6.10.0-rc1-00003-gac8755535862 #176 [ 48.302196] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 [ 48.302752] RIP: 0010:alloc_tagging_slab_free_hook+0x84/0xc7 [ 48.303169] Code: 8d 1c c4 48 85 db 74 4d 48 83 3b 00 75 1e 80 3d 65 02 86 04 00 75 15 48 c7 c7 11 48 1d 85 c6 05 55 02 86 04 01 e8 64 44 a5 ff <0f> 0b 48 8b 03 48 85 c0 74 21 48 83 f8 01 74 14 48 8b 50 20 48 f7 [ 48.304411] RSP: 0018:ffff8880111b7d40 EFLAGS: 00010282 [ 48.304916] RAX: 0000000000000000 RBX: ffff88800fcc9008 RCX: 0000000000000000 [ 48.305455] RDX: 0000000080000000 RSI: ffff888014060000 RDI: ffffed1002236f97 [ 48.305979] RBP: 0000000000001100 R08: fffffbfff0aa73a1 R09: 0000000000000000 [ 48.306473] R10: ffffffff814515e5 R11: 0000000000000003 R12: ffff88800fcc9000 [ 48.306943] R13: ffff88800b2e5cc0 R14: ffff8880111b7d90 R15: 0000000000000000 [ 48.307529] FS: 00007faf5d1908c0(0000) GS:ffff88806cf00000(0000) knlGS:0000000000000000 [ 48.308223] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 48.308710] CR2: 000058fb220c9118 CR3: 00000000110cc000 CR4: 0000000000750ef0 [ 48.309274] PKRU: 55555554 [ 48.309804] Call Trace: [ 48.310029] [ 48.310290] ? show_regs+0x84/0x8d [ 48.310722] ? alloc_tagging_slab_free_hook+0x84/0xc7 [ 48.311298] ? __warn+0x13b/0x2ff [ 48.311580] ? alloc_tagging_slab_free_hook+0x84/0xc7 [ 48.311987] ? report_bug+0x2ce/0x3ab [ 48.312292] ? handle_bug+0x8c/0x107 [ 48.312563] ? exc_invalid_op+0x34/0x6f [ 48.312842] ? asm_exc_invalid_op+0x1a/0x20 [ 48.313173] ? this_cpu_in_panic+0x1c/0x72 [ 48.313503] ? alloc_tagging_slab_free_hook+0x84/0xc7 [ 48.313880] ? putname+0x143/0x14e [ 48.314152] kmem_cache_free+0xe9/0x214 [ 48.314454] putname+0x143/0x14e [ 48.314712] do_unlinkat+0x413/0x45e [ 48.315001] ? __pfx_do_unlinkat+0x10/0x10 [ 48.315388] ? __check_object_size+0x4d7/0x525 [ 48.315744] ? __sanitizer_cov_trace_pc+0x20/0x4a [ 48.316167] ? __sanitizer_cov_trace_pc+0x20/0x4a [ 48.316757] ? getname_flags+0x4ed/0x500 [ 48.317261] __x64_sys_unlink+0x42/0x4a [ 48.317741] do_syscall_64+0xe2/0x149 [ 48.318171] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 48.318602] RIP: 0033:0x7faf5d8850ab [ 48.318891] Code: fd ff ff e8 27 dd 01 00 0f 1f 80 00 00 00 00 f3 0f 1e fa b8 5f 00 00 00 0f 05 c3 0f 1f 40 00 f3 0f 1e fa b8 57 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 05 c3 0f 1f 40 00 48 8b 15 41 2d 0e 00 f7 d8 [ 48.320649] RSP: 002b:00007ffc44982b38 EFLAGS: 00000246 ORIG_RAX: 0000000000000057 [ 48.321182] RAX: ffffffffffffffda RBX: 00005ba344a44680 RCX: 00007faf5d8850ab [ 48.321667] RDX: 0000000000000000 RSI: 00005ba344a44430 RDI: 00007ffc44982b40 [ 48.322139] RBP: 00007ffc44982c00 R08: 0000000000000000 R09: 0000000000000007 [ 48.322598] R10: 00005ba344a44430 R11: 0000000000000246 R12: 0000000000000000 [ 48.323071] R13: 00007ffc44982b40 R14: 0000000000000000 R15: 0000000000000000 [ 48.323596] This is due to a race when two objects are allocated from the same slab, which did not have an obj_exts allocated for. In such a case, the two threads will notice the NULL obj_exts and after one assigns slab->obj_exts, the second one will happily do the exchange if it reads this new assigned value. In order to avoid that, verify that the read obj_exts does not point to an allocated obj_exts before doing the exchange. Link: https://lkml.kernel.org/r/20240527183007.1595037-1-cascardo@igalia.com Fixes: 09c46563ff6d ("codetag: debug: introduce OBJEXTS_ALLOC_FAIL to mark failed slab_ext allocations") Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Vlastimil Babka Cc: Suren Baghdasaryan Cc: Christoph Lameter Cc: David Rientjes Cc: Gustavo A. R. Silva Cc: Hyeonggon Yoo <42.hyeyoo@gmail.com> Cc: Joonsoo Kim Cc: Kees Cook Cc: Pekka Enberg Cc: Roman Gushchin Cc: Thadeu Lima de Souza Cascardo Cc: Kent Overstreet Signed-off-by: Andrew Morton --- mm/slub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 0809760cf789..1373ac365a46 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1952,7 +1952,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, #ifdef CONFIG_MEMCG new_exts |= MEMCG_DATA_OBJEXTS; #endif - old_exts = slab->obj_exts; + old_exts = READ_ONCE(slab->obj_exts); handle_failed_objexts_alloc(old_exts, vec, objects); if (new_slab) { /* @@ -1961,7 +1961,8 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, * be simply assigned. */ slab->obj_exts = new_exts; - } else if (cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { + } else if ((old_exts & ~OBJEXTS_FLAGS_MASK) || + cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { /* * If the slab is already in use, somebody can allocate and * assign slabobj_exts in parallel. In this case the existing -- cgit v1.2.3 From 9415983599413f847ec9f081e9f9e5ed6cdeb342 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 31 May 2024 13:53:50 -0700 Subject: mm: fix xyz_noprof functions calling profiled functions Grepping /proc/allocinfo for "noprof" reveals several xyz_noprof functions, which means internally they are calling profiled functions. This should never happen as such calls move allocation charge from a higher level location where it should be accounted for into these lower level helpers. Fix this by replacing profiled function calls with noprof ones. Link: https://lkml.kernel.org/r/20240531205350.3973009-1-surenb@google.com Fixes: b951aaff5035 ("mm: enable page allocation tagging") Fixes: e26d8769da6d ("mempool: hook up to memory allocation profiling") Fixes: 88ae5fb755b0 ("mm: vmalloc: enable memory allocation profiling") Signed-off-by: Suren Baghdasaryan Cc: Kent Overstreet Reviewed-by: Kees Cook Acked-by: Vlastimil Babka Cc: Pasha Tatashin Signed-off-by: Andrew Morton --- mm/filemap.c | 2 +- mm/mempool.c | 2 +- mm/util.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 382c3d06bfb1..876cc64aadd7 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1000,7 +1000,7 @@ struct folio *filemap_alloc_folio_noprof(gfp_t gfp, unsigned int order) do { cpuset_mems_cookie = read_mems_allowed_begin(); n = cpuset_mem_spread_node(); - folio = __folio_alloc_node(gfp, order, n); + folio = __folio_alloc_node_noprof(gfp, order, n); } while (!folio && read_mems_allowed_retry(cpuset_mems_cookie)); return folio; diff --git a/mm/mempool.c b/mm/mempool.c index 6ece63a00acf..3223337135d0 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -273,7 +273,7 @@ mempool_t *mempool_create_node_noprof(int min_nr, mempool_alloc_t *alloc_fn, { mempool_t *pool; - pool = kzalloc_node(sizeof(*pool), gfp_mask, node_id); + pool = kmalloc_node_noprof(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id); if (!pool) return NULL; diff --git a/mm/util.c b/mm/util.c index c9e519e6811f..6c3e6710e4de 100644 --- a/mm/util.c +++ b/mm/util.c @@ -705,7 +705,7 @@ void *kvrealloc_noprof(const void *p, size_t oldsize, size_t newsize, gfp_t flag if (oldsize >= newsize) return (void *)p; - newp = kvmalloc(newsize, flags); + newp = kvmalloc_noprof(newsize, flags); if (!newp) return NULL; memcpy(newp, p, oldsize); @@ -726,7 +726,7 @@ void *__vmalloc_array_noprof(size_t n, size_t size, gfp_t flags) if (unlikely(check_mul_overflow(n, size, &bytes))) return NULL; - return __vmalloc(bytes, flags); + return __vmalloc_noprof(bytes, flags); } EXPORT_SYMBOL(__vmalloc_array_noprof); @@ -737,7 +737,7 @@ EXPORT_SYMBOL(__vmalloc_array_noprof); */ void *vmalloc_array_noprof(size_t n, size_t size) { - return __vmalloc_array(n, size, GFP_KERNEL); + return __vmalloc_array_noprof(n, size, GFP_KERNEL); } EXPORT_SYMBOL(vmalloc_array_noprof); @@ -749,7 +749,7 @@ EXPORT_SYMBOL(vmalloc_array_noprof); */ void *__vcalloc_noprof(size_t n, size_t size, gfp_t flags) { - return __vmalloc_array(n, size, flags | __GFP_ZERO); + return __vmalloc_array_noprof(n, size, flags | __GFP_ZERO); } EXPORT_SYMBOL(__vcalloc_noprof); @@ -760,7 +760,7 @@ EXPORT_SYMBOL(__vcalloc_noprof); */ void *vcalloc_noprof(size_t n, size_t size) { - return __vmalloc_array(n, size, GFP_KERNEL | __GFP_ZERO); + return __vmalloc_array_noprof(n, size, GFP_KERNEL | __GFP_ZERO); } EXPORT_SYMBOL(vcalloc_noprof); -- cgit v1.2.3 From 7373a51e7998b508af7136530f3a997b286ce81c Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 4 Jun 2024 22:42:55 +0900 Subject: nilfs2: fix nilfs_empty_dir() misjudgment and long loop on I/O errors The error handling in nilfs_empty_dir() when a directory folio/page read fails is incorrect, as in the old ext2 implementation, and if the folio/page cannot be read or nilfs_check_folio() fails, it will falsely determine the directory as empty and corrupt the file system. In addition, since nilfs_empty_dir() does not immediately return on a failed folio/page read, but continues to loop, this can cause a long loop with I/O if i_size of the directory's inode is also corrupted, causing the log writer thread to wait and hang, as reported by syzbot. Fix these issues by making nilfs_empty_dir() immediately return a false value (0) if it fails to get a directory folio/page. Link: https://lkml.kernel.org/r/20240604134255.7165-1-konishi.ryusuke@gmail.com Signed-off-by: Ryusuke Konishi Reported-by: syzbot+c8166c541d3971bf6c87@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c8166c541d3971bf6c87 Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations") Tested-by: Ryusuke Konishi Cc: Signed-off-by: Andrew Morton --- fs/nilfs2/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index a002a44ff161..52e50b1b7f22 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -607,7 +607,7 @@ int nilfs_empty_dir(struct inode *inode) kaddr = nilfs_get_folio(inode, i, &folio); if (IS_ERR(kaddr)) - continue; + return 0; de = (struct nilfs_dir_entry *)kaddr; kaddr += nilfs_last_byte(inode, i) - NILFS_DIR_REC_LEN(1); -- cgit v1.2.3 From b91e05f1fcf755c9d2c4ca10907383e39def05bd Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 18 May 2024 00:28:58 +0100 Subject: drm/vmwgfx: remove unused struct 'vmw_stdu_dma' 'vmw_stdu_dma' is unused since commit 39985eea5a6d ("drm/vmwgfx: Abstract placement selection") Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240517232858.230860-1-linux@treblig.org --- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index afc0bc4ef4e1..a04e0736318d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -92,11 +92,6 @@ struct vmw_stdu_update { SVGA3dCmdUpdateGBScreenTarget body; }; -struct vmw_stdu_dma { - SVGA3dCmdHeader header; - SVGA3dCmdSurfaceDMA body; -}; - struct vmw_stdu_surface_copy { SVGA3dCmdHeader header; SVGA3dCmdSurfaceCopy body; -- cgit v1.2.3 From 03e4a092be8ce3de7c1baa7ae14e68b64e3ea644 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 3 Jun 2024 14:42:30 -0700 Subject: ice: fix iteration of TLVs in Preserved Fields Area The ice_get_pfa_module_tlv() function iterates over the Type-Length-Value structures in the Preserved Fields Area (PFA) of the NVM. This is used by the driver to access data such as the Part Board Assembly identifier. The function uses simple logic to iterate over the PFA. First, the pointer to the PFA in the NVM is read. Then the total length of the PFA is read from the first word. A pointer to the first TLV is initialized, and a simple loop iterates over each TLV. The pointer is moved forward through the NVM until it exceeds the PFA area. The logic seems sound, but it is missing a key detail. The Preserved Fields Area length includes one additional final word. This is documented in the device data sheet as a dummy word which contains 0xFFFF. All NVMs have this extra word. If the driver tries to scan for a TLV that is not in the PFA, it will read past the size of the PFA. It reads and interprets the last dummy word of the PFA as a TLV with type 0xFFFF. It then reads the word following the PFA as a length. The PFA resides within the Shadow RAM portion of the NVM, which is relatively small. All of its offsets are within a 16-bit size. The PFA pointer and TLV pointer are stored by the driver as 16-bit values. In almost all cases, the word following the PFA will be such that interpreting it as a length will result in 16-bit arithmetic overflow. Once overflowed, the new next_tlv value is now below the maximum offset of the PFA. Thus, the driver will continue to iterate the data as TLVs. In the worst case, the driver hits on a sequence of reads which loop back to reading the same offsets in an endless loop. To fix this, we need to correct the loop iteration check to account for this extra word at the end of the PFA. This alone is sufficient to resolve the known cases of this issue in the field. However, it is plausible that an NVM could be misconfigured or have corrupt data which results in the same kind of overflow. Protect against this by using check_add_overflow when calculating both the maximum offset of the TLVs, and when calculating the next_tlv offset at the end of each loop iteration. This ensures that the driver will not get stuck in an infinite loop when scanning the PFA. Fixes: e961b679fb0b ("ice: add board identifier info to devlink .info_get") Co-developed-by: Paul Greenwalt Signed-off-by: Paul Greenwalt Reviewed-by: Przemek Kitszel Tested-by: Pucha Himasekhar Reddy Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-1-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_nvm.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c index 84eab92dc03c..ea7cbdf8492d 100644 --- a/drivers/net/ethernet/intel/ice/ice_nvm.c +++ b/drivers/net/ethernet/intel/ice/ice_nvm.c @@ -440,8 +440,7 @@ int ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, u16 module_type) { - u16 pfa_len, pfa_ptr; - u16 next_tlv; + u16 pfa_len, pfa_ptr, next_tlv, max_tlv; int status; status = ice_read_sr_word(hw, ICE_SR_PFA_PTR, &pfa_ptr); @@ -454,11 +453,23 @@ ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, ice_debug(hw, ICE_DBG_INIT, "Failed to read PFA length.\n"); return status; } + + /* The Preserved Fields Area contains a sequence of Type-Length-Value + * structures which define its contents. The PFA length includes all + * of the TLVs, plus the initial length word itself, *and* one final + * word at the end after all of the TLVs. + */ + if (check_add_overflow(pfa_ptr, pfa_len - 1, &max_tlv)) { + dev_warn(ice_hw_to_dev(hw), "PFA starts at offset %u. PFA length of %u caused 16-bit arithmetic overflow.\n", + pfa_ptr, pfa_len); + return -EINVAL; + } + /* Starting with first TLV after PFA length, iterate through the list * of TLVs to find the requested one. */ next_tlv = pfa_ptr + 1; - while (next_tlv < pfa_ptr + pfa_len) { + while (next_tlv < max_tlv) { u16 tlv_sub_module_type; u16 tlv_len; @@ -482,10 +493,13 @@ ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, } return -EINVAL; } - /* Check next TLV, i.e. current TLV pointer + length + 2 words - * (for current TLV's type and length) - */ - next_tlv = next_tlv + tlv_len + 2; + + if (check_add_overflow(next_tlv, 2, &next_tlv) || + check_add_overflow(next_tlv, tlv_len, &next_tlv)) { + dev_warn(ice_hw_to_dev(hw), "TLV of type %u and length 0x%04x caused 16-bit arithmetic overflow. The PFA starts at 0x%04x and has length of 0x%04x\n", + tlv_sub_module_type, tlv_len, pfa_ptr, pfa_len); + return -EINVAL; + } } /* Module does not exist */ return -ENOENT; -- cgit v1.2.3 From cfa747a66e5da34793ac08c26b814e7709613fab Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 3 Jun 2024 14:42:31 -0700 Subject: ice: fix reads from NVM Shadow RAM on E830 and E825-C devices The ice driver reads data from the Shadow RAM portion of the NVM during initialization, including data used to identify the NVM image and device, such as the ETRACK ID used to populate devlink dev info fw.bundle. Currently it is using a fixed offset defined by ICE_CSS_HEADER_LENGTH to compute the appropriate offset. This worked fine for E810 and E822 devices which both have CSS header length of 330 words. Other devices, including both E825-C and E830 devices have different sizes for their CSS header. The use of a hard coded value results in the driver reading from the wrong block in the NVM when attempting to access the Shadow RAM copy. This results in the driver reporting the fw.bundle as 0x0 in both the devlink dev info and ethtool -i output. The first E830 support was introduced by commit ba20ecb1d1bb ("ice: Hook up 4 E830 devices by adding their IDs") and the first E825-C support was introducted by commit f64e18944233 ("ice: introduce new E825C devices family") The NVM actually contains the CSS header length embedded in it. Remove the hard coded value and replace it with logic to read the length from the NVM directly. This is more resilient against all existing and future hardware, vs looking up the expected values from a table. It ensures the driver will read from the appropriate place when determining the ETRACK ID value used for populating the fw.bundle_id and for reporting in ethtool -i. The CSS header length for both the active and inactive flash bank is stored in the ice_bank_info structure to avoid unnecessary duplicate work when accessing multiple words of the Shadow RAM. Both banks are read in the unlikely event that the header length is different for the NVM in the inactive bank, rather than being different only by the overall device family. Fixes: ba20ecb1d1bb ("ice: Hook up 4 E830 devices by adding their IDs") Co-developed-by: Paul Greenwalt Signed-off-by: Paul Greenwalt Reviewed-by: Przemek Kitszel Tested-by: Pucha Himasekhar Reddy Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-2-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_nvm.c | 88 ++++++++++++++++++++++++++++++- drivers/net/ethernet/intel/ice/ice_type.h | 14 +++-- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c index ea7cbdf8492d..59e8879ac059 100644 --- a/drivers/net/ethernet/intel/ice/ice_nvm.c +++ b/drivers/net/ethernet/intel/ice/ice_nvm.c @@ -374,11 +374,25 @@ ice_read_nvm_module(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u1 * * Read the specified word from the copy of the Shadow RAM found in the * specified NVM module. + * + * Note that the Shadow RAM copy is always located after the CSS header, and + * is aligned to 64-byte (32-word) offsets. */ static int ice_read_nvm_sr_copy(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u16 *data) { - return ice_read_nvm_module(hw, bank, ICE_NVM_SR_COPY_WORD_OFFSET + offset, data); + u32 sr_copy; + + switch (bank) { + case ICE_ACTIVE_FLASH_BANK: + sr_copy = roundup(hw->flash.banks.active_css_hdr_len, 32); + break; + case ICE_INACTIVE_FLASH_BANK: + sr_copy = roundup(hw->flash.banks.inactive_css_hdr_len, 32); + break; + } + + return ice_read_nvm_module(hw, bank, sr_copy + offset, data); } /** @@ -1023,6 +1037,72 @@ static int ice_determine_active_flash_banks(struct ice_hw *hw) return 0; } +/** + * ice_get_nvm_css_hdr_len - Read the CSS header length from the NVM CSS header + * @hw: pointer to the HW struct + * @bank: whether to read from the active or inactive flash bank + * @hdr_len: storage for header length in words + * + * Read the CSS header length from the NVM CSS header and add the Authentication + * header size, and then convert to words. + * + * Return: zero on success, or a negative error code on failure. + */ +static int +ice_get_nvm_css_hdr_len(struct ice_hw *hw, enum ice_bank_select bank, + u32 *hdr_len) +{ + u16 hdr_len_l, hdr_len_h; + u32 hdr_len_dword; + int status; + + status = ice_read_nvm_module(hw, bank, ICE_NVM_CSS_HDR_LEN_L, + &hdr_len_l); + if (status) + return status; + + status = ice_read_nvm_module(hw, bank, ICE_NVM_CSS_HDR_LEN_H, + &hdr_len_h); + if (status) + return status; + + /* CSS header length is in DWORD, so convert to words and add + * authentication header size + */ + hdr_len_dword = hdr_len_h << 16 | hdr_len_l; + *hdr_len = (hdr_len_dword * 2) + ICE_NVM_AUTH_HEADER_LEN; + + return 0; +} + +/** + * ice_determine_css_hdr_len - Discover CSS header length for the device + * @hw: pointer to the HW struct + * + * Determine the size of the CSS header at the start of the NVM module. This + * is useful for locating the Shadow RAM copy in the NVM, as the Shadow RAM is + * always located just after the CSS header. + * + * Return: zero on success, or a negative error code on failure. + */ +static int ice_determine_css_hdr_len(struct ice_hw *hw) +{ + struct ice_bank_info *banks = &hw->flash.banks; + int status; + + status = ice_get_nvm_css_hdr_len(hw, ICE_ACTIVE_FLASH_BANK, + &banks->active_css_hdr_len); + if (status) + return status; + + status = ice_get_nvm_css_hdr_len(hw, ICE_INACTIVE_FLASH_BANK, + &banks->inactive_css_hdr_len); + if (status) + return status; + + return 0; +} + /** * ice_init_nvm - initializes NVM setting * @hw: pointer to the HW struct @@ -1069,6 +1149,12 @@ int ice_init_nvm(struct ice_hw *hw) return status; } + status = ice_determine_css_hdr_len(hw); + if (status) { + ice_debug(hw, ICE_DBG_NVM, "Failed to determine Shadow RAM copy offsets.\n"); + return status; + } + status = ice_get_nvm_ver_info(hw, ICE_ACTIVE_FLASH_BANK, &flash->nvm); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to read NVM info.\n"); diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index f0796a93f428..eef397e5baa0 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -482,6 +482,8 @@ struct ice_bank_info { u32 orom_size; /* Size of OROM bank */ u32 netlist_ptr; /* Pointer to 1st Netlist bank */ u32 netlist_size; /* Size of Netlist bank */ + u32 active_css_hdr_len; /* Active CSS header length */ + u32 inactive_css_hdr_len; /* Inactive CSS header length */ enum ice_flash_bank nvm_bank; /* Active NVM bank */ enum ice_flash_bank orom_bank; /* Active OROM bank */ enum ice_flash_bank netlist_bank; /* Active Netlist bank */ @@ -1087,17 +1089,13 @@ struct ice_aq_get_set_rss_lut_params { #define ICE_SR_SECTOR_SIZE_IN_WORDS 0x800 /* CSS Header words */ +#define ICE_NVM_CSS_HDR_LEN_L 0x02 +#define ICE_NVM_CSS_HDR_LEN_H 0x03 #define ICE_NVM_CSS_SREV_L 0x14 #define ICE_NVM_CSS_SREV_H 0x15 -/* Length of CSS header section in words */ -#define ICE_CSS_HEADER_LENGTH 330 - -/* Offset of Shadow RAM copy in the NVM bank area. */ -#define ICE_NVM_SR_COPY_WORD_OFFSET roundup(ICE_CSS_HEADER_LENGTH, 32) - -/* Size in bytes of Option ROM trailer */ -#define ICE_NVM_OROM_TRAILER_LENGTH (2 * ICE_CSS_HEADER_LENGTH) +/* Length of Authentication header section in words */ +#define ICE_NVM_AUTH_HEADER_LEN 0x08 /* The Link Topology Netlist section is stored as a series of words. It is * stored in the NVM as a TLV, with the first two words containing the type -- cgit v1.2.3 From adbf5a42341f6ea038d3626cd4437d9f0ad0b2dd Mon Sep 17 00:00:00 2001 From: Larysa Zaremba Date: Mon, 3 Jun 2024 14:42:32 -0700 Subject: ice: remove af_xdp_zc_qps bitmap Referenced commit has introduced a bitmap to distinguish between ZC and copy-mode AF_XDP queues, because xsk_get_pool_from_qid() does not do this for us. The bitmap would be especially useful when restoring previous state after rebuild, if only it was not reallocated in the process. This leads to e.g. xdpsock dying after changing number of queues. Instead of preserving the bitmap during the rebuild, remove it completely and distinguish between ZC and copy-mode queues based on the presence of a device associated with the pool. Fixes: e102db780e1c ("ice: track AF_XDP ZC enabled queues in bitmap") Reviewed-by: Przemek Kitszel Signed-off-by: Larysa Zaremba Reviewed-by: Simon Horman Tested-by: Chandan Kumar Rout Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-3-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice.h | 32 +++++++++++++++++++++----------- drivers/net/ethernet/intel/ice/ice_lib.c | 8 -------- drivers/net/ethernet/intel/ice/ice_xsk.c | 13 ++++++------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 6ad8002b22e1..5e51cb84b600 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -409,7 +409,6 @@ struct ice_vsi { struct ice_tc_cfg tc_cfg; struct bpf_prog *xdp_prog; struct ice_tx_ring **xdp_rings; /* XDP ring array */ - unsigned long *af_xdp_zc_qps; /* tracks AF_XDP ZC enabled qps */ u16 num_xdp_txq; /* Used XDP queues */ u8 xdp_mapping_mode; /* ICE_MAP_MODE_[CONTIG|SCATTER] */ @@ -746,6 +745,25 @@ static inline void ice_set_ring_xdp(struct ice_tx_ring *ring) ring->flags |= ICE_TX_FLAGS_RING_XDP; } +/** + * ice_get_xp_from_qid - get ZC XSK buffer pool bound to a queue ID + * @vsi: pointer to VSI + * @qid: index of a queue to look at XSK buff pool presence + * + * Return: A pointer to xsk_buff_pool structure if there is a buffer pool + * attached and configured as zero-copy, NULL otherwise. + */ +static inline struct xsk_buff_pool *ice_get_xp_from_qid(struct ice_vsi *vsi, + u16 qid) +{ + struct xsk_buff_pool *pool = xsk_get_pool_from_qid(vsi->netdev, qid); + + if (!ice_is_xdp_ena_vsi(vsi)) + return NULL; + + return (pool && pool->dev) ? pool : NULL; +} + /** * ice_xsk_pool - get XSK buffer pool bound to a ring * @ring: Rx ring to use @@ -758,10 +776,7 @@ static inline struct xsk_buff_pool *ice_xsk_pool(struct ice_rx_ring *ring) struct ice_vsi *vsi = ring->vsi; u16 qid = ring->q_index; - if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps)) - return NULL; - - return xsk_get_pool_from_qid(vsi->netdev, qid); + return ice_get_xp_from_qid(vsi, qid); } /** @@ -786,12 +801,7 @@ static inline void ice_tx_xsk_pool(struct ice_vsi *vsi, u16 qid) if (!ring) return; - if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps)) { - ring->xsk_pool = NULL; - return; - } - - ring->xsk_pool = xsk_get_pool_from_qid(vsi->netdev, qid); + ring->xsk_pool = ice_get_xp_from_qid(vsi, qid); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 5371e91f6bbb..c0a7ff6c7e87 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -114,14 +114,8 @@ static int ice_vsi_alloc_arrays(struct ice_vsi *vsi) if (!vsi->q_vectors) goto err_vectors; - vsi->af_xdp_zc_qps = bitmap_zalloc(max_t(int, vsi->alloc_txq, vsi->alloc_rxq), GFP_KERNEL); - if (!vsi->af_xdp_zc_qps) - goto err_zc_qps; - return 0; -err_zc_qps: - devm_kfree(dev, vsi->q_vectors); err_vectors: devm_kfree(dev, vsi->rxq_map); err_rxq_map: @@ -309,8 +303,6 @@ static void ice_vsi_free_arrays(struct ice_vsi *vsi) dev = ice_pf_to_dev(pf); - bitmap_free(vsi->af_xdp_zc_qps); - vsi->af_xdp_zc_qps = NULL; /* free the ring and vector containers */ devm_kfree(dev, vsi->q_vectors); vsi->q_vectors = NULL; diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 7541f223bf4f..a65955eb23c0 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -269,7 +269,6 @@ static int ice_xsk_pool_disable(struct ice_vsi *vsi, u16 qid) if (!pool) return -EINVAL; - clear_bit(qid, vsi->af_xdp_zc_qps); xsk_pool_dma_unmap(pool, ICE_RX_DMA_ATTR); return 0; @@ -300,8 +299,6 @@ ice_xsk_pool_enable(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid) if (err) return err; - set_bit(qid, vsi->af_xdp_zc_qps); - return 0; } @@ -349,11 +346,13 @@ ice_realloc_rx_xdp_bufs(struct ice_rx_ring *rx_ring, bool pool_present) int ice_realloc_zc_buf(struct ice_vsi *vsi, bool zc) { struct ice_rx_ring *rx_ring; - unsigned long q; + uint i; + + ice_for_each_rxq(vsi, i) { + rx_ring = vsi->rx_rings[i]; + if (!rx_ring->xsk_pool) + continue; - for_each_set_bit(q, vsi->af_xdp_zc_qps, - max_t(int, vsi->alloc_txq, vsi->alloc_rxq)) { - rx_ring = vsi->rx_rings[q]; if (ice_realloc_rx_xdp_bufs(rx_ring, zc)) return -ENOMEM; } -- cgit v1.2.3 From 744d197162c2070a6045a71e2666ed93a57cc65d Mon Sep 17 00:00:00 2001 From: Larysa Zaremba Date: Mon, 3 Jun 2024 14:42:33 -0700 Subject: ice: add flag to distinguish reset from .ndo_bpf in XDP rings config Commit 6624e780a577 ("ice: split ice_vsi_setup into smaller functions") has placed ice_vsi_free_q_vectors() after ice_destroy_xdp_rings() in the rebuild process. The behaviour of the XDP rings config functions is context-dependent, so the change of order has led to ice_destroy_xdp_rings() doing additional work and removing XDP prog, when it was supposed to be preserved. Also, dependency on the PF state reset flags creates an additional, fortunately less common problem: * PFR is requested e.g. by tx_timeout handler * .ndo_bpf() is asked to delete the program, calls ice_destroy_xdp_rings(), but reset flag is set, so rings are destroyed without deleting the program * ice_vsi_rebuild tries to delete non-existent XDP rings, because the program is still on the VSI * system crashes With a similar race, when requested to attach a program, ice_prepare_xdp_rings() can actually skip setting the program in the VSI and nevertheless report success. Instead of reverting to the old order of function calls, add an enum argument to both ice_prepare_xdp_rings() and ice_destroy_xdp_rings() in order to distinguish between calls from rebuild and .ndo_bpf(). Fixes: efc2214b6047 ("ice: Add support for XDP") Reviewed-by: Igor Bagnucki Signed-off-by: Larysa Zaremba Reviewed-by: Simon Horman Tested-by: Chandan Kumar Rout Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-4-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice.h | 11 +++++++++-- drivers/net/ethernet/intel/ice/ice_lib.c | 5 +++-- drivers/net/ethernet/intel/ice/ice_main.c | 22 ++++++++++++---------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 5e51cb84b600..a5de6ef9c07e 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -930,9 +930,16 @@ int ice_down(struct ice_vsi *vsi); int ice_down_up(struct ice_vsi *vsi); int ice_vsi_cfg_lan(struct ice_vsi *vsi); struct ice_vsi *ice_lb_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi); + +enum ice_xdp_cfg { + ICE_XDP_CFG_FULL, /* Fully apply new config in .ndo_bpf() */ + ICE_XDP_CFG_PART, /* Save/use part of config in VSI rebuild */ +}; + int ice_vsi_determine_xdp_res(struct ice_vsi *vsi); -int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog); -int ice_destroy_xdp_rings(struct ice_vsi *vsi); +int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, + enum ice_xdp_cfg cfg_type); +int ice_destroy_xdp_rings(struct ice_vsi *vsi, enum ice_xdp_cfg cfg_type); int ice_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, u32 flags); diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index c0a7ff6c7e87..dd8b374823ee 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -2285,7 +2285,8 @@ static int ice_vsi_cfg_def(struct ice_vsi *vsi) ret = ice_vsi_determine_xdp_res(vsi); if (ret) goto unroll_vector_base; - ret = ice_prepare_xdp_rings(vsi, vsi->xdp_prog); + ret = ice_prepare_xdp_rings(vsi, vsi->xdp_prog, + ICE_XDP_CFG_PART); if (ret) goto unroll_vector_base; } @@ -2429,7 +2430,7 @@ void ice_vsi_decfg(struct ice_vsi *vsi) /* return value check can be skipped here, it always returns * 0 if reset is in progress */ - ice_destroy_xdp_rings(vsi); + ice_destroy_xdp_rings(vsi, ICE_XDP_CFG_PART); ice_vsi_clear_rings(vsi); ice_vsi_free_q_vectors(vsi); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index f60c022f7960..2a270aacd24a 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -2711,10 +2711,12 @@ static void ice_vsi_assign_bpf_prog(struct ice_vsi *vsi, struct bpf_prog *prog) * ice_prepare_xdp_rings - Allocate, configure and setup Tx rings for XDP * @vsi: VSI to bring up Tx rings used by XDP * @prog: bpf program that will be assigned to VSI + * @cfg_type: create from scratch or restore the existing configuration * * Return 0 on success and negative value on error */ -int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog) +int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, + enum ice_xdp_cfg cfg_type) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; int xdp_rings_rem = vsi->num_xdp_txq; @@ -2790,7 +2792,7 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog) * taken into account at the end of ice_vsi_rebuild, where * ice_cfg_vsi_lan is being called */ - if (ice_is_reset_in_progress(pf->state)) + if (cfg_type == ICE_XDP_CFG_PART) return 0; /* tell the Tx scheduler that right now we have @@ -2842,22 +2844,21 @@ err_map_xdp: /** * ice_destroy_xdp_rings - undo the configuration made by ice_prepare_xdp_rings * @vsi: VSI to remove XDP rings + * @cfg_type: disable XDP permanently or allow it to be restored later * * Detach XDP rings from irq vectors, clean up the PF bitmap and free * resources */ -int ice_destroy_xdp_rings(struct ice_vsi *vsi) +int ice_destroy_xdp_rings(struct ice_vsi *vsi, enum ice_xdp_cfg cfg_type) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; struct ice_pf *pf = vsi->back; int i, v_idx; /* q_vectors are freed in reset path so there's no point in detaching - * rings; in case of rebuild being triggered not from reset bits - * in pf->state won't be set, so additionally check first q_vector - * against NULL + * rings */ - if (ice_is_reset_in_progress(pf->state) || !vsi->q_vectors[0]) + if (cfg_type == ICE_XDP_CFG_PART) goto free_qmap; ice_for_each_q_vector(vsi, v_idx) { @@ -2898,7 +2899,7 @@ free_qmap: if (static_key_enabled(&ice_xdp_locking_key)) static_branch_dec(&ice_xdp_locking_key); - if (ice_is_reset_in_progress(pf->state) || !vsi->q_vectors[0]) + if (cfg_type == ICE_XDP_CFG_PART) return 0; ice_vsi_assign_bpf_prog(vsi, NULL); @@ -3009,7 +3010,8 @@ ice_xdp_setup_prog(struct ice_vsi *vsi, struct bpf_prog *prog, if (xdp_ring_err) { NL_SET_ERR_MSG_MOD(extack, "Not enough Tx resources for XDP"); } else { - xdp_ring_err = ice_prepare_xdp_rings(vsi, prog); + xdp_ring_err = ice_prepare_xdp_rings(vsi, prog, + ICE_XDP_CFG_FULL); if (xdp_ring_err) NL_SET_ERR_MSG_MOD(extack, "Setting up XDP Tx resources failed"); } @@ -3020,7 +3022,7 @@ ice_xdp_setup_prog(struct ice_vsi *vsi, struct bpf_prog *prog, NL_SET_ERR_MSG_MOD(extack, "Setting up XDP Rx resources failed"); } else if (ice_is_xdp_ena_vsi(vsi) && !prog) { xdp_features_clear_redirect_target(vsi->netdev); - xdp_ring_err = ice_destroy_xdp_rings(vsi); + xdp_ring_err = ice_destroy_xdp_rings(vsi, ICE_XDP_CFG_FULL); if (xdp_ring_err) NL_SET_ERR_MSG_MOD(extack, "Freeing XDP Tx resources failed"); /* reallocate Rx queues that were used for zero-copy */ -- cgit v1.2.3 From f3df4044254c98128890b512bf19cc05588f1fe5 Mon Sep 17 00:00:00 2001 From: Larysa Zaremba Date: Mon, 3 Jun 2024 14:42:34 -0700 Subject: ice: map XDP queues to vectors in ice_vsi_map_rings_to_vectors() ice_pf_dcb_recfg() re-maps queues to vectors with ice_vsi_map_rings_to_vectors(), which does not restore the previous state for XDP queues. This leads to no AF_XDP traffic after rebuild. Map XDP queues to vectors in ice_vsi_map_rings_to_vectors(). Also, move the code around, so XDP queues are mapped independently only through .ndo_bpf(). Fixes: 6624e780a577 ("ice: split ice_vsi_setup into smaller functions") Reviewed-by: Przemek Kitszel Signed-off-by: Larysa Zaremba Reviewed-by: Simon Horman Tested-by: Chandan Kumar Rout Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-5-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice.h | 1 + drivers/net/ethernet/intel/ice/ice_base.c | 3 + drivers/net/ethernet/intel/ice/ice_lib.c | 14 ++--- drivers/net/ethernet/intel/ice/ice_main.c | 96 ++++++++++++++++++------------- 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index a5de6ef9c07e..99a75a59078e 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -940,6 +940,7 @@ int ice_vsi_determine_xdp_res(struct ice_vsi *vsi); int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, enum ice_xdp_cfg cfg_type); int ice_destroy_xdp_rings(struct ice_vsi *vsi, enum ice_xdp_cfg cfg_type); +void ice_map_xdp_rings(struct ice_vsi *vsi); int ice_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, u32 flags); diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 687f6cb2b917..5d396c1a7731 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -842,6 +842,9 @@ void ice_vsi_map_rings_to_vectors(struct ice_vsi *vsi) } rx_rings_rem -= rx_rings_per_v; } + + if (ice_is_xdp_ena_vsi(vsi)) + ice_map_xdp_rings(vsi); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index dd8b374823ee..7629b0190578 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -2274,13 +2274,6 @@ static int ice_vsi_cfg_def(struct ice_vsi *vsi) if (ret) goto unroll_vector_base; - ice_vsi_map_rings_to_vectors(vsi); - - /* Associate q_vector rings to napi */ - ice_vsi_set_napi_queues(vsi); - - vsi->stat_offsets_loaded = false; - if (ice_is_xdp_ena_vsi(vsi)) { ret = ice_vsi_determine_xdp_res(vsi); if (ret) @@ -2291,6 +2284,13 @@ static int ice_vsi_cfg_def(struct ice_vsi *vsi) goto unroll_vector_base; } + ice_vsi_map_rings_to_vectors(vsi); + + /* Associate q_vector rings to napi */ + ice_vsi_set_napi_queues(vsi); + + vsi->stat_offsets_loaded = false; + /* ICE_VSI_CTRL does not need RSS so skip RSS processing */ if (vsi->type != ICE_VSI_CTRL) /* Do not exit if configuring RSS had an issue, at diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 2a270aacd24a..1b61ca3a6eb6 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -2707,6 +2707,60 @@ static void ice_vsi_assign_bpf_prog(struct ice_vsi *vsi, struct bpf_prog *prog) bpf_prog_put(old_prog); } +static struct ice_tx_ring *ice_xdp_ring_from_qid(struct ice_vsi *vsi, int qid) +{ + struct ice_q_vector *q_vector; + struct ice_tx_ring *ring; + + if (static_key_enabled(&ice_xdp_locking_key)) + return vsi->xdp_rings[qid % vsi->num_xdp_txq]; + + q_vector = vsi->rx_rings[qid]->q_vector; + ice_for_each_tx_ring(ring, q_vector->tx) + if (ice_ring_is_xdp(ring)) + return ring; + + return NULL; +} + +/** + * ice_map_xdp_rings - Map XDP rings to interrupt vectors + * @vsi: the VSI with XDP rings being configured + * + * Map XDP rings to interrupt vectors and perform the configuration steps + * dependent on the mapping. + */ +void ice_map_xdp_rings(struct ice_vsi *vsi) +{ + int xdp_rings_rem = vsi->num_xdp_txq; + int v_idx, q_idx; + + /* follow the logic from ice_vsi_map_rings_to_vectors */ + ice_for_each_q_vector(vsi, v_idx) { + struct ice_q_vector *q_vector = vsi->q_vectors[v_idx]; + int xdp_rings_per_v, q_id, q_base; + + xdp_rings_per_v = DIV_ROUND_UP(xdp_rings_rem, + vsi->num_q_vectors - v_idx); + q_base = vsi->num_xdp_txq - xdp_rings_rem; + + for (q_id = q_base; q_id < (q_base + xdp_rings_per_v); q_id++) { + struct ice_tx_ring *xdp_ring = vsi->xdp_rings[q_id]; + + xdp_ring->q_vector = q_vector; + xdp_ring->next = q_vector->tx.tx_ring; + q_vector->tx.tx_ring = xdp_ring; + } + xdp_rings_rem -= xdp_rings_per_v; + } + + ice_for_each_rxq(vsi, q_idx) { + vsi->rx_rings[q_idx]->xdp_ring = ice_xdp_ring_from_qid(vsi, + q_idx); + ice_tx_xsk_pool(vsi, q_idx); + } +} + /** * ice_prepare_xdp_rings - Allocate, configure and setup Tx rings for XDP * @vsi: VSI to bring up Tx rings used by XDP @@ -2719,7 +2773,6 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, enum ice_xdp_cfg cfg_type) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; - int xdp_rings_rem = vsi->num_xdp_txq; struct ice_pf *pf = vsi->back; struct ice_qs_cfg xdp_qs_cfg = { .qs_mutex = &pf->avail_q_mutex, @@ -2732,8 +2785,7 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, .mapping_mode = ICE_VSI_MAP_CONTIG }; struct device *dev; - int i, v_idx; - int status; + int status, i; dev = ice_pf_to_dev(pf); vsi->xdp_rings = devm_kcalloc(dev, vsi->num_xdp_txq, @@ -2752,42 +2804,6 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, if (ice_xdp_alloc_setup_rings(vsi)) goto clear_xdp_rings; - /* follow the logic from ice_vsi_map_rings_to_vectors */ - ice_for_each_q_vector(vsi, v_idx) { - struct ice_q_vector *q_vector = vsi->q_vectors[v_idx]; - int xdp_rings_per_v, q_id, q_base; - - xdp_rings_per_v = DIV_ROUND_UP(xdp_rings_rem, - vsi->num_q_vectors - v_idx); - q_base = vsi->num_xdp_txq - xdp_rings_rem; - - for (q_id = q_base; q_id < (q_base + xdp_rings_per_v); q_id++) { - struct ice_tx_ring *xdp_ring = vsi->xdp_rings[q_id]; - - xdp_ring->q_vector = q_vector; - xdp_ring->next = q_vector->tx.tx_ring; - q_vector->tx.tx_ring = xdp_ring; - } - xdp_rings_rem -= xdp_rings_per_v; - } - - ice_for_each_rxq(vsi, i) { - if (static_key_enabled(&ice_xdp_locking_key)) { - vsi->rx_rings[i]->xdp_ring = vsi->xdp_rings[i % vsi->num_xdp_txq]; - } else { - struct ice_q_vector *q_vector = vsi->rx_rings[i]->q_vector; - struct ice_tx_ring *ring; - - ice_for_each_tx_ring(ring, q_vector->tx) { - if (ice_ring_is_xdp(ring)) { - vsi->rx_rings[i]->xdp_ring = ring; - break; - } - } - } - ice_tx_xsk_pool(vsi, i); - } - /* omit the scheduler update if in reset path; XDP queues will be * taken into account at the end of ice_vsi_rebuild, where * ice_cfg_vsi_lan is being called @@ -2795,6 +2811,8 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, if (cfg_type == ICE_XDP_CFG_PART) return 0; + ice_map_xdp_rings(vsi); + /* tell the Tx scheduler that right now we have * additional queues */ -- cgit v1.2.3 From 7d67d11fbe194f71298263f48e33ae2afa38197e Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Mon, 3 Jun 2024 14:42:35 -0700 Subject: igc: Fix Energy Efficient Ethernet support declaration The commit 01cf893bf0f4 ("net: intel: i40e/igc: Remove setting Autoneg in EEE capabilities") removed SUPPORTED_Autoneg field but left inappropriate ethtool_keee structure initialization. When "ethtool --show " (get_eee) invoke, the 'ethtool_keee' structure was accidentally overridden. Remove the 'ethtool_keee' overriding and add EEE declaration as per IEEE specification that allows reporting Energy Efficient Ethernet capabilities. Examples: Before fix: ethtool --show-eee enp174s0 EEE settings for enp174s0: EEE status: not supported After fix: EEE settings for enp174s0: EEE status: disabled Tx LPI: disabled Supported EEE link modes: 100baseT/Full 1000baseT/Full 2500baseT/Full Fixes: 01cf893bf0f4 ("net: intel: i40e/igc: Remove setting Autoneg in EEE capabilities") Suggested-by: Dima Ruinskiy Signed-off-by: Sasha Neftin Tested-by: Naama Meir Signed-off-by: Jacob Keller Link: https://lore.kernel.org/r/20240603-net-2024-05-30-intel-net-fixes-v2-6-e3563aa89b0c@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_ethtool.c | 9 +++++++-- drivers/net/ethernet/intel/igc/igc_main.c | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index f2c4f1966bb0..0cd2bd695db1 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1629,12 +1629,17 @@ static int igc_ethtool_get_eee(struct net_device *netdev, struct igc_hw *hw = &adapter->hw; u32 eeer; + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + edata->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + edata->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + edata->supported); + if (hw->dev_spec._base.eee_enable) mii_eee_cap1_mod_linkmode_t(edata->advertised, adapter->eee_advert); - *edata = adapter->eee; - eeer = rd32(IGC_EEER); /* EEE status on negotiated link */ diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 12f004f46082..305e05294a26 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -4975,6 +4976,9 @@ void igc_up(struct igc_adapter *adapter) /* start the watchdog. */ hw->mac.get_link_status = true; schedule_work(&adapter->watchdog_task); + + adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T | + MDIO_EEE_2_5GT; } /** -- cgit v1.2.3 From 5703fc058efdafcdd6b70776ee562478f0753acb Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Thu, 28 Mar 2024 14:07:16 -0500 Subject: drm/vmwgfx: Don't memcmp equivalent pointers These pointers are frequently the same and memcmp does not compare the pointers before comparing their contents so this was wasting cycles comparing 16 KiB of memory which will always be equal. Fixes: bb6780aa5a1d ("drm/vmwgfx: Diff cursors when using cmds") Signed-off-by: Ian Forbes Signed-off-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20240328190716.27367-1-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 9532258a0848..00c4ff684130 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -224,7 +224,7 @@ static bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, new_image = vmw_du_cursor_plane_acquire_image(new_vps); changed = false; - if (old_image && new_image) + if (old_image && new_image && old_image != new_image) changed = memcmp(old_image, new_image, size) != 0; return changed; -- cgit v1.2.3 From b0c9a26435413b81799047a7be53255640432547 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Tue, 4 Jun 2024 11:25:00 +0300 Subject: net: wwan: iosm: Fix tainted pointer delete is case of region creation fail In case of region creation fail in ipc_devlink_create_region(), previously created regions delete process starts from tainted pointer which actually holds error code value. Fix this bug by decreasing region index before delete. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 4dcd183fbd67 ("net: wwan: iosm: devlink registration") Signed-off-by: Aleksandr Mishin Acked-by: Sergey Ryazanov Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20240604082500.20769-1-amishin@t-argos.ru Signed-off-by: Paolo Abeni --- drivers/net/wwan/iosm/iosm_ipc_devlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.c b/drivers/net/wwan/iosm/iosm_ipc_devlink.c index bef6819986e9..33d6342124bc 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_devlink.c +++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.c @@ -211,7 +211,7 @@ static int ipc_devlink_create_region(struct iosm_devlink *devlink) rc = PTR_ERR(devlink->cd_regions[i]); dev_err(devlink->dev, "Devlink region fail,err %d", rc); /* Delete previously created regions */ - for ( ; i >= 0; i--) + for (i--; i >= 0; i--) devlink_region_destroy(devlink->cd_regions[i]); goto region_create_fail; } -- cgit v1.2.3 From 26bfb8b57063f52b867f9b6c8d1742fcb5bd656c Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:27 -0700 Subject: af_unix: Set sk->sk_state under unix_state_lock() for truly disconencted peer. When a SOCK_DGRAM socket connect()s to another socket, the both sockets' sk->sk_state are changed to TCP_ESTABLISHED so that we can register them to BPF SOCKMAP. When the socket disconnects from the peer by connect(AF_UNSPEC), the state is set back to TCP_CLOSE. Then, the peer's state is also set to TCP_CLOSE, but the update is done locklessly and unconditionally. Let's say socket A connect()ed to B, B connect()ed to C, and A disconnects from B. After the first two connect()s, all three sockets' sk->sk_state are TCP_ESTABLISHED: $ ss -xa Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess u_dgr ESTAB 0 0 @A 641 * 642 u_dgr ESTAB 0 0 @B 642 * 643 u_dgr ESTAB 0 0 @C 643 * 0 And after the disconnect, B's state is TCP_CLOSE even though it's still connected to C and C's state is TCP_ESTABLISHED. $ ss -xa Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess u_dgr UNCONN 0 0 @A 641 * 0 u_dgr UNCONN 0 0 @B 642 * 643 u_dgr ESTAB 0 0 @C 643 * 0 In this case, we cannot register B to SOCKMAP. So, when a socket disconnects from the peer, we should not set TCP_CLOSE to the peer if the peer is connected to yet another socket, and this must be done under unix_state_lock(). Note that we use WRITE_ONCE() for sk->sk_state as there are many lockless readers. These data-races will be fixed in the following patches. Fixes: 83301b5367a9 ("af_unix: Set TCP_ESTABLISHED for datagram sockets too") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 25b49efc0926..b162164b7a42 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -570,7 +570,6 @@ static void unix_dgram_disconnected(struct sock *sk, struct sock *other) sk_error_report(other); } } - other->sk_state = TCP_CLOSE; } static void unix_sock_destructor(struct sock *sk) @@ -1424,8 +1423,15 @@ restart: unix_state_double_unlock(sk, other); - if (other != old_peer) + if (other != old_peer) { unix_dgram_disconnected(sk, old_peer); + + unix_state_lock(old_peer); + if (!unix_peer(old_peer)) + WRITE_ONCE(old_peer->sk_state, TCP_CLOSE); + unix_state_unlock(old_peer); + } + sock_put(old_peer); } else { unix_peer(sk) = other; -- cgit v1.2.3 From 942238f9735a4a4ebf8274b218d9a910158941d1 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:28 -0700 Subject: af_unix: Annodate data-races around sk->sk_state for writers. sk->sk_state is changed under unix_state_lock(), but it's read locklessly in many places. This patch adds WRITE_ONCE() on the writer side. We will add READ_ONCE() to the lockless readers in the following patches. Fixes: 83301b5367a9 ("af_unix: Set TCP_ESTABLISHED for datagram sockets too") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b162164b7a42..424d021a4d7d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -616,7 +616,7 @@ static void unix_release_sock(struct sock *sk, int embrion) u->path.dentry = NULL; u->path.mnt = NULL; state = sk->sk_state; - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); skpair = unix_peer(sk); unix_peer(sk) = NULL; @@ -738,7 +738,8 @@ static int unix_listen(struct socket *sock, int backlog) if (backlog > sk->sk_max_ack_backlog) wake_up_interruptible_all(&u->peer_wait); sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; + WRITE_ONCE(sk->sk_state, TCP_LISTEN); + /* set credentials so connect can copy them */ init_peercred(sk); err = 0; @@ -1401,7 +1402,8 @@ restart: if (err) goto out_unlock; - sk->sk_state = other->sk_state = TCP_ESTABLISHED; + WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED); + WRITE_ONCE(other->sk_state, TCP_ESTABLISHED); } else { /* * 1003.1g breaking connected state with AF_UNSPEC @@ -1418,7 +1420,7 @@ restart: unix_peer(sk) = other; if (!other) - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer); unix_state_double_unlock(sk, other); @@ -1639,7 +1641,7 @@ restart: copy_peercred(sk, other); sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; + WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED); sock_hold(newsk); smp_mb__after_atomic(); /* sock_hold() does an atomic_inc() */ @@ -2050,7 +2052,7 @@ restart_locked: unix_peer(sk) = NULL; unix_dgram_peer_wake_disconnect_wakeup(sk, other); - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); unix_state_unlock(sk); unix_dgram_disconnected(sk, other); -- cgit v1.2.3 From 3a0f38eb285c8c2eead4b3230c7ac2983707599d Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:29 -0700 Subject: af_unix: Annotate data-race of sk->sk_state in unix_inq_len(). ioctl(SIOCINQ) calls unix_inq_len() that checks sk->sk_state first and returns -EINVAL if it's TCP_LISTEN. Then, for SOCK_STREAM sockets, unix_inq_len() returns the number of bytes in recvq. However, unix_inq_len() does not hold unix_state_lock(), and the concurrent listen() might change the state after checking sk->sk_state. If the race occurs, 0 is returned for the listener, instead of -EINVAL, because the length of skb with embryo is 0. We could hold unix_state_lock() in unix_inq_len(), but it's overkill given the result is true for pre-listen() TCP_CLOSE state. So, let's use READ_ONCE() for sk->sk_state in unix_inq_len(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 424d021a4d7d..b37b53767b29 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -3017,7 +3017,7 @@ long unix_inq_len(struct sock *sk) struct sk_buff *skb; long amount = 0; - if (sk->sk_state == TCP_LISTEN) + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) return -EINVAL; spin_lock(&sk->sk_receive_queue.lock); -- cgit v1.2.3 From eb0718fb3e97ad0d6f4529b810103451c90adf94 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:30 -0700 Subject: af_unix: Annotate data-races around sk->sk_state in unix_write_space() and poll(). unix_poll() and unix_dgram_poll() read sk->sk_state locklessly and calls unix_writable() which also reads sk->sk_state without holding unix_state_lock(). Let's use READ_ONCE() in unix_poll() and unix_dgram_poll() and pass it to unix_writable(). While at it, we remove TCP_SYN_SENT check in unix_dgram_poll() as that state does not exist for AF_UNIX socket since the code was added. Fixes: 1586a5877db9 ("af_unix: do not report POLLOUT on listeners") Fixes: 3c73419c09a5 ("af_unix: fix 'poll for write'/ connected DGRAM sockets") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b37b53767b29..3bacf47cb94d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -530,9 +530,9 @@ static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) return 0; } -static int unix_writable(const struct sock *sk) +static int unix_writable(const struct sock *sk, unsigned char state) { - return sk->sk_state != TCP_LISTEN && + return state != TCP_LISTEN && (refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; } @@ -541,7 +541,7 @@ static void unix_write_space(struct sock *sk) struct socket_wq *wq; rcu_read_lock(); - if (unix_writable(sk)) { + if (unix_writable(sk, READ_ONCE(sk->sk_state))) { wq = rcu_dereference(sk->sk_wq); if (skwq_has_sleeper(wq)) wake_up_interruptible_sync_poll(&wq->wait, @@ -3129,12 +3129,14 @@ static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned lon static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; + unsigned char state; __poll_t mask; u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; shutdown = READ_ONCE(sk->sk_shutdown); + state = READ_ONCE(sk->sk_state); /* exceptional events? */ if (READ_ONCE(sk->sk_err)) @@ -3156,14 +3158,14 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa /* Connection-based need to check for termination and startup */ if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) && - sk->sk_state == TCP_CLOSE) + state == TCP_CLOSE) mask |= EPOLLHUP; /* * we set writable also when the other side has shut down the * connection. This prevents stuck sockets. */ - if (unix_writable(sk)) + if (unix_writable(sk, state)) mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; return mask; @@ -3174,12 +3176,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, { struct sock *sk = sock->sk, *other; unsigned int writable; + unsigned char state; __poll_t mask; u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; shutdown = READ_ONCE(sk->sk_shutdown); + state = READ_ONCE(sk->sk_state); /* exceptional events? */ if (READ_ONCE(sk->sk_err) || @@ -3199,19 +3203,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, mask |= EPOLLIN | EPOLLRDNORM; /* Connection-based need to check for termination and startup */ - if (sk->sk_type == SOCK_SEQPACKET) { - if (sk->sk_state == TCP_CLOSE) - mask |= EPOLLHUP; - /* connection hasn't started yet? */ - if (sk->sk_state == TCP_SYN_SENT) - return mask; - } + if (sk->sk_type == SOCK_SEQPACKET && state == TCP_CLOSE) + mask |= EPOLLHUP; /* No write status requested, avoid expensive OUT tests. */ if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT))) return mask; - writable = unix_writable(sk); + writable = unix_writable(sk, state); if (writable) { unix_state_lock(sk); -- cgit v1.2.3 From a9bf9c7dc6a5899c01cb8f6e773a66315a5cd4b7 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:31 -0700 Subject: af_unix: Annotate data-race of sk->sk_state in unix_stream_connect(). As small optimisation, unix_stream_connect() prefetches the client's sk->sk_state without unix_state_lock() and checks if it's TCP_CLOSE. Later, sk->sk_state is checked again under unix_state_lock(). Let's use READ_ONCE() for the first check and TCP_CLOSE directly for the second check. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3bacf47cb94d..84552826530d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1481,7 +1481,6 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, struct sk_buff *skb = NULL; long timeo; int err; - int st; err = unix_validate_addr(sunaddr, addr_len); if (err) @@ -1571,9 +1570,7 @@ restart: Well, and we have to recheck the state after socket locked. */ - st = sk->sk_state; - - switch (st) { + switch (READ_ONCE(sk->sk_state)) { case TCP_CLOSE: /* This is ok... continue with connect */ break; @@ -1588,7 +1585,7 @@ restart: unix_state_lock_nested(sk, U_LOCK_SECOND); - if (sk->sk_state != st) { + if (sk->sk_state != TCP_CLOSE) { unix_state_unlock(sk); unix_state_unlock(other); sock_put(other); -- cgit v1.2.3 From 1b536948e805aab61a48c5aa5db10c9afee880bd Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:32 -0700 Subject: af_unix: Annotate data-race of sk->sk_state in unix_accept(). Once sk->sk_state is changed to TCP_LISTEN, it never changes. unix_accept() takes the advantage and reads sk->sk_state without holding unix_state_lock(). Let's use READ_ONCE() there. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 84552826530d..4763c26ae480 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1710,7 +1710,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, goto out; arg->err = -EINVAL; - if (sk->sk_state != TCP_LISTEN) + if (READ_ONCE(sk->sk_state) != TCP_LISTEN) goto out; /* If socket state is TCP_LISTEN it cannot change (for now...), -- cgit v1.2.3 From 8a34d4e8d9742a24f74998f45a6a98edd923319b Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:33 -0700 Subject: af_unix: Annotate data-races around sk->sk_state in sendmsg() and recvmsg(). The following functions read sk->sk_state locklessly and proceed only if the state is TCP_ESTABLISHED. * unix_stream_sendmsg * unix_stream_read_generic * unix_seqpacket_sendmsg * unix_seqpacket_recvmsg Let's use READ_ONCE() there. Fixes: a05d2ad1c1f3 ("af_unix: Only allow recv on connected seqpacket sockets.") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 4763c26ae480..4ef9c21783a5 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2226,7 +2226,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, } if (msg->msg_namelen) { - err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; + err = READ_ONCE(sk->sk_state) == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; goto out_err; } else { err = -ENOTCONN; @@ -2340,7 +2340,7 @@ static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg, if (err) return err; - if (sk->sk_state != TCP_ESTABLISHED) + if (READ_ONCE(sk->sk_state) != TCP_ESTABLISHED) return -ENOTCONN; if (msg->msg_namelen) @@ -2354,7 +2354,7 @@ static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; - if (sk->sk_state != TCP_ESTABLISHED) + if (READ_ONCE(sk->sk_state) != TCP_ESTABLISHED) return -ENOTCONN; return unix_dgram_recvmsg(sock, msg, size, flags); @@ -2683,7 +2683,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, size_t size = state->size; unsigned int last_len; - if (unlikely(sk->sk_state != TCP_ESTABLISHED)) { + if (unlikely(READ_ONCE(sk->sk_state) != TCP_ESTABLISHED)) { err = -EINVAL; goto out; } -- cgit v1.2.3 From af4c733b6b1aded4dc808fafece7dfe6e9d2ebb3 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:34 -0700 Subject: af_unix: Annotate data-race of sk->sk_state in unix_stream_read_skb(). unix_stream_read_skb() is called from sk->sk_data_ready() context where unix_state_lock() is not held. Let's use READ_ONCE() there. Fixes: 77462de14a43 ("af_unix: Add read_sock for stream socket types") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 4ef9c21783a5..e7b74207aa3b 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2659,7 +2659,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) { - if (unlikely(sk->sk_state != TCP_ESTABLISHED)) + if (unlikely(READ_ONCE(sk->sk_state) != TCP_ESTABLISHED)) return -ENOTCONN; return unix_read_skb(sk, recv_actor); -- cgit v1.2.3 From 0aa3be7b3e1f8f997312cc4705f8165e02806f8f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:35 -0700 Subject: af_unix: Annotate data-races around sk->sk_state in UNIX_DIAG. While dumping AF_UNIX sockets via UNIX_DIAG, sk->sk_state is read locklessly. Let's use READ_ONCE() there. Note that the result could be inconsistent if the socket is dumped during the state change. This is common for other SOCK_DIAG and similar interfaces. Fixes: c9da99e6475f ("unix_diag: Fixup RQLEN extension report") Fixes: 2aac7a2cb0d9 ("unix_diag: Pending connections IDs NLA") Fixes: 45a96b9be6ec ("unix_diag: Dumping all sockets core") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/diag.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/unix/diag.c b/net/unix/diag.c index ae39538c5042..116cf508aea4 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -65,7 +65,7 @@ static int sk_diag_dump_icons(struct sock *sk, struct sk_buff *nlskb) u32 *buf; int i; - if (sk->sk_state == TCP_LISTEN) { + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) { spin_lock(&sk->sk_receive_queue.lock); attr = nla_reserve(nlskb, UNIX_DIAG_ICONS, @@ -103,7 +103,7 @@ static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) { struct unix_diag_rqlen rql; - if (sk->sk_state == TCP_LISTEN) { + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) { rql.udiag_rqueue = sk->sk_receive_queue.qlen; rql.udiag_wqueue = sk->sk_max_ack_backlog; } else { @@ -136,7 +136,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r rep = nlmsg_data(nlh); rep->udiag_family = AF_UNIX; rep->udiag_type = sk->sk_type; - rep->udiag_state = sk->sk_state; + rep->udiag_state = READ_ONCE(sk->sk_state); rep->pad = 0; rep->udiag_ino = sk_ino; sock_diag_save_cookie(sk, rep->udiag_cookie); @@ -215,7 +215,7 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) sk_for_each(sk, &net->unx.table.buckets[slot]) { if (num < s_num) goto next; - if (!(req->udiag_states & (1 << sk->sk_state))) + if (!(req->udiag_states & (1 << READ_ONCE(sk->sk_state)))) goto next; if (sk_diag_dump(sk, skb, req, sk_user_ns(skb->sk), NETLINK_CB(cb->skb).portid, -- cgit v1.2.3 From b0632e53e0da8054e36bc973f0eec69d30f1b7c6 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:36 -0700 Subject: af_unix: Annotate data-races around sk->sk_sndbuf. sk_setsockopt() changes sk->sk_sndbuf under lock_sock(), but it's not used in af_unix.c. Let's use READ_ONCE() to read sk->sk_sndbuf in unix_writable(), unix_dgram_sendmsg(), and unix_stream_sendmsg(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e7b74207aa3b..de2a5e334a88 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -533,7 +533,7 @@ static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) static int unix_writable(const struct sock *sk, unsigned char state) { return state != TCP_LISTEN && - (refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; + (refcount_read(&sk->sk_wmem_alloc) << 2) <= READ_ONCE(sk->sk_sndbuf); } static void unix_write_space(struct sock *sk) @@ -1967,7 +1967,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, } err = -EMSGSIZE; - if (len > sk->sk_sndbuf - 32) + if (len > READ_ONCE(sk->sk_sndbuf) - 32) goto out; if (len > SKB_MAX_ALLOC) { @@ -2247,7 +2247,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, &err, 0); } else { /* Keep two messages in the pipe so it schedules better */ - size = min_t(int, size, (sk->sk_sndbuf >> 1) - 64); + size = min_t(int, size, (READ_ONCE(sk->sk_sndbuf) >> 1) - 64); /* allow fallback to order-0 allocations */ size = min_t(int, size, SKB_MAX_HEAD(0) + UNIX_SKB_FRAGS_SZ); -- cgit v1.2.3 From bd9f2d05731f6a112d0c7391a0d537bfc588dbe6 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:37 -0700 Subject: af_unix: Annotate data-race of net->unx.sysctl_max_dgram_qlen. net->unx.sysctl_max_dgram_qlen is exposed as a sysctl knob and can be changed concurrently. Let's use READ_ONCE() in unix_create1(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index de2a5e334a88..623ee39657e2 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -976,7 +976,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern, sk->sk_hash = unix_unbound_hash(sk); sk->sk_allocation = GFP_KERNEL_ACCOUNT; sk->sk_write_space = unix_write_space; - sk->sk_max_ack_backlog = net->unx.sysctl_max_dgram_qlen; + sk->sk_max_ack_backlog = READ_ONCE(net->unx.sysctl_max_dgram_qlen); sk->sk_destruct = unix_sock_destructor; u = unix_sk(sk); u->listener = NULL; -- cgit v1.2.3 From 45d872f0e65593176d880ec148f41ad7c02e40a7 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:38 -0700 Subject: af_unix: Use unix_recvq_full_lockless() in unix_stream_connect(). Once sk->sk_state is changed to TCP_LISTEN, it never changes. unix_accept() takes advantage of this characteristics; it does not hold the listener's unix_state_lock() and only acquires recvq lock to pop one skb. It means unix_state_lock() does not prevent the queue length from changing in unix_stream_connect(). Thus, we need to use unix_recvq_full_lockless() to avoid data-race. Now we remove unix_recvq_full() as no one uses it. Note that we can remove READ_ONCE() for sk->sk_max_ack_backlog in unix_recvq_full_lockless() because of the following reasons: (1) For SOCK_DGRAM, it is a written-once field in unix_create1() (2) For SOCK_STREAM and SOCK_SEQPACKET, it is changed under the listener's unix_state_lock() in unix_listen(), and we hold the lock in unix_stream_connect() Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 623ee39657e2..eb3ba3448ed3 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -221,15 +221,9 @@ static inline int unix_may_send(struct sock *sk, struct sock *osk) return unix_peer(osk) == NULL || unix_our_peer(sk, osk); } -static inline int unix_recvq_full(const struct sock *sk) -{ - return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog; -} - static inline int unix_recvq_full_lockless(const struct sock *sk) { - return skb_queue_len_lockless(&sk->sk_receive_queue) > - READ_ONCE(sk->sk_max_ack_backlog); + return skb_queue_len_lockless(&sk->sk_receive_queue) > sk->sk_max_ack_backlog; } struct sock *unix_peer_get(struct sock *s) @@ -1545,7 +1539,7 @@ restart: if (other->sk_shutdown & RCV_SHUTDOWN) goto out_unlock; - if (unix_recvq_full(other)) { + if (unix_recvq_full_lockless(other)) { err = -EAGAIN; if (!timeo) goto out_unlock; -- cgit v1.2.3 From 83690b82d228b3570565ebd0b41873933238b97f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:39 -0700 Subject: af_unix: Use skb_queue_empty_lockless() in unix_release_sock(). If the socket type is SOCK_STREAM or SOCK_SEQPACKET, unix_release_sock() checks the length of the peer socket's recvq under unix_state_lock(). However, unix_stream_read_generic() calls skb_unlink() after releasing the lock. Also, for SOCK_SEQPACKET, __skb_try_recv_datagram() unlinks skb without unix_state_lock(). Thues, unix_state_lock() does not protect qlen. Let's use skb_queue_empty_lockless() in unix_release_sock(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index eb3ba3448ed3..80846279de9f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -631,7 +631,7 @@ static void unix_release_sock(struct sock *sk, int embrion) unix_state_lock(skpair); /* No more writes */ WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK); - if (!skb_queue_empty(&sk->sk_receive_queue) || embrion) + if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || embrion) WRITE_ONCE(skpair->sk_err, ECONNRESET); unix_state_unlock(skpair); skpair->sk_state_change(skpair); -- cgit v1.2.3 From 5d915e584d8408211d4567c22685aae8820bfc55 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:40 -0700 Subject: af_unix: Use skb_queue_len_lockless() in sk_diag_show_rqlen(). We can dump the socket queue length via UNIX_DIAG by specifying UDIAG_SHOW_RQLEN. If sk->sk_state is TCP_LISTEN, we return the recv queue length, but here we do not hold recvq lock. Let's use skb_queue_len_lockless() in sk_diag_show_rqlen(). Fixes: c9da99e6475f ("unix_diag: Fixup RQLEN extension report") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/diag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/diag.c b/net/unix/diag.c index 116cf508aea4..321336f91a0a 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -104,7 +104,7 @@ static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) struct unix_diag_rqlen rql; if (READ_ONCE(sk->sk_state) == TCP_LISTEN) { - rql.udiag_rqueue = sk->sk_receive_queue.qlen; + rql.udiag_rqueue = skb_queue_len_lockless(&sk->sk_receive_queue); rql.udiag_wqueue = sk->sk_max_ack_backlog; } else { rql.udiag_rqueue = (u32) unix_inq_len(sk); -- cgit v1.2.3 From efaf24e30ec39ebbea9112227485805a48b0ceb1 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Jun 2024 09:52:41 -0700 Subject: af_unix: Annotate data-race of sk->sk_shutdown in sk_diag_fill(). While dumping sockets via UNIX_DIAG, we do not hold unix_state_lock(). Let's use READ_ONCE() to read sk->sk_shutdown. Fixes: e4e541a84863 ("sock-diag: Report shutdown for inet and unix sockets (v2)") Signed-off-by: Kuniyuki Iwashima Signed-off-by: Paolo Abeni --- net/unix/diag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/diag.c b/net/unix/diag.c index 321336f91a0a..937edf4afed4 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -165,7 +165,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sock_diag_put_meminfo(sk, skb, UNIX_DIAG_MEMINFO)) goto out_nlmsg_trim; - if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, sk->sk_shutdown)) + if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, READ_ONCE(sk->sk_shutdown))) goto out_nlmsg_trim; if ((req->udiag_show & UDIAG_SHOW_UID) && -- cgit v1.2.3 From c181689bc479d3b2300f91fc4d53e089d7631898 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:38 +0900 Subject: kconfig: remove unneeded code for user-supplied values being out of range This is a leftover from commit ce1fc9345a59 ("kconfig: do not clear SYMBOL_DEF_USER when the value is out of range"). This code is now redundant because if a user-supplied value is out of range, the value adjusted by sym_validate_range() differs, and conf_unsaved has already been incremented a few lines above. Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 387503daf0f7..85b53069ba7a 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -533,19 +533,6 @@ int conf_read(const char *name) */ if (sym->visible == no && !conf_unsaved) sym->flags &= ~SYMBOL_DEF_USER; - switch (sym->type) { - case S_STRING: - case S_INT: - case S_HEX: - /* Reset a string value if it's out of range */ - if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) - break; - sym->flags &= ~SYMBOL_VALID; - conf_unsaved++; - break; - default: - break; - } } } -- cgit v1.2.3 From 46edf4372e336ef3a61c3126e49518099d2e2e6d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:40 +0900 Subject: kconfig: gconf: give a proper initial state to the Save button Currently, the initial state of the "Save" button is always active. If none of the CONFIG options are changed while loading the .config file, the "Save" button should be greyed out. This can be fixed by calling conf_read() after widget initialization. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index cc400ffe6615..e04dbafd3add 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1422,7 +1422,6 @@ int main(int ac, char *av[]) conf_parse(name); fixup_rootmenu(&rootmenu); - conf_read(NULL); /* Load the interface and connect signals */ init_main_window(glade_file); @@ -1430,6 +1429,8 @@ int main(int ac, char *av[]) init_left_tree(); init_right_tree(); + conf_read(NULL); + switch (view_mode) { case SINGLE_VIEW: display_tree_part(); -- cgit v1.2.3 From bf83266a1eef8251e2f126dba635039de069104a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 4 Jun 2024 01:19:02 +0900 Subject: kconfig: doc: fix a typo in the note about 'imply' This sentence does not make sense due to a typo. Fix it. Fixes: def2fbffe62c ("kconfig: allow symbols implied by y to become m") Signed-off-by: Masahiro Yamada Reviewed-by: Randy Dunlap --- Documentation/kbuild/kconfig-language.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 555c2f839969..86be5b857cc4 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -184,7 +184,7 @@ applicable everywhere (see syntax). ability to hook into a secondary subsystem while allowing the user to configure that subsystem out without also having to unset these drivers. - Note: If the combination of FOO=y and BAR=m causes a link error, + Note: If the combination of FOO=y and BAZ=m causes a link error, you can guard the function call with IS_REACHABLE():: foo_init() -- cgit v1.2.3 From 45c7f555bf5e716d9c6ffb737e97d4cc9b4c21ef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 4 Jun 2024 01:19:03 +0900 Subject: kconfig: doc: document behavior of 'select' and 'imply' followed by 'if' Documentation/kbuild/kconfig-language.rst explains the behavior of 'select' as follows: reverse dependencies can be used to force a lower limit of another symbol. The value of the current menu symbol is used as the minimal value can be set to. This is not true when the 'select' property is followed by 'if'. [Test Code] config MODULES def_bool y modules config A def_tristate y select C if B config B def_tristate m config C tristate [Result] CONFIG_MODULES=y CONFIG_A=y CONFIG_B=m CONFIG_C=m If "the value of A is used as the minimal value C can be set to", C must be 'y'. The actual behavior is "C is selected by (A && B)". The lower limit of C is downgraded due to B being 'm'. This behavior is kind of weird, and this has arisen several times in the mailing list. I do not know whether it is a bug or intended behavior. Anyway, it is not feasible to change it now because many Kconfig files are written based on this behavior. The same applies to 'imply'. Document this (but reserve the possibility for a future change). Signed-off-by: Masahiro Yamada Reviewed-by: Randy Dunlap --- Documentation/kbuild/kconfig-language.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 86be5b857cc4..1fb3f5e6193c 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -150,6 +150,12 @@ applicable everywhere (see syntax). That will limit the usefulness but on the other hand avoid the illegal configurations all over. + If "select" is followed by "if" , will be + selected by the logical AND of the value of the current menu symbol + and . This means, the lower limit can be downgraded due to the + presence of "if" . This behavior may seem weird, but we rely on + it. (The future of this behavior is undecided.) + - weak reverse dependencies: "imply" ["if" ] This is similar to "select" as it enforces a lower limit on another @@ -202,6 +208,10 @@ applicable everywhere (see syntax). imply BAR imply BAZ + Note: If "imply" is followed by "if" , the default of + will be the logical AND of the value of the current menu symbol and . + (The future of this behavior is undecided.) + - limiting menu display: "visible if" This attribute is only applicable to menu blocks, if the condition is -- cgit v1.2.3 From b01e1c030770ff3b4fe37fc7cc6bca03f594133f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Jun 2024 19:35:49 +0000 Subject: ipv6: fix possible race in __fib6_drop_pcpu_from() syzbot found a race in __fib6_drop_pcpu_from() [1] If compiler reads more than once (*ppcpu_rt), second read could read NULL, if another cpu clears the value in rt6_get_pcpu_route(). Add a READ_ONCE() to prevent this race. Also add rcu_read_lock()/rcu_read_unlock() because we rely on RCU protection while dereferencing pcpu_rt. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000012: 0000 [#1] PREEMPT SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000090-0x0000000000000097] CPU: 0 PID: 7543 Comm: kworker/u8:17 Not tainted 6.10.0-rc1-syzkaller-00013-g2bfcfd584ff5 #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/02/2024 Workqueue: netns cleanup_net RIP: 0010:__fib6_drop_pcpu_from.part.0+0x10a/0x370 net/ipv6/ip6_fib.c:984 Code: f8 48 c1 e8 03 80 3c 28 00 0f 85 16 02 00 00 4d 8b 3f 4d 85 ff 74 31 e8 74 a7 fa f7 49 8d bf 90 00 00 00 48 89 f8 48 c1 e8 03 <80> 3c 28 00 0f 85 1e 02 00 00 49 8b 87 90 00 00 00 48 8b 0c 24 48 RSP: 0018:ffffc900040df070 EFLAGS: 00010206 RAX: 0000000000000012 RBX: 0000000000000001 RCX: ffffffff89932e16 RDX: ffff888049dd1e00 RSI: ffffffff89932d7c RDI: 0000000000000091 RBP: dffffc0000000000 R08: 0000000000000005 R09: 0000000000000007 R10: 0000000000000001 R11: 0000000000000006 R12: ffff88807fa080b8 R13: fffffbfff1a9a07d R14: ffffed100ff41022 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8880b9200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000001b32c26000 CR3: 000000005d56e000 CR4: 00000000003526f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: __fib6_drop_pcpu_from net/ipv6/ip6_fib.c:966 [inline] fib6_drop_pcpu_from net/ipv6/ip6_fib.c:1027 [inline] fib6_purge_rt+0x7f2/0x9f0 net/ipv6/ip6_fib.c:1038 fib6_del_route net/ipv6/ip6_fib.c:1998 [inline] fib6_del+0xa70/0x17b0 net/ipv6/ip6_fib.c:2043 fib6_clean_node+0x426/0x5b0 net/ipv6/ip6_fib.c:2205 fib6_walk_continue+0x44f/0x8d0 net/ipv6/ip6_fib.c:2127 fib6_walk+0x182/0x370 net/ipv6/ip6_fib.c:2175 fib6_clean_tree+0xd7/0x120 net/ipv6/ip6_fib.c:2255 __fib6_clean_all+0x100/0x2d0 net/ipv6/ip6_fib.c:2271 rt6_sync_down_dev net/ipv6/route.c:4906 [inline] rt6_disable_ip+0x7ed/0xa00 net/ipv6/route.c:4911 addrconf_ifdown.isra.0+0x117/0x1b40 net/ipv6/addrconf.c:3855 addrconf_notify+0x223/0x19e0 net/ipv6/addrconf.c:3778 notifier_call_chain+0xb9/0x410 kernel/notifier.c:93 call_netdevice_notifiers_info+0xbe/0x140 net/core/dev.c:1992 call_netdevice_notifiers_extack net/core/dev.c:2030 [inline] call_netdevice_notifiers net/core/dev.c:2044 [inline] dev_close_many+0x333/0x6a0 net/core/dev.c:1585 unregister_netdevice_many_notify+0x46d/0x19f0 net/core/dev.c:11193 unregister_netdevice_many net/core/dev.c:11276 [inline] default_device_exit_batch+0x85b/0xae0 net/core/dev.c:11759 ops_exit_list+0x128/0x180 net/core/net_namespace.c:178 cleanup_net+0x5b7/0xbf0 net/core/net_namespace.c:640 process_one_work+0x9fb/0x1b60 kernel/workqueue.c:3231 process_scheduled_works kernel/workqueue.c:3312 [inline] worker_thread+0x6c8/0xf70 kernel/workqueue.c:3393 kthread+0x2c1/0x3a0 kernel/kthread.c:389 ret_from_fork+0x45/0x80 arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244 Fixes: d52d3997f843 ("ipv6: Create percpu rt6_info") Signed-off-by: Eric Dumazet Cc: Martin KaFai Lau Link: https://lore.kernel.org/r/20240604193549.981839-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv6/ip6_fib.c | 6 +++++- net/ipv6/route.c | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 31d77885bcae..6e57c03e3255 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -966,6 +966,7 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, if (!fib6_nh->rt6i_pcpu) return; + rcu_read_lock(); /* release the reference to this fib entry from * all of its cached pcpu routes */ @@ -974,7 +975,9 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, struct rt6_info *pcpu_rt; ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); - pcpu_rt = *ppcpu_rt; + + /* Paired with xchg() in rt6_get_pcpu_route() */ + pcpu_rt = READ_ONCE(*ppcpu_rt); /* only dropping the 'from' reference if the cached route * is using 'match'. The cached pcpu_rt->from only changes @@ -988,6 +991,7 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, fib6_info_release(from); } } + rcu_read_unlock(); } struct fib6_nh_pcpu_arg { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a504b88ec06b..f083d9faba6b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1409,6 +1409,7 @@ static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res) struct rt6_info *prev, **p; p = this_cpu_ptr(res->nh->rt6i_pcpu); + /* Paired with READ_ONCE() in __fib6_drop_pcpu_from() */ prev = xchg(p, NULL); if (prev) { dst_dev_put(&prev->dst); -- cgit v1.2.3 From 77a92660d8fe8d29503fae768d9f5eb529c88b36 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 4 Jun 2024 01:19:04 +0900 Subject: kconfig: remove wrong expr_trans_bool() expr_trans_bool() performs an incorrect transformation. [Test Code] config MODULES def_bool y modules config A def_bool y select C if B != n config B def_tristate m config C tristate [Result] CONFIG_MODULES=y CONFIG_A=y CONFIG_B=m CONFIG_C=m This output is incorrect because CONFIG_C=y is expected. Documentation/kbuild/kconfig-language.rst clearly explains the function of the '!=' operator: If the values of both symbols are equal, it returns 'n', otherwise 'y'. Therefore, the statement: select C if B != n should be equivalent to: select C if y Or, more simply: select C Hence, the symbol C should be selected by the value of A, which is 'y'. However, expr_trans_bool() wrongly transforms it to: select C if B Therefore, the symbol C is selected by (A && B), which is 'm'. The comment block of expr_trans_bool() correctly explains its intention: * bool FOO!=n => FOO ^^^^ If FOO is bool, FOO!=n can be simplified into FOO. This is correct. However, the actual code performs this transformation when FOO is tristate: if (e->left.sym->type == S_TRISTATE) { ^^^^^^^^^^ While it can be fixed to S_BOOLEAN, there is no point in doing so because expr_tranform() already transforms FOO!=n to FOO when FOO is bool. (see the "case E_UNEQUAL" part) expr_trans_bool() is wrong and unnecessary. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/expr.c | 29 ----------------------------- scripts/kconfig/expr.h | 1 - scripts/kconfig/menu.c | 2 -- 3 files changed, 32 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 4d95fce5f9a7..fcc190b67b6f 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -396,35 +396,6 @@ static struct expr *expr_eliminate_yn(struct expr *e) return e; } -/* - * bool FOO!=n => FOO - */ -struct expr *expr_trans_bool(struct expr *e) -{ - if (!e) - return NULL; - switch (e->type) { - case E_AND: - case E_OR: - case E_NOT: - e->left.expr = expr_trans_bool(e->left.expr); - e->right.expr = expr_trans_bool(e->right.expr); - break; - case E_UNEQUAL: - // FOO!=n -> FOO - if (e->left.sym->type == S_TRISTATE) { - if (e->right.sym == &symbol_no) { - e->type = E_SYMBOL; - e->right.sym = NULL; - } - } - break; - default: - ; - } - return e; -} - /* * e1 || e2 -> ? */ diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index fa50fc45622e..7c0c242318bc 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -284,7 +284,6 @@ void expr_free(struct expr *e); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); int expr_eq(struct expr *e1, struct expr *e2); tristate expr_calc_value(struct expr *e); -struct expr *expr_trans_bool(struct expr *e); struct expr *expr_eliminate_dups(struct expr *e); struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 53151c5a6028..eef9b63cdf11 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -398,8 +398,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) dep = expr_transform(dep); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); - if (menu->sym && menu->sym->type != S_TRISTATE) - dep = expr_trans_bool(dep); prop->visible.expr = dep; /* -- cgit v1.2.3 From a1708fda9388c83215f0be82ad47df10be6e1975 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 6 Jun 2024 10:06:58 +0200 Subject: ASoC: dt-bindings: convert amlogic,g12a-tohdmitx to dt-schema Convert text bindings to dt-schema format for the Amlogic TX HDMI control glue. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://msgid.link/r/20240606-topic-amlogic-upstream-bindings-convert-g12a-tohdmitx-v2-1-70d44fa30790@linaro.org Signed-off-by: Mark Brown --- .../bindings/sound/amlogic,g12a-tohdmitx.txt | 58 ---------------------- .../bindings/sound/amlogic,g12a-tohdmitx.yaml | 54 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 58 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt create mode 100644 Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt deleted file mode 100644 index 4e8cd7eb7cec..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt +++ /dev/null @@ -1,58 +0,0 @@ -* Amlogic HDMI Tx control glue - -Required properties: -- compatible: "amlogic,g12a-tohdmitx" or - "amlogic,sm1-tohdmitx" -- reg: physical base address of the controller and length of memory - mapped region. -- #sound-dai-cells: should be 1. -- resets: phandle to the dedicated reset line of the hdmitx glue. - -Example on the S905X2 SoC: - -tohdmitx: audio-controller@744 { - compatible = "amlogic,g12a-tohdmitx"; - reg = <0x0 0x744 0x0 0x4>; - #sound-dai-cells = <1>; - resets = <&clkc_audio AUD_RESET_TOHDMITX>; -}; - -Example of an 'amlogic,axg-sound-card': - -sound { - compatible = "amlogic,axg-sound-card"; - -[...] - - dai-link-x { - sound-dai = <&tdmif_a>; - dai-format = "i2s"; - dai-tdm-slot-tx-mask-0 = <1 1>; - - codec-0 { - sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>; - }; - - codec-1 { - sound-dai = <&external_dac>; - }; - }; - - dai-link-y { - sound-dai = <&tdmif_c>; - dai-format = "i2s"; - dai-tdm-slot-tx-mask-0 = <1 1>; - - codec { - sound-dai = <&tohdmitx TOHDMITX_I2S_IN_C>; - }; - }; - - dai-link-z { - sound-dai = <&tohdmitx TOHDMITX_I2S_OUT>; - - codec { - sound-dai = <&hdmi_tx>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml new file mode 100644 index 000000000000..b4b78475c5b8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,g12a-tohdmitx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic G12a HDMI TX Control Glue + +maintainers: + - Jerome Brunet + +allOf: + - $ref: dai-common.yaml# + +properties: + $nodename: + pattern: "^audio-controller@.*" + + compatible: + oneOf: + - items: + - const: amlogic,g12a-tohdmitx + - items: + - enum: + - amlogic,sm1-tohdmitx + - const: amlogic,g12a-tohdmitx + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + "#sound-dai-cells": + const: 1 + +required: + - compatible + - reg + - resets + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include + + tohdmitx: audio-controller@744 { + compatible = "amlogic,g12a-tohdmitx"; + reg = <0x744 0x4>; + resets = <&clkc_audio AUD_RESET_TOHDMITX>; + #sound-dai-cells = <1>; + }; -- cgit v1.2.3 From 0dcc53abf58d572d34c5313de85f607cd33fc691 Mon Sep 17 00:00:00 2001 From: Su Hui Date: Wed, 5 Jun 2024 11:47:43 +0800 Subject: net: ethtool: fix the error condition in ethtool_get_phy_stats_ethtool() Clang static checker (scan-build) warning: net/ethtool/ioctl.c:line 2233, column 2 Called function pointer is null (null dereference). Return '-EOPNOTSUPP' when 'ops->get_ethtool_phy_stats' is NULL to fix this typo error. Fixes: 201ed315f967 ("net/ethtool/ioctl: split ethtool_get_phy_stats into multiple helpers") Signed-off-by: Su Hui Reviewed-by: Przemek Kitszel Reviewed-by: Hariprasad Kelam Link: https://lore.kernel.org/r/20240605034742.921751-1-suhui@nfschina.com Signed-off-by: Paolo Abeni --- net/ethtool/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 5a55270aa86e..e645d751a5e8 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2220,7 +2220,7 @@ static int ethtool_get_phy_stats_ethtool(struct net_device *dev, const struct ethtool_ops *ops = dev->ethtool_ops; int n_stats, ret; - if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats) + if (!ops || !ops->get_sset_count || !ops->get_ethtool_phy_stats) return -EOPNOTSUPP; n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS); -- cgit v1.2.3 From 09fe2bfa6b83f865126ce3964744863f69a4a030 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 6 Jun 2024 21:14:45 +1000 Subject: ata: pata_macio: Fix max_segment_size with PAGE_SIZE == 64K The pata_macio driver advertises a max_segment_size of 0xff00, because the hardware doesn't cope with requests >= 64K. However the SCSI core requires max_segment_size to be at least PAGE_SIZE, which is a problem for pata_macio when the kernel is built with 64K pages. In older kernels the SCSI core would just increase the segment size to be equal to PAGE_SIZE, however since the commit tagged below it causes a warning and the device fails to probe: WARNING: CPU: 0 PID: 26 at block/blk-settings.c:202 .blk_validate_limits+0x2f8/0x35c CPU: 0 PID: 26 Comm: kworker/u4:1 Not tainted 6.10.0-rc1 #1 Hardware name: PowerMac7,2 PPC970 0x390202 PowerMac ... NIP .blk_validate_limits+0x2f8/0x35c LR .blk_alloc_queue+0xc0/0x2f8 Call Trace: .blk_alloc_queue+0xc0/0x2f8 .blk_mq_alloc_queue+0x60/0xf8 .scsi_alloc_sdev+0x208/0x3c0 .scsi_probe_and_add_lun+0x314/0x52c .__scsi_add_device+0x170/0x1a4 .ata_scsi_scan_host+0x2bc/0x3e4 .async_port_probe+0x6c/0xa0 .async_run_entry_fn+0x60/0x1bc .process_one_work+0x228/0x510 .worker_thread+0x360/0x530 .kthread+0x134/0x13c .start_kernel_thread+0x10/0x14 ... scsi_alloc_sdev: Allocation failure during SCSI scanning, some SCSI devices might not be configured Although the hardware can't cope with a 64K segment, the driver already deals with that internally by splitting large requests in pata_macio_qc_prep(). That is how the driver has managed to function until now on 64K kernels. So fix the driver to advertise a max_segment_size of 64K, which avoids the warning and keeps the SCSI core happy. Fixes: afd53a3d8528 ("scsi: core: Initialize scsi midlayer limits before allocating the queue") Reported-by: Guenter Roeck Closes: https://lore.kernel.org/all/ce2bf6af-4382-4fe1-b392-cc6829f5ceb2@roeck-us.net/ Reported-by: Doru Iorgulescu Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218858 Signed-off-by: Michael Ellerman Reviewed-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: John Garry Signed-off-by: Niklas Cassel --- drivers/ata/pata_macio.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 817838e2f70e..3cb455a32d92 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -915,10 +915,13 @@ static const struct scsi_host_template pata_macio_sht = { .sg_tablesize = MAX_DCMDS, /* We may not need that strict one */ .dma_boundary = ATA_DMA_BOUNDARY, - /* Not sure what the real max is but we know it's less than 64K, let's - * use 64K minus 256 + /* + * The SCSI core requires the segment size to cover at least a page, so + * for 64K page size kernels this must be at least 64K. However the + * hardware can't handle 64K, so pata_macio_qc_prep() will split large + * requests. */ - .max_segment_size = MAX_DBDMA_SEG, + .max_segment_size = SZ_64K, .device_configure = pata_macio_device_configure, .sdev_groups = ata_common_sdev_groups, .can_queue = ATA_DEF_QUEUE, -- cgit v1.2.3 From 41b02ea4c0adfcc6761fbfed42c3ce6b6412d881 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Wed, 5 Jun 2024 11:21:16 +0200 Subject: selftests: net: lib: support errexit with busywait If errexit is enabled ('set -e'), loopy_wait -- or busywait and others using it -- will stop after the first failure. Note that if the returned status of loopy_wait is checked, and even if errexit is enabled, Bash will not stop at the first error. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org Acked-by: Geliang Tang Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Hangbin Liu Link: https://lore.kernel.org/r/20240605-upstream-net-20240605-selftests-net-lib-fixes-v1-1-b3afadd368c9@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/lib.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index edc030e81a46..a422e10d3d3a 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -67,9 +67,7 @@ loopy_wait() while true do local out - out=$("$@") - local ret=$? - if ((!ret)); then + if out=$("$@"); then echo -n "$out" return 0 fi -- cgit v1.2.3 From 79322174bcc780b99795cb89d237b26006a8b94b Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Wed, 5 Jun 2024 11:21:17 +0200 Subject: selftests: net: lib: avoid error removing empty netns name If there is an error to create the first netns with 'setup_ns()', 'cleanup_ns()' will be called with an empty string as first parameter. The consequences is that 'cleanup_ns()' will try to delete an invalid netns, and wait 20 seconds if the netns list is empty. Instead of just checking if the name is not empty, convert the string separated by spaces to an array. Manipulating the array is cleaner, and calling 'cleanup_ns()' with an empty array will be a no-op. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org Acked-by: Geliang Tang Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Petr Machata Reviewed-by: Hangbin Liu Link: https://lore.kernel.org/r/20240605-upstream-net-20240605-selftests-net-lib-fixes-v1-2-b3afadd368c9@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/lib.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index a422e10d3d3a..e2f51102d7e1 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -15,7 +15,7 @@ ksft_xfail=2 ksft_skip=4 # namespace list created by setup_ns -NS_LIST="" +NS_LIST=() ############################################################################## # Helpers @@ -137,6 +137,7 @@ cleanup_ns() fi for ns in "$@"; do + [ -z "${ns}" ] && continue ip netns delete "${ns}" &> /dev/null if ! busywait $BUSYWAIT_TIMEOUT ip netns list \| grep -vq "^$ns$" &> /dev/null; then echo "Warn: Failed to remove namespace $ns" @@ -150,7 +151,7 @@ cleanup_ns() cleanup_all_ns() { - cleanup_ns $NS_LIST + cleanup_ns "${NS_LIST[@]}" } # setup netns with given names as prefix. e.g @@ -159,7 +160,7 @@ setup_ns() { local ns="" local ns_name="" - local ns_list="" + local ns_list=() local ns_exist= for ns_name in "$@"; do # Some test may setup/remove same netns multi times @@ -175,13 +176,13 @@ setup_ns() if ! ip netns add "$ns"; then echo "Failed to create namespace $ns_name" - cleanup_ns "$ns_list" + cleanup_ns "${ns_list[@]}" return $ksft_skip fi ip -n "$ns" link set lo up - ! $ns_exist && ns_list="$ns_list $ns" + ! $ns_exist && ns_list+=("$ns") done - NS_LIST="$NS_LIST $ns_list" + NS_LIST+=("${ns_list[@]}") } tc_rule_stats_get() -- cgit v1.2.3 From 84a8bc3ec225b28067b168e9410e452c83d706da Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Wed, 5 Jun 2024 11:21:18 +0200 Subject: selftests: net: lib: set 'i' as local Without this, the 'i' variable declared before could be overridden by accident, e.g. for i in "${@}"; do __ksft_status_merge "${i}" ## 'i' has been modified foo "${i}" ## using 'i' with an unexpected value done After a quick look, it looks like 'i' is currently not used after having been modified in __ksft_status_merge(), but still, better be safe than sorry. I saw this while modifying the same file, not because I suspected an issue somewhere. Fixes: 596c8819cb78 ("selftests: forwarding: Have RET track kselftest framework constants") Acked-by: Geliang Tang Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Hangbin Liu Link: https://lore.kernel.org/r/20240605-upstream-net-20240605-selftests-net-lib-fixes-v1-3-b3afadd368c9@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index e2f51102d7e1..9155c914c064 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -27,6 +27,7 @@ __ksft_status_merge() local -A weights local weight=0 + local i for i in "$@"; do weights[$i]=$((weight++)) done -- cgit v1.2.3 From f3a5367c679d31473d3fbb391675055b4792c309 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 6 Jun 2024 11:01:51 +0930 Subject: btrfs: protect folio::private when attaching extent buffer folios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [BUG] Since v6.8 there are rare kernel crashes reported by various people, the common factor is bad page status error messages like this: BUG: Bad page state in process kswapd0 pfn:d6e840 page: refcount:0 mapcount:0 mapping:000000007512f4f2 index:0x2796c2c7c pfn:0xd6e840 aops:btree_aops ino:1 flags: 0x17ffffe0000008(uptodate|node=0|zone=2|lastcpupid=0x3fffff) page_type: 0xffffffff() raw: 0017ffffe0000008 dead000000000100 dead000000000122 ffff88826d0be4c0 raw: 00000002796c2c7c 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: non-NULL mapping [CAUSE] Commit 09e6cef19c9f ("btrfs: refactor alloc_extent_buffer() to allocate-then-attach method") changes the sequence when allocating a new extent buffer. Previously we always called grab_extent_buffer() under mapping->i_private_lock, to ensure the safety on modification on folio::private (which is a pointer to extent buffer for regular sectorsize). This can lead to the following race: Thread A is trying to allocate an extent buffer at bytenr X, with 4 4K pages, meanwhile thread B is trying to release the page at X + 4K (the second page of the extent buffer at X). Thread A | Thread B -----------------------------------+------------------------------------- | btree_release_folio() | | This is for the page at X + 4K, | | Not page X. | | alloc_extent_buffer() | |- release_extent_buffer() |- filemap_add_folio() for the | | |- atomic_dec_and_test(eb->refs) | page at bytenr X (the first | | | | page). | | | | Which returned -EEXIST. | | | | | | | |- filemap_lock_folio() | | | | Returned the first page locked. | | | | | | | |- grab_extent_buffer() | | | | |- atomic_inc_not_zero() | | | | | Returned false | | | | |- folio_detach_private() | | |- folio_detach_private() for X | |- folio_test_private() | | |- folio_test_private() | Returned true | | | Returned true |- folio_put() | |- folio_put() Now there are two puts on the same folio at folio X, leading to refcount underflow of the folio X, and eventually causing the BUG_ON() on the page->mapping. The condition is not that easy to hit: - The release must be triggered for the middle page of an eb If the release is on the same first page of an eb, page lock would kick in and prevent the race. - folio_detach_private() has a very small race window It's only between folio_test_private() and folio_clear_private(). That's exactly when mapping->i_private_lock is used to prevent such race, and commit 09e6cef19c9f ("btrfs: refactor alloc_extent_buffer() to allocate-then-attach method") screwed that up. At that time, I thought the page lock would kick in as filemap_release_folio() also requires the page to be locked, but forgot the filemap_release_folio() only locks one page, not all pages of an extent buffer. [FIX] Move all the code requiring i_private_lock into attach_eb_folio_to_filemap(), so that everything is done with proper lock protection. Furthermore to prevent future problems, add an extra lockdep_assert_locked() to ensure we're holding the proper lock. To reproducer that is able to hit the race (takes a few minutes with instrumented code inserting delays to alloc_extent_buffer()): #!/bin/sh drop_caches () { while(true); do echo 3 > /proc/sys/vm/drop_caches echo 1 > /proc/sys/vm/compact_memory done } run_tar () { while(true); do for x in `seq 1 80` ; do tar cf /dev/zero /mnt > /dev/null & done wait done } mkfs.btrfs -f -d single -m single /dev/vda mount -o noatime /dev/vda /mnt # create 200,000 files, 1K each ./simoop -n 200000 -E -f 1k /mnt drop_caches & (run_tar) Reported-by: Linus Torvalds Link: https://lore.kernel.org/linux-btrfs/CAHk-=wgt362nGfScVOOii8cgKn2LVVHeOvOA7OBwg1OwbuJQcw@mail.gmail.com/ Reported-by: Mikhail Gavrilov Link: https://lore.kernel.org/lkml/CABXGCsPktcHQOvKTbPaTwegMExije=Gpgci5NW=hqORo-s7diA@mail.gmail.com/ Reported-by: Toralf Förster Link: https://lore.kernel.org/linux-btrfs/e8b3311c-9a75-4903-907f-fc0f7a3fe423@gmx.de/ Reported-by: syzbot+f80b066392366b4af85e@syzkaller.appspotmail.com Fixes: 09e6cef19c9f ("btrfs: refactor alloc_extent_buffer() to allocate-then-attach method") CC: stable@vger.kernel.org # 6.8+ CC: Chris Mason Reviewed-by: Filipe Manana Reviewed-by: Josef Bacik Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 60 +++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 597387e9f040..f688fab55251 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3689,6 +3689,8 @@ static struct extent_buffer *grab_extent_buffer( struct folio *folio = page_folio(page); struct extent_buffer *exists; + lockdep_assert_held(&page->mapping->i_private_lock); + /* * For subpage case, we completely rely on radix tree to ensure we * don't try to insert two ebs for the same bytenr. So here we always @@ -3756,13 +3758,14 @@ static int check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) * The caller needs to free the existing folios and retry using the same order. */ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, + struct btrfs_subpage *prealloc, struct extent_buffer **found_eb_ret) { struct btrfs_fs_info *fs_info = eb->fs_info; struct address_space *mapping = fs_info->btree_inode->i_mapping; const unsigned long index = eb->start >> PAGE_SHIFT; - struct folio *existing_folio; + struct folio *existing_folio = NULL; int ret; ASSERT(found_eb_ret); @@ -3774,12 +3777,14 @@ retry: ret = filemap_add_folio(mapping, eb->folios[i], index + i, GFP_NOFS | __GFP_NOFAIL); if (!ret) - return 0; + goto finish; existing_folio = filemap_lock_folio(mapping, index + i); /* The page cache only exists for a very short time, just retry. */ - if (IS_ERR(existing_folio)) + if (IS_ERR(existing_folio)) { + existing_folio = NULL; goto retry; + } /* For now, we should only have single-page folios for btree inode. */ ASSERT(folio_nr_pages(existing_folio) == 1); @@ -3790,14 +3795,13 @@ retry: return -EAGAIN; } - if (fs_info->nodesize < PAGE_SIZE) { - /* - * We're going to reuse the existing page, can drop our page - * and subpage structure now. - */ +finish: + spin_lock(&mapping->i_private_lock); + if (existing_folio && fs_info->nodesize < PAGE_SIZE) { + /* We're going to reuse the existing page, can drop our folio now. */ __free_page(folio_page(eb->folios[i], 0)); eb->folios[i] = existing_folio; - } else { + } else if (existing_folio) { struct extent_buffer *existing_eb; existing_eb = grab_extent_buffer(fs_info, @@ -3805,6 +3809,7 @@ retry: if (existing_eb) { /* The extent buffer still exists, we can use it directly. */ *found_eb_ret = existing_eb; + spin_unlock(&mapping->i_private_lock); folio_unlock(existing_folio); folio_put(existing_folio); return 1; @@ -3813,6 +3818,22 @@ retry: __free_page(folio_page(eb->folios[i], 0)); eb->folios[i] = existing_folio; } + eb->folio_size = folio_size(eb->folios[i]); + eb->folio_shift = folio_shift(eb->folios[i]); + /* Should not fail, as we have preallocated the memory. */ + ret = attach_extent_buffer_folio(eb, eb->folios[i], prealloc); + ASSERT(!ret); + /* + * To inform we have an extra eb under allocation, so that + * detach_extent_buffer_page() won't release the folio private when the + * eb hasn't been inserted into radix tree yet. + * + * The ref will be decreased when the eb releases the page, in + * detach_extent_buffer_page(). Thus needs no special handling in the + * error path. + */ + btrfs_folio_inc_eb_refs(fs_info, eb->folios[i]); + spin_unlock(&mapping->i_private_lock); return 0; } @@ -3824,7 +3845,6 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, int attached = 0; struct extent_buffer *eb; struct extent_buffer *existing_eb = NULL; - struct address_space *mapping = fs_info->btree_inode->i_mapping; struct btrfs_subpage *prealloc = NULL; u64 lockdep_owner = owner_root; bool page_contig = true; @@ -3890,7 +3910,7 @@ reallocate: for (int i = 0; i < num_folios; i++) { struct folio *folio; - ret = attach_eb_folio_to_filemap(eb, i, &existing_eb); + ret = attach_eb_folio_to_filemap(eb, i, prealloc, &existing_eb); if (ret > 0) { ASSERT(existing_eb); goto out; @@ -3927,24 +3947,6 @@ reallocate: * and free the allocated page. */ folio = eb->folios[i]; - eb->folio_size = folio_size(folio); - eb->folio_shift = folio_shift(folio); - spin_lock(&mapping->i_private_lock); - /* Should not fail, as we have preallocated the memory */ - ret = attach_extent_buffer_folio(eb, folio, prealloc); - ASSERT(!ret); - /* - * To inform we have extra eb under allocation, so that - * detach_extent_buffer_page() won't release the folio private - * when the eb hasn't yet been inserted into radix tree. - * - * The ref will be decreased when the eb released the page, in - * detach_extent_buffer_page(). - * Thus needs no special handling in error path. - */ - btrfs_folio_inc_eb_refs(fs_info, folio); - spin_unlock(&mapping->i_private_lock); - WARN_ON(btrfs_folio_test_dirty(fs_info, folio, eb->start, eb->len)); /* -- cgit v1.2.3 From 96c965667b9dbbd713acdffa95ebab8c225f8595 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Wed, 5 Jun 2024 13:58:09 -0400 Subject: kbuild: explicitly run mksysmap as sed script from link-vmlinux.sh In commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script"), the mksysmap script was transformed into a sed script, made directly executable with "#!/bin/sed -f". Apparently, the path to sed is different on NixOS. The shebang can't use the env command, otherwise the "sed -f" command would be treated as a single argument. This can be solved with the -S flag, but that is a GNU extension. Explicitly use sed instead of relying on the executable shebang to fix NixOS builds without breaking build environments using Busybox. Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script") Reported-by: Kent Overstreet Signed-off-by: Richard Acayan Reviewed-by: Nathan Chancellor Tested-by: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Masahiro Yamada --- scripts/link-vmlinux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 46ce5d04dbeb..518c70b8db50 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -193,7 +193,7 @@ kallsyms_step() mksysmap() { info NM ${2} - ${NM} -n "${1}" | "${srctree}/scripts/mksysmap" > "${2}" + ${NM} -n "${1}" | sed -f "${srctree}/scripts/mksysmap" > "${2}" } sorttable() -- cgit v1.2.3 From 9185afeac2a3dcce8300a5684291a43c2838cfd6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 7 Jun 2024 03:36:12 +0900 Subject: modpost: do not warn about missing MODULE_DESCRIPTION() for vmlinux.o Building with W=1 incorrectly emits the following warning: WARNING: modpost: missing MODULE_DESCRIPTION() in vmlinux.o This check should apply only to modules. Fixes: 1fffe7a34c89 ("script: modpost: emit a warning when the description is missing") Signed-off-by: Masahiro Yamada Reviewed-by: Vincenzo Palazzo --- scripts/mod/modpost.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 937294ff164f..f48d72d22dc2 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1647,10 +1647,11 @@ static void read_symbols(const char *modname) namespace = get_next_modinfo(&info, "import_ns", namespace); } + + if (extra_warn && !get_modinfo(&info, "description")) + warn("missing MODULE_DESCRIPTION() in %s\n", modname); } - if (extra_warn && !get_modinfo(&info, "description")) - warn("missing MODULE_DESCRIPTION() in %s\n", modname); for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = remove_dot(info.strtab + sym->st_name); -- cgit v1.2.3 From 02c418774f76a0a36a6195c9dbf8971eb4130a15 Mon Sep 17 00:00:00 2001 From: Enzo Matsumiya Date: Thu, 6 Jun 2024 13:13:13 -0300 Subject: smb: client: fix deadlock in smb2_find_smb_tcon() Unlock cifs_tcp_ses_lock before calling cifs_put_smb_ses() to avoid such deadlock. Cc: stable@vger.kernel.org Signed-off-by: Enzo Matsumiya Reviewed-by: Shyam Prasad N Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/smb2transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index 02135a605305..1476c445cadc 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -216,8 +216,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) } tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); if (!tcon) { - cifs_put_smb_ses(ses); spin_unlock(&cifs_tcp_ses_lock); + cifs_put_smb_ses(ses); return NULL; } spin_unlock(&cifs_tcp_ses_lock); -- cgit v1.2.3 From a88d60903696c01de577558080ec4fc738a70475 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 31 May 2024 15:53:42 +0100 Subject: cifs: Don't advance the I/O iterator before terminating subrequest There's now no need to make sure subreq->io_iter is advanced to match subreq->transferred before calling one of the netfs subrequest termination functions as the check has been removed netfslib and the iterator is reset prior to retrying a subreq. Fixes: 3ee1a1fc3981 ("cifs: Cut over to using netfslib") Signed-off-by: David Howells cc: Steve French cc: Paulo Alcantara cc: Shyam Prasad N cc: Rohith Surabattula cc: Jeff Layton cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org cc: linux-mm@kvack.org Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 993ac36c3d58..38a06e8a0f90 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4577,8 +4577,6 @@ smb2_readv_callback(struct mid_q_entry *mid) if (rdata->subreq.start < rdata->subreq.rreq->i_size) rdata->result = 0; } - if (rdata->result == 0 || rdata->result == -EAGAIN) - iov_iter_advance(&rdata->subreq.io_iter, rdata->got_bytes); rdata->credits.value = 0; netfs_subreq_terminated(&rdata->subreq, (rdata->result == 0 || rdata->result == -EAGAIN) ? @@ -4789,7 +4787,6 @@ smb2_writev_callback(struct mid_q_entry *mid) wdata->result = -ENOSPC; else wdata->subreq.len = written; - iov_iter_advance(&wdata->subreq.io_iter, written); break; case MID_REQUEST_SUBMITTED: case MID_RETRY_NEEDED: -- cgit v1.2.3 From 64054eb716db52e4246527dc9414377c5bc5b01d Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Thu, 6 Jun 2024 20:23:50 -0700 Subject: gpio: add missing MODULE_DESCRIPTION() macros On x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpio/gpio-gw-pld.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpio/gpio-mc33880.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpio/gpio-pcf857x.o Add the missing invocations of the MODULE_DESCRIPTION() macro, including the one missing in gpio-pl061.c, which is not built for x86. Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240606-md-drivers-gpio-v1-1-cb42d240ca5c@quicinc.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-gw-pld.c | 1 + drivers/gpio/gpio-mc33880.c | 1 + drivers/gpio/gpio-pcf857x.c | 1 + drivers/gpio/gpio-pl061.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 899335da93c7..7e29a2d8de1a 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -130,5 +130,6 @@ static struct i2c_driver gw_pld_driver = { }; module_i2c_driver(gw_pld_driver); +MODULE_DESCRIPTION("Gateworks I2C PLD GPIO expander"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linus Walleij "); diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index cd9b16dbe1a9..94f6fefc011b 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -168,5 +168,6 @@ static void __exit mc33880_exit(void) module_exit(mc33880_exit); MODULE_AUTHOR("Mocean Laboratories "); +MODULE_DESCRIPTION("MC33880 high-side/low-side switch GPIO driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 53b69abe6787..7c57eaeb0afe 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -438,5 +438,6 @@ static void __exit pcf857x_exit(void) } module_exit(pcf857x_exit); +MODULE_DESCRIPTION("Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 9fc1f3dd4190..a211a02d4b4a 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -438,4 +438,5 @@ static struct amba_driver pl061_gpio_driver = { }; module_amba_driver(pl061_gpio_driver); +MODULE_DESCRIPTION("Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 89e1ee118d6f0ee6bd6e80d8fe08839875daa241 Mon Sep 17 00:00:00 2001 From: Andrew Ballance Date: Sun, 2 Jun 2024 03:50:23 -0500 Subject: hid: asus: asus_report_fixup: fix potential read out of bounds syzbot reported a potential read out of bounds in asus_report_fixup. this patch adds checks so that a read out of bounds will not occur Signed-off-by: Andrew Ballance Reported-by: Closes: https://syzkaller.appspot.com/bug?extid=07762f019fd03d01f04c Fixes: 59d2f5b7392e ("HID: asus: fix more n-key report descriptors if n-key quirked") Link: https://lore.kernel.org/r/20240602085023.1720492-1-andrewjballance@gmail.com Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-asus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 02de2bf4f790..37e6d25593c2 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -1204,8 +1204,8 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, } /* match many more n-key devices */ - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - for (int i = 0; i < *rsize + 1; i++) { + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && *rsize > 15) { + for (int i = 0; i < *rsize - 15; i++) { /* offset to the count from 0x5a report part always 14 */ if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a && rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) { -- cgit v1.2.3 From 0c7dd00de018ff70b3452c424901816e26366a8a Mon Sep 17 00:00:00 2001 From: Aseda Aboagye Date: Tue, 4 Jun 2024 23:10:47 +0000 Subject: input: Add event code for accessibility key HUTRR116 added support for a new usage titled "System Accessibility Binding" which toggles a system-wide bound accessibility UI or command. This commit simply adds a new event code for the usage. Signed-off-by: Aseda Aboagye Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/Zl-e97O9nvudco5z@google.com Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-debug.c | 1 + drivers/hid/hid-input.c | 1 + include/uapi/linux/input-event-codes.h | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 87a961cae775..322ed63a98ab 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3366,6 +3366,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable", [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", + [KEY_ACCESSIBILITY] = "Accessibility", [KEY_DICTATE] = "Dictate", [KEY_MICMUTE] = "MicrophoneMute", [KEY_BRIGHTNESS_MIN] = "BrightnessMin", diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e03d300d2bac..d5a6e89c3086 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -836,6 +836,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ switch (usage->hid & 0xf) { case 0x9: map_key_clear(KEY_MICMUTE); break; + case 0xa: map_key_clear(KEY_ACCESSIBILITY); break; default: goto ignore; } break; diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 03edf2ccdf6c..39f11ec676fa 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -618,6 +618,7 @@ #define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ +#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ -- cgit v1.2.3 From 22d6d060ac77955291deb43efc2f3f4f9632c6cb Mon Sep 17 00:00:00 2001 From: Aseda Aboagye Date: Tue, 4 Jun 2024 23:16:32 +0000 Subject: input: Add support for "Do Not Disturb" HUTRR94 added support for a new usage titled "System Do Not Disturb" which toggles a system-wide Do Not Disturb setting. This commit simply adds a new event code for the usage. Signed-off-by: Aseda Aboagye Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/Zl-gUHE70s7wCAoB@google.com Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-debug.c | 1 + drivers/hid/hid-input.c | 8 ++++++++ include/uapi/linux/input-event-codes.h | 1 + 3 files changed, 10 insertions(+) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 322ed63a98ab..d5abfe652fb5 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3367,6 +3367,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", [KEY_ACCESSIBILITY] = "Accessibility", + [KEY_DO_NOT_DISTURB] = "DoNotDisturb", [KEY_DICTATE] = "Dictate", [KEY_MICMUTE] = "MicrophoneMute", [KEY_BRIGHTNESS_MIN] = "BrightnessMin", diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d5a6e89c3086..8bb16e9b94aa 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -833,6 +833,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; } + if ((usage->hid & 0xf0) == 0x90) { /* SystemControl*/ + switch (usage->hid & 0xf) { + case 0xb: map_key_clear(KEY_DO_NOT_DISTURB); break; + default: goto ignore; + } + break; + } + if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ switch (usage->hid & 0xf) { case 0x9: map_key_clear(KEY_MICMUTE); break; diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 39f11ec676fa..a4206723f503 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -619,6 +619,7 @@ #define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ #define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */ +#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ -- cgit v1.2.3 From 8d3ae46c64336b538898af76b4f504dfbe89b886 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 May 2024 16:48:15 +0200 Subject: dt-bindings: HID: i2c-hid: add dedicated Ilitek ILI2901 schema The Ilitek ILI2901 touch screen controller was apparently incorrectly added to the Elan eKTH6915 schema simply because it also has a reset gpio and is currently managed by the Elan driver in Linux. The two controllers are not related even if an unfortunate wording in the commit message adding the Ilitek compatible made it sound like they were. Add a dedicated schema for the ILI2901 which does not specify the I2C address (which is likely 0x41 rather than 0x10 as for other Ilitek touch controllers) to avoid cluttering the Elan schema with unrelated devices and to make it easier to find the correct schema when adding further Ilitek controllers. Fixes: d74ac6f60a7e ("dt-bindings: HID: i2c-hid: elan: Introduce Ilitek ili2901") Cc: Zhengqiao Xia Reviewed-by: Krzysztof Kozlowski Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20240507144821.12275-2-johan+linaro@kernel.org Signed-off-by: Benjamin Tissoires --- .../devicetree/bindings/input/elan,ekth6915.yaml | 1 - .../devicetree/bindings/input/ilitek,ili2901.yaml | 66 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/input/ilitek,ili2901.yaml diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index dc4ac41f2441..c1fcf8c90c24 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -20,7 +20,6 @@ properties: compatible: enum: - elan,ekth6915 - - ilitek,ili2901 reg: const: 0x10 diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml b/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml new file mode 100644 index 000000000000..1abeec768d79 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/ilitek,ili2901.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI2901 touchscreen controller + +maintainers: + - Jiri Kosina + +description: + Supports the Ilitek ILI2901 touchscreen controller. + This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + enum: + - ilitek,ili2901 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + panel: true + + reset-gpios: + maxItems: 1 + + vcc33-supply: true + + vccio-supply: true + +required: + - compatible + - reg + - interrupts + - vcc33-supply + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili2901"; + reg = <0x41>; + + interrupt-parent = <&tlmm>; + interrupts = <9 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_ts>; + }; + }; -- cgit v1.2.3 From 07fc16fa55522520f5e4b2e35c72650f11079c35 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 May 2024 16:48:16 +0200 Subject: dt-bindings: HID: i2c-hid: elan: add Elan eKTH5015M Add a compatible string for the Elan eKTH5015M touch controller. Judging from the current binding and commit bd3cba00dcc6 ("HID: i2c-hid: elan: Add support for Elan eKTH6915 i2c-hid touchscreens"), eKTH5015M appears to be compatible with eKTH6915. Notably the power-on sequence is the same. While at it, drop a redundant label from the example. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20240507144821.12275-3-johan+linaro@kernel.org Signed-off-by: Benjamin Tissoires --- Documentation/devicetree/bindings/input/elan,ekth6915.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index c1fcf8c90c24..be84f7ed0abc 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -18,8 +18,12 @@ allOf: properties: compatible: - enum: - - elan,ekth6915 + oneOf: + - items: + - enum: + - elan,ekth5015m + - const: elan,ekth6915 + - const: elan,ekth6915 reg: const: 0x10 @@ -57,8 +61,8 @@ examples: #address-cells = <1>; #size-cells = <0>; - ap_ts: touchscreen@10 { - compatible = "elan,ekth6915"; + touchscreen@10 { + compatible = "elan,ekth5015m", "elan,ekth6915"; reg = <0x10>; interrupt-parent = <&tlmm>; -- cgit v1.2.3 From e538d4b85b8f1e3534dfbb42c2273f18bbb59d6e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 May 2024 16:48:17 +0200 Subject: dt-bindings: HID: i2c-hid: elan: add 'no-reset-on-power-off' property When the power supply is shared with other peripherals the reset line can be wired in such a way that it can remain deasserted regardless of whether the supply is on or not. This is important as it can be used to avoid holding the controller in reset for extended periods of time when it remains powered, something which can lead to increased power consumption. Leaving reset deasserted also avoids leaking current through the reset circuitry pull-up resistors. Add a new 'no-reset-on-power-off' devicetree property which can be used by the OS to determine when reset needs to be asserted on power down. Note that this property can also be used when the supply cannot be turned off by the OS at all. Signed-off-by: Johan Hovold Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20240507144821.12275-4-johan+linaro@kernel.org Signed-off-by: Benjamin Tissoires --- Documentation/devicetree/bindings/input/elan,ekth6915.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index be84f7ed0abc..a62916d07a08 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -36,6 +36,12 @@ properties: reset-gpios: description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. + no-reset-on-power-off: + type: boolean + description: + Reset line is wired so that it can (and should) be left deasserted when + the power supply is off. + vcc33-supply: description: The 3.3V supply to the touchscreen. -- cgit v1.2.3 From 0eafc58f2194dbd01d4be40f99a697681171995b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 May 2024 16:48:18 +0200 Subject: HID: i2c-hid: elan: fix reset suspend current leakage The Elan eKTH5015M touch controller found on the Lenovo ThinkPad X13s shares the VCC33 supply with other peripherals that may remain powered during suspend (e.g. when enabled as wakeup sources). The reset line is also wired so that it can be left deasserted when the supply is off. This is important as it avoids holding the controller in reset for extended periods of time when it remains powered, which can lead to increased power consumption, and also avoids leaking current through the X13s reset circuitry during suspend (and after driver unbind). Use the new 'no-reset-on-power-off' devicetree property to determine when reset needs to be asserted on power down. Notably this also avoids wasting power on machine variants without a touchscreen for which the driver would otherwise exit probe with reset asserted. Fixes: bd3cba00dcc6 ("HID: i2c-hid: elan: Add support for Elan eKTH6915 i2c-hid touchscreens") Cc: # 6.0 Cc: Douglas Anderson Tested-by: Steev Klimaszewski Signed-off-by: Johan Hovold Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20240507144821.12275-5-johan+linaro@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/i2c-hid/i2c-hid-of-elan.c | 59 ++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index 5b91fb106cfc..091e37933225 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -31,6 +31,7 @@ struct i2c_hid_of_elan { struct regulator *vcc33; struct regulator *vccio; struct gpio_desc *reset_gpio; + bool no_reset_on_power_off; const struct elan_i2c_hid_chip_data *chip_data; }; @@ -40,17 +41,17 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) container_of(ops, struct i2c_hid_of_elan, ops); int ret; + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->vcc33) { ret = regulator_enable(ihid_elan->vcc33); if (ret) - return ret; + goto err_deassert_reset; } ret = regulator_enable(ihid_elan->vccio); - if (ret) { - regulator_disable(ihid_elan->vcc33); - return ret; - } + if (ret) + goto err_disable_vcc33; if (ihid_elan->chip_data->post_power_delay_ms) msleep(ihid_elan->chip_data->post_power_delay_ms); @@ -60,6 +61,15 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms); return 0; + +err_disable_vcc33: + if (ihid_elan->vcc33) + regulator_disable(ihid_elan->vcc33); +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static void elan_i2c_hid_power_down(struct i2chid_ops *ops) @@ -67,7 +77,14 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) struct i2c_hid_of_elan *ihid_elan = container_of(ops, struct i2c_hid_of_elan, ops); - gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + /* + * Do not assert reset when the hardware allows for it to remain + * deasserted regardless of the state of the (shared) power supply to + * avoid wasting power when the supply is left on. + */ + if (!ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms) msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms); @@ -79,6 +96,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) static int i2c_hid_of_elan_probe(struct i2c_client *client) { struct i2c_hid_of_elan *ihid_elan; + int ret; ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); if (!ihid_elan) @@ -93,21 +111,38 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client) if (IS_ERR(ihid_elan->reset_gpio)) return PTR_ERR(ihid_elan->reset_gpio); + ihid_elan->no_reset_on_power_off = of_property_read_bool(client->dev.of_node, + "no-reset-on-power-off"); + ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); - if (IS_ERR(ihid_elan->vccio)) - return PTR_ERR(ihid_elan->vccio); + if (IS_ERR(ihid_elan->vccio)) { + ret = PTR_ERR(ihid_elan->vccio); + goto err_deassert_reset; + } ihid_elan->chip_data = device_get_match_data(&client->dev); if (ihid_elan->chip_data->main_supply_name) { ihid_elan->vcc33 = devm_regulator_get(&client->dev, ihid_elan->chip_data->main_supply_name); - if (IS_ERR(ihid_elan->vcc33)) - return PTR_ERR(ihid_elan->vcc33); + if (IS_ERR(ihid_elan->vcc33)) { + ret = PTR_ERR(ihid_elan->vcc33); + goto err_deassert_reset; + } } - return i2c_hid_core_probe(client, &ihid_elan->ops, - ihid_elan->chip_data->hid_descriptor_address, 0); + ret = i2c_hid_core_probe(client, &ihid_elan->ops, + ihid_elan->chip_data->hid_descriptor_address, 0); + if (ret) + goto err_deassert_reset; + + return 0; + +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { -- cgit v1.2.3 From a3a5a37efba11b7cf1a86abe7bccfbcdb521764e Mon Sep 17 00:00:00 2001 From: Louis Dalibard Date: Fri, 7 Jun 2024 16:53:43 +0200 Subject: HID: Ignore battery for ELAN touchscreens 2F2C and 4116 At least ASUS Zenbook 14 (2023) and ASUS Zenbook 14 Pro (2023) are affected. The touchscreen reports a battery status of 0% and jumps to 1% when a stylus is used. The device ID was added and the battery ignore quirk was enabled for it. [jkosina@suse.com: reformatted changelog a bit] Signed-off-by: Louis Dalibard Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 2 ++ drivers/hid/hid-input.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 61d2a21affa2..72d56ee7ce1b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -423,6 +423,8 @@ #define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF #define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8 #define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82 +#define I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN 0x2F2C +#define I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN 0x4116 #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8bb16e9b94aa..c9094a4f281e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -377,6 +377,10 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), -- cgit v1.2.3 From 83a7eefedc9b56fe7bfeff13b6c7356688ffa670 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 9 Jun 2024 14:19:43 -0700 Subject: Linux 6.10-rc3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7f921ae547f1..925a75b8ba7d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 10 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3 From 27173bb0b64461acf4e00f1bae3b15d8d2348c14 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:29 +0530 Subject: ASoC: dt-bindings: document wcd937x Audio Codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the Qualcomm WCD9370/WCD9375 Audio Codec and the Soundwire devices than can be found on Qualcomm QCM6490 based platforms. The Qualcomm WCD9370/WCD9375 Audio Codec communicates with the host SoC over 2 Soundwire links to provide: - 3 TX ADC paths with 4 differential AMIC inputs - 6 DMIC inputs that are shared with AMIC input - 4 Microphone BIAS - RX paths with 4 PAs – HPHL/R, EAR and AUX - Stereo Headphone output - MBHC engine for Headset Detection Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240524035535.3119208-2-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- .../bindings/sound/qcom,wcd937x-sdw.yaml | 91 ++++++++++++++++++++++ .../devicetree/bindings/sound/qcom,wcd937x.yaml | 82 +++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml new file mode 100644 index 000000000000..d3cf8f59cb23 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd937x-sdw.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SoundWire Slave devices on WCD9370/WCD9375 + +maintainers: + - Srinivas Kandagatla + +description: | + Qualcomm WCD9370/WCD9375 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire slave devices. This bindings is for the + slave devices. + +properties: + compatible: + const: sdw20217010a00 + + reg: + maxItems: 1 + + qcom,tx-port-mapping: + description: | + Specifies static port mapping between device and host tx ports. + In the order of the device port index which are adc1_port, adc23_port, + dmic03_mbhc_port, dmic46_port. + Supports maximum 4 tx soundwire ports. + + WCD9370 TX Port 1 (ADC1) <=> SWR2 Port 2 + WCD9370 TX Port 2 (ADC2, 3) <=> SWR2 Port 2 + WCD9370 TX Port 3 (DMIC0,1,2,3 & MBHC) <=> SWR2 Port 3 + WCD9370 TX Port 4 (DMIC4,5,6,7) <=> SWR2 Port 4 + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + items: + enum: [1, 2, 3, 4] + + qcom,rx-port-mapping: + description: | + Specifies static port mapping between device and host rx ports. + In the order of device port index which are hph_port, clsh_port, + comp_port, lo_port, dsd port. + Supports maximum 5 rx soundwire ports. + + WCD9370 RX Port 1 (HPH_L/R) <==> SWR1 Port 1 (HPH_L/R) + WCD9370 RX Port 2 (CLSH) <==> SWR1 Port 2 (CLSH) + WCD9370 RX Port 3 (COMP_L/R) <==> SWR1 Port 3 (COMP_L/R) + WCD9370 RX Port 4 (LO) <==> SWR1 Port 4 (LO) + WCD9370 RX Port 5 (DSD_L/R) <==> SWR1 Port 5 (DSD) + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 5 + maxItems: 5 + items: + enum: [1, 2, 3, 4, 5] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soundwire@3210000 { + reg = <0x03210000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_rx: codec@0,4 { + compatible = "sdw20217010a00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; + }; + + soundwire@3230000 { + reg = <0x03230000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_tx: codec@0,3 { + compatible = "sdw20217010a00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 2 3 4>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml new file mode 100644 index 000000000000..de397d879acc --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd937x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCD9370/WCD9375 Audio Codec + +maintainers: + - Srinivas Kandagatla + +description: + Qualcomm WCD9370/WCD9375 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire slave devices. + +allOf: + - $ref: dai-common.yaml# + - $ref: qcom,wcd93xx-common.yaml# + +properties: + compatible: + oneOf: + - const: qcom,wcd9370-codec + - items: + - const: qcom,wcd9375-codec + - const: qcom,wcd9370-codec + + vdd-px-supply: + description: A reference to the 1.8V I/O supply + +required: + - compatible + - vdd-px-supply + +unevaluatedProperties: false + +examples: + - | + #include + codec { + compatible = "qcom,wcd9370-codec"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&wcd_reset_n>; + pinctrl-1 = <&wcd_reset_n_sleep>; + reset-gpios = <&tlmm 83 GPIO_ACTIVE_HIGH>; + vdd-buck-supply = <&vreg_l17b_1p8>; + vdd-rxtx-supply = <&vreg_l18b_1p8>; + vdd-px-supply = <&vreg_l18b_1p8>; + vdd-mic-bias-supply = <&vreg_bob>; + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,rx-device = <&wcd937x_rx>; + qcom,tx-device = <&wcd937x_tx>; + #sound-dai-cells = <1>; + }; + + /* ... */ + + soundwire@3210000 { + reg = <0x03210000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_rx: codec@0,4 { + compatible = "sdw20217010a00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; + }; + + soundwire@3230000 { + reg = <0x03230000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_tx: codec@0,3 { + compatible = "sdw20217010a00"; + reg = <0 3>; + qcom,tx-port-mapping = <1 2 3 4>; + }; + }; +... -- cgit v1.2.3 From c99a515ff15380ec5f8827049914145ba908e8da Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:30 +0530 Subject: ASoC: codecs: wcd937x-sdw: add SoundWire driver This patch adds support to SoundWire devices on WCD9370/WCD9375 Codec. The WCD9370/WCD9375 Soundwire devices will be used by the main WCD9370/WCD9375 Audio Codec driver to access registers and configure Soundwire RX and TX ports. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-3-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x-sdw.c | 1139 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wcd937x.h | 653 +++++++++++++++++++++++ 2 files changed, 1792 insertions(+) create mode 100644 sound/soc/codecs/wcd937x-sdw.c create mode 100644 sound/soc/codecs/wcd937x.h diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c new file mode 100644 index 000000000000..a45025bf96c6 --- /dev/null +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -0,0 +1,1139 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd937x.h" + +static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_COMP_L, WCD937X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_COMP_R, WCD937X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_LO, WCD937X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DSD_L, WCD937X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), +}; + +static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC0, WCD937X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC1, WCD937X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_MBHC, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD937X_DMIC2, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD937X_DMIC3, WCD937X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD937X_DMIC4, WCD937X_DMIC_4_6_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC5, WCD937X_DMIC_4_6_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_DMIC6, WCD937X_DMIC_4_6_PORT, BIT(2)), +}; + +static struct sdw_dpn_prop wcd937x_dpn_prop[WCD937X_MAX_SWR_PORTS] = { + { + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 8, + .simple_ch_prep_sm = true, + }, { + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 5, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd937x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); +} +EXPORT_SYMBOL_GPL(wcd937x_sdw_device_get); + +int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD937X_MAX_SWR_PORTS; i++) { + ch_mask = wcd->port_config[i].ch_mask; + if (!ch_mask) + continue; + + for_each_set_bit(j, &ch_mask, 4) + wcd->sconfig.ch_count++; + + port_config[wcd->active_ports] = wcd->port_config[i]; + wcd->active_ports++; + } + + wcd->sconfig.bps = 1; + wcd->sconfig.frame_rate = params_rate(params); + wcd->sconfig.direction = wcd->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX; + wcd->sconfig.type = SDW_STREAM_PCM; + + return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, + &port_config[0], wcd->active_ports, + wcd->sruntime); +} +EXPORT_SYMBOL_GPL(wcd937x_sdw_hw_params); + +static int wcd9370_update_status(struct sdw_slave *slave, enum sdw_slave_status status) +{ + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + + if (wcd->regmap && status == SDW_SLAVE_ATTACHED) { + /* Write out any cached changes that happened between probe and attach */ + regcache_cache_only(wcd->regmap, false); + return regcache_sync(wcd->regmap); + } + + return 0; +} + +/* + * Handle Soundwire out-of-band interrupt event by triggering + * the first irq of the slave_irq irq domain, which then will + * be handled by the regmap_irq threaded irq. + * Looping is to ensure no interrupts were missed in the process. + */ +static int wcd9370_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + struct irq_domain *slave_irq = wcd->slave_irq; + u32 sts1, sts2, sts3; + + do { + handle_nested_irq(irq_find_mapping(slave_irq, 0)); + regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd937x_defaults[] = { + /* Default values except for Read-Only & Volatile registers */ + { WCD937X_ANA_BIAS, 0x00 }, + { WCD937X_ANA_RX_SUPPLIES, 0x00 }, + { WCD937X_ANA_HPH, 0x0c }, + { WCD937X_ANA_EAR, 0x00 }, + { WCD937X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD937X_ANA_TX_CH1, 0x20 }, + { WCD937X_ANA_TX_CH2, 0x00 }, + { WCD937X_ANA_TX_CH3, 0x20 }, + { WCD937X_ANA_TX_CH3_HPF, 0x00 }, + { WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MBHC_MECH, 0x39 }, + { WCD937X_ANA_MBHC_ELECT, 0x08 }, + { WCD937X_ANA_MBHC_ZDET, 0x00 }, + { WCD937X_ANA_MBHC_BTN0, 0x00 }, + { WCD937X_ANA_MBHC_BTN1, 0x10 }, + { WCD937X_ANA_MBHC_BTN2, 0x20 }, + { WCD937X_ANA_MBHC_BTN3, 0x30 }, + { WCD937X_ANA_MBHC_BTN4, 0x40 }, + { WCD937X_ANA_MBHC_BTN5, 0x50 }, + { WCD937X_ANA_MBHC_BTN6, 0x60 }, + { WCD937X_ANA_MBHC_BTN7, 0x70 }, + { WCD937X_ANA_MICB1, 0x10 }, + { WCD937X_ANA_MICB2, 0x10 }, + { WCD937X_ANA_MICB2_RAMP, 0x00 }, + { WCD937X_ANA_MICB3, 0x10 }, + { WCD937X_BIAS_CTL, 0x2a }, + { WCD937X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD937X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD937X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD937X_MBHC_CTL_CLK, 0x00 }, + { WCD937X_MBHC_CTL_ANA, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD937X_MBHC_CTL_BCS, 0x00 }, + { WCD937X_MBHC_TEST_CTL, 0x00 }, + { WCD937X_LDOH_MODE, 0x2b }, + { WCD937X_LDOH_BIAS, 0x68 }, + { WCD937X_LDOH_STB_LOADS, 0x00 }, + { WCD937X_LDOH_SLOWRAMP, 0x50 }, + { WCD937X_MICB1_TEST_CTL_1, 0x1a }, + { WCD937X_MICB1_TEST_CTL_2, 0x18 }, + { WCD937X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD937X_MICB2_TEST_CTL_1, 0x1a }, + { WCD937X_MICB2_TEST_CTL_2, 0x18 }, + { WCD937X_MICB2_TEST_CTL_3, 0xa4 }, + { WCD937X_MICB3_TEST_CTL_1, 0x1a }, + { WCD937X_MICB3_TEST_CTL_2, 0x18 }, + { WCD937X_MICB3_TEST_CTL_3, 0xa4 }, + { WCD937X_TX_COM_ADC_VCM, 0x39 }, + { WCD937X_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD937X_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD937X_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD937X_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD937X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD937X_TX_1_2_TEST_EN, 0xcc }, + { WCD937X_TX_1_2_ADC_IB, 0x09 }, + { WCD937X_TX_1_2_ATEST_REFCTL, 0x0a }, + { WCD937X_TX_1_2_TEST_CTL, 0x38 }, + { WCD937X_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD937X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_3_TEST_EN, 0xcc }, + { WCD937X_TX_3_ADC_IB, 0x09 }, + { WCD937X_TX_3_ATEST_REFCTL, 0x0a }, + { WCD937X_TX_3_TEST_CTL, 0x38 }, + { WCD937X_TX_3_TEST_BLK_EN, 0xff }, + { WCD937X_TX_3_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_3_SPARE_MONO, 0x00 }, + { WCD937X_CLASSH_MODE_1, 0x40 }, + { WCD937X_CLASSH_MODE_2, 0x3a }, + { WCD937X_CLASSH_MODE_3, 0x00 }, + { WCD937X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD937X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD937X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD937X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD937X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD937X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD937X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD937X_CLASSH_SPARE, 0x00 }, + { WCD937X_FLYBACK_EN, 0x4e }, + { WCD937X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD937X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD937X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD937X_FLYBACK_VNEG_CTRL_4, 0x7f }, + { WCD937X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD937X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD937X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD937X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD937X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD937X_FLYBACK_CTRL_1, 0x65 }, + { WCD937X_FLYBACK_TEST_CTL, 0x00 }, + { WCD937X_RX_AUX_SW_CTL, 0x00 }, + { WCD937X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD937X_RX_TIMER_DIV, 0x32 }, + { WCD937X_RX_OCP_CTL, 0x1f }, + { WCD937X_RX_OCP_COUNT, 0x77 }, + { WCD937X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD937X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD937X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD937X_RX_BIAS_HPH_PA, 0xaa }, + { WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD937X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD937X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD937X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD937X_RX_BIAS_AUX_DAC, 0xa0 }, + { WCD937X_RX_BIAS_AUX_AMP, 0xaa }, + { WCD937X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD937X_RX_BIAS_MISC, 0x00 }, + { WCD937X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD937X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD937X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD937X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD937X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD937X_HPH_CNP_EN, 0x80 }, + { WCD937X_HPH_CNP_WG_CTL, 0x9a }, + { WCD937X_HPH_CNP_WG_TIME, 0x14 }, + { WCD937X_HPH_OCP_CTL, 0x28 }, + { WCD937X_HPH_AUTO_CHOP, 0x16 }, + { WCD937X_HPH_CHOP_CTL, 0x83 }, + { WCD937X_HPH_PA_CTL1, 0x46 }, + { WCD937X_HPH_PA_CTL2, 0x50 }, + { WCD937X_HPH_L_EN, 0x80 }, + { WCD937X_HPH_L_TEST, 0xe0 }, + { WCD937X_HPH_L_ATEST, 0x50 }, + { WCD937X_HPH_R_EN, 0x80 }, + { WCD937X_HPH_R_TEST, 0xe0 }, + { WCD937X_HPH_R_ATEST, 0x54 }, + { WCD937X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD937X_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD937X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD937X_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD937X_HPH_REFBUFF_LP_CTL, 0x0e }, + { WCD937X_HPH_L_DAC_CTL, 0x20 }, + { WCD937X_HPH_R_DAC_CTL, 0x20 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0x19 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xa0 }, + { WCD937X_EAR_EAR_EN_REG, 0x22 }, + { WCD937X_EAR_EAR_PA_CON, 0x44 }, + { WCD937X_EAR_EAR_SP_CON, 0xdb }, + { WCD937X_EAR_EAR_DAC_CON, 0x80 }, + { WCD937X_EAR_EAR_CNP_FSM_CON, 0xb2 }, + { WCD937X_EAR_TEST_CTL, 0x00 }, + { WCD937X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD937X_SLEEP_CTL, 0x16 }, + { WCD937X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD937X_MBHC_NEW_CTL_1, 0x02 }, + { WCD937X_MBHC_NEW_CTL_2, 0x05 }, + { WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD937X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD937X_TX_NEW_TX_CH2_SEL, 0x00 }, + { WCD937X_AUX_AUXPA, 0x00 }, + { WCD937X_LDORXTX_MODE, 0x0c }, + { WCD937X_LDORXTX_CONFIG, 0x10 }, + { WCD937X_DIE_CRACK_DIE_CRK_DET_EN, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD937X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, + { WCD937X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD937X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER1, 0xfe }, + { WCD937X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER3, 0x4e }, + { WCD937X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD937X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xa8 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00 }, + { WCD937X_AUX_INT_EN_REG, 0x00 }, + { WCD937X_AUX_INT_PA_CTRL, 0x06 }, + { WCD937X_AUX_INT_SP_CTRL, 0xd2 }, + { WCD937X_AUX_INT_DAC_CTRL, 0x80 }, + { WCD937X_AUX_INT_CLK_CTRL, 0x50 }, + { WCD937X_AUX_INT_TEST_CTRL, 0x00 }, + { WCD937X_AUX_INT_STATUS_REG, 0x00 }, + { WCD937X_AUX_INT_MISC, 0x00 }, + { WCD937X_LDORXTX_INT_BIAS, 0x6e }, + { WCD937X_LDORXTX_INT_STB_LOADS_DTEST, 0x50 }, + { WCD937X_LDORXTX_INT_TEST0, 0x1c }, + { WCD937X_LDORXTX_INT_STARTUP_TIMER, 0xff }, + { WCD937X_LDORXTX_INT_TEST1, 0x1f }, + { WCD937X_LDORXTX_INT_STATUS, 0x00 }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02 }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60 }, + { WCD937X_DIGITAL_PAGE_REGISTER, 0x00 }, + { WCD937X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD937X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD937X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD937X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_RX0_CTL, 0xfc }, + { WCD937X_DIGITAL_CDC_RX1_CTL, 0xfc }, + { WCD937X_DIGITAL_CDC_RX2_CTL, 0xfc }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD937X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD937X_DIGITAL_CDC_RX_DELAY_CTL, 0x66 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R1, 0x4b }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R2, 0x26 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R3, 0x32 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x57 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R5, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R6, 0x7c }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R7, 0x57 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_0, 0xab }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1c }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A6_0, 0xaa }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A7_0, 0xe3 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_0, 0x69 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_1, 0x54 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_2, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_3, 0x15 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R1, 0xa4 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R2, 0xb5 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R3, 0x86 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R4, 0x85 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R5, 0xaa }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R6, 0xe2 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R7, 0x62 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xfc }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD937X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD937X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_AMIC_CTL, 0x07 }, + { WCD937X_DIGITAL_CDC_DMIC_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC3_CTL, 0x01 }, + { WCD937X_DIGITAL_EFUSE_CTL, 0x2b }, + { WCD937X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD937X_DIGITAL_INTR_MODE, 0x00 }, + { WCD937X_DIGITAL_INTR_MASK_0, 0xff }, + { WCD937X_DIGITAL_INTR_MASK_1, 0xff }, + { WCD937X_DIGITAL_INTR_MASK_2, 0x0f }, + { WCD937X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX0_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX1_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX2_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_TX_CTL, 0x00 }, + { WCD937X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD937X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 }, + { WCD937X_DIGITAL_PAD_INP_DIS_0, 0x00 }, + { WCD937X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD937X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f }, + { WCD937X_DIGITAL_TX_DATA_EDGE_CTL, 0x10 }, + { WCD937X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD937X_DIGITAL_SSP_DBG, 0x00 }, + { WCD937X_DIGITAL_SPARE_0, 0x00 }, + { WCD937X_DIGITAL_SPARE_1, 0x00 }, + { WCD937X_DIGITAL_SPARE_2, 0x00 }, +}; + +static bool wcd937x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_BIAS: + case WCD937X_ANA_RX_SUPPLIES: + case WCD937X_ANA_HPH: + case WCD937X_ANA_EAR: + case WCD937X_ANA_EAR_COMPANDER_CTL: + case WCD937X_ANA_TX_CH1: + case WCD937X_ANA_TX_CH2: + case WCD937X_ANA_TX_CH3: + case WCD937X_ANA_TX_CH3_HPF: + case WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD937X_ANA_MICB3_DSP_EN_LOGIC: + case WCD937X_ANA_MBHC_MECH: + case WCD937X_ANA_MBHC_ELECT: + case WCD937X_ANA_MBHC_ZDET: + case WCD937X_ANA_MBHC_BTN0: + case WCD937X_ANA_MBHC_BTN1: + case WCD937X_ANA_MBHC_BTN2: + case WCD937X_ANA_MBHC_BTN3: + case WCD937X_ANA_MBHC_BTN4: + case WCD937X_ANA_MBHC_BTN5: + case WCD937X_ANA_MBHC_BTN6: + case WCD937X_ANA_MBHC_BTN7: + case WCD937X_ANA_MICB1: + case WCD937X_ANA_MICB2: + case WCD937X_ANA_MICB2_RAMP: + case WCD937X_ANA_MICB3: + case WCD937X_BIAS_CTL: + case WCD937X_BIAS_VBG_FINE_ADJ: + case WCD937X_LDOL_VDDCX_ADJUST: + case WCD937X_LDOL_DISABLE_LDOL: + case WCD937X_MBHC_CTL_CLK: + case WCD937X_MBHC_CTL_ANA: + case WCD937X_MBHC_CTL_SPARE_1: + case WCD937X_MBHC_CTL_SPARE_2: + case WCD937X_MBHC_CTL_BCS: + case WCD937X_MBHC_TEST_CTL: + case WCD937X_LDOH_MODE: + case WCD937X_LDOH_BIAS: + case WCD937X_LDOH_STB_LOADS: + case WCD937X_LDOH_SLOWRAMP: + case WCD937X_MICB1_TEST_CTL_1: + case WCD937X_MICB1_TEST_CTL_2: + case WCD937X_MICB1_TEST_CTL_3: + case WCD937X_MICB2_TEST_CTL_1: + case WCD937X_MICB2_TEST_CTL_2: + case WCD937X_MICB2_TEST_CTL_3: + case WCD937X_MICB3_TEST_CTL_1: + case WCD937X_MICB3_TEST_CTL_2: + case WCD937X_MICB3_TEST_CTL_3: + case WCD937X_TX_COM_ADC_VCM: + case WCD937X_TX_COM_BIAS_ATEST: + case WCD937X_TX_COM_ADC_INT1_IB: + case WCD937X_TX_COM_ADC_INT2_IB: + case WCD937X_TX_COM_TXFE_DIV_CTL: + case WCD937X_TX_COM_TXFE_DIV_START: + case WCD937X_TX_COM_TXFE_DIV_STOP_9P6M: + case WCD937X_TX_COM_TXFE_DIV_STOP_12P288M: + case WCD937X_TX_1_2_TEST_EN: + case WCD937X_TX_1_2_ADC_IB: + case WCD937X_TX_1_2_ATEST_REFCTL: + case WCD937X_TX_1_2_TEST_CTL: + case WCD937X_TX_1_2_TEST_BLK_EN: + case WCD937X_TX_1_2_TXFE_CLKDIV: + case WCD937X_TX_3_TEST_EN: + case WCD937X_TX_3_ADC_IB: + case WCD937X_TX_3_ATEST_REFCTL: + case WCD937X_TX_3_TEST_CTL: + case WCD937X_TX_3_TEST_BLK_EN: + case WCD937X_TX_3_TXFE_CLKDIV: + case WCD937X_CLASSH_MODE_1: + case WCD937X_CLASSH_MODE_2: + case WCD937X_CLASSH_MODE_3: + case WCD937X_CLASSH_CTRL_VCL_1: + case WCD937X_CLASSH_CTRL_VCL_2: + case WCD937X_CLASSH_CTRL_CCL_1: + case WCD937X_CLASSH_CTRL_CCL_2: + case WCD937X_CLASSH_CTRL_CCL_3: + case WCD937X_CLASSH_CTRL_CCL_4: + case WCD937X_CLASSH_CTRL_CCL_5: + case WCD937X_CLASSH_BUCK_TMUX_A_D: + case WCD937X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD937X_CLASSH_SPARE: + case WCD937X_FLYBACK_EN: + case WCD937X_FLYBACK_VNEG_CTRL_1: + case WCD937X_FLYBACK_VNEG_CTRL_2: + case WCD937X_FLYBACK_VNEG_CTRL_3: + case WCD937X_FLYBACK_VNEG_CTRL_4: + case WCD937X_FLYBACK_VNEG_CTRL_5: + case WCD937X_FLYBACK_VNEG_CTRL_6: + case WCD937X_FLYBACK_VNEG_CTRL_7: + case WCD937X_FLYBACK_VNEG_CTRL_8: + case WCD937X_FLYBACK_VNEG_CTRL_9: + case WCD937X_FLYBACK_VNEGDAC_CTRL_1: + case WCD937X_FLYBACK_VNEGDAC_CTRL_2: + case WCD937X_FLYBACK_VNEGDAC_CTRL_3: + case WCD937X_FLYBACK_CTRL_1: + case WCD937X_FLYBACK_TEST_CTL: + case WCD937X_RX_AUX_SW_CTL: + case WCD937X_RX_PA_AUX_IN_CONN: + case WCD937X_RX_TIMER_DIV: + case WCD937X_RX_OCP_CTL: + case WCD937X_RX_OCP_COUNT: + case WCD937X_RX_BIAS_EAR_DAC: + case WCD937X_RX_BIAS_EAR_AMP: + case WCD937X_RX_BIAS_HPH_LDO: + case WCD937X_RX_BIAS_HPH_PA: + case WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD937X_RX_BIAS_HPH_RDAC_LDO: + case WCD937X_RX_BIAS_HPH_CNP1: + case WCD937X_RX_BIAS_HPH_LOWPOWER: + case WCD937X_RX_BIAS_AUX_DAC: + case WCD937X_RX_BIAS_AUX_AMP: + case WCD937X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD937X_RX_BIAS_MISC: + case WCD937X_RX_BIAS_BUCK_RST: + case WCD937X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD937X_RX_BIAS_FLYB_ERRAMP: + case WCD937X_RX_BIAS_FLYB_BUFF: + case WCD937X_RX_BIAS_FLYB_MID_RST: + case WCD937X_HPH_CNP_EN: + case WCD937X_HPH_CNP_WG_CTL: + case WCD937X_HPH_CNP_WG_TIME: + case WCD937X_HPH_OCP_CTL: + case WCD937X_HPH_AUTO_CHOP: + case WCD937X_HPH_CHOP_CTL: + case WCD937X_HPH_PA_CTL1: + case WCD937X_HPH_PA_CTL2: + case WCD937X_HPH_L_EN: + case WCD937X_HPH_L_TEST: + case WCD937X_HPH_L_ATEST: + case WCD937X_HPH_R_EN: + case WCD937X_HPH_R_TEST: + case WCD937X_HPH_R_ATEST: + case WCD937X_HPH_RDAC_CLK_CTL1: + case WCD937X_HPH_RDAC_CLK_CTL2: + case WCD937X_HPH_RDAC_LDO_CTL: + case WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD937X_HPH_REFBUFF_UHQA_CTL: + case WCD937X_HPH_REFBUFF_LP_CTL: + case WCD937X_HPH_L_DAC_CTL: + case WCD937X_HPH_R_DAC_CTL: + case WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL: + case WCD937X_HPH_SURGE_HPHLR_SURGE_EN: + case WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1: + case WCD937X_EAR_EAR_EN_REG: + case WCD937X_EAR_EAR_PA_CON: + case WCD937X_EAR_EAR_SP_CON: + case WCD937X_EAR_EAR_DAC_CON: + case WCD937X_EAR_EAR_CNP_FSM_CON: + case WCD937X_EAR_TEST_CTL: + case WCD937X_HPH_NEW_ANA_HPH2: + case WCD937X_HPH_NEW_ANA_HPH3: + case WCD937X_SLEEP_CTL: + case WCD937X_SLEEP_WATCHDOG_CTL: + case WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD937X_MBHC_NEW_CTL_1: + case WCD937X_MBHC_NEW_CTL_2: + case WCD937X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD937X_MBHC_NEW_ZDET_ANA_CTL: + case WCD937X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD937X_TX_NEW_TX_CH2_SEL: + case WCD937X_AUX_AUXPA: + case WCD937X_LDORXTX_MODE: + case WCD937X_LDORXTX_CONFIG: + case WCD937X_DIE_CRACK_DIE_CRK_DET_EN: + case WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD937X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD937X_HPH_NEW_INT_PA_MISC1: + case WCD937X_HPH_NEW_INT_PA_MISC2: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD937X_HPH_NEW_INT_HPH_TIMER1: + case WCD937X_HPH_NEW_INT_HPH_TIMER2: + case WCD937X_HPH_NEW_INT_HPH_TIMER3: + case WCD937X_HPH_NEW_INT_HPH_TIMER4: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD937X_MBHC_NEW_INT_SPARE_2: + case WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON: + case WCD937X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD937X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS: + case WCD937X_AUX_INT_EN_REG: + case WCD937X_AUX_INT_PA_CTRL: + case WCD937X_AUX_INT_SP_CTRL: + case WCD937X_AUX_INT_DAC_CTRL: + case WCD937X_AUX_INT_CLK_CTRL: + case WCD937X_AUX_INT_TEST_CTRL: + case WCD937X_AUX_INT_MISC: + case WCD937X_LDORXTX_INT_BIAS: + case WCD937X_LDORXTX_INT_STB_LOADS_DTEST: + case WCD937X_LDORXTX_INT_TEST0: + case WCD937X_LDORXTX_INT_STARTUP_TIMER: + case WCD937X_LDORXTX_INT_TEST1: + case WCD937X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD937X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1: + case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2: + case WCD937X_DIGITAL_CDC_RST_CTL: + case WCD937X_DIGITAL_TOP_CLK_CFG: + case WCD937X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD937X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD937X_DIGITAL_SWR_RST_EN: + case WCD937X_DIGITAL_CDC_PATH_MODE: + case WCD937X_DIGITAL_CDC_RX_RST: + case WCD937X_DIGITAL_CDC_RX0_CTL: + case WCD937X_DIGITAL_CDC_RX1_CTL: + case WCD937X_DIGITAL_CDC_RX2_CTL: + case WCD937X_DIGITAL_DEM_BYPASS_DATA0: + case WCD937X_DIGITAL_DEM_BYPASS_DATA1: + case WCD937X_DIGITAL_DEM_BYPASS_DATA2: + case WCD937X_DIGITAL_DEM_BYPASS_DATA3: + case WCD937X_DIGITAL_CDC_COMP_CTL_0: + case WCD937X_DIGITAL_CDC_RX_DELAY_CTL: + case WCD937X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD937X_DIGITAL_CDC_HPH_DSM_R1: + case WCD937X_DIGITAL_CDC_HPH_DSM_R2: + case WCD937X_DIGITAL_CDC_HPH_DSM_R3: + case WCD937X_DIGITAL_CDC_HPH_DSM_R4: + case WCD937X_DIGITAL_CDC_HPH_DSM_R5: + case WCD937X_DIGITAL_CDC_HPH_DSM_R6: + case WCD937X_DIGITAL_CDC_HPH_DSM_R7: + case WCD937X_DIGITAL_CDC_AUX_DSM_A1_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A1_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A2_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A2_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A3_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A3_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A4_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A4_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A5_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A5_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A6_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A7_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_2: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_3: + case WCD937X_DIGITAL_CDC_AUX_DSM_R1: + case WCD937X_DIGITAL_CDC_AUX_DSM_R2: + case WCD937X_DIGITAL_CDC_AUX_DSM_R3: + case WCD937X_DIGITAL_CDC_AUX_DSM_R4: + case WCD937X_DIGITAL_CDC_AUX_DSM_R5: + case WCD937X_DIGITAL_CDC_AUX_DSM_R6: + case WCD937X_DIGITAL_CDC_AUX_DSM_R7: + case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2: + case WCD937X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD937X_DIGITAL_CDC_AUX_GAIN_CTL: + case WCD937X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD937X_DIGITAL_CDC_SWR_CLH: + case WCD937X_DIGITAL_SWR_CLH_BYP: + case WCD937X_DIGITAL_CDC_TX0_CTL: + case WCD937X_DIGITAL_CDC_TX1_CTL: + case WCD937X_DIGITAL_CDC_TX2_CTL: + case WCD937X_DIGITAL_CDC_TX_RST: + case WCD937X_DIGITAL_CDC_REQ_CTL: + case WCD937X_DIGITAL_CDC_AMIC_CTL: + case WCD937X_DIGITAL_CDC_DMIC_CTL: + case WCD937X_DIGITAL_CDC_DMIC1_CTL: + case WCD937X_DIGITAL_CDC_DMIC2_CTL: + case WCD937X_DIGITAL_CDC_DMIC3_CTL: + case WCD937X_DIGITAL_EFUSE_CTL: + case WCD937X_DIGITAL_EFUSE_PRG_CTL: + case WCD937X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD937X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD937X_DIGITAL_PDM_WD_CTL0: + case WCD937X_DIGITAL_PDM_WD_CTL1: + case WCD937X_DIGITAL_PDM_WD_CTL2: + case WCD937X_DIGITAL_INTR_MODE: + case WCD937X_DIGITAL_INTR_MASK_0: + case WCD937X_DIGITAL_INTR_MASK_1: + case WCD937X_DIGITAL_INTR_MASK_2: + case WCD937X_DIGITAL_INTR_CLEAR_0: + case WCD937X_DIGITAL_INTR_CLEAR_1: + case WCD937X_DIGITAL_INTR_CLEAR_2: + case WCD937X_DIGITAL_INTR_LEVEL_0: + case WCD937X_DIGITAL_INTR_LEVEL_1: + case WCD937X_DIGITAL_INTR_LEVEL_2: + case WCD937X_DIGITAL_INTR_SET_0: + case WCD937X_DIGITAL_INTR_SET_1: + case WCD937X_DIGITAL_INTR_SET_2: + case WCD937X_DIGITAL_INTR_TEST_0: + case WCD937X_DIGITAL_INTR_TEST_1: + case WCD937X_DIGITAL_INTR_TEST_2: + case WCD937X_DIGITAL_CDC_CONN_RX0_CTL: + case WCD937X_DIGITAL_CDC_CONN_RX1_CTL: + case WCD937X_DIGITAL_CDC_CONN_RX2_CTL: + case WCD937X_DIGITAL_CDC_CONN_TX_CTL: + case WCD937X_DIGITAL_LOOP_BACK_MODE: + case WCD937X_DIGITAL_SWR_DAC_TEST: + case WCD937X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD937X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD937X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD937X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD937X_DIGITAL_SWR_HM_TEST: + case WCD937X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD937X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD937X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD937X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD937X_DIGITAL_PAD_INP_DIS_0: + case WCD937X_DIGITAL_PAD_INP_DIS_1: + case WCD937X_DIGITAL_DRIVE_STRENGTH_0: + case WCD937X_DIGITAL_DRIVE_STRENGTH_1: + case WCD937X_DIGITAL_DRIVE_STRENGTH_2: + case WCD937X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD937X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD937X_DIGITAL_GPIO_MODE: + case WCD937X_DIGITAL_PIN_CTL_OE: + case WCD937X_DIGITAL_PIN_CTL_DATA_0: + case WCD937X_DIGITAL_PIN_CTL_DATA_1: + case WCD937X_DIGITAL_PIN_STATUS_0: + case WCD937X_DIGITAL_PIN_STATUS_1: + case WCD937X_DIGITAL_DIG_DEBUG_CTL: + case WCD937X_DIGITAL_DIG_DEBUG_EN: + case WCD937X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD937X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD937X_DIGITAL_SSP_DBG: + case WCD937X_DIGITAL_MODE_STATUS_0: + case WCD937X_DIGITAL_MODE_STATUS_1: + case WCD937X_DIGITAL_SPARE_0: + case WCD937X_DIGITAL_SPARE_1: + case WCD937X_DIGITAL_SPARE_2: + return true; + } + + return false; +} + +static bool wcd937x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_MBHC_RESULT_1: + case WCD937X_ANA_MBHC_RESULT_2: + case WCD937X_ANA_MBHC_RESULT_3: + case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD937X_TX_1_2_SAR2_ERR: + case WCD937X_TX_1_2_SAR1_ERR: + case WCD937X_TX_3_SPARE_MONO: + case WCD937X_TX_3_SAR1_ERR: + case WCD937X_HPH_L_STATUS: + case WCD937X_HPH_R_STATUS: + case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS: + case WCD937X_EAR_STATUS_REG_1: + case WCD937X_EAR_STATUS_REG_2: + case WCD937X_MBHC_NEW_FSM_STATUS: + case WCD937X_MBHC_NEW_ADC_RESULT: + case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT: + case WCD937X_AUX_INT_STATUS_REG: + case WCD937X_LDORXTX_INT_STATUS: + case WCD937X_DIGITAL_CHIP_ID0: + case WCD937X_DIGITAL_CHIP_ID1: + case WCD937X_DIGITAL_CHIP_ID2: + case WCD937X_DIGITAL_CHIP_ID3: + case WCD937X_DIGITAL_EFUSE_T_DATA_0: + case WCD937X_DIGITAL_EFUSE_T_DATA_1: + case WCD937X_DIGITAL_INTR_STATUS_0: + case WCD937X_DIGITAL_INTR_STATUS_1: + case WCD937X_DIGITAL_INTR_STATUS_2: + case WCD937X_DIGITAL_EFUSE_REG_0: + case WCD937X_DIGITAL_EFUSE_REG_1: + case WCD937X_DIGITAL_EFUSE_REG_2: + case WCD937X_DIGITAL_EFUSE_REG_3: + case WCD937X_DIGITAL_EFUSE_REG_4: + case WCD937X_DIGITAL_EFUSE_REG_5: + case WCD937X_DIGITAL_EFUSE_REG_6: + case WCD937X_DIGITAL_EFUSE_REG_7: + case WCD937X_DIGITAL_EFUSE_REG_8: + case WCD937X_DIGITAL_EFUSE_REG_9: + case WCD937X_DIGITAL_EFUSE_REG_10: + case WCD937X_DIGITAL_EFUSE_REG_11: + case WCD937X_DIGITAL_EFUSE_REG_12: + case WCD937X_DIGITAL_EFUSE_REG_13: + case WCD937X_DIGITAL_EFUSE_REG_14: + case WCD937X_DIGITAL_EFUSE_REG_15: + case WCD937X_DIGITAL_EFUSE_REG_16: + case WCD937X_DIGITAL_EFUSE_REG_17: + case WCD937X_DIGITAL_EFUSE_REG_18: + case WCD937X_DIGITAL_EFUSE_REG_19: + case WCD937X_DIGITAL_EFUSE_REG_20: + case WCD937X_DIGITAL_EFUSE_REG_21: + case WCD937X_DIGITAL_EFUSE_REG_22: + case WCD937X_DIGITAL_EFUSE_REG_23: + case WCD937X_DIGITAL_EFUSE_REG_24: + case WCD937X_DIGITAL_EFUSE_REG_25: + case WCD937X_DIGITAL_EFUSE_REG_26: + case WCD937X_DIGITAL_EFUSE_REG_27: + case WCD937X_DIGITAL_EFUSE_REG_28: + case WCD937X_DIGITAL_EFUSE_REG_29: + case WCD937X_DIGITAL_EFUSE_REG_30: + case WCD937X_DIGITAL_EFUSE_REG_31: + return true; + } + + return wcd937x_rdwr_register(dev, reg); +} + +static bool wcd937x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_MBHC_RESULT_1: + case WCD937X_ANA_MBHC_RESULT_2: + case WCD937X_ANA_MBHC_RESULT_3: + case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD937X_TX_1_2_SAR1_ERR: + case WCD937X_TX_1_2_SAR2_ERR: + case WCD937X_TX_3_SAR1_ERR: + case WCD937X_HPH_L_STATUS: + case WCD937X_HPH_R_STATUS: + case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS: + case WCD937X_EAR_STATUS_REG_1: + case WCD937X_EAR_STATUS_REG_2: + case WCD937X_MBHC_NEW_FSM_STATUS: + case WCD937X_MBHC_NEW_ADC_RESULT: + case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT: + case WCD937X_DIGITAL_INTR_STATUS_0: + case WCD937X_DIGITAL_INTR_STATUS_1: + case WCD937X_DIGITAL_INTR_STATUS_2: + case WCD937X_DIGITAL_SWR_HM_TEST: + case WCD937X_DIGITAL_PIN_STATUS_0: + case WCD937X_DIGITAL_PIN_STATUS_1: + case WCD937X_DIGITAL_MODE_STATUS_0: + case WCD937X_DIGITAL_MODE_STATUS_1: + return true; + } + return false; +} + +static const struct regmap_config wcd937x_regmap_config = { + .name = "wcd937x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd937x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd937x_defaults), + .max_register = WCD937X_MAX_REGISTER, + .readable_reg = wcd937x_readable_register, + .writeable_reg = wcd937x_rdwr_register, + .volatile_reg = wcd937x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9370_slave_ops = { + .update_status = wcd9370_update_status, + .interrupt_callback = wcd9370_interrupt_callback, +}; + +static int wcd937x_sdw_component_bind(struct device *dev, + struct device *master, void *data) +{ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static void wcd937x_sdw_component_unbind(struct device *dev, + struct device *master, void *data) +{ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); +} + +static const struct component_ops wcd937x_sdw_component_ops = { + .bind = wcd937x_sdw_component_bind, + .unbind = wcd937x_sdw_component_unbind, +}; + +static int wcd9370_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd937x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /* Port map index starts at 0, however the data port for this codec start at index 1 */ + if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + wcd->is_tx = true; + ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping", + &pdev->m_port_map[1], + WCD937X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD937X_MAX_SWR_PORTS); + } + if (ret < 0) + dev_info(dev, "Error getting static port mapping for %s (%d)\n", + wcd->is_tx ? "TX" : "RX", ret); + + wcd->sdev = pdev; + dev_set_drvdata(dev, wcd); + + pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | + SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd937x_dpn_prop; + wcd->ch_info = &wcd937x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + + wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Regmap init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wcd->regmap, true); + } else { + pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd937x_dpn_prop; + wcd->ch_info = &wcd937x_sdw_rx_ch_info[0]; + } + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return component_add(dev, &wcd937x_sdw_component_ops); +} + +static int wcd9370_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + + component_del(dev, &wcd937x_sdw_component_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + return 0; +} + +static const struct sdw_device_id wcd9370_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10a, 0), /* WCD9370 RX/TX Device ID */ + { }, +}; +MODULE_DEVICE_TABLE(sdw, wcd9370_slave_id); + +static int __maybe_unused wcd937x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + } + + return 0; +} + +static int __maybe_unused wcd937x_sdw_runtime_resume(struct device *dev) +{ + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); + regcache_sync(wcd->regmap); + } + + return 0; +} + +static const struct dev_pm_ops wcd937x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd937x_sdw_runtime_suspend, wcd937x_sdw_runtime_resume, NULL) +}; + +static struct sdw_driver wcd9370_codec_driver = { + .probe = wcd9370_probe, + .remove = wcd9370_remove, + .ops = &wcd9370_slave_ops, + .id_table = wcd9370_slave_id, + .driver = { + .name = "wcd9370-codec", + .pm = &wcd937x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9370_codec_driver); + +MODULE_DESCRIPTION("WCD937X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h new file mode 100644 index 000000000000..e6e1ad5926d5 --- /dev/null +++ b/sound/soc/codecs/wcd937x.h @@ -0,0 +1,653 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _WCD937X_REGISTERS_H +#define _WCD937X_REGISTERS_H + +#include +#include + +#define WCD937X_BASE_ADDRESS 0x3000 +#define WCD937X_ANA_BIAS 0x3001 +#define WCD937X_ANA_RX_SUPPLIES 0x3008 +#define WCD937X_ANA_HPH 0x3009 +#define WCD937X_ANA_EAR 0x300A +#define WCD937X_ANA_EAR_COMPANDER_CTL 0x300B +#define WCD937X_EAR_GAIN_MASK GENMASK(6, 2) +#define WCD937X_ANA_TX_CH1 0x300E +#define WCD937X_ANA_TX_CH2 0x300F +#define WCD937X_ANA_TX_CH3 0x3010 +#define WCD937X_ANA_TX_CH3_HPF 0x3011 +#define WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC 0x3012 +#define WCD937X_ANA_MICB3_DSP_EN_LOGIC 0x3013 +#define WCD937X_ANA_MBHC_MECH 0x3014 +#define WCD937X_MBHC_L_DET_EN_MASK BIT(7) +#define WCD937X_MBHC_L_DET_EN BIT(7) +#define WCD937X_MBHC_GND_DET_EN_MASK BIT(6) +#define WCD937X_MBHC_MECH_DETECT_TYPE_MASK BIT(5) +#define WCD937X_MBHC_MECH_DETECT_TYPE_INS 1 +#define WCD937X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4) +#define WCD937X_MBHC_HPHL_PLUG_TYPE_NO 1 +#define WCD937X_MBHC_GND_PLUG_TYPE_MASK BIT(3) +#define WCD937X_MBHC_GND_PLUG_TYPE_NO 1 +#define WCD937X_MBHC_HSL_PULLUP_COMP_EN BIT(2) +#define WCD937X_MBHC_HSG_PULLUP_COMP_EN BIT(1) +#define WCD937X_MBHC_HPHL_100K_TO_GND_EN BIT(0) +#define WCD937X_ANA_MBHC_ELECT 0x3015 +#define WCD937X_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4) +#define WCD937X_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4) +#define WCD937X_ANA_MBHC_BD_ISRC_OFF 0 +#define WCD937X_ANA_MBHC_BIAS_EN_MASK BIT(0) +#define WCD937X_ANA_MBHC_BIAS_EN BIT(0) +#define WCD937X_ANA_MBHC_ZDET 0x3016 +#define WCD937X_ANA_MBHC_RESULT_1 0x3017 +#define WCD937X_ANA_MBHC_RESULT_2 0x3018 +#define WCD937X_ANA_MBHC_RESULT_3 0x3019 +#define WCD937X_MBHC_BTN_RESULT_MASK GENMASK(2, 0) +#define WCD937X_ANA_MBHC_BTN0 0x301A +#define WCD937X_MBHC_BTN_VTH_MASK GENMASK(7, 2) +#define WCD937X_ANA_MBHC_BTN1 0x301B +#define WCD937X_ANA_MBHC_BTN2 0x301C +#define WCD937X_ANA_MBHC_BTN3 0x301D +#define WCD937X_ANA_MBHC_BTN4 0x301E +#define WCD937X_ANA_MBHC_BTN5 0x301F +#define WCD937X_VTH_MASK GENMASK(7, 2) +#define WCD937X_ANA_MBHC_BTN6 0x3020 +#define WCD937X_ANA_MBHC_BTN7 0x3021 +#define WCD937X_ANA_MICB1 0x3022 +#define WCD937X_MICB_VOUT_MASK GENMASK(5, 0) +#define WCD937X_MICB_EN_MASK GENMASK(7, 6) +#define WCD937X_MICB_DISABLE 0 +#define WCD937X_MICB_ENABLE 1 +#define WCD937X_MICB_PULL_UP 2 +#define WCD937X_MICB_PULL_DOWN 3 +#define WCD937X_ANA_MICB2 0x3023 +#define WCD937X_ANA_MICB2_ENABLE BIT(6) +#define WCD937X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) +#define WCD937X_ANA_MICB2_VOUT_MASK GENMASK(5, 0) +#define WCD937X_ANA_MICB2_RAMP 0x3024 +#define WCD937X_RAMP_EN_MASK BIT(7) +#define WCD937X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) +#define WCD937X_ANA_MICB3 0x3025 +#define WCD937X_ANA_MICB_EN GENMASK(7, 6) +#define WCD937X_MICB_DISABLE 0 +#define WCD937X_MICB_ENABLE 1 +#define WCD937X_MICB_PULL_UP 2 +#define WCD937X_ANA_MICB_VOUT GENMASK(5, 0) +#define WCD937X_BIAS_CTL 0x3028 +#define WCD937X_BIAS_VBG_FINE_ADJ 0x3029 +#define WCD937X_LDOL_VDDCX_ADJUST 0x3040 +#define WCD937X_LDOL_DISABLE_LDOL 0x3041 +#define WCD937X_MBHC_CTL_CLK 0x3056 +#define WCD937X_MBHC_CTL_ANA 0x3057 +#define WCD937X_MBHC_CTL_SPARE_1 0x3058 +#define WCD937X_MBHC_CTL_SPARE_2 0x3059 +#define WCD937X_MBHC_CTL_BCS 0x305A +#define WCD937X_MBHC_MOISTURE_DET_FSM_STATUS 0x305B +#define WCD937X_MBHC_TEST_CTL 0x305C +#define WCD937X_LDOH_MODE 0x3067 +#define WCD937X_LDOH_BIAS 0x3068 +#define WCD937X_LDOH_STB_LOADS 0x3069 +#define WCD937X_LDOH_SLOWRAMP 0x306A +#define WCD937X_MICB1_TEST_CTL_1 0x306B +#define WCD937X_MICB1_TEST_CTL_2 0x306C +#define WCD937X_MICB1_TEST_CTL_3 0x306D +#define WCD937X_MICB2_TEST_CTL_1 0x306E +#define WCD937X_MICB2_TEST_CTL_2 0x306F +#define WCD937X_MICB2_TEST_CTL_3 0x3070 +#define WCD937X_MICB3_TEST_CTL_1 0x3071 +#define WCD937X_MICB3_TEST_CTL_2 0x3072 +#define WCD937X_MICB3_TEST_CTL_3 0x3073 +#define WCD937X_TX_COM_ADC_VCM 0x3077 +#define WCD937X_TX_COM_BIAS_ATEST 0x3078 +#define WCD937X_TX_COM_ADC_INT1_IB 0x3079 +#define WCD937X_TX_COM_ADC_INT2_IB 0x307A +#define WCD937X_TX_COM_TXFE_DIV_CTL 0x307B +#define WCD937X_TX_COM_TXFE_DIV_START 0x307C +#define WCD937X_TX_COM_TXFE_DIV_STOP_9P6M 0x307D +#define WCD937X_TX_COM_TXFE_DIV_STOP_12P288M 0x307E +#define WCD937X_TX_1_2_TEST_EN 0x307F +#define WCD937X_TX_1_2_ADC_IB 0x3080 +#define WCD937X_TX_1_2_ATEST_REFCTL 0x3081 +#define WCD937X_TX_1_2_TEST_CTL 0x3082 +#define WCD937X_TX_1_2_TEST_BLK_EN 0x3083 +#define WCD937X_TX_1_2_TXFE_CLKDIV 0x3084 +#define WCD937X_TX_1_2_SAR2_ERR 0x3085 +#define WCD937X_TX_1_2_SAR1_ERR 0x3086 +#define WCD937X_TX_3_TEST_EN 0x3087 +#define WCD937X_TX_3_ADC_IB 0x3088 +#define WCD937X_TX_3_ATEST_REFCTL 0x3089 +#define WCD937X_TX_3_TEST_CTL 0x308A +#define WCD937X_TX_3_TEST_BLK_EN 0x308B +#define WCD937X_TX_3_TXFE_CLKDIV 0x308C +#define WCD937X_TX_3_SPARE_MONO 0x308D +#define WCD937X_TX_3_SAR1_ERR 0x308E +#define WCD937X_CLASSH_MODE_1 0x3097 +#define WCD937X_CLASSH_MODE_2 0x3098 +#define WCD937X_CLASSH_MODE_3 0x3099 +#define WCD937X_CLASSH_CTRL_VCL_1 0x309A +#define WCD937X_CLASSH_CTRL_VCL_2 0x309B +#define WCD937X_CLASSH_CTRL_CCL_1 0x309C +#define WCD937X_CLASSH_CTRL_CCL_2 0x309D +#define WCD937X_CLASSH_CTRL_CCL_3 0x309E +#define WCD937X_CLASSH_CTRL_CCL_4 0x309F +#define WCD937X_CLASSH_CTRL_CCL_5 0x30A0 +#define WCD937X_CLASSH_BUCK_TMUX_A_D 0x30A1 +#define WCD937X_CLASSH_BUCK_SW_DRV_CNTL 0x30A2 +#define WCD937X_CLASSH_SPARE 0x30A3 +#define WCD937X_FLYBACK_EN 0x30A4 +#define WCD937X_FLYBACK_VNEG_CTRL_1 0x30A5 +#define WCD937X_FLYBACK_VNEG_CTRL_2 0x30A6 +#define WCD937X_FLYBACK_VNEG_CTRL_3 0x30A7 +#define WCD937X_FLYBACK_VNEG_CTRL_4 0x30A8 +#define WCD937X_FLYBACK_VNEG_CTRL_5 0x30A9 +#define WCD937X_FLYBACK_VNEG_CTRL_6 0x30AA +#define WCD937X_FLYBACK_VNEG_CTRL_7 0x30AB +#define WCD937X_FLYBACK_VNEG_CTRL_8 0x30AC +#define WCD937X_FLYBACK_VNEG_CTRL_9 0x30AD +#define WCD937X_FLYBACK_VNEGDAC_CTRL_1 0x30AE +#define WCD937X_FLYBACK_VNEGDAC_CTRL_2 0x30AF +#define WCD937X_FLYBACK_VNEGDAC_CTRL_3 0x30B0 +#define WCD937X_FLYBACK_CTRL_1 0x30B1 +#define WCD937X_FLYBACK_TEST_CTL 0x30B2 +#define WCD937X_RX_AUX_SW_CTL 0x30B3 +#define WCD937X_RX_PA_AUX_IN_CONN 0x30B4 +#define WCD937X_RX_TIMER_DIV 0x30B5 +#define WCD937X_RX_OCP_CTL 0x30B6 +#define WCD937X_RX_OCP_COUNT 0x30B7 +#define WCD937X_RX_BIAS_EAR_DAC 0x30B8 +#define WCD937X_RX_BIAS_EAR_AMP 0x30B9 +#define WCD937X_RX_BIAS_HPH_LDO 0x30BA +#define WCD937X_RX_BIAS_HPH_PA 0x30BB +#define WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2 0x30BC +#define WCD937X_RX_BIAS_HPH_RDAC_LDO 0x30BD +#define WCD937X_RX_BIAS_HPH_CNP1 0x30BE +#define WCD937X_RX_BIAS_HPH_LOWPOWER 0x30BF +#define WCD937X_RX_BIAS_AUX_DAC 0x30C0 +#define WCD937X_RX_BIAS_AUX_AMP 0x30C1 +#define WCD937X_RX_BIAS_VNEGDAC_BLEEDER 0x30C2 +#define WCD937X_RX_BIAS_MISC 0x30C3 +#define WCD937X_RX_BIAS_BUCK_RST 0x30C4 +#define WCD937X_RX_BIAS_BUCK_VREF_ERRAMP 0x30C5 +#define WCD937X_RX_BIAS_FLYB_ERRAMP 0x30C6 +#define WCD937X_RX_BIAS_FLYB_BUFF 0x30C7 +#define WCD937X_RX_BIAS_FLYB_MID_RST 0x30C8 +#define WCD937X_HPH_L_STATUS 0x30C9 +#define WCD937X_HPH_R_STATUS 0x30CA +#define WCD937X_HPH_CNP_EN 0x30CB +#define WCD937X_HPH_CNP_WG_CTL 0x30CC +#define WCD937X_HPH_CNP_WG_TIME 0x30CD +#define WCD937X_HPH_OCP_CTL 0x30CE +#define WCD937X_HPH_AUTO_CHOP 0x30CF +#define WCD937X_HPH_CHOP_CTL 0x30D0 +#define WCD937X_HPH_PA_CTL1 0x30D1 +#define WCD937X_HPH_PA_CTL2 0x30D2 +#define WCD937X_HPHPA_GND_R_MASK BIT(6) +#define WCD937X_HPHPA_GND_L_MASK BIT(4) +#define WCD937X_HPH_L_EN 0x30D3 +#define WCD937X_HPH_L_TEST 0x30D4 +#define WCD937X_HPH_L_ATEST 0x30D5 +#define WCD937X_HPH_R_EN 0x30D6 +#define WCD937X_GAIN_SRC_SEL_MASK BIT(5) +#define WCD937X_GAIN_SRC_SEL_REGISTER 1 +#define WCD937X_HPH_R_TEST 0x30D7 +#define WCD937X_HPH_R_ATEST 0x30D8 +#define WCD937X_HPH_RDAC_CLK_CTL1 0x30D9 +#define WCD937X_HPHPA_GND_OVR_MASK BIT(1) +#define WCD937X_CHOP_CLK_EN_MASK BIT(7) +#define WCD937X_HPH_RDAC_CLK_CTL2 0x30DA +#define WCD937X_HPH_RDAC_LDO_CTL 0x30DB +#define WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL 0x30DC +#define WCD937X_HPH_REFBUFF_UHQA_CTL 0x30DD +#define WCD937X_HPH_REFBUFF_LP_CTL 0x30DE +#define WCD937X_PREREF_FLIT_BYPASS_MASK BIT(0) +#define WCD937X_HPH_L_DAC_CTL 0x30DF +#define WCD937X_HPH_R_DAC_CTL 0x30E0 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL 0x30E1 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_EN 0x30E2 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1 0x30E3 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS 0x30E4 +#define WCD937X_EAR_EAR_EN_REG 0x30E9 +#define WCD937X_EAR_EAR_PA_CON 0x30EA +#define WCD937X_EAR_EAR_SP_CON 0x30EB +#define WCD937X_EAR_EAR_DAC_CON 0x30EC +#define WCD937X_EAR_EAR_CNP_FSM_CON 0x30ED +#define WCD937X_EAR_TEST_CTL 0x30EE +#define WCD937X_EAR_STATUS_REG_1 0x30EF +#define WCD937X_EAR_STATUS_REG_2 0x30F0 +#define WCD937X_ANA_NEW_PAGE_REGISTER 0x3100 +#define WCD937X_HPH_NEW_ANA_HPH2 0x3101 +#define WCD937X_HPH_NEW_ANA_HPH3 0x3102 +#define WCD937X_SLEEP_CTL 0x3103 +#define WCD937X_SLEEP_WATCHDOG_CTL 0x3104 +#define WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x311F +#define WCD937X_MBHC_NEW_CTL_1 0x3120 +#define WCD937X_MBHC_CTL_RCO_EN_MASK BIT(7) +#define WCD937X_MBHC_CTL_RCO_EN BIT(7) +#define WCD937X_MBHC_BTN_DBNC_MASK GENMASK(1, 0) +#define WCD937X_MBHC_BTN_DBNC_T_16_MS 0x2 +#define WCD937X_MBHC_NEW_CTL_2 0x3121 +#define WCD937X_MBHC_NEW_PLUG_DETECT_CTL 0x3122 +#define WCD937X_MBHC_NEW_ZDET_ANA_CTL 0x3123 +#define WCD937X_M_RTH_CTL_MASK GENMASK(3, 2) +#define WCD937X_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0) +#define WCD937X_MBHC_HS_VREF_1P5_V 0x1 +#define WCD937X_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6 +#define WCD937X_ZDET_RANGE_CTL_MASK GENMASK(3, 0) +#define WCD937X_ZDET_MAXV_CTL_MASK GENMASK(6, 4) +#define WCD937X_MBHC_NEW_ZDET_RAMP_CTL 0x3124 +#define WCD937X_MBHC_NEW_FSM_STATUS 0x3125 +#define WCD937X_MBHC_NEW_ADC_RESULT 0x3126 +#define WCD937X_TX_NEW_TX_CH2_SEL 0x3127 +#define WCD937X_AUX_AUXPA 0x3128 +#define WCD937X_AUXPA_CLK_EN_MASK BIT(4) +#define WCD937X_AUXPA_CLK_EN_MASK BIT(4) +#define WCD937X_LDORXTX_MODE 0x3129 +#define WCD937X_LDORXTX_CONFIG 0x312A +#define WCD937X_DIE_CRACK_DIE_CRK_DET_EN 0x312C +#define WCD937X_DIE_CRACK_DIE_CRK_DET_OUT 0x312D +#define WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL 0x3132 +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x3133 +#define WCD937X_HPH_NEW_INT_RDAC_VREF_CTL 0x3134 +#define WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x3135 +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x3136 +#define WCD937X_HPH_NEW_INT_PA_MISC1 0x3137 +#define WCD937X_HPH_NEW_INT_PA_MISC2 0x3138 +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC 0x3139 +#define WCD937X_HPH_NEW_INT_HPH_TIMER1 0x313A +#define WCD937X_HPH_NEW_INT_HPH_TIMER2 0x313B +#define WCD937X_HPH_NEW_INT_HPH_TIMER3 0x313C +#define WCD937X_HPH_NEW_INT_HPH_TIMER4 0x313D +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC2 0x313E +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC3 0x313F +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x3145 +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x3146 +#define WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x3147 +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL 0x31AF +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL 0x31B0 +#define WCD937X_MOISTURE_EN_POLLING_MASK BIT(2) +#define WCD937X_HSDET_PULLUP_C_MASK GENMASK(4, 0) +#define WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT 0x31B1 +#define WCD937X_MBHC_NEW_INT_SPARE_2 0x31B2 +#define WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON 0x31B7 +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON1 0x31B8 +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON2 0x31B9 +#define WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS 0x31BA +#define WCD937X_AUX_INT_EN_REG 0x31BD +#define WCD937X_AUX_INT_PA_CTRL 0x31BE +#define WCD937X_AUX_INT_SP_CTRL 0x31BF +#define WCD937X_AUX_INT_DAC_CTRL 0x31C0 +#define WCD937X_AUX_INT_CLK_CTRL 0x31C1 +#define WCD937X_AUX_INT_TEST_CTRL 0x31C2 +#define WCD937X_AUX_INT_STATUS_REG 0x31C3 +#define WCD937X_AUX_INT_MISC 0x31C4 +#define WCD937X_LDORXTX_INT_BIAS 0x31C5 +#define WCD937X_LDORXTX_INT_STB_LOADS_DTEST 0x31C6 +#define WCD937X_LDORXTX_INT_TEST0 0x31C7 +#define WCD937X_LDORXTX_INT_STARTUP_TIMER 0x31C8 +#define WCD937X_LDORXTX_INT_TEST1 0x31C9 +#define WCD937X_LDORXTX_INT_STATUS 0x31CA +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_1 0x31D0 +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_2 0x31D1 +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1 0x31D3 +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2 0x31D4 +#define WCD937X_DIGITAL_PAGE_REGISTER 0x3400 +#define WCD937X_DIGITAL_CHIP_ID0 0x3401 +#define WCD937X_DIGITAL_CHIP_ID1 0x3402 +#define WCD937X_DIGITAL_CHIP_ID2 0x3403 +#define WCD937X_DIGITAL_CHIP_ID3 0x3404 +#define WCD937X_DIGITAL_CDC_RST_CTL 0x3406 +#define WCD937X_DIGITAL_TOP_CLK_CFG 0x3407 +#define WCD937X_DIGITAL_CDC_ANA_CLK_CTL 0x3408 +#define WCD937X_DIGITAL_CDC_DIG_CLK_CTL 0x3409 +#define WCD937X_DIGITAL_SWR_RST_EN 0x340A +#define WCD937X_DIGITAL_CDC_PATH_MODE 0x340B +#define WCD937X_DIGITAL_CDC_RX_RST 0x340C +#define WCD937X_DIGITAL_CDC_RX0_CTL 0x340D +#define WCD937X_DIGITAL_CDC_RX1_CTL 0x340E +#define WCD937X_DIGITAL_CDC_RX2_CTL 0x340F +#define WCD937X_DIGITAL_DEM_BYPASS_DATA0 0x3410 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA1 0x3411 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA2 0x3412 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA3 0x3413 +#define WCD937X_DIGITAL_CDC_COMP_CTL_0 0x3414 +#define WCD937X_DIGITAL_CDC_RX_DELAY_CTL 0x3417 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_0 0x3418 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_1 0x3419 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_0 0x341A +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_1 0x341B +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_0 0x341C +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_1 0x341D +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_0 0x341E +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_1 0x341F +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_0 0x3420 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_1 0x3421 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A6_0 0x3422 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A7_0 0x3423 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_0 0x3424 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_1 0x3425 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_2 0x3426 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_3 0x3427 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R1 0x3428 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R2 0x3429 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R3 0x342A +#define WCD937X_DIGITAL_CDC_HPH_DSM_R4 0x342B +#define WCD937X_DIGITAL_CDC_HPH_DSM_R5 0x342C +#define WCD937X_DIGITAL_CDC_HPH_DSM_R6 0x342D +#define WCD937X_DIGITAL_CDC_HPH_DSM_R7 0x342E +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_0 0x342F +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_1 0x3430 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_0 0x3431 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_1 0x3432 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_0 0x3433 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_1 0x3434 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_0 0x3435 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_1 0x3436 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_0 0x3437 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_1 0x3438 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A6_0 0x3439 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A7_0 0x343A +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_0 0x343B +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_1 0x343C +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_2 0x343D +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_3 0x343E +#define WCD937X_DIGITAL_CDC_AUX_DSM_R1 0x343F +#define WCD937X_DIGITAL_CDC_AUX_DSM_R2 0x3440 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R3 0x3441 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R4 0x3442 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R5 0x3443 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R6 0x3444 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R7 0x3445 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0 0x3446 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1 0x3447 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0 0x3448 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1 0x3449 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2 0x344A +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0 0x344B +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1 0x344C +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2 0x344D +#define WCD937X_DIGITAL_CDC_HPH_GAIN_CTL 0x344E +#define WCD937X_DIGITAL_CDC_AUX_GAIN_CTL 0x344F +#define WCD937X_DIGITAL_CDC_EAR_PATH_CTL 0x3450 +#define WCD937X_DIGITAL_CDC_SWR_CLH 0x3451 +#define WCD937X_DIGITAL_SWR_CLH_BYP 0x3452 +#define WCD937X_DIGITAL_CDC_TX0_CTL 0x3453 +#define WCD937X_DIGITAL_CDC_TX1_CTL 0x3454 +#define WCD937X_DIGITAL_CDC_TX2_CTL 0x3455 +#define WCD937X_DIGITAL_CDC_TX_RST 0x3456 +#define WCD937X_DIGITAL_CDC_REQ_CTL 0x3457 +#define WCD937X_DIGITAL_CDC_AMIC_CTL 0x345A +#define WCD937X_DIGITAL_CDC_DMIC_CTL 0x345B +#define WCD937X_DIGITAL_CDC_DMIC1_CTL 0x345C +#define WCD937X_DIGITAL_CDC_DMIC2_CTL 0x345D +#define WCD937X_DIGITAL_CDC_DMIC3_CTL 0x345E +#define WCD937X_DIGITAL_EFUSE_CTL 0x345F +#define WCD937X_DIGITAL_EFUSE_PRG_CTL 0x3460 +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_0 0x3461 +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_1 0x3462 +#define WCD937X_DIGITAL_EFUSE_T_DATA_0 0x3463 +#define WCD937X_DIGITAL_EFUSE_T_DATA_1 0x3464 +#define WCD937X_DIGITAL_PDM_WD_CTL0 0x3465 +#define WCD937X_DIGITAL_PDM_WD_CTL1 0x3466 +#define WCD937X_DIGITAL_PDM_WD_CTL2 0x3467 +#define WCD937X_DIGITAL_INTR_MODE 0x346A +#define WCD937X_DIGITAL_INTR_MASK_0 0x346B +#define WCD937X_DIGITAL_INTR_MASK_1 0x346C +#define WCD937X_DIGITAL_INTR_MASK_2 0x346D +#define WCD937X_DIGITAL_INTR_STATUS_0 0x346E +#define WCD937X_DIGITAL_INTR_STATUS_1 0x346F +#define WCD937X_DIGITAL_INTR_STATUS_2 0x3470 +#define WCD937X_DIGITAL_INTR_CLEAR_0 0x3471 +#define WCD937X_DIGITAL_INTR_CLEAR_1 0x3472 +#define WCD937X_DIGITAL_INTR_CLEAR_2 0x3473 +#define WCD937X_DIGITAL_INTR_LEVEL_0 0x3474 +#define WCD937X_DIGITAL_INTR_LEVEL_1 0x3475 +#define WCD937X_DIGITAL_INTR_LEVEL_2 0x3476 +#define WCD937X_DIGITAL_INTR_SET_0 0x3477 +#define WCD937X_DIGITAL_INTR_SET_1 0x3478 +#define WCD937X_DIGITAL_INTR_SET_2 0x3479 +#define WCD937X_DIGITAL_INTR_TEST_0 0x347A +#define WCD937X_DIGITAL_INTR_TEST_1 0x347B +#define WCD937X_DIGITAL_INTR_TEST_2 0x347C +#define WCD937X_DIGITAL_CDC_CONN_RX0_CTL 0x347F +#define WCD937X_DIGITAL_CDC_CONN_RX1_CTL 0x3480 +#define WCD937X_DIGITAL_CDC_CONN_RX2_CTL 0x3481 +#define WCD937X_DIGITAL_CDC_CONN_TX_CTL 0x3482 +#define WCD937X_DIGITAL_LOOP_BACK_MODE 0x3483 +#define WCD937X_DIGITAL_SWR_DAC_TEST 0x3484 +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_0 0x3485 +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_0 0x3491 +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_1 0x3492 +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_1 0x3493 +#define WCD937X_DIGITAL_SWR_HM_TEST 0x3494 +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX0 0x3495 +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX1 0x3496 +#define WCD937X_DIGITAL_PAD_CTL_PDM_TX0 0x3497 +#define WCD937X_DIGITAL_PAD_CTL_PDM_TX1 0x3498 +#define WCD937X_DIGITAL_PAD_INP_DIS_0 0x3499 +#define WCD937X_DIGITAL_PAD_INP_DIS_1 0x349A +#define WCD937X_DIGITAL_DRIVE_STRENGTH_0 0x349B +#define WCD937X_DIGITAL_DRIVE_STRENGTH_1 0x349C +#define WCD937X_DIGITAL_DRIVE_STRENGTH_2 0x349D +#define WCD937X_DIGITAL_RX_DATA_EDGE_CTL 0x349E +#define WCD937X_DIGITAL_TX_DATA_EDGE_CTL 0x349F +#define WCD937X_DIGITAL_GPIO_MODE 0x34A0 +#define WCD937X_DIGITAL_PIN_CTL_OE 0x34A1 +#define WCD937X_DIGITAL_PIN_CTL_DATA_0 0x34A2 +#define WCD937X_DIGITAL_PIN_CTL_DATA_1 0x34A3 +#define WCD937X_DIGITAL_PIN_STATUS_0 0x34A4 +#define WCD937X_DIGITAL_PIN_STATUS_1 0x34A5 +#define WCD937X_DIGITAL_DIG_DEBUG_CTL 0x34A6 +#define WCD937X_DIGITAL_DIG_DEBUG_EN 0x34A7 +#define WCD937X_DIGITAL_ANA_CSR_DBG_ADD 0x34A8 +#define WCD937X_DIGITAL_ANA_CSR_DBG_CTL 0x34A9 +#define WCD937X_DIGITAL_SSP_DBG 0x34AA +#define WCD937X_DIGITAL_MODE_STATUS_0 0x34AB +#define WCD937X_DIGITAL_MODE_STATUS_1 0x34AC +#define WCD937X_DIGITAL_SPARE_0 0x34AD +#define WCD937X_DIGITAL_SPARE_1 0x34AE +#define WCD937X_DIGITAL_SPARE_2 0x34AF +#define WCD937X_DIGITAL_EFUSE_REG_0 0x34B0 +#define WCD937X_DIGITAL_EFUSE_REG_1 0x34B1 +#define WCD937X_DIGITAL_EFUSE_REG_2 0x34B2 +#define WCD937X_DIGITAL_EFUSE_REG_3 0x34B3 +#define WCD937X_DIGITAL_EFUSE_REG_4 0x34B4 +#define WCD937X_DIGITAL_EFUSE_REG_5 0x34B5 +#define WCD937X_DIGITAL_EFUSE_REG_6 0x34B6 +#define WCD937X_DIGITAL_EFUSE_REG_7 0x34B7 +#define WCD937X_DIGITAL_EFUSE_REG_8 0x34B8 +#define WCD937X_DIGITAL_EFUSE_REG_9 0x34B9 +#define WCD937X_DIGITAL_EFUSE_REG_10 0x34BA +#define WCD937X_DIGITAL_EFUSE_REG_11 0x34BB +#define WCD937X_DIGITAL_EFUSE_REG_12 0x34BC +#define WCD937X_DIGITAL_EFUSE_REG_13 0x34BD +#define WCD937X_DIGITAL_EFUSE_REG_14 0x34BE +#define WCD937X_DIGITAL_EFUSE_REG_15 0x34BF +#define WCD937X_DIGITAL_EFUSE_REG_16 0x34C0 +#define WCD937X_DIGITAL_EFUSE_REG_17 0x34C1 +#define WCD937X_DIGITAL_EFUSE_REG_18 0x34C2 +#define WCD937X_DIGITAL_EFUSE_REG_19 0x34C3 +#define WCD937X_DIGITAL_EFUSE_REG_20 0x34C4 +#define WCD937X_DIGITAL_EFUSE_REG_21 0x34C5 +#define WCD937X_DIGITAL_EFUSE_REG_22 0x34C6 +#define WCD937X_DIGITAL_EFUSE_REG_23 0x34C7 +#define WCD937X_DIGITAL_EFUSE_REG_24 0x34C8 +#define WCD937X_DIGITAL_EFUSE_REG_25 0x34C9 +#define WCD937X_DIGITAL_EFUSE_REG_26 0x34CA +#define WCD937X_DIGITAL_EFUSE_REG_27 0x34CB +#define WCD937X_DIGITAL_EFUSE_REG_28 0x34CC +#define WCD937X_DIGITAL_EFUSE_REG_29 0x34CD +#define WCD937X_DIGITAL_EFUSE_REG_30 0x34CE +#define WCD937X_DIGITAL_EFUSE_REG_31 0x34CF +#define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31) + +#define WCD937X_MAX_MICBIAS 3 +#define WCD937X_MAX_BULK_SUPPLY 3 +#define WCD937X_MAX_TX_SWR_PORTS 4 +#define WCD937X_MAX_SWR_PORTS 5 +#define WCD937X_MAX_SWR_CH_IDS 15 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) +#define MAX_PORT 8 +#define MAX_CH_PER_PORT 8 +#define MAX_TX_PWR_CH 2 +#define SWR_NUM_PORTS 4 + +#define WCD937X_MAX_SLAVE_PORT_TYPES 10 + +struct codec_port_info { + u32 slave_port_type; + u32 master_port_type; + u32 ch_mask; + u32 num_ch; + u32 ch_rate; +}; + +struct wcd937x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +struct wcd937x_priv; +struct wcd937x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; + struct wcd937x_sdw_ch_info *ch_info; + bool port_enable[WCD937X_MAX_SWR_CH_IDS]; + int active_ports; + int num_ports; + bool is_tx; + struct wcd937x_priv *wcd937x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD937X_SDW) +int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd937x_sdw_device_get(struct device_node *np); + +#else +int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} +#endif + +enum { + WCD_RX1, + WCD_RX2, + WCD_RX3 +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD937X_IRQ_MBHC_SW_DET, + WCD937X_IRQ_HPHR_OCP_INT, + WCD937X_IRQ_HPHR_CNP_INT, + WCD937X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD937X_IRQ_HPHL_CNP_INT, + WCD937X_IRQ_EAR_CNP_INT, + WCD937X_IRQ_EAR_SCD_INT, + WCD937X_IRQ_AUX_CNP_INT, + WCD937X_IRQ_AUX_SCD_INT, + WCD937X_IRQ_HPHL_PDM_WD_INT, + WCD937X_IRQ_HPHR_PDM_WD_INT, + WCD937X_IRQ_AUX_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD937X_IRQ_LDORT_SCD_INT, + WCD937X_IRQ_MBHC_MOISTURE_INT, + WCD937X_IRQ_HPHL_SURGE_DET_INT, + WCD937X_IRQ_HPHR_SURGE_DET_INT, + WCD937X_NUM_IRQS, +}; + +enum wcd937x_tx_sdw_ports { + WCD937X_ADC_1_PORT = 1, + WCD937X_ADC_2_3_PORT, + WCD937X_DMIC_0_3_MBHC_PORT, + WCD937X_DMIC_4_6_PORT, +}; + +enum wcd937x_tx_sdw_channels { + WCD937X_ADC1, + WCD937X_ADC2, + WCD937X_ADC3, + WCD937X_DMIC0, + WCD937X_DMIC1, + WCD937X_MBHC, + WCD937X_DMIC2, + WCD937X_DMIC3, + WCD937X_DMIC4, + WCD937X_DMIC5, + WCD937X_DMIC6, +}; + +enum wcd937x_rx_sdw_ports { + WCD937X_HPH_PORT = 1, + WCD937X_CLSH_PORT, + WCD937X_COMP_PORT, + WCD937X_LO_PORT, + WCD937X_DSD_PORT, +}; + +enum wcd937x_rx_sdw_channels { + WCD937X_HPH_L, + WCD937X_HPH_R, + WCD937X_CLSH, + WCD937X_COMP_L, + WCD937X_COMP_R, + WCD937X_LO, + WCD937X_DSD_R, + WCD937X_DSD_L, +}; + +enum { + WCD937X_SDW_DIR_RX, + WCD937X_SDW_DIR_TX, +}; + +#endif -- cgit v1.2.3 From 9be3ec196da41b20b624ae4ed0303df58548644e Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:31 +0530 Subject: ASoC: codecs: wcd937x: add wcd937x codec driver This patch adds basic SoundWire codec driver to support for WCD9370/WCD9375 TX and RX devices. The WCD9370/WCD9375 has Multi Button Headset Control hardware to support Headset insertion, type detection, 8 headset buttons detection, Over Current detection and Impedence measurements. This patch adds support for this using wcd-mbhc apis. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-4-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 1677 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1677 insertions(+) create mode 100644 sound/soc/codecs/wcd937x.c diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c new file mode 100644 index 000000000000..4470ee0ecae6 --- /dev/null +++ b/sound/soc/codecs/wcd937x.c @@ -0,0 +1,1677 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#include "wcd937x.h" + +enum { + CHIPID_WCD9370 = 0, + CHIPID_WCD9375 = 5, +}; + +/* Z value defined in milliohm */ +#define WCD937X_ZDET_VAL_32 (32000) +#define WCD937X_ZDET_VAL_400 (400000) +#define WCD937X_ZDET_VAL_1200 (1200000) +#define WCD937X_ZDET_VAL_100K (100000000) +/* Z floating defined in ohms */ +#define WCD937X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD937X_ZDET_NUM_MEASUREMENTS (900) +#define WCD937X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14) +#define WCD937X_MBHC_GET_X1(x) ((x) & 0x3FFF) +/* Z value compared in milliOhm */ +#define WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z) (((z) > 400000) || ((z) < 32000)) +#define WCD937X_MBHC_ZDET_CONST (86 * 16384) +#define WCD937X_MBHC_MOISTURE_RREF R_24_KOHM +#define WCD_MBHC_HS_V_MAX 1600 +#define EAR_RX_PATH_AUX 1 +#define WCD937X_MBHC_MAX_BUTTONS 8 + +#define WCD937X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) + +/* Fractional Rates */ +#define WCD937X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WCD937X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +enum { + ALLOW_BUCK_DISABLE, + HPH_COMP_DELAY, + HPH_PA_DELAY, + AMIC2_BCS_ENABLE, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +struct wcd937x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd937x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode; + struct device_node *txnode; + struct regmap *regmap; + /* micb setup lock */ + struct mutex micb_lock; + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; + struct wcd_clsh_ctrl *clsh_info; + struct irq_domain *virq; + struct regmap_irq_chip *wcd_regmap_irq_chip; + struct regmap_irq_chip_data *irq_chip; + struct regulator_bulk_data supplies[WCD937X_MAX_BULK_SUPPLY]; + struct regulator *buck_supply; + struct snd_soc_jack *jack; + unsigned long status_mask; + s32 micb_ref[WCD937X_MAX_MICBIAS]; + s32 pullup_ref[WCD937X_MAX_MICBIAS]; + u32 hph_mode; + int ear_rx_path; + u32 chipid; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; /* 9375 only */ + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int aux_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + + struct gpio_desc *us_euro_gpio; + struct gpio_desc *reset_gpio; + + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + atomic_t rx_clk_cnt; + atomic_t ana_clk_count; +}; + +struct wcd937x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD937X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD937X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD937X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD937X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD937X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD937X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD937X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD937X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD937X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD937X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD937X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD937X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD937X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD937X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD937X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD937X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD937X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD937X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD937X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD937X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD937X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD937X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD937X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD937X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD937X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD937X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD937X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD937X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD937X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD937X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD937X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD937X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD937X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd937x_irqs[WCD937X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_SW_DET, 0, BIT(4)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_OCP_INT, 0, BIT(5)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_CNP_INT, 0, BIT(6)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_OCP_INT, 0, BIT(7)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_CNP_INT, 1, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_CNP_INT, 1, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_SCD_INT, 1, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_CNP_INT, 1, BIT(3)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_SCD_INT, 1, BIT(4)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_PDM_WD_INT, 1, BIT(7)), + REGMAP_IRQ_REG(WCD937X_IRQ_LDORT_SCD_INT, 2, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)), +}; + +static int wcd937x_handle_post_irq(void *data) +{ + struct wcd937x_priv *wcd937x; + + if (data) + wcd937x = (struct wcd937x_priv *)data; + else + return IRQ_HANDLED; + + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_0, 0); + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_1, 0); + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_2, 0); + + return IRQ_HANDLED; +} + +static const u32 wcd937x_config_regs[] = { + WCD937X_DIGITAL_INTR_LEVEL_0, +}; + +static struct regmap_irq_chip wcd937x_regmap_irq_chip = { + .name = "wcd937x", + .irqs = wcd937x_irqs, + .num_irqs = ARRAY_SIZE(wcd937x_irqs), + .num_regs = 3, + .status_base = WCD937X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD937X_DIGITAL_INTR_MASK_0, + .ack_base = WCD937X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .clear_ack = 1, + .config_base = wcd937x_config_regs, + .num_config_bases = ARRAY_SIZE(wcd937x_config_regs), + .num_config_regs = 1, + .runtime_pm = true, + .handle_post_irq = wcd937x_handle_post_irq, + .irq_drv_data = NULL, +}; + +static void wcd937x_reset(struct wcd937x_priv *wcd937x) +{ + usleep_range(20, 30); + + gpiod_set_value(wcd937x->reset_gpio, 1); + + usleep_range(20, 30); +} + +static void wcd937x_io_init(struct regmap *regmap) +{ + u32 val = 0, temp = 0, temp1 = 0; + + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_29, &val); + + val = val & 0x0F; + + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &temp); + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_17, &temp1); + + if (temp == 0x02 || temp1 > 0x09) + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0E, val); + else + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0e, 0x0e); + + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x80, 0x80); + usleep_range(1000, 1010); + + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x40, 0x40); + usleep_range(1000, 1010); + + regmap_update_bits(regmap, WCD937X_LDORXTX_CONFIG, BIT(4), 0x00); + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xf0, BIT(7)); + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(7), BIT(7)); + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), BIT(6)); + usleep_range(10000, 10010); + + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), 0x00); + regmap_update_bits(regmap, WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xff, 0xd9); + regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_1, 0xff, 0xfa); + regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_1, 0xff, 0xfa); + regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_1, 0xff, 0xfa); + + regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_2, 0x38, 0x00); + regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_2, 0x38, 0x00); + regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_2, 0x38, 0x00); + + /* Set Bandgap Fine Adjustment to +5mV for Tanggu SMIC part */ + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &val); + if (val == 0x01) { + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0); + } else if (val == 0x02) { + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x1F, 0x04); + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x1F, 0x04); + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0); + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xF0, 0x50); + } +} + +static int wcd937x_rx_clk_enable(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (atomic_read(&wcd937x->rx_clk_cnt)) + return 0; + + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), BIT(0)); + snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), BIT(0)); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX0_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX1_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX2_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), BIT(1)); + + atomic_inc(&wcd937x->rx_clk_cnt); + + return 0; +} + +static int wcd937x_rx_clk_disable(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (!atomic_read(&wcd937x->rx_clk_cnt)) { + dev_err(component->dev, "clk already disabled\n"); + return 0; + } + + atomic_dec(&wcd937x->rx_clk_cnt); + + snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), 0x00); + + return 0; +} + +static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) +{ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("Unsupported micbias voltage (%u mV)\n", micb_mv); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static int wcd937x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + if (micb_index < 0 || (micb_index > WCD937X_MAX_MICBIAS - 1)) { + dev_err(component->dev, "Invalid micbias index, micb_ind:%d\n", micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + dev_err(component->dev, "Invalid micbias number: %d\n", micb_num); + return -EINVAL; + } + + mutex_lock(&wcd937x->micb_lock); + switch (req) { + case MICB_PULLUP_ENABLE: + wcd937x->pullup_ref[micb_index]++; + if (wcd937x->pullup_ref[micb_index] == 1 && + wcd937x->micb_ref[micb_index] == 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, BIT(7)); + break; + case MICB_PULLUP_DISABLE: + if (wcd937x->pullup_ref[micb_index] > 0) + wcd937x->pullup_ref[micb_index]++; + if (wcd937x->pullup_ref[micb_index] == 0 && + wcd937x->micb_ref[micb_index] == 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, 0x00); + break; + case MICB_ENABLE: + wcd937x->micb_ref[micb_index]++; + atomic_inc(&wcd937x->ana_clk_count); + if (wcd937x->micb_ref[micb_index] == 1) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0xf0, 0xf0); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), BIT(4)); + snd_soc_component_update_bits(component, + WCD937X_MICB1_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_MICB2_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_MICB3_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + micb_reg, 0xc0, BIT(6)); + + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + } + break; + case MICB_DISABLE: + atomic_dec(&wcd937x->ana_clk_count); + if (wcd937x->micb_ref[micb_index] > 0) + wcd937x->micb_ref[micb_index]--; + if (wcd937x->micb_ref[micb_index] == 0 && + wcd937x->pullup_ref[micb_index] > 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, BIT(7)); + else if (wcd937x->micb_ref[micb_index] == 0 && + wcd937x->pullup_ref[micb_index] == 0) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_update_bits(component, micb_reg, + 0xc0, 0x00); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + if (atomic_read(&wcd937x->ana_clk_count) <= 0) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), 0x00); + atomic_set(&wcd937x->ana_clk_count, 0); + } + break; + } + mutex_unlock(&wcd937x->micb_lock); + + return 0; +} + +static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable) +{ + struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1]; + struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; + u8 port_num = ch_info->port_num; + u8 ch_mask = ch_info->ch_mask; + + port_config->num = port_num; + + if (enable) + port_config->ch_mask |= ch_mask; + else + port_config->ch_mask &= ~ch_mask; + + return 0; +} + +/* MBHC related */ +static void wcd937x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_1, + WCD937X_MBHC_CTL_RCO_EN_MASK, enable); +} + +static void wcd937x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_ELECT, + WCD937X_ANA_MBHC_BIAS_EN, enable); +} + +static void wcd937x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_BTN0 + i, + WCD937X_MBHC_BTN_VTH_MASK, vth); + } +} + +static bool wcd937x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = snd_soc_component_read_field(component, + WCD937X_ANA_MICB2, + WCD937X_ANA_MICB2_ENABLE_MASK); + if (val == WCD937X_MICB_ENABLE) + return true; + } + return false; +} + +static void wcd937x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + snd_soc_component_write_field(component, + WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD937X_HSDET_PULLUP_C_MASK, pull_up_cur); +} + +static int wcd937x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd937x_micbias_control(component, micb_num, req, false); +} + +static void wcd937x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_SHIFT_CTRL_MASK, 0x0C); + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_EN_MASK, 0); + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_SHIFT_CTRL_MASK, 0); + } +} + +static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd937x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + WCD937X_MICB_EN_MASK); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + WCD937X_MICB_VOUT_MASK); + + req_vout_ctl = wcd937x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + if (micb_en == WCD937X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_EN_MASK, + WCD937X_MICB_PULL_UP); + + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_VOUT_MASK, + req_vout_ctl); + + if (micb_en == WCD937X_MICB_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_EN_MASK, + WCD937X_MICB_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd937x->micb_lock); + return ret; +} + +static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd937x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd937x->micb2_mv; + + return wcd937x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +static void wcd937x_mbhc_get_result_params(struct snd_soc_component *component, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int i; + int val, val1; + s16 c1; + s32 x1, d1; + s32 denom; + static const int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD937X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD937X_MBHC_GET_X1(val); + c1 = WCD937X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if (c1 < 2 && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n", + c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD937X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD937X_ZDET_FLOATING_IMPEDANCE; + + dev_err(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d (milliohm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_read(wcd937x->regmap, + WCD937X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd937x->regmap, + WCD937X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x08; + val |= val1; + x1 = WCD937X_MBHC_GET_X1(val); + i++; + if (i == WCD937X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd937x_mbhc_zdet_ramp(struct snd_soc_component *component, + struct wcd937x_mbhc_zdet_param *zdet_param, + s32 *zl, s32 *zr, s16 *d1_a) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + s32 zdet = 0; + + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, + WCD937X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN5, + WCD937X_VTH_MASK, zdet_param->btn5); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN6, + WCD937X_VTH_MASK, zdet_param->btn6); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN7, + WCD937X_VTH_MASK, zdet_param->btn7); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, + WCD937X_ZDET_RANGE_CTL_MASK, zdet_param->noff); + snd_soc_component_update_bits(component, WCD937X_MBHC_NEW_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x80); + wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x40); + wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static void wcd937x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + s32 *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD937X_ZDET_VAL_400 / 1000)) + q1 = snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd937x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + u32 *zl, u32 *zr) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + s16 reg0, reg1, reg2, reg3, reg4; + s32 z1l, z1r, z1ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd937x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd937x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + reg0 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD937X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read(component, WCD937X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd937x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x00); + + /* Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + regmap_update_bits(wcd937x->regmap, + WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00); + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1); + + if (!WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1l)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1l < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1l > WCD937X_ZDET_VAL_400) && + (z1l <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1l > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1); + +left_ch_impedance: + if (z1l == WCD937X_ZDET_FLOATING_IMPEDANCE || + z1l > WCD937X_ZDET_VAL_100K) { + *zl = WCD937X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1l / 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + + /* Start of right impedance ramp and calculation */ + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1); + if (WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1r)) { + if ((z1r > WCD937X_ZDET_VAL_1200 && + zdet_param_ptr->noff == 0x6) || + ((*zl) != WCD937X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1r < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1r > WCD937X_ZDET_VAL_400) && + (z1r <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1r > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1); + } +right_ch_impedance: + if (z1r == WCD937X_ZDET_FLOATING_IMPEDANCE || + z1r > WCD937X_ZDET_VAL_100K) { + *zr = WCD937X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1r / 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + + /* Mono/stereo detection */ + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE)) { + dev_err(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST, + WCD937X_HPHPA_GND_OVR_MASK, 1); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, 1); + if (*zl < (WCD937X_ZDET_VAL_32 / 1000)) + wcd937x_mbhc_zdet_ramp(component, &zdet_param[0], &z1ls, NULL, d1); + else + wcd937x_mbhc_zdet_ramp(component, &zdet_param[1], &z1ls, NULL, d1); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, 0); + snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST, + WCD937X_HPHPA_GND_OVR_MASK, 0); + z1ls /= 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, &z1ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1ls > zMono) ? (z1ls - zMono) : (zMono - z1ls); + z_diff2 = ((*zl) > z1ls) ? ((*zl) - z1ls) : (z1ls - (*zl)); + if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + zMono))) + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + else + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO); + + /* Enable surge protection again after impedance detection */ + regmap_update_bits(wcd937x->regmap, + WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); +zdet_complete: + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd937x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD937X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd937x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_HSG_PULLUP_COMP_EN, 1); + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_GND_DET_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_GND_DET_EN_MASK, 0); + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_HSG_PULLUP_COMP_EN, 0); + } +} + +static void wcd937x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, enable); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_L_MASK, enable); +} + +static void wcd937x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (wcd937x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd937x->mbhc_cfg.hphl_swh) { + dev_err(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref); +} + +static void wcd937x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); +} + +static bool wcd937x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd937x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd937x->mbhc_cfg.hphl_swh) { + dev_err(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD937X_MBHC_NEW_CTL_2, WCD937X_M_RTH_CTL_MASK)) + goto done; + + wcd937x_mbhc_moisture_detect_en(component, true); + /* Read moisture comparator status */ + ret = ((snd_soc_component_read(component, WCD937X_MBHC_NEW_FSM_STATUS) + & 0x20) ? 0 : 1); +done: + return ret; +} + +static void wcd937x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD937X_MOISTURE_EN_POLLING_MASK, enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd937x_mbhc_clk_setup, + .mbhc_bias = wcd937x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd937x_mbhc_program_btn_thr, + .micbias_enable_status = wcd937x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd937x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd937x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd937x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd937x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd937x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd937x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd937x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd937x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd937x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd937x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd937x_mbhc_moisture_detect_en, +}; + +static int wcd937x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd937x->wcd_mbhc); + + return 0; +} + +static int wcd937x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 zl, zr; + bool hphr; + struct soc_mixer_control *mc; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(wcd937x->wcd_mbhc, &zl, &zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, + wcd937x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, + wcd937x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, + wcd937x_hph_impedance_get, NULL), +}; + +static int wcd937x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd937x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHR_OCP_INT); + + wcd937x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd937x->wcd_mbhc)) + return PTR_ERR(wcd937x->wcd_mbhc); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd937x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd937x->wcd_mbhc); +} + +/* END MBHC */ + +static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) +{ + int vout_ctl[3]; + + /* Set micbias voltage */ + vout_ctl[0] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb1_mv); + vout_ctl[1] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb2_mv); + vout_ctl[2] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb3_mv); + if ((vout_ctl[0] | vout_ctl[1] | vout_ctl[2]) < 0) + return -EINVAL; + + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, WCD937X_ANA_MICB_VOUT, vout_ctl[0]); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, WCD937X_ANA_MICB_VOUT, vout_ctl[1]); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, WCD937X_ANA_MICB_VOUT, vout_ctl[2]); + + return 0; +} + +static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static struct irq_chip wcd_irq_chip = { + .name = "WCD937x", +}; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +static int wcd937x_irq_init(struct wcd937x_priv *wcd, struct device *dev) +{ + wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(wcd->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + return devm_regmap_add_irq_chip(dev, wcd->regmap, + irq_create_mapping(wcd->virq, 0), + IRQF_ONESHOT, 0, &wcd937x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd937x_soc_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd937x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int i, ret; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(5000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd937x->regmap); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + wcd937x->chipid = (snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; + if (wcd937x->chipid != CHIPID_WCD9370 && + wcd937x->chipid != CHIPID_WCD9375) { + dev_err(dev, "Got unknown chip id: 0x%x\n", wcd937x->chipid); + pm_runtime_put(dev); + return -EINVAL; + } + + wcd937x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD937X); + if (IS_ERR(wcd937x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd937x->clsh_info); + } + + wcd937x_io_init(wcd937x->regmap); + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd937x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd937x->regmap, (WCD937X_DIGITAL_INTR_LEVEL_0 + i), 0); + + pm_runtime_put(dev); + + wcd937x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHR_PDM_WD_INT); + wcd937x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHL_PDM_WD_INT); + wcd937x->aux_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_AUX_PDM_WD_INT); + + /* Request for watchdog interrupt */ + ret = devm_request_threaded_irq(dev, wcd937x->hphr_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request HPHR watchdog interrupt (%d)\n", ret); + + ret = devm_request_threaded_irq(dev, wcd937x->hphl_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request HPHL watchdog interrupt (%d)\n", ret); + + ret = devm_request_threaded_irq(dev, wcd937x->aux_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request Aux watchdog interrupt (%d)\n", ret); + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd937x->hphr_pdm_wd_int); + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + + ret = wcd937x_mbhc_init(component); + if (ret) + dev_err(component->dev, "mbhc initialization failed\n"); + + return ret; +} + +static void wcd937x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + wcd937x_mbhc_deinit(component); + free_irq(wcd937x->aux_pdm_wd_int, wcd937x); + free_irq(wcd937x->hphl_pdm_wd_int, wcd937x); + free_irq(wcd937x->hphr_pdm_wd_int, wcd937x); + + wcd_clsh_ctrl_free(wcd937x->clsh_info); +} + +static int wcd937x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd937x_priv *wcd = dev_get_drvdata(comp->dev); + int ret = 0; + + if (jack) + ret = wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + else + wcd_mbhc_stop(wcd->wcd_mbhc); + + return ret; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd937x = { + .name = "wcd937x_codec", + .probe = wcd937x_soc_codec_probe, + .remove = wcd937x_soc_codec_remove, + .set_jack = wcd937x_codec_set_jack, + .endianness = 1, +}; + +static void wcd937x_dt_parse_micbias_info(struct device *dev, struct wcd937x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int ret = 0; + + ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!ret) + wcd->micb1_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias1 DT property not found\n"); + + ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!ret) + wcd->micb2_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias2 DT property not found\n"); + + ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!ret) + wcd->micb3_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias3 DT property not found\n"); +} + +static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + int value; + struct wcd937x_priv *wcd937x; + + wcd937x = snd_soc_component_get_drvdata(component); + + value = gpiod_get_value(wcd937x->us_euro_gpio); + gpiod_set_value(wcd937x->us_euro_gpio, !value); + + return true; +} + +static int wcd937x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + return wcd937x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd937x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + return sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); +} + +static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + wcd->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = { + .hw_params = wcd937x_codec_hw_params, + .hw_free = wcd937x_codec_free, + .set_stream = wcd937x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd937x_dais[] = { + [0] = { + .name = "wcd937x-sdw-rx", + .playback = { + .stream_name = "WCD AIF Playback", + .rates = WCD937X_RATES | WCD937X_FRAC_RATES, + .formats = WCD937X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd937x_sdw_dai_ops, + }, + [1] = { + .name = "wcd937x-sdw-tx", + .capture = { + .stream_name = "WCD AIF Capture", + .rates = WCD937X_RATES, + .formats = WCD937X_FORMATS, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd937x_sdw_dai_ops, + }, +}; + +static int wcd937x_bind(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + int ret; + + /* Give the SDW subdevices some more time to settle */ + usleep_range(5000, 5010); + + ret = component_bind_all(dev, wcd937x); + if (ret) { + dev_err(dev, "Slave bind failed, ret = %d\n", ret); + return ret; + } + + wcd937x->rxdev = wcd937x_sdw_device_get(wcd937x->rxnode); + if (!wcd937x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + return -EINVAL; + } + + wcd937x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd937x->rxdev); + wcd937x->sdw_priv[AIF1_PB]->wcd937x = wcd937x; + + wcd937x->txdev = wcd937x_sdw_device_get(wcd937x->txnode); + if (!wcd937x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + return -EINVAL; + } + + wcd937x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd937x->txdev); + wcd937x->sdw_priv[AIF1_CAP]->wcd937x = wcd937x; + wcd937x->tx_sdw_dev = dev_to_sdw_dev(wcd937x->txdev); + if (!wcd937x->tx_sdw_dev) { + dev_err(dev, "could not get txslave with matching of dev\n"); + return -EINVAL; + } + + /* + * As TX is the main CSR reg interface, which should not be suspended first. + * expicilty add the dependency link + */ + if (!device_link_add(wcd937x->rxdev, wcd937x->txdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink TX and RX\n"); + return -EINVAL; + } + + if (!device_link_add(dev, wcd937x->txdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink WCD and TX\n"); + return -EINVAL; + } + + if (!device_link_add(dev, wcd937x->rxdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink WCD and RX\n"); + return -EINVAL; + } + + wcd937x->regmap = dev_get_regmap(&wcd937x->tx_sdw_dev->dev, NULL); + if (!wcd937x->regmap) { + dev_err(dev, "could not get TX device regmap\n"); + return -EINVAL; + } + + ret = wcd937x_irq_init(wcd937x, dev); + if (ret) { + dev_err(dev, "IRQ init failed: %d\n", ret); + return ret; + } + + wcd937x->sdw_priv[AIF1_PB]->slave_irq = wcd937x->virq; + wcd937x->sdw_priv[AIF1_CAP]->slave_irq = wcd937x->virq; + + ret = wcd937x_set_micbias_data(wcd937x); + if (ret < 0) { + dev_err(dev, "Bad micbias pdata\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd937x, + wcd937x_dais, ARRAY_SIZE(wcd937x_dais)); + if (ret) + dev_err(dev, "Codec registration failed\n"); + + return ret; +} + +static void wcd937x_unbind(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd937x->txdev); + device_link_remove(dev, wcd937x->rxdev); + device_link_remove(wcd937x->rxdev, wcd937x->txdev); + component_unbind_all(dev, wcd937x); + mutex_destroy(&wcd937x->micb_lock); +} + +static const struct component_master_ops wcd937x_comp_ops = { + .bind = wcd937x_bind, + .unbind = wcd937x_unbind, +}; + +static int wcd937x_add_slave_components(struct wcd937x_priv *wcd937x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np = dev->of_node; + + wcd937x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd937x->rxnode) { + dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n"); + return -ENODEV; + } + of_node_get(wcd937x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd937x->rxnode); + + wcd937x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd937x->txnode) { + dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n"); + return -ENODEV; + } + of_node_get(wcd937x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd937x->txnode); + + return 0; +} + +static int wcd937x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct device *dev = &pdev->dev; + struct wcd937x_priv *wcd937x; + struct wcd_mbhc_config *cfg; + int ret; + + wcd937x = devm_kzalloc(dev, sizeof(*wcd937x), GFP_KERNEL); + if (!wcd937x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd937x); + mutex_init(&wcd937x->micb_lock); + + wcd937x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(wcd937x->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd937x->reset_gpio), + "failed to reset wcd gpio\n"); + + wcd937x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW); + if (IS_ERR(wcd937x->us_euro_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd937x->us_euro_gpio), + "us-euro swap Control GPIO not found\n"); + + cfg = &wcd937x->mbhc_cfg; + cfg->swap_gnd_mic = wcd937x_swap_gnd_mic; + + wcd937x->supplies[0].supply = "vdd-rxtx"; + wcd937x->supplies[1].supply = "vdd-px"; + wcd937x->supplies[2].supply = "vdd-mic-bias"; + + ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + + /* Get the buck separately, as it needs special handling */ + wcd937x->buck_supply = devm_regulator_get(dev, "vdd-buck"); + if (IS_ERR(wcd937x->buck_supply)) + return dev_err_probe(dev, PTR_ERR(wcd937x->buck_supply), + "Failed to get buck supply\n"); + + ret = regulator_enable(wcd937x->buck_supply); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable buck supply\n"); + + wcd937x_dt_parse_micbias_info(dev, wcd937x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD937X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd937x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, &wcd937x->mbhc_cfg); + + ret = wcd937x_add_slave_components(wcd937x, dev, &match); + if (ret) + return ret; + + wcd937x_reset(wcd937x); + + ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return ret; +} + +static void wcd937x_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + component_master_del(&pdev->dev, &wcd937x_comp_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); +} + +#if defined(CONFIG_OF) +static const struct of_device_id wcd937x_of_match[] = { + { .compatible = "qcom,wcd9370-codec" }, + { .compatible = "qcom,wcd9375-codec" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd937x_of_match); +#endif + +static struct platform_driver wcd937x_codec_driver = { + .probe = wcd937x_probe, + .remove_new = wcd937x_remove, + .driver = { + .name = "wcd937x_codec", + .of_match_table = of_match_ptr(wcd937x_of_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd937x_codec_driver); +MODULE_DESCRIPTION("WCD937X Codec driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 82be8c62a38c6a44e64ecb29d7a9b5cb35c6cad4 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:32 +0530 Subject: ASoC: codecs: wcd937x: add basic controls This patch adds basic controls found in WCD9370/WCD9375 codec. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-5-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 4470ee0ecae6..293a84a53918 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -120,6 +120,10 @@ struct wcd937x_priv { atomic_t ana_clk_count; }; +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + struct wcd937x_mbhc_zdet_param { u16 ldo_ctl; u16 noff; @@ -476,6 +480,157 @@ static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch return 0; } +static int wcd937x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd937x->hph_mode; + return 0; +} + +static int wcd937x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (!mode_val) + mode_val = CLS_AB; + + if (mode_val == wcd937x->hph_mode) + return 0; + + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_HIFI: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_LP: + case CLS_AB_HIFI: + wcd937x->hph_mode = mode_val; + return 1; + } + + dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__); + return -EINVAL; +} + +static int wcd937x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + ucontrol->value.integer.value[0] = hphr ? wcd937x->comp2_enable : + wcd937x->comp1_enable; + return 0; +} + +static int wcd937x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[AIF1_PB]; + int value = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc; + int portidx; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + if (hphr) { + if (value == wcd937x->comp2_enable) + return 0; + + wcd937x->comp2_enable = value; + } else { + if (value == wcd937x->comp1_enable) + return 0; + + wcd937x->comp1_enable = value; + } + + portidx = wcd->ch_info[mc->reg].port_num; + + if (value) + wcd937x_connect_port(wcd, portidx, mc->reg, true); + else + wcd937x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd937x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp); + struct wcd937x_sdw_priv *wcd; + int dai_id = mixer->shift; + int ch_idx = mixer->reg; + int portidx; + + wcd = wcd937x->sdw_priv[dai_id]; + portidx = wcd->ch_info[ch_idx].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx]; + + return 0; +} + +static int wcd937x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp); + struct wcd937x_sdw_priv *wcd; + int dai_id = mixer->shift; + int ch_idx = mixer->reg; + int portidx; + bool enable; + + wcd = wcd937x->sdw_priv[dai_id]; + + portidx = wcd->ch_info[ch_idx].port_num; + + enable = ucontrol->value.integer.value[0]; + + if (enable == wcd->port_enable[portidx]) { + wcd937x_connect_port(wcd, portidx, ch_idx, enable); + return 0; + } + + wcd->port_enable[portidx] = enable; + wcd937x_connect_port(wcd, portidx, ch_idx, enable); + + return 1; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_NORMAL", "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_LP", "CLS_AB_HIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), rx_hph_mode_mux_text); + /* MBHC related */ static void wcd937x_mbhc_clk_setup(struct snd_soc_component *component, bool enable) @@ -1150,6 +1305,50 @@ static void wcd937x_mbhc_deinit(struct snd_soc_component *component) /* END MBHC */ +static const struct snd_kcontrol_new wcd937x_snd_controls[] = { + SOC_SINGLE_TLV("EAR_PA Volume", WCD937X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put), + + SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + + SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD937X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("ADC1 Volume", WCD937X_ANA_TX_CH1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD937X_ANA_TX_CH2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD937X_ANA_TX_CH3, 0, 20, 0, analog_gain), + + SOC_SINGLE_EXT("HPHL Switch", WCD937X_HPH_L, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD937X_HPH_R, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + + SOC_SINGLE_EXT("ADC1 Switch", WCD937X_ADC1, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD937X_ADC2, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD937X_ADC3, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD937X_DMIC0, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD937X_DMIC1, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD937X_MBHC, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD937X_DMIC2, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD937X_DMIC3, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD937X_DMIC4, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD937X_DMIC5, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), +}; + static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) { int vout_ctl[3]; @@ -1316,6 +1515,8 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd937x = { .name = "wcd937x_codec", .probe = wcd937x_soc_codec_probe, .remove = wcd937x_soc_codec_remove, + .controls = wcd937x_snd_controls, + .num_controls = ARRAY_SIZE(wcd937x_snd_controls), .set_jack = wcd937x_codec_set_jack, .endianness = 1, }; -- cgit v1.2.3 From 57fe69db7a015e828ec69d819707c5b8eac6d052 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:33 +0530 Subject: ASoC: codecs: wcd937x: add playback dapm widgets This patch adds required dapm widgets for playback. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-6-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 667 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 293a84a53918..3b0e4bd4eb4f 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -342,6 +342,569 @@ static int wcd937x_rx_clk_disable(struct snd_soc_component *component) return 0; } +static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_HPH_RDAC_CLK_CTL1, + BIT(7), 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, 0x06); + + if (wcd937x->comp1_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), 0x00); + + if (wcd937x->comp2_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, BIT(5), 0x00); + } + + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5110); + clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + } + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), 0x00); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), BIT(5)); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(0)); + break; + } + + return 0; +} + +static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + WCD937X_HPH_RDAC_CLK_CTL1, BIT(7), 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, 0x06); + if (wcd937x->comp2_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, BIT(5), 0x00); + if (wcd937x->comp1_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), 0x00); + } + + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5110); + clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + } + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), 0x00); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, + BIT(5), BIT(5)); + } + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, BIT(0)); + break; + } + + return 0; +} + +static int wcd937x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), BIT(0)); + + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, 0x06); + if (wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + usleep_range(5000, 5010); + + snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN, BIT(2), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(0)); + if (wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), 0x00); + break; + } + + return 0; +} + +static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, + BIT(0), BIT(0)); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_AUX, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(2), 0x00); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(4), BIT(4)); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL1, + 0x07, 0x03); + break; + case SND_SOC_DAPM_POST_PMU: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (wcd937x->comp2_enable) + usleep_range(7000, 7100); + else + usleep_range(20000, 20100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), BIT(1)); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->hphr_pdm_wd_int); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHR_PA_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (wcd937x->comp2_enable) + usleep_range(7000, 7100); + else + usleep_range(20000, 20100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHR_PA_OFF); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL1, 0x07, 0x00); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(4), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(5), BIT(5)); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x03); + break; + case SND_SOC_DAPM_POST_PMU: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), BIT(1)); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_HPH, BIT(5), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), BIT(0)); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(2000, 2010); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_AUX, + hph_mode); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), 0x00); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable watchdog interrupt for HPHL or AUX depending on mux value */ + wcd937x->ear_rx_path = snd_soc_component_read(component, + WCD937X_DIGITAL_CDC_EAR_PATH_CTL); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), BIT(0)); + else + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, + 0x07, 0x03); + if (!wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_ANA_EAR_COMPANDER_CTL, + BIT(7), BIT(7)); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(6000, 6010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + enable_irq(wcd937x->aux_pdm_wd_int); + else + enable_irq(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + else + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + if (!wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_ANA_EAR_COMPANDER_CTL, + BIT(7), 0x00); + usleep_range(7000, 7010); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN, + BIT(2), BIT(2)); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), 0x00); + else + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, + 0x07, 0x00); + break; + } + + return 0; +} + +static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), 0x00); + } + + return 0; +} + +static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(1), 0x00); + } + + return 0; +} + +static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + usleep_range(6000, 6010); + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(2), 0x00); + } + + return 0; +} + +static int wcd937x_codec_enable_vdd_buck(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (test_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask)) { + dev_err(component->dev, "buck already in enabled state\n"); + clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + return 0; + } + ret = regulator_enable(wcd937x->buck_supply); + if (ret) { + dev_err(component->dev, "VDD_BUCK is not enabled\n"); + return ret; + } + clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + usleep_range(200, 250); + break; + case SND_SOC_DAPM_POST_PMD: + set_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + break; + } + + return 0; +} + static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) { if (micb_mv < 1000 || micb_mv > 2850) { @@ -1349,6 +1912,108 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_get_swr_port, wcd937x_set_swr_port), }; +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new aux_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const rdac3_mux_text[] = { + "RX1", "RX3" +}; + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new rx_rdac3_mux = SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { + /* Input widgets */ + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_AUX"), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_vdd_buck, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + /* RX widgets */ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0, + wcd937x_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AUX PGA", WCD937X_AUX_AUXPA, 7, 0, NULL, 0, + wcd937x_codec_enable_aux_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PGA", WCD937X_ANA_HPH, 7, 0, NULL, 0, + wcd937x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD937X_ANA_HPH, 6, 0, NULL, 0, + wcd937x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_aux_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx1, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx2, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx3, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* RX mixer widgets*/ + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("AUX_RDAC", SND_SOC_NOPM, 0, 0, + aux_rdac_switch, ARRAY_SIZE(aux_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /* RX output widgets */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("AUX"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), +}; + static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) { int vout_ctl[3]; @@ -1517,6 +2182,8 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd937x = { .remove = wcd937x_soc_codec_remove, .controls = wcd937x_snd_controls, .num_controls = ARRAY_SIZE(wcd937x_snd_controls), + .dapm_widgets = wcd937x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets), .set_jack = wcd937x_codec_set_jack, .endianness = 1, }; -- cgit v1.2.3 From 8ee78493be89c42d016f941a9b00c203ec08daab Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:34 +0530 Subject: ASoC: codecs: wcd937x: add capture dapm widgets This patch adds required dapm widgets for capture path. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-7-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 404 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 3b0e4bd4eb4f..f8b57f47ad55 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -915,6 +915,145 @@ static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) return (micb_mv - 1000) / 50; } +static int wcd937x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + bool use_amic3 = snd_soc_component_read(component, WCD937X_TX_NEW_TX_CH2_SEL) & BIT(7); + + /* Enable BCS for Headset mic */ + if (event == SND_SOC_DAPM_PRE_PMU && strnstr(w->name, "ADC", sizeof("ADC"))) + if (w->shift == 1 && !use_amic3) + set_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); + + return 0; +} + +static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + atomic_inc(&wcd937x->ana_clk_count); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(4), BIT(4)); + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask)) + clear_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); + + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), 0x00); + break; + } + + return 0; +} + +static int wcd937x_enable_req(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_REQ_CTL, BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_REQ_CTL, BIT(0), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(6), BIT(6)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3_HPF, BIT(6), BIT(6)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x70, 0x70); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH1, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(6), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3, BIT(7), BIT(7)); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH1, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(4), 0x00); + + atomic_dec(&wcd937x->ana_clk_count); + if (atomic_read(&wcd937x->ana_clk_count) <= 0) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), 0x00); + atomic_set(&wcd937x->ana_clk_count, 0); + } + + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(7), 0x00); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 dmic_clk_reg; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC1_CTL; + break; + case 2: + case 3: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC2_CTL; + break; + case 4: + case 5: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC3_CTL; + break; + default: + dev_err(component->dev, "Invalid DMIC Selection\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x07, BIT(1)); + snd_soc_component_update_bits(component, + dmic_clk_reg, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x70, BIT(5)); + break; + } + + return 0; +} + static int wcd937x_micbias_control(struct snd_soc_component *component, int micb_num, int req, bool is_dapm) { @@ -1026,6 +1165,82 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, return 0; } +static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num; + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_micbias_control(component, micb_num, + MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_micbias_control(component, micb_num, + MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __wcd937x_codec_enable_micbias(w, event); +} + +static int __wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num; + + if (strnstr(w->name, "VA MIC BIAS1", sizeof("VA MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "VA MIC BIAS2", sizeof("VA MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "VA MIC BIAS3", sizeof("VA MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __wcd937x_codec_enable_micbias_pullup(w, event); +} + static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable) { struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1]; @@ -1912,6 +2127,42 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_get_swr_port, wcd937x_set_swr_port), }; +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + static const struct snd_kcontrol_new ear_rdac_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; @@ -1928,22 +2179,76 @@ static const struct snd_kcontrol_new hphr_rdac_switch[] = { SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) }; +static const char * const adc2_mux_text[] = { + "INP2", "INP3" +}; + static const char * const rdac3_mux_text[] = { "RX1", "RX3" }; +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD937X_TX_NEW_TX_CH2_SEL, 7, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + static const struct soc_enum rdac3_enum = SOC_ENUM_SINGLE(WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0, ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); +static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + static const struct snd_kcontrol_new rx_rdac3_mux = SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { /* Input widgets */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("IN1_HPHL"), SND_SOC_DAPM_INPUT("IN2_HPHR"), SND_SOC_DAPM_INPUT("IN3_AUX"), + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + + /* TX mixers */ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* MIC_BIAS widgets */ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, wcd937x_codec_enable_vdd_buck, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -2007,11 +2312,101 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + /* TX output widgets */ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("WCD_TX_OUTPUT"), + /* RX output widgets */ SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("AUX"), SND_SOC_DAPM_OUTPUT("HPHL"), SND_SOC_DAPM_OUTPUT("HPHR"), + + /* MIC_BIAS pull up widgets */ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = { + /* Input widgets */ + SND_SOC_DAPM_INPUT("AMIC4"), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX mixer widgets */ + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, + 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1, + 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 2, + 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 3, + 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 4, + 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 5, + 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 2, 0, adc3_switch, + ARRAY_SIZE(adc3_switch), wcd937x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output widgets */ + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), }; static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) @@ -2143,6 +2538,15 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd937x->hphl_pdm_wd_int); disable_irq_nosync(wcd937x->aux_pdm_wd_int); + if (wcd937x->chipid == CHIPID_WCD9375) { + ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets, + ARRAY_SIZE(wcd9375_dapm_widgets)); + if (ret < 0) { + dev_err(component->dev, "Failed to add snd_ctls\n"); + return ret; + } + } + ret = wcd937x_mbhc_init(component); if (ret) dev_err(component->dev, "mbhc initialization failed\n"); -- cgit v1.2.3 From 313e978df7fc38b9e949ac5933d0d9d56d5e8a9c Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 May 2024 09:25:35 +0530 Subject: ASoC: codecs: wcd937x: add audio routing and Kconfig This patch adds audio routing for both playback and capture and Makefile and Kconfigs changes for wcd937x. Signed-off-by: Prasad Kumpatla Co-developed-by: Mohammad Rafi Shaik Signed-off-by: Mohammad Rafi Shaik Link: https://lore.kernel.org/r/20240524035535.3119208-8-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 20 ++++++++++++ sound/soc/codecs/Makefile | 7 ++++ sound/soc/codecs/wcd937x.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3621781d63c4..7b99556f24d3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -279,6 +279,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X + imply SND_SOC_WCD937X_SDW imply SND_SOC_WCD938X_SDW imply SND_SOC_WCD939X_SDW imply SND_SOC_LPASS_MACRO_COMMON @@ -2111,6 +2112,25 @@ config SND_SOC_WCD934X The WCD9340/9341 is a audio codec IC Integrated in Qualcomm SoCs like SDM845. +config SND_SOC_WCD937X + depends on SND_SOC_WCD937X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + select SND_SOC_WCD_CLASSH + +config SND_SOC_WCD937X_SDW + tristate "WCD9370/WCD9375 Codec - SDW" + select SND_SOC_WCD937X + select SND_SOC_WCD_MBHC + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9370/9375 is an audio codec IC used with SoCs + like SC7280 or QCM6490 chipsets, and it connected + via soundwire. + To compile this codec driver say Y or m. + config SND_SOC_WCD938X depends on SND_SOC_WCD938X_SDW tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d343d23c8f0f..ca69f35cc0f7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -318,6 +318,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o snd-soc-wcd9335-y := wcd9335.o snd-soc-wcd934x-y := wcd934x.o +snd-soc-wcd937x-objs := wcd937x.o +snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o snd-soc-wcd938x-y := wcd938x.o snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o @@ -714,6 +716,11 @@ obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o +obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x.o +ifdef CONFIG_SND_SOC_WCD937X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o +endif obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o ifdef CONFIG_SND_SOC_WCD938X_SDW # avoid link failure by forcing sdw code built-in when needed diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index f8b57f47ad55..ef649ed77fb2 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2409,6 +2409,77 @@ static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), }; +static const struct snd_soc_dapm_route wcd937x_audio_map[] = { + { "ADC1_OUTPUT", NULL, "ADC1_MIXER" }, + { "ADC1_MIXER", "Switch", "ADC1 REQ" }, + { "ADC1 REQ", NULL, "ADC1" }, + { "ADC1", NULL, "AMIC1" }, + + { "ADC2_OUTPUT", NULL, "ADC2_MIXER" }, + { "ADC2_MIXER", "Switch", "ADC2 REQ" }, + { "ADC2 REQ", NULL, "ADC2" }, + { "ADC2", NULL, "ADC2 MUX" }, + { "ADC2 MUX", "INP3", "AMIC3" }, + { "ADC2 MUX", "INP2", "AMIC2" }, + + { "IN1_HPHL", NULL, "VDD_BUCK" }, + { "IN1_HPHL", NULL, "CLS_H_PORT" }, + { "RX1", NULL, "IN1_HPHL" }, + { "RDAC1", NULL, "RX1" }, + { "HPHL_RDAC", "Switch", "RDAC1" }, + { "HPHL PGA", NULL, "HPHL_RDAC" }, + { "HPHL", NULL, "HPHL PGA" }, + + { "IN2_HPHR", NULL, "VDD_BUCK" }, + { "IN2_HPHR", NULL, "CLS_H_PORT" }, + { "RX2", NULL, "IN2_HPHR" }, + { "RDAC2", NULL, "RX2" }, + { "HPHR_RDAC", "Switch", "RDAC2" }, + { "HPHR PGA", NULL, "HPHR_RDAC" }, + { "HPHR", NULL, "HPHR PGA" }, + + { "IN3_AUX", NULL, "VDD_BUCK" }, + { "IN3_AUX", NULL, "CLS_H_PORT" }, + { "RX3", NULL, "IN3_AUX" }, + { "RDAC4", NULL, "RX3" }, + { "AUX_RDAC", "Switch", "RDAC4" }, + { "AUX PGA", NULL, "AUX_RDAC" }, + { "AUX", NULL, "AUX PGA" }, + + { "RDAC3_MUX", "RX3", "RX3" }, + { "RDAC3_MUX", "RX1", "RX1" }, + { "RDAC3", NULL, "RDAC3_MUX" }, + { "EAR_RDAC", "Switch", "RDAC3" }, + { "EAR PGA", NULL, "EAR_RDAC" }, + { "EAR", NULL, "EAR PGA" }, +}; + +static const struct snd_soc_dapm_route wcd9375_audio_map[] = { + { "ADC3_OUTPUT", NULL, "ADC3_MIXER" }, + { "ADC3_OUTPUT", NULL, "ADC3_MIXER" }, + { "ADC3_MIXER", "Switch", "ADC3 REQ" }, + { "ADC3 REQ", NULL, "ADC3" }, + { "ADC3", NULL, "AMIC4" }, + + { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" }, + { "DMIC1_MIXER", "Switch", "DMIC1" }, + + { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" }, + { "DMIC2_MIXER", "Switch", "DMIC2" }, + + { "DMIC3_OUTPUT", NULL, "DMIC3_MIXER" }, + { "DMIC3_MIXER", "Switch", "DMIC3" }, + + { "DMIC4_OUTPUT", NULL, "DMIC4_MIXER" }, + { "DMIC4_MIXER", "Switch", "DMIC4" }, + + { "DMIC5_OUTPUT", NULL, "DMIC5_MIXER" }, + { "DMIC5_MIXER", "Switch", "DMIC5" }, + + { "DMIC6_OUTPUT", NULL, "DMIC6_MIXER" }, + { "DMIC6_MIXER", "Switch", "DMIC6" }, +}; + static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) { int vout_ctl[3]; @@ -2545,6 +2616,13 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) dev_err(component->dev, "Failed to add snd_ctls\n"); return ret; } + + ret = snd_soc_dapm_add_routes(dapm, wcd9375_audio_map, + ARRAY_SIZE(wcd9375_audio_map)); + if (ret < 0) { + dev_err(component->dev, "Failed to add routes\n"); + return ret; + } } ret = wcd937x_mbhc_init(component); @@ -2588,6 +2666,8 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd937x = { .num_controls = ARRAY_SIZE(wcd937x_snd_controls), .dapm_widgets = wcd937x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets), + .dapm_routes = wcd937x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd937x_audio_map), .set_jack = wcd937x_codec_set_jack, .endianness = 1, }; -- cgit v1.2.3 From 0b7e448119428e1dcb854abb5855f66966fb82dc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 28 May 2024 14:29:33 -0500 Subject: ACPI: utils: introduce acpi_get_local_u64_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACPI _ADR is a 64-bit value. We changed the definitions in commit ca6f998cf9a2 ("ACPI: bus: change _ADR representation to 64 bits") but some helpers still assume the value is a 32-bit value. This patch adds a new helper to extract the full 64-bits. The existing 32-bit helper is kept for backwards-compatibility and cases where the _ADR is known to fit in a 32-bit value. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Acked-by: Rafael J. Wysocki Reviewed-by: Takashi Iwai Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240528192936.16180-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- drivers/acpi/utils.c | 16 +++++++++++++--- include/linux/acpi.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 202234ba54bd..ae9384282273 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -277,15 +277,25 @@ acpi_evaluate_integer(acpi_handle handle, EXPORT_SYMBOL(acpi_evaluate_integer); -int acpi_get_local_address(acpi_handle handle, u32 *addr) +int acpi_get_local_u64_address(acpi_handle handle, u64 *addr) { - unsigned long long adr; acpi_status status; - status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, addr); if (ACPI_FAILURE(status)) return -ENODATA; + return 0; +} +EXPORT_SYMBOL(acpi_get_local_u64_address); + +int acpi_get_local_address(acpi_handle handle, u32 *addr) +{ + u64 adr; + int ret; + ret = acpi_get_local_u64_address(handle, &adr); + if (ret < 0) + return ret; *addr = (u32)adr; return 0; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 28c3fb2bef0d..65e7177bcb02 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -761,6 +761,7 @@ static inline u64 acpi_arch_get_root_pointer(void) } #endif +int acpi_get_local_u64_address(acpi_handle handle, u64 *addr); int acpi_get_local_address(acpi_handle handle, u32 *addr); const char *acpi_get_subsystem_id(acpi_handle handle); -- cgit v1.2.3 From b6212f9bf489daf9716aed0e8c4dc6a807ce839f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 28 May 2024 14:29:34 -0500 Subject: soundwire: slave: simplify code with acpi_get_local_u64_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a helper so there's no need to open-code. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Acked-by: Vinod Koul Reviewed-by: Takashi Iwai Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240528192936.16180-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/slave.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 9963b92eb505..f1a4df6cfebd 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -97,18 +97,13 @@ static bool find_slave(struct sdw_bus *bus, struct acpi_device *adev, struct sdw_slave_id *id) { - u64 addr; unsigned int link_id; - acpi_status status; - - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); + u64 addr; + int ret; - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); + ret = acpi_get_local_u64_address(adev->handle, &addr); + if (ret < 0) return false; - } if (bus->ops->override_adr) addr = bus->ops->override_adr(bus, addr); -- cgit v1.2.3 From 9b7dc68eeba04d20f4a1733e791bc71355423612 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 28 May 2024 14:29:35 -0500 Subject: ALSA: hda: intel-sdw-acpi: use acpi_get_local_u64_address() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have a helper so there's no need to open-code. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Takashi Iwai Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240528192936.16180-4-pierre-louis.bossart@linux.intel.com Acked-by: Vinod Koul Signed-off-by: Mark Brown --- sound/hda/intel-sdw-acpi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index d7417a40392b..f3b2a610df23 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -125,11 +125,11 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { struct sdw_intel_acpi_info *info = cdata; - acpi_status status; u64 adr; + int ret; - status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); - if (ACPI_FAILURE(status)) + ret = acpi_get_local_u64_address(handle, &adr); + if (ret < 0) return AE_OK; /* keep going */ if (!acpi_fetch_acpi_dev(handle)) { -- cgit v1.2.3 From d52378dda71cd9a0a25f863ffb9ccc4280133a4f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:24 +0300 Subject: ASoC: codecs: Remove unused of_gpio.h of_gpio.h is deprecated and subject to remove. The drivers in question don't use it, simply remove the unused header. Reviewed-by: Kuninori Morimoto Reviewed-by: Charles Keepax Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-2-andriy.shevchenko@linux.intel.com Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/codecs/ak4118.c | 1 - sound/soc/codecs/ak4458.c | 1 - sound/soc/codecs/aw88399.c | 2 +- sound/soc/codecs/cs53l30.c | 1 - sound/soc/codecs/max98390.c | 1 - sound/soc/codecs/pcm3168a.c | 1 - sound/soc/codecs/rk817_codec.c | 1 - sound/soc/codecs/tas2552.c | 1 - sound/soc/codecs/tas2764.c | 1 - sound/soc/codecs/tas2770.c | 1 - sound/soc/codecs/tas2780.c | 1 - sound/soc/codecs/tlv320adc3xxx.c | 1 - sound/soc/codecs/tlv320adcx140.c | 1 - sound/soc/codecs/tlv320aic31xx.c | 1 - sound/soc/codecs/ts3a227e.c | 1 - sound/soc/codecs/wsa883x.c | 1 - 16 files changed, 1 insertion(+), 16 deletions(-) diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 9a43235e6a11..23e868e4e3fb 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 73cf482f104f..32cb802ad635 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 9fcb805bf971..df6d52a1cfef 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include "aw88399.h" diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index c0893146423b..2ee13d885fdc 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 57fa2db1e148..1bae253618fd 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 9d6431338fb7..3c0e0fdbfc5c 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index d4da98469f8b..5fea600bc3a4 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index a7ed59ec49a6..9e68afc09897 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 1dc719d726ab..5eaddf07aadc 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 67bc1c8b0131..5601fba17c96 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index a18ccf5fb7ad..6902bfef185b 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index e100cc9f5c19..eb180df9a72a 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 41342b340680..d594bf166c0e 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 4d7c5a80c6ed..2f94cfda0e33 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index dbf448dd8864..b9eb59e3bfa0 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index a2e86ef7d18f..8abce2160d65 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From f10d0956bc0460744fa4c085cff41ce862619a2c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:25 +0300 Subject: ASoC: fsl: Remove unused of_gpio.h of_gpio.h is deprecated and subject to remove. The drivers in question don't use it, simply remove the unused header. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-3-andriy.shevchenko@linux.intel.com Reviewed-by: Kuninori Morimoto Reviewed-by: Charles Keepax Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/fsl/imx-es8328.c | 1 - sound/soc/fsl/imx-rpmsg.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 5b9648f3b087..3ef92f6dfc6b 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 0f1ad7ad7d27..ce98d2288193 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -5,9 +5,7 @@ #include #include #include -#include #include -#include #include #include #include -- cgit v1.2.3 From 640557f3de900974d35a46f5a7b0f2161fe2ba35 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:26 +0300 Subject: ASoC: rockchip: Remove unused of_gpio.h of_gpio.h is deprecated and subject to remove. The drivers in question don't use it, simply remove the unused header. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-4-andriy.shevchenko@linux.intel.com Reviewed-by: Kuninori Morimoto Reviewed-by: Charles Keepax Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 1 - sound/soc/rockchip/rockchip_spdif.c | 1 - 2 files changed, 2 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b0c3ef030e06..b378f870b3ad 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 1a24b78e9e02..eb9d5dee196e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From e9bf7ed59d2e4a1d071f11ff8d04895377970e42 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:27 +0300 Subject: ASoC: codecs: Replace of_gpio.h by proper one of_gpio.h is deprecated and subject to remove. The driver doesn't use it directly, replace it with what is really being used. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-5-andriy.shevchenko@linux.intel.com Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/codecs/aw88395/aw88395.c | 2 +- sound/soc/codecs/aw88399.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index 3c459a67ad0c..be6ebcb51cca 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -8,9 +8,9 @@ // Author: Weidong Wang // +#include #include #include -#include #include #include #include "aw88395.h" diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index df6d52a1cfef..5d8481612eab 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -8,9 +8,9 @@ // #include +#include #include #include -#include #include #include #include "aw88399.h" -- cgit v1.2.3 From 7a31da79c13ef6449c84f6007d81d1c082dd3aaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:28 +0300 Subject: ASoC: generic: Replace of_gpio.h by proper one of_gpio.h is deprecated and subject to remove. The driver doesn't use it directly, replace it with what is really being used. Acked-by: Kuninori Morimoto Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-6-andriy.shevchenko@linux.intel.com Reviewed-by: Kuninori Morimoto Reviewed-by: Charles Keepax Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2-custom-sample.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.c b/sound/soc/generic/audio-graph-card2-custom-sample.c index 1b6ccd2de964..8e5a51098490 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.c +++ b/sound/soc/generic/audio-graph-card2-custom-sample.c @@ -5,8 +5,9 @@ // Copyright (C) 2020 Renesas Electronics Corp. // Copyright (C) 2020 Kuninori Morimoto // +#include +#include #include -#include #include #include -- cgit v1.2.3 From 7f4c7e43453fe0bcd0d40bcab8567e0dac5f50b8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Jun 2024 00:27:29 +0300 Subject: ASoC: samsung: Replace of_gpio.h by proper one of_gpio.h is deprecated and subject to remove. The driver doesn't use it directly, replace it with what is really being used. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240605221446.2624964-7-andriy.shevchenko@linux.intel.com Reviewed-by: Kuninori Morimoto Reviewed-by: Charles Keepax Acked-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/samsung/aries_wm8994.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index a548ac33dd94..01716df0c842 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ #include +#include #include #include #include #include #include -#include #include #include #include -- cgit v1.2.3 From b97e40905580c4585faa491189214e74101ca2ad Mon Sep 17 00:00:00 2001 From: Mithil Bavishi Date: Sat, 8 Jun 2024 15:23:02 +0530 Subject: ASoC: dt-bindings: omap-mcpdm: Convert to DT schema Convert the OMAP4+ McPDM bindings from txt to yaml (dtschema). Drop ti,hwmods property as it is not needed since the sysc conversion. Add dma, dma-names, reg-names properties to match the DTS so as to not break the already existing ABI. Also update example node to match the existing node in the DTS. Signed-off-by: Mithil Bavishi Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240608095305.2887-1-bavishimithil@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/omap-mcpdm.txt | 30 --------- .../devicetree/bindings/sound/ti,omap4-mcpdm.yaml | 73 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 30 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/omap-mcpdm.txt create mode 100644 Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml diff --git a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt b/Documentation/devicetree/bindings/sound/omap-mcpdm.txt deleted file mode 100644 index ff98a0cb5b3f..000000000000 --- a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Texas Instruments OMAP4+ McPDM - -Required properties: -- compatible: "ti,omap4-mcpdm" -- reg: Register location and size as an array: - , - ; -- interrupts: Interrupt number for McPDM -- ti,hwmods: Name of the hwmod associated to the McPDM -- clocks: phandle for the pdmclk provider, likely <&twl6040> -- clock-names: Must be "pdmclk" - -Example: - -mcpdm: mcpdm@40132000 { - compatible = "ti,omap4-mcpdm"; - reg = <0x40132000 0x7f>, /* MPU private access */ - <0x49032000 0x7f>; /* L3 Interconnect */ - interrupts = <0 112 0x4>; - interrupt-parent = <&gic>; - ti,hwmods = "mcpdm"; -}; - -In board DTS file the pdmclk needs to be added: - -&mcpdm { - clocks = <&twl6040>; - clock-names = "pdmclk"; - status = "okay"; -}; diff --git a/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml b/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml new file mode 100644 index 000000000000..cdea0a00826a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,omap4-mcpdm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OMAP McPDM + +maintainers: + - Misael Lopez Cruz + +description: + OMAP ALSA SoC DAI driver using McPDM port used by TWL6040 + +properties: + compatible: + const: ti,omap4-mcpdm + + reg: + items: + - description: MPU access base address + - description: L3 interconnect address + + reg-names: + items: + - const: mpu + - const: dma + + interrupts: + maxItems: 1 + + dmas: + maxItems: 2 + + dma-names: + items: + - const: up_link + - const: dn_link + + clocks: + maxItems: 1 + + clock-names: + items: + - const: pdmclk + +required: + - compatible + - reg + - reg-names + - interrupts + - dmas + - dma-names + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + mcpdm@0 { + compatible = "ti,omap4-mcpdm"; + reg = <0x0 0x7f>, /* MPU private access */ + <0x49032000 0x7f>; /* L3 Interconnect */ + reg-names = "mpu", "dma"; + interrupts = ; + interrupt-parent = <&gic>; + dmas = <&sdma 65>, <&sdma 66>; + dma-names = "up_link", "dn_link"; + clocks = <&twl6040>; + clock-names = "pdmclk"; + }; -- cgit v1.2.3 From 0ac3f1a4c2a345c48d22e8eb5b5aeb9304cb11db Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Fri, 7 Jun 2024 11:27:20 +0530 Subject: ASoC: dt-bindings: ak4554: Convert to dtschema Convert the AK4554 sound codec bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240607055725.38057-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4554.txt | 11 --------- .../bindings/sound/asahi-kasei,ak4554.yaml | 27 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/ak4554.txt create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml diff --git a/Documentation/devicetree/bindings/sound/ak4554.txt b/Documentation/devicetree/bindings/sound/ak4554.txt deleted file mode 100644 index 934fa02754b3..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4554.txt +++ /dev/null @@ -1,11 +0,0 @@ -AK4554 ADC/DAC - -Required properties: - - - compatible : "asahi-kasei,ak4554" - -Example: - -ak4554-adc-dac { - compatible = "asahi-kasei,ak4554"; -}; diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml new file mode 100644 index 000000000000..c77d85df239e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4554.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4554 sound codec + +maintainers: + - Kuninori Morimoto + - Liam Girdwood + - Mark Brown + +properties: + compatible: + const: asahi-kasei,ak4554 + +required: + - compatible + +additionalProperties: false + +examples: + - | + codec { + compatible = "asahi-kasei,ak4554"; + }; -- cgit v1.2.3 From c38082bf223fb4a3f2bdf1f79650af53d3499dea Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Tue, 11 Jun 2024 14:25:56 +0100 Subject: ASoC: cs35l56: Attempt to read from cirrus,speaker-id device property first When cs35l56 is connected via cs42l43 there isn't an ACPI node for the cs35l56 so all properties are under the cs42l43 ACPI node. We're adding a property as a way for the cs42l43 driver to pass this info in via a software node. Signed-off-by: Simon Trimmer Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20240611132556.1557075-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 8af89a263594..e89027cd40d1 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -853,9 +853,16 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED); int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base) { struct gpio_descs *descs; - int speaker_id; + u32 speaker_id; int i, ret; + /* Attempt to read the speaker type from a device property first */ + ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id); + if (!ret) { + dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id); + return speaker_id; + } + /* Read the speaker type qualifier from the motherboard GPIOs */ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN); if (!descs) { -- cgit v1.2.3 From 5c33876a20e1f42471c2b6fd1804428311d35f1f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 8 Jun 2024 17:24:31 +0300 Subject: ASoC: samsung: midas_wm1811: Fix error code in probe() This accidentally returns success instead of -EINVAL. Fixes: c91d0c2e198d ("ASoC: samsung: midas_wm1811: Add GPIO-based headset jack detection") Signed-off-by: Dan Carpenter Acked-by: Artur Weber Link: https://lore.kernel.org/r/01590109-cf27-404b-88ff-b42bb73ca1c6@moroto.mountain Signed-off-by: Mark Brown --- sound/soc/samsung/midas_wm1811.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 3a269c7de169..bbfe5fef59af 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -623,7 +623,7 @@ static int midas_probe(struct platform_device *pdev) if (channel_type != IIO_VOLTAGE) { dev_err(dev, "ADC channel is not voltage\n"); - return ret; + return -EINVAL; } priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key", -- cgit v1.2.3 From a694956df4ca75d74bcd422908ddcd8e2ea3042e Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Thu, 6 Jun 2024 09:42:00 +0530 Subject: ASoC: dt-bindings: linux,spdif: Convert spdif-reciever.txt to dtschema Convert the dummy SPDIF receiver bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Acked-by: Neil Armstrong Link: https://lore.kernel.org/r/20240606041212.78428-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/linux,spdif-dit.yaml | 37 -------------------- .../devicetree/bindings/sound/linux,spdif.yaml | 39 ++++++++++++++++++++++ .../devicetree/bindings/sound/spdif-receiver.txt | 10 ------ 3 files changed, 39 insertions(+), 47 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml create mode 100644 Documentation/devicetree/bindings/sound/linux,spdif.yaml delete mode 100644 Documentation/devicetree/bindings/sound/spdif-receiver.txt diff --git a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml b/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml deleted file mode 100644 index fe5f0756af2f..000000000000 --- a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/linux,spdif-dit.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Dummy SPDIF Transmitter - -maintainers: - - Mark Brown - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - const: linux,spdif-dit - - "#sound-dai-cells": - const: 0 - - sound-name-prefix: true - -required: - - "#sound-dai-cells" - - compatible - -additionalProperties: false - -examples: - - | - spdif-out { - #sound-dai-cells = <0>; - compatible = "linux,spdif-dit"; - }; - -... diff --git a/Documentation/devicetree/bindings/sound/linux,spdif.yaml b/Documentation/devicetree/bindings/sound/linux,spdif.yaml new file mode 100644 index 000000000000..0f4893e11ec4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/linux,spdif.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/linux,spdif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dummy SPDIF Transmitter/Receiver + +maintainers: + - Mark Brown + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - linux,spdif-dit + - linux,spdif-dir + + "#sound-dai-cells": + const: 0 + + sound-name-prefix: true + +required: + - "#sound-dai-cells" + - compatible + +additionalProperties: false + +examples: + - | + spdif-out { + #sound-dai-cells = <0>; + compatible = "linux,spdif-dit"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/spdif-receiver.txt b/Documentation/devicetree/bindings/sound/spdif-receiver.txt deleted file mode 100644 index 80f807bf8a1d..000000000000 --- a/Documentation/devicetree/bindings/sound/spdif-receiver.txt +++ /dev/null @@ -1,10 +0,0 @@ -Device-Tree bindings for dummy spdif receiver - -Required properties: - - compatible: should be "linux,spdif-dir". - -Example node: - - codec: spdif-receiver { - compatible = "linux,spdif-dir"; - }; -- cgit v1.2.3 From 01e29260c645bb844fb73ad40a022d6647fb52a0 Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Tue, 11 Jun 2024 18:14:00 +0530 Subject: ASoC: dt-bindings: wlf,wm8782: Convert to dtschema Convert the WM8782 audio codec bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240611124405.63427-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/wlf,wm8782.yaml | 47 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/wm8782.txt | 24 ----------- 2 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8782.yaml delete mode 100644 Documentation/devicetree/bindings/sound/wm8782.txt diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml new file mode 100644 index 000000000000..d0bbdc9f9ced --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8782.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wolfson Microelectromics WM8782 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: wlf,wm8782 + + Vdda-supply: + description: Regulator for the analog power supply (2.7V - 5.5V) + + Vdd-supply: + description: Regulator for the digital power supply (2.7V - 3.6V) + + wlf,fsampen: + description: FSAMPEN pin value, 0 for low, 1 for high, 2 for disconnected. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - Vdda-supply + - Vdd-supply + +unevaluatedProperties: false + +examples: + - | + wm8782: codec { + compatible = "wlf,wm8782"; + Vdda-supply = <&vdda_supply>; + Vdd-supply = <&vdd_supply>; + wlf,fsampen = <2>; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8782.txt b/Documentation/devicetree/bindings/sound/wm8782.txt deleted file mode 100644 index 1a28f3280972..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8782.txt +++ /dev/null @@ -1,24 +0,0 @@ -WM8782 stereo ADC - -This device does not have any control interface or reset pins. - -Required properties: - - - compatible : "wlf,wm8782" - - Vdda-supply : phandle to a regulator for the analog power supply (2.7V - 5.5V) - - Vdd-supply : phandle to a regulator for the digital power supply (2.7V - 3.6V) - -Optional properties: - - - wlf,fsampen: - FSAMPEN pin value, 0 for low, 1 for high, 2 for disconnected. - Defaults to 0 if left unspecified. - -Example: - -wm8782: stereo-adc { - compatible = "wlf,wm8782"; - Vdda-supply = <&vdda_supply>; - Vdd-supply = <&vdd_supply>; - wlf,fsampen = <2>; /* 192KHz */ -}; -- cgit v1.2.3 From e8ffc08366f9787a69ac4b4fb3bfa0f837cd846a Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Tue, 11 Jun 2024 17:52:51 +0530 Subject: ASoC: dt-bindings: wlf,wm8804: Convert to dtschema Convert the WM8804 audio codec bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240611122258.47406-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/wlf,wm8804.yaml | 58 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/wm8804.txt | 25 ---------- 2 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8804.yaml delete mode 100644 Documentation/devicetree/bindings/sound/wm8804.txt diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml new file mode 100644 index 000000000000..3c060179f06e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8804.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WM8804 audio codec + +description: | + This device supports both I2C and SPI (configured with pin strapping on the + board). + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + const: wlf,wm8804 + + reg: + description: + The I2C address of the device for I2C, the chip select number for SPI. + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + PVDD-supply: + description: PLL core supply + + DVDD-supply: + description: Digital core supply + + wlf,reset-gpio: + description: A GPIO specifier for the GPIO controlling the reset pin. + maxItems: 1 + +required: + - reg + - compatible + - PVDD-supply + - DVDD-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "wlf,wm8804"; + reg = <0x1a>; + PVDD-supply = <&pvdd_reg>; + DVDD-supply = <&dvdd_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8804.txt b/Documentation/devicetree/bindings/sound/wm8804.txt deleted file mode 100644 index 2c1641c17a91..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8804.txt +++ /dev/null @@ -1,25 +0,0 @@ -WM8804 audio CODEC - -This device supports both I2C and SPI (configured with pin strapping -on the board). - -Required properties: - - - compatible : "wlf,wm8804" - - - reg : the I2C address of the device for I2C, the chip select - number for SPI. - - - PVDD-supply, DVDD-supply : Power supplies for the device, as covered - in Documentation/devicetree/bindings/regulator/regulator.txt - -Optional properties: - - - wlf,reset-gpio: A GPIO specifier for the GPIO controlling the reset pin - -Example: - -wm8804: codec@1a { - compatible = "wlf,wm8804"; - reg = <0x1a>; -}; -- cgit v1.2.3 From fe833e4397fbdc3ae13a60202dfc7c335b032499 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 20:26:26 -0700 Subject: ASoC: amd: add missing MODULE_DESCRIPTION() macros With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/renoir/snd-acp3x-rn.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/yc/snd-soc-acp6x-mach.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/acp/snd-acp-i2s.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/acp/snd-acp-pdm.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/acp/snd-acp-legacy-common.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/acp/snd-acp-pci.o WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/amd/ps/snd-soc-ps-mach.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240612-md-sound-soc-amd-v1-1-ad1de0409c11@quicinc.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 1 + sound/soc/amd/acp/acp-legacy-common.c | 1 + sound/soc/amd/acp/acp-pci.c | 1 + sound/soc/amd/acp/acp-pdm.c | 1 + sound/soc/amd/ps/ps-mach.c | 1 + sound/soc/amd/renoir/acp3x-rn.c | 1 + sound/soc/amd/yc/acp6x-mach.c | 1 + 7 files changed, 7 insertions(+) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 60cbc881be6e..0bc8617e922a 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -616,5 +616,6 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { }; EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP Audio I2S controller"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index 3be7c6d55a6f..4422cec81e3c 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -475,4 +475,5 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) } EXPORT_SYMBOL_NS_GPL(check_acp_config, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP legacy common features"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index ad320b29e87d..565623afd42e 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -247,6 +247,7 @@ static struct pci_driver snd_amd_acp_pci_driver = { }; module_pci_driver(snd_amd_acp_pci_driver); +MODULE_DESCRIPTION("AMD ACP common PCI support"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index f754bf79b5e3..bb79269c2fc1 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -178,5 +178,6 @@ const struct snd_soc_dai_ops acp_dmic_dai_ops = { }; EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP Audio PDM controller"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c index e675b8f569eb..ff8ad036b077 100644 --- a/sound/soc/amd/ps/ps-mach.c +++ b/sound/soc/amd/ps/ps-mach.c @@ -75,5 +75,6 @@ static struct platform_driver acp63_mach_driver = { module_platform_driver(acp63_mach_driver); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); +MODULE_DESCRIPTION("AMD Pink Sardine support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/renoir/acp3x-rn.c b/sound/soc/amd/renoir/acp3x-rn.c index 5d979a7b77fb..3249f74a0197 100644 --- a/sound/soc/amd/renoir/acp3x-rn.c +++ b/sound/soc/amd/renoir/acp3x-rn.c @@ -72,5 +72,6 @@ static struct platform_driver acp_mach_driver = { module_platform_driver(acp_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD Renoir support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 1760b5d42460..4b32517c1b7c 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -504,5 +504,6 @@ static struct platform_driver acp6x_mach_driver = { module_platform_driver(acp6x_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD Yellow Carp support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3 From a9c8ca5d5d8452ca38300c7fb74bf8735d08f43b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 11 Jun 2024 09:30:04 +0200 Subject: ASoC: dt-bindings: convert tas571x.txt to dt-schema Convert the text bindings for the Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers to dt-schema. Signed-off-by: Neil Armstrong Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240611-topic-amlogic-upstream-bindings-convert-tas57xx-v2-1-38269edc1d95@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tas571x.txt | 49 -------- .../devicetree/bindings/sound/ti,tas57xx.yaml | 133 +++++++++++++++++++++ 2 files changed, 133 insertions(+), 49 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/tas571x.txt create mode 100644 Documentation/devicetree/bindings/sound/ti,tas57xx.yaml diff --git a/Documentation/devicetree/bindings/sound/tas571x.txt b/Documentation/devicetree/bindings/sound/tas571x.txt deleted file mode 100644 index 1addc75989d5..000000000000 --- a/Documentation/devicetree/bindings/sound/tas571x.txt +++ /dev/null @@ -1,49 +0,0 @@ -Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers - -The codec is controlled through an I2C interface. It also has two other -signals that can be wired up to GPIOs: reset (strongly recommended), and -powerdown (optional). - -Required properties: - -- compatible: should be one of the following: - - "ti,tas5707" - - "ti,tas5711", - - "ti,tas5717", - - "ti,tas5719", - - "ti,tas5721" - - "ti,tas5733" -- reg: The I2C address of the device -- #sound-dai-cells: must be equal to 0 - -Optional properties: - -- reset-gpios: GPIO specifier for the TAS571x's active low reset line -- pdn-gpios: GPIO specifier for the TAS571x's active low powerdown line -- clocks: clock phandle for the MCLK input -- clock-names: should be "mclk" -- AVDD-supply: regulator phandle for the AVDD supply (all chips) -- DVDD-supply: regulator phandle for the DVDD supply (all chips) -- HPVDD-supply: regulator phandle for the HPVDD supply (5717/5719) -- PVDD_AB-supply: regulator phandle for the PVDD_AB supply (5717/5719) -- PVDD_CD-supply: regulator phandle for the PVDD_CD supply (5717/5719) -- PVDD_A-supply: regulator phandle for the PVDD_A supply (5711) -- PVDD_B-supply: regulator phandle for the PVDD_B supply (5711) -- PVDD_C-supply: regulator phandle for the PVDD_C supply (5711) -- PVDD_D-supply: regulator phandle for the PVDD_D supply (5711) -- DRVDD-supply: regulator phandle for the DRVDD supply (5721) -- PVDD-supply: regulator phandle for the PVDD supply (5721) - -Example: - - tas5717: audio-codec@2a { - compatible = "ti,tas5717"; - reg = <0x2a>; - #sound-dai-cells = <0>; - - reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; - pdn-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>; - - clocks = <&clk_core CLK_I2S>; - clock-names = "mclk"; - }; diff --git a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml new file mode 100644 index 000000000000..2f917238db95 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas57xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers + +maintainers: + - Neil Armstrong + +properties: + compatible: + enum: + - ti,tas5707 + - ti,tas5711 + - ti,tas5717 + - ti,tas5719 + - ti,tas5721 + - ti,tas5733 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: GPIO for the active low reset line + + pdn-gpios: + maxItems: 1 + description: GPIO for the active low powerdown line + + clocks: + maxItems: 1 + + clock-names: + const: mclk + + AVDD-supply: true + DVDD-supply: true + HPVDD-supply: true + PVDD_AB-supply: true + PVDD_CD-supply: true + PVDD_A-supply: true + PVDD_B-supply: true + PVDD_C-supply: true + PVDD_D-supply: true + DRVDD-supply: true + PVDD-supply: true + + '#sound-dai-cells': + const: 0 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - '#sound-dai-cells' + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - ti,tas5717 + - ti,tas5719 + then: + properties: + PVDD_A-supply: false + PVDD_B-supply: false + PVDD_C-supply: false + PVDD_D-supply: false + DRVDD-supply: false + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - ti,tas5711 + then: + properties: + HPVDD-supply: false + PVDD_AB-supply: false + PVDD_CD-supply: false + DRVDD-supply: false + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - ti,tas5721 + then: + properties: + HPVDD-supply: false + PVDD_AB-supply: false + PVDD_CD-supply: false + PVDD_A-supply: false + PVDD_B-supply: false + PVDD_C-supply: false + PVDD_D-supply: false + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@2a { + compatible = "ti,tas5717"; + reg = <0x2a>; + #sound-dai-cells = <0>; + reset-gpios = <&gpio1 15 0>; + pdn-gpios = <&gpio1 15 0>; + AVDD-supply = <&avdd_supply>; + DVDD-supply = <&dvdd_supply>; + HPVDD-supply = <&hpvdd_supply>; + PVDD_AB-supply = <&pvdd_ab_supply>; + PVDD_CD-supply = <&pvdd_cd_supply>; + }; + }; + +... -- cgit v1.2.3 From 7fbc4f5e633cbc23576c32ff9773faa1788afc2d Mon Sep 17 00:00:00 2001 From: Abdulrasaq Lawani Date: Wed, 12 Jun 2024 12:36:50 -0400 Subject: dt-bindings: sound: Convert max98088 to dtschema Convert the max98088 audio codec txt bindings to DT schema. Signed-off-by: Abdulrasaq Lawani Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240612163730.515819-1-abdulrasaqolawani@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/maxim,max98088.txt | 23 ----------- .../devicetree/bindings/sound/maxim,max98088.yaml | 47 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/maxim,max98088.txt create mode 100644 Documentation/devicetree/bindings/sound/maxim,max98088.yaml diff --git a/Documentation/devicetree/bindings/sound/maxim,max98088.txt b/Documentation/devicetree/bindings/sound/maxim,max98088.txt deleted file mode 100644 index da764d913319..000000000000 --- a/Documentation/devicetree/bindings/sound/maxim,max98088.txt +++ /dev/null @@ -1,23 +0,0 @@ -MAX98088 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible: "maxim,max98088" or "maxim,max98089". -- reg: The I2C address of the device. - -Optional properties: - -- clocks: the clock provider of MCLK, see ../clock/clock-bindings.txt section - "consumer" for more information. -- clock-names: must be set to "mclk" - -Example: - -max98089: codec@10 { - compatible = "maxim,max98089"; - reg = <0x10>; - clocks = <&clks IMX6QDL_CLK_CKO2>; - clock-names = "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98088.yaml b/Documentation/devicetree/bindings/sound/maxim,max98088.yaml new file mode 100644 index 000000000000..e4a2967e1e81 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/maxim,max98088.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/maxim,max98088.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAX98088 audio CODEC + +maintainers: + - Abdulrasaq Lawani + +properties: + compatible: + enum: + - maxim,max98088 + - maxim,max98089 + + reg: + maxItems: 1 + + clocks: + items: + - description: master clock + + clock-names: + items: + - const: mclk + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@10 { + compatible = "maxim,max98089"; + reg = <0x10>; + clocks = <&clks 0>; + clock-names = "mclk"; + }; + }; -- cgit v1.2.3 From 378918d5918116b95300dd7f03913a1d0841f223 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 13 Jun 2024 11:49:31 +0100 Subject: ASoC: codecs: lpass-macro: add helpers to get codec version LPASS Digital codec have changes in register layout across multiple versions. Add a proper way read the codec version allowint all the lpass macro drivers (tx, rx, wsa, va) to configure the registers correctly. LPASS VA macro has the required registers to read the codec version. Read the the version and make it available to other lpass codec macros using the common helper functions. Existing method of using LPASS IP version is not accurate as the same the codec versioning is totally independent of LPASS IP block versions. These helper functions should be able to provide a convient way to get the codec version, and will help scale the drivers in right direction. Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Link: https://lore.kernel.org/r/20240612-lpass-codec-v25-v4-1-f63d3676dbc4@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.c | 23 +++++++++++++++++++++++ sound/soc/codecs/lpass-macro-common.h | 35 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/lpass-va-macro.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index da1b422250b8..8b038a99a8f9 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -11,6 +11,9 @@ #include "lpass-macro-common.h" +static DEFINE_MUTEX(lpass_codec_mutex); +static int lpass_codec_version; + struct lpass_macro *lpass_macro_pds_init(struct device *dev) { struct lpass_macro *l_pds; @@ -66,5 +69,25 @@ void lpass_macro_pds_exit(struct lpass_macro *pds) } EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); +void lpass_macro_set_codec_version(int version) +{ + mutex_lock(&lpass_codec_mutex); + lpass_codec_version = version; + mutex_unlock(&lpass_codec_mutex); +} +EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); + +int lpass_macro_get_codec_version(void) +{ + int ver; + + mutex_lock(&lpass_codec_mutex); + ver = lpass_codec_version; + mutex_unlock(&lpass_codec_mutex); + + return ver; +} +EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); + MODULE_DESCRIPTION("Common macro driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index d98718b3dc4b..f6f1bfe8eb77 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -18,6 +18,18 @@ enum lpass_version { LPASS_VER_11_0_0, }; +enum lpass_codec_version { + LPASS_CODEC_VERSION_1_0 = 1, + LPASS_CODEC_VERSION_1_1, + LPASS_CODEC_VERSION_1_2, + LPASS_CODEC_VERSION_2_0, + LPASS_CODEC_VERSION_2_1, + LPASS_CODEC_VERSION_2_5, + LPASS_CODEC_VERSION_2_6, + LPASS_CODEC_VERSION_2_7, + LPASS_CODEC_VERSION_2_8, +}; + struct lpass_macro { struct device *macro_pd; struct device *dcodec_pd; @@ -25,5 +37,28 @@ struct lpass_macro { struct lpass_macro *lpass_macro_pds_init(struct device *dev); void lpass_macro_pds_exit(struct lpass_macro *pds); +void lpass_macro_set_codec_version(int version); +int lpass_macro_get_codec_version(void); + +static inline const char *lpass_macro_get_codec_version_string(int version) +{ + switch (version) { + case LPASS_CODEC_VERSION_2_0: + return "v2.0"; + case LPASS_CODEC_VERSION_2_1: + return "v2.1"; + case LPASS_CODEC_VERSION_2_5: + return "v2.5"; + case LPASS_CODEC_VERSION_2_6: + return "v2.6"; + case LPASS_CODEC_VERSION_2_7: + return "v2.7"; + case LPASS_CODEC_VERSION_2_8: + return "v2.8"; + default: + break; + } + return "NA"; +} #endif /* __LPASS_MACRO_COMMON_H__ */ diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 6eceeff10bf6..a555e1e078eb 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1461,6 +1461,32 @@ undefined_rate: return dmic_sample_rate; } +static void va_macro_set_lpass_codec_version(struct va_macro *va) +{ + int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0, version; + + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &core_id_0); + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &core_id_1); + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_2, &core_id_2); + + if ((core_id_0 == 0x01) && (core_id_1 == 0x0F)) + version = LPASS_CODEC_VERSION_2_0; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0E)) + version = LPASS_CODEC_VERSION_2_1; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x50 || core_id_2 == 0x51)) + version = LPASS_CODEC_VERSION_2_5; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x60 || core_id_2 == 0x61)) + version = LPASS_CODEC_VERSION_2_6; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x70 || core_id_2 == 0x71)) + version = LPASS_CODEC_VERSION_2_7; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81)) + version = LPASS_CODEC_VERSION_2_8; + + lpass_macro_set_codec_version(version); + + dev_dbg(va->dev, "LPASS Codec Version %s\n", lpass_macro_get_codec_version_string(version)); +} + static int va_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1554,6 +1580,8 @@ static int va_macro_probe(struct platform_device *pdev) goto err_npl; } + va_macro_set_lpass_codec_version(va); + if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, -- cgit v1.2.3 From dbacef05898d65f586fb9b90ba367e6bf898d68d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 13 Jun 2024 11:49:32 +0100 Subject: ASoC: codec: lpass-rx-macro: prepare driver to accomdate new codec versions LPASS Codec v2.5 has significant changes in the rx block register strides. This is a preparatory patch to do the required changes in the existing driver to be able to accomdate these changes. Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Link: https://lore.kernel.org/r/20240612-lpass-codec-v25-v4-2-f63d3676dbc4@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 397 +++++++++++++++++++++++--------------- 1 file changed, 244 insertions(+), 153 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index f35187d69cac..360c9003a890 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -158,7 +158,7 @@ #define CDC_RX_INTR_CTRL_LEVEL0 (0x03C0) #define CDC_RX_INTR_CTRL_BYPASS0 (0x03C8) #define CDC_RX_INTR_CTRL_SET0 (0x03D0) -#define CDC_RX_RXn_RX_PATH_CTL(n) (0x0400 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CTL(rx, n) (0x0400 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_CTL (0x0400) #define CDC_RX_PATH_RESET_EN_MASK BIT(6) #define CDC_RX_PATH_CLK_EN_MASK BIT(5) @@ -166,45 +166,47 @@ #define CDC_RX_PATH_PGA_MUTE_MASK BIT(4) #define CDC_RX_PATH_PGA_MUTE_ENABLE BIT(4) #define CDC_RX_PATH_PCM_RATE_MASK GENMASK(3, 0) -#define CDC_RX_RXn_RX_PATH_CFG0(n) (0x0404 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG0(rx, n) (0x0404 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_COMP_EN_MASK BIT(1) #define CDC_RX_RX0_RX_PATH_CFG0 (0x0404) #define CDC_RX_RXn_CLSH_EN_MASK BIT(6) #define CDC_RX_DLY_ZN_EN_MASK BIT(3) #define CDC_RX_DLY_ZN_ENABLE BIT(3) #define CDC_RX_RXn_HD2_EN_MASK BIT(2) -#define CDC_RX_RXn_RX_PATH_CFG1(n) (0x0408 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG1(rx, n) (0x0408 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_SIDETONE_EN_MASK BIT(4) #define CDC_RX_RX0_RX_PATH_CFG1 (0x0408) #define CDC_RX_RX0_HPH_L_EAR_SEL_MASK BIT(1) -#define CDC_RX_RXn_RX_PATH_CFG2(n) (0x040C + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG2(rx, n) (0x040C + rx->rxn_reg_stride * n) #define CDC_RX_RXn_HPF_CUT_FREQ_MASK GENMASK(1, 0) #define CDC_RX_RX0_RX_PATH_CFG2 (0x040C) -#define CDC_RX_RXn_RX_PATH_CFG3(n) (0x0410 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG3(rx, n) (0x0410 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_CFG3 (0x0410) #define CDC_RX_DC_COEFF_SEL_MASK GENMASK(1, 0) #define CDC_RX_DC_COEFF_SEL_TWO 0x2 -#define CDC_RX_RXn_RX_VOL_CTL(n) (0x0414 + 0x80 * n) +#define CDC_RX_RXn_RX_VOL_CTL(rx, n) (0x0414 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_VOL_CTL (0x0414) -#define CDC_RX_RXn_RX_PATH_MIX_CTL(n) (0x0418 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_MIX_CTL(rx, n) (0x0418 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_MIX_PCM_RATE_MASK GENMASK(3, 0) #define CDC_RX_RXn_MIX_RESET_MASK BIT(6) #define CDC_RX_RXn_MIX_RESET BIT(6) #define CDC_RX_RXn_MIX_CLK_EN_MASK BIT(5) #define CDC_RX_RX0_RX_PATH_MIX_CTL (0x0418) #define CDC_RX_RX0_RX_PATH_MIX_CFG (0x041C) -#define CDC_RX_RXn_RX_VOL_MIX_CTL(n) (0x0420 + 0x80 * n) +#define CDC_RX_RXn_RX_VOL_MIX_CTL(rx, n) (0x0420 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_VOL_MIX_CTL (0x0420) #define CDC_RX_RX0_RX_PATH_SEC1 (0x0424) #define CDC_RX_RX0_RX_PATH_SEC2 (0x0428) #define CDC_RX_RX0_RX_PATH_SEC3 (0x042C) +#define CDC_RX_RXn_RX_PATH_SEC3(rx, n) (0x042c + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_SEC4 (0x0430) #define CDC_RX_RX0_RX_PATH_SEC7 (0x0434) +#define CDC_RX_RXn_RX_PATH_SEC7(rx, n) (0x0434 + rx->rxn_reg_stride * n) #define CDC_RX_DSM_OUT_DELAY_SEL_MASK GENMASK(2, 0) #define CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE 0x2 #define CDC_RX_RX0_RX_PATH_MIX_SEC0 (0x0438) #define CDC_RX_RX0_RX_PATH_MIX_SEC1 (0x043C) -#define CDC_RX_RXn_RX_PATH_DSM_CTL(n) (0x0440 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_DSM_CTL(rx, n) (0x0440 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_DSM_CLK_EN_MASK BIT(0) #define CDC_RX_RX0_RX_PATH_DSM_CTL (0x0440) #define CDC_RX_RX0_RX_PATH_DSM_DATA1 (0x0444) @@ -213,6 +215,7 @@ #define CDC_RX_RX0_RX_PATH_DSM_DATA4 (0x0450) #define CDC_RX_RX0_RX_PATH_DSM_DATA5 (0x0454) #define CDC_RX_RX0_RX_PATH_DSM_DATA6 (0x0458) +/* RX offsets prior to 2.5 codec version */ #define CDC_RX_RX1_RX_PATH_CTL (0x0480) #define CDC_RX_RX1_RX_PATH_CFG0 (0x0484) #define CDC_RX_RX1_RX_PATH_CFG1 (0x0488) @@ -598,6 +601,8 @@ struct rx_macro { int rx_mclk_users; int clsh_users; int rx_mclk_cnt; + int codec_version; + int rxn_reg_stride; bool is_ear_mode_on; bool hph_pwr_mode; bool hph_hd2_mode; @@ -976,49 +981,6 @@ static const struct reg_default rx_defaults[] = { { CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55 }, { CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55 }, { CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55 }, - { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, - { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, - { CDC_RX_RX1_RX_PATH_CFG1, 0x64 }, - { CDC_RX_RX1_RX_PATH_CFG2, 0x8F }, - { CDC_RX_RX1_RX_PATH_CFG3, 0x00 }, - { CDC_RX_RX1_RX_VOL_CTL, 0x00 }, - { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, - { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, - { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC1, 0x08 }, - { CDC_RX_RX1_RX_PATH_SEC2, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC3, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC4, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC7, 0x00 }, - { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, - { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, - { CDC_RX_RX2_RX_PATH_CTL, 0x04 }, - { CDC_RX_RX2_RX_PATH_CFG0, 0x00 }, - { CDC_RX_RX2_RX_PATH_CFG1, 0x64 }, - { CDC_RX_RX2_RX_PATH_CFG2, 0x8F }, - { CDC_RX_RX2_RX_PATH_CFG3, 0x00 }, - { CDC_RX_RX2_RX_VOL_CTL, 0x00 }, - { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, - { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, - { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC0, 0x04 }, - { CDC_RX_RX2_RX_PATH_SEC1, 0x08 }, - { CDC_RX_RX2_RX_PATH_SEC2, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC3, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC4, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC5, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC6, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC7, 0x00 }, - { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, - { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, - { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, { CDC_RX_IDLE_DETECT_PATH_CTL, 0x00 }, { CDC_RX_IDLE_DETECT_CFG0, 0x07 }, { CDC_RX_IDLE_DETECT_CFG1, 0x3C }, @@ -1121,6 +1083,53 @@ static const struct reg_default rx_defaults[] = { { CDC_RX_DSD1_CFG2, 0x96 }, }; +static const struct reg_default rx_pre_2_5_defaults[] = { + { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX1_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX1_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX1_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX1_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX1_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_RX_RX2_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX2_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX2_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX2_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX2_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC0, 0x04 }, + { CDC_RX_RX2_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX2_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC5, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC6, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, + +}; + static bool rx_is_wronly_register(struct device *dev, unsigned int reg) { @@ -1175,8 +1184,62 @@ static bool rx_is_volatile_register(struct device *dev, unsigned int reg) return false; } +static bool rx_pre_2_5_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_RX_RX1_RX_PATH_CTL: + case CDC_RX_RX1_RX_PATH_CFG0: + case CDC_RX_RX1_RX_PATH_CFG1: + case CDC_RX_RX1_RX_PATH_CFG2: + case CDC_RX_RX1_RX_PATH_CFG3: + case CDC_RX_RX1_RX_VOL_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CFG: + case CDC_RX_RX1_RX_VOL_MIX_CTL: + case CDC_RX_RX1_RX_PATH_SEC1: + case CDC_RX_RX1_RX_PATH_SEC2: + case CDC_RX_RX1_RX_PATH_SEC3: + case CDC_RX_RX1_RX_PATH_SEC4: + case CDC_RX_RX1_RX_PATH_SEC7: + case CDC_RX_RX1_RX_PATH_MIX_SEC0: + case CDC_RX_RX1_RX_PATH_MIX_SEC1: + case CDC_RX_RX1_RX_PATH_DSM_CTL: + case CDC_RX_RX1_RX_PATH_DSM_DATA1: + case CDC_RX_RX1_RX_PATH_DSM_DATA2: + case CDC_RX_RX1_RX_PATH_DSM_DATA3: + case CDC_RX_RX1_RX_PATH_DSM_DATA4: + case CDC_RX_RX1_RX_PATH_DSM_DATA5: + case CDC_RX_RX1_RX_PATH_DSM_DATA6: + case CDC_RX_RX2_RX_PATH_CTL: + case CDC_RX_RX2_RX_PATH_CFG0: + case CDC_RX_RX2_RX_PATH_CFG1: + case CDC_RX_RX2_RX_PATH_CFG2: + case CDC_RX_RX2_RX_PATH_CFG3: + case CDC_RX_RX2_RX_VOL_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CFG: + case CDC_RX_RX2_RX_VOL_MIX_CTL: + case CDC_RX_RX2_RX_PATH_SEC0: + case CDC_RX_RX2_RX_PATH_SEC1: + case CDC_RX_RX2_RX_PATH_SEC2: + case CDC_RX_RX2_RX_PATH_SEC3: + case CDC_RX_RX2_RX_PATH_SEC4: + case CDC_RX_RX2_RX_PATH_SEC5: + case CDC_RX_RX2_RX_PATH_SEC6: + case CDC_RX_RX2_RX_PATH_SEC7: + case CDC_RX_RX2_RX_PATH_MIX_SEC0: + case CDC_RX_RX2_RX_PATH_MIX_SEC1: + case CDC_RX_RX2_RX_PATH_DSM_CTL: + return true; + } + + return false; +} + static bool rx_is_rw_register(struct device *dev, unsigned int reg) { + struct rx_macro *rx = dev_get_drvdata(dev); + switch (reg) { case CDC_RX_TOP_TOP_CFG0: case CDC_RX_TOP_SWR_CTRL: @@ -1306,49 +1369,6 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) case CDC_RX_RX0_RX_PATH_DSM_DATA4: case CDC_RX_RX0_RX_PATH_DSM_DATA5: case CDC_RX_RX0_RX_PATH_DSM_DATA6: - case CDC_RX_RX1_RX_PATH_CTL: - case CDC_RX_RX1_RX_PATH_CFG0: - case CDC_RX_RX1_RX_PATH_CFG1: - case CDC_RX_RX1_RX_PATH_CFG2: - case CDC_RX_RX1_RX_PATH_CFG3: - case CDC_RX_RX1_RX_VOL_CTL: - case CDC_RX_RX1_RX_PATH_MIX_CTL: - case CDC_RX_RX1_RX_PATH_MIX_CFG: - case CDC_RX_RX1_RX_VOL_MIX_CTL: - case CDC_RX_RX1_RX_PATH_SEC1: - case CDC_RX_RX1_RX_PATH_SEC2: - case CDC_RX_RX1_RX_PATH_SEC3: - case CDC_RX_RX1_RX_PATH_SEC4: - case CDC_RX_RX1_RX_PATH_SEC7: - case CDC_RX_RX1_RX_PATH_MIX_SEC0: - case CDC_RX_RX1_RX_PATH_MIX_SEC1: - case CDC_RX_RX1_RX_PATH_DSM_CTL: - case CDC_RX_RX1_RX_PATH_DSM_DATA1: - case CDC_RX_RX1_RX_PATH_DSM_DATA2: - case CDC_RX_RX1_RX_PATH_DSM_DATA3: - case CDC_RX_RX1_RX_PATH_DSM_DATA4: - case CDC_RX_RX1_RX_PATH_DSM_DATA5: - case CDC_RX_RX1_RX_PATH_DSM_DATA6: - case CDC_RX_RX2_RX_PATH_CTL: - case CDC_RX_RX2_RX_PATH_CFG0: - case CDC_RX_RX2_RX_PATH_CFG1: - case CDC_RX_RX2_RX_PATH_CFG2: - case CDC_RX_RX2_RX_PATH_CFG3: - case CDC_RX_RX2_RX_VOL_CTL: - case CDC_RX_RX2_RX_PATH_MIX_CTL: - case CDC_RX_RX2_RX_PATH_MIX_CFG: - case CDC_RX_RX2_RX_VOL_MIX_CTL: - case CDC_RX_RX2_RX_PATH_SEC0: - case CDC_RX_RX2_RX_PATH_SEC1: - case CDC_RX_RX2_RX_PATH_SEC2: - case CDC_RX_RX2_RX_PATH_SEC3: - case CDC_RX_RX2_RX_PATH_SEC4: - case CDC_RX_RX2_RX_PATH_SEC5: - case CDC_RX_RX2_RX_PATH_SEC6: - case CDC_RX_RX2_RX_PATH_SEC7: - case CDC_RX_RX2_RX_PATH_MIX_SEC0: - case CDC_RX_RX2_RX_PATH_MIX_SEC1: - case CDC_RX_RX2_RX_PATH_DSM_CTL: case CDC_RX_IDLE_DETECT_PATH_CTL: case CDC_RX_IDLE_DETECT_CFG0: case CDC_RX_IDLE_DETECT_CFG1: @@ -1435,6 +1455,16 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) return true; } + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + return rx_pre_2_5_is_rw_register(dev, reg); + default: + break; + } + return false; } @@ -1485,14 +1515,12 @@ static bool rx_is_readable_register(struct device *dev, unsigned int reg) return rx_is_rw_register(dev, reg); } -static const struct regmap_config rx_regmap_config = { +static struct regmap_config rx_regmap_config = { .name = "rx_macro", .reg_bits = 16, .val_bits = 32, /* 8 but with 32 bit read/write */ .reg_stride = 4, .cache_type = REGCACHE_FLAT, - .reg_defaults = rx_defaults, - .num_reg_defaults = ARRAY_SIZE(rx_defaults), .max_register = RX_MAX_OFFSET, .writeable_reg = rx_is_writeable_register, .volatile_reg = rx_is_volatile_register, @@ -1504,16 +1532,17 @@ static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned short look_ahead_dly_reg; unsigned int val; val = ucontrol->value.enumerated.item[0]; - if (e->reg == CDC_RX_RX0_RX_PATH_CFG1) - look_ahead_dly_reg = CDC_RX_RX0_RX_PATH_CFG0; - else if (e->reg == CDC_RX_RX1_RX_PATH_CFG1) - look_ahead_dly_reg = CDC_RX_RX1_RX_PATH_CFG0; + if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 0)) + look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0); + else if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 1)) + look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1); /* Set Look Ahead Delay */ if (val) @@ -1567,7 +1596,7 @@ static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { - int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(j); + int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(rx, j); /* sample_rate is in Hz */ snd_soc_component_update_bits(component, int_fs_reg, CDC_RX_PATH_PCM_RATE_MASK, @@ -1600,7 +1629,7 @@ static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, CDC_RX_INTX_2_SEL_MASK); if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { - int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); + int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j); snd_soc_component_update_bits(component, int_fs_reg, CDC_RX_RXn_MIX_PCM_RATE_MASK, rate_reg_val); @@ -1719,6 +1748,7 @@ static int rx_macro_get_channel_map(struct snd_soc_dai *dai, static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); uint16_t j, reg, mix_reg, dsm_reg; u16 int_mux_cfg0, int_mux_cfg1; u8 int_mux_cfg0_val, int_mux_cfg1_val; @@ -1729,9 +1759,9 @@ static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) case RX_MACRO_AIF3_PB: case RX_MACRO_AIF4_PB: for (j = 0; j < INTERP_MAX; j++) { - reg = CDC_RX_RXn_RX_PATH_CTL(j); - mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); - dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j); + reg = CDC_RX_RXn_RX_PATH_CTL(rx, j); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, j); if (mute) { snd_soc_component_update_bits(component, reg, @@ -1748,7 +1778,7 @@ static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) } if (j == INTERP_AUX) - dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 2); int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8; int_mux_cfg1 = int_mux_cfg0 + 4; @@ -1956,10 +1986,11 @@ static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); u16 gain_reg, reg; - reg = CDC_RX_RXn_RX_PATH_CTL(w->shift); - gain_reg = CDC_RX_RXn_RX_VOL_CTL(w->shift); + reg = CDC_RX_RXn_RX_PATH_CTL(rx, w->shift); + gain_reg = CDC_RX_RXn_RX_VOL_CTL(rx, w->shift); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1991,7 +2022,7 @@ static int rx_macro_config_compander(struct snd_soc_component *component, if (comp == INTERP_AUX) return 0; - pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(comp)) & 0x0F; + pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(rx, comp)) & 0x0F; if (pcm_rate < 0x06) val = 0x03; else if (pcm_rate < 0x08) @@ -2002,11 +2033,11 @@ static int rx_macro_config_compander(struct snd_soc_component *component, val = 0x00; if (SND_SOC_DAPM_EVENT_ON(event)) - snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp), CDC_RX_DC_COEFF_SEL_MASK, val); if (SND_SOC_DAPM_EVENT_OFF(event)) - snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp), CDC_RX_DC_COEFF_SEL_MASK, 0x3); if (!rx->comp_enabled[comp]) return 0; @@ -2019,14 +2050,14 @@ static int rx_macro_config_compander(struct snd_soc_component *component, CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x1); snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x0); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp), CDC_RX_RXn_COMP_EN_MASK, 0x1); } if (SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_HALT_MASK, 0x1); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp), CDC_RX_RXn_COMP_EN_MASK, 0x0); snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_CLK_EN_MASK, 0x0); @@ -2125,13 +2156,13 @@ static int rx_macro_config_aux_hpf(struct snd_soc_component *component, /* Update Aux HPF control */ if (!rx->is_aux_hpf_on) snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x00); + CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x00); } if (SND_SOC_DAPM_EVENT_OFF(event)) { /* Reset to default (HPF=ON) */ snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x04); + CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x04); } return 0; @@ -2183,7 +2214,7 @@ static int rx_macro_config_classh(struct snd_soc_component *component, CDC_RX_CLSH_DECAY_CTRL, CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 0), CDC_RX_RXn_CLSH_EN_MASK, 0x1); break; case INTERP_HPHR: @@ -2199,15 +2230,15 @@ static int rx_macro_config_classh(struct snd_soc_component *component, CDC_RX_CLSH_DECAY_CTRL, CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); snd_soc_component_write_field(component, - CDC_RX_RX1_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 1), CDC_RX_RXn_CLSH_EN_MASK, 0x1); break; case INTERP_AUX: snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 2), CDC_RX_RX2_DLY_Z_EN_MASK, 1); snd_soc_component_write_field(component, - CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 2), CDC_RX_RX2_CLSH_EN_MASK, 1); break; } @@ -2218,16 +2249,17 @@ static int rx_macro_config_classh(struct snd_soc_component *component, static void rx_macro_hd2_control(struct snd_soc_component *component, u16 interp_idx, int event) { + struct rx_macro *rx = snd_soc_component_get_drvdata(component); u16 hd2_scale_reg, hd2_enable_reg; switch (interp_idx) { case INTERP_HPHL: - hd2_scale_reg = CDC_RX_RX0_RX_PATH_SEC3; - hd2_enable_reg = CDC_RX_RX0_RX_PATH_CFG0; + hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 0); + hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0); break; case INTERP_HPHR: - hd2_scale_reg = CDC_RX_RX1_RX_PATH_SEC3; - hd2_enable_reg = CDC_RX_RX1_RX_PATH_CFG0; + hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 1); + hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1); break; } @@ -2482,7 +2514,7 @@ static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, if (interp_idx == INTERP_HPHL) { if (rx->is_ear_mode_on) snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RXn_RX_PATH_CFG1(rx, 0), CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x1); else snd_soc_component_write_field(component, @@ -2499,7 +2531,7 @@ static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RXn_RX_PATH_CFG1(rx, 0), CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x0); snd_soc_component_update_bits(component, hph_lut_bypass_reg, CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 0); @@ -2516,11 +2548,12 @@ static int rx_macro_enable_interp_clk(struct snd_soc_component *component, u16 main_reg, dsm_reg, rx_cfg2_reg; struct rx_macro *rx = snd_soc_component_get_drvdata(component); - main_reg = CDC_RX_RXn_RX_PATH_CTL(interp_idx); - dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(interp_idx); + main_reg = CDC_RX_RXn_RX_PATH_CTL(rx, interp_idx); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, interp_idx); if (interp_idx == INTERP_AUX) - dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; - rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(interp_idx); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 2); + + rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(rx, interp_idx); if (SND_SOC_DAPM_EVENT_ON(event)) { if (rx->main_clk_users[interp_idx] == 0) { @@ -2587,10 +2620,11 @@ static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); u16 gain_reg, mix_reg; - gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(w->shift); - mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(w->shift); + gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(rx, w->shift); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, w->shift); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -2621,17 +2655,18 @@ static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_PRE_PMU: rx_macro_enable_interp_clk(component, event, w->shift); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift), CDC_RX_RXn_SIDETONE_EN_MASK, 1); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(rx, w->shift), CDC_RX_PATH_CLK_EN_MASK, 1); break; case SND_SOC_DAPM_POST_PMD: - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift), CDC_RX_RXn_SIDETONE_EN_MASK, 0); rx_macro_enable_interp_clk(component, event, w->shift); break; @@ -2801,20 +2836,22 @@ static int rx_macro_iir_filter_info(struct snd_kcontrol *kcontrol, return 0; } -static const struct snd_kcontrol_new rx_macro_snd_controls[] = { - SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, - -84, 40, digital_gain), +static const struct snd_kcontrol_new rx_macro_def_snd_controls[] = { SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_RX_RX1_RX_VOL_CTL, -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_RX_RX2_RX_VOL_CTL, -84, 40, digital_gain), - SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL, - -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_RX_RX1_RX_VOL_MIX_CTL, -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_RX_RX2_RX_VOL_MIX_CTL, -84, 40, digital_gain), +}; +static const struct snd_kcontrol_new rx_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0, rx_macro_get_compander, rx_macro_set_compander), SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0, @@ -2931,6 +2968,10 @@ static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w, return 0; } +static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), +}; static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0, @@ -3003,8 +3044,6 @@ static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, &rx_int0_dem_inp_mux), - SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, - &rx_int1_dem_inp_mux), SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, &rx_int0_2_mux, rx_macro_enable_mix_path, @@ -3399,32 +3438,55 @@ static const struct snd_soc_dapm_route rx_audio_map[] = { static int rx_macro_component_probe(struct snd_soc_component *component) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct rx_macro *rx = snd_soc_component_get_drvdata(component); + const struct snd_soc_dapm_widget *widgets; + const struct snd_kcontrol_new *controls; + unsigned int num_controls; + int ret, num_widgets; snd_soc_component_init_regmap(component, rx->regmap); - snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 0), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 1), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 2), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 0), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); - snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 1), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); - snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 2), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + controls = rx_macro_def_snd_controls; + num_controls = ARRAY_SIZE(rx_macro_def_snd_controls); + widgets = rx_macro_def_dapm_widgets; + num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets); + break; + default: + return -EINVAL; + } + rx->component = component; - return 0; + ret = snd_soc_add_component_controls(component, controls, num_controls); + if (ret) + return ret; + + return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); } static int swclk_gate_enable(struct clk_hw *hw) @@ -3523,11 +3585,12 @@ static const struct snd_soc_component_driver rx_macro_component_drv = { static int rx_macro_probe(struct platform_device *pdev) { + struct reg_default *reg_defaults; struct device *dev = &pdev->dev; kernel_ulong_t flags; struct rx_macro *rx; void __iomem *base; - int ret; + int ret, def_count; flags = (kernel_ulong_t)device_get_match_data(dev); @@ -3566,11 +3629,36 @@ static int rx_macro_probe(struct platform_device *pdev) ret = PTR_ERR(base); goto err; } + rx->codec_version = lpass_macro_get_codec_version(); + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + rx->rxn_reg_stride = 0x80; + def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults); + reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); + if (!reg_defaults) { + ret = -ENOMEM; + goto err; + } + memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); + memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], + rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults)); + break; + default: + dev_err(rx->dev, "Unsupported Codec version (%d)\n", rx->codec_version); + ret = -EINVAL; + goto err; + } + + rx_regmap_config.reg_defaults = reg_defaults; + rx_regmap_config.num_reg_defaults = def_count; rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); if (IS_ERR(rx->regmap)) { ret = PTR_ERR(rx->regmap); - goto err; + goto err_ver; } dev_set_drvdata(dev, rx); @@ -3583,7 +3671,7 @@ static int rx_macro_probe(struct platform_device *pdev) ret = clk_prepare_enable(rx->macro); if (ret) - goto err; + goto err_ver; ret = clk_prepare_enable(rx->dcodec); if (ret) @@ -3629,6 +3717,7 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; + kfree(reg_defaults); return 0; err_clkout: @@ -3641,6 +3730,8 @@ err_mclk: clk_disable_unprepare(rx->dcodec); err_dcodec: clk_disable_unprepare(rx->macro); +err_ver: + kfree(reg_defaults); err: lpass_macro_pds_exit(rx->pds); -- cgit v1.2.3 From 432e5074f805d0f976c7430af376a0dd07f1c6d7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 13 Jun 2024 11:49:33 +0100 Subject: ASoC: codec: lpass-rx-macro: add support for 2.5 codec version LPASS Codec v2.5 has significant changes in the rx register strides. Due to this headset playback on SM8550, SM8650, x1e80100 and all SoCs after SM8450 have only Left working. This patch adjusts the registers to accomdate 2.5 changes. With this fixed now L and R are functional on Headset playback. Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Link: https://lore.kernel.org/r/20240612-lpass-codec-v25-v4-3-f63d3676dbc4@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 198 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 360c9003a890..d962a9599694 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -262,6 +262,53 @@ #define CDC_RX_RX2_RX_PATH_MIX_SEC0 (0x0544) #define CDC_RX_RX2_RX_PATH_MIX_SEC1 (0x0548) #define CDC_RX_RX2_RX_PATH_DSM_CTL (0x054C) + +/* LPASS CODEC version 2.5 rx reg offsets */ +#define CDC_2_5_RX_RX1_RX_PATH_CTL (0x04c0) +#define CDC_2_5_RX_RX1_RX_PATH_CFG0 (0x04c4) +#define CDC_2_5_RX_RX1_RX_PATH_CFG1 (0x04c8) +#define CDC_2_5_RX_RX1_RX_PATH_CFG2 (0x04cC) +#define CDC_2_5_RX_RX1_RX_PATH_CFG3 (0x04d0) +#define CDC_2_5_RX_RX1_RX_VOL_CTL (0x04d4) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_CTL (0x04d8) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_CFG (0x04dC) +#define CDC_2_5_RX_RX1_RX_VOL_MIX_CTL (0x04e0) +#define CDC_2_5_RX_RX1_RX_PATH_SEC1 (0x04e4) +#define CDC_2_5_RX_RX1_RX_PATH_SEC2 (0x04e8) +#define CDC_2_5_RX_RX1_RX_PATH_SEC3 (0x04eC) +#define CDC_2_5_RX_RX1_RX_PATH_SEC4 (0x04f0) +#define CDC_2_5_RX_RX1_RX_PATH_SEC7 (0x04f4) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0 (0x04f8) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1 (0x04fC) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_CTL (0x0500) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1 (0x0504) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2 (0x0508) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3 (0x050C) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4 (0x0510) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5 (0x0514) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6 (0x0518) + +#define CDC_2_5_RX_RX2_RX_PATH_CTL (0x0580) +#define CDC_2_5_RX_RX2_RX_PATH_CFG0 (0x0584) +#define CDC_2_5_RX_RX2_RX_PATH_CFG1 (0x0588) +#define CDC_2_5_RX_RX2_RX_PATH_CFG2 (0x058C) +#define CDC_2_5_RX_RX2_RX_PATH_CFG3 (0x0590) +#define CDC_2_5_RX_RX2_RX_VOL_CTL (0x0594) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_CTL (0x0598) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_CFG (0x059C) +#define CDC_2_5_RX_RX2_RX_VOL_MIX_CTL (0x05a0) +#define CDC_2_5_RX_RX2_RX_PATH_SEC0 (0x05a4) +#define CDC_2_5_RX_RX2_RX_PATH_SEC1 (0x05a8) +#define CDC_2_5_RX_RX2_RX_PATH_SEC2 (0x05aC) +#define CDC_2_5_RX_RX2_RX_PATH_SEC3 (0x05b0) +#define CDC_2_5_RX_RX2_RX_PATH_SEC4 (0x05b4) +#define CDC_2_5_RX_RX2_RX_PATH_SEC5 (0x05b8) +#define CDC_2_5_RX_RX2_RX_PATH_SEC6 (0x05bC) +#define CDC_2_5_RX_RX2_RX_PATH_SEC7 (0x05c0) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0 (0x05c4) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1 (0x05c8) +#define CDC_2_5_RX_RX2_RX_PATH_DSM_CTL (0x05cC) + #define CDC_RX_IDLE_DETECT_PATH_CTL (0x0780) #define CDC_RX_IDLE_DETECT_CFG0 (0x0784) #define CDC_RX_IDLE_DETECT_CFG1 (0x0788) @@ -764,6 +811,8 @@ static SOC_ENUM_SINGLE_DECL(rx_int0_dem_inp_enum, CDC_RX_RX0_RX_PATH_CFG1, 0, rx_int_dem_inp_mux_text); static SOC_ENUM_SINGLE_DECL(rx_int1_dem_inp_enum, CDC_RX_RX1_RX_PATH_CFG1, 0, rx_int_dem_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_2_5_int1_dem_inp_enum, CDC_2_5_RX_RX1_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text); static SOC_ENUM_SINGLE_DECL(rx_macro_rx0_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); static SOC_ENUM_SINGLE_DECL(rx_macro_rx1_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); @@ -1083,6 +1132,52 @@ static const struct reg_default rx_defaults[] = { { CDC_RX_DSD1_CFG2, 0x96 }, }; +static const struct reg_default rx_2_5_defaults[] = { + { CDC_2_5_RX_RX1_RX_PATH_CTL, 0x04 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG0, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG1, 0x64 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG2, 0x8F }, + { CDC_2_5_RX_RX1_RX_PATH_CFG3, 0x00 }, + { CDC_2_5_RX_RX1_RX_VOL_CTL, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, + { CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC1, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC2, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC3, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC4, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC7, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_2_5_RX_RX2_RX_PATH_CTL, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG0, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG1, 0x64 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG2, 0x8F }, + { CDC_2_5_RX_RX2_RX_PATH_CFG3, 0x00 }, + { CDC_2_5_RX_RX2_RX_VOL_CTL, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, + { CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC0, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC1, 0x08 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC2, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC3, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC4, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC5, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC6, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC7, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, +}; + static const struct reg_default rx_pre_2_5_defaults[] = { { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, @@ -1236,6 +1331,58 @@ static bool rx_pre_2_5_is_rw_register(struct device *dev, unsigned int reg) return false; } +static bool rx_2_5_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_RX_RX1_RX_PATH_CTL: + case CDC_2_5_RX_RX1_RX_PATH_CFG0: + case CDC_2_5_RX_RX1_RX_PATH_CFG1: + case CDC_2_5_RX_RX1_RX_PATH_CFG2: + case CDC_2_5_RX_RX1_RX_PATH_CFG3: + case CDC_2_5_RX_RX1_RX_VOL_CTL: + case CDC_2_5_RX_RX1_RX_PATH_MIX_CTL: + case CDC_2_5_RX_RX1_RX_PATH_MIX_CFG: + case CDC_2_5_RX_RX1_RX_VOL_MIX_CTL: + case CDC_2_5_RX_RX1_RX_PATH_SEC1: + case CDC_2_5_RX_RX1_RX_PATH_SEC2: + case CDC_2_5_RX_RX1_RX_PATH_SEC3: + case CDC_2_5_RX_RX1_RX_PATH_SEC4: + case CDC_2_5_RX_RX1_RX_PATH_SEC7: + case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0: + case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1: + case CDC_2_5_RX_RX1_RX_PATH_DSM_CTL: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6: + case CDC_2_5_RX_RX2_RX_PATH_CTL: + case CDC_2_5_RX_RX2_RX_PATH_CFG0: + case CDC_2_5_RX_RX2_RX_PATH_CFG1: + case CDC_2_5_RX_RX2_RX_PATH_CFG2: + case CDC_2_5_RX_RX2_RX_PATH_CFG3: + case CDC_2_5_RX_RX2_RX_VOL_CTL: + case CDC_2_5_RX_RX2_RX_PATH_MIX_CTL: + case CDC_2_5_RX_RX2_RX_PATH_MIX_CFG: + case CDC_2_5_RX_RX2_RX_VOL_MIX_CTL: + case CDC_2_5_RX_RX2_RX_PATH_SEC0: + case CDC_2_5_RX_RX2_RX_PATH_SEC1: + case CDC_2_5_RX_RX2_RX_PATH_SEC2: + case CDC_2_5_RX_RX2_RX_PATH_SEC3: + case CDC_2_5_RX_RX2_RX_PATH_SEC4: + case CDC_2_5_RX_RX2_RX_PATH_SEC5: + case CDC_2_5_RX_RX2_RX_PATH_SEC6: + case CDC_2_5_RX_RX2_RX_PATH_SEC7: + case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0: + case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1: + case CDC_2_5_RX_RX2_RX_PATH_DSM_CTL: + return true; + } + + return false; +} + static bool rx_is_rw_register(struct device *dev, unsigned int reg) { struct rx_macro *rx = dev_get_drvdata(dev); @@ -1461,6 +1608,11 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) case LPASS_CODEC_VERSION_1_2: case LPASS_CODEC_VERSION_2_0: return rx_pre_2_5_is_rw_register(dev, reg); + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + return rx_2_5_is_rw_register(dev, reg); default: break; } @@ -1563,6 +1715,10 @@ static const struct snd_kcontrol_new rx_int1_dem_inp_mux = SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_int1_dem_inp_enum, snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); +static const struct snd_kcontrol_new rx_2_5_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_2_5_int1_dem_inp_enum, + snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); + static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, int rate_reg_val, u32 sample_rate) { @@ -2847,6 +3003,18 @@ static const struct snd_kcontrol_new rx_macro_def_snd_controls[] = { -84, 40, digital_gain), }; +static const struct snd_kcontrol_new rx_macro_2_5_snd_controls[] = { + + SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_2_5_RX_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_2_5_RX_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), +}; + static const struct snd_kcontrol_new rx_macro_snd_controls[] = { SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, -84, 40, digital_gain), @@ -2968,6 +3136,12 @@ static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w, return 0; } + +static const struct snd_soc_dapm_widget rx_macro_2_5_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_2_5_int1_dem_inp_mux), +}; + static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = { SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, &rx_int1_dem_inp_mux), @@ -3476,6 +3650,15 @@ static int rx_macro_component_probe(struct snd_soc_component *component) widgets = rx_macro_def_dapm_widgets; num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets); break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + controls = rx_macro_2_5_snd_controls; + num_controls = ARRAY_SIZE(rx_macro_2_5_snd_controls); + widgets = rx_macro_2_5_dapm_widgets; + num_widgets = ARRAY_SIZE(rx_macro_2_5_dapm_widgets); + break; default: return -EINVAL; } @@ -3646,6 +3829,21 @@ static int rx_macro_probe(struct platform_device *pdev) memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults)); break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + rx->rxn_reg_stride = 0xc0; + def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults); + reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); + if (!reg_defaults) { + ret = -ENOMEM; + goto err; + } + memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); + memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], + rx_2_5_defaults, sizeof(rx_2_5_defaults)); + break; default: dev_err(rx->dev, "Unsupported Codec version (%d)\n", rx->codec_version); ret = -EINVAL; -- cgit v1.2.3 From 39eab0148752055928c4c54db12d6cf89881e6cd Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 16 Jun 2024 20:52:26 -0700 Subject: ASoC: fsl: imx-pcm-fiq: add missing MODULE_DESCRIPTION() macro With ARCH=arm, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in sound/soc/fsl/imx-pcm-fiq.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Link: https://msgid.link/r/20240616-md-arm-sound-soc-fsl-v2-1-228772e81a54@quicinc.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-pcm-fiq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 0d124002678e..3391430e4253 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -319,4 +319,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit); +MODULE_DESCRIPTION("Freescale i.MX PCM FIQ handler"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 20346f5c7bc9f83c74b5efed163ae4e2ea7616a9 Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Sun, 16 Jun 2024 13:19:30 +0530 Subject: ASoC: dt-bindings: realtek,rt5631: Convert to dtschema Convert the ALC5631/RT5631 audio CODEC bindings to DT Schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240616074936.151267-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/realtek,rt5631.yaml | 67 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/rt5631.txt | 48 ---------------- 2 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5631.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5631.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml new file mode 100644 index 000000000000..747a731c44c9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5631.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ALC5631/RT5631 audio CODEC + +maintainers: + - Animesh Agarwal + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes): + * SPK_OUT_R_P + * SPK_OUT_R_N + * SPK_OUT_L_P + * SPK_OUT_L_N + * HP_OUT_L + * HP_OUT_R + * AUX_OUT2_LP + * AUX_OUT2_RN + * AUX_OUT1_LP + * AUX_OUT1_RN + * AUX_IN_L_JD + * AUX_IN_R_JD + * MONO_IN_P + * MONO_IN_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MONO_OUT_P + * MONO_OUT_N + * MICBIAS1 + * MICBIAS2 + +properties: + compatible: + enum: + - realtek,alc5631 + - realtek,rt5631 + + reg: + maxItems: 1 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@1a { + compatible = "realtek,alc5631"; + reg = <0x1a>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt deleted file mode 100644 index 56bc85232c49..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5631.txt +++ /dev/null @@ -1,48 +0,0 @@ -ALC5631/RT5631 audio CODEC - -This device supports I2C only. - -Required properties: - - - compatible : "realtek,alc5631" or "realtek,rt5631" - - - reg : the I2C address of the device. - -Pins on the device (for linking into audio routes): - - * SPK_OUT_R_P - * SPK_OUT_R_N - * SPK_OUT_L_P - * SPK_OUT_L_N - * HP_OUT_L - * HP_OUT_R - * AUX_OUT2_LP - * AUX_OUT2_RN - * AUX_OUT1_LP - * AUX_OUT1_RN - * AUX_IN_L_JD - * AUX_IN_R_JD - * MONO_IN_P - * MONO_IN_N - * MIC1_P - * MIC1_N - * MIC2_P - * MIC2_N - * MONO_OUT_P - * MONO_OUT_N - * MICBIAS1 - * MICBIAS2 - -Example: - -alc5631: audio-codec@1a { - compatible = "realtek,alc5631"; - reg = <0x1a>; -}; - -or - -rt5631: audio-codec@1a { - compatible = "realtek,rt5631"; - reg = <0x1a>; -}; -- cgit v1.2.3 From 2618b2ec8d140951588e4b4c8dd745676427d2cb Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Sun, 16 Jun 2024 15:22:19 +0530 Subject: ASoC: dt-bindings: realtek,rt5514: Convert to dtschema Convert the RT5514 audio CODEC bindings to DT Schema. Make bindings complete by adding 'spi-max-frequency', 'wakeup-source' properties. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240616095223.260786-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/realtek,rt5514.yaml | 70 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/rt5514.txt | 37 ------------ 2 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5514.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5514.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml new file mode 100644 index 000000000000..7fbf7739c371 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5514.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5514 audio CODEC + +maintainers: + - Animesh Agarwal + +description: | + This device supports both I2C and SPI. + + Pins on the device (for linking into audio routes) for I2C: + * DMIC1L + * DMIC1R + * DMIC2L + * DMIC2R + * AMICL + * AMICR + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - $ref: dai-common.yaml# + +properties: + compatible: + const: realtek,rt5514 + + reg: + maxItems: 1 + + clocks: + items: + - description: Master clock to the CODEC + + clock-names: + items: + - const: mclk + + interrupts: + maxItems: 1 + description: The interrupt number to the cpu. + + realtek,dmic-init-delay-ms: + description: Set the DMIC initial delay (ms) to wait it ready for I2C. + + spi-max-frequency: true + + wakeup-source: + type: boolean + description: Flag to indicate this device can wake system (suspend/resume). + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@57 { + compatible = "realtek,rt5514"; + reg = <0x57>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt deleted file mode 100644 index d2cc171f22f2..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5514.txt +++ /dev/null @@ -1,37 +0,0 @@ -RT5514 audio CODEC - -This device supports both I2C and SPI. - -Required properties: - -- compatible : "realtek,rt5514". - -- reg : the I2C address of the device for I2C, the chip select - number for SPI. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC -- clock-names: Should be "mclk" - -- interrupts: The interrupt number to the cpu. The interrupt specifier format - depends on the interrupt controller. - -- realtek,dmic-init-delay-ms - Set the DMIC initial delay (ms) to wait it ready for I2C. - -Pins on the device (for linking into audio routes) for I2C: - - * DMIC1L - * DMIC1R - * DMIC2L - * DMIC2R - * AMICL - * AMICR - -Example: - -rt5514: codec@57 { - compatible = "realtek,rt5514"; - reg = <0x57>; -}; -- cgit v1.2.3 From 1c75adb22d49ca9389333ca5e6939052a7203111 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 13 Jun 2024 22:59:09 +0200 Subject: ASoC: SOF: mediatek: Constify struct mtk_adsp_ipc_ops 'struct mtk_adsp_ipc_ops' is not modified in these drivers. Constifying this structure moves some data to a read-only section, so increase overall security. In order to do it, "struct mtk_adsp_ipc" also needs to be adjusted to this new const qualifier. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 15533 2383 0 17916 45fc sound/soc/sof/mediatek/mt8195/mt8195.o After: ===== text data bss dec hex filename 15557 2367 0 17924 4604 sound/soc/sof/mediatek/mt8195/mt8195.o Signed-off-by: Christophe JAILLET Link: https://msgid.link/r/a45d6b2b5ec040ea0fc78fca662c2dca3f13a49f.1718312321.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- include/linux/firmware/mediatek/mtk-adsp-ipc.h | 2 +- sound/soc/sof/mediatek/mt8186/mt8186.c | 2 +- sound/soc/sof/mediatek/mt8195/mt8195.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h index 5b1d16fa3f56..6e86799a7dc4 100644 --- a/include/linux/firmware/mediatek/mtk-adsp-ipc.h +++ b/include/linux/firmware/mediatek/mtk-adsp-ipc.h @@ -40,7 +40,7 @@ struct mtk_adsp_chan { struct mtk_adsp_ipc { struct mtk_adsp_chan chans[MTK_ADSP_MBOX_NUM]; struct device *dev; - struct mtk_adsp_ipc_ops *ops; + const struct mtk_adsp_ipc_ops *ops; void *private_data; }; diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index bea1b9d9ca28..74522400207e 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -82,7 +82,7 @@ static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc) } } -static struct mtk_adsp_ipc_ops dsp_ops = { +static const struct mtk_adsp_ipc_ops dsp_ops = { .handle_reply = mt8186_dsp_handle_reply, .handle_request = mt8186_dsp_handle_request, }; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 31dc98d1b1d8..24ae1d4959be 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -82,7 +82,7 @@ static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc) } } -static struct mtk_adsp_ipc_ops dsp_ops = { +static const struct mtk_adsp_ipc_ops dsp_ops = { .handle_reply = mt8195_dsp_handle_reply, .handle_request = mt8195_dsp_handle_request, }; -- cgit v1.2.3 From 195815c2755d70df92f46e6d737cd677db7adeef Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Jun 2024 04:26:38 +0000 Subject: ASoC: audio-graph-card2: add support for aux devices Add device tree property to define auxiliary devices to be added to Audio Graph Card which is already supported on Simle Card. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/878qz4ry81.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 8eea81888758..be5a4ebfddd4 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1398,6 +1398,10 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret < 0) + goto err; + ret = devm_snd_soc_register_card(dev, card); err: devm_kfree(dev, li); -- cgit v1.2.3 From d4a7d067e061c95c6387cf537258082074a4d299 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 14:57:34 +0200 Subject: ASoC: soc-dai.h: Constify DAI ops auto_selectable_formats The core ASoC code does not modify contents of the 'auto_selectable_formats' array passed in 'struct snd_soc_dai_ops', so make it const for code safety. Reviewed-by: Herve Codina Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617125735.582963-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 15ef268c9845..279223c4ef5e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -361,7 +361,7 @@ struct snd_soc_dai_ops { * see * snd_soc_dai_get_fmt() */ - u64 *auto_selectable_formats; + const u64 *auto_selectable_formats; int num_auto_selectable_formats; /* probe ordering - for components with runtime dependencies */ -- cgit v1.2.3 From 595265c92668cbdbd9f299c49907734d468c12e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 14:57:35 +0200 Subject: ASoC: Constify DAI ops auto_selectable_formats The static arrays passed as 'auto_selectable_formats' are not modified by the drivers nor by the core code, so make it const for code safety. Signed-off-by: Krzysztof Kozlowski Acked-by: Herve Codina Link: https://msgid.link/r/20240617125735.582963-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 2 +- sound/soc/codecs/da7213.c | 2 +- sound/soc/codecs/framer-codec.c | 2 +- sound/soc/codecs/hdmi-codec.c | 2 +- sound/soc/codecs/idt821034.c | 2 +- sound/soc/codecs/pcm3168a.c | 2 +- sound/soc/codecs/peb2466.c | 2 +- sound/soc/generic/test-component.c | 2 +- sound/soc/sh/fsi.c | 2 +- sound/soc/sh/rcar/core.c | 2 +- sound/soc/soc-utils.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 73fb35560e51..551738abd1a5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -753,7 +753,7 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 ak4613_dai_formats = +static const u64 ak4613_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a2b328f3b39f..f3ef6fb55304 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1720,7 +1720,7 @@ static int da7213_set_component_pll(struct snd_soc_component *component, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 da7213_dai_formats = +static const u64 da7213_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c index e5fcde9ee308..6f57a3aeecc8 100644 --- a/sound/soc/codecs/framer-codec.c +++ b/sound/soc/codecs/framer-codec.c @@ -238,7 +238,7 @@ static int framer_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 framer_dai_formats[] = { +static const u64 framer_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d3abb7ce2153..74caae52e127 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -715,7 +715,7 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) * For example, * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c */ -static u64 hdmi_codec_formats = +static const u64 hdmi_codec_formats = SND_SOC_POSSIBLE_DAIFMT_NB_NF | SND_SOC_POSSIBLE_DAIFMT_NB_IF | SND_SOC_POSSIBLE_DAIFMT_IB_NF | diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 2cc7b9166e69..cb7a68c799f8 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -860,7 +860,7 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 idt821034_dai_formats[] = { +static const u64 idt821034_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 3c0e0fdbfc5c..fac0617ab95b 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -562,7 +562,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return 0; } -static u64 pcm3168a_dai_formats[] = { +static const u64 pcm3168a_dai_formats[] = { /* * Select below from Sound Card, not here * SND_SOC_DAIFMT_CBC_CFC diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 5dec69be0acb..76ee7e3f4d9b 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -814,7 +814,7 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream, &peb2466_sample_bits_constr); } -static u64 peb2466_dai_formats[] = { +static const u64 peb2466_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index e4967540a2e1..e9e5e235a8a6 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -189,7 +189,7 @@ static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } -static u64 test_dai_formats = +static const u64 test_dai_formats = /* * Select below from Sound Card, not auto * SND_SOC_POSSIBLE_DAIFMT_BP_FP diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 84601ba43b7d..087e379aa3bc 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1713,7 +1713,7 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 fsi_dai_formats = +static const u64 fsi_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_NB_NF | diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6bc7027ed4db..63b3c8bf0fde 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1061,7 +1061,7 @@ static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream, return rsnd_dai_call(prepare, io, priv); } -static u64 rsnd_soc_dai_formats[] = { +static const u64 rsnd_soc_dai_formats[] = { /* * 1st Priority * diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index d05e712c9518..20135a8359bf 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -163,7 +163,7 @@ static const struct snd_soc_component_driver dummy_codec = { * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC */ -static u64 dummy_dai_formats = +static const u64 dummy_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -- cgit v1.2.3 From 2fbafecb0f05818e25f6c926c6f9ad9ef597429c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 15:03:19 +0200 Subject: ASoC: Constify of_phandle_args in snd_soc_dai_driver ASoC core code does not modify contents of 'of_phandle_args' in 'struct snd_soc_dai_driver', so the pointer can be made as a pointer to const. This makes code safer, serves as clear annotation of core's intentions and allows putting pointed structures in rodata (if ever applicable). Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617-n-asoc-const-auto-selectable-formats-v1-1-8004f346ee38@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 15ef268c9845..e6c61c79c483 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -413,7 +413,7 @@ struct snd_soc_dai_driver { unsigned int id; unsigned int base; struct snd_soc_dobj dobj; - struct of_phandle_args *dai_args; + const struct of_phandle_args *dai_args; /* ops */ const struct snd_soc_dai_ops *ops; -- cgit v1.2.3 From 020b37d06f97de289940805bc821190d5858eda0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 15:03:20 +0200 Subject: ASoC: Constify of_phandle_args in snd_soc_dai_link_component ASoC core code does not modify contents of 'of_phandle_args' in 'struct snd_soc_dai_link_component', so the pointer can be made as a pointer to const. This makes code safer, serves as clear annotation of core's intentions and allows putting pointed structures in rodata (if ever applicable). Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617-n-asoc-const-auto-selectable-formats-v1-2-8004f346ee38@linaro.org Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 33671437ee89..f02a51694ab4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -675,7 +675,7 @@ struct snd_soc_dai_link_component { const char *name; struct device_node *of_node; const char *dai_name; - struct of_phandle_args *dai_args; + const struct of_phandle_args *dai_args; }; /* -- cgit v1.2.3 From f3ac3da7e4d0957b3402fb31a4ca480e739e086f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 15:03:21 +0200 Subject: ASoC: Constify passed data to core function Several ASoC functions receive pointers to data which is not modified, e.g. pointers to 'snd_soc_dai', 'snd_soc_pcm_runtime', 'snd_pcm_hw_params' and 'snd_soc_dai_link'. All these pointers can be made as a pointer to const. This makes code safer, serves as clear annotation of function's intentions (no ownership passed to the function, no modifications) and allows putting pointed structures in rodata (if ever applicable). Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617-n-asoc-const-auto-selectable-formats-v1-3-8004f346ee38@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 18 ++++++++++-------- include/sound/soc.h | 17 +++++++++-------- sound/soc/soc-core.c | 4 ++-- sound/soc/soc-dai.c | 8 ++++---- sound/soc/soc-utils.c | 8 ++++---- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e6c61c79c483..3c45b418270e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -180,8 +180,8 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); /* Digital Audio interface formatting */ -int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd); -u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority); +int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd); +u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority); int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, @@ -202,7 +202,7 @@ int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); -int snd_soc_dai_is_dummy(struct snd_soc_dai *dai); +int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai); int snd_soc_dai_hw_params(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, @@ -218,7 +218,7 @@ void snd_soc_dai_suspend(struct snd_soc_dai *dai); void snd_soc_dai_resume(struct snd_soc_dai *dai); int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num); -bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream); +bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int stream); void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link *dai_link); void snd_soc_dai_action(struct snd_soc_dai *dai, int stream, int action); @@ -232,7 +232,7 @@ static inline void snd_soc_dai_deactivate(struct snd_soc_dai *dai, { snd_soc_dai_action(dai, stream, -1); } -int snd_soc_dai_active(struct snd_soc_dai *dai); +int snd_soc_dai_active(const struct snd_soc_dai *dai); int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order); int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order); @@ -271,7 +271,7 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata); -const char *snd_soc_dai_name_get(struct snd_soc_dai *dai); +const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai); struct snd_soc_dai_ops { /* DAI driver callbacks */ @@ -518,7 +518,8 @@ static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *play snd_soc_dai_dma_data_set_capture(dai, capture); } -static inline unsigned int snd_soc_dai_tdm_mask_get(struct snd_soc_dai *dai, int stream) +static inline unsigned int snd_soc_dai_tdm_mask_get(const struct snd_soc_dai *dai, + int stream) { return dai->stream[stream].tdm_mask; } @@ -529,7 +530,8 @@ static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream, dai->stream[stream].tdm_mask = tdm_mask; } -static inline unsigned int snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) +static inline unsigned int snd_soc_dai_stream_active(const struct snd_soc_dai *dai, + int stream) { /* see snd_soc_dai_action() for setup */ return dai->stream[stream].active; diff --git a/include/sound/soc.h b/include/sound/soc.h index f02a51694ab4..a8e66bbf932b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -534,10 +534,10 @@ static inline int snd_soc_set_dmi_name(struct snd_soc_card *card, /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); -int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); +int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params); int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots); -int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); -int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, +int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *parms); +int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params, int tdm_width, int tdm_slots, int slot_multiple); /* set runtime hw params */ @@ -837,7 +837,8 @@ struct snd_soc_dai_link { #endif }; -static inline int snd_soc_link_num_ch_map(struct snd_soc_dai_link *link) { +static inline int snd_soc_link_num_ch_map(const struct snd_soc_dai_link *link) +{ return max(link->num_cpus, link->num_codecs); } @@ -1299,7 +1300,7 @@ struct soc_enum { #endif }; -static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) +static inline bool snd_soc_volsw_is_stereo(const struct soc_mixer_control *mc) { if (mc->reg == mc->rreg && mc->shift == mc->rshift) return false; @@ -1311,7 +1312,7 @@ static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) return true; } -static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e, +static inline unsigned int snd_soc_enum_val_to_item(const struct soc_enum *e, unsigned int val) { unsigned int i; @@ -1326,7 +1327,7 @@ static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e, return 0; } -static inline unsigned int snd_soc_enum_item_to_val(struct soc_enum *e, +static inline unsigned int snd_soc_enum_item_to_val(const struct soc_enum *e, unsigned int item) { if (!e->values) @@ -1401,7 +1402,7 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, snd_soc_daifmt_clock_provider_from_bitmap( \ snd_soc_daifmt_parse_clock_provider_as_bitmap(np, prefix)) -int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream); +int snd_soc_get_stream_cpu(const struct snd_soc_dai_link *dai_link, int stream); int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_component *dlc); int snd_soc_of_get_dlc(struct device_node *of_node, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3ab6626ad680..724fe1f033b5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -297,7 +297,7 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, return 0; } -const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) +const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai) { /* see snd_soc_is_matching_dai() */ if (dai->driver->name) @@ -3430,7 +3430,7 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_clock_provider_raw); -int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream) +int snd_soc_get_stream_cpu(const struct snd_soc_dai_link *dai_link, int stream) { /* * [Normal] diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 03afd5efb24c..55d1a5a099df 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -134,7 +134,7 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); -int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd) +int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai; int i, max = 0; @@ -166,7 +166,7 @@ int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd) * modes. This will mean that sometimes fewer formats * are reported here than are supported by set_fmt(). */ -u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority) +u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority) { const struct snd_soc_dai_ops *ops = dai->driver->ops; u64 fmt = 0; @@ -471,7 +471,7 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, * * Returns true if the DAI supports the indicated stream type. */ -bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) +bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int dir) { struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); @@ -528,7 +528,7 @@ void snd_soc_dai_action(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(snd_soc_dai_action); -int snd_soc_dai_active(struct snd_soc_dai *dai) +int snd_soc_dai_active(const struct snd_soc_dai *dai) { int stream, active; diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index d05e712c9518..5dea5609baf4 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -21,7 +21,7 @@ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) } EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); -int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) +int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params) { int sample_size; @@ -40,7 +40,7 @@ int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) } EXPORT_SYMBOL_GPL(snd_soc_calc_bclk); -int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) +int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *params) { int ret; @@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); * Return: bclk frequency in Hz, else a negative error code if params format * is invalid. */ -int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, +int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params, int tdm_width, int tdm_slots, int slot_multiple) { if (!tdm_slots) @@ -211,7 +211,7 @@ static struct snd_soc_dai_driver dummy_dai = { .ops = &dummy_dai_ops, }; -int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) +int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai) { if (dai->driver == &dummy_dai) return 1; -- cgit v1.2.3 From 785d64c4941221044940ab199e6625af17296470 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 15:03:22 +0200 Subject: ASoC: Constify DAI passed to get_channel_map get_channel_map() is supposed to obtain map of channels without modifying the state of the given DAI, so make the pointer to 'struct snd_soc_dai' as pointing to const. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617-n-asoc-const-auto-selectable-formats-v1-4-8004f346ee38@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 4 ++-- sound/soc/codecs/lpass-rx-macro.c | 2 +- sound/soc/codecs/lpass-tx-macro.c | 2 +- sound/soc/codecs/lpass-va-macro.c | 2 +- sound/soc/codecs/lpass-wsa-macro.c | 2 +- sound/soc/codecs/wcd9335.c | 2 +- sound/soc/codecs/wcd934x.c | 2 +- sound/soc/soc-dai.c | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3c45b418270e..f22969298de1 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -198,7 +198,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, int direction); -int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, +int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); @@ -307,7 +307,7 @@ struct snd_soc_dai_ops { int (*set_channel_map)(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot); - int (*get_channel_map)(struct snd_soc_dai *dai, + int (*get_channel_map)(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 00ecc470ba8b..83791507ed13 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1833,7 +1833,7 @@ static int rx_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int rx_macro_get_channel_map(struct snd_soc_dai *dai, +static int rx_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index c98b0b747a92..209c12ec16dd 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1167,7 +1167,7 @@ static int tx_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int tx_macro_get_channel_map(struct snd_soc_dai *dai, +static int tx_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index a555e1e078eb..b0006f4ce8a2 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -892,7 +892,7 @@ static int va_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int va_macro_get_channel_map(struct snd_soc_dai *dai, +static int va_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 6ce309980cd1..1e19c2b28a4f 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -992,7 +992,7 @@ static int wsa_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, +static int wsa_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 42a99978fe5a..fc0ab00a253f 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -2014,7 +2014,7 @@ static int wcd9335_set_channel_map(struct snd_soc_dai *dai, return 0; } -static int wcd9335_get_channel_map(struct snd_soc_dai *dai, +static int wcd9335_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index fcad2c9fba55..b5a929659dc8 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1960,7 +1960,7 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai, return 0; } -static int wcd934x_get_channel_map(struct snd_soc_dai *dai, +static int wcd934x_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 55d1a5a099df..0c3a834e504d 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -11,7 +11,7 @@ #include #define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret) -static inline int _soc_dai_ret(struct snd_soc_dai *dai, +static inline int _soc_dai_ret(const struct snd_soc_dai *dai, const char *func, int ret) { /* Positive, Zero values are not errors */ @@ -327,7 +327,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); * @rx_slot: pointer to an array which imply the RX slot number channel * 0~num-1 uses */ -int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, +int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { -- cgit v1.2.3 From de267e7a6ea8e6fa29af2287adfc9fc9d87e6dc9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 17 Jun 2024 15:03:23 +0200 Subject: ASoC: Constify return of snd_soc_dai_get_pcm_stream() Returned 'struct snd_soc_pcm_stream' by snd_soc_dai_get_pcm_stream() is not modified by the users, so it can be changed as pointer to const. This is a necessary step towards making the 'dai->driver' a pointer to const. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240617-n-asoc-const-auto-selectable-formats-v1-5-8004f346ee38@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 2 +- sound/soc/intel/avs/pcm.c | 4 ++-- sound/soc/soc-dai.c | 2 +- sound/soc/soc-pcm.c | 26 +++++++++++++------------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index f22969298de1..bd249710d716 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -473,7 +473,7 @@ struct snd_soc_dai { unsigned int probed:1; }; -static inline struct snd_soc_pcm_stream * +static inline const struct snd_soc_pcm_stream * snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) { return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 88e711875004..c76b86254a8b 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -341,7 +341,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + const struct snd_soc_pcm_stream *stream_info; struct hdac_ext_stream *link_stream; struct hdac_ext_link *link; struct avs_dma_data *data; @@ -637,7 +637,7 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + const struct snd_soc_pcm_stream *stream_info; struct avs_dma_data *data; struct hdac_ext_stream *host_stream; unsigned int format_val; diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 0c3a834e504d..9e47053419c1 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -473,7 +473,7 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, */ bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int dir) { - struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); + const struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); /* If the codec specifies any channels at all, it supports the stream */ return stream->channels_min; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 711b2f49ed88..bad823718ae4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -504,7 +504,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) unsigned int bits = 0, cpu_bits = 0; for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); + const struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); if (pcm_codec->sig_bits == 0) { bits = 0; @@ -514,7 +514,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); + const struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); if (pcm_cpu->sig_bits == 0) { cpu_bits = 0; @@ -538,7 +538,7 @@ static void soc_pcm_hw_init(struct snd_pcm_hardware *hw) } static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->rates = snd_pcm_rate_mask_intersect(hw->rates, p->rates); @@ -551,20 +551,20 @@ static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, } static void soc_pcm_hw_update_chan(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->channels_min = max(hw->channels_min, p->channels_min); hw->channels_max = min(hw->channels_max, p->channels_max); } static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->formats &= p->formats; } static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->subformats &= p->subformats; } @@ -583,8 +583,8 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; - struct snd_soc_pcm_stream *codec_stream; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *cpu_stream; unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; int i; @@ -1712,7 +1712,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) hw->formats &= formats; for_each_rtd_cpu_dais(fe, i, dai) { - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; /* * Skip CPUs which don't support the current stream @@ -1750,7 +1750,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *codec_stream; int i; for_each_rtd_codec_dais(be, i, dai) { @@ -1787,7 +1787,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; struct snd_soc_dai *dai; int i; @@ -1809,7 +1809,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) * DAIs connected to a single CPU DAI, use CPU DAI's directly */ if (be->dai_link->num_codecs == 1) { - struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( + const struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( snd_soc_rtd_to_codec(be, 0), stream); soc_pcm_hw_update_chan(hw, codec_stream); @@ -1835,7 +1835,7 @@ static void dpcm_runtime_setup_be_rate(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *pcm; + const struct snd_soc_pcm_stream *pcm; struct snd_soc_dai *dai; int i; -- cgit v1.2.3 From 6d620e50bb055e072c8c50cf95cd397fc24378c2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 6 Jun 2024 11:49:19 +0100 Subject: ASoC: qcom: q6dsp: parse Display port tokens Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov # X13s Link: https://msgid.link/r/20240606104922.114229-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index c15d1a2b6dbf..f4d62ea60baa 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -730,6 +730,29 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, return 0; } +static int audioreach_widget_dp_module_load(struct audioreach_module *mod, + struct snd_soc_tplg_vendor_array *mod_array) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + static int audioreach_widget_load_buffer(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) @@ -760,6 +783,9 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, case MODULE_ID_I2S_SOURCE: audioreach_widget_i2s_module_load(mod, mod_array); break; + case MODULE_ID_DISPLAY_PORT_SINK: + audioreach_widget_dp_module_load(mod, mod_array); + break; default: return -EINVAL; } -- cgit v1.2.3 From 735db4ea16caaebf8e4884ec0c2e419c96391ac8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 6 Jun 2024 11:49:20 +0100 Subject: ASoC: qcom: common: add Display port Jack function Add a common function to add Display port jack. Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov # X13s Link: https://msgid.link/r/20240606104922.114229-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 35 +++++++++++++++++++++++++++++++++++ sound/soc/qcom/common.h | 3 +++ 2 files changed, 38 insertions(+) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 3d02aa3844f2..56b4a3654aec 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -8,9 +8,19 @@ #include #include "common.h" +#define NAME_SIZE 32 + static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; int qcom_snd_parse_of(struct snd_soc_card *card) @@ -240,5 +250,30 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup); +int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *dp_jack, int dp_pcm_id) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_card *card = rtd->card; + char jack_name[NAME_SIZE]; + int rval, i; + + snprintf(jack_name, sizeof(jack_name), "DP%d Jack", dp_pcm_id); + rval = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, dp_jack); + if (rval) + return rval; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + rval = snd_soc_component_set_jack(codec_dai->component, dp_jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_snd_dp_jack_setup); + MODULE_DESCRIPTION("ASoC Qualcomm helper functions"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index d7f80ee5ae26..1b8d3f90bffa 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -9,5 +9,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); +int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *dp_jack, int id); + #endif -- cgit v1.2.3 From 7e815bb9abd16f99c987987242a9fe13dfa0f052 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 6 Jun 2024 11:49:21 +0100 Subject: ASoC: qcom: sc8280xp: add Display port Jack Add support for Display Port Jack events. Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov # X13s Link: https://msgid.link/r/20240606104922.114229-4-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 06fd47c4178f..922ecada1cd8 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -19,6 +19,7 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; bool jack_setup; }; @@ -27,6 +28,8 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_card *card = rtd->card; + struct snd_soc_jack *dp_jack = NULL; + int dp_pcm_id = 0; switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: @@ -41,10 +44,22 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); snd_soc_limit_volume(card, "SpkrRight PA Volume", 17); break; + case DISPLAY_PORT_RX_0: + /* DISPLAY_PORT dai ids are not contiguous */ + dp_pcm_id = 0; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; default: break; } + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } -- cgit v1.2.3 From 24790a3cd1bdc083f9989f2fdb223f6494ebc99c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 Jun 2024 11:49:22 +0100 Subject: ASoC: qcom: x1e80100: Add USB DisplayPort plug support Add support for handling jack events of USB (DisplayPort). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Tested-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov # X13s Link: https://msgid.link/r/20240606104922.114229-5-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/x1e80100.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index d7e0bd03dffd..898b5c26bf1e 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -20,12 +20,32 @@ struct x1e80100_snd_data { struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; bool jack_setup; }; static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) { struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_jack *dp_jack = NULL; + int dp_pcm_id = 0; + + switch (cpu_dai->id) { + case DISPLAY_PORT_RX_0: + dp_pcm_id = 0; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + default: + break; + } + + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } -- cgit v1.2.3 From 4e617f0cb2dd9675a3b2707c10d89f0f5303ad1e Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Fri, 7 Jun 2024 17:22:04 +0200 Subject: ASoC: dt-bindings: tlv320adc3xxx: Add MICBIAS-as-GPO properties Add properties for configuring the MICBIAS pins as general purpose outputs, with some limitations: The voltage on the pin when activated may be set using another property to 2.0 V, 2.5 V or AVDD. When deactivated the pin will float. Signed-off-by: Ricard Wanderlof Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240607-tlv320adc3xxx-micbias-gpo-v3-1-59dbec8b98f4@axis.com Signed-off-by: Mark Brown --- .../bindings/sound/ti,tlv320adc3xxx.yaml | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml index 8ac741f4cd56..66b76656229f 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -82,6 +82,26 @@ properties: Note that there is currently no support for reading the GPIO pins as inputs. + ti,micbias1-gpo: + type: boolean + description: | + When set, the MICBIAS1 pin may be controlled via the GPIO framework, + as pin number 3 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias1-vg property. When deactivated, the pin will + float. + + ti,micbias2-gpo: + type: boolean + description: | + When set, the MICBIAS2 pin may be controlled via the GPIO framework, + as pin number 4 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias2-vg property. When deactivated, the pin will + float. + ti,micbias1-vg: $ref: /schemas/types.yaml#/definitions/uint32 enum: @@ -104,6 +124,10 @@ properties: description: | Mic bias voltage output on MICBIAS2 pin +dependencies: + ti,micbias1-gpo: ['ti,micbias1-vg'] + ti,micbias2-gpo: ['ti,micbias2-vg'] + required: - compatible - reg -- cgit v1.2.3 From 6c01001414ce477311d872c40e23dfec4866d608 Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Fri, 7 Jun 2024 17:22:05 +0200 Subject: ASoC: tlv320adc3xxx: Add support for using MICBIAS pins as GPO Add boolean ti,micbias1-gpo and ti,micbias2-gpo devicetree properties. When set, the respective MICBIAS pins can be used as general purpose outputs controlled via the GPIO framework, in addition to the two configurable GPIO pins. This is useful in applications where the MICBIAS functionality is not required, but it is useful to have a couple of extra GPIO pins. The voltage on the respective MICBIAS pin in the active state is governed by the ti,micbias1-vg and ti,micbias2-vg properties, respectively (same properties as when the pins are used as MICBIAS pins). Signed-off-by: Ricard Wanderlof Link: https://msgid.link/r/20240607-tlv320adc3xxx-micbias-gpo-v3-2-59dbec8b98f4@axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 105 +++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index eb180df9a72a..7073b9d1cda8 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -39,9 +39,10 @@ */ #define ADC3XXX_MICBIAS_PINS 2 +#define ADC3XXX_GPIO_PINS 2 /* Number of GPIO pins exposed via the gpiolib interface */ -#define ADC3XXX_GPIOS_MAX 2 +#define ADC3XXX_GPIOS_MAX (ADC3XXX_MICBIAS_PINS + ADC3XXX_GPIO_PINS) #define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 #define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -320,7 +321,8 @@ struct adc3xxx { struct gpio_desc *rst_pin; unsigned int pll_mode; unsigned int sysclk; - unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int gpio_cfg[ADC3XXX_GPIO_PINS]; /* value+1 (0 => not set) */ + unsigned int micbias_gpo[ADC3XXX_MICBIAS_PINS]; /* 1 => pin is GPO */ unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; int master; u8 page_no; @@ -328,7 +330,7 @@ struct adc3xxx { struct gpio_chip gpio_chip; }; -static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIO_PINS] = { ADC3XXX_GPIO1_CTRL, ADC3XXX_GPIO2_CTRL }; @@ -959,14 +961,23 @@ static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) if (offset >= ADC3XXX_GPIOS_MAX) return -EINVAL; - /* GPIO1 is offset 0, GPIO2 is offset 1 */ - /* We check here that the GPIO pins are either not configured in the - * DT, or that they purposely are set as outputs. - * (Input mode not yet implemented). - */ - if (adc3xxx->gpio_cfg[offset] != 0 && - adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) - return -EINVAL; + if (offset >= 0 && offset < ADC3XXX_GPIO_PINS) { + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured + * in the DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + } else if (offset >= ADC3XXX_GPIO_PINS && offset < ADC3XXX_GPIOS_MAX) { + /* MICBIAS1 is offset 2, MICBIAS2 is offset 3 */ + /* We check here if the MICBIAS pins are in fact configured + * as GPOs. + */ + if (!adc3xxx->micbias_gpo[offset - ADC3XXX_GPIO_PINS]) + return -EINVAL; + } return 0; } @@ -976,6 +987,21 @@ static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, { struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + /* For the MICBIAS pins, they are by definition outputs. */ + if (offset >= ADC3XXX_GPIO_PINS) { + unsigned int vg; + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + if (value) + vg = adc3xxx->micbias_vg[micbias]; + else + vg = ADC3XXX_MICBIAS_OFF; + return regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } + /* Set GPIO output function. */ return regmap_update_bits(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], @@ -1004,9 +1030,17 @@ static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) unsigned int regval; int ret; - /* We only allow output pins, so just read the value set in the output - * pin register field. - */ + /* We only allow output pins, so just read the value prevously set. */ + if (offset >= ADC3XXX_GPIO_PINS) { + /* MICBIAS pins */ + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + ret = regmap_read(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, ®val); + if (ret) + return ret; + return ((regval >> adc3xxx_micbias_shift[micbias]) & ADC3XXX_MICBIAS_MASK) != + ADC3XXX_MICBIAS_OFF; + } ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); if (ret) return ret; @@ -1048,7 +1082,7 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) * This allows us to set up things which are not software * controllable GPIOs, such as PDM microphone I/O, */ - for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + for (gpio = 0; gpio < ADC3XXX_GPIO_PINS; gpio++) { unsigned int cfg = adc3xxx->gpio_cfg[gpio]; if (cfg) { @@ -1060,9 +1094,15 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) } } - /* Set up micbias voltage */ + /* Set up micbias voltage. */ + /* If pin is configured as GPO, set off initially. */ for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { - unsigned int vg = adc3xxx->micbias_vg[micbias]; + unsigned int vg; + + if (adc3xxx->micbias_gpo[micbias]) + vg = ADC3XXX_MICBIAS_OFF; + else + vg = adc3xxx->micbias_vg[micbias]; regmap_update_bits(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, @@ -1090,8 +1130,19 @@ static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, return 0; } -static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, - const char *propname, unsigned int *vg) +static int adc3xxx_parse_dt_micbias_gpo(struct adc3xxx *adc3xxx, + const char *propname, + unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + + *cfg = of_property_read_bool(np, propname); + return 0; +} + +static int adc3xxx_parse_dt_micbias_vg(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) { struct device *dev = adc3xxx->dev; struct device_node *np = dev->of_node; @@ -1382,16 +1433,28 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c) dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); } + /* Configure mode for DMDIN/GPIO1 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); if (ret < 0) goto err_unprepare_mclk; + /* Configure mode for DMCLK/GPIO2 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + /* Configure mode for MICBIAS1: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias1-gpo", &adc3xxx->micbias_gpo[0]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure mode for MICBIAS2: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias2-gpo", &adc3xxx->micbias_gpo[1]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure voltage for MICBIAS1 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + /* Configure voltage for MICBIAS2 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); if (ret < 0) goto err_unprepare_mclk; -- cgit v1.2.3 From 9292013bd3738bd00ee78bf1705620a9881031b5 Mon Sep 17 00:00:00 2001 From: Andrejs Cainikovs Date: Thu, 13 Jun 2024 10:46:50 +0200 Subject: ASoC: nau8822: move nau8822_set_dai_sysclk() Next commit in series makes a change which calls nau8822_set_pll() from nau8822_set_dai_sysclk(). Moving latter after the former would avoid a forward declaration, and this is exactly what this change does. Signed-off-by: Andrejs Cainikovs Link: https://msgid.link/r/20240613084652.13113-2-andrejs.cainikovs@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8822.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index e6909e64dfa3..a6741bda6a9e 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -612,20 +612,6 @@ static const struct snd_soc_dapm_route nau8822_dapm_routes[] = { {"Right DAC", NULL, "Digital Loopback"}, }; -static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct snd_soc_component *component = dai->component; - struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); - - nau8822->div_id = clk_id; - nau8822->sysclk = freq; - dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, - clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); - - return 0; -} - static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs, struct nau8822_pll *pll_param) { @@ -782,6 +768,20 @@ static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source, return 0; } +static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + nau8822->div_id = clk_id; + nau8822->sysclk = freq; + dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, + clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; -- cgit v1.2.3 From 9c3b21cc8528d52321c66236c3b794300c78d9e6 Mon Sep 17 00:00:00 2001 From: Andrejs Cainikovs Date: Thu, 13 Jun 2024 10:46:51 +0200 Subject: ASoC: nau8822: set NAU8822_REFIMP_80K only once Following bias state machine logic this bit is set twice before playback. This change makes sure this bit set is set only once. Signed-off-by: Andrejs Cainikovs Suggested-by: Emanuele Ghidoli Link: https://msgid.link/r/20240613084652.13113-3-andrejs.cainikovs@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8822.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index a6741bda6a9e..5f9f0953aa2f 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -942,6 +942,8 @@ static int nau8822_set_bias_level(struct snd_soc_component *component, { switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: snd_soc_component_update_bits(component, NAU8822_REG_POWER_MANAGEMENT_1, -- cgit v1.2.3 From 93f12a75682698ad4d2614b88cc68f52bb8fb917 Mon Sep 17 00:00:00 2001 From: Andrejs Cainikovs Date: Thu, 13 Jun 2024 10:46:52 +0200 Subject: ASoC: nau8822: add MCLK support This change adds MCLK clock handling directly within driver. When used in combination with simple-audio-card, and mclk-fs is set, simple-audio-card will change MCLK frequency before configuring PLL. In some cases, however, MCLK reference clock should be static (see [1]), which means it needs to be moved away from simple-audio-card. [1]: https://lore.kernel.org/all/ZfBdxrzX3EnPuGOn@ediswmail9.ad.cirrus.com/ Signed-off-by: Andrejs Cainikovs Link: https://msgid.link/r/20240613084652.13113-4-andrejs.cainikovs@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8822.c | 48 ++++++++++++++++++++++++++++++++++++++++++---- sound/soc/codecs/nau8822.h | 1 + 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index 5f9f0953aa2f..e1cbaf8a944d 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -773,11 +774,26 @@ static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_component *component = dai->component; struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + unsigned long mclk_freq; nau8822->div_id = clk_id; nau8822->sysclk = freq; + + if (nau8822->mclk) { + mclk_freq = clk_get_rate(nau8822->mclk); + if (mclk_freq != freq) { + int ret = nau8822_set_pll(dai, NAU8822_CLK_MCLK, + NAU8822_CLK_MCLK, mclk_freq, freq); + if (ret) { + dev_err(component->dev, "Failed to set PLL\n"); + return ret; + } + nau8822->div_id = NAU8822_CLK_PLL; + } + } + dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, - clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); + nau8822->div_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); return 0; } @@ -848,7 +864,7 @@ static int nau8822_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); - int val_len = 0, val_rate = 0; + int div = 0, val_len = 0, val_rate = 0; unsigned int ctrl_val, bclk_fs, bclk_div; /* make BCLK and LRC divide configuration if the codec as master. */ @@ -915,8 +931,10 @@ static int nau8822_hw_params(struct snd_pcm_substream *substream, /* If the master clock is from MCLK, provide the runtime FS for driver * to get the master clock prescaler configuration. */ - if (nau8822->div_id == NAU8822_CLK_MCLK) - nau8822_config_clkdiv(dai, 0, params_rate(params)); + if (nau8822->div_id != NAU8822_CLK_MCLK) + div = nau8822->pll.mclk_scaler; + + nau8822_config_clkdiv(dai, div, params_rate(params)); return 0; } @@ -940,17 +958,34 @@ static int nau8822_mute(struct snd_soc_dai *dai, int mute, int direction) static int nau8822_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: + if (nau8822->mclk && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) { + int ret = clk_prepare_enable(nau8822->mclk); + + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", ret); + return ret; + } + } + snd_soc_component_update_bits(component, NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K); break; case SND_SOC_BIAS_STANDBY: + if (nau8822->mclk && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_OFF) + clk_disable_unprepare(nau8822->mclk); + snd_soc_component_update_bits(component, NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_IOBUF_EN | NAU8822_ABIAS_EN, @@ -1127,6 +1162,11 @@ static int nau8822_i2c_probe(struct i2c_client *i2c) } i2c_set_clientdata(i2c, nau8822); + nau8822->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(nau8822->mclk)) + return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk), + "Error getting mclk\n"); + nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config); if (IS_ERR(nau8822->regmap)) { ret = PTR_ERR(nau8822->regmap); diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index 6ecd46e45923..13fe0a091e9e 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -215,6 +215,7 @@ struct nau8822_pll { struct nau8822 { struct device *dev; struct regmap *regmap; + struct clk *mclk; struct nau8822_pll pll; int sysclk; int div_id; -- cgit v1.2.3 From 7109f10ca4225c4a7e47dee5eab0d8d72edbf324 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 18 Jun 2024 00:10:15 +0000 Subject: ASoC: dt-bindings: audio-graph-card2: add support for aux devices Add device tree property to define auxiliary devices to be added to Audio Graph Card which is already supported on Simle Card. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87jzin3yc8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/audio-graph-card2.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml index d3ce4de449d5..f943f90d8b15 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml @@ -23,6 +23,11 @@ properties: Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. $ref: /schemas/types.yaml#/definitions/non-unique-string-array + aux-devs: + description: | + List of phandles pointing to auxiliary devices, such + as amplifiers, to be added to the sound card. + $ref: /schemas/types.yaml#/definitions/phandle-array multi: type: object description: Multi-CPU/Codec node -- cgit v1.2.3 From 9f774c757e3fb2ac32dc4377e8f21f3364a8df81 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 14 Jun 2024 21:36:45 +0800 Subject: ASoc: tas2781: Enable RCA-based playback without DSP firmware download In only loading RCA (Reconfigurable Architecture) binary case, no DSP program will be working inside tas2563/tas2781, that is dsp-bypass mode, do not support speaker protection, or audio acoustic algorithms in this mode. Fixes: ef3bcde75d06 ("ASoC: tas2781: Add tas2781 driver") Signed-off-by: Shenghao Ding Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240614133646.910-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781-dsp.h | 11 +++++++++-- sound/soc/codecs/tas2781-fmwlib.c | 18 +++++++++++++----- sound/soc/codecs/tas2781-i2c.c | 39 ++++++++++++++++++++++++++------------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index 7fba7ea26a4b..3cda9da14f6d 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -117,10 +117,17 @@ struct tasdevice_fw { struct device *dev; }; -enum tasdevice_dsp_fw_state { - TASDEVICE_DSP_FW_NONE = 0, +enum tasdevice_fw_state { + /* Driver in startup mode, not load any firmware. */ TASDEVICE_DSP_FW_PENDING, + /* DSP firmware in the system, but parsing error. */ TASDEVICE_DSP_FW_FAIL, + /* + * Only RCA (Reconfigurable Architecture) firmware load + * successfully. + */ + TASDEVICE_RCA_FW_OK, + /* Both RCA and DSP firmware load successfully. */ TASDEVICE_DSP_FW_ALL_OK, }; diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 265a8ca25cbb..838d29fead96 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2324,14 +2324,21 @@ void tasdevice_tuning_switch(void *context, int state) struct tasdevice_fw *tas_fmw = tas_priv->fmw; int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + /* + * Only RCA-based Playback can still work with no dsp program running + * inside the chip. + */ + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + break; + default: return; } if (state == 0) { - if (tas_priv->cur_prog < tas_fmw->nr_programs) { - /*dsp mode or tuning mode*/ + if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { + /* dsp mode or tuning mode */ profile_cfg_id = tas_priv->rcabin.profile_cfg_id; tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog, tas_priv->cur_conf, @@ -2340,9 +2347,10 @@ void tasdevice_tuning_switch(void *context, int state) tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_POWER_UP); - } else + } else { tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_SHUTDOWN); + } } EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9350972dfefe..c64d458e524e 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -380,23 +380,37 @@ static void tasdevice_fw_ready(const struct firmware *fmw, mutex_lock(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) + if (ret) { + tasdevice_config_info_remove(tas_priv); goto out; + } tasdevice_create_control(tas_priv); tasdevice_dsp_remove(tas_priv); tasdevice_calbin_remove(tas_priv); - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + /* + * The baseline is the RCA-only case, and then the code attempts to + * load DSP firmware but in case of failures just keep going, i.e. + * failing to load DSP firmware is NOT an error. + */ + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", tas_priv->dev_name); ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; goto out; } - tasdevice_dsp_create_ctrls(tas_priv); + + /* + * If no dsp-related kcontrol created, the dsp resource will be freed. + */ + ret = tasdevice_dsp_create_ctrls(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dsp controls error\n"); + goto out; + } tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; @@ -417,9 +431,8 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; out: - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - /*If DSP FW fail, kcontrol won't be created */ - tasdevice_config_info_remove(tas_priv); + if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { + /* If DSP FW fail, DSP kcontrol won't be created. */ tasdevice_dsp_remove(tas_priv); } mutex_unlock(&tas_priv->codec_lock); @@ -466,14 +479,14 @@ static int tasdevice_startup(struct snd_pcm_substream *substream, { struct snd_soc_component *codec = dai->component; struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int ret = 0; - if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); - ret = -EINVAL; + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + return 0; + default: + return -EINVAL; } - - return ret; } static int tasdevice_hw_params(struct snd_pcm_substream *substream, -- cgit v1.2.3 From e565ab184946650f19afb9fe74f8be84fcb6d210 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:14 +0200 Subject: ASoC: codecs: wcd-mbhc: Constify passed MBHC reg fields The wcd-mbhc-v2 helper code/module does not modify passed array of registry fields, thus it can be made const for code safety. It will also allow individual drivers to allocate the array in rodata. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-1-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 4 ++-- sound/soc/codecs/wcd-mbhc-v2.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 0e6218ed0e5e..d589a212b768 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -50,7 +50,7 @@ struct wcd_mbhc { struct wcd_mbhc_config *cfg; const struct wcd_mbhc_cb *mbhc_cb; const struct wcd_mbhc_intr *intr_ids; - struct wcd_mbhc_field *fields; + const struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; /* Work to handle plug report */ @@ -1505,7 +1505,7 @@ EXPORT_SYMBOL(wcd_dt_parse_mbhc_data); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { struct device *dev = component->dev; diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index df68e99c81a3..b977e8f87d7c 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -279,7 +279,7 @@ int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en); int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr); @@ -300,7 +300,7 @@ static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { return ERR_PTR(-ENOTSUPP); -- cgit v1.2.3 From 60ce48f5311103dd7a09e12d0b75d7ec44e291d9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:15 +0200 Subject: ASoC: codecs: wcd9335: Drop unused state container fields The driver does not use few 'struct wcd9335_codec' members, so just drop them for smaller code. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-2-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 42a99978fe5a..60e7704e48d2 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -297,7 +297,6 @@ struct wcd9335_codec { struct clk *mclk; struct clk *native_clk; u32 mclk_rate; - u8 version; struct slim_device *slim; struct slim_device *slim_ifc_dev; @@ -347,8 +346,6 @@ struct wcd9335_codec { int dmic_4_5_clk_cnt; int dmic_sample_rate; int mad_dmic_sample_rate; - - int native_clk_users; }; struct wcd9335_irq { @@ -5109,7 +5106,6 @@ static int wcd9335_bring_up(struct wcd9335_codec *wcd) if (byte0 == 0x1) { dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n"); - wcd->version = WCD9335_VERSION_2_0; regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01); regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00); regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F); -- cgit v1.2.3 From b2ff7c88bf751fa502f5707f0b7b047afcba87dc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:16 +0200 Subject: ASoC: codecs: wcd9335: Constify static data Driver does not modify few static data (arrays with sample rates, interrupt description, regmap config), so make them const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-3-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 60e7704e48d2..c5ac2d46e556 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -394,13 +394,13 @@ struct interp_sample_rate { int rate_val; }; -static struct interp_sample_rate int_mix_rate_val[] = { +static const struct interp_sample_rate int_mix_rate_val[] = { {48000, 0x4}, /* 48K */ {96000, 0x5}, /* 96K */ {192000, 0x6}, /* 192K */ }; -static struct interp_sample_rate int_prim_rate_val[] = { +static const struct interp_sample_rate int_prim_rate_val[] = { {8000, 0x0}, /* 8K */ {16000, 0x1}, /* 16K */ {24000, -EINVAL},/* 24K */ @@ -4023,7 +4023,7 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) return ret; } -static struct wcd9335_irq wcd9335_irqs[] = { +static const struct wcd9335_irq wcd9335_irqs[] = { { .irq = WCD9335_IRQ_SLIMBUS, .handler = wcd9335_slimbus_irq, @@ -4960,7 +4960,7 @@ static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) } } -static struct regmap_config wcd9335_regmap_config = { +static const struct regmap_config wcd9335_regmap_config = { .reg_bits = 16, .val_bits = 8, .cache_type = REGCACHE_MAPLE, @@ -4984,7 +4984,7 @@ static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { }, }; -static struct regmap_config wcd9335_ifc_regmap_config = { +static const struct regmap_config wcd9335_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .can_multi_write = true, -- cgit v1.2.3 From 4a03b5dbad466c902d522f3405daa4e5d80578c5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:17 +0200 Subject: ASoC: codecs: wcd9335: Handle nicer probe deferral and simplify with dev_err_probe() wcd9335_parse_dt() function is called only from probe(), so printing errors on resource acquisition is discouraged, because it can pollute dmesg. Use dev_err_probe() to fix this and also make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-4-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index c5ac2d46e556..334c5241617f 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5031,22 +5031,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) int ret; wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (wcd->reset_gpio < 0) { - dev_err(dev, "Reset GPIO missing from DT\n"); - return wcd->reset_gpio; - } + if (wcd->reset_gpio < 0) + return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); wcd->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(wcd->mclk)) { - dev_err(dev, "mclk not found\n"); - return PTR_ERR(wcd->mclk); - } + if (IS_ERR(wcd->mclk)) + return dev_err_probe(dev, PTR_ERR(wcd->mclk), "mclk not found\n"); wcd->native_clk = devm_clk_get(dev, "slimbus"); - if (IS_ERR(wcd->native_clk)) { - dev_err(dev, "slimbus clock not found\n"); - return PTR_ERR(wcd->native_clk); - } + if (IS_ERR(wcd->native_clk)) + return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n"); wcd->supplies[0].supply = "vdd-buck"; wcd->supplies[1].supply = "vdd-buck-sido"; @@ -5055,10 +5049,8 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) wcd->supplies[4].supply = "vdd-io"; ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); - if (ret) { - dev_err(dev, "Failed to get supplies: err = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); return 0; } @@ -5157,10 +5149,8 @@ static int wcd9335_slim_probe(struct slim_device *slim) wcd->dev = dev; ret = wcd9335_parse_dt(wcd); - if (ret) { - dev_err(dev, "Error parsing DT: %d\n", ret); + if (ret) return ret; - } ret = wcd9335_power_on_reset(wcd); if (ret) -- cgit v1.2.3 From 3ed4beba49463997eb9a4afa0d46ece7dc5dbf19 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:18 +0200 Subject: ASoC: codecs: wcd9335: Drop unneeded error message Error-level should not be used as debugging. The code (function wcd9335_get_dmic_clk_val()) will always be called with same parameters, so this is not really useful debug anyway, so drop it. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-5-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 334c5241617f..6a1376b3b98c 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -2849,10 +2849,6 @@ static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, u32 div_factor; u8 dmic_ctl_val; - dev_err(component->dev, - "%s: mclk_rate = %d, dmic_sample_rate = %d\n", - __func__, mclk_rate, dmic_clk_rate); - /* Default value to return in case of error */ if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; -- cgit v1.2.3 From 1d1cda22da5db03a0c794d139e06519e3a94f815 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:19 +0200 Subject: ASoC: codecs: wcd9335: Drop unused dmic rate handling Driver was prepared to adjust DMIC microphone parameters according to DMIC clock rate which is read from platform data or Devicetree. The latter part never happened, so the code is always called with dmic_clk_rate=0, prints error and uses default/fallback values. All this part can be simplified by dropping dead parts of code. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-6-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 52 +++------------------------------------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 6a1376b3b98c..f5fe2583b807 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -344,8 +344,6 @@ struct wcd9335_codec { int dmic_0_1_clk_cnt; int dmic_2_3_clk_cnt; int dmic_4_5_clk_cnt; - int dmic_sample_rate; - int mad_dmic_sample_rate; }; struct wcd9335_irq { @@ -2844,52 +2842,15 @@ out: } static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, - u32 mclk_rate, u32 dmic_clk_rate) + u32 mclk_rate) { - u32 div_factor; u8 dmic_ctl_val; - /* Default value to return in case of error */ if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; else dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - if (dmic_clk_rate == 0) { - dev_err(component->dev, - "%s: dmic_sample_rate cannot be 0\n", - __func__); - goto done; - } - - div_factor = mclk_rate / dmic_clk_rate; - switch (div_factor) { - case 2: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; - break; - case 3: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - break; - case 4: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; - break; - case 6: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; - break; - case 8: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; - break; - case 16: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; - break; - default: - dev_err(component->dev, - "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", - __func__, div_factor, mclk_rate, dmic_clk_rate); - break; - } - -done: return dmic_ctl_val; } @@ -2943,11 +2904,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->dmic_sample_rate); - + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)++; if (*dmic_clk_cnt == 1) { snd_soc_component_update_bits(comp, dmic_clk_reg, @@ -2959,10 +2916,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->mad_dmic_sample_rate); + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)--; if (*dmic_clk_cnt == 0) { snd_soc_component_update_bits(comp, dmic_clk_reg, -- cgit v1.2.3 From 1bce5c586ba56dfa6acf8d13f0f608f5f5d717bd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:20 +0200 Subject: ASoC: codecs: wcd934x: Drop unused interp path enum The enum with inter path is not used. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-7-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index fcad2c9fba55..cdb68f34e55a 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -475,11 +475,6 @@ enum { INTn_2_INP_SEL_PROXIMITY, }; -enum { - INTERP_MAIN_PATH, - INTERP_MIX_PATH, -}; - struct interp_sample_rate { int sample_rate; int rate_val; -- cgit v1.2.3 From 5ad81bf49a18dc22bd7e1c56f67be9b5eb50267c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:21 +0200 Subject: ASoC: codecs: wcd934x: Constify static data Driver does not modify few static data (arrays with sample rates, MBHC reg fields, regmap config), so make them const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-8-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index cdb68f34e55a..7885aa423886 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -480,7 +480,7 @@ struct interp_sample_rate { int rate_val; }; -static struct interp_sample_rate sr_val_tbl[] = { +static const struct interp_sample_rate sr_val_tbl[] = { {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, @@ -522,7 +522,7 @@ static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { }, }; -static struct regmap_config wcd934x_ifc_regmap_config = { +static const struct regmap_config wcd934x_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = 0xffff, @@ -1212,7 +1212,7 @@ static const struct soc_enum cdc_if_tx13_mux_enum = SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20), -- cgit v1.2.3 From a252188244aeff72e4331abba2fee6b9093c847b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:22 +0200 Subject: ASoC: codecs: wcd934x: Drop unused mic bias voltage fields Driver stores the voltage of mic bias in fields in state container structure, but actually never reads them - except for the mic2 bias (micb2_mv field). Drop the fields from the structure so the code will be a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-9-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 7885aa423886..2a5fb4370ba3 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -566,10 +566,7 @@ struct wcd934x_codec { struct mutex micb_lock; u32 micb_ref[WCD934X_MAX_MICBIAS]; u32 pullup_ref[WCD934X_MAX_MICBIAS]; - u32 micb1_mv; u32 micb2_mv; - u32 micb3_mv; - u32 micb4_mv; }; #define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) @@ -2203,7 +2200,8 @@ static int wcd934x_get_micbias_val(struct device *dev, const char *micbias, mv = WCD934X_DEF_MICBIAS_MV; } - *micb_mv = mv; + if (micb_mv) + *micb_mv = mv; return (mv - 1000) / 50; } @@ -2215,17 +2213,14 @@ static int wcd934x_init_dmic(struct snd_soc_component *comp) u32 def_dmic_rate, dmic_clk_drv; vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias1-microvolt", - &wcd->micb1_mv); + "qcom,micbias1-microvolt", NULL); vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, "qcom,micbias2-microvolt", &wcd->micb2_mv); vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias3-microvolt", - &wcd->micb3_mv); + "qcom,micbias3-microvolt", NULL); vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias4-microvolt", - &wcd->micb4_mv); + "qcom,micbias4-microvolt", NULL); snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, WCD934X_MICB_VAL_MASK, vout_ctl_1); -- cgit v1.2.3 From 9ea22713227397b56a2b36a68c8bda13f0b43d70 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:23 +0200 Subject: ASoC: codecs: wcd934x: Handle nicer probe deferral and simplify with dev_err_probe() wcd934x_codec_parse_data() function is called only from probe(), so printing errors on resource acquisition is discouraged, because it can pollute dmesg in case of probe deferral. The actual deferral is here unlikely, but still the code is a bit simpler with dev_err_probe(). Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-10-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 2a5fb4370ba3..d1bbc963856b 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -5856,17 +5856,13 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) struct device_node *ifc_dev_np; ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); - if (!ifc_dev_np) { - dev_err(dev, "No Interface device found\n"); - return -EINVAL; - } + if (!ifc_dev_np) + return dev_err_probe(dev, -EINVAL, "No Interface device found\n"); wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); of_node_put(ifc_dev_np); - if (!wcd->sidev) { - dev_err(dev, "Unable to get SLIM Interface device\n"); - return -EINVAL; - } + if (!wcd->sidev) + return dev_err_probe(dev, -EINVAL, "Unable to get SLIM Interface device\n"); slim_get_logical_addr(wcd->sidev); wcd->if_regmap = regmap_init_slimbus(wcd->sidev, @@ -5912,10 +5908,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) mutex_init(&wcd->micb_lock); ret = wcd934x_codec_parse_data(wcd); - if (ret) { - dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + if (ret) return ret; - } /* set default rate 9P6MHz */ regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, -- cgit v1.2.3 From 0ad42c04e9b223d913ad4b1463c0f1cdc00f3cc5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:24 +0200 Subject: ASoC: codecs: wcd937x: Constify static data Driver does not modify few static data (MBHC reg fields, IRQ chip), so make them const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-11-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index ef649ed77fb2..4cf17d62a0bc 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -133,7 +133,7 @@ struct wcd937x_mbhc_zdet_param { u16 btn7; }; -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD937X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD937X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD937X_ANA_MBHC_MECH, 0x20), @@ -227,7 +227,7 @@ static const u32 wcd937x_config_regs[] = { WCD937X_DIGITAL_INTR_LEVEL_0, }; -static struct regmap_irq_chip wcd937x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd937x_regmap_irq_chip = { .name = "wcd937x", .irqs = wcd937x_irqs, .num_irqs = ARRAY_SIZE(wcd937x_irqs), @@ -2503,7 +2503,7 @@ static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD937x", }; -- cgit v1.2.3 From c8ed66cbc3b7ccd9e5ae98b06023c1fe3e834ca8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:25 +0200 Subject: ASoC: codecs: wcd937x: Constify wcd937x_sdw_ch_info Driver does not modify static wcd937x_sdw_ch_info array, so it can be made const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-12-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x-sdw.c | 4 ++-- sound/soc/codecs/wcd937x.c | 2 +- sound/soc/codecs/wcd937x.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index a45025bf96c6..a24d6c620dba 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -19,7 +19,7 @@ #include #include "wcd937x.h" -static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { +static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), @@ -30,7 +30,7 @@ static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), }; -static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { +static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 4cf17d62a0bc..8b000ba9a469 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -1244,7 +1244,7 @@ static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable) { struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1]; - struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; + const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; u8 port_num = ch_info->port_num; u8 ch_mask = ch_info->ch_mask; diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index e6e1ad5926d5..6f857ce7d8b7 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -522,7 +522,7 @@ struct wcd937x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; - struct wcd937x_sdw_ch_info *ch_info; + const struct wcd937x_sdw_ch_info *ch_info; bool port_enable[WCD937X_MAX_SWR_CH_IDS]; int active_ports; int num_ports; -- cgit v1.2.3 From d8e746719d705427e69c7f0ac2ca20aaf560a03e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:26 +0200 Subject: ASoC: codecs: wcd937x: Drop unused enums, defines and types Few declarations (defines, struct codec_port_info, enums) are not used and can be safely dropped. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-13-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.h | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 6f857ce7d8b7..834a3e7713a3 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -488,23 +488,6 @@ #define WCD937X_MAX_SWR_PORTS 5 #define WCD937X_MAX_SWR_CH_IDS 15 -/* Convert from vout ctl to micbias voltage in mV */ -#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) -#define MAX_PORT 8 -#define MAX_CH_PER_PORT 8 -#define MAX_TX_PWR_CH 2 -#define SWR_NUM_PORTS 4 - -#define WCD937X_MAX_SLAVE_PORT_TYPES 10 - -struct codec_port_info { - u32 slave_port_type; - u32 master_port_type; - u32 ch_mask; - u32 num_ch; - u32 ch_rate; -}; - struct wcd937x_sdw_ch_info { int port_num; unsigned int ch_mask; @@ -570,12 +553,6 @@ int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, } #endif -enum { - WCD_RX1, - WCD_RX2, - WCD_RX3 -}; - enum { /* INTR_CTRL_INT_MASK_0 */ WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0, @@ -645,9 +622,4 @@ enum wcd937x_rx_sdw_channels { WCD937X_DSD_L, }; -enum { - WCD937X_SDW_DIR_RX, - WCD937X_SDW_DIR_TX, -}; - #endif -- cgit v1.2.3 From c2d9fd2e94588eb83f8855e942d77b5f539c5a5f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:27 +0200 Subject: ASoC: codecs: wcd937x: Drop unused state container fields The driver does not use few 'struct wcd937x_priv' and 'struct wcd937x_sdw_priv' members, so just drop them for smaller code. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-14-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 4 ---- sound/soc/codecs/wcd937x.h | 1 - 2 files changed, 5 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 8b000ba9a469..96e2989f9722 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -103,7 +103,6 @@ struct wcd937x_priv { u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; - u32 micb4_mv; /* 9375 only */ int hphr_pdm_wd_int; int hphl_pdm_wd_int; int aux_pdm_wd_int; @@ -113,9 +112,6 @@ struct wcd937x_priv { struct gpio_desc *us_euro_gpio; struct gpio_desc *reset_gpio; - int dmic_0_1_clk_cnt; - int dmic_2_3_clk_cnt; - int dmic_4_5_clk_cnt; atomic_t rx_clk_cnt; atomic_t ana_clk_count; }; diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 834a3e7713a3..f267c66ca959 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -508,7 +508,6 @@ struct wcd937x_sdw_priv { const struct wcd937x_sdw_ch_info *ch_info; bool port_enable[WCD937X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd937x_priv *wcd937x; struct irq_domain *slave_irq; -- cgit v1.2.3 From 6dc7b8a10419a267cd5eb7ac2bb407f7fc7865e2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:28 +0200 Subject: ASoC: codecs: wcd937x: Drop unused chipid member The driver stores the read chipid in 'struct wcd937x_priv' but never needs it after. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-15-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 96e2989f9722..4beb70bc4d8c 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -99,7 +99,6 @@ struct wcd937x_priv { s32 pullup_ref[WCD937X_MAX_MICBIAS]; u32 hph_mode; int ear_rx_path; - u32 chipid; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; @@ -2539,6 +2538,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) struct device *dev = component->dev; unsigned long time_left; int i, ret; + u32 chipid; time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, msecs_to_jiffies(5000)); @@ -2552,11 +2552,10 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; - wcd937x->chipid = (snd_soc_component_read(component, - WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; - if (wcd937x->chipid != CHIPID_WCD9370 && - wcd937x->chipid != CHIPID_WCD9375) { - dev_err(dev, "Got unknown chip id: 0x%x\n", wcd937x->chipid); + chipid = (snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; + if (chipid != CHIPID_WCD9370 && chipid != CHIPID_WCD9375) { + dev_err(dev, "Got unknown chip id: 0x%x\n", chipid); pm_runtime_put(dev); return -EINVAL; } @@ -2605,7 +2604,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd937x->hphl_pdm_wd_int); disable_irq_nosync(wcd937x->aux_pdm_wd_int); - if (wcd937x->chipid == CHIPID_WCD9375) { + if (chipid == CHIPID_WCD9375) { ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets, ARRAY_SIZE(wcd9375_dapm_widgets)); if (ret < 0) { -- cgit v1.2.3 From 424e6bc4328b39a924cc2acb251868cfd23fe369 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:29 +0200 Subject: ASoC: codecs: wcd938x: Constify static data Driver does not modify few static data (MBHC reg fields, IRQ chip), so make them const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-16-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 73387152ce02..124c18712422 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -221,7 +221,7 @@ struct wcd938x_mbhc_zdet_param { u16 btn7; }; -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20), @@ -3027,7 +3027,7 @@ static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD938x", }; -- cgit v1.2.3 From af57d5e3b58886ee5efc6ba9d0e25c8d1d3c3c37 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:30 +0200 Subject: ASoC: codecs: wcd938x: Constify wcd938x_sdw_ch_info Driver does not modify static wcd938x_sdw_ch_info array, so it can be made const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-17-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x-sdw.c | 4 ++-- sound/soc/codecs/wcd938x.c | 2 +- sound/soc/codecs/wcd938x.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index a1f04010da95..c995bcc59ead 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -21,7 +21,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)), @@ -32,7 +32,7 @@ static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)), }; -static struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)), WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)), WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)), diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 124c18712422..12b32d5dc580 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -418,7 +418,7 @@ static int wcd938x_io_init(struct wcd938x_priv *wcd938x) } -static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, +static int wcd938x_sdw_connect_port(const struct wcd938x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 0d332cb555ac..09e39a19fbe4 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -653,7 +653,7 @@ struct wcd938x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; - struct wcd938x_sdw_ch_info *ch_info; + const struct wcd938x_sdw_ch_info *ch_info; bool port_enable[WCD938X_MAX_SWR_CH_IDS]; int active_ports; int num_ports; -- cgit v1.2.3 From 43e7400f6bf51ecedf4ddad942cd812fe351ce74 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:31 +0200 Subject: ASoC: codecs: wcd938x: Drop unused RX/TX direction enum The enum with RX/TX soundwire direction is not used. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-18-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 09e39a19fbe4..45b7de242209 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -642,10 +642,6 @@ enum wcd938x_rx_sdw_channels { WCD938X_DSD_R, WCD938X_DSD_L, }; -enum { - WCD938X_SDW_DIR_RX, - WCD938X_SDW_DIR_TX, -}; struct wcd938x_priv; struct wcd938x_sdw_priv { -- cgit v1.2.3 From 3f1deca19e68e48bd89a72c4fe648275d9af574b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:32 +0200 Subject: ASoC: codecs: wcd938x: Drop unused num_ports field The driver does not use few 'num_ports' in 'struct wcd938x_sdw_priv'. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-19-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 45b7de242209..b2ad98026ae2 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -652,7 +652,6 @@ struct wcd938x_sdw_priv { const struct wcd938x_sdw_ch_info *ch_info; bool port_enable[WCD938X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd938x_priv *wcd938x; struct irq_domain *slave_irq; -- cgit v1.2.3 From 8e5d5b2c96fadecdaebff5afcbb7f51580701122 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:33 +0200 Subject: ASoC: codecs: wcd939x: Constify static data Driver does not modify few static data (MBHC reg fields, IRQ chip), so make them const for code safety. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-20-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 72d8a6a35052..1f94f49f9829 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -220,7 +220,7 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20), @@ -291,7 +291,7 @@ static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), }; -static struct regmap_irq_chip wcd939x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd939x_regmap_irq_chip = { .name = "wcd939x", .irqs = wcd939x_irqs, .num_irqs = ARRAY_SIZE(wcd939x_irqs), @@ -2957,7 +2957,7 @@ static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) * \- regmap_irq_thread() * \- handle_nested_irq(i) */ -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD939x", }; -- cgit v1.2.3 From f49100faccb5d2ffb4c9e5e4bba6173f5ac43d22 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:34 +0200 Subject: ASoC: codecs: wcd939x: Constify wcd939x_sdw_ch_info Driver does not modify static wcd939x_sdw_ch_info array, so it can be made const for code safety. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-21-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x-sdw.c | 4 ++-- sound/soc/codecs/wcd939x.c | 2 +- sound/soc/codecs/wcd939x.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index 8acb5651c5bc..94b1e99a3ca0 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -23,7 +23,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { +static const struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)), @@ -36,7 +36,7 @@ static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), }; -static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { +static const struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)), WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)), WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)), diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 1f94f49f9829..66af035bd0e5 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -414,7 +414,7 @@ static int wcd939x_io_init(struct snd_soc_component *component) return 0; } -static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, +static int wcd939x_sdw_connect_port(const struct wcd939x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 756f497a337c..a6c9c6cee056 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -914,7 +914,7 @@ struct wcd939x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; - struct wcd939x_sdw_ch_info *ch_info; + const struct wcd939x_sdw_ch_info *ch_info; bool port_enable[WCD939X_MAX_SWR_CH_IDS]; int active_ports; int num_ports; -- cgit v1.2.3 From 2642b6aa9d0f43c52c83461bc7ae4917336e7643 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:35 +0200 Subject: ASoC: codecs: wcd939x: Drop unused RX/TX direction enum The enum with RX/TX soundwire direction is not used. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-22-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index a6c9c6cee056..0aa4d9d542c6 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -903,11 +903,6 @@ enum wcd939x_rx_sdw_channels { WCD939X_HIFI_PCM_R, }; -enum { - WCD939X_SDW_DIR_RX, - WCD939X_SDW_DIR_TX, -}; - struct wcd939x_priv; struct wcd939x_sdw_priv { struct sdw_slave *sdev; -- cgit v1.2.3 From 34935cd4721f0492eafbc516dd1fc7f5f687fa76 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 12 Jun 2024 18:15:36 +0200 Subject: ASoC: codecs: wcd939x: Drop unused num_ports field The driver does not use few 'num_ports' in 'struct wcd939x_sdw_priv'. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-23-0d15885b2a06@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 0aa4d9d542c6..1571c2120cfc 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -912,7 +912,6 @@ struct wcd939x_sdw_priv { const struct wcd939x_sdw_ch_info *ch_info; bool port_enable[WCD939X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd939x_priv *wcd939x; struct irq_domain *slave_irq; -- cgit v1.2.3 From 3ec1428d7b7c519d757a013cef908d7e33dee882 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 17 Jun 2024 14:53:38 +0100 Subject: ASoC: cs35l56: Accept values greater than 0 as IRQ numbers IRQ lookup functions such as those in ACPI can return error values when an IRQ is not defined. The i2c core driver converts the error codes to a value of 0 and the SPI bus driver passes them unaltered to client device drivers. The cs35l56 driver should only accept positive non-zero values as IRQ numbers. Signed-off-by: Simon Trimmer Fixes: 8a731fd37f8b ("ASoC: cs35l56: Move utility functions to shared file") Link: https://msgid.link/r/20240617135338.82006-1-simont@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e89027cd40d1..569bab3a2a6e 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -393,7 +393,7 @@ int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) { int ret; - if (!irq) + if (irq < 1) return 0; ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, -- cgit v1.2.3 From ca73d314363d6682b6c736ea45e3a4ca551923a6 Mon Sep 17 00:00:00 2001 From: Khanh Le Date: Wed, 19 Jun 2024 00:59:36 +0000 Subject: ASoC: Add ak4619 codec support Add support for the Asahi Kasei AK4619 audio codec. [Kuninori cleanuped and adjusted to upstream code] Signed-off-by: Khanh Le Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87iky5wxvr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4619.c | 912 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 919 insertions(+) create mode 100644 sound/soc/codecs/ak4619.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7b99556f24d3..910303adef70 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -45,6 +45,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK4535 imply SND_SOC_AK4554 imply SND_SOC_AK4613 + imply SND_SOC_AK4619 imply SND_SOC_AK4641 imply SND_SOC_AK4642 imply SND_SOC_AK4671 @@ -598,6 +599,10 @@ config SND_SOC_AK4613 tristate "AKM AK4613 CODEC" depends on I2C +config SND_SOC_AK4619 + tristate "AKM AK4619 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ca69f35cc0f7..2db7760d2aac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -39,6 +39,7 @@ snd-soc-ak4458-y := ak4458.o snd-soc-ak4535-y := ak4535.o snd-soc-ak4554-y := ak4554.o snd-soc-ak4613-y := ak4613.o +snd-soc-ak4619-y := ak4619.o snd-soc-ak4641-y := ak4641.o snd-soc-ak4642-y := ak4642.o snd-soc-ak4671-y := ak4671.o @@ -439,6 +440,7 @@ obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o +obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c new file mode 100644 index 000000000000..cbe27abe1f50 --- /dev/null +++ b/sound/soc/codecs/ak4619.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4619.c -- Asahi Kasei ALSA SoC Audio driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + * Khanh Le + * + * Based on ak4613.c by Kuninori Morimoto + * Based on da7213.c by Adam Thomson + * Based on ak4641.c by Harald Welte + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Registers + */ + +#define PWR_MGMT 0x00 /* Power Management */ +#define AU_IFF1 0x01 /* Audio I/F Format */ +#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */ +#define SYS_CLK 0x03 /* System Clock Setting */ +#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */ +#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */ +#define LADC1 0x06 /* ADC1 Lch Digital Volume */ +#define RADC1 0x07 /* ADC1 Rch Digital Volume */ +#define LADC2 0x08 /* ADC2 Lch Digital Volume */ +#define RADC2 0x09 /* ADC2 Rch Digital Volume */ +#define ADC_DF 0x0a /* ADC Digital Filter Setting */ +#define ADC_AI 0x0b /* ADC Analog Input Setting */ +#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */ +#define LDAC1 0x0E /* DAC1 Lch Digital Volume */ +#define RDAC1 0x0F /* DAC1 Rch Digital Volume */ +#define LDAC2 0x10 /* DAC2 Lch Digital Volume */ +#define RDAC2 0x11 /* DAC2 Rch Digital Volume */ +#define DAC_IS 0x12 /* DAC Input Select Setting */ +#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */ +#define DAC_MF 0x14 /* DAC Mute & Filter Setting */ + +/* + * Bit fields + */ + +/* Power Management */ +#define PMAD2 BIT(5) +#define PMAD1 BIT(4) +#define PMDA2 BIT(2) +#define PMDA1 BIT(1) +#define RSTN BIT(0) + +/* Audio_I/F Format */ +#define DCF_STEREO_I2S (0x0 << 4) +#define DCF_STEREO_MSB (0x5 << 4) +#define DCF_PCM_SF (0x6 << 4) +#define DCF_PCM_LF (0x7 << 4) +#define DSL_32 (0x3 << 2) +#define DCF_MASK (0x7 << 4) +#define DSL_MASK (0x3 << 2) +#define BCKP BIT(1) + +/* Audio_I/F Format (Extended) */ +#define DIDL_24 (0x0 << 2) +#define DIDL_20 (0x1 << 2) +#define DIDL_16 (0x2 << 2) +#define DIDL_32 (0x3 << 2) +#define DODL_24 (0x0 << 0) +#define DODL_20 (0x1 << 0) +#define DODL_16 (0x2 << 0) +#define DIDL_MASK (0x3 << 2) +#define DODL_MASK (0x3 << 0) +#define SLOT BIT(4) + +/* System Clock Setting */ +#define FS_MASK 0x7 + +/* MIC AMP Gain */ +#define MGNL_SHIFT 4 +#define MGNR_SHIFT 0 +#define MGN_MAX 0xB + +/* ADC Digital Volume */ +#define VOLAD_SHIFT 0 +#define VOLAD_MAX 0xFF + +/* ADC Digital Filter Setting */ +#define AD1SL_SHIFT 0 +#define AD2SL_SHIFT 4 + +/* Analog Input Select */ +#define AD1LSEL_SHIFT 6 +#define AD1RSEL_SHIFT 4 +#define AD2LSEL_SHIFT 2 +#define AD2RSEL_SHIFT 0 + +/* ADC Mute & HPF Control */ +#define ATSPAD_SHIFT 7 +#define AD1MUTE_SHIFT 5 +#define AD2MUTE_SHIFT 6 +#define AD1MUTE_MAX 1 +#define AD2MUTE_MAX 1 +#define AD1MUTE_EN BIT(5) +#define AD2MUTE_EN BIT(6) +#define AD1HPFN_SHIFT 1 +#define AD1HPFN_MAX 1 +#define AD2HPFN_SHIFT 2 +#define AD2HPFN_MAX 1 + +/* DAC Digital Volume */ +#define VOLDA_SHIFT 0 +#define VOLDA_MAX 0xFF + +/* DAC Input Select Setting */ +#define DAC1SEL_SHIFT 0 +#define DAC2SEL_SHIFT 2 + +/* DAC De-Emphasis Setting */ +#define DEM1_32000 (0x3 << 0) +#define DEM1_44100 (0x0 << 0) +#define DEM1_48000 (0x2 << 0) +#define DEM1_OFF (0x1 << 0) +#define DEM2_32000 (0x3 << 2) +#define DEM2_44100 (0x0 << 2) +#define DEM2_48000 (0x2 << 2) +#define DEM2_OFF (0x1 << 2) +#define DEM1_MASK (0x3 << 0) +#define DEM2_MASK (0x3 << 2) +#define DEM1_SHIFT 0 +#define DEM2_SHIFT 2 + +/* DAC Mute & Filter Setting */ +#define DA1MUTE_SHIFT 4 +#define DA1MUTE_MAX 1 +#define DA2MUTE_SHIFT 5 +#define DA2MUTE_MAX 1 +#define DA1MUTE_EN BIT(4) +#define DA2MUTE_EN BIT(5) +#define ATSPDA_SHIFT 7 +#define DA1SL_SHIFT 0 +#define DA2SL_SHIFT 2 + +/* Codec private data */ +struct ak4619_priv { + struct regmap *regmap; + struct snd_pcm_hw_constraint_list constraint; + int deemph_en; + unsigned int playback_rate; + unsigned int sysclk; +}; + +/* + * DAC Volume + * + * max : 0x00 : +12.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -115.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1); + +/* + * MIC Volume + * + * max : 0x0B : +27.0 dB + * ( 3 dB step ) + * min: 0x00 : -6.0 dB + */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0); + +/* + * ADC Volume + * + * max : 0x00 : +24.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -103.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); + +/* ADC & DAC Volume Level Transition Time select */ +static const char * const ak4619_vol_trans_txt[] = { + "4/fs", "16/fs" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt); + +/* ADC Digital Filter select */ +static const char * const ak4619_adc_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter", + "Voice Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt); + +/* DAC De-Emphasis Filter select */ +static const char * const ak4619_dac_de_emp_txt[] = { + "44.1kHz", "OFF", "48kHz", "32kHz" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt); + +/* DAC Digital Filter select */ +static const char * const ak4619_dac_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt); + +/* + * Control functions + */ + +static void ak4619_set_deemph(struct snd_soc_component *component) +{ + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + u8 dem = 0; + + if (!ak4619->deemph_en) + return; + + switch (ak4619->playback_rate) { + case 32000: + dem |= DEM1_32000 | DEM2_32000; + break; + case 44100: + dem |= DEM1_44100 | DEM2_44100; + break; + case 48000: + dem |= DEM1_48000 | DEM2_48000; + break; + default: + dem |= DEM1_OFF | DEM2_OFF; + break; + } + snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem); +} + +static int ak4619_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + int deemph_en = ucontrol->value.integer.value[0]; + int ret = 0; + + switch (deemph_en) { + case 0: + case 1: + break; + default: + return -EINVAL; + } + + if (ak4619->deemph_en != deemph_en) + ret = 1; /* The value changed */ + + ak4619->deemph_en = deemph_en; + ak4619_set_deemph(component); + + return ret; +} + +static int ak4619_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = ak4619->deemph_en; + + return 0; +}; + +/* + * KControls + */ +static const struct snd_kcontrol_new ak4619_snd_controls[] = { + + /* Volume controls */ + SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + + SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + + /* Volume Level Transition Time controls */ + SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans), + SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans), + + /* Mute controls */ + SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1), + SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1), + + SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1), + SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1), + + /* Filter controls */ + SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil), + SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil), + + SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1), + SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1), + + SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp), + SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp), + + SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil), + SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil), + + SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph), +}; + +/* + * DAPM + */ + +/* Analog input mode */ +static const char * const ak4619_analog_in_txt[] = { + "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt); + +static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in); +static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in); +static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in); +static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in); + +/* DAC source mux */ +static const char * const ak4619_dac_in_txt[] = { + "SDIN1", "SDIN2", "SDOUT1", "SDOUT2" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt); + +static const struct snd_kcontrol_new ak4619_dac_1_in_mux = + SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in); +static const struct snd_kcontrol_new ak4619_dac_2_in_mux = + SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in); + +static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = { + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN5L"), + + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN4R"), + SND_SOC_DAPM_INPUT("AIN5R"), + + SND_SOC_DAPM_INPUT("MIC1L"), + SND_SOC_DAPM_INPUT("MIC1R"), + SND_SOC_DAPM_INPUT("MIC2L"), + SND_SOC_DAPM_INPUT("MIC2R"), + + /* DAI */ + SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux), + + /* MUXs for DAC source selection */ + SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux), + SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux), +}; + +static const struct snd_soc_dapm_route ak4619_intercon[] = { + /* Dest Connecting Widget Source */ + + /* Output path */ + {"AOUT1L", NULL, "DAC1"}, + {"AOUT2L", NULL, "DAC2"}, + + {"AOUT1R", NULL, "DAC1"}, + {"AOUT2R", NULL, "DAC2"}, + + {"DAC1", NULL, "DAC 1 Source MUX"}, + {"DAC2", NULL, "DAC 2 Source MUX"}, + + {"DAC 1 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 1 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"}, + + {"DAC 2 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 2 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"}, + + /* Input path */ + {"SDOUT1", NULL, "ADC1"}, + {"SDOUT2", NULL, "ADC2"}, + + {"ADC1", NULL, "Analog Input 1 Left MUX"}, + {"ADC1", NULL, "Analog Input 1 Right MUX"}, + + {"ADC2", NULL, "Analog Input 2 Left MUX"}, + {"ADC2", NULL, "Analog Input 2 Right MUX"}, + + {"Analog Input 1 Left MUX", "Differential", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"}, + {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"}, + + {"Analog Input 1 Right MUX", "Differential", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"}, + {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"}, + + {"Analog Input 2 Left MUX", "Differential", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"}, + {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"}, + + {"Analog Input 2 Right MUX", "Differential", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"}, + {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"}, + + {"MIC1L", NULL, "AIN1L"}, + {"MIC1L", NULL, "AIN2L"}, + + {"MIC1R", NULL, "AIN1R"}, + {"MIC1R", NULL, "AIN2R"}, + + {"MIC2L", NULL, "AIN4L"}, + {"MIC2L", NULL, "AIN5L"}, + + {"MIC2R", NULL, "AIN4R"}, + {"MIC2R", NULL, "AIN5R"}, +}; + +static const struct reg_default ak4619_reg_defaults[] = { + { PWR_MGMT, 0x00 }, + { AU_IFF1, 0x0C }, + { AU_IFF2, 0x0C }, + { SYS_CLK, 0x00 }, + { MIC_AMP1, 0x22 }, + { MIC_AMP2, 0x22 }, + { LADC1, 0x30 }, + { RADC1, 0x30 }, + { LADC2, 0x30 }, + { RADC2, 0x30 }, + { ADC_DF, 0x00 }, + { ADC_AI, 0x00 }, + { ADC_MHPF, 0x00 }, + { LDAC1, 0x18 }, + { RDAC1, 0x18 }, + { LDAC2, 0x18 }, + { RDAC2, 0x18 }, + { DAC_IS, 0x04 }, + { DAC_DEMP, 0x05 }, + { DAC_MF, 0x0A }, +}; + +static int ak4619_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u8 pwr_ctrl = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + pwr_ctrl |= RSTN; + fallthrough; + case SND_SOC_BIAS_PREPARE: + pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2; + fallthrough; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_component_write(component, PWR_MGMT, pwr_ctrl); + + return 0; +} + +static int ak4619_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + unsigned int width; + unsigned int rate; + unsigned int fs; + bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 dai_ctrl = 0; + u8 clk_mode = 0; + + width = params_width(params); + switch (width) { + case 16: + dai_ctrl |= is_play ? DIDL_16 : DODL_16; + break; + case 20: + dai_ctrl |= is_play ? DIDL_20 : DODL_20; + break; + case 24: + dai_ctrl |= is_play ? DIDL_24 : DODL_24; + break; + case 32: + if (is_play) + dai_ctrl |= DIDL_32; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + rate = params_rate(params); + if (rate) + fs = ak4619->sysclk / rate; + else + return -EINVAL; + + switch (rate) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + switch (fs) { + case 256: + clk_mode |= (0x0 << 0); + break; + case 384: + clk_mode |= (0x2 << 0); + break; + case 512: + clk_mode |= (0x3 << 0); + break; + default: + return -EINVAL; + } + break; + case 64000: + case 88200: + case 96000: + if (fs == 256) + clk_mode |= (0x1 << 0); + else + return -EINVAL; + break; + case 176400: + case 192000: + if (fs == 128) + clk_mode |= (0x4 << 0); + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode); + snd_soc_component_update_bits(component, AU_IFF2, + is_play ? DIDL_MASK : DODL_MASK, dai_ctrl); + + if (is_play) { + ak4619->playback_rate = rate; + ak4619_set_deemph(component); + } + + return 0; +} + +static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 dai_fmt1 = 0; + u8 dai_fmt2 = 0; + + /* Set clock normal/inverted */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + dai_fmt1 |= BCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + default: + return -EINVAL; + } + + /* Only Stereo modes are supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_fmt1 |= DCF_STEREO_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_fmt1 |= DCF_STEREO_MSB; + break; + case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ + dai_fmt1 |= DCF_PCM_SF; + dai_fmt2 |= SLOT; + break; + case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ + dai_fmt1 |= DCF_PCM_LF; + dai_fmt2 |= SLOT; + break; + default: + return -EINVAL; + } + + /* Only slave mode is support */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* By default only 64 BICK per LRCLK is supported */ + dai_fmt1 |= DSL_32; + + snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK | + DSL_MASK | BCKP, dai_fmt1); + snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2); + + return 0; +} + +static int ak4619_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619->sysclk = freq; + + return 0; +} + +static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0); + snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0); + + return 0; +} + +static void ak4619_hw_constraints(struct ak4619_priv *ak4619, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint; + int ak4619_rate_mask = 0; + unsigned int fs; + int i; + static const unsigned int ak4619_sr[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, + }; + + /* + * [8kHz - 48kHz] : 256fs, 384fs or 512fs + * [64kHz - 96kHz] : 256fs + * [176.4kHz, 192kHz] : 128fs + */ + + for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) { + fs = ak4619->sysclk / ak4619_sr[i]; + + switch (fs) { + case 512: + case 384: + case 256: + ak4619_rate_mask |= (1 << i); + break; + case 128: + switch (i) { + case (ARRAY_SIZE(ak4619_sr) - 1): + case (ARRAY_SIZE(ak4619_sr) - 2): + ak4619_rate_mask |= (1 << i); + break; + default: + break; + } + break; + default: + break; + } + }; + + constraint->list = ak4619_sr; + constraint->mask = ak4619_rate_mask; + constraint->count = ARRAY_SIZE(ak4619_sr); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint); +}; + +#define PLAYBACK_MODE 0 +#define CAPTURE_MODE 1 + +static int ak4619_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619_hw_constraints(ak4619, substream->runtime); + + return 0; +} + +static u64 ak4619_dai_formats[] = { + /* + * Select below from Sound Card, not here + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ + + /* First Priority */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J, + + /* Second Priority */ + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops ak4619_dai_ops = { + .startup = ak4619_dai_startup, + .set_sysclk = ak4619_dai_set_sysclk, + .set_fmt = ak4619_dai_set_fmt, + .hw_params = ak4619_dai_hw_params, + .mute_stream = ak4619_dai_mute, + .auto_selectable_formats = ak4619_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats), +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4619 = { + .set_bias_level = ak4619_set_bias_level, + .controls = ak4619_snd_controls, + .num_controls = ARRAY_SIZE(ak4619_snd_controls), + .dapm_widgets = ak4619_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets), + .dapm_routes = ak4619_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4619_intercon), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4619_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x14, + .reg_defaults = ak4619_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static const struct of_device_id ak4619_of_match[] = { + { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4619_of_match); + +static const struct i2c_device_id ak4619_i2c_id[] = { + { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id); + +#define AK4619_RATES SNDRV_PCM_RATE_8000_192000 + +#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4619_dai = { + .name = "ak4619-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_DAC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_ADC_FORMATS, + }, + .ops = &ak4619_dai_ops, + .symmetric_rate = 1, +}; + +static int ak4619_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct ak4619_priv *ak4619; + int ret; + + ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL); + if (!ak4619) + return -ENOMEM; + + i2c_set_clientdata(i2c, ak4619); + + ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg); + if (IS_ERR(ak4619->regmap)) { + ret = PTR_ERR(ak4619->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619, + &ak4619_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register ak4619 component: %d\n", + ret); + return ret; + } + + return 0; +} + +static struct i2c_driver ak4619_i2c_driver = { + .driver = { + .name = "ak4619-codec", + .of_match_table = ak4619_of_match, + }, + .probe = ak4619_i2c_probe, + .id_table = ak4619_i2c_id, +}; +module_i2c_driver(ak4619_i2c_driver); + +MODULE_DESCRIPTION("SoC AK4619 driver"); +MODULE_AUTHOR("Khanh Le "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 6469267afed4ffb9d16ce8c44a2b518b9b31c215 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 19 Jun 2024 01:00:23 +0000 Subject: ASoC: dt-bindings: ak4619: Add initial DT binding Add Asahi Kasei ak4619 audio codec bindings. Signed-off-by: Kuninori Morimoto Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/87h6dpwxug.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../bindings/sound/asahi-kasei,ak4619.yaml | 62 ++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml new file mode 100644 index 000000000000..d412531ef9a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4619.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4619 I2C transmitter + +maintainers: + - Kuninori Morimoto + - Khanh Le + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: asahi-kasei,ak4619 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: mclk + + "#sound-dai-cells": + const: 0 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@10 { + compatible = "asahi-kasei,ak4619"; + reg = <0x10>; + + clocks = <&rcar_sound>; + clock-names = "mclk"; + + #sound-dai-cells = <0>; + port { + ak4619_endpoint: endpoint { + remote-endpoint = <&rsnd_endpoint>; + }; + }; + }; + }; -- cgit v1.2.3 From fe1ff61487ace69cd4e680e36169c90667eb9624 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Wed, 19 Jun 2024 05:53:42 +0000 Subject: ASoC: rt1318: Add RT1318 audio amplifier driver This is the initial i2s-based amplifier driver for rt1318. Signed-off-by: Jack Yu Link: https://msgid.link/r/b3055442ce6d4994aa01aa1fad6ba1fe@realtek.com Signed-off-by: Mark Brown --- include/sound/rt1318.h | 16 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt1318.c | 1354 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt1318.h | 342 ++++++++++++ 5 files changed, 1719 insertions(+) create mode 100644 include/sound/rt1318.h create mode 100644 sound/soc/codecs/rt1318.c create mode 100644 sound/soc/codecs/rt1318.h diff --git a/include/sound/rt1318.h b/include/sound/rt1318.h new file mode 100644 index 000000000000..fe6bff06036c --- /dev/null +++ b/include/sound/rt1318.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/sound/rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ + +#ifndef __LINUX_SND_RT1318_H +#define __LINUX_SND_RT1318_H + +struct rt1318_platform_data { + unsigned int init_r0_l; + unsigned int init_r0_r; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7b99556f24d3..a99cb26eb280 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -221,6 +221,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318 imply SND_SOC_RT1318_SDW imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 @@ -1576,6 +1577,10 @@ config SND_SOC_RT1316_SDW depends on SOUNDWIRE select REGMAP_SOUNDWIRE +config SND_SOC_RT1318 + tristate + depends on I2C + config SND_SOC_RT1318_SDW tristate "Realtek RT1318 Codec - SDW" depends on SOUNDWIRE diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ca69f35cc0f7..ad0f77224c74 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -222,6 +222,7 @@ snd-soc-rt1305-y := rt1305.o snd-soc-rt1308-y := rt1308.o snd-soc-rt1308-sdw-y := rt1308-sdw.o snd-soc-rt1316-sdw-y := rt1316-sdw.o +snd-soc-rt1318-y := rt1318.o snd-soc-rt1318-sdw-y := rt1318-sdw.o snd-soc-rt1320-sdw-y := rt1320-sdw.o snd-soc-rt274-y := rt274.o @@ -618,6 +619,7 @@ obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o +obj-$(CONFIG_SND_SOC_RT1318) += snd-soc-rt1318.o obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c new file mode 100644 index 000000000000..4e47db4fc7fb --- /dev/null +++ b/sound/soc/codecs/rt1318.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1318.c -- RT1318 ALSA SoC audio amplifier driver +// Author: Jack Yu +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt1318.h" + +static struct reg_sequence init_list[] = { + { 0x0000C000, 0x01}, + { 0x0000F20D, 0x00}, + { 0x0000F212, 0x3E}, + { 0x0000C001, 0x02}, + { 0x0000C003, 0x22}, + { 0x0000C004, 0x44}, + { 0x0000C005, 0x44}, + { 0x0000C007, 0x64}, + { 0x0000C00E, 0xE7}, + { 0x0000F223, 0x7F}, + { 0x0000F224, 0xDB}, + { 0x0000F225, 0xEE}, + { 0x0000F226, 0x3F}, + { 0x0000F227, 0x0F}, + { 0x0000F21A, 0x78}, + { 0x0000F242, 0x3C}, + { 0x0000C120, 0x40}, + { 0x0000C125, 0x03}, + { 0x0000C321, 0x0A}, + { 0x0000C200, 0xD8}, + { 0x0000C201, 0x27}, + { 0x0000C202, 0x0F}, + { 0x0000C400, 0x0E}, + { 0x0000C401, 0x43}, + { 0x0000C402, 0xE0}, + { 0x0000C403, 0x00}, + { 0x0000C404, 0x4C}, + { 0x0000C406, 0x40}, + { 0x0000C407, 0x02}, + { 0x0000C408, 0x3F}, + { 0x0000C300, 0x01}, + { 0x0000C125, 0x03}, + { 0x0000DF00, 0x10}, + { 0x0000F20B, 0x2A}, + { 0x0000DF5F, 0x01}, + { 0x0000DF60, 0xA7}, + { 0x0000C203, 0x84}, + { 0x0000C206, 0x78}, + { 0x0000F10A, 0x09}, + { 0x0000F10B, 0x4C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x03}, + { 0x0000F109, 0xE0}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x04}, + { 0x0000F109, 0x65}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x02}, + { 0x0000F109, 0x30}, + { 0x0000F10B, 0x5C}, + { 0x0000E706, 0x0F}, + { 0x0000E707, 0x30}, + { 0x0000E806, 0x0F}, + { 0x0000E807, 0x30}, + { 0x0000CE04, 0x03}, + { 0x0000CE05, 0x5F}, + { 0x0000CE06, 0xA2}, + { 0x0000CE07, 0x6B}, + { 0x0000CF04, 0x03}, + { 0x0000CF05, 0x5F}, + { 0x0000CF06, 0xA2}, + { 0x0000CF07, 0x6B}, + { 0x0000CE60, 0xE3}, + { 0x0000C130, 0x51}, + { 0x0000E000, 0xA8}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x04}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x41001888, 0x00}, + { 0x0000C121, 0x0B}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x00}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x0000F800, 0x20}, + { 0x0000CA00, 0x80}, + { 0x0000CA10, 0x00}, + { 0x0000CA02, 0x78}, + { 0x0000CA12, 0x78}, + { 0x0000ED00, 0x90}, + { 0x0000E604, 0x00}, + { 0x0000DB00, 0x0C}, + { 0x0000DD00, 0x0C}, + { 0x0000DC19, 0x00}, + { 0x0000DC1A, 0x6A}, + { 0x0000DC1B, 0xAA}, + { 0x0000DC1C, 0xAB}, + { 0x0000DC1D, 0x00}, + { 0x0000DC1E, 0x16}, + { 0x0000DC1F, 0xDB}, + { 0x0000DC20, 0x6D}, + { 0x0000DE19, 0x00}, + { 0x0000DE1A, 0x6A}, + { 0x0000DE1B, 0xAA}, + { 0x0000DE1C, 0xAB}, + { 0x0000DE1D, 0x00}, + { 0x0000DE1E, 0x16}, + { 0x0000DE1F, 0xDB}, + { 0x0000DE20, 0x6D}, + { 0x0000DB32, 0x00}, + { 0x0000DD32, 0x00}, + { 0x0000DB33, 0x0A}, + { 0x0000DD33, 0x0A}, + { 0x0000DB34, 0x1A}, + { 0x0000DD34, 0x1A}, + { 0x0000DB15, 0xEF}, + { 0x0000DD15, 0xEF}, + { 0x0000DB17, 0xEF}, + { 0x0000DD17, 0xEF}, + { 0x0000DB94, 0x70}, + { 0x0000DD94, 0x70}, + { 0x0000DB19, 0x40}, + { 0x0000DD19, 0x40}, + { 0x0000DB12, 0xC0}, + { 0x0000DD12, 0xC0}, + { 0x0000DB00, 0x4C}, + { 0x0000DB04, 0x05}, + { 0x0000DB05, 0x03}, + { 0x0000DD04, 0x05}, + { 0x0000DD05, 0x03}, + { 0x0000DBBB, 0x09}, + { 0x0000DBBC, 0x30}, + { 0x0000DBBD, 0xF0}, + { 0x0000DBBE, 0xF1}, + { 0x0000DDBB, 0x09}, + { 0x0000DDBC, 0x30}, + { 0x0000DDBD, 0xF0}, + { 0x0000DDBE, 0xF1}, + { 0x0000DB01, 0x79}, + { 0x0000DD01, 0x79}, + { 0x0000DB08, 0x40}, + { 0x0000DD08, 0x40}, + { 0x0000DC52, 0xEF}, + { 0x0000DE52, 0xEF}, + { 0x0000DB00, 0xCC}, + { 0x0000CC2C, 0x00}, + { 0x0000CC2D, 0x2A}, + { 0x0000CC2E, 0x83}, + { 0x0000CC2F, 0xA8}, + { 0x0000CD2C, 0x00}, + { 0x0000CD2D, 0x2A}, + { 0x0000CD2E, 0x83}, + { 0x0000CD2F, 0xA8}, + { 0x0000CC24, 0x00}, + { 0x0000CC25, 0x51}, + { 0x0000CC26, 0xEB}, + { 0x0000CC27, 0x85}, + { 0x0000CD24, 0x00}, + { 0x0000CD25, 0x51}, + { 0x0000CD26, 0xEB}, + { 0x0000CD27, 0x85}, + { 0x0000CC20, 0x00}, + { 0x0000CC21, 0x00}, + { 0x0000CC22, 0x43}, + { 0x0000CD20, 0x00}, + { 0x0000CD21, 0x00}, + { 0x0000CD22, 0x43}, + { 0x0000CC16, 0x0F}, + { 0x0000CC17, 0x00}, + { 0x0000CD16, 0x0F}, + { 0x0000CD17, 0x00}, + { 0x0000CC29, 0x5D}, + { 0x0000CC2A, 0xC0}, + { 0x0000CD29, 0x5D}, + { 0x0000CD2A, 0xC0}, + { 0x0000CC31, 0x20}, + { 0x0000CC32, 0x00}, + { 0x0000CC33, 0x00}, + { 0x0000CC34, 0x00}, + { 0x0000CD31, 0x20}, + { 0x0000CD32, 0x00}, + { 0x0000CD33, 0x00}, + { 0x0000CD34, 0x00}, + { 0x0000CC36, 0x79}, + { 0x0000CC37, 0x99}, + { 0x0000CC38, 0x99}, + { 0x0000CC39, 0x99}, + { 0x0000CD36, 0x79}, + { 0x0000CD37, 0x99}, + { 0x0000CD38, 0x99}, + { 0x0000CD39, 0x99}, + { 0x0000CC09, 0x00}, + { 0x0000CC0A, 0x07}, + { 0x0000CC0B, 0x5F}, + { 0x0000CC0C, 0x6F}, + { 0x0000CD09, 0x00}, + { 0x0000CD0A, 0x07}, + { 0x0000CD0B, 0x5F}, + { 0x0000CD0C, 0x6F}, + { 0x0000CC0E, 0x00}, + { 0x0000CC0F, 0x03}, + { 0x0000CC10, 0xAF}, + { 0x0000CC11, 0xB7}, + { 0x0000CD0E, 0x00}, + { 0x0000CD0F, 0x03}, + { 0x0000CD10, 0xAF}, + { 0x0000CD11, 0xB7}, + { 0x0000CCD6, 0x00}, + { 0x0000CCD7, 0x03}, + { 0x0000CDD6, 0x00}, + { 0x0000CDD7, 0x03}, + { 0x0000CCD8, 0x00}, + { 0x0000CCD9, 0x03}, + { 0x0000CDD8, 0x00}, + { 0x0000CDD9, 0x03}, + { 0x0000CCDA, 0x00}, + { 0x0000CCDB, 0x03}, + { 0x0000CDDA, 0x00}, + { 0x0000CDDB, 0x03}, + { 0x0000C320, 0x20}, + { 0x0000C203, 0x9C}, +}; +#define rt1318_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt1318_reg[] = { + { 0xc000, 0x00 }, + { 0xc001, 0x43 }, + { 0xc003, 0x22 }, + { 0xc004, 0x44 }, + { 0xc005, 0x44 }, + { 0xc006, 0x33 }, + { 0xc007, 0x64 }, + { 0xc008, 0x05 }, + { 0xc00a, 0xfc }, + { 0xc00b, 0x0f }, + { 0xc00c, 0x0e }, + { 0xc00d, 0xef }, + { 0xc00e, 0xe5 }, + { 0xc00f, 0xff }, + { 0xc120, 0xc0 }, + { 0xc121, 0x00 }, + { 0xc122, 0x00 }, + { 0xc123, 0x14 }, + { 0xc125, 0x00 }, + { 0xc130, 0x59 }, + { 0xc200, 0x00 }, + { 0xc201, 0x00 }, + { 0xc202, 0x00 }, + { 0xc203, 0x04 }, + { 0xc204, 0x00 }, + { 0xc205, 0x00 }, + { 0xc206, 0x68 }, + { 0xc207, 0x70 }, + { 0xc208, 0x00 }, + { 0xc20a, 0x00 }, + { 0xc20b, 0x01 }, + { 0xc20c, 0x7f }, + { 0xc20d, 0x01 }, + { 0xc20e, 0x7f }, + { 0xc300, 0x00 }, + { 0xc301, 0x00 }, + { 0xc303, 0x80 }, + { 0xc320, 0x00 }, + { 0xc321, 0x09 }, + { 0xc322, 0x02 }, + { 0xc400, 0x00 }, + { 0xc401, 0x00 }, + { 0xc402, 0x00 }, + { 0xc403, 0x00 }, + { 0xc404, 0x00 }, + { 0xc405, 0x00 }, + { 0xc406, 0x00 }, + { 0xc407, 0x00 }, + { 0xc408, 0x00 }, + { 0xc410, 0x04 }, + { 0xc430, 0x00 }, + { 0xc431, 0x00 }, + { 0xca00, 0x10 }, + { 0xca01, 0x00 }, + { 0xca02, 0x0b }, + { 0xca10, 0x10 }, + { 0xca11, 0x00 }, + { 0xca12, 0x0b }, + { 0xce04, 0x08 }, + { 0xce05, 0x00 }, + { 0xce06, 0x00 }, + { 0xce07, 0x00 }, + { 0xce60, 0x63 }, + { 0xcf04, 0x08 }, + { 0xcf05, 0x00 }, + { 0xcf06, 0x00 }, + { 0xcf07, 0x00 }, + { 0xdb00, 0x00 }, + { 0xdb08, 0x40 }, + { 0xdb12, 0x00 }, + { 0xdb35, 0x00 }, + { 0xdbb5, 0x00 }, + { 0xdbb6, 0x40 }, + { 0xdbb7, 0x00 }, + { 0xdbb8, 0x00 }, + { 0xdbc5, 0x00 }, + { 0xdbc6, 0x00 }, + { 0xdbc7, 0x00 }, + { 0xdbc8, 0x00 }, + { 0xdd08, 0x40 }, + { 0xdd12, 0x00 }, + { 0xdd35, 0x00 }, + { 0xddb5, 0x00 }, + { 0xddb6, 0x40 }, + { 0xddb7, 0x00 }, + { 0xddb8, 0x00 }, + { 0xddc5, 0x00 }, + { 0xddc6, 0x00 }, + { 0xddc7, 0x00 }, + { 0xddc8, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, + { 0xdf00, 0x00 }, + { 0xdf5f, 0x00 }, + { 0xdf60, 0x00 }, + { 0xe000, 0x08 }, + { 0xe300, 0xa0 }, + { 0xe400, 0x22 }, + { 0xe706, 0x2f }, + { 0xe707, 0x2f }, + { 0xe806, 0x2f }, + { 0xe807, 0x2f }, + { 0xea00, 0x43 }, + { 0xed00, 0x80 }, + { 0xed01, 0x0f }, + { 0xed02, 0xff }, + { 0xed03, 0x00 }, + { 0xed04, 0x00 }, + { 0xed05, 0x0f }, + { 0xed06, 0xff }, + { 0xf010, 0x10 }, + { 0xf011, 0xec }, + { 0xf012, 0x68 }, + { 0xf013, 0x21 }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0x00 }, + { 0xf105, 0x00 }, + { 0xf106, 0x00 }, + { 0xf107, 0x00 }, + { 0xf108, 0x00 }, + { 0xf109, 0x00 }, + { 0xf10a, 0x03 }, + { 0xf10b, 0x40 }, + { 0xf20b, 0x28 }, + { 0xf20d, 0x00 }, + { 0xf212, 0x00 }, + { 0xf21a, 0x00 }, + { 0xf223, 0x40 }, + { 0xf224, 0x00 }, + { 0xf225, 0x00 }, + { 0xf226, 0x00 }, + { 0xf227, 0x00 }, + { 0xf242, 0x0c }, + { 0xf800, 0x00 }, + { 0xf801, 0x12 }, + { 0xf802, 0xe0 }, + { 0xf803, 0x2f }, + { 0xf804, 0x00 }, + { 0xf805, 0x00 }, + { 0xf806, 0x07 }, + { 0xf807, 0xff }, +}; + +static bool rt1318_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000: + case 0xc301: + case 0xc410: + case 0xc430 ... 0xc431: + case 0xdb06: + case 0xdb12: + case 0xdb1d ... 0xdb1f: + case 0xdb35: + case 0xdb37: + case 0xdb8a ... 0xdb92: + case 0xdbc5 ... 0xdbc8: + case 0xdc2b ... 0xdc49: + case 0xdd0b: + case 0xdd12: + case 0xdd1d ... 0xdd1f: + case 0xdd35: + case 0xdd8a ... 0xdd92: + case 0xddc5 ... 0xddc8: + case 0xde2b ... 0xde44: + case 0xdf4a ... 0xdf55: + case 0xe224 ... 0xe23b: + case 0xea01: + case 0xebc5: + case 0xebc8: + case 0xebcb ... 0xebcc: + case 0xed03 ... 0xed06: + case 0xf010 ... 0xf014: + return true; + + default: + return false; + } +} + +static bool rt1318_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000 ... 0xc00f: + case 0xc120 ... 0xc130: + case 0xc200 ... 0xc20e: + case 0xc300 ... 0xc303: + case 0xc320 ... 0xc322: + case 0xc400 ... 0xc408: + case 0xc430 ... 0xc431: + case 0xca00 ... 0xca02: + case 0xca10 ... 0xca12: + case 0xcb00 ... 0xcb0b: + case 0xcc00 ... 0xcce5: + case 0xcd00 ... 0xcde5: + case 0xce00 ... 0xce6a: + case 0xcf00 ... 0xcf53: + case 0xd000 ... 0xd0cc: + case 0xd100 ... 0xd1b9: + case 0xdb00 ... 0xdc53: + case 0xdd00 ... 0xde53: + case 0xdf00 ... 0xdf6b: + case 0xe000: + case 0xe300: + case 0xe400: + case 0xe706 ... 0xe707: + case 0xe806 ... 0xe807: + case 0xea00: + case 0xeb00 ... 0xebcc: + case 0xec00 ... 0xecb9: + case 0xed00 ... 0xed06: + case 0xf010 ... 0xf014: + case 0xf102 ... 0xf10b: + case 0xf20b: + case 0xf20d ... 0xf242: + case 0xf800 ... 0xf807: + return true; + default: + return false; + } +} + +static int rt1318_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH); + break; + + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW); + break; + + default: + break; + } + return 0; +} + +static int rt1318_dvol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->rt1318_dvol = ucontrol->value.integer.value[0]; + + if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) { + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7, + rt1318->rt1318_dvol & 0xff); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7, + rt1318->rt1318_dvol & 0xff); + return 1; + } + + return 0; +} + +static int rt1318_dvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1318->rt1318_dvol; + + return 0; +} + +static const struct snd_kcontrol_new rt1318_snd_controls[] = { + SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0, + rt1318_dvol_get, rt1318_dvol_put), +}; + +static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + /* DACs */ + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + rt1318_dac_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("Amp"), +}; + +static const struct snd_soc_dapm_route rt1318_dapm_routes[] = { + {"DAC", NULL, "AIF1RX"}, + {"Amp", NULL, "DAC"}, +}; + +static int rt1318_get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 4, 8, 16, 24}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (lrclk) { + case RT1318_LRCLK_48000: + case RT1318_LRCLK_44100: + case RT1318_LRCLK_16000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4); + break; + case RT1318_LRCLK_96000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2); + break; + case RT1318_LRCLK_192000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1); + break; + default: + dev_err(component->dev, "Unsupported clock rate.\n"); + return -EINVAL; + } + + return 0; +} + +static int rt1318_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int data_len = 0, ch_len = 0; + int pre_div, ret; + + rt1318->lrck = params_rate(params); + pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + ret = rt1318_clk_ip_info(component, rt1318->lrck); + if (ret < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + data_len = RT1318_I2S_DL_20; + ch_len = RT1318_I2S_DL_20; + break; + case 24: + data_len = RT1318_I2S_DL_24; + ch_len = RT1318_I2S_DL_24; + break; + case 32: + data_len = RT1318_I2S_DL_32; + ch_len = RT1318_I2S_DL_32; + break; + case 8: + data_len = RT1318_I2S_DL_8; + ch_len = RT1318_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_CLK2, + RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK, + pre_div << RT1318_DIV_AP_SFT | + pre_div << RT1318_DIV_DAMOD_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK3, + RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK, + pre_div << RT1318_AD_STO1_SFT | + pre_div << RT1318_AD_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK4, + RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK, + pre_div << RT1318_AD_ANA_STO1_SFT | + pre_div << RT1318_AD_ANA_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK5, + RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK, + pre_div << RT1318_DIV_FIFO_IN_SFT | + pre_div << RT1318_DIV_FIFO_OUT_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK6, + RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK | + RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT | + pre_div << RT1318_DIV_AD_MONO_SFT | + pre_div << RT1318_DIV_POST_G_SFT); + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, + ch_len << RT1318_I2S_TX_CHL_SFT | + ch_len << RT1318_I2S_RX_CHL_SFT); + + return 0; +} + +static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val2 |= RT1318_TDM_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1318_FMT_LEFT_J; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1318_FMT_PCM_A_R; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1318_FMT_PCM_B_R; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_I2S_FMT_MASK, reg_val); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_TDM_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int reg_val = 0; + + if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src) + return 0; + + switch (clk_id) { + case RT1318_SCLK_S_BCLK: + reg_val |= RT1318_SYSCLK_BCLK; + break; + case RT1318_SCLK_S_SDW: + reg_val |= RT1318_SYSCLK_SDW; + break; + case RT1318_SCLK_S_PLL2F: + reg_val |= RT1318_SYSCLK_PLL2F; + break; + case RT1318_SCLK_S_PLL2B: + reg_val |= RT1318_SYSCLK_PLL2B; + break; + case RT1318_SCLK_S_MCLK: + reg_val |= RT1318_SYSCLK_MCLK; + break; + case RT1318_SCLK_S_RC0: + reg_val |= RT1318_SYSCLK_RC1; + break; + case RT1318_SCLK_S_RC1: + reg_val |= RT1318_SYSCLK_RC2; + break; + case RT1318_SCLK_S_RC2: + reg_val |= RT1318_SYSCLK_RC3; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1318->sysclk = freq; + rt1318->sysclk_src = clk_id; + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_SYSCLK_SEL_MASK, reg_val); + + return 0; +} + +static const struct pll_calc_map pll_preset_table[] = { + {512000, 4096000, 22, 190, 0, true, false}, + {1024000, 4096000, 22, 94, 0, true, false}, + {1024000, 16384000, 4, 190, 0, true, false}, + {1411200, 11289600, 6, 62, 0, true, false}, + {1536000, 12288000, 6, 62, 0, true, false}, + {2822400, 11289600, 6, 62, 0, true, false}, + {2822400, 45158400, 0, 62, 0, true, false}, + {2822400, 49152000, 0, 62, 0, true, false}, + {3072000, 12288000, 6, 62, 0, true, false}, + {3072000, 24576000, 2, 62, 0, true, false}, + {3072000, 49152000, 0, 62, 0, true, false}, + {6144000, 24576000, 2, 94, 4, false, false}, + {6144000, 49152000, 0, 30, 0, true, false}, + {6144000, 98304000, 0, 94, 4, false, true}, + {12288000, 49152000, 0, 62, 6, false, false}, +}; + +static int rt1318_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt1318_pll_code *pll_code) +{ + int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX; + int i, k, red, n_t, pll_out, in_t, out_t; + int n = 0, m = 0, m_t = 0; + int red_t = abs(freq_out - freq_in); + bool m_bypass = false, k_bypass = false; + + if (RT1318_PLL_INP_MAX < freq_in || RT1318_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { + if (freq_in == pll_preset_table[i].pll_in && + freq_out == pll_preset_table[i].pll_out) { + k = pll_preset_table[i].k; + m = pll_preset_table[i].m; + n = pll_preset_table[i].n; + m_bypass = pll_preset_table[i].m_bp; + k_bypass = pll_preset_table[i].k_bp; + goto code_find; + } + } + + k = 100000000 / freq_out - 2; + if (k > RT1318_PLL_K_MAX) + k = RT1318_PLL_K_MAX; + if (k < 0) { + k = 0; + k_bypass = true; + } + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = freq_in / (k_bypass ? 1 : (k + 2)); + pll_out = freq_out / (n_t + 2); + if (in_t < 0) + continue; + if (in_t == pll_out) { + m_bypass = true; + n = n_t; + goto code_find; + } + red = abs(in_t - pll_out); + if (red < red_t) { + m_bypass = true; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - pll_out); + if (red < red_t) { + m_bypass = false; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + + pll_code->m_bp = m_bypass; + pll_code->k_bp = k_bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = k; + return 0; +} + +static int rt1318_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + struct rt1318_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + rt1318->pll_in = 0; + rt1318->pll_out = 0; + return 0; + } + + if (source == rt1318->pll_src && freq_in == rt1318->pll_in && + freq_out == rt1318->pll_out) + return 0; + + switch (source) { + case RT1318_PLL_S_BCLK0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0); + break; + case RT1318_PLL_S_BCLK1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1); + break; + case RT1318_PLL_S_RC: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_RC); + break; + case RT1318_PLL_S_MCLK: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK); + break; + case RT1318_PLL_S_SDW_IN_PLL: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1); + break; + case RT1318_PLL_S_SDW_0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2); + break; + case RT1318_PLL_S_SDW_1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3); + break; + case RT1318_PLL_S_SDW_2: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt1318_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_update_bits(rt1318->regmap, RT1318_PLL1_K, + RT1318_K_PLL1_MASK, pll_code.k_code); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_M, + RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code)); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8, + RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0, + RT1318_N_7_0_PLL1_MASK, pll_code.n_code); + + rt1318->pll_in = freq_in; + rt1318->pll_out = freq_out; + rt1318->pll_src = source; + + return 0; +} + +static int rt1318_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int cn = 0, cl = 0, rx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 4: + cn |= RT1318_I2S_CH_TX_4CH; + cn |= RT1318_I2S_CH_RX_4CH; + break; + case 6: + cn |= RT1318_I2S_CH_TX_6CH; + cn |= RT1318_I2S_CH_RX_6CH; + break; + case 8: + cn |= RT1318_I2S_CH_TX_8CH; + cn |= RT1318_I2S_CH_RX_8CH; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + cl |= RT1318_I2S_TX_CHL_20; + cl |= RT1318_I2S_RX_CHL_20; + break; + case 24: + cl |= RT1318_I2S_TX_CHL_24; + cl |= RT1318_I2S_RX_CHL_24; + break; + case 32: + cl |= RT1318_I2S_TX_CHL_32; + cl |= RT1318_I2S_RX_CHL_32; + break; + case 8: + cl |= RT1318_I2S_TX_CHL_8; + cl |= RT1318_I2S_RX_CHL_8; + break; + case 16: + break; + default: + return -EINVAL; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl); + +_set_tdm_err_: + return ret; +} + +static int rt1318_probe(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->component = component; + + schedule_work(&rt1318->cali_work); + rt1318->rt1318_dvol = RT1318_DVOL_STEP; + + return 0; +} + +static void rt1318_remove(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + cancel_work_sync(&rt1318->cali_work); +} + +#ifdef CONFIG_PM +static int rt1318_suspend(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, true); + regcache_mark_dirty(rt1318->regmap); + return 0; +} + +static int rt1318_resume(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, false); + regcache_sync(rt1318->regmap); + return 0; +} +#else +#define rt1318_suspend NULL +#define rt1318_resume NULL +#endif + +#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1318_aif_dai_ops = { + .hw_params = rt1318_hw_params, + .set_fmt = rt1318_set_dai_fmt, + .set_sysclk = rt1318_set_dai_sysclk, + .set_pll = rt1318_set_dai_pll, + .set_tdm_slot = rt1318_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1318_dai[] = { + { + .name = "rt1318-aif", + .id = 0, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1318_STEREO_RATES, + .formats = RT1318_FORMATS, + }, + .ops = &rt1318_aif_dai_ops, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1318 = { + .probe = rt1318_probe, + .remove = rt1318_remove, + .suspend = rt1318_suspend, + .resume = rt1318_resume, + .controls = rt1318_snd_controls, + .num_controls = ARRAY_SIZE(rt1318_snd_controls), + .dapm_widgets = rt1318_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets), + .dapm_routes = rt1318_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1318_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1318_readable_register, + .volatile_reg = rt1318_volatile_register, + .max_register = 0x41001888, + .reg_defaults = rt1318_reg, + .num_reg_defaults = ARRAY_SIZE(rt1318_reg), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt1318_i2c_id[] = { + { "rt1318", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); + +static const struct of_device_id rt1318_of_match[] = { + { .compatible = "realtek,rt1318", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1318_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1318_acpi_match[] = { + { "10EC1318", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match); +#endif + +static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev) +{ + device_property_read_u32(dev, "realtek,r0_l", + &rt1318->pdata.init_r0_l); + device_property_read_u32(dev, "realtek,r0_r", + &rt1318->pdata.init_r0_r); + + return 0; +} + +static void rt1318_calibration_sequence(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_CLK1, 0x22); + regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40); + regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20); + regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00); + regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B); + regmap_write(rt1318->regmap, RT1318_TCON, 0x1C); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2); +} + +static void rt1318_r0_calculate(struct rt1318_priv *rt1318) +{ + unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3; + unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3; + unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor; + unsigned int format = 16777216; /* 2^24 */ + + regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0); + regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1); + regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2); + regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3); + r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3; + r0_l_integer = format / r0_l; + r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10; + + regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0); + regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1); + regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2); + regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3); + r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3; + r0_r_integer = format / r0_r; + r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10; + + dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor); + dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor); +} + +static void rt1318_r0_restore(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24, + (rt1318->pdata.init_r0_l >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16, + (rt1318->pdata.init_r0_l >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8, + (rt1318->pdata.init_r0_l >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0, + (rt1318->pdata.init_r0_l >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24, + (rt1318->pdata.init_r0_r >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16, + (rt1318->pdata.init_r0_r >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8, + (rt1318->pdata.init_r0_r >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0, + (rt1318->pdata.init_r0_r >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80); + regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc); + regmap_write(rt1318->regmap, RT1318_TCON, 0x9c); +} + +static int rt1318_calibrate(struct rt1318_priv *rt1318) +{ + int chk_cnt = 30, count = 0; + int val, val2; + + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1); + usleep_range(0, 10000); + rt1318_calibration_sequence(rt1318); + + while (count < chk_cnt) { + msleep(100); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + val = (val >> 1) & 0x1; + val2 = (val2 >> 1) & 0x1; + if (val & val2) { + dev_dbg(rt1318->component->dev, "Calibration done.\n"); + break; + } + count++; + if (count == chk_cnt) { + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + return RT1318_R0_CALIB_NOT_DONE; + } + } + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + if ((val & 0x1) & (val2 & 0x1)) + return RT1318_R0_IN_RANGE; + else + return RT1318_R0_OUT_OF_RANGE; +} + +static void rt1318_calibration_work(struct work_struct *work) +{ + struct rt1318_priv *rt1318 = + container_of(work, struct rt1318_priv, cali_work); + int ret; + + if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r) + rt1318_r0_restore(rt1318); + else { + ret = rt1318_calibrate(rt1318); + if (ret == RT1318_R0_IN_RANGE) + rt1318_r0_calculate(rt1318); + dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret); + } +} + +static int rt1318_i2c_probe(struct i2c_client *i2c) +{ + struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt1318_priv *rt1318; + int ret, val, val2, dev_id; + + rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv), + GFP_KERNEL); + if (!rt1318) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1318); + + if (pdata) + rt1318->pdata = *pdata; + else + rt1318_parse_dt(rt1318, &i2c->dev); + + rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap); + if (IS_ERR(rt1318->regmap)) { + ret = PTR_ERR(rt1318->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val); + regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2); + dev_id = (val << 8) | val2; + if (dev_id != 0x6821) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt1318\n", + dev_id); + return -ENODEV; + } + + ret = regmap_register_patch(rt1318->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + INIT_WORK(&rt1318->cali_work, rt1318_calibration_work); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai)); +} + +static struct i2c_driver rt1318_i2c_driver = { + .driver = { + .name = "rt1318", + .of_match_table = of_match_ptr(rt1318_of_match), + .acpi_match_table = ACPI_PTR(rt1318_acpi_match), + }, + .probe = rt1318_i2c_probe, + .id_table = rt1318_i2c_id, +}; +module_i2c_driver(rt1318_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1318 driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h new file mode 100644 index 000000000000..cec40b484216 --- /dev/null +++ b/sound/soc/codecs/rt1318.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ +#include + +#ifndef __RT1318_H__ +#define __RT1318_H__ + +struct rt1318_priv { + struct snd_soc_component *component; + struct rt1318_platform_data pdata; + struct work_struct cali_work; + struct regmap *regmap; + + unsigned int r0_l_integer; + unsigned int r0_l_factor; + unsigned int r0_r_integer; + unsigned int r0_r_factor; + int rt1318_init; + int rt1318_dvol; + int sysclk_src; + int sysclk; + int lrck; + int bclk; + int master; + int pll_src; + int pll_in; + int pll_out; +}; + +#define RT1318_PLL_INP_MAX 40000000 +#define RT1318_PLL_INP_MIN 256000 +#define RT1318_PLL_N_MAX 0x1ff +#define RT1318_PLL_K_MAX 0x1f +#define RT1318_PLL_M_MAX 0x1f + +#define RT1318_LRCLK_192000 192000 +#define RT1318_LRCLK_96000 96000 +#define RT1318_LRCLK_48000 48000 +#define RT1318_LRCLK_44100 44100 +#define RT1318_LRCLK_16000 16000 +#define RT1318_DVOL_STEP 383 + +#define RT1318_CLK1 0xc001 +#define RT1318_CLK2 0xc003 +#define RT1318_CLK3 0xc004 +#define RT1318_CLK4 0xc005 +#define RT1318_CLK5 0xc006 +#define RT1318_CLK6 0xc007 +#define RT1318_CLK7 0xc008 +#define RT1318_PWR_STA1 0xc121 +#define RT1318_SPK_VOL_TH 0xc130 +#define RT1318_TCON 0xc203 +#define RT1318_SRC_TCON 0xc204 +#define RT1318_TCON_RELATE 0xc206 +#define RT1318_DA_VOL_L_8 0xc20b +#define RT1318_DA_VOL_L_1_7 0xc20c +#define RT1318_DA_VOL_R_8 0xc20d +#define RT1318_DA_VOL_R_1_7 0xc20e +#define RT1318_FEEDBACK_PATH 0xc321 +#define RT1318_STP_TEMP_L 0xdb00 +#define RT1318_STP_SEL_L 0xdb08 +#define RT1318_STP_R0_EN_L 0xdb12 +#define RT1318_R0_CMP_L_FLAG 0xdb35 +#define RT1318_PRE_R0_L_24 0xdbb5 +#define RT1318_PRE_R0_L_23_16 0xdbb6 +#define RT1318_PRE_R0_L_15_8 0xdbb7 +#define RT1318_PRE_R0_L_7_0 0xdbb8 +#define RT1318_R0_L_24 0xdbc5 +#define RT1318_R0_L_23_16 0xdbc6 +#define RT1318_R0_L_15_8 0xdbc7 +#define RT1318_R0_L_7_0 0xdbc8 +#define RT1318_STP_SEL_R 0xdd08 +#define RT1318_STP_R0_EN_R 0xdd12 +#define RT1318_R0_CMP_R_FLAG 0xdd35 +#define RT1318_PRE_R0_R_24 0xddb5 +#define RT1318_PRE_R0_R_23_16 0xddb6 +#define RT1318_PRE_R0_R_15_8 0xddb7 +#define RT1318_PRE_R0_R_7_0 0xddb8 +#define RT1318_R0_R_24 0xddc5 +#define RT1318_R0_R_23_16 0xddc6 +#define RT1318_R0_R_15_8 0xddc7 +#define RT1318_R0_R_7_0 0xddc8 +#define RT1318_DEV_ID1 0xf012 +#define RT1318_DEV_ID2 0xf013 +#define RT1318_PLL1_K 0xf20d +#define RT1318_PLL1_M 0xf20f +#define RT1318_PLL1_N_8 0xf211 +#define RT1318_PLL1_N_7_0 0xf212 +#define RT1318_SINE_GEN0 0xf800 +#define RT1318_TDM_CTRL1 0xf900 +#define RT1318_TDM_CTRL2 0xf901 +#define RT1318_TDM_CTRL3 0xf902 +#define RT1318_TDM_CTRL9 0xf908 + + +/* Clock-1 (0xC001) */ +#define RT1318_PLLIN_MASK (0x7 << 4) +#define RT1318_PLLIN_BCLK0 (0x0 << 4) +#define RT1318_PLLIN_BCLK1 (0x1 << 4) +#define RT1318_PLLIN_RC (0x2 << 4) +#define RT1318_PLLIN_MCLK (0x3 << 4) +#define RT1318_PLLIN_SDW1 (0x4 << 4) +#define RT1318_PLLIN_SDW2 (0x5 << 4) +#define RT1318_PLLIN_SDW3 (0x6 << 4) +#define RT1318_PLLIN_SDW4 (0x7 << 4) +#define RT1318_SYSCLK_SEL_MASK (0x7 << 0) +#define RT1318_SYSCLK_BCLK (0x0 << 0) +#define RT1318_SYSCLK_SDW (0x1 << 0) +#define RT1318_SYSCLK_PLL2F (0x2 << 0) +#define RT1318_SYSCLK_PLL2B (0x3 << 0) +#define RT1318_SYSCLK_MCLK (0x4 << 0) +#define RT1318_SYSCLK_RC1 (0x5 << 0) +#define RT1318_SYSCLK_RC2 (0x6 << 0) +#define RT1318_SYSCLK_RC3 (0x7 << 0) +/* Clock-2 (0xC003) */ +#define RT1318_DIV_AP_MASK (0x3 << 4) +#define RT1318_DIV_AP_SFT 4 +#define RT1318_DIV_AP_DIV1 (0x0 << 4) +#define RT1318_DIV_AP_DIV2 (0x1 << 4) +#define RT1318_DIV_AP_DIV4 (0x2 << 4) +#define RT1318_DIV_AP_DIV8 (0x3 << 4) +#define RT1318_DIV_DAMOD_MASK (0x3 << 0) +#define RT1318_DIV_DAMOD_SFT 0 +#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0) +#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0) +#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0) +#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0) +/* Clock-3 (0xC004) */ +#define RT1318_AD_STO1_MASK (0x7 << 4) +#define RT1318_AD_STO1_SFT 4 +#define RT1318_AD_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_STO2_MASK (0x7 << 0) +#define RT1318_AD_STO2_SFT 0 +#define RT1318_AD_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_STO2_SFT 0 +/* Clock-4 (0xC005) */ +#define RT1318_AD_ANA_STO1_MASK (0x7 << 4) +#define RT1318_AD_ANA_STO1_SFT 4 +#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_ANA_STO2_MASK (0x7 << 0) +#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_ANA_STO2_SFT 0 +/* Clock-5 (0xC006) */ +#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4) +#define RT1318_DIV_FIFO_IN_SFT 4 +#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4) +#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4) +#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4) +#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4) +#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0) +#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0) +#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0) +#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_SFT 0 +/* Clock-6 (0xC007) */ +#define RT1318_DIV_NLMS_MASK (0x3 << 6) +#define RT1318_DIV_NLMS_SFT 6 +#define RT1318_DIV_NLMS_DIV1 (0x0 << 6) +#define RT1318_DIV_NLMS_DIV2 (0x1 << 6) +#define RT1318_DIV_NLMS_DIV4 (0x2 << 6) +#define RT1318_DIV_NLMS_DIV8 (0x3 << 6) +#define RT1318_DIV_AD_MONO_MASK (0x7 << 3) +#define RT1318_DIV_AD_MONO_SFT 3 +#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3) +#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3) +#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3) +#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3) +#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3) +#define RT1318_DIV_POST_G_MASK (0x7 << 0) +#define RT1318_DIV_POST_G_SFT 0 +#define RT1318_DIV_POST_G_DIV1 (0x0 << 0) +#define RT1318_DIV_POST_G_DIV2 (0x1 << 0) +#define RT1318_DIV_POST_G_DIV4 (0x2 << 0) +#define RT1318_DIV_POST_G_DIV8 (0x3 << 0) +#define RT1318_DIV_POST_G_DIV16 (0x4 << 0) +/* Power Status 1 (0xC121) */ +#define RT1318_PDB_CTRL_MASK (0x1) +#define RT1318_PDB_CTRL_LOW (0x0) +#define RT1318_PDB_CTRL_HIGH (0x1) +#define RT1318_PDB_CTRL_SFT 0 +/* SRC Tcon(0xc204) */ +#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6) +#define RT1318_SRCIN_IN_48K (0x0 << 6) +#define RT1318_SRCIN_IN_44P1 (0x1 << 6) +#define RT1318_SRCIN_IN_32K (0x2 << 6) +#define RT1318_SRCIN_IN_16K (0x3 << 6) +#define RT1318_SRCIN_F12288_MASK (0x3 << 4) +#define RT1318_SRCIN_TCON1 (0x0 << 4) +#define RT1318_SRCIN_TCON2 (0x1 << 4) +#define RT1318_SRCIN_TCON4 (0x2 << 4) +#define RT1318_SRCIN_TCON8 (0x3 << 4) +#define RT1318_SRCIN_DACLK_MASK (0x3 << 2) +#define RT1318_DACLK_TCON1 (0x0 << 2) +#define RT1318_DACLK_TCON2 (0x1 << 2) +#define RT1318_DACLK_TCON4 (0x2 << 2) +#define RT1318_DACLK_TCON8 (0x3 << 2) +/* R0 Compare Flag (0xDB35) */ +#define RT1318_R0_RANGE_MASK (0x1) +#define RT1318_R0_OUTOFRANGE (0x0) +#define RT1318_R0_INRANGE (0x1) +/* PLL internal setting (0xF20D), K value */ +#define RT1318_K_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF20F), M value */ +#define RT1318_M_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF211), N_8 value */ +#define RT1318_N_8_PLL1_MASK (0x1 << 0) +/* PLL internal setting (0xF212), N_7_0 value */ +#define RT1318_N_7_0_PLL1_MASK (0xff << 0) +/* TDM CTRL 1 (0xf900) */ +#define RT1318_TDM_BCLK_MASK (0x1 << 7) +#define RT1318_TDM_BCLK_NORM (0x0 << 7) +#define RT1318_TDM_BCLK_INV (0x1 << 7) +#define RT1318_I2S_FMT_MASK (0x7 << 0) +#define RT1318_FMT_I2S (0x0 << 0) +#define RT1318_FMT_LEFT_J (0x1 << 0) +#define RT1318_FMT_PCM_A_R (0x2 << 0) +#define RT1318_FMT_PCM_B_R (0x3 << 0) +#define RT1318_FMT_PCM_A_F (0x6 << 0) +#define RT1318_FMT_PCM_B_F (0x7 << 0) +#define RT1318_I2S_FMT_SFT 0 +/* TDM CTRL 2 (0xf901) */ +#define RT1318_I2S_CH_TX_MASK (0x3 << 6) +#define RT1318_I2S_CH_TX_2CH (0x0 << 6) +#define RT1318_I2S_CH_TX_4CH (0x1 << 6) +#define RT1318_I2S_CH_TX_6CH (0x2 << 6) +#define RT1318_I2S_CH_TX_8CH (0x3 << 6) +#define RT1318_I2S_CH_RX_MASK (0x3 << 4) +#define RT1318_I2S_CH_RX_2CH (0x0 << 4) +#define RT1318_I2S_CH_RX_4CH (0x1 << 4) +#define RT1318_I2S_CH_RX_6CH (0x2 << 4) +#define RT1318_I2S_CH_RX_8CH (0x3 << 4) +#define RT1318_I2S_DL_MASK 0x7 +#define RT1318_I2S_DL_SFT 0 +#define RT1318_I2S_DL_16 0x0 +#define RT1318_I2S_DL_20 0x1 +#define RT1318_I2S_DL_24 0x2 +#define RT1318_I2S_DL_32 0x3 +#define RT1318_I2S_DL_8 0x4 +/* TDM CTRL 3 (0xf902) */ +#define RT1318_I2S_TX_CHL_MASK (0x7 << 4) +#define RT1318_I2S_TX_CHL_SFT 4 +#define RT1318_I2S_TX_CHL_16 (0x0 << 4) +#define RT1318_I2S_TX_CHL_20 (0x1 << 4) +#define RT1318_I2S_TX_CHL_24 (0x2 << 4) +#define RT1318_I2S_TX_CHL_32 (0x3 << 4) +#define RT1318_I2S_TX_CHL_8 (0x4 << 4) +#define RT1318_I2S_RX_CHL_MASK (0x7 << 0) +#define RT1318_I2S_RX_CHL_SFT 0 +#define RT1318_I2S_RX_CHL_16 (0x0 << 0) +#define RT1318_I2S_RX_CHL_20 (0x1 << 0) +#define RT1318_I2S_RX_CHL_24 (0x2 << 0) +#define RT1318_I2S_RX_CHL_32 (0x3 << 0) +#define RT1318_I2S_RX_CHL_8 (0x4 << 0) +/* TDM CTRL 9 (0xf908) */ +#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4) +#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7 +#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4 +#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0 + +#define RT1318_REG_DISP_LEN 23 + +/* System Clock Source */ +enum { + RT1318_SCLK_S_BCLK, + RT1318_SCLK_S_SDW, + RT1318_SCLK_S_PLL2F, + RT1318_SCLK_S_PLL2B, + RT1318_SCLK_S_MCLK, + RT1318_SCLK_S_RC0, + RT1318_SCLK_S_RC1, + RT1318_SCLK_S_RC2, +}; + +/* PLL Source */ +enum { + RT1318_PLL_S_BCLK0, + RT1318_PLL_S_BCLK1, + RT1318_PLL_S_RC, + RT1318_PLL_S_MCLK, + RT1318_PLL_S_SDW_IN_PLL, + RT1318_PLL_S_SDW_0, + RT1318_PLL_S_SDW_1, + RT1318_PLL_S_SDW_2, +}; + +/* TDM channel */ +enum { + RT1318_2CH, + RT1318_4CH, + RT1318_6CH, + RT1318_8CH, +}; + +/* R0 calibration result */ +enum { + RT1318_R0_OUT_OF_RANGE, + RT1318_R0_IN_RANGE, + RT1318_R0_CALIB_NOT_DONE, +}; + +/* PLL pre-defined M/N/K */ + +struct pll_calc_map { + unsigned int pll_in; + unsigned int pll_out; + int k; + int n; + int m; + bool m_bp; + bool k_bp; +}; + +struct rt1318_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + bool k_bp; /* Indicates bypass k code or not. */ + int m_code; + int n_code; + int k_code; +}; + +#endif /* __RT1318_H__ */ -- cgit v1.2.3 From ae8fc2948b48f001514d4b73167fcef3b398a5fb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 20 Jun 2024 00:32:39 +0000 Subject: ASoC: dt-bindings: add missing vender prefix on filename Many Sound yaml files doesn't have vender prefix on filename. Add missing vender prefix for these files. Signed-off-by: Kuninori Morimoto Reviewed-by: Daniel Baluta Reviewed-by: Krzysztof Kozlowski Link: https://msgid.link/r/87wmmkpi6w.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ak4375.yaml | 60 ------ .../devicetree/bindings/sound/ak4613.yaml | 59 ------ .../devicetree/bindings/sound/ak4642.yaml | 59 ------ .../bindings/sound/asahi-kasei,ak4375.yaml | 60 ++++++ .../bindings/sound/asahi-kasei,ak4613.yaml | 59 ++++++ .../bindings/sound/asahi-kasei,ak4642.yaml | 59 ++++++ .../devicetree/bindings/sound/fsl,sgtl5000.yaml | 113 +++++++++++ .../devicetree/bindings/sound/mscc,zl38060.yaml | 72 +++++++ .../devicetree/bindings/sound/realtek,rt1019.yaml | 35 ++++ .../devicetree/bindings/sound/rt1019.yaml | 35 ---- .../devicetree/bindings/sound/sgtl5000.yaml | 113 ----------- .../devicetree/bindings/sound/tas2562.yaml | 81 -------- .../devicetree/bindings/sound/tas2770.yaml | 87 --------- .../devicetree/bindings/sound/tas27xx.yaml | 82 -------- .../devicetree/bindings/sound/tas5805m.yaml | 57 ------ .../devicetree/bindings/sound/ti,tas2562.yaml | 81 ++++++++ .../devicetree/bindings/sound/ti,tas2770.yaml | 87 +++++++++ .../devicetree/bindings/sound/ti,tas27xx.yaml | 82 ++++++++ .../devicetree/bindings/sound/ti,tas5805m.yaml | 57 ++++++ .../bindings/sound/ti,tlv320adcx140.yaml | 209 +++++++++++++++++++++ .../devicetree/bindings/sound/tlv320adcx140.yaml | 209 --------------------- .../devicetree/bindings/sound/wlf,wm8750.yaml | 42 +++++ .../devicetree/bindings/sound/wm8750.yaml | 42 ----- .../devicetree/bindings/sound/zl38060.yaml | 72 ------- 24 files changed, 956 insertions(+), 956 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/ak4375.yaml delete mode 100644 Documentation/devicetree/bindings/sound/ak4613.yaml delete mode 100644 Documentation/devicetree/bindings/sound/ak4642.yaml create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml create mode 100644 Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml create mode 100644 Documentation/devicetree/bindings/sound/mscc,zl38060.yaml create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt1019.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt1019.yaml delete mode 100644 Documentation/devicetree/bindings/sound/sgtl5000.yaml delete mode 100644 Documentation/devicetree/bindings/sound/tas2562.yaml delete mode 100644 Documentation/devicetree/bindings/sound/tas2770.yaml delete mode 100644 Documentation/devicetree/bindings/sound/tas27xx.yaml delete mode 100644 Documentation/devicetree/bindings/sound/tas5805m.yaml create mode 100644 Documentation/devicetree/bindings/sound/ti,tas2562.yaml create mode 100644 Documentation/devicetree/bindings/sound/ti,tas2770.yaml create mode 100644 Documentation/devicetree/bindings/sound/ti,tas27xx.yaml create mode 100644 Documentation/devicetree/bindings/sound/ti,tas5805m.yaml create mode 100644 Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml delete mode 100644 Documentation/devicetree/bindings/sound/tlv320adcx140.yaml create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8750.yaml delete mode 100644 Documentation/devicetree/bindings/sound/wm8750.yaml delete mode 100644 Documentation/devicetree/bindings/sound/zl38060.yaml diff --git a/Documentation/devicetree/bindings/sound/ak4375.yaml b/Documentation/devicetree/bindings/sound/ak4375.yaml deleted file mode 100644 index 587598e122c6..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4375.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/ak4375.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: AK4375 DAC and headphones amplifier - -maintainers: - - Vincent Knecht - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - const: asahi-kasei,ak4375 - - reg: - maxItems: 1 - - '#sound-dai-cells': - const: 0 - - avdd-supply: - description: regulator phandle for the AVDD power supply. - - tvdd-supply: - description: regulator phandle for the TVDD power supply. - - pdn-gpios: - description: optional GPIO to set the PDN pin. - -required: - - compatible - - reg - - '#sound-dai-cells' - - avdd-supply - - tvdd-supply - -unevaluatedProperties: false - -examples: - - | - #include - i2c { - #address-cells = <1>; - #size-cells = <0>; - - headphones: audio-codec@10 { - compatible = "asahi-kasei,ak4375"; - reg = <0x10>; - avdd-supply = <®_headphones_avdd>; - tvdd-supply = <&pm8916_l6>; - pdn-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&headphones_pdn_default>; - #sound-dai-cells = <0>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/ak4613.yaml b/Documentation/devicetree/bindings/sound/ak4613.yaml deleted file mode 100644 index 75e13414d6eb..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4613.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/ak4613.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: AK4613 I2C transmitter - -maintainers: - - Kuninori Morimoto - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - const: asahi-kasei,ak4613 - - reg: - maxItems: 1 - - clocks: - maxItems: 1 - - "#sound-dai-cells": - const: 0 - - ports: - $ref: audio-graph-port.yaml#/definitions/ports - - port: - $ref: audio-graph-port.yaml# - unevaluatedProperties: false - -patternProperties: - "^asahi-kasei,in[1-2]-single-end$": - description: Input Pin 1 - 2. - $ref: /schemas/types.yaml#/definitions/flag - - "^asahi-kasei,out[1-6]-single-end$": - description: Output Pin 1 - 6. - $ref: /schemas/types.yaml#/definitions/flag - -required: - - compatible - - reg - -unevaluatedProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - ak4613: codec@10 { - compatible = "asahi-kasei,ak4613"; - reg = <0x10>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/ak4642.yaml b/Documentation/devicetree/bindings/sound/ak4642.yaml deleted file mode 100644 index 437fe5d7cae1..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4642.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/ak4642.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: AK4642 I2C transmitter - -maintainers: - - Kuninori Morimoto - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - enum: - - asahi-kasei,ak4642 - - asahi-kasei,ak4643 - - asahi-kasei,ak4648 - - reg: - maxItems: 1 - - "#clock-cells": - const: 0 - "#sound-dai-cells": - const: 0 - - clocks: - maxItems: 1 - - clock-frequency: - description: common clock binding; frequency of MCKO - - clock-output-names: - description: common clock name - -required: - - compatible - - reg - -unevaluatedProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - ak4643: codec@12 { - compatible = "asahi-kasei,ak4643"; - #sound-dai-cells = <0>; - reg = <0x12>; - #clock-cells = <0>; - clocks = <&audio_clock>; - clock-frequency = <12288000>; - clock-output-names = "ak4643_mcko"; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml new file mode 100644 index 000000000000..bc07fcba535b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4375.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4375 DAC and headphones amplifier + +maintainers: + - Vincent Knecht + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: asahi-kasei,ak4375 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + avdd-supply: + description: regulator phandle for the AVDD power supply. + + tvdd-supply: + description: regulator phandle for the TVDD power supply. + + pdn-gpios: + description: optional GPIO to set the PDN pin. + +required: + - compatible + - reg + - '#sound-dai-cells' + - avdd-supply + - tvdd-supply + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + headphones: audio-codec@10 { + compatible = "asahi-kasei,ak4375"; + reg = <0x10>; + avdd-supply = <®_headphones_avdd>; + tvdd-supply = <&pm8916_l6>; + pdn-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&headphones_pdn_default>; + #sound-dai-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml new file mode 100644 index 000000000000..b49a6cff9f1f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4613.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4613 I2C transmitter + +maintainers: + - Kuninori Morimoto + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: asahi-kasei,ak4613 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + ports: + $ref: audio-graph-port.yaml#/definitions/ports + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +patternProperties: + "^asahi-kasei,in[1-2]-single-end$": + description: Input Pin 1 - 2. + $ref: /schemas/types.yaml#/definitions/flag + + "^asahi-kasei,out[1-6]-single-end$": + description: Output Pin 1 - 6. + $ref: /schemas/types.yaml#/definitions/flag + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + ak4613: codec@10 { + compatible = "asahi-kasei,ak4613"; + reg = <0x10>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml new file mode 100644 index 000000000000..fc03f0373a1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4642.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4642 I2C transmitter + +maintainers: + - Kuninori Morimoto + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - asahi-kasei,ak4642 + - asahi-kasei,ak4643 + - asahi-kasei,ak4648 + + reg: + maxItems: 1 + + "#clock-cells": + const: 0 + "#sound-dai-cells": + const: 0 + + clocks: + maxItems: 1 + + clock-frequency: + description: common clock binding; frequency of MCKO + + clock-output-names: + description: common clock name + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + ak4643: codec@12 { + compatible = "asahi-kasei,ak4643"; + #sound-dai-cells = <0>; + reg = <0x12>; + #clock-cells = <0>; + clocks = <&audio_clock>; + clock-frequency = <12288000>; + clock-output-names = "ak4643_mcko"; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml new file mode 100644 index 000000000000..c6ab1ca16763 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/fsl,sgtl5000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale SGTL5000 Stereo Codec + +maintainers: + - Fabio Estevam + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: fsl,sgtl5000 + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + assigned-clock-parents: true + assigned-clock-rates: true + assigned-clocks: true + + clocks: + items: + - description: the clock provider of SYS_MCLK + + VDDA-supply: + description: the regulator provider of VDDA + + VDDIO-supply: + description: the regulator provider of VDDIO + + VDDD-supply: + description: the regulator provider of VDDD + + micbias-resistor-k-ohms: + description: The bias resistor to be used in kOhms. The resistor can take + values of 2k, 4k or 8k. If set to 0 it will be off. If this node is not + mentioned or if the value is unknown, then micbias resistor is set to + 4k. + enum: [ 0, 2, 4, 8 ] + + micbias-voltage-m-volts: + description: The bias voltage to be used in mVolts. The voltage can take + values from 1.25V to 3V by 250mV steps. If this node is not mentioned + or the value is unknown, then the value is set to 1.25V. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 ] + + lrclk-strength: + description: | + The LRCLK pad strength. Possible values are: 0, 1, 2 and 3 as per the + table below: + + VDDIO 1.8V 2.5V 3.3V + 0 = Disable + 1 = 1.66 mA 2.87 mA 4.02 mA + 2 = 3.33 mA 5.74 mA 8.03 mA + 3 = 4.99 mA 8.61 mA 12.05 mA + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + + sclk-strength: + description: | + The SCLK pad strength. Possible values are: 0, 1, 2 and 3 as per the + table below: + + VDDIO 1.8V 2.5V 3.3V + 0 = Disable + 1 = 1.66 mA 2.87 mA 4.02 mA + 2 = 3.33 mA 5.74 mA 8.03 mA + 3 = 4.99 mA 8.61 mA 12.05 mA + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - "#sound-dai-cells" + - clocks + - VDDA-supply + - VDDIO-supply + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + #sound-dai-cells = <0>; + clocks = <&clks 150>; + micbias-resistor-k-ohms = <2>; + micbias-voltage-m-volts = <2250>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml new file mode 100644 index 000000000000..994313fd12b2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mscc,zl38060.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ZL38060 Connected Home Audio Processor from Microsemi. + +description: | + The ZL38060 is a "Connected Home Audio Processor" from Microsemi, + which consists of a Digital Signal Processor (DSP), several Digital + Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs. + +maintainers: + - Jaroslav Kysela + - Takashi Iwai + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: mscc,zl38060 + + reg: + description: + SPI device address. + maxItems: 1 + + spi-max-frequency: + maximum: 24000000 + + reset-gpios: + description: + A GPIO line handling reset of the chip. As the line is active low, + it should be marked GPIO_ACTIVE_LOW (see ../gpio/gpio.txt) + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - reg + - '#gpio-cells' + - gpio-controller + - '#sound-dai-cells' + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + codec: zl38060@0 { + gpio-controller; + #gpio-cells = <2>; + #sound-dai-cells = <0>; + compatible = "mscc,zl38060"; + reg = <0>; + spi-max-frequency = <12000000>; + reset-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml new file mode 100644 index 000000000000..adf5e38f4dbc --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt1019.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT1019 Mono Class-D Audio Amplifier + +maintainers: + - jack.yu@realtek.com + +properties: + compatible: + const: realtek,rt1019 + + reg: + maxItems: 1 + description: I2C address of the device. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + rt1019: codec@28 { + compatible = "realtek,rt1019"; + reg = <0x28>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt1019.yaml b/Documentation/devicetree/bindings/sound/rt1019.yaml deleted file mode 100644 index 3d5a91a942f4..000000000000 --- a/Documentation/devicetree/bindings/sound/rt1019.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/rt1019.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: RT1019 Mono Class-D Audio Amplifier - -maintainers: - - jack.yu@realtek.com - -properties: - compatible: - const: realtek,rt1019 - - reg: - maxItems: 1 - description: I2C address of the device. - -required: - - compatible - - reg - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - rt1019: codec@28 { - compatible = "realtek,rt1019"; - reg = <0x28>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.yaml b/Documentation/devicetree/bindings/sound/sgtl5000.yaml deleted file mode 100644 index 1353c051488f..000000000000 --- a/Documentation/devicetree/bindings/sound/sgtl5000.yaml +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/sgtl5000.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Freescale SGTL5000 Stereo Codec - -maintainers: - - Fabio Estevam - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - const: fsl,sgtl5000 - - reg: - maxItems: 1 - - "#sound-dai-cells": - const: 0 - - assigned-clock-parents: true - assigned-clock-rates: true - assigned-clocks: true - - clocks: - items: - - description: the clock provider of SYS_MCLK - - VDDA-supply: - description: the regulator provider of VDDA - - VDDIO-supply: - description: the regulator provider of VDDIO - - VDDD-supply: - description: the regulator provider of VDDD - - micbias-resistor-k-ohms: - description: The bias resistor to be used in kOhms. The resistor can take - values of 2k, 4k or 8k. If set to 0 it will be off. If this node is not - mentioned or if the value is unknown, then micbias resistor is set to - 4k. - enum: [ 0, 2, 4, 8 ] - - micbias-voltage-m-volts: - description: The bias voltage to be used in mVolts. The voltage can take - values from 1.25V to 3V by 250mV steps. If this node is not mentioned - or the value is unknown, then the value is set to 1.25V. - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [ 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 ] - - lrclk-strength: - description: | - The LRCLK pad strength. Possible values are: 0, 1, 2 and 3 as per the - table below: - - VDDIO 1.8V 2.5V 3.3V - 0 = Disable - 1 = 1.66 mA 2.87 mA 4.02 mA - 2 = 3.33 mA 5.74 mA 8.03 mA - 3 = 4.99 mA 8.61 mA 12.05 mA - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [ 0, 1, 2, 3 ] - - sclk-strength: - description: | - The SCLK pad strength. Possible values are: 0, 1, 2 and 3 as per the - table below: - - VDDIO 1.8V 2.5V 3.3V - 0 = Disable - 1 = 1.66 mA 2.87 mA 4.02 mA - 2 = 3.33 mA 5.74 mA 8.03 mA - 3 = 4.99 mA 8.61 mA 12.05 mA - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [ 0, 1, 2, 3 ] - - port: - $ref: audio-graph-port.yaml# - unevaluatedProperties: false - -required: - - compatible - - reg - - "#sound-dai-cells" - - clocks - - VDDA-supply - - VDDIO-supply - -unevaluatedProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - codec@a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - #sound-dai-cells = <0>; - clocks = <&clks 150>; - micbias-resistor-k-ohms = <2>; - micbias-voltage-m-volts = <2250>; - VDDA-supply = <®_3p3v>; - VDDIO-supply = <®_3p3v>; - }; - }; -... diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/tas2562.yaml deleted file mode 100644 index d28c102c0ce7..000000000000 --- a/Documentation/devicetree/bindings/sound/tas2562.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2019 Texas Instruments Incorporated -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/tas2562.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Texas Instruments TAS2562 Smart PA - -maintainers: - - Andrew Davis - -description: | - The TAS2562 is a mono, digital input Class-D audio amplifier optimized for - efficiently driving high peak power into small loudspeakers. - Integrated speaker voltage and current sense provides for - real time monitoring of loudspeaker behavior. - - Specifications about the audio amplifier can be found at: - https://www.ti.com/lit/gpn/tas2562 - https://www.ti.com/lit/gpn/tas2564 - https://www.ti.com/lit/gpn/tas2110 - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - enum: - - ti,tas2562 - - ti,tas2564 - - ti,tas2110 - - reg: - maxItems: 1 - description: | - I2C address of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f - - shut-down-gpios: - maxItems: 1 - description: GPIO used to control the state of the device. - deprecated: true - - shutdown-gpios: - maxItems: 1 - description: GPIO used to control the state of the device. - - interrupts: - maxItems: 1 - - ti,imon-slot-no: - $ref: /schemas/types.yaml#/definitions/uint32 - description: TDM TX current sense time slot. - - '#sound-dai-cells': - # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward - # compatibility but is deprecated. - enum: [0, 1] - -required: - - compatible - - reg - -unevaluatedProperties: false - -examples: - - | - #include - i2c { - #address-cells = <1>; - #size-cells = <0>; - codec: codec@4c { - compatible = "ti,tas2562"; - reg = <0x4c>; - #sound-dai-cells = <0>; - interrupt-parent = <&gpio1>; - interrupts = <14>; - shutdown-gpios = <&gpio1 15 0>; - ti,imon-slot-no = <0>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/tas2770.yaml b/Documentation/devicetree/bindings/sound/tas2770.yaml deleted file mode 100644 index be2536e8c440..000000000000 --- a/Documentation/devicetree/bindings/sound/tas2770.yaml +++ /dev/null @@ -1,87 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2019-20 Texas Instruments Incorporated -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/tas2770.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Texas Instruments TAS2770 Smart PA - -maintainers: - - Shi Fu - -description: | - The TAS2770 is a mono, digital input Class-D audio amplifier optimized for - efficiently driving high peak power into small loudspeakers. - Integrated speaker voltage and current sense provides for - real time monitoring of loudspeaker behavior. - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - enum: - - ti,tas2770 - - reg: - maxItems: 1 - description: | - I2C address of the device can be between 0x41 to 0x48. - - reset-gpio: - maxItems: 1 - description: GPIO used to reset the device. - - shutdown-gpios: - maxItems: 1 - description: GPIO used to control the state of the device. - - interrupts: - maxItems: 1 - - ti,imon-slot-no: - $ref: /schemas/types.yaml#/definitions/uint32 - description: TDM TX current sense time slot. - - ti,vmon-slot-no: - $ref: /schemas/types.yaml#/definitions/uint32 - description: TDM TX voltage sense time slot. - - ti,asi-format: - deprecated: true - $ref: /schemas/types.yaml#/definitions/uint32 - description: Sets TDM RX capture edge. - enum: - - 0 # Rising edge - - 1 # Falling edge - - '#sound-dai-cells': - # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward - # compatibility but is deprecated. - enum: [0, 1] - -required: - - compatible - - reg - -unevaluatedProperties: false - -examples: - - | - #include - i2c { - #address-cells = <1>; - #size-cells = <0>; - codec: codec@41 { - compatible = "ti,tas2770"; - reg = <0x41>; - #sound-dai-cells = <0>; - interrupt-parent = <&gpio1>; - interrupts = <14>; - reset-gpio = <&gpio1 15 0>; - shutdown-gpios = <&gpio1 14 0>; - ti,imon-slot-no = <0>; - ti,vmon-slot-no = <2>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/tas27xx.yaml b/Documentation/devicetree/bindings/sound/tas27xx.yaml deleted file mode 100644 index f2d878f6f495..000000000000 --- a/Documentation/devicetree/bindings/sound/tas27xx.yaml +++ /dev/null @@ -1,82 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020-2022 Texas Instruments Incorporated -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/tas27xx.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Texas Instruments TAS2764/TAS2780 Smart PA - -maintainers: - - Shenghao Ding - -description: | - The TAS2764/TAS2780 is a mono, digital input Class-D audio amplifier - optimized for efficiently driving high peak power into small - loudspeakers. Integrated speaker voltage and current sense provides - for real time monitoring of loudspeaker behavior. - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - enum: - - ti,tas2764 - - ti,tas2780 - - reg: - maxItems: 1 - description: | - I2C address of the device can be between 0x38 to 0x45. - - reset-gpios: - maxItems: 1 - description: GPIO used to reset the device. - - shutdown-gpios: - maxItems: 1 - description: GPIO used to control the state of the device. - - interrupts: - maxItems: 1 - - ti,imon-slot-no: - $ref: /schemas/types.yaml#/definitions/uint32 - description: TDM TX current sense time slot. - - ti,vmon-slot-no: - $ref: /schemas/types.yaml#/definitions/uint32 - description: TDM TX voltage sense time slot. - - '#sound-dai-cells': - # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward - # compatibility but is deprecated. - enum: [0, 1] - -required: - - compatible - - reg - -unevaluatedProperties: false - -examples: - - | - #include - i2c { - #address-cells = <1>; - #size-cells = <0>; - codec: codec@38 { - compatible = "ti,tas2764"; - reg = <0x38>; - #sound-dai-cells = <0>; - interrupt-parent = <&gpio1>; - interrupts = <14>; - reset-gpios = <&gpio1 15 0>; - shutdown-gpios = <&gpio1 15 0>; - ti,imon-slot-no = <0>; - ti,vmon-slot-no = <2>; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/sound/tas5805m.yaml b/Documentation/devicetree/bindings/sound/tas5805m.yaml deleted file mode 100644 index 12c41974274e..000000000000 --- a/Documentation/devicetree/bindings/sound/tas5805m.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/tas5805m.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: TAS5805M audio amplifier - -maintainers: - - Daniel Beer - -description: | - The TAS5805M is a class D audio amplifier with a built-in DSP. - -properties: - compatible: - enum: - - ti,tas5805m - - reg: - maxItems: 1 - description: | - I2C address of the amplifier. See the datasheet for possible values. - - pvdd-supply: - description: | - Regulator for audio power supply (PVDD in the datasheet). - - pdn-gpios: - description: | - Power-down control GPIO (PDN pin in the datasheet). - - ti,dsp-config-name: - description: | - The name of the DSP configuration that should be loaded for this - instance. Configuration blobs are sequences of register writes - generated from TI's PPC3 tool. - $ref: /schemas/types.yaml#/definitions/string - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - tas5805m: tas5805m@2c { - reg = <0x2c>; - compatible = "ti,tas5805m"; - - pvdd-supply = <&audiopwr>; - pdn-gpios = <&tlmm 160 0>; - - ti,dsp-config-name = "mono_pbtl_48khz"; - }; - }; -... diff --git a/Documentation/devicetree/bindings/sound/ti,tas2562.yaml b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml new file mode 100644 index 000000000000..8bc3b0c7531e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas2562.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS2562 Smart PA + +maintainers: + - Andrew Davis + +description: | + The TAS2562 is a mono, digital input Class-D audio amplifier optimized for + efficiently driving high peak power into small loudspeakers. + Integrated speaker voltage and current sense provides for + real time monitoring of loudspeaker behavior. + + Specifications about the audio amplifier can be found at: + https://www.ti.com/lit/gpn/tas2562 + https://www.ti.com/lit/gpn/tas2564 + https://www.ti.com/lit/gpn/tas2110 + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - ti,tas2562 + - ti,tas2564 + - ti,tas2110 + + reg: + maxItems: 1 + description: | + I2C address of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f + + shut-down-gpios: + maxItems: 1 + description: GPIO used to control the state of the device. + deprecated: true + + shutdown-gpios: + maxItems: 1 + description: GPIO used to control the state of the device. + + interrupts: + maxItems: 1 + + ti,imon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TDM TX current sense time slot. + + '#sound-dai-cells': + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec: codec@4c { + compatible = "ti,tas2562"; + reg = <0x4c>; + #sound-dai-cells = <0>; + interrupt-parent = <&gpio1>; + interrupts = <14>; + shutdown-gpios = <&gpio1 15 0>; + ti,imon-slot-no = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ti,tas2770.yaml b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml new file mode 100644 index 000000000000..362c2e6154f0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019-20 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas2770.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS2770 Smart PA + +maintainers: + - Shi Fu + +description: | + The TAS2770 is a mono, digital input Class-D audio amplifier optimized for + efficiently driving high peak power into small loudspeakers. + Integrated speaker voltage and current sense provides for + real time monitoring of loudspeaker behavior. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - ti,tas2770 + + reg: + maxItems: 1 + description: | + I2C address of the device can be between 0x41 to 0x48. + + reset-gpio: + maxItems: 1 + description: GPIO used to reset the device. + + shutdown-gpios: + maxItems: 1 + description: GPIO used to control the state of the device. + + interrupts: + maxItems: 1 + + ti,imon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TDM TX current sense time slot. + + ti,vmon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TDM TX voltage sense time slot. + + ti,asi-format: + deprecated: true + $ref: /schemas/types.yaml#/definitions/uint32 + description: Sets TDM RX capture edge. + enum: + - 0 # Rising edge + - 1 # Falling edge + + '#sound-dai-cells': + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec: codec@41 { + compatible = "ti,tas2770"; + reg = <0x41>; + #sound-dai-cells = <0>; + interrupt-parent = <&gpio1>; + interrupts = <14>; + reset-gpio = <&gpio1 15 0>; + shutdown-gpios = <&gpio1 14 0>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml new file mode 100644 index 000000000000..530bc3937847 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020-2022 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas27xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS2764/TAS2780 Smart PA + +maintainers: + - Shenghao Ding + +description: | + The TAS2764/TAS2780 is a mono, digital input Class-D audio amplifier + optimized for efficiently driving high peak power into small + loudspeakers. Integrated speaker voltage and current sense provides + for real time monitoring of loudspeaker behavior. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - ti,tas2764 + - ti,tas2780 + + reg: + maxItems: 1 + description: | + I2C address of the device can be between 0x38 to 0x45. + + reset-gpios: + maxItems: 1 + description: GPIO used to reset the device. + + shutdown-gpios: + maxItems: 1 + description: GPIO used to control the state of the device. + + interrupts: + maxItems: 1 + + ti,imon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TDM TX current sense time slot. + + ti,vmon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + description: TDM TX voltage sense time slot. + + '#sound-dai-cells': + # The codec has a single DAI, the #sound-dai-cells=<1>; case is left in for backward + # compatibility but is deprecated. + enum: [0, 1] + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec: codec@38 { + compatible = "ti,tas2764"; + reg = <0x38>; + #sound-dai-cells = <0>; + interrupt-parent = <&gpio1>; + interrupts = <14>; + reset-gpios = <&gpio1 15 0>; + shutdown-gpios = <&gpio1 15 0>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml new file mode 100644 index 000000000000..c2c2835a9e1d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas5805m.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TAS5805M audio amplifier + +maintainers: + - Daniel Beer + +description: | + The TAS5805M is a class D audio amplifier with a built-in DSP. + +properties: + compatible: + enum: + - ti,tas5805m + + reg: + maxItems: 1 + description: | + I2C address of the amplifier. See the datasheet for possible values. + + pvdd-supply: + description: | + Regulator for audio power supply (PVDD in the datasheet). + + pdn-gpios: + description: | + Power-down control GPIO (PDN pin in the datasheet). + + ti,dsp-config-name: + description: | + The name of the DSP configuration that should be loaded for this + instance. Configuration blobs are sequences of register writes + generated from TI's PPC3 tool. + $ref: /schemas/types.yaml#/definitions/string + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + tas5805m: tas5805m@2c { + reg = <0x2c>; + compatible = "ti,tas5805m"; + + pvdd-supply = <&audiopwr>; + pdn-gpios = <&tlmm 160 0>; + + ti,dsp-config-name = "mono_pbtl_48khz"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml new file mode 100644 index 000000000000..876fa97bfbcd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml @@ -0,0 +1,209 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tlv320adcx140.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter + +maintainers: + - Andrew Davis + +description: | + The TLV320ADCX140 are multichannel (4-ch analog recording or 8-ch digital + PDM microphones recording), high-performance audio, analog-to-digital + converter (ADC) with analog inputs supporting up to 2V RMS. The TLV320ADCX140 + family supports line and microphone Inputs, and offers a programmable + microphone bias or supply voltage generation. + + Specifications can be found at: + https://www.ti.com/lit/ds/symlink/tlv320adc3140.pdf + https://www.ti.com/lit/ds/symlink/tlv320adc5140.pdf + https://www.ti.com/lit/ds/symlink/tlv320adc6140.pdf + +properties: + compatible: + enum: + - ti,tlv320adc3140 + - ti,tlv320adc5140 + - ti,tlv320adc6140 + + reg: + maxItems: 1 + description: | + I2C address of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f + + reset-gpios: + maxItems: 1 + description: | + GPIO used for hardware reset. + + areg-supply: + description: | + Regulator with AVDD at 3.3V. If not defined then the internal regulator + is enabled. + + ti,mic-bias-source: + description: | + Indicates the source for MIC Bias. + 0 - Mic bias is set to VREF + 1 - Mic bias is set to VREF × 1.096 + 6 - Mic bias is set to AVDD + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 6] + + ti,vref-source: + description: | + Indicates the source for MIC Bias. + 0 - Set VREF to 2.75V + 1 - Set VREF to 2.5V + 2 - Set VREF to 1.375V + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + ti,pdm-edge-select: + description: | + Defines the PDMCLK sampling edge configuration for the PDM inputs. This + array is defined as . + + 0 - (default) Odd channel is latched on the negative edge and even + channel is latched on the positive edge. + 1 - Odd channel is latched on the positive edge and even channel is + latched on the negative edge. + + PDMIN1 - PDMCLK latching edge used for channel 1 and 2 data + PDMIN2 - PDMCLK latching edge used for channel 3 and 4 data + PDMIN3 - PDMCLK latching edge used for channel 5 and 6 data + PDMIN4 - PDMCLK latching edge used for channel 7 and 8 data + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + items: + maximum: 1 + default: [0, 0, 0, 0] + + ti,gpi-config: + description: | + Defines the configuration for the general purpose input pins (GPI). + The array is defined as . + + 0 - (default) disabled + 1 - GPIX is configured as a general-purpose input (GPI) + 2 - GPIX is configured as a master clock input (MCLK) + 3 - GPIX is configured as an ASI input for daisy-chain (SDIN) + 4 - GPIX is configured as a PDM data input for channel 1 and channel + (PDMDIN1) + 5 - GPIX is configured as a PDM data input for channel 3 and channel + (PDMDIN2) + 6 - GPIX is configured as a PDM data input for channel 5 and channel + (PDMDIN3) + 7 - GPIX is configured as a PDM data input for channel 7 and channel + (PDMDIN4) + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + items: + maximum: 7 + default: [0, 0, 0, 0] + + ti,gpio-config: + description: | + Defines the configuration and output drive for the General Purpose + Input and Output pin (GPIO1). Its value is a pair, the first value is for + the configuration type and the second value is for the output drive + type. The array is defined as + + configuration for the GPIO pin can be one of the following: + 0 - disabled + 1 - GPIO1 is configured as a general-purpose output (GPO) + 2 - (default) GPIO1 is configured as a device interrupt output (IRQ) + 3 - GPIO1 is configured as a secondary ASI output (SDOUT2) + 4 - GPIO1 is configured as a PDM clock output (PDMCLK) + 8 - GPIO1 is configured as an input to control when MICBIAS turns on or + off (MICBIAS_EN) + 9 - GPIO1 is configured as a general-purpose input (GPI) + 10 - GPIO1 is configured as a master clock input (MCLK) + 11 - GPIO1 is configured as an ASI input for daisy-chain (SDIN) + 12 - GPIO1 is configured as a PDM data input for channel 1 and channel 2 + (PDMDIN1) + 13 - GPIO1 is configured as a PDM data input for channel 3 and channel 4 + (PDMDIN2) + 14 - GPIO1 is configured as a PDM data input for channel 5 and channel 6 + (PDMDIN3) + 15 - GPIO1 is configured as a PDM data input for channel 7 and channel 8 + (PDMDIN4) + + output drive type for the GPIO pin can be one of the following: + 0 - Hi-Z output + 1 - Drive active low and active high + 2 - (default) Drive active low and weak high + 3 - Drive active low and Hi-Z + 4 - Drive weak low and active high + 5 - Drive Hi-Z and active high + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + items: + maximum: 15 + default: [2, 2] + + ti,asi-tx-drive: + type: boolean + description: | + When set the device will set the Tx ASI output to a Hi-Z state for unused + data cycles. Default is to drive the output low on unused ASI cycles. + +patternProperties: + '^ti,gpo-config-[1-4]$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Defines the configuration and output driver for the general purpose + output pins (GPO). These values are pairs, the first value is for the + configuration type and the second value is for the output drive type. + The array is defined as + + GPO output configuration can be one of the following: + + 0 - (default) disabled + 1 - GPOX is configured as a general-purpose output (GPO) + 2 - GPOX is configured as a device interrupt output (IRQ) + 3 - GPOX is configured as a secondary ASI output (SDOUT2) + 4 - GPOX is configured as a PDM clock output (PDMCLK) + + GPO output drive configuration for the GPO pins can be one of the following: + + 0d - (default) Hi-Z output + 1d - Drive active low and active high + 2d - Drive active low and weak high + 3d - Drive active low and Hi-Z + 4d - Drive weak low and active high + 5d - Drive Hi-Z and active high + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec: codec@4c { + compatible = "ti,tlv320adc5140"; + reg = <0x4c>; + ti,mic-bias-source = <6>; + ti,pdm-edge-select = <0 1 0 1>; + ti,gpi-config = <4 5 6 7>; + ti,gpio-config = <10 2>; + ti,gpo-config-1 = <0 0>; + ti,gpo-config-2 = <0 0>; + reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml deleted file mode 100644 index f3274bcc4c05..000000000000 --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +++ /dev/null @@ -1,209 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause) -# Copyright (C) 2019 Texas Instruments Incorporated -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/tlv320adcx140.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter - -maintainers: - - Andrew Davis - -description: | - The TLV320ADCX140 are multichannel (4-ch analog recording or 8-ch digital - PDM microphones recording), high-performance audio, analog-to-digital - converter (ADC) with analog inputs supporting up to 2V RMS. The TLV320ADCX140 - family supports line and microphone Inputs, and offers a programmable - microphone bias or supply voltage generation. - - Specifications can be found at: - https://www.ti.com/lit/ds/symlink/tlv320adc3140.pdf - https://www.ti.com/lit/ds/symlink/tlv320adc5140.pdf - https://www.ti.com/lit/ds/symlink/tlv320adc6140.pdf - -properties: - compatible: - enum: - - ti,tlv320adc3140 - - ti,tlv320adc5140 - - ti,tlv320adc6140 - - reg: - maxItems: 1 - description: | - I2C address of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f - - reset-gpios: - maxItems: 1 - description: | - GPIO used for hardware reset. - - areg-supply: - description: | - Regulator with AVDD at 3.3V. If not defined then the internal regulator - is enabled. - - ti,mic-bias-source: - description: | - Indicates the source for MIC Bias. - 0 - Mic bias is set to VREF - 1 - Mic bias is set to VREF × 1.096 - 6 - Mic bias is set to AVDD - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 6] - - ti,vref-source: - description: | - Indicates the source for MIC Bias. - 0 - Set VREF to 2.75V - 1 - Set VREF to 2.5V - 2 - Set VREF to 1.375V - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2] - - ti,pdm-edge-select: - description: | - Defines the PDMCLK sampling edge configuration for the PDM inputs. This - array is defined as . - - 0 - (default) Odd channel is latched on the negative edge and even - channel is latched on the positive edge. - 1 - Odd channel is latched on the positive edge and even channel is - latched on the negative edge. - - PDMIN1 - PDMCLK latching edge used for channel 1 and 2 data - PDMIN2 - PDMCLK latching edge used for channel 3 and 4 data - PDMIN3 - PDMCLK latching edge used for channel 5 and 6 data - PDMIN4 - PDMCLK latching edge used for channel 7 and 8 data - - $ref: /schemas/types.yaml#/definitions/uint32-array - minItems: 1 - maxItems: 4 - items: - maximum: 1 - default: [0, 0, 0, 0] - - ti,gpi-config: - description: | - Defines the configuration for the general purpose input pins (GPI). - The array is defined as . - - 0 - (default) disabled - 1 - GPIX is configured as a general-purpose input (GPI) - 2 - GPIX is configured as a master clock input (MCLK) - 3 - GPIX is configured as an ASI input for daisy-chain (SDIN) - 4 - GPIX is configured as a PDM data input for channel 1 and channel - (PDMDIN1) - 5 - GPIX is configured as a PDM data input for channel 3 and channel - (PDMDIN2) - 6 - GPIX is configured as a PDM data input for channel 5 and channel - (PDMDIN3) - 7 - GPIX is configured as a PDM data input for channel 7 and channel - (PDMDIN4) - - $ref: /schemas/types.yaml#/definitions/uint32-array - minItems: 1 - maxItems: 4 - items: - maximum: 7 - default: [0, 0, 0, 0] - - ti,gpio-config: - description: | - Defines the configuration and output drive for the General Purpose - Input and Output pin (GPIO1). Its value is a pair, the first value is for - the configuration type and the second value is for the output drive - type. The array is defined as - - configuration for the GPIO pin can be one of the following: - 0 - disabled - 1 - GPIO1 is configured as a general-purpose output (GPO) - 2 - (default) GPIO1 is configured as a device interrupt output (IRQ) - 3 - GPIO1 is configured as a secondary ASI output (SDOUT2) - 4 - GPIO1 is configured as a PDM clock output (PDMCLK) - 8 - GPIO1 is configured as an input to control when MICBIAS turns on or - off (MICBIAS_EN) - 9 - GPIO1 is configured as a general-purpose input (GPI) - 10 - GPIO1 is configured as a master clock input (MCLK) - 11 - GPIO1 is configured as an ASI input for daisy-chain (SDIN) - 12 - GPIO1 is configured as a PDM data input for channel 1 and channel 2 - (PDMDIN1) - 13 - GPIO1 is configured as a PDM data input for channel 3 and channel 4 - (PDMDIN2) - 14 - GPIO1 is configured as a PDM data input for channel 5 and channel 6 - (PDMDIN3) - 15 - GPIO1 is configured as a PDM data input for channel 7 and channel 8 - (PDMDIN4) - - output drive type for the GPIO pin can be one of the following: - 0 - Hi-Z output - 1 - Drive active low and active high - 2 - (default) Drive active low and weak high - 3 - Drive active low and Hi-Z - 4 - Drive weak low and active high - 5 - Drive Hi-Z and active high - - $ref: /schemas/types.yaml#/definitions/uint32-array - minItems: 2 - maxItems: 2 - items: - maximum: 15 - default: [2, 2] - - ti,asi-tx-drive: - type: boolean - description: | - When set the device will set the Tx ASI output to a Hi-Z state for unused - data cycles. Default is to drive the output low on unused ASI cycles. - -patternProperties: - '^ti,gpo-config-[1-4]$': - $ref: /schemas/types.yaml#/definitions/uint32-array - description: | - Defines the configuration and output driver for the general purpose - output pins (GPO). These values are pairs, the first value is for the - configuration type and the second value is for the output drive type. - The array is defined as - - GPO output configuration can be one of the following: - - 0 - (default) disabled - 1 - GPOX is configured as a general-purpose output (GPO) - 2 - GPOX is configured as a device interrupt output (IRQ) - 3 - GPOX is configured as a secondary ASI output (SDOUT2) - 4 - GPOX is configured as a PDM clock output (PDMCLK) - - GPO output drive configuration for the GPO pins can be one of the following: - - 0d - (default) Hi-Z output - 1d - Drive active low and active high - 2d - Drive active low and weak high - 3d - Drive active low and Hi-Z - 4d - Drive weak low and active high - 5d - Drive Hi-Z and active high - -required: - - compatible - - reg - -additionalProperties: false - -examples: - - | - #include - i2c { - #address-cells = <1>; - #size-cells = <0>; - codec: codec@4c { - compatible = "ti,tlv320adc5140"; - reg = <0x4c>; - ti,mic-bias-source = <6>; - ti,pdm-edge-select = <0 1 0 1>; - ti,gpi-config = <4 5 6 7>; - ti,gpio-config = <10 2>; - ti,gpo-config-1 = <0 0>; - ti,gpo-config-2 = <0 0>; - reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml new file mode 100644 index 000000000000..96859e38315b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8750.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WM8750 and WM8987 audio CODECs + +description: | + These devices support both I2C and SPI (configured with pin strapping + on the board). + +maintainers: + - Mark Brown + +properties: + compatible: + enum: + - wlf,wm8750 + - wlf,wm8987 + + reg: + description: + The I2C address of the device for I2C, the chip select number for SPI + maxItems: 1 + +additionalProperties: false + +required: + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8750.yaml b/Documentation/devicetree/bindings/sound/wm8750.yaml deleted file mode 100644 index 24246ac7bbdf..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8750.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/wm8750.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: WM8750 and WM8987 audio CODECs - -description: | - These devices support both I2C and SPI (configured with pin strapping - on the board). - -maintainers: - - Mark Brown - -properties: - compatible: - enum: - - wlf,wm8750 - - wlf,wm8987 - - reg: - description: - The I2C address of the device for I2C, the chip select number for SPI - maxItems: 1 - -additionalProperties: false - -required: - - reg - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - codec@1a { - compatible = "wlf,wm8750"; - reg = <0x1a>; - }; - }; diff --git a/Documentation/devicetree/bindings/sound/zl38060.yaml b/Documentation/devicetree/bindings/sound/zl38060.yaml deleted file mode 100644 index 8bd201e573aa..000000000000 --- a/Documentation/devicetree/bindings/sound/zl38060.yaml +++ /dev/null @@ -1,72 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/zl38060.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: ZL38060 Connected Home Audio Processor from Microsemi. - -description: | - The ZL38060 is a "Connected Home Audio Processor" from Microsemi, - which consists of a Digital Signal Processor (DSP), several Digital - Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs. - -maintainers: - - Jaroslav Kysela - - Takashi Iwai - -allOf: - - $ref: dai-common.yaml# - -properties: - compatible: - const: mscc,zl38060 - - reg: - description: - SPI device address. - maxItems: 1 - - spi-max-frequency: - maximum: 24000000 - - reset-gpios: - description: - A GPIO line handling reset of the chip. As the line is active low, - it should be marked GPIO_ACTIVE_LOW (see ../gpio/gpio.txt) - maxItems: 1 - - '#gpio-cells': - const: 2 - - gpio-controller: true - - '#sound-dai-cells': - const: 0 - -required: - - compatible - - reg - - '#gpio-cells' - - gpio-controller - - '#sound-dai-cells' - -unevaluatedProperties: false - -examples: - - | - #include - spi { - #address-cells = <1>; - #size-cells = <0>; - - codec: zl38060@0 { - gpio-controller; - #gpio-cells = <2>; - #sound-dai-cells = <0>; - compatible = "mscc,zl38060"; - reg = <0>; - spi-max-frequency = <12000000>; - reset-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; - }; - }; -- cgit v1.2.3 From 78df231dae2e1a9bf22f76b5cf03c0bf98738fd7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 20 Jun 2024 11:39:57 +0200 Subject: ASoC: dt-bindings: convert everest,es7241.txt to dt-schema Convert the text biddings for the Everest ES7241 2 channels I2S analog to digital converter to dt-schema. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20240620-topic-amlogic-upstream-bindings-convert-everest-v2-1-660985615522@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/everest,es7241.txt | 28 --------- .../devicetree/bindings/sound/everest,es7241.yaml | 67 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/everest,es7241.txt create mode 100644 Documentation/devicetree/bindings/sound/everest,es7241.yaml diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.txt b/Documentation/devicetree/bindings/sound/everest,es7241.txt deleted file mode 100644 index 28f82cf4959f..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7241.txt +++ /dev/null @@ -1,28 +0,0 @@ -ES7241 i2s AD converter - -Required properties: -- compatible : "everest,es7241" -- VDDP-supply: regulator phandle for the VDDA supply -- VDDA-supply: regulator phandle for the VDDP supply -- VDDD-supply: regulator phandle for the VDDD supply - -Optional properties: -- reset-gpios: gpio connected to the reset pin -- m0-gpios : gpio connected to the m0 pin -- m1-gpios : gpio connected to the m1 pin -- everest,sdout-pull-down: - Format used by the serial interface is controlled by pulling - the sdout. If the sdout is pulled down, leftj format is used. - If this property is not provided, sdout is assumed to pulled - up and i2s format is used - -Example: - -linein: audio-codec@2 { - #sound-dai-cells = <0>; - compatible = "everest,es7241"; - VDDA-supply = <&vcc_3v3>; - VDDP-supply = <&vcc_3v3>; - VDDD-supply = <&vcc_3v3>; - reset-gpios = <&gpio GPIOH_42>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.yaml b/Documentation/devicetree/bindings/sound/everest,es7241.yaml new file mode 100644 index 000000000000..f179af758730 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es7241.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es7241.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7241 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong + +properties: + compatible: + enum: + - everest,es7241 + + reset-gpios: + maxItems: 1 + description: GPIO connected to the reset pin + + m0-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + m1-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + everest,sdout-pull-down: + type: boolean + description: + Format used by the serial interface is controlled by pulling + the sdout. If the sdout is pulled down, leftj format is used. + If this property is not provided, sdout is assumed to pulled + up and i2s format is used + + VDDP-supply: true + VDDA-supply: true + VDDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDDP-supply + - VDDA-supply + - VDDD-supply + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + codec { + compatible = "everest,es7241"; + #sound-dai-cells = <0>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + VDDP-supply = <&vddp_supply>; + VDDA-supply = <&vdda_supply>; + VDDD-supply = <&vddd_supply>; + }; + +... -- cgit v1.2.3 From 534a0e5e737f8117344372d8658a4702ae816281 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 20 Jun 2024 11:39:58 +0200 Subject: ASoC: dt-bindings: convert everest,es7134.txt to dt-schema Convert the text bindings of the Everest ES7134/7144/7154 2 channels I2S analog to digital converter to dt-schema. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20240620-topic-amlogic-upstream-bindings-convert-everest-v2-2-660985615522@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/everest,es7134.txt | 15 ------ .../devicetree/bindings/sound/everest,es71x4.yaml | 62 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 15 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/everest,es7134.txt create mode 100644 Documentation/devicetree/bindings/sound/everest,es71x4.yaml diff --git a/Documentation/devicetree/bindings/sound/everest,es7134.txt b/Documentation/devicetree/bindings/sound/everest,es7134.txt deleted file mode 100644 index 091666069bde..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7134.txt +++ /dev/null @@ -1,15 +0,0 @@ -ES7134 i2s DA converter - -Required properties: -- compatible : "everest,es7134" or - "everest,es7144" or - "everest,es7154" -- VDD-supply : regulator phandle for the VDD supply -- PVDD-supply: regulator phandle for the PVDD supply for the es7154 - -Example: - -i2s_codec: external-codec { - compatible = "everest,es7134"; - VDD-supply = <&vcc_5v>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es71x4.yaml b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml new file mode 100644 index 000000000000..fd1b32812228 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es71x4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7134/7144/7154 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong + +properties: + compatible: + enum: + - everest,es7134 + - everest,es7144 + - everest,es7154 + + VDD-supply: true + PVDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDD-supply + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - everest,es7134 + - everest,es7144 + then: + properties: + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - everest,es7154 + then: + required: + - PVDD-supply + +unevaluatedProperties: false + +examples: + - | + codec { + compatible = "everest,es7134"; + #sound-dai-cells = <0>; + VDD-supply = <&vdd_supply>; + }; + +... -- cgit v1.2.3 From 75a08ec8c3a6aef914235c409a99046a3d29d1d4 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 17 Jun 2024 12:58:37 +0530 Subject: ASoC: amd: acp: remove acp_i2s_probe function In acp_i2s_probe(), acp_base null check is verified. As already acp_base null check will be verified in acp platform driver probe sequence, additional NULL check in acp_i2s_probe() is not needed. Remove acp_i2s_probe() function. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20240617072844.871468-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 6815e751a819..54985fd0eede 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -584,21 +584,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } -static int acp_i2s_probe(struct snd_soc_dai *dai) -{ - struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - - if (!adata->acp_base) { - dev_err(dev, "I2S base is NULL\n"); - return -EINVAL; - } - - return 0; -} - const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .probe = acp_i2s_probe, .startup = acp_i2s_startup, .hw_params = acp_i2s_hwparams, .prepare = acp_i2s_prepare, -- cgit v1.2.3 From 50f1670145392398f4f2ffe9abe4599c86d11ec2 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 17 Jun 2024 12:58:38 +0530 Subject: ASoC: amd: acp: remove unused variables from acp_resource structure Remove unused variables i2s_pin_cfg_offset and i2s_mode from acp_resource structure entries. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20240617072844.871468-5-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-rembrandt.c | 2 -- sound/soc/amd/acp/acp-renoir.c | 2 -- sound/soc/amd/acp/acp63.c | 2 -- sound/soc/amd/acp/acp70.c | 2 -- sound/soc/amd/acp/amd.h | 2 -- 5 files changed, 10 deletions(-) diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 158f819f8da4..953a793de9a3 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -39,8 +39,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index b0e181c9a733..db835ed7c208 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -32,8 +32,6 @@ static struct acp_resource rsrc = { .no_of_ctrls = 1, .irqp_used = 0, .irq_reg_offset = 0x1800, - .i2s_pin_cfg_offset = 0x1400, - .i2s_mode = 0x04, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x02052800, }; diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index 4d342441a650..f223311b6740 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -55,8 +55,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index 0d7cdd4017e5..a2cbdcca4313 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -31,8 +31,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index d75b4eb34de8..cbf9ca26f3ee 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -162,8 +162,6 @@ struct acp_resource { int irqp_used; bool soc_mclk; u32 irq_reg_offset; - u32 i2s_pin_cfg_offset; - int i2s_mode; u64 scratch_reg_offset; u64 sram_pte_offset; }; -- cgit v1.2.3 From 5b162f60e7e051624e187e45b0fdc481c3573f17 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 17 Jun 2024 12:58:39 +0530 Subject: ASoC: amd: acp: modify conditional check for programming i2s mclk ACP provides different IO configurations(ACP PDM, I2S and SoundWire). I2S mclk should be programmed only when I2S configuration is selected and I2S controller is programmed as clock master. Modify the conditional check for programming i2s mclk. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20240617072844.871468-6-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-rembrandt.c | 5 +++-- sound/soc/amd/acp/acp63.c | 5 +++-- sound/soc/amd/acp/amd.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 953a793de9a3..e19981c7d65a 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -229,12 +229,13 @@ static int rembrandt_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = REMBRANDT; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_rmb_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp6x_master_clock_generate(dev); if (ret) return ret; @@ -267,7 +268,7 @@ static int __maybe_unused rmb_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp6x_master_clock_generate(dev); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index f223311b6740..f340920b3289 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -239,11 +239,12 @@ static int acp63_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = ACP63; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_acp63_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp63_i2s_master_clock_generate(adata); if (ret) return ret; @@ -276,7 +277,7 @@ static int __maybe_unused acp63_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp63_i2s_master_clock_generate(adata); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index cbf9ca26f3ee..87a4813783f9 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -173,6 +173,7 @@ struct acp_dev_data { unsigned int i2s_irq; bool tdm_mode; + bool is_i2s_config; /* SOC specific dais */ struct snd_soc_dai_driver *dai_driver; int num_dai; -- cgit v1.2.3 From d85695b01cbb2455a2f70528bb9e53f2463a39cf Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 17 Jun 2024 12:58:40 +0530 Subject: ASoC: amd: acp: move i2s clock generation sequence I2S clock generation registers should be programmed before starting the I2S dma when I2S controller is programmed as clock master. Move i2s clock generation register programming sequence prior to i2s dma start. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20240617072844.871468-7-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 54985fd0eede..97258b4cf89b 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -369,12 +369,12 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct } writel(period_bytes, adata->acp_base + water_val); writel(buf_size, adata->acp_base + buf_reg); + if (rsrc->soc_mclk) + acp_set_i2s_clk(adata, dai->driver->id); val = readl(adata->acp_base + reg_val); val = val | BIT(0); writel(val, adata->acp_base + reg_val); writel(1, adata->acp_base + ier_val); - if (rsrc->soc_mclk) - acp_set_i2s_clk(adata, dai->driver->id); return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: -- cgit v1.2.3 From 8978e1f7bc26655e0373c4a6b31e17fcdd497329 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 17 Jun 2024 12:58:41 +0530 Subject: ASoC: amd: acp: add pcm constraints for buffer size and period size ACP common dma driver has a buffer size and period size restriction which should be 64 byte aligned. Add pcm constraints for the same. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20240617072844.871468-8-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-platform.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index aaac8aa744cb..4f409cd09c11 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -197,6 +197,20 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs else runtime->hw = acp_pcm_hardware_capture; + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n"); + kfree(stream); + return ret; + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n"); + kfree(stream); + return ret; + } + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(component->dev, "set integer constraint failed\n"); -- cgit v1.2.3 From 3722873d49a1788d5420894d4f6f63e35f5c1f13 Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Mon, 17 Jun 2024 10:09:54 +0800 Subject: ASoc: PCM6240: Return directly after a failed devm_kzalloc() in pcmdevice_i2c_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value “-ENOMEM” was assigned to the local variable “ret” in one if branch after a devm_kzalloc() call failed at the beginning. This error code will trigger then a pcmdevice_remove() call with a passed null pointer so that an undesirable dereference will be performed. Thus return the appropriate error code directly. Fixes: 1324eafd37aa ("ASoc: PCM6240: Create PCM6240 Family driver code") Signed-off-by: Hao Ge Link: https://patch.msgid.link/20240617020954.17252-1-hao.ge@linux.dev Signed-off-by: Mark Brown --- sound/soc/codecs/pcm6240.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 86e126783a1d..8f7057e689fb 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -2087,10 +2087,8 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) #endif pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); - if (!pcm_dev) { - ret = -ENOMEM; - goto out; - } + if (!pcm_dev) + return -ENOMEM; pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; -- cgit v1.2.3 From 0e2407ae89b340c385afdca1e1c61dd651ae6b35 Mon Sep 17 00:00:00 2001 From: Paul Handrigan Date: Fri, 21 Jun 2024 10:17:56 -0500 Subject: ASoC: dt-bindings: cirrus,cs530x: Add initial DT binding Add the YAML DT bindings for the cs530x high performance audio ADCs. Signed-off-by: Paul Handrigan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240621151757.1661265-2-paulha@opensource.cirrus.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cirrus,cs530x.yaml | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml new file mode 100644 index 000000000000..9582eb8eb418 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs530x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic cs530x family of audio ADCs + +maintainers: + - Paul Handrigan + - patches@opensource.cirrus.com + +description: + The CS530X devices are a family of high performance audio ADCs. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs5302 + - cirrus,cs5304 + - cirrus,cs5308 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 1 + + reset-gpios: + maxItems: 1 + + vdd-a-supply: + description: Analog power supply + + vdd-io-supply: + description: Digital IO power supply + + cirrus,in-hiz-pin12: + description: + Sets input channels one and two to high impedance. + type: boolean + + cirrus,in-hiz-pin34: + description: + Sets input channels three and four to high impedance. + type: boolean + + cirrus,in-hiz-pin56: + description: + Sets input channels five and six to high impedance. + type: boolean + + cirrus,in-hiz-pin78: + description: + Sets input channels seven and eight to high impedance. + type: boolean + +required: + - compatible + - reg + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs5304: adc@48 { + compatible = "cirrus,cs5304"; + reg = <0x48>; + #sound-dai-cells = <1>; + reset-gpios = <&gpio 110 GPIO_ACTIVE_LOW>; + vdd-a-supply = <&vreg>; + vdd-io-supply = <&vreg>; + cirrus,in-hiz-pin34; + }; + }; -- cgit v1.2.3 From 2884c29152c098c32e0041061f8327bcdb4e0697 Mon Sep 17 00:00:00 2001 From: Paul Handrigan Date: Fri, 21 Jun 2024 10:17:57 -0500 Subject: ASoC: cs530x: Support for cs530x ADCs Add support for the cs530x family of high performance ADCs. Signed-off-by: Paul Handrigan Link: https://patch.msgid.link/20240621151757.1661265-3-paulha@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 14 + sound/soc/codecs/Makefile | 4 + sound/soc/codecs/cs530x-i2c.c | 72 ++++ sound/soc/codecs/cs530x.c | 966 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs530x.h | 223 ++++++++++ 5 files changed, 1279 insertions(+) create mode 100644 sound/soc/codecs/cs530x-i2c.c create mode 100644 sound/soc/codecs/cs530x.c create mode 100644 sound/soc/codecs/cs530x.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a99cb26eb280..1f539c4b9432 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -99,6 +99,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS47L90 imply SND_SOC_CS47L92 imply SND_SOC_CS53L30 + imply SND_SOC_CS530X_I2C imply SND_SOC_CX20442 imply SND_SOC_CX2072X imply SND_SOC_DA7210 @@ -1000,6 +1001,19 @@ config SND_SOC_CS53L30 tristate "Cirrus Logic CS53L30 CODEC" depends on I2C +config SND_SOC_CS530X + tristate + +config SND_SOC_CS530X_I2C + tristate "Cirrus Logic CS530x ADCs (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS530X + help + Enable support for Cirrus Logic CS530X ADCs + with I2C control. + config SND_SOC_CX20442 tristate depends on TTY diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ad0f77224c74..c2c0d02b5484 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -107,6 +107,8 @@ snd-soc-cs47l85-y := cs47l85.o snd-soc-cs47l90-y := cs47l90.o snd-soc-cs47l92-y := cs47l92.o snd-soc-cs53l30-y := cs53l30.o +snd-soc-cs530x-y := cs530x.o +snd-soc-cs530x-i2c-y := cs530x-i2c.o snd-soc-cx20442-y := cx20442.o snd-soc-cx2072x-y := cx2072x.o snd-soc-da7210-y := da7210.o @@ -509,6 +511,8 @@ obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o +obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o +obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c new file mode 100644 index 000000000000..56659bf735db --- /dev/null +++ b/sound/soc/codecs/cs530x-i2c.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include + +#include "cs530x.h" + +static const struct of_device_id cs530x_of_match[] = { + { + .compatible = "cirrus,cs5302", + .data = (void *)CS5302, + }, { + .compatible = "cirrus,cs5304", + .data = (void *)CS5304, + }, { + .compatible = "cirrus,cs5308", + .data = (void *)CS5308, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cs530x_of_match); + +static const struct i2c_device_id cs530x_i2c_id[] = { + { "cs5302", CS5302 }, + { "cs5304", CS5304 }, + { "cs5308", CS5308 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id); + +static int cs530x_i2c_probe(struct i2c_client *client) +{ + struct cs530x_priv *cs530x; + + cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL); + if (!cs530x) + return -ENOMEM; + + i2c_set_clientdata(client, cs530x); + + cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap); + if (IS_ERR(cs530x->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap), + "Failed to allocate register map\n"); + + cs530x->devtype = (uintptr_t)i2c_get_match_data(client); + cs530x->dev = &client->dev; + + return cs530x_probe(cs530x); +} + +static struct i2c_driver cs530x_i2c_driver = { + .driver = { + .name = "cs530x", + .of_match_table = cs530x_of_match, + }, + .probe = cs530x_i2c_probe, + .id_table = cs530x_i2c_id, +}; +module_i2c_driver(cs530x_i2c_driver); + +MODULE_DESCRIPTION("I2C CS530X driver"); +MODULE_IMPORT_NS(SND_SOC_CS530X); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c new file mode 100644 index 000000000000..036d0f45e3ba --- /dev/null +++ b/sound/soc/codecs/cs530x.c @@ -0,0 +1,966 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs530x.h" + +#define CS530X_MAX_ADC_CH 8 +#define CS530X_MIN_ADC_CH 2 + +static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = { + "vdd-a", + "vdd-io", +}; + +static const struct reg_default cs530x_reg_defaults[] = { + { CS530X_CLK_CFG_0, 0x30 }, + { CS530X_CLK_CFG_1, 0x0001 }, + { CS530X_CHIP_ENABLE, 0 }, + { CS530X_ASP_CFG, 0 }, + { CS530X_SIGNAL_PATH_CFG, 0 }, + { CS530X_IN_ENABLES, 0 }, + { CS530X_IN_RAMP_SUM, 0x0022 }, + { CS530X_IN_FILTER, 0 }, + { CS530X_IN_HIZ, 0 }, + { CS530X_IN_INV, 0 }, + { CS530X_IN_VOL_CTRL1_0, 0x8000 }, + { CS530X_IN_VOL_CTRL1_1, 0x8000 }, + { CS530X_IN_VOL_CTRL2_0, 0x8000 }, + { CS530X_IN_VOL_CTRL2_1, 0x8000 }, + { CS530X_IN_VOL_CTRL3_0, 0x8000 }, + { CS530X_IN_VOL_CTRL3_1, 0x8000 }, + { CS530X_IN_VOL_CTRL4_0, 0x8000 }, + { CS530X_IN_VOL_CTRL4_1, 0x8000 }, + { CS530X_PAD_FN, 0 }, + { CS530X_PAD_LVL, 0 }, +}; + +static bool cs530x_read_and_write_regs(unsigned int reg) +{ + switch (reg) { + case CS530X_CLK_CFG_0: + case CS530X_CLK_CFG_1: + case CS530X_CHIP_ENABLE: + case CS530X_ASP_CFG: + case CS530X_SIGNAL_PATH_CFG: + case CS530X_IN_ENABLES: + case CS530X_IN_RAMP_SUM: + case CS530X_IN_FILTER: + case CS530X_IN_HIZ: + case CS530X_IN_INV: + case CS530X_IN_VOL_CTRL1_0: + case CS530X_IN_VOL_CTRL1_1: + case CS530X_IN_VOL_CTRL2_0: + case CS530X_IN_VOL_CTRL2_1: + case CS530X_IN_VOL_CTRL3_0: + case CS530X_IN_VOL_CTRL3_1: + case CS530X_IN_VOL_CTRL4_0: + case CS530X_IN_VOL_CTRL4_1: + case CS530X_PAD_FN: + case CS530X_PAD_LVL: + return true; + default: + return false; + } +} + +static bool cs530x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_DEVID: + case CS530X_REVID: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static bool cs530x_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_SW_RESET: + case CS530X_IN_VOL_CTRL5: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret) + goto volsw_err; + + /* Write IN_VU bit for the volume change to take effect */ + regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU); + +volsw_err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0); + +static const char * const cs530x_in_hpf_text[] = { + "Min Phase Slow Roll-off", + "Min Phase Fast Roll-off", + "Linear Phase Slow Roll-off", + "Linear Phase Fast Roll-off", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_hpf_enum, CS530X_IN_FILTER, + CS530X_IN_FILTER_SHIFT, + cs530x_in_hpf_text); + +static const char * const cs530x_in_4ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_4ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum), +}; + +static const char * const cs530x_in_8ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", + "Groups of 8", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_8ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum), +}; + + +static const char * const cs530x_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_INC_SHIFT, + cs530x_vol_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_DEC_SHIFT, + cs530x_vol_ramp_text); + +static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = { +SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_ENUM("IN HPF Select", cs530x_in_hpf_enum), +SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum), +SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum), + +SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = { +SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = { +SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0), +}; + +static int cs530x_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + cs530x->adc_pairs_count++; + break; + case SND_SOC_DAPM_POST_PMU: + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + + cs530x->adc_pairs_count--; + if (!cs530x->adc_pairs_count) { + usleep_range(1000, 1100); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + } + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_kcontrol_new adc12_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc34_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc56_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc78_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new in_hpf_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +/* General DAPM widgets for all devices */ +static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0), +}; + +/* ADC's Channels 1 and 2 plus generic ADC DAPM events */ +static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1"), +SND_SOC_DAPM_INPUT("IN2"), +SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0), +SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl), +SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT, + 0, &in_hpf_ctrl), +}; + +/* ADC's Channels 3 and 4 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN3"), +SND_SOC_DAPM_INPUT("IN4"), +SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0), +SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl), +}; + +/* ADC's Channels 5 to 8 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN5"), +SND_SOC_DAPM_INPUT("IN6"), +SND_SOC_DAPM_INPUT("IN7"), +SND_SOC_DAPM_INPUT("IN8"), +SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0), +SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl), +SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0), +SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl), +}; + +static const struct snd_soc_dapm_route adc_ch1_2_routes[] = { + { "ADC1", NULL, "Global Enable" }, + { "ADC2", NULL, "Global Enable" }, + + { "ADC12 Enable", "Switch", "IN1" }, + { "ADC12 Enable", "Switch", "IN2" }, + { "ADC1", NULL, "ADC12 Enable" }, + { "ADC2", NULL, "ADC12 Enable" }, + { "IN HPF", "Switch", "ADC1" }, + { "IN HPF", "Switch", "ADC2" }, + + { "AIF Capture", NULL, "IN HPF" }, + { "AIF Capture", NULL, "ADC1" }, + { "AIF Capture", NULL, "ADC2" }, +}; + +static const struct snd_soc_dapm_route adc_ch3_4_routes[] = { + { "ADC3", NULL, "Global Enable" }, + { "ADC4", NULL, "Global Enable" }, + + { "ADC34 Enable", "Switch", "IN3" }, + { "ADC34 Enable", "Switch", "IN4" }, + { "ADC3", NULL, "ADC34 Enable" }, + { "ADC4", NULL, "ADC34 Enable" }, + { "IN HPF", "Switch", "ADC3" }, + { "IN HPF", "Switch", "ADC4" }, + + { "AIF Capture", NULL, "ADC3" }, + { "AIF Capture", NULL, "ADC4" }, +}; + +static const struct snd_soc_dapm_route adc_ch5_8_routes[] = { + { "ADC5", NULL, "Global Enable" }, + { "ADC6", NULL, "Global Enable" }, + { "ADC7", NULL, "Global Enable" }, + { "ADC8", NULL, "Global Enable" }, + + { "ADC56 Enable", "Switch", "IN5" }, + { "ADC56 Enable", "Switch", "IN6" }, + { "ADC5", NULL, "ADC56 Enable" }, + { "ADC6", NULL, "ADC56 Enable" }, + { "IN HPF", "Switch", "ADC5" }, + { "IN HPF", "Switch", "ADC6" }, + + { "AIF Capture", NULL, "ADC5" }, + { "AIF Capture", NULL, "ADC6" }, + + { "ADC78 Enable", "Switch", "IN7" }, + { "ADC78 Enable", "Switch", "IN8" }, + { "ADC7", NULL, "ADC78 Enable" }, + { "ADC8", NULL, "ADC78 Enable" }, + { "IN HPF", "Switch", "ADC7" }, + { "IN HPF", "Switch", "ADC8" }, + + { "AIF Capture", NULL, "ADC7" }, + { "AIF Capture", NULL, "ADC8" }, +}; + +static void cs530x_add_12_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_1_to_2_controls, + ARRAY_SIZE(cs530x_in_1_to_2_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes, + ARRAY_SIZE(adc_ch1_2_routes)); +} + +static void cs530x_add_34_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_3_to_4_controls, + ARRAY_SIZE(cs530x_in_3_to_4_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes, + ARRAY_SIZE(adc_ch3_4_routes)); +} + +static int cs530x_set_bclk(struct snd_soc_component *component) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int bclk_val; + + switch (cs530x->bclk) { + case 2822400: + case 3072000: + bclk_val = CS530X_BCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + bclk_val = CS530X_BCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + bclk_val = CS530X_BCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + bclk_val = CS530X_BCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid BCLK %d\n", cs530x->bclk); + return -EINVAL; + } + + dev_dbg(component->dev, "BCLK is %d\n", cs530x->bclk); + + return regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_BCLK_FREQ_MASK, bclk_val); +} + +static int cs530x_set_pll_refclk(struct snd_soc_component *component, + const unsigned int freq) +{ + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int refclk; + + switch (freq) { + case 2822400: + case 3072000: + refclk = CS530X_REFCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + refclk = CS530X_REFCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + refclk = CS530X_REFCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + refclk = CS530X_REFCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid PLL refclk %d\n", freq); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_FREQ_MASK, refclk); +} + +static int cs530x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret = 0, fs = params_rate(params); + unsigned int fs_val; + + + switch (fs) { + case 32000: + fs_val = CS530X_FS_32K; + break; + case 44100: + case 48000: + fs_val = CS530X_FS_48K_44P1K; + break; + case 88200: + case 96000: + fs_val = CS530X_FS_96K_88P2K; + break; + case 176400: + case 192000: + fs_val = CS530X_FS_192K_176P4K; + break; + case 356800: + case 384000: + fs_val = CS530X_FS_384K_356P8K; + break; + case 705600: + case 768000: + fs_val = CS530X_FS_768K_705P6K; + break; + default: + dev_err(component->dev, "Invalid sample rate %d\n", fs); + return -EINVAL; + } + + cs530x->fs = fs; + regmap_update_bits(regmap, CS530X_CLK_CFG_1, + CS530X_SAMPLE_RATE_MASK, fs_val); + + if (cs530x->tdm_slots) { + dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", + cs530x->tdm_slots, cs530x->tdm_width); + cs530x->bclk = snd_soc_calc_bclk(cs530x->fs, + cs530x->tdm_width, + params_channels(params), + cs530x->tdm_slots); + } else { + cs530x->bclk = snd_soc_params_to_bclk(params); + } + + if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK)) { + ret = cs530x_set_pll_refclk(component, cs530x->bclk); + if (ret) + return ret; + } + + return cs530x_set_bclk(component); +} + +static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int asp_fmt, asp_cfg = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + asp_cfg = CS530X_ASP_PRIMARY; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = CS530X_ASP_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = CS530X_ASP_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + asp_fmt = CS530X_ASP_FMT_LJ; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + asp_cfg |= CS530X_ASP_BCLK_INV; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV, + asp_cfg); + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_FMT_MASK, asp_fmt); +} + +static bool cs530x_check_mclk_freq(struct snd_soc_component *component, + const unsigned int freq) +{ + switch (freq) { + case 24576000: + case 22579200: + case 12288000: + case 11289600: + return true; + default: + dev_err(component->dev, "Invalid MCLK %d\n", freq); + return false; + } +} + +static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int val; + + switch (tx_mask) { + case CS530X_0_1_TDM_SLOT_MASK: + case CS530X_0_3_TDM_SLOT_MASK: + case CS530X_0_7_TDM_SLOT_MASK: + val = CS530X_0_7_TDM_SLOT_VAL; + break; + case CS530X_2_3_TDM_SLOT_MASK: + val = CS530X_2_3_TDM_SLOT_VAL; + break; + case CS530X_4_5_TDM_SLOT_MASK: + case CS530X_4_7_TDM_SLOT_MASK: + val = CS530X_4_7_TDM_SLOT_VAL; + break; + case CS530X_6_7_TDM_SLOT_MASK: + val = CS530X_6_7_TDM_SLOT_VAL; + break; + case CS530X_8_9_TDM_SLOT_MASK: + case CS530X_8_11_TDM_SLOT_MASK: + case CS530X_8_15_TDM_SLOT_MASK: + val = CS530X_8_15_TDM_SLOT_VAL; + break; + case CS530X_10_11_TDM_SLOT_MASK: + val = CS530X_10_11_TDM_SLOT_VAL; + break; + case CS530X_12_13_TDM_SLOT_MASK: + case CS530X_12_15_TDM_SLOT_MASK: + val = CS530X_12_15_TDM_SLOT_VAL; + break; + case CS530X_14_15_TDM_SLOT_MASK: + val = CS530X_14_15_TDM_SLOT_VAL; + break; + default: + dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_TDM_SLOT_MASK, + val << CS530X_ASP_TDM_SLOT_SHIFT); +} + +static const struct snd_soc_dai_ops cs530x_dai_ops = { + .set_fmt = cs530x_set_fmt, + .hw_params = cs530x_hw_params, + .set_tdm_slot = cs530x_set_tdm_slot, +}; + +static const struct snd_soc_dai_driver cs530x_dai = { + .name = "cs530x-dai", + .capture = { + .stream_name = "AIF Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &cs530x_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static int cs530x_set_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int sysclk_src; + int ret; + + regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src); + + /* Check if the source is the PLL */ + if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0) + return 0; + + switch (source) { + case CS530X_PLL_SRC_MCLK: + if (!cs530x_check_mclk_freq(component, freq_in)) + return -EINVAL; + + ret = cs530x_set_pll_refclk(component, freq_in); + if (ret) + return ret; + + break; + case CS530X_PLL_SRC_BCLK: + break; + default: + dev_err(component->dev, "Invalid PLL source %d\n", source); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK, source); +} + +static int cs530x_component_probe(struct snd_soc_component *component) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int num_widgets; + + snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets, + ARRAY_SIZE(cs530x_gen_dapm_widgets)); + + switch (cs530x->devtype) { + case CS5302: + cs530x_add_12_adc_widgets(component); + break; + case CS5304: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_4ch_controls, + num_widgets); + break; + + case CS5308: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls); + snd_soc_add_component_controls(component, + cs530x_in_5_to_8_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_8ch_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets); + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets, + num_widgets); + + snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes, + ARRAY_SIZE(adc_ch5_8_routes)); + break; + default: + dev_err(component->dev, "Invalid device type %d\n", + cs530x->devtype); + return -EINVAL; + } + + return 0; +} + +static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (source) { + case CS530X_SYSCLK_SRC_MCLK: + if (freq != 24560000 && freq != 22572000) { + dev_err(component->dev, "Invalid MCLK source rate %d\n", + freq); + return -EINVAL; + } + + cs530x->mclk_rate = freq; + break; + case CS530X_SYSCLK_SRC_PLL: + break; + default: + dev_err(component->dev, "Invalid clock id %d\n", clk_id); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_SYSCLK_SRC_MASK, + source << CS530X_SYSCLK_SRC_SHIFT); +} + +static const struct snd_soc_component_driver soc_component_dev_cs530x = { + .probe = cs530x_component_probe, + .set_sysclk = cs530x_set_sysclk, + .set_pll = cs530x_set_pll, + .endianness = 1, +}; + +const struct regmap_config cs530x_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = CS530X_MAX_REGISTER, + .readable_reg = cs530x_readable_register, + .writeable_reg = cs530x_writeable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs530x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults), +}; +EXPORT_SYMBOL_NS_GPL(cs530x_regmap, SND_SOC_CS530X); + +static int cs530x_check_device_id(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + unsigned int dev_id, rev; + int ret; + + ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id); + if (ret) + return dev_err_probe(dev, ret, "Can't read device ID\n"); + + ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev); + if (ret) + return dev_err_probe(dev, ret, "Can't read REV ID\n"); + + dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev); + + switch (dev_id) { + case CS530X_2CH_ADC_DEV_ID: + cs530x->num_adcs = 2; + break; + case CS530X_4CH_ADC_DEV_ID: + cs530x->num_adcs = 4; + break; + case CS530X_8CH_ADC_DEV_ID: + cs530x->num_adcs = 8; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n", + dev_id); + } + + return 0; +} + +static int cs530x_parse_device_properties(struct cs530x_priv *cs530x) +{ + struct regmap *regmap = cs530x->regmap; + struct device *dev = cs530x->dev; + unsigned int val = 0; + + switch (cs530x->num_adcs) { + case 8: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin78")) + val = CS530X_IN78_HIZ; + + if (device_property_read_bool(dev, "cirrus,in-hiz-pin56")) + val |= CS530X_IN56_HIZ; + + fallthrough; + case 4: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin34")) + val |= CS530X_IN34_HIZ; + + fallthrough; + case 2: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin12")) + val |= CS530X_IN12_HIZ; + + return regmap_set_bits(regmap, CS530X_IN_HIZ, val); + default: + return dev_err_probe(dev, -EINVAL, + "Invalid number of adcs %d\n", + cs530x->num_adcs); + } +} + +int cs530x_probe(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + int ret, i; + + cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai, + sizeof(*(cs530x->dev_dai)), + GFP_KERNEL); + if (!cs530x->dev_dai) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++) + cs530x->supplies[i].supply = cs530x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to request supplies"); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to enable supplies"); + + cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(cs530x->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio), + "Reset gpio not available\n"); + goto err_regulator; + } + + if (cs530x->reset_gpio) { + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs530x->reset_gpio, 0); + } + + usleep_range(5000, 5100); + ret = cs530x_check_device_id(cs530x); + if (ret) + goto err_reset; + + if (!cs530x->reset_gpio) { + ret = regmap_write(cs530x->regmap, CS530X_SW_RESET, + CS530X_SW_RST_VAL); + if (ret) { + dev_err_probe(dev, ret, "Soft Reset Failed\n"); + goto err_reset; + } + } + + ret = cs530x_parse_device_properties(cs530x); + if (ret) + goto err_reset; + + cs530x->dev_dai->capture.channels_max = cs530x->num_adcs; + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_cs530x, cs530x->dev_dai, 1); + if (ret) { + dev_err_probe(dev, ret, "Can't register cs530x component\n"); + goto err_reset; + } + + return 0; + +err_reset: + gpiod_set_value_cansleep(cs530x->reset_gpio, 1); + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs530x_probe, SND_SOC_CS530X); + +MODULE_DESCRIPTION("CS530X CODEC Driver"); +MODULE_AUTHOR("Paul Handrigan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h new file mode 100644 index 000000000000..1c85310a5d03 --- /dev/null +++ b/sound/soc/codecs/cs530x.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS530x CODEC driver internal data + * + * Copyright (C) 2023-2024 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef _CS530X_H +#define _CS530X_H + +#include +#include +#include +#include + +/* Devices */ +#define CS530X_2CH_ADC_DEV_ID 0x5302 +#define CS530X_4CH_ADC_DEV_ID 0x5304 +#define CS530X_8CH_ADC_DEV_ID 0x5308 + +/* Registers */ + +#define CS530X_DEVID 0x0000000 +#define CS530X_REVID 0x0000004 +#define CS530X_SW_RESET 0x0000022 + +#define CS530X_CLK_CFG_0 0x0000040 +#define CS530X_CLK_CFG_1 0x0000042 +#define CS530X_CHIP_ENABLE 0x0000044 +#define CS530X_ASP_CFG 0x0000048 +#define CS530X_SIGNAL_PATH_CFG 0x0000050 +#define CS530X_IN_ENABLES 0x0000080 +#define CS530X_IN_RAMP_SUM 0x0000082 +#define CS530X_IN_FILTER 0x0000086 +#define CS530X_IN_HIZ 0x0000088 +#define CS530X_IN_INV 0x000008A +#define CS530X_IN_VOL_CTRL1_0 0x0000090 +#define CS530X_IN_VOL_CTRL1_1 0x0000092 +#define CS530X_IN_VOL_CTRL2_0 0x0000094 +#define CS530X_IN_VOL_CTRL2_1 0x0000096 +#define CS530X_IN_VOL_CTRL3_0 0x0000098 +#define CS530X_IN_VOL_CTRL3_1 0x000009A +#define CS530X_IN_VOL_CTRL4_0 0x000009C +#define CS530X_IN_VOL_CTRL4_1 0x000009E +#define CS530X_IN_VOL_CTRL5 0x00000A0 + +#define CS530X_PAD_FN 0x0003D24 +#define CS530X_PAD_LVL 0x0003D28 + +#define CS530X_MAX_REGISTER CS530X_PAD_LVL + +/* Register Fields */ + +/* REVID */ +#define CS530X_MTLREVID GENMASK(3, 0) +#define CS530X_AREVID GENMASK(7, 4) + +/* SW_RESET */ +#define CS530X_SW_RST_SHIFT 8 +#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT) + +/* CLK_CFG_0 */ +#define CS530X_PLL_REFCLK_SRC_MASK BIT(0) +#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4) +#define CS530X_SYSCLK_SRC_MASK BIT(12) +#define CS530X_SYSCLK_SRC_SHIFT 12 +#define CS530X_REFCLK_2P822_3P072 0 +#define CS530X_REFCLK_5P6448_6P144 0x10 +#define CS530X_REFCLK_11P2896_12P288 0x20 +#define CS530X_REFCLK_24P5792_24P576 0x30 + +/* CLK_CFG_1 */ +#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0) +#define CS530X_FS_32K 0 +#define CS530X_FS_48K_44P1K 1 +#define CS530X_FS_96K_88P2K 2 +#define CS530X_FS_192K_176P4K 3 +#define CS530X_FS_384K_356P8K 4 +#define CS530X_FS_768K_705P6K 5 + +/* CHIP_ENABLE */ +#define CS530X_GLOBAL_EN BIT(0) + +/* ASP_CFG */ +#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0) +#define CS530X_ASP_PRIMARY BIT(5) +#define CS530X_ASP_BCLK_INV BIT(6) +#define CS530X_BCLK_2P822_3P072 0 +#define CS530X_BCLK_5P6448_6P144 1 +#define CS530X_BCLK_11P2896_12P288 2 +#define CS530X_BCLK_24P5792_24P576 3 + +/* SIGNAL_PATH_CFG */ +#define CS530X_ASP_FMT_MASK GENMASK(2, 0) +#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3) +#define CS530X_ASP_TDM_SLOT_SHIFT 3 +#define CS530X_ASP_CH_REVERSE BIT(9) +#define CS530X_ASP_FMT_I2S 0 +#define CS530X_ASP_FMT_LJ 1 +#define CS530X_ASP_FMT_DSP_A 0x6 + +/* TDM Slots */ +#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0) +#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0) +#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0) +#define CS530X_0_7_TDM_SLOT_VAL 0 + +#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2) +#define CS530X_2_3_TDM_SLOT_VAL 1 + +#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4) +#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4) +#define CS530X_4_7_TDM_SLOT_VAL 2 + +#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6) +#define CS530X_6_7_TDM_SLOT_VAL 3 + +#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8) +#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8) +#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8) +#define CS530X_8_15_TDM_SLOT_VAL 4 + +#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10) +#define CS530X_10_11_TDM_SLOT_VAL 5 + +#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12) +#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12) +#define CS530X_12_15_TDM_SLOT_VAL 6 + +#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14) +#define CS530X_14_15_TDM_SLOT_VAL 7 + +/* IN_RAMP_SUM */ +#define CS530X_RAMP_RATE_INC_SHIFT 0 +#define CS530X_RAMP_RATE_DEC_SHIFT 4 +#define CS530X_IN_SUM_MODE_SHIFT 13 + +/* IN_FILTER */ +#define CS530X_IN_FILTER_SHIFT 8 +#define CS530X_IN_HPF_EN_SHIFT 12 + +/* IN_HIZ */ +#define CS530X_IN12_HIZ BIT(0) +#define CS530X_IN34_HIZ BIT(1) +#define CS530X_IN56_HIZ BIT(2) +#define CS530X_IN78_HIZ BIT(3) + +/* IN_INV */ +#define CS530X_IN1_INV_SHIFT 0 +#define CS530X_IN2_INV_SHIFT 1 +#define CS530X_IN3_INV_SHIFT 2 +#define CS530X_IN4_INV_SHIFT 3 +#define CS530X_IN5_INV_SHIFT 4 +#define CS530X_IN6_INV_SHIFT 5 +#define CS530X_IN7_INV_SHIFT 6 +#define CS530X_IN8_INV_SHIFT 7 + +/* IN_VOL_CTLy_z */ +#define CS530X_IN_MUTE BIT(15) + +/* IN_VOL_CTL5 */ +#define CS530X_IN_VU BIT(0) + +/* PAD_FN */ +#define CS530X_DOUT2_FN BIT(0) +#define CS530X_DOUT3_FN BIT(1) +#define CS530X_DOUT4_FN BIT(2) +#define CS530X_SPI_CS_FN BIT(3) +#define CS530X_CONFIG2_FN BIT(6) +#define CS530X_CONFIG3_FN BIT(7) +#define CS530X_CONFIG4_FN BIT(8) +#define CS530X_CONFIG5_FN BIT(9) + +/* PAD_LVL */ +#define CS530X_CONFIG2_LVL BIT(6) +#define CS530X_CONFIG3_LVL BIT(7) +#define CS530X_CONFIG4_LVL BIT(8) +#define CS530X_CONFIG5_LVL BIT(9) + +/* System Clock Source */ +#define CS530X_SYSCLK_SRC_MCLK 0 +#define CS530X_SYSCLK_SRC_PLL 1 + +/* PLL Reference Clock Source */ +#define CS530X_PLL_SRC_BCLK 0 +#define CS530X_PLL_SRC_MCLK 1 + +#define CS530X_NUM_SUPPLIES 2 + +enum cs530x_type { + CS5302, + CS5304, + CS5308, +}; + +/* codec private data */ +struct cs530x_priv { + struct regmap *regmap; + struct device *dev; + struct snd_soc_dai_driver *dev_dai; + + enum cs530x_type devtype; + int num_adcs; + int num_dacs; + + struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES]; + + unsigned int mclk_rate; + + int tdm_width; + int tdm_slots; + int bclk; + int fs; + int adc_pairs_count; + + struct gpio_desc *reset_gpio; +}; + +extern const struct regmap_config cs530x_regmap; +int cs530x_probe(struct cs530x_priv *cs530x); + +#endif -- cgit v1.2.3 From 9c9b172fdd918c2f852140fc4a3cd827b78fa947 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Sat, 22 Jun 2024 13:12:45 +0000 Subject: ASoC: Add support for ti,pcm5242 to the pcm512x driver Add a compatible string to enable support for the ti,pcm5242 DAC chip in the pcm512x driver. Signed-off-by: Christian Hewitt Link: https://patch.msgid.link/20240622131245.2607533-3-christianshewitt@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm512x-i2c.c | 2 ++ sound/soc/codecs/pcm512x-spi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 4be476a280e1..92bcf5179779 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -39,6 +39,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { "tas5754", }, { "tas5756", }, { } @@ -51,6 +52,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { .compatible = "ti,tas5754", }, { .compatible = "ti,tas5756", }, { } diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 4d29e7196380..6629b862f47d 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -36,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -45,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); -- cgit v1.2.3 From 9427997a5e13320a3c404a0cb67a292ad8e00b62 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Sat, 22 Jun 2024 13:12:44 +0000 Subject: ASoC: dt-bindings: add ti,pcm5242 to pcm512x Add ti,pcm5242 to the pcm512x driver file Signed-off-by: Christian Hewitt Link: https://patch.msgid.link/20240622131245.2607533-2-christianshewitt@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/pcm512x.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 77006a4aec4a..47878a6df608 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -6,7 +6,7 @@ on the board). The TAS575x devices only support I2C. Required properties: - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141", - "ti,pcm5142", "ti,tas5754" or "ti,tas5756" + "ti,pcm5142", "ti,pcm5242", "ti,tas5754" or "ti,tas5756" - reg : the I2C address of the device for I2C, the chip select number for SPI. -- cgit v1.2.3 From 4eed78198b30c4c5975e454e7b1e6e25a7ac7353 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 24 Jun 2024 11:06:04 +0800 Subject: ASoC: codecs: ES8326: Slove headphone detection issue We modified the headphone detection setting to avoid an error button state after codec resume from suspend state Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20240624030607.4307-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 6a4e42e5e35b..0468e6db81ca 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -877,6 +877,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ dev_dbg(comp->dev, "button pressed\n"); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); queue_delayed_work(system_wq, &es8326->button_press_work, 10); goto exit; } @@ -1052,11 +1054,6 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f); - es8326->jack_remove_retry = 0; - es8326->hp = 0; - es8326->hpl_vol = 0x03; - es8326->hpr_vol = 0x03; - es8326_irq(es8326->irq, es8326); return 0; } @@ -1211,6 +1208,10 @@ static int es8326_i2c_probe(struct i2c_client *i2c) } es8326->irq = i2c->irq; + es8326->jack_remove_retry = 0; + es8326->hp = 0; + es8326->hpl_vol = 0x03; + es8326->hpr_vol = 0x03; INIT_DELAYED_WORK(&es8326->jack_detect_work, es8326_jack_detect_handler); INIT_DELAYED_WORK(&es8326->button_press_work, -- cgit v1.2.3 From 7e7dbdee96cbc660e7e8d3d9d7a512acaa6ca69d Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 24 Jun 2024 11:06:06 +0800 Subject: ASoC: codecs: ES8326: Minimize the pop noise Executing regcache_sync at initialization, we can hear a gentle pop noise. So we created es8326_init for initialization instead of executing es8326_resume Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20240624030607.4307-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 0468e6db81ca..55edab04fa4b 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -974,14 +974,10 @@ static int es8326_calibrate(struct snd_soc_component *component) return 0; } -static int es8326_resume(struct snd_soc_component *component) +static void es8326_init(struct snd_soc_component *component) { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - regcache_cache_only(es8326->regmap, false); - regcache_sync(es8326->regmap); - - /* reset internal clock state */ regmap_write(es8326->regmap, ES8326_RESET, 0x1f); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); @@ -1037,7 +1033,6 @@ static int es8326_resume(struct snd_soc_component *component) es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, @@ -1053,6 +1048,28 @@ static int es8326_resume(struct snd_soc_component *component) ES8326_MUTE); regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f); + regmap_write(es8326->regmap, ES8326_CLK_DIV_LRCK, 0xff); + + msleep(200); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); +} + +static int es8326_resume(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + regcache_cache_only(es8326->regmap, false); + regcache_cache_bypass(es8326->regmap, true); + regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, ®); + regcache_cache_bypass(es8326->regmap, false); + /* reset internal clock state */ + if (reg == 0x05) + regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); + else + es8326_init(component); + + regcache_sync(es8326->regmap); es8326_irq(es8326->irq, es8326); return 0; @@ -1112,7 +1129,7 @@ static int es8326_probe(struct snd_soc_component *component) } dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); - es8326_resume(component); + es8326_init(component); return 0; } -- cgit v1.2.3 From 34fa846f52f9fbef8aa262d3b39e71188e8dd884 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 24 Jun 2024 11:06:07 +0800 Subject: ASoC: codecs: ES8326: regcache_sync error issue We modified the regmap_config members to fix cach sync error. There are several registers that should be read-only registers. If these registers are written while synchronizing the register values, the codec will enter an error state.So we create es8326_writeable_register, and set these registers to false Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20240624030607.4307-5-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 55edab04fa4b..b246694ebb4f 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -329,11 +329,29 @@ static bool es8326_volatile_register(struct device *dev, unsigned int reg) } } +static bool es8326_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8326_BIAS_SW1: + case ES8326_BIAS_SW2: + case ES8326_BIAS_SW3: + case ES8326_BIAS_SW4: + case ES8326_ADC_HPFS1: + case ES8326_ADC_HPFS2: + return false; + default: + return true; + } +} + static const struct regmap_config es8326_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, + .use_single_read = true, + .use_single_write = true, .volatile_reg = es8326_volatile_register, + .writeable_reg = es8326_writeable_register, .cache_type = REGCACHE_RBTREE, }; -- cgit v1.2.3 From 4ae814dabcdae3e434820c64396e15ccb33cc0ce Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Thu, 13 Jun 2024 10:07:25 +0800 Subject: ASoC: mediatek: mt6358: Add "Dmic Mode Switch" kcontrol for switch DMIC mode. There are two hardware connection methods for DMICs on the MT6358. In cases where more than two DMICs are used, we need to time-multiplex these DMICs. Therefore, we need to dynamically switch the modes of these DMICs based on the actual usage scenarios. ---- DMIC1 AU_VIN0 --- ---- DMIC2 AU_VIN2 --- ----DMIC3 When we want to use DMIC1/2, configure it to one-wire mode. When we want to use DMIC1/3, configure it to two-wire mode. Signed-off-by: Jiaxin Yu Link: https://patch.msgid.link/20240613020725.27874-1-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/codecs/mt6358.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index 0284e29c11d3..9247b90d1b99 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -96,7 +96,7 @@ struct mt6358_priv { int wov_enabled; - unsigned int dmic_one_wire_mode; + int dmic_one_wire_mode; }; int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, @@ -577,6 +577,39 @@ static int mt6358_put_wov(struct snd_kcontrol *kcontrol, return 0; } +static int mt6358_dmic_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->dmic_one_wire_mode; + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 0; +} + +static int mt6358_dmic_mode_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + + if (enabled < 0 || enabled > 1) + return -EINVAL; + + if (priv->dmic_one_wire_mode != enabled) { + priv->dmic_one_wire_mode = enabled; + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 1; + } + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 0; +} + static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); @@ -599,6 +632,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = { SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0, mt6358_get_wov, mt6358_put_wov), + + SOC_SINGLE_BOOL_EXT("Dmic Mode Switch", 0, + mt6358_dmic_mode_get, mt6358_dmic_mode_set), }; /* MUX */ -- cgit v1.2.3 From fe836c78ef1ff16da32912c22348091a0d67bda1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 24 Jun 2024 14:15:18 +0200 Subject: ASoC: SOF: ipc4-topology: Use correct queue_id for requesting input pin format It is incorrect to request the input pin format of the destination widget using the output pin index of the source module as the indexes are not necessarily matching. moduleA.out_pin1 can be connected to moduleB.in_pin0 for example. Use the dst_queue_id to request the input format of the destination module. This bug remained unnoticed likely because in nocodec topologies we don't have process modules after a module copier, thus the pin/queue index is ignored. For the process module case, the code was likely have been tested in a controlled way where all the pin/queue/format properties were present to work. Update the debug prints to have better information. Reviewed-by: Kai Vehmanen Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Cc: stable@vger.kernel.org # v6.8+ Link: https://patch.msgid.link/20240624121519.91703-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index d123edfa3bae..7755c01735f5 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2869,7 +2869,7 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id, static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct snd_sof_widget *src_widget, struct snd_sof_widget *sink_widget, - int sink_id) + struct snd_sof_route *sroute) { struct sof_ipc4_copier_config_set_sink_format format; const struct sof_ipc_ops *iops = sdev->ipc->ops; @@ -2878,9 +2878,6 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct sof_ipc4_fw_module *fw_module; struct sof_ipc4_msg msg = {{ 0 }}; - dev_dbg(sdev->dev, "%s set copier sink %d format\n", - src_widget->widget->name, sink_id); - if (WIDGET_IS_DAI(src_widget->id)) { struct snd_sof_dai *dai = src_widget->private; @@ -2891,13 +2888,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, fw_module = src_widget->module_info; - format.sink_id = sink_id; + format.sink_id = sroute->src_queue_id; memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt)); - pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id); + pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id); if (!pin_fmt) { - dev_err(sdev->dev, "Unable to get pin %d format for %s", - sink_id, sink_widget->widget->name); + dev_err(sdev->dev, + "Failed to get input audio format of %s:%d for output of %s:%d\n", + sink_widget->widget->name, sroute->dst_queue_id, + src_widget->widget->name, sroute->src_queue_id); return -EINVAL; } @@ -2955,7 +2954,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { - dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n", + dev_err(sdev->dev, + "failed to get src_queue_id ID from source widget %s\n", src_widget->widget->name); return sroute->src_queue_id; } @@ -2963,7 +2963,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_INPUT); if (sroute->dst_queue_id < 0) { - dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n", + dev_err(sdev->dev, + "failed to get dst_queue_id ID from sink widget %s\n", sink_widget->widget->name); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); @@ -2972,10 +2973,11 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * /* Pin 0 format is already set during copier module init */ if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) { - ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, - sroute->src_queue_id); + ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, + sink_widget, sroute); if (ret < 0) { - dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n", + dev_err(sdev->dev, + "failed to set sink format for source %s:%d\n", src_widget->widget->name, sroute->src_queue_id); goto out; } -- cgit v1.2.3 From 00dd4d86ed908e70d912a96ad91d1248ff055b62 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 21 Jun 2024 21:23:07 +0800 Subject: ASoc: tas2781: Add name_prefix as the prefix name of firmwares and kcontrol to support corresponding TAS2563/TAS2781s Add name_prefix as the prefix name of firmwares and kcontrol to support corresponding TAS2563/TAS2781s. name_prefix is not mandatory. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240621132309.564-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781.h | 1 + sound/soc/codecs/tas2781-comlib.c | 13 +++++++++---- sound/soc/codecs/tas2781-i2c.c | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 99ca3e401fd1..cd8ce522b78e 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -108,6 +108,7 @@ struct tasdevice_priv { unsigned char coef_binaryname[64]; unsigned char rca_binaryname[64]; unsigned char dev_name[32]; + const char *name_prefix; unsigned char ndev; unsigned int magic_num; unsigned int chip_id; diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 3aa81514dad7..6db1a260da82 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // -// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// TAS2781 Common functions for HDA and ASoC Audio drivers // -// Copyright 2023 Texas Instruments, Inc. +// Copyright 2023 - 2024 Texas Instruments, Inc. // // Author: Shenghao Ding @@ -277,8 +277,13 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, */ mutex_lock(&tas_priv->codec_lock); - scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", - tas_priv->dev_name, tas_priv->ndev); + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", + tas_priv->name_prefix, tas_priv->dev_name, + tas_priv->ndev); + else + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); tas_priv->codec = codec; ret = request_firmware_nowait(module, FW_ACTION_UEVENT, diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c64d458e524e..4d1a0d836e77 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -579,6 +579,7 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + tas_priv->name_prefix = codec->name_prefix; return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready); } -- cgit v1.2.3 From 65c90df918205bc84f5448550cde76a54dae5f52 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Jun 2024 14:11:16 +0200 Subject: ASoC: Intel: sof_sdw: fix jack detection on ADL-N variant RVP Experimental tests show that JD2_100K is required, otherwise the jack is detected always even with nothing plugged-in. To avoid matching with other known quirks the SKU information is used. Signed-off-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20240624121119.91552-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b646b32dd311..89b92a061489 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -277,6 +277,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "0000000000070000"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2_100K), + }, { .callback = sof_sdw_quirk_cb, .matches = { -- cgit v1.2.3 From e364ffceab9252c06388727250d7583d6e0aea87 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 24 Jun 2024 14:11:17 +0200 Subject: ASoC: Intel: maxim-common: add max_98373_get_tx_mask function Add a helper function max_98373_get_tx_mask() to get tx mask from max98373 ACPI device properties at runtime. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20240624121119.91552-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_maxim_common.c | 56 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index f965b172fa36..fcc3b95e57a4 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -77,19 +77,36 @@ static struct snd_soc_dai_link_component max_98373_components[] = { * According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask * should choose two channels from TDM slots, the LSB of rx mask is left channel * and the other one is right channel. - * - * For tx mask, each codec requires two channels: one for V-sense and the other - * one for I-sense. Must match the device property "maxim,vmon-slot-no" and - * "maxim,imon-slot-no" in ACPI table. */ static const struct { - unsigned int tx; unsigned int rx; } max_98373_tdm_mask[] = { - {.tx = 0x03, .rx = 0x3}, - {.tx = 0x0c, .rx = 0x3}, + {.rx = 0x3}, + {.rx = 0x3}, }; +/* + * The tx mask indicates which channel(s) contains output IV-sense data and + * others should set to Hi-Z. Here we get the channel number from codec's ACPI + * device property "maxim,vmon-slot-no" and "maxim,imon-slot-no" to generate the + * mask. Refer to the max98373_slot_config() function in max98373.c codec driver. + */ +static unsigned int max_98373_get_tx_mask(struct device *dev) +{ + int vmon_slot; + int imon_slot; + + if (device_property_read_u32(dev, "maxim,vmon-slot-no", &vmon_slot)) + vmon_slot = 0; + + if (device_property_read_u32(dev, "maxim,imon-slot-no", &imon_slot)) + imon_slot = 1; + + dev_dbg(dev, "vmon_slot %d imon_slot %d\n", vmon_slot, imon_slot); + + return (0x1 << vmon_slot) | (0x1 << imon_slot); +} + static int max_98373_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -98,6 +115,8 @@ static int max_98373_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; int i; int tdm_slots; + unsigned int tx_mask; + unsigned int tx_mask_used = 0x0; int ret = 0; for_each_rtd_codec_dais(rtd, i, codec_dai) { @@ -117,13 +136,26 @@ static int max_98373_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* get the tx mask from ACPI device properties */ + tx_mask = max_98373_get_tx_mask(codec_dai->dev); + if (!tx_mask) + return -EINVAL; + + if (tx_mask & tx_mask_used) { + dev_err(codec_dai->dev, "invalid tx mask 0x%x, used 0x%x\n", + tx_mask, tx_mask_used); + return -EINVAL; + } + + tx_mask_used |= tx_mask; + /* * check if tdm slot number is too small for channel * allocation */ - if (fls(max_98373_tdm_mask[i].tx) > tdm_slots) { + if (fls(tx_mask) > tdm_slots) { dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n", - fls(max_98373_tdm_mask[i].tx), tdm_slots); + fls(tx_mask), tdm_slots); return -EINVAL; } @@ -134,12 +166,10 @@ static int max_98373_hw_params(struct snd_pcm_substream *substream, } dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n", - max_98373_tdm_mask[i].tx, - max_98373_tdm_mask[i].rx, + tx_mask, max_98373_tdm_mask[i].rx, tdm_slots, params_width(params)); - ret = snd_soc_dai_set_tdm_slot(codec_dai, - max_98373_tdm_mask[i].tx, + ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask, max_98373_tdm_mask[i].rx, tdm_slots, params_width(params)); -- cgit v1.2.3 From 92d5b5930e7d55ca07b483490d6298eee828bbe4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Jun 2024 14:11:18 +0200 Subject: ASoC: Intel: sof_sdw: add quirk for Dell SKU 0B8C MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jack detection needs to rely on JD2, as most other Dell AlderLake-based devices. Closes: https://github.com/thesofproject/linux/issues/5021 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://patch.msgid.link/20240624121119.91552-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 89b92a061489..e94849b84a6b 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -406,6 +406,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { /* No Jack */ .driver_data = (void *)SOF_SDW_TGL_HDMI, }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B8C"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2), + }, { .callback = sof_sdw_quirk_cb, .matches = { -- cgit v1.2.3 From c073f0757663c104271c8749f7e6b19b29c7b8ac Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 24 Jun 2024 14:11:19 +0200 Subject: ASoC: Intel: sof_sdw: select PINCTRL_CS42L43 and SPI_CS42L43 Cs42l43 bridge mode needs the two configurations. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20240624121119.91552-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 4e0586034de4..f1faa5dd2a4f 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -677,6 +677,8 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_CS42L43_SDW select MFD_CS42L43 select MFD_CS42L43_SDW + select PINCTRL_CS42L43 + select SPI_CS42L43 select SND_SOC_CS35L56_SPI select SND_SOC_CS35L56_SDW select SND_SOC_DMIC -- cgit v1.2.3 From 15c99e7fa9e2bb536e813478250d2a609863dcfb Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 25 Jun 2024 05:34:19 +0200 Subject: MAINTAINERS: adjust file entries after adding vendor prefix in sound dtbs Commit ae8fc2948b48 ("ASoC: dt-bindings: add missing vender prefix on filename") renames a few files in Documentation/devicetree/bindings/sound/, but misses to adjust the file entries pointing to those files in MAINTAINERS. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about broken references. Adjust the file entries in NXP SGTL5000 DRIVER and TEXAS INSTRUMENTS AUDIO (ASoC/HDA) DRIVERS. Signed-off-by: Lukas Bulwahn Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20240625033419.149775-1-lukas.bulwahn@redhat.com Signed-off-by: Mark Brown --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c35c03901252..d3b064ce0ba6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16195,7 +16195,7 @@ NXP SGTL5000 DRIVER M: Fabio Estevam L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/sound/sgtl5000.yaml +F: Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml F: sound/soc/codecs/sgtl5000* NXP SJA1105 ETHERNET SWITCH DRIVER @@ -22175,13 +22175,13 @@ M: Baojun Xu L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/sound/tas2552.txt -F: Documentation/devicetree/bindings/sound/tas2562.yaml -F: Documentation/devicetree/bindings/sound/tas2770.yaml -F: Documentation/devicetree/bindings/sound/tas27xx.yaml +F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml +F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml +F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml F: Documentation/devicetree/bindings/sound/ti,pcm1681.yaml F: Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml -F: Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +F: Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml F: Documentation/devicetree/bindings/sound/tlv320aic31xx.txt F: Documentation/devicetree/bindings/sound/tpa6130a2.txt F: include/sound/tas2*.h -- cgit v1.2.3 From 6f9faf1410fe151350ece4a93df30b9e06deec92 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 24 Jun 2024 15:17:27 +0200 Subject: ASoC: codecs: Drop explicit initialization of struct i2c_device_id::driver_data to 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These drivers don't use the driver_data member of struct i2c_device_id, so don't explicitly initialize this member. This prepares putting driver_data in an anonymous union which requires either no initialization or named designators. But it's also a nice cleanup on its own. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/20240624131728.1244053-2-u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw87390.c | 2 +- sound/soc/codecs/aw88261.c | 2 +- sound/soc/codecs/aw88395/aw88395.c | 2 +- sound/soc/codecs/aw88399.c | 2 +- sound/soc/codecs/es8311.c | 5 ++++- sound/soc/codecs/rt1318.c | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 79521ff44001..110009616966 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -445,7 +445,7 @@ static int aw87390_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw87390_i2c_id[] = { - { AW87390_I2C_NAME, 0 }, + { AW87390_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a78ceedd0334..fb99871578c5 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1266,7 +1266,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88261_i2c_id[] = { - { AW88261_I2C_NAME, 0 }, + { AW88261_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index be6ebcb51cca..aea44a199b98 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -560,7 +560,7 @@ static int aw88395_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88395_i2c_id[] = { - { AW88395_I2C_NAME, 0 }, + { AW88395_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 5d8481612eab..8dc2b8aa6832 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -1892,7 +1892,7 @@ static int aw88399_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88399_i2c_id[] = { - { AW88399_I2C_NAME, 0 }, + { AW88399_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id); diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c index b2ee2d04b0e4..f557e33c26ad 100644 --- a/sound/soc/codecs/es8311.c +++ b/sound/soc/codecs/es8311.c @@ -943,7 +943,10 @@ static int es8311_i2c_probe(struct i2c_client *i2c_client) &es8311_dai, 1); } -static const struct i2c_device_id es8311_id[] = { { "es8311", 0 }, {} }; +static const struct i2c_device_id es8311_id[] = { + { "es8311" }, + { } +}; MODULE_DEVICE_TABLE(i2c, es8311_id); static const struct of_device_id es8311_of_match[] = { diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index 4e47db4fc7fb..83b29b441be9 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -1141,7 +1141,7 @@ static const struct regmap_config rt1318_regmap = { }; static const struct i2c_device_id rt1318_i2c_id[] = { - { "rt1318", 0 }, + { "rt1318" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); -- cgit v1.2.3 From 903e85093670c3e1107980b5e5a718682458f316 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:06:14 +0200 Subject: ASoC: codecs: lpass-rx-macro: add missing handling of v2.1 codec We have also v2.1 version of the codec (see 'enum lpass_codec_version'), so handle it as well in all switch cases. Fixes: dbacef05898d ("ASoC: codec: lpass-rx-macro: prepare driver to accomdate new codec versions") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20240625160614.450506-1-krzysztof.kozlowski@linaro.org Reviewed-by: Dmitry Baryshkov Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 83791507ed13..8af86509a3ff 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1601,6 +1601,7 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) case LPASS_CODEC_VERSION_1_1: case LPASS_CODEC_VERSION_1_2: case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: return rx_pre_2_5_is_rw_register(dev, reg); case LPASS_CODEC_VERSION_2_5: case LPASS_CODEC_VERSION_2_6: @@ -3639,6 +3640,7 @@ static int rx_macro_component_probe(struct snd_soc_component *component) case LPASS_CODEC_VERSION_1_1: case LPASS_CODEC_VERSION_1_2: case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: controls = rx_macro_def_snd_controls; num_controls = ARRAY_SIZE(rx_macro_def_snd_controls); widgets = rx_macro_def_dapm_widgets; @@ -3812,6 +3814,7 @@ static int rx_macro_probe(struct platform_device *pdev) case LPASS_CODEC_VERSION_1_1: case LPASS_CODEC_VERSION_1_2: case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: rx->rxn_reg_stride = 0x80; def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults); reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); -- cgit v1.2.3 From 50b8affb5665efa028998ad2c116b1106623409e Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 25 Jun 2024 16:43:03 +0800 Subject: ASoC: rt711-sdca: add GE selected mode control The SDCA spec defines a 'selected_mode' control which can override the 'detected_mode' reported by hardware. This is useful for platform integration as well as in cases where the hardware(e.g. 3.5mm jack cable) is not able to accurately detect the jack type. Signed-off-by: Shuming Fan Tested-by: yung-chuan.liao@linux.intel.com Link: https://patch.msgid.link/20240625084303.2273911-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdca.c | 72 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt711-sdca.h | 1 + 2 files changed, 73 insertions(+) diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 1e8dbfc3ecd9..dd6ccf17afd4 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -81,6 +81,24 @@ static void rt711_sdca_reset(struct rt711_sdca_priv *rt711) RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1); } +static void rt711_sdca_ge_force_jack_type(struct rt711_sdca_priv *rt711, unsigned int det_mode) +{ + switch (det_mode) { + case 0x00: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x0000); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x10, 0x00); + break; + case 0x03: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8000); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x13); + break; + case 0x05: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8400); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x15); + break; + } +} + static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711) { unsigned int val, loop_rc = 0, loop_dc = 0; @@ -248,6 +266,8 @@ static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711) unsigned int det_mode; int ret; + rt711_sdca_ge_force_jack_type(rt711, rt711->ge_mode_override); + /* get detected_mode */ ret = regmap_read(rt711->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0), @@ -790,6 +810,56 @@ static int rt711_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, return changed; } +static int rt711_sdca_ge_select_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val, item; + + val = (rt711->ge_mode_override >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + return 0; +} + +static int rt711_sdca_ge_select_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val, change = 0; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + if (rt711->ge_mode_override != val) { + rt711->ge_mode_override = val; + change = 1; + } + + return change; +} + +static const char * const rt711_sdca_ge_select[] = { + "Auto", + "Headphone", + "Headset", +}; + +static int rt711_sdca_ge_select_values[] = { + 0, + 3, + 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt711_sdca_ge_mode_enum, SND_SOC_NOPM, + 0, 0x7, rt711_sdca_ge_select, rt711_sdca_ge_select_values); + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); @@ -824,6 +894,8 @@ static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv), + SOC_ENUM_EXT("GE49 Selected Mode", rt711_sdca_ge_mode_enum, + rt711_sdca_ge_select_get, rt711_sdca_ge_select_put), }; static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 11d421e8ab2b..15263dcb0314 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -33,6 +33,7 @@ struct rt711_sdca_priv { int hw_ver; bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute; bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute; + unsigned int ge_mode_override; }; /* NID */ -- cgit v1.2.3 From 04f4de6f68eec73595682f32952467591f0cc016 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:58:45 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Drop unused define WSA_MACRO_MUX_INP_MASK2 define is not used. Signed-off-by: Krzysztof Kozlowski Acked-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240625-qcom-audio-wsa-second-speaker-v1-1-f65ffdfc368c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 1e19c2b28a4f..1a36a384cc88 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -235,7 +235,6 @@ #define NUM_INTERPOLATORS 2 #define WSA_NUM_CLKS_MAX 5 #define WSA_MACRO_MCLK_FREQ 19200000 -#define WSA_MACRO_MUX_INP_MASK2 0x38 #define WSA_MACRO_MUX_CFG_OFFSET 0x8 #define WSA_MACRO_MUX_CFG1_OFFSET 0x4 #define WSA_MACRO_RX_COMP_OFFSET 0x40 -- cgit v1.2.3 From 5dcf442bbbcada62631f5a376c44ff794596c2f0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:58:46 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Prepare to accommodate new codec versions The driver for Qualcomm LPASS WSA macro codec was developed and tested on codec v2.1, however v2.5 has significant changes in the registers. The driver correctly works for v2.1 codec, but has issues when running on SoC with v2.5 codec (so starting with SM8450, even though playback works properly on that SoC). Prepare the driver for handling differences in register layouts of newer version. This does not have functional impact on older codec versions, but just: 1. Renames few soc_enums and widgets as v2.1, 2. For registers being different in v2.5, moves the defaults and regmap configuration to new structures, 3. Adds new 'struct wsa_reg_layout' with offsets and masks for few registers, so most of the code can stay unchaged on v2.5, 4. Chooses proper widgets, regmap config and register layout based on version of the codec. Signed-off-by: Krzysztof Kozlowski Acked-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240625-qcom-audio-wsa-second-speaker-v1-2-f65ffdfc368c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 340 ++++++++++++++++++++++++------------- 1 file changed, 218 insertions(+), 122 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 1a36a384cc88..8cdf8f7f64b1 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -44,11 +44,7 @@ #define CDC_WSA_TOP_I2S_CLK (0x00A4) #define CDC_WSA_TOP_I2S_RESET (0x00A8) #define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100) -#define CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(2, 0) -#define CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(5, 3) #define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104) -#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0) -#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3) #define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108) #define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C) #define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110) @@ -173,22 +169,7 @@ #define CDC_WSA_COMPANDER0_CTL5 (0x0594) #define CDC_WSA_COMPANDER0_CTL6 (0x0598) #define CDC_WSA_COMPANDER0_CTL7 (0x059C) -#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) -#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) -#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) -#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) -#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) -#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) -#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) -#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) -#define CDC_WSA_SOFTCLIP0_CRC (0x0600) -#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) -#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) -#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) -#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) -#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) -#define CDC_WSA_SOFTCLIP1_CRC (0x0640) -#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) +/* CDC_WSA_COMPANDER1_CTLx and CDC_WSA_SOFTCLIPx differ per LPASS codec versions */ #define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680) #define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0) #define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0) @@ -217,6 +198,24 @@ #define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760) #define WSA_MAX_OFFSET (0x0760) +/* LPASS codec version <=2.4 register offsets */ +#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) +#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) +#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) +#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) +#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) +#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) +#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) +#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) +#define CDC_WSA_SOFTCLIP0_CRC (0x0600) +#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) +#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP1_CRC (0x0640) +#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) + #define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) @@ -237,8 +236,6 @@ #define WSA_MACRO_MCLK_FREQ 19200000 #define WSA_MACRO_MUX_CFG_OFFSET 0x8 #define WSA_MACRO_MUX_CFG1_OFFSET 0x4 -#define WSA_MACRO_RX_COMP_OFFSET 0x40 -#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 #define WSA_MACRO_RX_PATH_OFFSET 0x80 #define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 #define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C @@ -334,12 +331,34 @@ enum { WSA_MACRO_MAX_DAIS, }; +/** + * struct wsa_reg_layout - Register layout differences + * @rx_intx_1_mix_inp0_sel_mask: register mask for RX_INTX_1_MIX_INP0_SEL_MASK + * @rx_intx_1_mix_inp1_sel_mask: register mask for RX_INTX_1_MIX_INP1_SEL_MASK + * @rx_intx_1_mix_inp2_sel_mask: register mask for RX_INTX_1_MIX_INP2_SEL_MASK + * @rx_intx_2_sel_mask: register mask for RX_INTX_2_SEL_MASK + * @compander1_reg_offset: offset between compander registers (compander1 - compander0) + * @softclip0_reg_base: base address of softclip0 register + * @softclip1_reg_offset: offset between compander registers (softclip1 - softclip0) + */ +struct wsa_reg_layout { + unsigned int rx_intx_1_mix_inp0_sel_mask; + unsigned int rx_intx_1_mix_inp1_sel_mask; + unsigned int rx_intx_1_mix_inp2_sel_mask; + unsigned int rx_intx_2_sel_mask; + unsigned int compander1_reg_offset; + unsigned int softclip0_reg_base; + unsigned int softclip1_reg_offset; +}; + struct wsa_macro { struct device *dev; int comp_enabled[WSA_MACRO_COMP_MAX]; int ec_hq[WSA_MACRO_RX1 + 1]; u16 prim_int_users[WSA_MACRO_RX1 + 1]; u16 wsa_mclk_users; + enum lpass_codec_version codec_version; + const struct wsa_reg_layout *reg_layout; unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; int rx_port_value[WSA_MACRO_RX_MAX]; @@ -358,13 +377,23 @@ struct wsa_macro { }; #define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) +static const struct wsa_reg_layout wsa_codec_v2_1 = { + .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0), + .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3), + .rx_intx_1_mix_inp2_sel_mask = GENMASK(5, 3), + .rx_intx_2_sel_mask = GENMASK(2, 0), + .compander1_reg_offset = 0x40, + .softclip0_reg_base = 0x600, + .softclip1_reg_offset = 0x40, +}; + static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); -static const char *const rx_text[] = { +static const char *const rx_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" }; -static const char *const rx_mix_text[] = { +static const char *const rx_mix_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" }; @@ -389,68 +418,68 @@ static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, wsa_macro_ear_spkr_pa_gain_text); /* RX INT0 */ -static const struct soc_enum rx0_prim_inp0_chain_enum = +static const struct soc_enum rx0_prim_inp0_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, - 0, 7, rx_text); + 0, 7, rx_text_v2_1); -static const struct soc_enum rx0_prim_inp1_chain_enum = +static const struct soc_enum rx0_prim_inp1_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx0_prim_inp2_chain_enum = +static const struct soc_enum rx0_prim_inp2_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx0_mix_chain_enum = +static const struct soc_enum rx0_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, - 0, 5, rx_mix_text); + 0, 5, rx_mix_text_v2_1); static const struct soc_enum rx0_sidetone_mix_enum = SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); -static const struct snd_kcontrol_new rx0_prim_inp0_mux = - SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_1); -static const struct snd_kcontrol_new rx0_prim_inp1_mux = - SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_1); -static const struct snd_kcontrol_new rx0_prim_inp2_mux = - SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_1); -static const struct snd_kcontrol_new rx0_mix_mux = - SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); +static const struct snd_kcontrol_new rx0_mix_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1); static const struct snd_kcontrol_new rx0_sidetone_mix_mux = SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); /* RX INT1 */ -static const struct soc_enum rx1_prim_inp0_chain_enum = +static const struct soc_enum rx1_prim_inp0_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, - 0, 7, rx_text); + 0, 7, rx_text_v2_1); -static const struct soc_enum rx1_prim_inp1_chain_enum = +static const struct soc_enum rx1_prim_inp1_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx1_prim_inp2_chain_enum = +static const struct soc_enum rx1_prim_inp2_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx1_mix_chain_enum = +static const struct soc_enum rx1_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, - 0, 5, rx_mix_text); + 0, 5, rx_mix_text_v2_1); -static const struct snd_kcontrol_new rx1_prim_inp0_mux = - SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); +static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1); -static const struct snd_kcontrol_new rx1_prim_inp1_mux = - SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); +static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_1); -static const struct snd_kcontrol_new rx1_prim_inp2_mux = - SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); +static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_1); -static const struct snd_kcontrol_new rx1_mix_mux = - SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); +static const struct snd_kcontrol_new rx1_mix_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1); static const struct soc_enum rx_mix_ec0_enum = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, @@ -489,14 +518,6 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, - { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, { CDC_WSA_INTR_CTRL_CFG, 0x00}, { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, @@ -561,18 +582,6 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_COMPANDER0_CTL5, 0x00}, { CDC_WSA_COMPANDER0_CTL6, 0x01}, { CDC_WSA_COMPANDER0_CTL7, 0x28}, - { CDC_WSA_COMPANDER1_CTL0, 0x60}, - { CDC_WSA_COMPANDER1_CTL1, 0xDB}, - { CDC_WSA_COMPANDER1_CTL2, 0xFF}, - { CDC_WSA_COMPANDER1_CTL3, 0x35}, - { CDC_WSA_COMPANDER1_CTL4, 0xFF}, - { CDC_WSA_COMPANDER1_CTL5, 0x00}, - { CDC_WSA_COMPANDER1_CTL6, 0x01}, - { CDC_WSA_COMPANDER1_CTL7, 0x28}, - { CDC_WSA_SOFTCLIP0_CRC, 0x00}, - { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, - { CDC_WSA_SOFTCLIP1_CRC, 0x00}, - { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, @@ -597,6 +606,29 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, }; +static const struct reg_default wsa_defaults_v2_1[] = { + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, +}; + static bool wsa_is_wronly_register(struct device *dev, unsigned int reg) { @@ -610,6 +642,26 @@ static bool wsa_is_wronly_register(struct device *dev, return false; } +static bool wsa_is_rw_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL0: + case CDC_WSA_COMPANDER1_CTL1: + case CDC_WSA_COMPANDER1_CTL2: + case CDC_WSA_COMPANDER1_CTL3: + case CDC_WSA_COMPANDER1_CTL4: + case CDC_WSA_COMPANDER1_CTL5: + case CDC_WSA_COMPANDER1_CTL7: + case CDC_WSA_SOFTCLIP0_CRC: + case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_WSA_SOFTCLIP1_CRC: + case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + return true; + } + + return false; +} + static bool wsa_is_rw_register(struct device *dev, unsigned int reg) { switch (reg) { @@ -701,17 +753,6 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) case CDC_WSA_COMPANDER0_CTL4: case CDC_WSA_COMPANDER0_CTL5: case CDC_WSA_COMPANDER0_CTL7: - case CDC_WSA_COMPANDER1_CTL0: - case CDC_WSA_COMPANDER1_CTL1: - case CDC_WSA_COMPANDER1_CTL2: - case CDC_WSA_COMPANDER1_CTL3: - case CDC_WSA_COMPANDER1_CTL4: - case CDC_WSA_COMPANDER1_CTL5: - case CDC_WSA_COMPANDER1_CTL7: - case CDC_WSA_SOFTCLIP0_CRC: - case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: - case CDC_WSA_SOFTCLIP1_CRC: - case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL: case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0: case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL: @@ -727,7 +768,7 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) return true; } - return false; + return wsa_is_rw_register_v2_1(dev, reg); } static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) @@ -741,6 +782,16 @@ static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) return ret; } +static bool wsa_is_readable_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL6: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + static bool wsa_is_readable_register(struct device *dev, unsigned int reg) { switch (reg) { @@ -750,7 +801,6 @@ static bool wsa_is_readable_register(struct device *dev, unsigned int reg) case CDC_WSA_INTR_CTRL_PIN1_STATUS0: case CDC_WSA_INTR_CTRL_PIN2_STATUS0: case CDC_WSA_COMPANDER0_CTL6: - case CDC_WSA_COMPANDER1_CTL6: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: @@ -764,7 +814,17 @@ static bool wsa_is_readable_register(struct device *dev, unsigned int reg) return true; } - return wsa_is_rw_register(dev, reg); + return wsa_is_readable_register_v2_1(dev, reg); +} + +static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL6: + return true; + } + + return false; } static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) @@ -774,7 +834,6 @@ static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) case CDC_WSA_INTR_CTRL_PIN1_STATUS0: case CDC_WSA_INTR_CTRL_PIN2_STATUS0: case CDC_WSA_COMPANDER0_CTL6: - case CDC_WSA_COMPANDER1_CTL6: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: @@ -787,7 +846,8 @@ static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: return true; } - return false; + + return wsa_is_volatile_register_v2_1(dev, reg); } static const struct regmap_config wsa_regmap_config = { @@ -796,8 +856,7 @@ static const struct regmap_config wsa_regmap_config = { .val_bits = 32, /* 8 but with 32 bit read/write */ .reg_stride = 4, .cache_type = REGCACHE_FLAT, - .reg_defaults = wsa_defaults, - .num_reg_defaults = ARRAY_SIZE(wsa_defaults), + /* .reg_defaults and .num_reg_defaults set in probe() */ .max_register = WSA_MAX_OFFSET, .writeable_reg = wsa_is_writeable_register, .volatile_reg = wsa_is_volatile_register, @@ -871,11 +930,11 @@ static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, for (j = 0; j < NUM_INTERPOLATORS; j++) { int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); - inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask); + inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, + wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask); inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp2_sel_mask); if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || @@ -916,7 +975,7 @@ static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; for (j = 0; j < NUM_INTERPOLATORS; j++) { int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_2_SEL_MASK); + wsa->reg_layout->rx_intx_2_sel_mask); if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL + @@ -1299,7 +1358,7 @@ static int wsa_macro_config_compander(struct snd_soc_component *component, return 0; comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 + - (comp * WSA_MACRO_RX_COMP_OFFSET); + (comp * wsa->reg_layout->compander1_reg_offset); rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 + (comp * WSA_MACRO_RX_PATH_OFFSET); @@ -1345,8 +1404,8 @@ static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, int path, bool enable) { - u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC + - (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u16 softclip_clk_reg = wsa->reg_layout->softclip0_reg_base + + (path * wsa->reg_layout->softclip1_reg_offset); u8 softclip_mux_mask = (1 << path); u8 softclip_mux_value = (1 << path); @@ -1391,7 +1450,7 @@ static int wsa_macro_config_softclip(struct snd_soc_component *component, return 0; softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + - (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + (softclip_path * wsa->reg_layout->softclip1_reg_offset); if (SND_SOC_DAPM_EVENT_ON(event)) { /* Enable Softclip clock and mux */ @@ -1416,6 +1475,7 @@ static int wsa_macro_config_softclip(struct snd_soc_component *component, static bool wsa_macro_adie_lb(struct snd_soc_component *component, int interp_idx) { + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); u16 int_mux_cfg0, int_mux_cfg1; u8 int_n_inp0, int_n_inp1, int_n_inp2; @@ -1423,19 +1483,19 @@ static bool wsa_macro_adie_lb(struct snd_soc_component *component, int_mux_cfg1 = int_mux_cfg0 + 4; int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask); if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || int_n_inp0 == INTn_1_INP_SEL_DEC1) return true; int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask); if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || int_n_inp1 == INTn_1_INP_SEL_DEC1) return true; int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp2_sel_mask); if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || int_n_inp2 == INTn_1_INP_SEL_DEC1) return true; @@ -2073,19 +2133,6 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux), - SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux), - SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux), - SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, - 0, &rx0_mix_mux, wsa_macro_enable_mix_path, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux), - SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux), - SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux), - SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, - 0, &rx1_mix_mux, wsa_macro_enable_mix_path, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0, wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0, @@ -2136,6 +2183,21 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_1[] = { + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_1), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, + 0, &rx0_mix_mux_v2_1, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_1), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, + 0, &rx1_mix_mux_v2_1, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + static const struct snd_soc_dapm_route wsa_audio_map[] = { /* VI Feedback */ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, @@ -2281,7 +2343,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) static int wsa_macro_component_probe(struct snd_soc_component *comp) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp); struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp); + const struct snd_soc_dapm_widget *widgets; + unsigned int num_widgets; snd_soc_component_init_regmap(comp, wsa->regmap); @@ -2298,7 +2363,10 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp) wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1); - return 0; + widgets = wsa_macro_dapm_widgets_v2_1; + num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1); + + return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); } static int swclk_gate_enable(struct clk_hw *hw) @@ -2378,10 +2446,12 @@ static const struct snd_soc_component_driver wsa_macro_component_drv = { static int wsa_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct reg_default *reg_defaults; + struct regmap_config *reg_config; struct wsa_macro *wsa; kernel_ulong_t flags; void __iomem *base; - int ret; + int ret, def_count; flags = (kernel_ulong_t)device_get_match_data(dev); @@ -2415,10 +2485,36 @@ static int wsa_macro_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config); + wsa->codec_version = lpass_macro_get_codec_version(); + switch (wsa->codec_version) { + default: + wsa->reg_layout = &wsa_codec_v2_1; + def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1); + reg_defaults = devm_kmalloc_array(dev, def_count, + sizeof(*reg_defaults), + GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); + memcpy(®_defaults[ARRAY_SIZE(wsa_defaults)], + wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1)); + break; + } + + reg_config = devm_kmemdup(dev, &wsa_regmap_config, + sizeof(*reg_config), GFP_KERNEL); + if (!reg_config) + return -ENOMEM; + + reg_config->reg_defaults = reg_defaults; + reg_config->num_reg_defaults = def_count; + + wsa->regmap = devm_regmap_init_mmio(dev, base, reg_config); if (IS_ERR(wsa->regmap)) return PTR_ERR(wsa->regmap); + devm_kfree(dev, reg_config); + devm_kfree(dev, reg_defaults); dev_set_drvdata(dev, wsa); wsa->dev = dev; -- cgit v1.2.3 From 727de4fbc5466c7150482b532f2b7f7e514134f3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:58:47 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Correct support for newer v2.5 version Starting with v2.5 of Qualcomm LPASS Codec, few registers in the WSA macro block change. Bring proper support for this v2.5 and newer versions, to fix second speaker playback (speaker was silent). Signed-off-by: Krzysztof Kozlowski Acked-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240625-qcom-audio-wsa-second-speaker-v1-3-f65ffdfc368c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 309 ++++++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 8cdf8f7f64b1..8351f0974e6a 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -216,6 +216,47 @@ #define CDC_WSA_SOFTCLIP1_CRC (0x0640) #define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) +/* LPASS codec version >=2.5 register offsets */ +#define CDC_WSA_TOP_FS_UNGATE (0x00AC) +#define CDC_WSA_TOP_GRP_SEL (0x00B0) +#define CDC_WSA_TOP_FS_UNGATE2 (0x00DC) +#define CDC_2_5_WSA_COMPANDER0_CTL8 (0x05A0) +#define CDC_2_5_WSA_COMPANDER0_CTL9 (0x05A4) +#define CDC_2_5_WSA_COMPANDER0_CTL10 (0x05A8) +#define CDC_2_5_WSA_COMPANDER0_CTL11 (0x05AC) +#define CDC_2_5_WSA_COMPANDER0_CTL12 (0x05B0) +#define CDC_2_5_WSA_COMPANDER0_CTL13 (0x05B4) +#define CDC_2_5_WSA_COMPANDER0_CTL14 (0x05B8) +#define CDC_2_5_WSA_COMPANDER0_CTL15 (0x05BC) +#define CDC_2_5_WSA_COMPANDER0_CTL16 (0x05C0) +#define CDC_2_5_WSA_COMPANDER0_CTL17 (0x05C4) +#define CDC_2_5_WSA_COMPANDER0_CTL18 (0x05C8) +#define CDC_2_5_WSA_COMPANDER0_CTL19 (0x05CC) +#define CDC_2_5_WSA_COMPANDER1_CTL0 (0x05E0) +#define CDC_2_5_WSA_COMPANDER1_CTL1 (0x05E4) +#define CDC_2_5_WSA_COMPANDER1_CTL2 (0x05E8) +#define CDC_2_5_WSA_COMPANDER1_CTL3 (0x05EC) +#define CDC_2_5_WSA_COMPANDER1_CTL4 (0x05F0) +#define CDC_2_5_WSA_COMPANDER1_CTL5 (0x05F4) +#define CDC_2_5_WSA_COMPANDER1_CTL6 (0x05F8) +#define CDC_2_5_WSA_COMPANDER1_CTL7 (0x05FC) +#define CDC_2_5_WSA_COMPANDER1_CTL8 (0x0600) +#define CDC_2_5_WSA_COMPANDER1_CTL9 (0x0604) +#define CDC_2_5_WSA_COMPANDER1_CTL10 (0x0608) +#define CDC_2_5_WSA_COMPANDER1_CTL11 (0x060C) +#define CDC_2_5_WSA_COMPANDER1_CTL12 (0x0610) +#define CDC_2_5_WSA_COMPANDER1_CTL13 (0x0614) +#define CDC_2_5_WSA_COMPANDER1_CTL14 (0x0618) +#define CDC_2_5_WSA_COMPANDER1_CTL15 (0x061C) +#define CDC_2_5_WSA_COMPANDER1_CTL16 (0x0620) +#define CDC_2_5_WSA_COMPANDER1_CTL17 (0x0624) +#define CDC_2_5_WSA_COMPANDER1_CTL18 (0x0628) +#define CDC_2_5_WSA_COMPANDER1_CTL19 (0x062C) +#define CDC_2_5_WSA_SOFTCLIP0_CRC (0x0640) +#define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644) +#define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660) +#define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664) + #define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) @@ -387,16 +428,34 @@ static const struct wsa_reg_layout wsa_codec_v2_1 = { .softclip1_reg_offset = 0x40, }; +static const struct wsa_reg_layout wsa_codec_v2_5 = { + .rx_intx_1_mix_inp0_sel_mask = GENMASK(3, 0), + .rx_intx_1_mix_inp1_sel_mask = GENMASK(7, 4), + .rx_intx_1_mix_inp2_sel_mask = GENMASK(7, 4), + .rx_intx_2_sel_mask = GENMASK(3, 0), + .compander1_reg_offset = 0x60, + .softclip0_reg_base = 0x640, + .softclip1_reg_offset = 0x20, +}; + static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); static const char *const rx_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" }; +static const char *const rx_text_v2_5[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8", "DEC0", "DEC1" +}; + static const char *const rx_mix_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" }; +static const char *const rx_mix_text_v2_5[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8" +}; + static const char *const rx_mix_ec_text[] = { "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" }; @@ -434,6 +493,22 @@ static const struct soc_enum rx0_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0, 5, rx_mix_text_v2_1); +static const struct soc_enum rx0_prim_inp0_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 12, rx_text_v2_5); + +static const struct soc_enum rx0_prim_inp1_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx0_prim_inp2_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx0_mix_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 10, rx_mix_text_v2_5); + static const struct soc_enum rx0_sidetone_mix_enum = SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); @@ -449,6 +524,18 @@ static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 = static const struct snd_kcontrol_new rx0_mix_mux_v2_1 = SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1); +static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx0_mix_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_5); + static const struct snd_kcontrol_new rx0_sidetone_mix_mux = SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); @@ -469,6 +556,22 @@ static const struct soc_enum rx1_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0, 5, rx_mix_text_v2_1); +static const struct soc_enum rx1_prim_inp0_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 12, rx_text_v2_5); + +static const struct soc_enum rx1_prim_inp1_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx1_prim_inp2_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx1_mix_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 10, rx_mix_text_v2_5); + static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 = SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1); @@ -481,6 +584,18 @@ static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 = static const struct snd_kcontrol_new rx1_mix_mux_v2_1 = SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1); +static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx1_mix_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_5); + static const struct soc_enum rx_mix_ec0_enum = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0, 3, rx_mix_ec_text); @@ -629,6 +744,56 @@ static const struct reg_default wsa_defaults_v2_1[] = { { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, }; +static const struct reg_default wsa_defaults_v2_5[] = { + { CDC_WSA_TOP_FS_UNGATE, 0xFF}, + { CDC_WSA_TOP_GRP_SEL, 0x08}, + { CDC_WSA_TOP_FS_UNGATE2, 0x1F}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_2_5_WSA_COMPANDER0_CTL8, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL9, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL10, 0x06}, + { CDC_2_5_WSA_COMPANDER0_CTL11, 0x12}, + { CDC_2_5_WSA_COMPANDER0_CTL12, 0x1E}, + { CDC_2_5_WSA_COMPANDER0_CTL13, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL14, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL15, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL16, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL17, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL18, 0x2A}, + { CDC_2_5_WSA_COMPANDER0_CTL19, 0x16}, + { CDC_2_5_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_2_5_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_2_5_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_2_5_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_2_5_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_2_5_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_2_5_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_2_5_WSA_COMPANDER1_CTL8, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL9, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL10, 0x06}, + { CDC_2_5_WSA_COMPANDER1_CTL11, 0x12}, + { CDC_2_5_WSA_COMPANDER1_CTL12, 0x1E}, + { CDC_2_5_WSA_COMPANDER1_CTL13, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL14, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL15, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL16, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL17, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL18, 0x2A}, + { CDC_2_5_WSA_COMPANDER1_CTL19, 0x16}, + { CDC_2_5_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_2_5_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, +}; + static bool wsa_is_wronly_register(struct device *dev, unsigned int reg) { @@ -662,8 +827,57 @@ static bool wsa_is_rw_register_v2_1(struct device *dev, unsigned int reg) return false; } +static bool wsa_is_rw_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_TOP_FS_UNGATE: + case CDC_WSA_TOP_GRP_SEL: + case CDC_WSA_TOP_FS_UNGATE2: + case CDC_2_5_WSA_COMPANDER0_CTL8: + case CDC_2_5_WSA_COMPANDER0_CTL9: + case CDC_2_5_WSA_COMPANDER0_CTL10: + case CDC_2_5_WSA_COMPANDER0_CTL11: + case CDC_2_5_WSA_COMPANDER0_CTL12: + case CDC_2_5_WSA_COMPANDER0_CTL13: + case CDC_2_5_WSA_COMPANDER0_CTL14: + case CDC_2_5_WSA_COMPANDER0_CTL15: + case CDC_2_5_WSA_COMPANDER0_CTL16: + case CDC_2_5_WSA_COMPANDER0_CTL17: + case CDC_2_5_WSA_COMPANDER0_CTL18: + case CDC_2_5_WSA_COMPANDER0_CTL19: + case CDC_2_5_WSA_COMPANDER1_CTL0: + case CDC_2_5_WSA_COMPANDER1_CTL1: + case CDC_2_5_WSA_COMPANDER1_CTL2: + case CDC_2_5_WSA_COMPANDER1_CTL3: + case CDC_2_5_WSA_COMPANDER1_CTL4: + case CDC_2_5_WSA_COMPANDER1_CTL5: + case CDC_2_5_WSA_COMPANDER1_CTL7: + case CDC_2_5_WSA_COMPANDER1_CTL8: + case CDC_2_5_WSA_COMPANDER1_CTL9: + case CDC_2_5_WSA_COMPANDER1_CTL10: + case CDC_2_5_WSA_COMPANDER1_CTL11: + case CDC_2_5_WSA_COMPANDER1_CTL12: + case CDC_2_5_WSA_COMPANDER1_CTL13: + case CDC_2_5_WSA_COMPANDER1_CTL14: + case CDC_2_5_WSA_COMPANDER1_CTL15: + case CDC_2_5_WSA_COMPANDER1_CTL16: + case CDC_2_5_WSA_COMPANDER1_CTL17: + case CDC_2_5_WSA_COMPANDER1_CTL18: + case CDC_2_5_WSA_COMPANDER1_CTL19: + case CDC_2_5_WSA_SOFTCLIP0_CRC: + case CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_2_5_WSA_SOFTCLIP1_CRC: + case CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + return true; + } + + return false; +} + static bool wsa_is_rw_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + switch (reg) { case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL: case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL: @@ -768,6 +982,9 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) return true; } + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_rw_register_v2_5(dev, reg); + return wsa_is_rw_register_v2_1(dev, reg); } @@ -792,8 +1009,20 @@ static bool wsa_is_readable_register_v2_1(struct device *dev, unsigned int reg) return wsa_is_rw_register(dev, reg); } +static bool wsa_is_readable_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_WSA_COMPANDER1_CTL6: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + static bool wsa_is_readable_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + switch (reg) { case CDC_WSA_INTR_CTRL_CLR_COMMIT: case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: @@ -814,6 +1043,9 @@ static bool wsa_is_readable_register(struct device *dev, unsigned int reg) return true; } + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_readable_register_v2_5(dev, reg); + return wsa_is_readable_register_v2_1(dev, reg); } @@ -827,8 +1059,20 @@ static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg) return false; } +static bool wsa_is_volatile_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_WSA_COMPANDER1_CTL6: + return true; + } + + return false; +} + static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + /* Update volatile list for rx/tx macros */ switch (reg) { case CDC_WSA_INTR_CTRL_PIN1_STATUS0: @@ -847,6 +1091,9 @@ static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) return true; } + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_volatile_register_v2_5(dev, reg); + return wsa_is_volatile_register_v2_1(dev, reg); } @@ -2198,6 +2445,21 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_1[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_5[] = { + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_5), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, + 0, &rx0_mix_mux_v2_5, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_5), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, + 0, &rx1_mix_mux_v2_5, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + static const struct snd_soc_dapm_route wsa_audio_map[] = { /* VI Feedback */ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, @@ -2363,8 +2625,25 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp) wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1); - widgets = wsa_macro_dapm_widgets_v2_1; - num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1); + switch (wsa->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + widgets = wsa_macro_dapm_widgets_v2_1; + num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1); + break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + widgets = wsa_macro_dapm_widgets_v2_5; + num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5); + break; + default: + return -EINVAL; + } return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); } @@ -2487,7 +2766,11 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa->codec_version = lpass_macro_get_codec_version(); switch (wsa->codec_version) { - default: + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: wsa->reg_layout = &wsa_codec_v2_1; def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1); reg_defaults = devm_kmalloc_array(dev, def_count, @@ -2499,6 +2782,26 @@ static int wsa_macro_probe(struct platform_device *pdev) memcpy(®_defaults[ARRAY_SIZE(wsa_defaults)], wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1)); break; + + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + wsa->reg_layout = &wsa_codec_v2_5; + def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5); + reg_defaults = devm_kmalloc_array(dev, def_count, + sizeof(*reg_defaults), + GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); + memcpy(®_defaults[ARRAY_SIZE(wsa_defaults)], + wsa_defaults_v2_5, sizeof(wsa_defaults_v2_5)); + break; + + default: + dev_err(wsa->dev, "Unsupported Codec version (%d)\n", wsa->codec_version); + return -EINVAL; } reg_config = devm_kmemdup(dev, &wsa_regmap_config, -- cgit v1.2.3 From ebc1a54051b58d1aa3a8855faac8bf16b88b8af7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:57:35 +0200 Subject: ASoC: codecs: lpass-macro: Gracefully handle unknown version Qualcomm LPASS macro codec driver parses registers in order to detect version of the codec. It recognizes codecs v2.0 - v2.8, however we know that there are earlier versions and 'enum lpass_codec_version' has also v1.0, v1.1 and v1.2. If by any chance we run on unrecognized version, driver will use random value from the stack as the codec version. Fix it by mapping such cases to an enum of value 0: LPASS_CODEC_VERSION_UNKNOWN. Fixes: 378918d59181 ("ASoC: codecs: lpass-macro: add helpers to get codec version") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240625165736.722106-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.h | 3 ++- sound/soc/codecs/lpass-va-macro.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index f6f1bfe8eb77..94697d0ba8c9 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -19,7 +19,8 @@ enum lpass_version { }; enum lpass_codec_version { - LPASS_CODEC_VERSION_1_0 = 1, + LPASS_CODEC_VERSION_UNKNOWN, + LPASS_CODEC_VERSION_1_0, LPASS_CODEC_VERSION_1_1, LPASS_CODEC_VERSION_1_2, LPASS_CODEC_VERSION_2_0, diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index b0006f4ce8a2..b852cc7ffad9 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1463,7 +1463,8 @@ undefined_rate: static void va_macro_set_lpass_codec_version(struct va_macro *va) { - int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0, version; + int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0; + int version = LPASS_CODEC_VERSION_UNKNOWN; regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &core_id_0); regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &core_id_1); -- cgit v1.2.3 From 06462d6f3fb3c445b5abac7b134d9fe9e361113f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 25 Jun 2024 18:57:36 +0200 Subject: ASoC: codecs: lpass-macro: Use enum for handling codec version Replace 'int' with proper 'enum lpass_codec_version' in every place which handles the parsed codec version (not raw register values!) to be explicit about contents of the variable. This makes code easier to read and compilers could check missing switch cases. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240625165736.722106-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.c | 8 ++++---- sound/soc/codecs/lpass-macro-common.h | 4 ++-- sound/soc/codecs/lpass-rx-macro.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index 8b038a99a8f9..6e3b8d0897dd 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -12,7 +12,7 @@ #include "lpass-macro-common.h" static DEFINE_MUTEX(lpass_codec_mutex); -static int lpass_codec_version; +static enum lpass_codec_version lpass_codec_version; struct lpass_macro *lpass_macro_pds_init(struct device *dev) { @@ -69,7 +69,7 @@ void lpass_macro_pds_exit(struct lpass_macro *pds) } EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); -void lpass_macro_set_codec_version(int version) +void lpass_macro_set_codec_version(enum lpass_codec_version version) { mutex_lock(&lpass_codec_mutex); lpass_codec_version = version; @@ -77,9 +77,9 @@ void lpass_macro_set_codec_version(int version) } EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); -int lpass_macro_get_codec_version(void) +enum lpass_codec_version lpass_macro_get_codec_version(void) { - int ver; + enum lpass_codec_version ver; mutex_lock(&lpass_codec_mutex); ver = lpass_codec_version; diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index 94697d0ba8c9..3aa9737f2737 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -38,8 +38,8 @@ struct lpass_macro { struct lpass_macro *lpass_macro_pds_init(struct device *dev); void lpass_macro_pds_exit(struct lpass_macro *pds); -void lpass_macro_set_codec_version(int version); -int lpass_macro_get_codec_version(void); +void lpass_macro_set_codec_version(enum lpass_codec_version version); +enum lpass_codec_version lpass_macro_get_codec_version(void); static inline const char *lpass_macro_get_codec_version_string(int version) { diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 8af86509a3ff..9c4f0763675d 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -642,7 +642,7 @@ struct rx_macro { int rx_mclk_users; int clsh_users; int rx_mclk_cnt; - int codec_version; + enum lpass_codec_version codec_version; int rxn_reg_stride; bool is_ear_mode_on; bool hph_pwr_mode; -- cgit v1.2.3 From fafc20ded3f4659873c83c2af6d389983d480994 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Jun 2024 06:02:26 +0000 Subject: ASoC: audio-graph-port: add link-trigger-order Sound Card need to consider/adjust HW control ordering based on the combination of CPU/Codec. The controlling feature is already supported on ASoC, but Simple Audio Card / Audio Graph Card still not support it. Let's support it. Cc: Maxim Kochetkov Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87sexizojx.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- .../bindings/sound/audio-graph-port.yaml | 9 ++++++++ include/dt-bindings/sound/audio-graph.h | 26 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 include/dt-bindings/sound/audio-graph.h diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 28b27e7e45de..d1cbfc5edd3a 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -25,6 +25,15 @@ definitions: capture-only: description: port connection used only for capture $ref: /schemas/types.yaml#/definitions/flag + link-trigger-order: + description: trigger order for both start/stop + $ref: /schemas/types.yaml#/definitions/uint32-array + link-trigger-order-start: + description: trigger order for start + $ref: /schemas/types.yaml#/definitions/uint32-array + link-trigger-order-stop: + description: trigger order for stop + $ref: /schemas/types.yaml#/definitions/uint32-array endpoint-base: allOf: diff --git a/include/dt-bindings/sound/audio-graph.h b/include/dt-bindings/sound/audio-graph.h new file mode 100644 index 000000000000..bdb70c6b7332 --- /dev/null +++ b/include/dt-bindings/sound/audio-graph.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * audio-graph.h + * + * Copyright (c) 2024 Kuninori Morimoto + */ +#ifndef __AUDIO_GRAPH_H +#define __AUDIO_GRAPH_H + +/* + * used in + * link-trigger-order + * link-trigger-order-start + * link-trigger-order-stop + * + * default is + * link-trigger-order = ; + */ +#define SND_SOC_TRIGGER_LINK 0 +#define SND_SOC_TRIGGER_COMPONENT 1 +#define SND_SOC_TRIGGER_DAI 2 +#define SND_SOC_TRIGGER_SIZE 3 /* shoud be last */ + +#endif /* __AUDIO_GRAPH_H */ -- cgit v1.2.3 From 5d9cacdccf17bd33dac3ea378324650159c2a863 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Jun 2024 06:02:33 +0000 Subject: ASoC: simple-card-utils: add link-trigger-order support Some Sound Card might need special trigger ordering which is based on CPU/Codec connection. It is already supported on ASoC, but Simple Audio Card / Audio Graph Card still not support it. Let's support it. Cc: Maxim Kochetkov Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87r0d2zojq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 4 ++ sound/soc/generic/simple-card-utils.c | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 0a6435ac5c5f..3360d9eab068 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -199,6 +199,10 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep, void graph_util_parse_link_direction(struct device_node *np, bool *is_playback_only, bool *is_capture_only); +void graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + enum snd_soc_trigger_order *trigger_start, + enum snd_soc_trigger_order *trigger_stop); #ifdef DEBUG static inline void simple_util_debug_dai(struct simple_util_priv *priv, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index dcd0569157ce..a18de86b3c88 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -4,6 +4,7 @@ // // Copyright (c) 2016 Kuninori Morimoto +#include #include #include #include @@ -1156,6 +1157,76 @@ void graph_util_parse_link_direction(struct device_node *np, } EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); +static enum snd_soc_trigger_order +__graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + const char *prop) +{ + u32 val[SND_SOC_TRIGGER_SIZE]; + int ret; + + ret = of_property_read_u32_array(np, prop, val, SND_SOC_TRIGGER_SIZE); + if (ret == 0) { + struct device *dev = simple_priv_to_dev(priv); + u32 order = (val[0] << 8) + + (val[1] << 4) + + (val[2]); + + switch (order) { + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_COMPONENT << 4) + + (SND_SOC_TRIGGER_DAI): + return SND_SOC_TRIGGER_ORDER_DEFAULT; + + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_DAI << 4) + + (SND_SOC_TRIGGER_COMPONENT): + return SND_SOC_TRIGGER_ORDER_LDC; + + default: + dev_err(dev, "unsupported trigger order [0x%x]\n", order); + } + } + + /* SND_SOC_TRIGGER_ORDER_MAX means error */ + return SND_SOC_TRIGGER_ORDER_MAX; +} + +void graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + enum snd_soc_trigger_order *trigger_start, + enum snd_soc_trigger_order *trigger_stop) +{ + static enum snd_soc_trigger_order order; + + /* + * We can use it like below + * + * #include + * + * link-trigger-order = ; + */ + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) { + *trigger_start = order; + *trigger_stop = order; + } + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-start"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_start = order; + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-stop"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_stop = order; + + return; +} +EXPORT_SYMBOL_GPL(graph_util_parse_trigger_order); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto "); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); -- cgit v1.2.3 From 8696d732f1c804312d44806242d4a91c49a7a154 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Jun 2024 06:02:39 +0000 Subject: ASoC: simple-audio-card: add link-trigger-order support Some Sound Card might need special trigger ordering which is based on CPU/Codec connection. It is already supported on ASoC, but Simple Audio Card still not yet support it. Let's support it. Cc: Maxim Kochetkov Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87plsmzojk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 2de5e6efe947..edbb6322e9be 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -176,6 +176,8 @@ static int simple_link_init(struct simple_util_priv *priv, struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *node = of_get_parent(cpu); + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; bool playback_only = 0, capture_only = 0; int ret; @@ -198,9 +200,17 @@ static int simple_link_init(struct simple_util_priv *priv, of_property_read_u32(codec, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(codec, PREFIX "mclk-fs", &dai_props->mclk_fs); + graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, node, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, codec, &trigger_start, &trigger_stop); + dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; + dai_link->init = simple_util_dai_init; dai_link->ops = &simple_ops; -- cgit v1.2.3 From 4d4125d8f5e19476c006014d5530b8127ed5703f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Jun 2024 06:02:46 +0000 Subject: ASoC: audio-graph-card: add link-trigger-order support Some Sound Card might need special trigger ordering which is based on CPU/Codec connection. It is already supported on ASoC, but Audio Graph Card still not yet support it. Let's support it. Cc: Maxim Kochetkov Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o786zojd.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 7b981aa8690a..acf7d92d21e6 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -143,6 +143,8 @@ static int graph_link_init(struct simple_util_priv *priv, struct device_node *port_codec = ep_to_port(ep_codec); struct device_node *ports_cpu = port_to_ports(port_cpu); struct device_node *ports_codec = port_to_ports(port_codec); + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; bool playback_only = 0, capture_only = 0; int ret; @@ -165,9 +167,20 @@ static int graph_link_init(struct simple_util_priv *priv, of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop); + dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; + dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) -- cgit v1.2.3 From e64343fa57335662b555477d9ddebd8285db2b66 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Jun 2024 06:02:52 +0000 Subject: ASoC: audio-graph-card2: add link-trigger-order support Some Sound Card might need special trigger ordering which is based on CPU/Codec connection. It is already supported on ASoC, but Audio Graph Card2 still not yet support it. Let's support it. Cc: Maxim Kochetkov Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87msnqzoj8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index be5a4ebfddd4..93d9d045587b 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -759,6 +759,8 @@ static void graph_link_init(struct simple_util_priv *priv, struct device_node *ports_cpu, *ports_codec; unsigned int daifmt = 0, daiclk = 0; bool playback_only = 0, capture_only = 0; + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; unsigned int bit_frame = 0; of_node_get(port_cpu); @@ -806,6 +808,14 @@ static void graph_link_init(struct simple_util_priv *priv, of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + graph_util_parse_trigger_order(priv, lnk, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop); + /* * convert bit_frame * We need to flip clock_provider if it was CPU node, @@ -818,6 +828,9 @@ static void graph_link_init(struct simple_util_priv *priv, dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; + dai_link->dai_fmt = daifmt | daiclk; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; -- cgit v1.2.3 From 15c958390460f964ec707ae6c5d1843638ebfe69 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 12 Jun 2024 14:40:50 +0800 Subject: ASoC: fsl_sai: Add separate DAI for transmitter and receiver The transmitter and receiver part of the SAI interface need to be configured with different master/slave mode, especially to work with the audiomix module. +-------+ +-----------+ | SAI1 | --TX--> | | | | <--RX-- | | +-------+ | | | AUDIOMIX | +-------+ | | | SAI2 | --TX--> | | +-------+ +-----------+ The SAI1 TX is in master mode, but SAI1 RX is in slave mode. So add another two DAIs for TX and RX separately. but only defined fsl_sai_set_dai_fmt_tx() and fsl_sai_set_dai_fmt_rx() ops function for current case, in the future, the other ops function for TX and RX can be defined if required. Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/1718174452-17596-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 141 ++++++++++++++++++++++++++++++++++-------------- sound/soc/fsl/fsl_sai.h | 4 +- 2 files changed, 104 insertions(+), 41 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 0e2c31439670..d03b0172b8ad 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -357,18 +357,18 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_BP_FP: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_consumer_mode = false; + sai->is_consumer_mode[tx] = false; break; case SND_SOC_DAIFMT_BC_FC: - sai->is_consumer_mode = true; + sai->is_consumer_mode[tx] = true; break; case SND_SOC_DAIFMT_BP_FC: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; - sai->is_consumer_mode = false; + sai->is_consumer_mode[tx] = false; break; case SND_SOC_DAIFMT_BC_FP: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_consumer_mode = true; + sai->is_consumer_mode[tx] = true; break; default: return -EINVAL; @@ -400,6 +400,16 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return ret; } +static int fsl_sai_set_dai_fmt_tx(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true); +} + +static int fsl_sai_set_dai_fmt_rx(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false); +} + static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); @@ -412,7 +422,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) bool support_1_1_ratio = sai->verid.version >= 0x0301; /* Don't apply to consumer mode */ - if (sai->is_consumer_mode) + if (sai->is_consumer_mode[tx]) return 0; /* @@ -575,7 +585,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, } } - if (!sai->is_consumer_mode) { + if (!sai->is_consumer_mode[tx]) { ret = fsl_sai_set_bclk(cpu_dai, tx, bclk); if (ret) return ret; @@ -613,7 +623,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, * RCR5(TCR5) for playback(capture), or there will be sync error. */ - if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) { + if (!sai->is_consumer_mode[tx] && fsl_sai_dir_is_synced(sai, adir)) { regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | FSL_SAI_CR4_CHMOD_MASK, @@ -683,7 +693,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, * FSD_MSTR bit for this specific case. */ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output && - !sai->is_consumer_mode) + !sai->is_consumer_mode[tx]) regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), FSL_SAI_CR4_FSD_MSTR, 0); @@ -697,7 +707,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, /* Enable FSD_MSTR after configuring word width */ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output && - !sai->is_consumer_mode) + !sai->is_consumer_mode[tx]) regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR); @@ -720,8 +730,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_consumer_mode && - sai->mclk_streams & BIT(substream->stream)) { + if (!sai->is_consumer_mode[tx] && + sai->mclk_streams & BIT(substream->stream)) { clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); sai->mclk_streams &= ~BIT(substream->stream); } @@ -759,7 +769,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) * This is a hardware bug, and will be fix in the * next sai version. */ - if (!sai->is_consumer_mode) { + if (!sai->is_consumer_mode[tx]) { /* Software Reset */ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */ @@ -914,6 +924,30 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .startup = fsl_sai_startup, }; +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt_tx, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt_rx, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + static int fsl_sai_dai_resume(struct snd_soc_component *component) { struct fsl_sai *sai = snd_soc_component_get_drvdata(component); @@ -931,26 +965,55 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component) return 0; } -static struct snd_soc_dai_driver fsl_sai_dai_template = { - .playback = { - .stream_name = "CPU-Playback", - .channels_min = 1, - .channels_max = 32, - .rate_min = 8000, - .rate_max = 2822400, - .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_SAI_FORMATS, +static struct snd_soc_dai_driver fsl_sai_dai_template[] = { + { + .name = "sai-tx-rx", + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, + }, + { + .name = "sai-tx", + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_tx_ops, }, - .capture = { - .stream_name = "CPU-Capture", - .channels_min = 1, - .channels_max = 32, - .rate_min = 8000, - .rate_max = 2822400, - .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_SAI_FORMATS, + { + .name = "sai-rx", + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_rx_ops, }, - .ops = &fsl_sai_pcm_dai_ops, }; static const struct snd_soc_component_driver fsl_component = { @@ -1399,15 +1462,15 @@ static int fsl_sai_probe(struct platform_device *pdev) return ret; } - memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template, - sizeof(fsl_sai_dai_template)); + memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template, + sizeof(*fsl_sai_dai_template) * ARRAY_SIZE(fsl_sai_dai_template)); /* Sync Tx with Rx as default by following old DT binding */ sai->synchronous[RX] = true; sai->synchronous[TX] = false; - sai->cpu_dai_drv.symmetric_rate = 1; - sai->cpu_dai_drv.symmetric_channels = 1; - sai->cpu_dai_drv.symmetric_sample_bits = 1; + sai->cpu_dai_drv[0].symmetric_rate = 1; + sai->cpu_dai_drv[0].symmetric_channels = 1; + sai->cpu_dai_drv[0].symmetric_sample_bits = 1; if (of_property_read_bool(np, "fsl,sai-synchronous-rx") && of_property_read_bool(np, "fsl,sai-asynchronous")) { @@ -1424,9 +1487,9 @@ static int fsl_sai_probe(struct platform_device *pdev) /* Discard all settings for asynchronous mode */ sai->synchronous[RX] = false; sai->synchronous[TX] = false; - sai->cpu_dai_drv.symmetric_rate = 0; - sai->cpu_dai_drv.symmetric_channels = 0; - sai->cpu_dai_drv.symmetric_sample_bits = 0; + sai->cpu_dai_drv[0].symmetric_rate = 0; + sai->cpu_dai_drv[0].symmetric_channels = 0; + sai->cpu_dai_drv[0].symmetric_sample_bits = 0; } sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output"); @@ -1505,7 +1568,7 @@ static int fsl_sai_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_component(dev, &fsl_component, - &sai->cpu_dai_drv, 1); + sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template)); if (ret) goto err_pm_get_sync; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 550df87b6a06..dadbd16ee394 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -282,7 +282,7 @@ struct fsl_sai { struct clk *pll11k_clk; struct resource *res; - bool is_consumer_mode; + bool is_consumer_mode[2]; bool is_lsb_first; bool is_dsp_mode; bool is_pdm_mode; @@ -299,7 +299,7 @@ struct fsl_sai { unsigned int bclk_ratio; const struct fsl_sai_soc_data *soc_data; - struct snd_soc_dai_driver cpu_dai_drv; + struct snd_soc_dai_driver cpu_dai_drv[3]; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; struct fsl_sai_verid verid; -- cgit v1.2.3 From 6232a7eb1afc842d0daf1c0314e1eda65731783f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 12 Jun 2024 14:40:51 +0800 Subject: ASoC: fsl_audmix: Split playback and capture stream to different DAI As audmix requires playback and capture stream in different master/slave mode, so separate playback and capture stream to different DAI. There are three DAIs required, two DAIs for playback one DAI for capture. Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/1718174452-17596-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 0ab2c1962117..1671a3037c60 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -326,15 +326,6 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = { .rates = SNDRV_PCM_RATE_8000_96000, .formats = FSL_AUDMIX_FORMATS, }, - .capture = { - .stream_name = "AUDMIX-Capture-0", - .channels_min = 8, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 96000, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = FSL_AUDMIX_FORMATS, - }, .ops = &fsl_audmix_dai_ops, }, { @@ -349,8 +340,13 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = { .rates = SNDRV_PCM_RATE_8000_96000, .formats = FSL_AUDMIX_FORMATS, }, + .ops = &fsl_audmix_dai_ops, + }, + { + .id = 2, + .name = "audmix-2", .capture = { - .stream_name = "AUDMIX-Capture-1", + .stream_name = "AUDMIX-Capture-0", .channels_min = 8, .channels_max = 8, .rate_min = 8000, -- cgit v1.2.3 From 05d996e113481fdd9ac40ccf5cadabd1e73f2404 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 12 Jun 2024 14:40:52 +0800 Subject: ASoC: imx-audmix: Split capture device for audmix There will be three devices for this sound card, hw:x,0 is the playback device for one SAI, hw:x,1 is the playback device for another SAI, hw:x,2 is the capture device for audmix output. then capture device and playback device can be configured with different master/slave mode. Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/1718174452-17596-4-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmix.c | 79 ++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 2aeb18397bcb..6fbcf33fd0de 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -140,6 +140,13 @@ static const struct snd_soc_ops imx_audmix_be_ops = { .hw_params = imx_audmix_be_hw_params, }; +static const char *name[][3] = { + {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"}, + {"sai-tx", "sai-tx", "sai-rx"}, + {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"}, + {"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"}, +}; + static int imx_audmix_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -150,7 +157,7 @@ static int imx_audmix_probe(struct platform_device *pdev) struct imx_audmix *priv; int i, num_dai, ret; const char *fe_name_pref = "HiFi-AUDMIX-FE-"; - char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name; + char *be_name, *dai_name; if (pdev->dev.parent) { audmix_np = pdev->dev.parent->of_node; @@ -183,6 +190,7 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + num_dai += 1; priv->num_dai = 2 * num_dai; priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai, sizeof(struct snd_soc_dai_link), GFP_KERNEL); @@ -196,7 +204,7 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!priv->dai_conf) return -ENOMEM; - priv->num_dapm_routes = 3 * num_dai; + priv->num_dapm_routes = num_dai; priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes, sizeof(struct snd_soc_dapm_route), GFP_KERNEL); @@ -211,8 +219,12 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!dlc) return -ENOMEM; - ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i, - &args); + if (i == num_dai - 1) + ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, 0, + &args); + else + ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i, + &args); if (ret < 0) { dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n"); return ret; @@ -226,20 +238,14 @@ static int imx_audmix_probe(struct platform_device *pdev) put_device(&cpu_pdev->dev); dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s", - fe_name_pref, args.np->full_name + 1); + fe_name_pref, args.np->full_name); if (!dai_name) return -ENOMEM; dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name); - if (i == 0) { + if (i == num_dai - 1) out_cpu_np = args.np; - capture_dai_name = - devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", - dai_name, "CPU-Capture"); - if (!capture_dai_name) - return -ENOMEM; - } /* * CPU == Platform @@ -252,27 +258,23 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[i].num_cpus = 1; priv->dai[i].num_codecs = 1; priv->dai[i].num_platforms = 1; - - priv->dai[i].name = dai_name; + priv->dai[i].name = name[0][i]; priv->dai[i].stream_name = "HiFi-AUDMIX-FE"; priv->dai[i].cpus->of_node = args.np; - priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev); + priv->dai[i].cpus->dai_name = name[1][i]; + priv->dai[i].dynamic = 1; priv->dai[i].dpcm_playback = 1; - priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0); + if (i == num_dai - 1) { + priv->dai[i].dpcm_capture = 1; + priv->dai[i].dpcm_playback = 0; + } priv->dai[i].ignore_pmdown_time = 1; priv->dai[i].ops = &imx_audmix_fe_ops; /* Add AUDMIX Backend */ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "audmix-%d", i); - be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "AUDMIX-Playback-%d", i); - be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "AUDMIX-Capture-%d", i); - if (!be_name || !be_pb || !be_cp) - return -ENOMEM; - priv->dai[num_dai + i].cpus = &dlc[1]; priv->dai[num_dai + i].codecs = &snd_soc_dummy_dlc; @@ -284,24 +286,33 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[num_dai + i].cpus->dai_name = be_name; priv->dai[num_dai + i].no_pcm = 1; priv->dai[num_dai + i].dpcm_playback = 1; - priv->dai[num_dai + i].dpcm_capture = 1; + if (i == num_dai - 1) { + priv->dai[num_dai + i].dpcm_capture = 1; + priv->dai[num_dai + i].dpcm_playback = 0; + } priv->dai[num_dai + i].ignore_pmdown_time = 1; priv->dai[num_dai + i].ops = &imx_audmix_be_ops; priv->dai_conf[i].dlc.of_node = args.np; priv->dai_conf[i].name_prefix = dai_name; - priv->dapm_routes[i].source = - devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", - dai_name, "CPU-Playback"); - if (!priv->dapm_routes[i].source) - return -ENOMEM; + if (i == num_dai - 1) { + priv->dapm_routes[i].sink = + devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", + dai_name, name[2][i]); + if (!priv->dapm_routes[i].sink) + return -ENOMEM; - priv->dapm_routes[i].sink = be_pb; - priv->dapm_routes[num_dai + i].source = be_pb; - priv->dapm_routes[num_dai + i].sink = be_cp; - priv->dapm_routes[2 * num_dai + i].source = be_cp; - priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name; + priv->dapm_routes[i].source = name[3][i]; + } else { + priv->dapm_routes[i].source = + devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", + dai_name, name[3][i]); + if (!priv->dapm_routes[i].source) + return -ENOMEM; + + priv->dapm_routes[i].sink = name[2][i]; + } } cpu_pdev = of_find_device_by_node(out_cpu_np); -- cgit v1.2.3 From f9f7f29f64454bb20896c7d918c3abc3a1aa487b Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Wed, 5 Jun 2024 13:49:53 +0300 Subject: ASoC: qcom: Adjust issues in case of DT error in asoc_qcom_lpass_cpu_platform_probe() If IORESOURCE_MEM "lpass-rxtx-cdc-dma-lpm" or "lpass-va-cdc-dma-lpm" resources is not provided in Device Tree due to any error, platform_get_resource_byname() will return NULL which is later dereferenced. According to sound/qcom,lpass-cpu.yaml, these resources are provided, but DT can be broken due to any error. In such cases driver must be able to protect itself, since the DT is external data for the driver. Adjust this issues by adding NULL return check. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: b138706225c9 ("ASoC: qcom: Add regmap config support for codec dma driver") Signed-off-by: Aleksandr Mishin Link: https://patch.msgid.link/20240605104953.12072-1-amishin@t-argos.ru Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index b0f3e02cb043..5a47f661e0c6 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -1166,9 +1166,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm"); + if (!res) + return -EINVAL; drvdata->rxtx_cdc_dma_lpm_buf = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm"); + if (!res) + return -EINVAL; drvdata->va_cdc_dma_lpm_buf = res->start; } -- cgit v1.2.3 From 4faed8ca581c0307af4a58e6c0027ce2b2cbd5d3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2024 11:40:51 +0200 Subject: ASoC: dt-bindings: amlogic,gx-sound-card: drop minItems for audio-widgets Like "audio-routing" drop the minItems: 2 from the "audio-widgets", because any limit here - lower or upper- is rather meaningless. This will also fix `dtbs_check` warnings like: sound: audio-widgets: ['Speaker', '7J4-14 LEFT', 'Speaker', '7J4-11 RIGHT'] is too long Signed-off-by: Neil Armstrong Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240605-topic-amlogic-upstream-bindings-fixes-audio-widgets-v1-1-65bd7cc2e09b@linaro.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index d4277d342e69..0ecdaf7190e9 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -23,7 +23,6 @@ properties: audio-widgets: $ref: /schemas/types.yaml#/definitions/non-unique-string-array - minItems: 2 description: |- A list off component DAPM widget. Each entry is a pair of strings, the first being the widget type, the second being the widget name -- cgit v1.2.3 From f2177731b6cdaedfe1dea5ec7d185be579b14cc5 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 20 Jun 2024 18:32:24 +0800 Subject: ASoC: rt712-sdca: change the definition name of SDCA channel number This patch doesn't have any change of functionality. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20240620103224.2124179-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 12 ++++++------ sound/soc/codecs/rt712-sdca-sdw.h | 28 ++++++++++++++-------------- sound/soc/codecs/rt712-sdca.c | 38 +++++++++++++++++++------------------- sound/soc/codecs/rt712-sdca.h | 10 ++++++---- 4 files changed, 45 insertions(+), 43 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index ce337db86d1a..ecc541c5a583 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -79,12 +79,12 @@ static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x5d00000 ... 0x5d00009: case 0x5f00000 ... 0x5f00030: case 0x6100000 ... 0x6100068: - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R): - case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02): return true; default: return false; diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index 4be22ccd8561..f0cb871b9fa2 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -57,16 +57,16 @@ static const struct reg_default rt712_sdca_reg_defaults[] = { { 0x3303, 0x1f }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; @@ -95,14 +95,14 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x6100064, 0x8000 }, { 0x6100065, 0x0000 }, { 0x6100067, 0xff12 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, }; #endif /* __RT712_SDW_H__ */ diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index b503de9fda80..e69e1a14fb51 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -592,20 +592,20 @@ static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol, static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712) { int err; - unsigned int ch_l, ch_r; + unsigned int ch_01, ch_02; - ch_l = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; - ch_r = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; + ch_01 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; err = regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, - RT712_SDCA_CTL_FU_MUTE, CH_L), ch_l); + RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01); if (err < 0) return err; err = regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, - RT712_SDCA_CTL_FU_MUTE, CH_R), ch_r); + RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02); if (err < 0) return err; @@ -649,28 +649,28 @@ static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); static const struct snd_kcontrol_new rt712_sdca_controls[] = { SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x57, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put), SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x3f, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 8, 3, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv), }; static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = { SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x57, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), }; @@ -763,21 +763,21 @@ static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_L), + RT712_SDCA_CTL_FU_MUTE, CH_01), unmute); regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_R), + RT712_SDCA_CTL_FU_MUTE, CH_02), unmute); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_L), + RT712_SDCA_CTL_FU_MUTE, CH_01), mute); regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_R), + RT712_SDCA_CTL_FU_MUTE, CH_02), mute); break; } @@ -885,8 +885,8 @@ static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, static const struct snd_kcontrol_new rt712_spk_sto_dac = SOC_DAPM_DOUBLE_R("Switch", - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0, 1, 1); static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = { diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index ff79e03118ce..bf462a837c0d 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -39,6 +39,12 @@ struct rt712_sdca_priv { bool fu0f_mixer_r_mute; }; +/* SDCA (Channel) */ +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 + /* NID */ #define RT712_VENDOR_REG 0x20 #define RT712_VENDOR_CALI 0x58 @@ -176,10 +182,6 @@ struct rt712_sdca_priv { #define RT712_SDCA_CTL_VENDOR_DEF 0x30 #define RT712_SDCA_CTL_FU_CH_GAIN 0x0b -/* RT712 SDCA channel */ -#define CH_L 0x01 -#define CH_R 0x02 - /* sample frequency index */ #define RT712_SDCA_RATE_16000HZ 0x04 #define RT712_SDCA_RATE_32000HZ 0x07 -- cgit v1.2.3 From 936abb09c1c7ce2c9c8b7838fc780e98c0140759 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Thu, 20 Jun 2024 18:32:37 +0800 Subject: ASoC: rt712-sdca: add the function for version B The version B will support the multi-lane function and integrate the DMIC function in one SoundWire interface. Due to some registers having different default values between version A and B, this patch also removes the redundant default registers to avoid confusion. Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20240620103237.2124196-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 24 +- sound/soc/codecs/rt712-sdca-sdw.h | 69 ++--- sound/soc/codecs/rt712-sdca.c | 627 +++++++++++++++++++++++++++++++++++--- sound/soc/codecs/rt712-sdca.h | 40 +++ 4 files changed, 660 insertions(+), 100 deletions(-) diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index ecc541c5a583..90d5aaddbd5b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -34,6 +34,10 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: return true; default: @@ -56,6 +60,10 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: return true; default: @@ -78,13 +86,21 @@ static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x5c00000 ... 0x5c0009a: case 0x5d00000 ... 0x5d00009: case 0x5f00000 ... 0x5f00030: - case 0x6100000 ... 0x6100068: + case 0x6100000 ... 0x61000f1: case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02): case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01): case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04): return true; default: return false; @@ -96,7 +112,9 @@ static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int re switch (reg) { case 0x2000000: case 0x200001a: + case 0x2000020: case 0x2000024: + case 0x2000030: case 0x2000046: case 0x200008a: case 0x5800000: @@ -178,13 +196,15 @@ static int rt712_sdca_read_prop(struct sdw_slave *slave) unsigned long addr; struct sdw_dpn_prop *dpn; + sdw_slave_read_prop(slave); + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->paging_support = true; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = BIT(4); /* BITMAP: 00010000 */ + prop->source_ports = BIT(8) | BIT(4); /* BITMAP: 100010000 */ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ nval = hweight32(prop->source_ports); diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index f0cb871b9fa2..99fd2d67f04d 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -12,49 +12,7 @@ #include static const struct reg_default rt712_sdca_reg_defaults[] = { - { 0x201a, 0x00 }, - { 0x201b, 0x00 }, - { 0x201c, 0x00 }, - { 0x201d, 0x00 }, - { 0x201e, 0x00 }, - { 0x201f, 0x00 }, - { 0x2029, 0x00 }, - { 0x202a, 0x00 }, - { 0x202d, 0x00 }, - { 0x202e, 0x00 }, - { 0x202f, 0x00 }, - { 0x2030, 0x00 }, - { 0x2031, 0x00 }, - { 0x2032, 0x00 }, - { 0x2033, 0x00 }, - { 0x2034, 0x00 }, - { 0x2230, 0x00 }, - { 0x2231, 0x2f }, - { 0x2232, 0x80 }, - { 0x2f01, 0x00 }, - { 0x2f02, 0x09 }, - { 0x2f03, 0x00 }, - { 0x2f04, 0x00 }, - { 0x2f05, 0x0b }, - { 0x2f06, 0x01 }, - { 0x2f08, 0x00 }, - { 0x2f09, 0x00 }, - { 0x2f0a, 0x01 }, - { 0x2f35, 0x01 }, - { 0x2f36, 0xcf }, - { 0x2f50, 0x0f }, - { 0x2f54, 0x01 }, - { 0x2f58, 0x07 }, - { 0x2f59, 0x09 }, - { 0x2f5a, 0x01 }, - { 0x2f5b, 0x07 }, - { 0x2f5c, 0x05 }, - { 0x2f5d, 0x05 }, - { 0x3201, 0x01 }, - { 0x320c, 0x00 }, - { 0x3301, 0x01 }, - { 0x3302, 0x00 }, - { 0x3303, 0x1f }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, @@ -63,17 +21,22 @@ static const struct reg_default rt712_sdca_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x2000004, 0xaa01 }, { 0x200000e, 0x21e0 }, - { 0x2000024, 0x01ba }, { 0x200004a, 0x8830 }, { 0x2000067, 0xf100 }, { 0x5800000, 0x1893 }, @@ -81,12 +44,8 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x5b00005, 0x0000 }, { 0x5b00029, 0x3fff }, { 0x5b0002a, 0xf000 }, - { 0x5f00008, 0x7000 }, + { 0x6100000, 0x04e4 }, { 0x610000e, 0x0007 }, - { 0x6100022, 0x2828 }, - { 0x6100023, 0x2929 }, - { 0x6100026, 0x2c29 }, - { 0x610002c, 0x4150 }, { 0x6100045, 0x0860 }, { 0x6100046, 0x0029 }, { 0x6100053, 0x3fff }, @@ -101,6 +60,14 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, }; diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index e69e1a14fb51..e210c574bb74 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -82,7 +82,8 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) dev = regmap_get_device(regmap); /* Set HP-JD source from JD1 */ - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + if (rt712->version_id == RT712_VA) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); /* FSM switch to calibration manual mode */ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); @@ -198,11 +199,17 @@ static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712) _end_btn_det_: /* Host is owner, so set back to device */ - if (owner == 0) + if (owner == 0) { /* set owner to device */ - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, - RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + if (rt712->version_id == RT712_VA) + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + else + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + } return btn_type; } @@ -415,8 +422,9 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) switch (rt712->jd_src) { case RT712_JD1: - /* Set HP-JD source from JD1 */ - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + /* Set HP-JD source from JD1, VB uses JD1 in default */ + if (rt712->version_id == RT712_VA) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); break; default: dev_warn(rt712->component->dev, "Wrong JD source\n"); @@ -854,7 +862,7 @@ static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w, return 0; } -static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, +static int rt712_sdca_pde23_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = @@ -883,9 +891,12 @@ static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_kcontrol_new rt712_spk_sto_dac = - SOC_DAPM_DOUBLE_R("Switch", +static const struct snd_kcontrol_new rt712_spk_l_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), + 0, 1, 1); +static const struct snd_kcontrol_new rt712_spk_r_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0, 1, 1); @@ -931,20 +942,26 @@ static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), /* Digital Interface */ - SND_SOC_DAPM_SWITCH("FU06", SND_SOC_NOPM, 0, 0, &rt712_spk_sto_dac), + SND_SOC_DAPM_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), /* Output */ - SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, - rt712_sdca_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt712_spk_l_dac), + SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt712_spk_r_dac), SND_SOC_DAPM_OUTPUT("SPOL"), SND_SOC_DAPM_OUTPUT("SPOR"), }; static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = { - { "FU06", "Switch", "DP3RX" }, - { "CLASS D", NULL, "FU06" }, - { "SPOL", NULL, "CLASS D" }, - { "SPOR", NULL, "CLASS D" }, + { "FU06", NULL, "DP3RX" }, + { "FU06", NULL, "PDE 23" }, + { "OT23 L", "Switch", "FU06" }, + { "OT23 R", "Switch", "FU06" }, + { "SPOL", NULL, "OT23 L" }, + { "SPOR", NULL, "OT23 R" }, }; static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev) @@ -983,6 +1000,376 @@ static int rt712_sdca_probe(struct snd_soc_component *component) return 0; } +static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / 0x0a00; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * 0x0a00; + else { /* ADC gain */ + gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_priv *rt712) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) { + ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00; + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, + RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt712_sdca_set_fu1e_capture_ctl(rt712); + if (err < 0) + return err; + + return changed; +} + +static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt712_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt712_sdca_fu_info, \ + .get = rt712_sdca_dmic_fu1e_capture_get, \ + .put = rt712_sdca_dmic_fu1e_capture_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt712_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = { + RT712_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), + 1, 1, 4), + RT712_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv), + RT712_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, dmic_vol_tlv), +}; + +static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 0A Mux")) + mask_sft = 0; + else if (strstr(ucontrol->id.name, "ADC 0B Mux")) + mask_sft = 4; + else + return -EINVAL; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (((val >> mask_sft) & 0xf) == 0x4) ? 0 : 1; + + return 0; +} + +static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 0A Mux")) + mask_sft = 0; + else if (strstr(ucontrol->id.name, "ADC 0B Mux")) + mask_sft = 4; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val2); + val2 = ((0xf << mask_sft) & val2) >> mask_sft; + + if (val == 0) + val = 0x4; + else if (val >= 1) + val = 0xe; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, 0xf << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const adc_dmic_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt712_adc0a_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt712_adc0b_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc0a_mux = + SOC_DAPM_ENUM_EXT("ADC 0A Mux", rt712_adc0a_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc0b_mux = + SOC_DAPM_ENUM_EXT("ADC 0B Mux", rt712_adc0b_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static int rt712_sdca_dmic_fu1e_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt712->fu1e_dapm_mute = false; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + case SND_SOC_DAPM_PRE_PMD: + rt712->fu1e_dapm_mute = true; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + } + return 0; +} + +static int rt712_sdca_dmic_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_fu1e_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 0A Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc0a_mux), + SND_SOC_DAPM_MUX("ADC 0B Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc0b_mux), + + SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = { + {"DP8TX", NULL, "FU 1E"}, + + {"FU 1E", NULL, "PDE 11"}, + {"FU 1E", NULL, "ADC 0A Mux"}, + {"FU 1E", NULL, "ADC 0B Mux"}, + {"ADC 0A Mux", "DMIC1", "DMIC1"}, + {"ADC 0A Mux", "DMIC2", "DMIC2"}, + {"ADC 0B Mux", "DMIC1", "DMIC1"}, + {"ADC 0B Mux", "DMIC2", "DMIC2"}, +}; + +static int rt712_sdca_dmic_probe(struct snd_soc_component *component) +{ + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712->dmic_component = component; + + if (!rt712->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { .probe = rt712_sdca_probe, .controls = rt712_sdca_controls, @@ -995,6 +1382,20 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { .endianness = 1, }; +static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = { + .probe = rt712_sdca_dmic_probe, + .controls = rt712_sdca_dmic_snd_controls, + .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls), + .dapm_widgets = rt712_sdca_dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets), + .dapm_routes = rt712_sdca_dmic_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map), + .endianness = 1, +#ifdef CONFIG_DEBUG_FS + .debugfs_prefix = "dmic", +#endif +}; + static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) { @@ -1022,7 +1423,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, int retval, port, num_channels; unsigned int sampling_rate; - dev_dbg(dai->dev, "%s %s", __func__, dai->name); + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); sdw_stream = snd_soc_dai_get_dma_data(dai, substream); if (!sdw_stream) @@ -1031,6 +1432,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, if (!rt712->slave) return -EINVAL; + /* VA doesn't support AIF3 */ + if (dai->id == RT712_AIF3 && rt712->version_id == RT712_VA) + return -EINVAL; + /* SoundWire specific configuration */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { direction = SDW_DATA_DIR_RX; @@ -1044,6 +1449,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, direction = SDW_DATA_DIR_TX; if (dai->id == RT712_AIF1) port = 4; + else if (dai->id == RT712_AIF3) + port = 8; else return -EINVAL; } @@ -1105,6 +1512,14 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); break; + case RT712_AIF3: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; default: dev_err(component->dev, "%s: Wrong DAI id\n", __func__); return -EINVAL; @@ -1174,6 +1589,21 @@ static struct snd_soc_dai_driver rt712_sdca_dai[] = { } }; +static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = { + { + .name = "rt712-sdca-aif3", + .id = RT712_AIF3, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + } +}; + int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave) { @@ -1206,6 +1636,9 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, rt712->first_hw_init = false; rt712->fu0f_dapm_mute = true; rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true; + rt712->fu1e_dapm_mute = true; + rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] = + rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true; /* JD source uses JD1 in default */ rt712->jd_src = RT712_JD1; @@ -1239,35 +1672,11 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, return 0; } -int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) { - struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); int ret = 0; - unsigned int val, hibernation_flag; - - rt712->disable_irq = false; - - if (rt712->hw_init) - return 0; - - regcache_cache_only(rt712->regmap, false); - regcache_cache_only(rt712->mbq_regmap, false); - if (rt712->first_hw_init) { - regcache_cache_bypass(rt712->regmap, true); - regcache_cache_bypass(rt712->mbq_regmap, true); - } else { - /* - * PM runtime status is marked as 'active' only when a Slave reports as Attached - */ - - /* update count of parent 'active' children */ - pm_runtime_set_active(&slave->dev); - } - - pm_runtime_get_noresume(&slave->dev); - - rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); - rt712->hw_id = (val & 0xf000) >> 12; + unsigned int hibernation_flag; + struct device *dev = &rt712->slave->dev; rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); @@ -1307,6 +1716,131 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); } +} + +static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) +{ + int ret = 0; + unsigned int jack_func_status, mic_func_status, amp_func_status; + struct device *dev = &rt712->slave->dev; + + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status); + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &_func_status); + dev_dbg(dev, "%s jack/mic/amp func_status=0x%x, 0x%x, 0x%x\n", + __func__, jack_func_status, mic_func_status, amp_func_status); + + /* DMIC */ + if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_CH12_FLOAT_CTL, 0x0304); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL, 0x1f1e); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0B_FU_CH12_FLOAT_CTL, 0x0304); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_HDA_LEGACY_CONFIG_CTL0, 0x8010); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT11, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + rt712_sdca_index_write(rt712, RT712_ULTRA_SOUND_DET, RT712_ULTRA_SOUND_DETECTOR6, 0x3200); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + + /* Jack */ + if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_SEL_VEE2_HP_CTL1, 0x042a); + rt712_sdca_index_write(rt712, RT712_CHARGE_PUMP, RT712_HP_DET_CTL3, 0x1fff); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec67); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL1, 0x2802); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL6, 0xf215); + + /* calibration */ + ret = rt712_sdca_calibration(rt712); + if (ret < 0) + dev_err(dev, "%s, calibration failed!\n", __func__); + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, 0x3000, 0x0000); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_MISC_CTL_FOR_UAJ, 0x0003); + + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + + /* SPK */ + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + if (rt712->hw_id != RT712_DEV_ID_713) { + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec63); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL1, 0xfff5); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); + } + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } +} + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val; + struct sdw_slave_prop *prop = &slave->prop; + + rt712->disable_irq = false; + + if (rt712->hw_init) + return 0; + + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, true); + regcache_cache_bypass(rt712->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); + rt712->hw_id = (val & 0xf000) >> 12; + rt712->version_id = (val & 0x0f00) >> 8; + dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id); + + if (rt712->version_id == RT712_VA) + rt712_sdca_va_io_init(rt712); + else { + /* multilanes and DMIC are supported by rt712vb */ + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712_dmic, rt712_sdca_dmic_dai, ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; + + prop->lane_control_support = true; + rt712_sdca_vb_io_init(rt712); + } /* * if set_jack callback occurred early than io_init, @@ -1315,8 +1849,7 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hs_jack) rt712_sdca_jack_init(rt712); - if (!hibernation_flag) - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); if (rt712->first_hw_init) { regcache_cache_bypass(rt712->regmap, false); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index bf462a837c0d..2169f2f726b9 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -19,6 +19,7 @@ struct rt712_sdca_priv { struct regmap *regmap; struct regmap *mbq_regmap; struct snd_soc_component *component; + struct snd_soc_component *dmic_component; struct sdw_slave *slave; struct sdw_bus_params params; bool hw_init; @@ -34,9 +35,19 @@ struct rt712_sdca_priv { unsigned int scp_sdca_stat1; unsigned int scp_sdca_stat2; unsigned int hw_id; + unsigned int version_id; bool fu0f_dapm_mute; bool fu0f_mixer_l_mute; bool fu0f_mixer_r_mute; + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt712_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; }; /* SDCA (Channel) */ @@ -47,6 +58,8 @@ struct rt712_sdca_priv { /* NID */ #define RT712_VENDOR_REG 0x20 +#define RT712_EQ_CTRL 0x53 +#define RT712_CHARGE_PUMP 0x57 #define RT712_VENDOR_CALI 0x58 #define RT712_ULTRA_SOUND_DET 0x59 #define RT712_VENDOR_IMS_DRE 0x5b @@ -56,9 +69,13 @@ struct rt712_sdca_priv { /* Index (NID:20h) */ #define RT712_JD_PRODUCT_NUM 0x00 #define RT712_ANALOG_BIAS_CTL3 0x04 +#define RT712_JD_CTL1 0x09 +#define RT712_IO_CTL 0x0c #define RT712_LDO2_3_CTL1 0x0e #define RT712_PARA_VERB_CTL 0x1a #define RT712_CC_DET1 0x24 +#define RT712_CLASSD_AMP_CTL1 0x37 +#define RT712_CLASSD_AMP_CTL6 0x3c #define RT712_COMBO_JACK_AUTO_CTL1 0x45 #define RT712_COMBO_JACK_AUTO_CTL2 0x46 #define RT712_COMBO_JACK_AUTO_CTL3 0x47 @@ -67,6 +84,9 @@ struct rt712_sdca_priv { #define RT712_SW_CONFIG1 0x8a #define RT712_SW_CONFIG2 0x8b +/* Index (NID:57h) */ +#define RT712_HP_DET_CTL3 0x0c + /* Index (NID:58h) */ #define RT712_DAC_DC_CALI_CTL1 0x00 #define RT712_DAC_DC_CALI_CTL2 0x01 @@ -77,6 +97,7 @@ struct rt712_sdca_priv { /* Index (NID:5bh) */ #define RT712_IMS_DIGITAL_CTL1 0x00 #define RT712_IMS_DIGITAL_CTL5 0x05 +#define RT712_SEL_VEE2_HP_CTL1 0x23 #define RT712_HP_DETECT_RLDET_CTL1 0x29 #define RT712_HP_DETECT_RLDET_CTL2 0x2a @@ -115,6 +136,11 @@ struct rt712_sdca_priv { #define RT712_UMP_HID_CTL6 0x66 #define RT712_UMP_HID_CTL7 0x67 #define RT712_UMP_HID_CTL8 0x68 +#define RT712_MISC_CTL_FOR_UAJ 0x72 +#define RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL 0xa2 +#define RT712_DMIC2_FU_IT_FLOAT_CTL 0xa6 +#define RT712_ADC0B_FU_CH12_FLOAT_CTL 0xb0 +#define RT712_DMIC2_FU_CH12_FLOAT_CTL 0xb1 /* Parameter & Verb control 01 (0x1a)(NID:20h) */ #define RT712_HIDDEN_REG_SW_RESET (0x1 << 14) @@ -145,6 +171,7 @@ struct rt712_sdca_priv { #define FUNC_NUM_AMP 0x04 /* RT712 SDCA entity */ +#define RT712_SDCA_ENT0 0x00 #define RT712_SDCA_ENT_HID01 0x01 #define RT712_SDCA_ENT_GE49 0x49 #define RT712_SDCA_ENT_USER_FU05 0x05 @@ -163,6 +190,7 @@ struct rt712_sdca_priv { #define RT712_SDCA_ENT_CS1C 0x1c #define RT712_SDCA_ENT_CS31 0x31 #define RT712_SDCA_ENT_OT23 0x42 +#define RT712_SDCA_ENT_IT11 0x26 #define RT712_SDCA_ENT_IT26 0x26 #define RT712_SDCA_ENT_IT09 0x09 #define RT712_SDCA_ENT_PLATFORM_FU15 0x15 @@ -181,6 +209,12 @@ struct rt712_sdca_priv { #define RT712_SDCA_CTL_REQ_POWER_STATE 0x01 #define RT712_SDCA_CTL_VENDOR_DEF 0x30 #define RT712_SDCA_CTL_FU_CH_GAIN 0x0b +#define RT712_SDCA_CTL_FUNC_STATUS 0x10 + +/* Function_Status */ +#define FUNCTION_NEEDS_INITIALIZATION BIT(5) +#define FUNCTION_HAS_BEEN_RESET BIT(6) +#define FUNCTION_BUSY BIT(7) /* sample frequency index */ #define RT712_SDCA_RATE_16000HZ 0x04 @@ -193,6 +227,7 @@ struct rt712_sdca_priv { enum { RT712_AIF1, RT712_AIF2, + RT712_AIF3, }; enum rt712_sdca_jd_src { @@ -209,6 +244,11 @@ enum rt712_sdca_hw_id { #define RT712_PART_ID_713 0x713 +enum rt712_sdca_version { + RT712_VA, + RT712_VB, +}; + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave); -- cgit v1.2.3 From 36ac1e29f49ff4cbf1500465db311b17d8ba9213 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 27 Jun 2024 14:52:03 +0200 Subject: ASoC: codecs: lpass-wsa-macro: add missing select of common code The Qualcomm LPASS WSA macro codec driver uses now parts of common module, so it has to select SND_SOC_LPASS_MACRO_COMMON. sound/soc/codecs/lpass-wsa-macro.o: in function `wsa_macro_probe': sound/soc/codecs/lpass-wsa-macro.c:2767:(.text+0x1c9c): undefined reference to `lpass_macro_get_codec_version' Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202406272231.th1LtuLk-lkp@intel.com/ Fixes: 5dcf442bbbca ("ASoC: codecs: lpass-wsa-macro: Prepare to accommodate new codec versions") Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240627125203.171048-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2019da190dcb..97bb69c9848d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2557,6 +2557,7 @@ config SND_SOC_LPASS_MACRO_COMMON config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_VA_MACRO -- cgit v1.2.3 From 752fea92d9b3e4255a2f75e7efa6fe148562ccbf Mon Sep 17 00:00:00 2001 From: Piotr Wojtaszczyk Date: Thu, 27 Jun 2024 17:00:21 +0200 Subject: ASoC: dt-bindings: lpc32xx: Add lpc32xx i2s DT binding Add nxp,lpc3220-i2s DT binding documentation. Signed-off-by: Piotr Wojtaszczyk Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240627150046.258795-4-piotr.wojtaszczyk@timesys.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/nxp,lpc3220-i2s.yaml | 73 ++++++++++++++++++++++ MAINTAINERS | 9 +++ 2 files changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml diff --git a/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml new file mode 100644 index 000000000000..40a0877a8aba --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nxp,lpc3220-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32XX I2S Controller + +description: + The I2S controller in LPC32XX SoCs, ASoC DAI. + +maintainers: + - J.M.B. Downing + - Piotr Wojtaszczyk + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - nxp,lpc3220-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: input clock of the peripheral. + + dmas: + items: + - description: RX DMA Channel + - description: TX DMA Channel + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - interrupts + - clocks + - dmas + - dma-names + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + i2s@20094000 { + compatible = "nxp,lpc3220-i2s"; + reg = <0x20094000 0x1000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_I2S0>; + dmas = <&dma 0 1>, <&dma 13 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index d3b064ce0ba6..5e5701402009 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8909,6 +8909,15 @@ S: Maintained F: sound/soc/fsl/fsl* F: sound/soc/fsl/imx* +FREESCALE SOC LPC32XX SOUND DRIVERS +M: J.M.B. Downing +M: Piotr Wojtaszczyk +R: Vladimir Zapolskiy +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml + FREESCALE SOC SOUND QMC DRIVER M: Herve Codina L: alsa-devel@alsa-project.org (moderated for non-subscribers) -- cgit v1.2.3 From 0959de657a10cc40b2cc41cff9169ab0e0fd4456 Mon Sep 17 00:00:00 2001 From: Piotr Wojtaszczyk Date: Thu, 27 Jun 2024 17:00:29 +0200 Subject: ASoC: fsl: Add i2s and pcm drivers for LPC32xx CPUs This driver was ported from an old version in linux 2.6.27 and adjusted for the new ASoC framework and DMA API. Signed-off-by: Piotr Wojtaszczyk Link: https://patch.msgid.link/20240627150046.258795-12-piotr.wojtaszczyk@timesys.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + sound/soc/fsl/Kconfig | 7 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/lpc3xxx-i2s.c | 375 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/lpc3xxx-i2s.h | 79 ++++++++++ sound/soc/fsl/lpc3xxx-pcm.c | 72 +++++++++ 6 files changed, 536 insertions(+) create mode 100644 sound/soc/fsl/lpc3xxx-i2s.c create mode 100644 sound/soc/fsl/lpc3xxx-i2s.h create mode 100644 sound/soc/fsl/lpc3xxx-pcm.c diff --git a/MAINTAINERS b/MAINTAINERS index 5e5701402009..93f8f2ed3cb2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8917,6 +8917,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Maintained F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml +F: sound/soc/fsl/lpc3xxx-* FREESCALE SOC SOUND QMC DRIVER M: Herve Codina diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 9a371d4496c2..8b7088623b6a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -131,6 +131,13 @@ config SND_SOC_FSL_RPMSG This option is only useful for out-of-tree drivers since in-tree drivers select it automatically. +config SND_SOC_FSL_LPC3XXX + tristate "SoC Audio for NXP LPC32XX CPUs" + depends on ARCH_LPC32XX || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the LPC3XXX I2S interface. + config SND_SOC_IMX_PCM_DMA tristate select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 2fe78eed3a48..2a61e2f96438 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o snd-soc-fsl-audmix-y := fsl_audmix.o snd-soc-fsl-asoc-card-y := fsl-asoc-card.o snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o snd-soc-fsl-sai-y := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o @@ -29,6 +30,7 @@ snd-soc-fsl-qmc-audio-y := fsl_qmc_audio.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o +obj-$(CONFIG_SND_SOC_FSL_LPC3XXX) += snd-soc-fsl-lpc3xxx.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c new file mode 100644 index 000000000000..0e5b4d5202ff --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lpc3xxx-i2s.h" + +#define I2S_PLAYBACK_FLAG 0x1 +#define I2S_CAPTURE_FLAG 0x2 + +#define LPC3XXX_I2S_RATES ( \ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + +#define LPC3XXX_I2S_FORMATS ( \ + SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate) +{ + u32 i2srate; + u32 idxx, idyy; + u32 savedbitclkrate, diff, trate, baseclk; + + /* Adjust rate for sample size (bits) and 2 channels and offset for + * divider in clock output + */ + i2srate = (freq / 100) * 2 * (8 * xbytes); + i2srate = i2srate << 1; + clkrate = clkrate / 100; + baseclk = clkrate; + *clkx = 1; + *clky = 1; + + /* Find the best divider */ + *clkx = *clky = 0; + savedbitclkrate = 0; + diff = ~0; + for (idxx = 1; idxx < 0xFF; idxx++) { + for (idyy = 1; idyy < 0xFF; idyy++) { + trate = (baseclk * idxx) / idyy; + if (abs(trate - i2srate) < diff) { + diff = abs(trate - i2srate); + savedbitclkrate = trate; + *clkx = idxx; + *clky = idyy; + } + } + } +} + +static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + u32 flag; + int ret = 0; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + flag = I2S_PLAYBACK_FLAG; + else + flag = I2S_CAPTURE_FLAG; + + if (flag & i2s_info_p->streams_in_use) { + dev_warn(dev, "I2S channel is busy\n"); + ret = -EBUSY; + return ret; + } + + if (i2s_info_p->streams_in_use == 0) { + ret = clk_prepare_enable(i2s_info_p->clk); + if (ret) { + dev_err(dev, "Can't enable clock, err=%d\n", ret); + return ret; + } + } + + i2s_info_p->streams_in_use |= flag; + return 0; +} + +static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP); + u32 flag; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + flag = I2S_PLAYBACK_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits); + } else { + flag = I2S_CAPTURE_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits); + } + i2s_info_p->streams_in_use &= ~flag; + + if (i2s_info_p->streams_in_use == 0) + clk_disable_unprepare(i2s_info_p->clk); +} + +static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + + /* Will use in HW params later */ + i2s_info_p->freq = freq; + + return 0; +} + +static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { + dev_warn(dev, "unsupported bus format %d\n", fmt); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + dev_warn(dev, "unsupported clock provider %d\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + struct regmap *regs = i2s_info_p->regs; + int xfersize; + u32 tmp, clkx, clky; + + tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP); + xfersize = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP); + xfersize = 2; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP); + xfersize = 4; + break; + + default: + dev_warn(dev, "Unsupported audio data format %d\n", params_format(params)); + return -EINVAL; + } + + if (params_channels(params) == 1) + tmp |= LPC3XXX_I2S_MONO; + + __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate); + + dev_dbg(dev, "Stream : %s\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq); + dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate); + dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize); + dev_dbg(dev, "Clock divider (x) : %d\n", clkx); + dev_dbg(dev, "Clock divider (y) : %d\n", clky); + dev_dbg(dev, "Channels : %d\n", params_channels(params)); + dev_dbg(dev, "Data format : %s\n", "I2S"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(regs, LPC3XXX_REG_I2S_DMA1, + LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp); + } else { + regmap_write(regs, LPC3XXX_REG_I2S_DMA0, + LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp); + } + + return 0; +} + +static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + break; + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config, + &i2s_info_p->capture_dma_config); + return 0; +} + +const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = { + .probe = lpc3xxx_i2s_dai_probe, + .startup = lpc3xxx_i2s_startup, + .shutdown = lpc3xxx_i2s_shutdown, + .trigger = lpc3xxx_i2s_trigger, + .hw_params = lpc3xxx_i2s_hw_params, + .set_sysclk = lpc3xxx_i2s_set_dai_sysclk, + .set_fmt = lpc3xxx_i2s_set_dai_fmt, +}; + +struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = { + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .ops = &lpc3xxx_i2s_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + +static const struct snd_soc_component_driver lpc32xx_i2s_component = { + .name = "lpc32xx-i2s", + .legacy_dai_naming = 1, +}; + +static const struct regmap_config lpc32xx_i2s_regconfig = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPC3XXX_REG_I2S_RX_RATE, +}; + +static int lpc32xx_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpc3xxx_i2s_info *i2s_info_p; + struct resource *res; + void __iomem *iomem; + int ret; + + i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL); + if (!i2s_info_p) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s_info_p); + i2s_info_p->dev = dev; + + iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(iomem)) + return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n"); + + i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig); + if (IS_ERR(i2s_info_p->regs)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs), + "failed to init register map: %d\n", ret); + + i2s_info_p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(i2s_info_p->clk)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n"); + + i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk); + if (i2s_info_p->clkrate == 0) + return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n"); + + mutex_init(&i2s_info_p->lock); + + ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component, + &lpc3xxx_i2s_dai_driver, 1); + if (ret) + return dev_err_probe(dev, ret, "Can't register cpu_dai component\n"); + + i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO); + i2s_info_p->playback_dma_config.maxburst = 4; + + i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO); + i2s_info_p->capture_dma_config.maxburst = 4; + + ret = lpc3xxx_pcm_register(pdev); + if (ret) + return dev_err_probe(dev, ret, "Can't register pcm component\n"); + + return 0; +} + +static const struct of_device_id lpc32xx_i2s_match[] = { + { .compatible = "nxp,lpc3220-i2s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match); + +static struct platform_driver lpc32xx_i2s_driver = { + .probe = lpc32xx_i2s_probe, + .driver = { + .name = "lpc3xxx-i2s", + .of_match_table = lpc32xx_i2s_match, + }, +}; + +module_platform_driver(lpc32xx_i2s_driver); + +MODULE_AUTHOR("Kevin Wells "); +MODULE_AUTHOR("Piotr Wojtaszczyk "); +MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h new file mode 100644 index 000000000000..eec755448478 --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Author: Kevin Wells + * + * Copyright (C) 2008 NXP Semiconductors + * Copyright 2023 Timesys Corporation + */ + +#ifndef __SOUND_SOC_LPC3XXX_I2S_H +#define __SOUND_SOC_LPC3XXX_I2S_H + +#include +#include + +struct lpc3xxx_i2s_info { + struct device *dev; + struct clk *clk; + struct mutex lock; /* To serialize user-space access */ + struct regmap *regs; + u32 streams_in_use; + u32 clkrate; + int freq; + struct snd_dmaengine_dai_dma_data playback_dma_config; + struct snd_dmaengine_dai_dma_data capture_dma_config; +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev); + +/* I2S controller register offsets */ +#define LPC3XXX_REG_I2S_DAO 0x00 +#define LPC3XXX_REG_I2S_DAI 0x04 +#define LPC3XXX_REG_I2S_TX_FIFO 0x08 +#define LPC3XXX_REG_I2S_RX_FIFO 0x0C +#define LPC3XXX_REG_I2S_STAT 0x10 +#define LPC3XXX_REG_I2S_DMA0 0x14 +#define LPC3XXX_REG_I2S_DMA1 0x18 +#define LPC3XXX_REG_I2S_IRQ 0x1C +#define LPC3XXX_REG_I2S_TX_RATE 0x20 +#define LPC3XXX_REG_I2S_RX_RATE 0x24 + +/* i2s_daO i2s_dai register definitions */ +#define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */ +#define LPC3XXX_I2S_WW16 FIELD_PREP(0x3, 1) /* Word width is 16bit */ +#define LPC3XXX_I2S_WW32 FIELD_PREP(0x3, 3) /* Word width is 32bit */ +#define LPC3XXX_I2S_MONO BIT(2) /* Mono */ +#define LPC3XXX_I2S_STOP BIT(3) /* Stop, diables the access to FIFO, mutes the channel */ +#define LPC3XXX_I2S_RESET BIT(4) /* Reset the channel */ +#define LPC3XXX_I2S_WS_SEL BIT(5) /* Channel Master(0) or slave(1) mode select */ +#define LPC3XXX_I2S_WS_HP(s) FIELD_PREP(0x7FC0, s) /* Word select half period - 1 */ +#define LPC3XXX_I2S_MUTE BIT(15) /* Mute the channel, Transmit channel only */ + +#define LPC3XXX_I2S_WW32_HP 0x1f /* Word select half period for 32bit word width */ +#define LPC3XXX_I2S_WW16_HP 0x0f /* Word select half period for 16bit word width */ +#define LPC3XXX_I2S_WW8_HP 0x7 /* Word select half period for 8bit word width */ + +/* i2s_stat register definitions */ +#define LPC3XXX_I2S_IRQ_STAT BIT(0) +#define LPC3XXX_I2S_DMA0_REQ BIT(1) +#define LPC3XXX_I2S_DMA1_REQ BIT(2) + +/* i2s_dma0 Configuration register definitions */ +#define LPC3XXX_I2S_DMA0_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA0_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA0_RX_DEPTH(s) FIELD_PREP(0xF00, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */ + +/* i2s_dma1 Configuration register definitions */ +#define LPC3XXX_I2S_DMA1_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA1_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA1_TX_DEPTH(s) FIELD_PREP(0x70000, s) /* Set the DMA1 TX Request level */ + +/* i2s_irq register definitions */ +#define LPC3XXX_I2S_RX_IRQ_EN BIT(0) /* Enable RX IRQ */ +#define LPC3XXX_I2S_TX_IRQ_EN BIT(1) /* Enable TX IRQ */ +#define LPC3XXX_I2S_IRQ_RX_DEPTH(s) FIELD_PREP(0xFF00, s) /* valid values ar 0 to 7 */ +#define LPC3XXX_I2S_IRQ_TX_DEPTH(s) FIELD_PREP(0xFF0000, s) /* valid values ar 0 to 7 */ + +#endif diff --git a/sound/soc/fsl/lpc3xxx-pcm.c b/sound/soc/fsl/lpc3xxx-pcm.c new file mode 100644 index 000000000000..c0d499b9b8ba --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-pcm.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "lpc3xxx-i2s.h" + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static const struct snd_pcm_hardware lpc3xxx_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = STUB_FORMATS, + .period_bytes_min = 128, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024 +}; + +static const struct snd_dmaengine_pcm_config lpc3xxx_dmaengine_pcm_config = { + .pcm_hardware = &lpc3xxx_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = pl08x_filter_id, + .prealloc_buffer_size = 128 * 1024, +}; + +const struct snd_soc_component_driver lpc3xxx_soc_platform_driver = { + .name = "lpc32xx-pcm", +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev) +{ + int ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &lpc3xxx_dmaengine_pcm_config, 0); + if (ret) { + dev_err(&pdev->dev, "failed to register dmaengine: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, &lpc3xxx_soc_platform_driver, + NULL, 0); +} +EXPORT_SYMBOL(lpc3xxx_pcm_register); -- cgit v1.2.3 From 4adf454ff62883ee64b0b9818b951f34aa078d5a Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 28 Jun 2024 14:01:29 +0200 Subject: ASoC: soc-utils: allow sample rate up to 768kHz for the dummy dai The dummy DAI should allow any (reasonable) rates possible. Make the rate continuous for dummy and set range from 5512Hz to 768kHz The change is mostly cosmetic as dummy is skipped when setting the hwparams. Signed-off-by: Jerome Brunet Link: https://patch.msgid.link/20240628120130.2015665-1-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/soc-utils.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 11ba89c6b83c..303823dc45d7 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -144,7 +144,6 @@ static const struct snd_soc_component_driver dummy_codec = { .endianness = 1, }; -#define STUB_RATES SNDRV_PCM_RATE_8000_384000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ @@ -198,14 +197,18 @@ static struct snd_soc_dai_driver dummy_dai = { .stream_name = "Playback", .channels_min = 1, .channels_max = 384, - .rates = STUB_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 768000, .formats = STUB_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 384, - .rates = STUB_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 768000, .formats = STUB_FORMATS, }, .ops = &dummy_dai_ops, -- cgit v1.2.3 From 061505a1a61169bd25dd908f95ba22f33dffdd69 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 28 Jun 2024 13:24:06 +0800 Subject: ASoC: Remove unneeded semicolon ./sound/soc/codecs/ak4619.c:757:2-3: Unneeded semicolon Reported-by: Abaci Robot Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=9442 Signed-off-by: Yang Li Link: https://patch.msgid.link/20240628052406.36644-1-yang.lee@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4619.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c index cbe27abe1f50..8f2442482f72 100644 --- a/sound/soc/codecs/ak4619.c +++ b/sound/soc/codecs/ak4619.c @@ -754,7 +754,7 @@ static void ak4619_hw_constraints(struct ak4619_priv *ak4619, default: break; } - }; + } constraint->list = ak4619_sr; constraint->mask = ak4619_rate_mask; -- cgit v1.2.3 From 1a70579723fde3624a72dfea6e79e55be6e36659 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Fri, 28 Jun 2024 16:05:34 +0800 Subject: ASoC: max98088: Check for clk_prepare_enable() error clk_prepare_enable() may fail, so we should better check its return value and propagate it in the case of error. Fixes: 62a7fc32a628 ("ASoC: max98088: Add master clock handling") Signed-off-by: Chen Ni Link: https://patch.msgid.link/20240628080534.843815-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/max98088.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 8b56ee550c09..8b0645c63462 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1318,6 +1318,7 @@ static int max98088_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -1333,10 +1334,13 @@ static int max98088_set_bias_level(struct snd_soc_component *component, */ if (!IS_ERR(max98088->mclk)) { if (snd_soc_component_get_bias_level(component) == - SND_SOC_BIAS_ON) + SND_SOC_BIAS_ON) { clk_disable_unprepare(max98088->mclk); - else - clk_prepare_enable(max98088->mclk); + } else { + ret = clk_prepare_enable(max98088->mclk); + if (ret) + return ret; + } } break; -- cgit v1.2.3 From 288921232dd97bb2e35cff2ee38957db5b3e5ef1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 27 Jun 2024 17:52:59 +0800 Subject: ASoC: dt-bindings: fsl,xcvr: Adjust the number of interrupts On i.MX8MP, there are 3 interrupts, the third interrupt is SPDIF wakeup interrupt from PHY Add ref: dai-common.yaml for #sound-dai-cells is needed and XCVR is a DAI device. Signed-off-by: Shengjiu Wang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/1719481981-4069-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,xcvr.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml index 197ae8ba9c30..5e2801014221 100644 --- a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml @@ -42,6 +42,7 @@ properties: items: - description: WAKEUPMIX Audio XCVR Interrupt 1 - description: WAKEUPMIX Audio XCVR Interrupt 2 + - description: SPDIF wakeup interrupt from PHY minItems: 1 clocks: @@ -88,6 +89,7 @@ required: - dma-names allOf: + - $ref: dai-common.yaml# - if: properties: compatible: @@ -112,7 +114,8 @@ allOf: else: properties: interrupts: - maxItems: 1 + minItems: 3 + maxItems: 3 - if: properties: @@ -128,7 +131,7 @@ allOf: clock-names: maxItems: 4 -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -143,7 +146,9 @@ examples: <0x30cc0c00 0x080>, <0x30cc0e00 0x080>; reg-names = "ram", "regs", "rxfifo", "txfifo"; - interrupts = <0x0 128 IRQ_TYPE_LEVEL_HIGH>; + interrupts = , + , + ; clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>, <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>, <&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>, -- cgit v1.2.3 From 730674b21db12be6ea6b3034c618de5d7a16987d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 28 Jun 2024 14:32:54 +0200 Subject: ASoC: meson: tdm: add sample rate support up to 768kHz Add support for 705.6kHz and 768kHz sample rates Signed-off-by: Jerome Brunet Link: https://patch.msgid.link/20240628123256.2019224-1-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.c | 2 +- sound/soc/meson/axg-frddr.c | 4 ++-- sound/soc/meson/axg-tdm.h | 2 +- sound/soc/meson/axg-toddr.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index 59abe0b3c59f..7e6090af720b 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -32,7 +32,7 @@ static const struct snd_pcm_hardware axg_fifo_hw = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), .formats = AXG_FIFO_FORMATS, .rate_min = 5512, - .rate_max = 384000, + .rate_max = 768000, .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, .period_bytes_min = AXG_FIFO_BURST, diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index e97d43ae7fd2..e70c8c34c7db 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -112,7 +112,7 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, @@ -189,7 +189,7 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h index daaca10fec9e..1a17f546ce6e 100644 --- a/sound/soc/meson/axg-tdm.h +++ b/sound/soc/meson/axg-tdm.h @@ -16,7 +16,7 @@ #define AXG_TDM_NUM_LANES 4 #define AXG_TDM_CHANNEL_MAX 128 #define AXG_TDM_RATES (SNDRV_PCM_RATE_5512 | \ - SNDRV_PCM_RATE_8000_384000) + SNDRV_PCM_RATE_8000_768000) #define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index e03a6e21c1c6..03512da4092b 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -131,7 +131,7 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, @@ -228,7 +228,7 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, -- cgit v1.2.3 From 5d7e328e20b3d2bd3e1e8bea7a868ab8892aeed1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Jul 2024 11:44:42 +0100 Subject: ASoC: cs35l56: Revert support for dual-ownership of ASP registers This patch reverts a series of commits that allowed for the ASP registers to be owned by either the driver or the firmware. Nothing currently depends on the functionality that is being reverted, so it is safe to remove. The commits being reverted are (last 3 are bugfixes to the first 2): commit 72a77d7631c6 ("ASoC: cs35l56: Fix to ensure ASP1 registers match cache") commit 07f7d6e7a124 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") commit 4703b014f28b ("ASoC: cs35l56: fix reversed if statement in cs35l56_dspwait_asp1tx_put()") commit c14f09f010cc ("ASoC: cs35l56: Fix deadlock in ASP1 mixer register initialization") commit dfd2ffb37399 ("ASoC: cs35l56: Prevent overwriting firmware ASP config") These reverts have been squashed into a single commit because there would be no reason to revert only some of them (which would just reintroduce bugs). The changes introduced by the commits were well-intentioned but somewhat misguided. ACPI does not provide any information about how audio hardware is linked together, so that information has to be hardcoded into drivers. On Windows the firmware is customized to statically setup appropriate configuration of the audio links, and the intent of the commits was to re-use this information if the Linux host drivers aren't taking control of the ASP. This would avoid having to hardcode the ASP config into the machine driver on some systems. However, this added complexity and race conditions into the driver. It also complicates implementation of new code. The only case where the ASP is used but the host is not taking ownership is when CS35L56 is used in SoundWire mode with the ASP as a reference audio interconnect. But even in that case it's not necessarily required even if the firmware initialized it. Typically it is used to avoid the host SDCA drivers having to be capable of aggregating capture paths from multiple SoundWire peripherals. But the SOF SoundWire support is capable of doing that aggregation. Reverting all these commits significantly simplifies the driver. Let's just use the normal Linux mechanisms of the machine driver and ALSA controls to set things up instead of trying to use the firmware to do use-case setup. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240701104444.172556-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 9 +- sound/soc/codecs/cs35l56-shared.c | 101 +++++-------------- sound/soc/codecs/cs35l56.c | 205 +++----------------------------------- sound/soc/codecs/cs35l56.h | 1 - 4 files changed, 43 insertions(+), 273 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 1a3c6f66f620..2e19722989f7 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -267,13 +267,18 @@ struct cs35l56_base { bool fw_patched; bool secured; bool can_hibernate; - bool fw_owns_asp1; bool cal_data_valid; s8 cal_index; struct cirrus_amp_cal_data cal_data; struct gpio_desc *reset_gpio; }; +/* Temporary to avoid a build break with the HDA driver */ +static inline int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) +{ + return 0; +} + extern struct regmap_config cs35l56_regmap_i2c; extern struct regmap_config cs35l56_regmap_spi; extern struct regmap_config cs35l56_regmap_sdw; @@ -284,8 +289,6 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; int cs35l56_set_patch(struct cs35l56_base *cs35l56_base); -int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base); -int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base); int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base); int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 880228f89baf..035d7c3c4683 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -20,6 +20,18 @@ static const struct reg_sequence cs35l56_patch[] = { * Firmware can change these to non-defaults to satisfy SDCA. * Ensure that they are at known defaults. */ + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000000 }, + { CS35L56_ASP1TX2_INPUT, 0x00000000 }, + { CS35L56_ASP1TX3_INPUT, 0x00000000 }, + { CS35L56_ASP1TX4_INPUT, 0x00000000 }, { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, @@ -41,12 +53,18 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { /* no defaults for OTP_MEM - first read populates cache */ - /* - * No defaults for ASP1 control or ASP1TX mixer. See - * cs35l56_populate_asp1_register_defaults() and - * cs35l56_sync_asp1_mixer_widgets_with_firmware(). - */ - + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000000 }, + { CS35L56_ASP1TX2_INPUT, 0x00000000 }, + { CS35L56_ASP1TX3_INPUT, 0x00000000 }, + { CS35L56_ASP1TX4_INPUT, 0x00000000 }, { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, @@ -206,77 +224,6 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } -static const struct reg_sequence cs35l56_asp1_defaults[] = { - REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000), - REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028), - REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200), - REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002), - REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100), - REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100), - REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018), - REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018), - REG_SEQ0(CS35L56_ASP1TX1_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX2_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX3_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX4_INPUT, 0x00000000), -}; - -/* - * The firmware can have control of the ASP so we don't provide regmap - * with defaults for these registers, to prevent a regcache_sync() from - * overwriting the firmware settings. But if the machine driver hooks up - * the ASP it means the driver is taking control of the ASP, so then the - * registers are populated with the defaults. - */ -int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base) -{ - if (!cs35l56_base->fw_owns_asp1) - return 0; - - cs35l56_base->fw_owns_asp1 = false; - - return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults, - ARRAY_SIZE(cs35l56_asp1_defaults)); -} -EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED); - -/* - * The firmware boot sequence can overwrite the ASP1 config registers so that - * they don't match regmap's view of their values. Rewrite the values from the - * regmap cache into the hardware registers. - */ -int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) -{ - struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)]; - int i, ret; - - if (cs35l56_base->fw_owns_asp1) - return 0; - - memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs)); - - /* Read current values from regmap cache into the write sequence */ - for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { - ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); - if (ret) - goto err; - } - - /* Write the values cache-bypassed so that they will be written to silicon */ - ret = regmap_multi_reg_write_bypassed(cs35l56_base->regmap, asp1_regs, - ARRAY_SIZE(asp1_regs)); - if (ret) - goto err; - - return 0; - -err: - dev_err(cs35l56_base->dev, "Failed to sync ASP1 registers: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_force_sync_asp1_registers_from_cache, SND_SOC_CS35L56_SHARED); - int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command) { unsigned int val; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 758dfdf9d3ea..7cac9812f5b5 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -63,131 +63,6 @@ static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol, return snd_soc_put_volsw(kcontrol, ucontrol); } -static const unsigned short cs35l56_asp1_mixer_regs[] = { - CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX2_INPUT, - CS35L56_ASP1TX3_INPUT, CS35L56_ASP1TX4_INPUT, -}; - -static const char * const cs35l56_asp1_mux_control_names[] = { - "ASP1 TX1 Source", "ASP1 TX2 Source", "ASP1 TX3 Source", "ASP1 TX4 Source" -}; - -static int cs35l56_sync_asp1_mixer_widgets_with_firmware(struct cs35l56_private *cs35l56) -{ - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component); - const char *prefix = cs35l56->component->name_prefix; - char full_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - const char *name; - struct snd_kcontrol *kcontrol; - struct soc_enum *e; - unsigned int val[4]; - int i, item, ret; - - if (cs35l56->asp1_mixer_widgets_initialized) - return 0; - - /* - * Resume so we can read the registers from silicon if the regmap - * cache has not yet been populated. - */ - ret = pm_runtime_resume_and_get(cs35l56->base.dev); - if (ret < 0) - return ret; - - /* Wait for firmware download and reboot */ - cs35l56_wait_dsp_ready(cs35l56); - - ret = regmap_bulk_read(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, - val, ARRAY_SIZE(val)); - - pm_runtime_mark_last_busy(cs35l56->base.dev); - pm_runtime_put_autosuspend(cs35l56->base.dev); - - if (ret) { - dev_err(cs35l56->base.dev, "Failed to read ASP1 mixer regs: %d\n", ret); - return ret; - } - - for (i = 0; i < ARRAY_SIZE(cs35l56_asp1_mux_control_names); ++i) { - name = cs35l56_asp1_mux_control_names[i]; - - if (prefix) { - snprintf(full_name, sizeof(full_name), "%s %s", prefix, name); - name = full_name; - } - - kcontrol = snd_soc_card_get_kcontrol_locked(dapm->card, name); - if (!kcontrol) { - dev_warn(cs35l56->base.dev, "Could not find control %s\n", name); - continue; - } - - e = (struct soc_enum *)kcontrol->private_value; - item = snd_soc_enum_val_to_item(e, val[i] & CS35L56_ASP_TXn_SRC_MASK); - snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); - } - - cs35l56->asp1_mixer_widgets_initialized = true; - - return 0; -} - -static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int index = e->shift_l; - unsigned int addr, val; - int ret; - - ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); - if (ret) - return ret; - - addr = cs35l56_asp1_mixer_regs[index]; - ret = regmap_read(cs35l56->base.regmap, addr, &val); - if (ret) - return ret; - - val &= CS35L56_ASP_TXn_SRC_MASK; - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); - - return 0; -} - -static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int item = ucontrol->value.enumerated.item[0]; - int index = e->shift_l; - unsigned int addr, val; - bool changed; - int ret; - - ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); - if (ret) - return ret; - - addr = cs35l56_asp1_mixer_regs[index]; - val = snd_soc_enum_item_to_val(e, item); - - ret = regmap_update_bits_check(cs35l56->base.regmap, addr, - CS35L56_ASP_TXn_SRC_MASK, val, &changed); - if (ret) - return ret; - - if (changed) - snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); - - return changed; -} - static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); static const struct snd_kcontrol_new cs35l56_controls[] = { @@ -206,44 +81,40 @@ static const struct snd_kcontrol_new cs35l56_controls[] = { }; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, - SND_SOC_NOPM, - 0, 0, + CS35L56_ASP1TX1_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx1_mux = - SOC_DAPM_ENUM_EXT("ASP1TX1 SRC", cs35l56_asp1tx1_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum, - SND_SOC_NOPM, - 1, 0, + CS35L56_ASP1TX2_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx2_mux = - SOC_DAPM_ENUM_EXT("ASP1TX2 SRC", cs35l56_asp1tx2_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum, - SND_SOC_NOPM, - 2, 0, + CS35L56_ASP1TX3_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx3_mux = - SOC_DAPM_ENUM_EXT("ASP1TX3 SRC", cs35l56_asp1tx3_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum, - SND_SOC_NOPM, - 3, 0, + CS35L56_ASP1TX4_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx4_mux = - SOC_DAPM_ENUM_EXT("ASP1TX4 SRC", cs35l56_asp1tx4_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum, CS35L56_SWIRE_DP3_CH1_INPUT, @@ -281,21 +152,6 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, static const struct snd_kcontrol_new sdw1_tx4_mux = SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); -static int cs35l56_asp1_cfg_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - /* Override register values set by firmware boot */ - return cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); - default: - return 0; - } -} - static int cs35l56_play_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -332,9 +188,6 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), - SND_SOC_DAPM_SUPPLY("ASP1 CFG", SND_SOC_NOPM, 0, 0, cs35l56_asp1_cfg_event, - SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -402,9 +255,6 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { { "AMP", NULL, "VDD_B" }, { "AMP", NULL, "VDD_AMP" }, - { "ASP1 Playback", NULL, "ASP1 CFG" }, - { "ASP1 Capture", NULL, "ASP1 CFG" }, - { "ASP1 Playback", NULL, "PLAY" }, { "SDW1 Playback", NULL, "PLAY" }, @@ -455,14 +305,9 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); unsigned int val; - int ret; dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; @@ -536,11 +381,6 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx unsigned int rx_mask, int slots, int slot_width) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; if ((slots == 0) || (slot_width == 0)) { dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); @@ -589,11 +429,6 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); unsigned int rate = params_rate(params); u8 asp_width, asp_wl; - int ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; asp_wl = params_width(params); if (cs35l56->asp_slot_width) @@ -650,11 +485,7 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int freq_id, ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; + int freq_id; if (freq == 0) { cs35l56->sysclk_set = false; @@ -1035,13 +866,6 @@ static int cs35l56_component_probe(struct snd_soc_component *component) debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); - /* - * The widgets for the ASP1TX mixer can't be initialized - * until the firmware has been downloaded and rebooted. - */ - regcache_drop_region(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX4_INPUT); - cs35l56->asp1_mixer_widgets_initialized = false; - queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); return 0; @@ -1432,9 +1256,6 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; - /* Assume that the firmware owns ASP1 until we know different */ - cs35l56->base.fw_owns_asp1 = true; - dev_set_drvdata(cs35l56->base.dev, cs35l56); cs35l56_fill_supply_names(cs35l56->supplies); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index b000e7365e40..200f695efca3 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -51,7 +51,6 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; - bool asp1_mixer_widgets_initialized; u8 old_sdw_clock_scale; }; -- cgit v1.2.3 From e2996141d6db0d8b353e1c221a37c8e1be109d4a Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Jul 2024 11:44:43 +0100 Subject: ASoC: cs35l56: Remove support for A1 silicon No product was ever released with A1 silicon so there is no need for the driver to include support for it. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240701104444.172556-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 2 -- sound/soc/codecs/cs35l56-sdw.c | 74 --------------------------------------- sound/soc/codecs/cs35l56-shared.c | 21 ++--------- sound/soc/codecs/cs35l56.h | 1 - 4 files changed, 2 insertions(+), 96 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 2e19722989f7..642ef690ebc2 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -80,9 +80,7 @@ #define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044 #define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000 #define CS35L56_DSP1_FW_VER 0x2800010 -#define CS35L56_DSP1_HALO_STATE_A1 0x2801E58 #define CS35L56_DSP1_HALO_STATE 0x28021E0 -#define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000 #define CS35L56_DSP1_PM_CUR_STATE 0x2804308 #define CS35L56_DSP1_XMEM_UNPACKED24_8191 0x2807FFC #define CS35L56_DSP1_CORE_BASE 0x2B80000 diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 70ff55c1517f..8862107105ae 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -317,79 +317,6 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, return 0; } -static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, - struct sdw_slave *peripheral) -{ - unsigned int curr_scale_reg, next_scale_reg; - int curr_scale, next_scale, ret; - - if (!cs35l56->base.init_done) - return 0; - - if (peripheral->bus->params.curr_bank) { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - } else { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - } - - /* - * Current clock scale value must be different to new value. - * Modify current to guarantee this. If next still has the dummy - * value we wrote when it was current, the core code has not set - * a new scale so restore its original good value - */ - curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); - if (curr_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); - return curr_scale; - } - - next_scale = sdw_read_no_pm(peripheral, next_scale_reg); - if (next_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); - return next_scale; - } - - if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { - next_scale = cs35l56->old_sdw_clock_scale; - ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", - ret); - return ret; - } - } - - cs35l56->old_sdw_clock_scale = curr_scale; - ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); - return ret; - } - - dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); - - return 0; -} - -static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, - struct sdw_bus_params *params) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - int sclk; - - sclk = params->curr_dr_freq / 2; - dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", - __func__, sclk, params->col, params->row); - - if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0)) - return cs35l56_a1_kick_divider(cs35l56, peripheral); - - return 0; -} - static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -405,7 +332,6 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, - .bus_config = cs35l56_sdw_bus_config, #ifdef DEBUG .clk_stop = cs35l56_sdw_clk_stop, #endif diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 035d7c3c4683..e7e8d617da94 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -245,19 +245,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base) { int ret; - unsigned int reg; unsigned int val; ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN); if (ret) return ret; - if (cs35l56_base->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_PM_CUR_STATE_A1; - else - reg = CS35L56_DSP1_PM_CUR_STATE; - - ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, + ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP1_PM_CUR_STATE, val, (val == CS35L56_HALO_STATE_SHUTDOWN), CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US); @@ -270,15 +264,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, SND_SOC_CS35L56_SHARED); int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) { - unsigned int reg; unsigned int val = 0; int read_ret, poll_ret; - if (cs35l56_base->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_HALO_STATE_A1; - else - reg = CS35L56_DSP1_HALO_STATE; - /* * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read of the status register. @@ -288,7 +276,7 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US, false, - cs35l56_base->regmap, reg, &val); + cs35l56_base->regmap, CS35L56_DSP1_HALO_STATE, &val); if (poll_ret) { dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n", @@ -726,11 +714,6 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) else cs35l56_wait_control_port_ready(); - /* - * The HALO_STATE register is in different locations on Ax and B0 - * devices so the REVID needs to be determined before waiting for the - * firmware to boot. - */ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 200f695efca3..8a987ec01507 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -51,7 +51,6 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; - u8 old_sdw_clock_scale; }; extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; -- cgit v1.2.3 From e5524e3316ac54ca04dde3bfef4565bacf72c594 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 1 Jul 2024 11:44:44 +0100 Subject: ASoC: cs35l56: Remove redundant clearing of clk_stop_mode1 struct sdw_slave_prop is zero-initialized by the SoundWire core so there is no need to clear clk_stop_mode1 to false. Removing this also avoids having an unnecessary build dependency on a struct member. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240701104444.172556-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 8862107105ae..fc03bb7ecae1 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -271,7 +271,6 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); prop->paging_support = true; - prop->clk_stop_mode1 = false; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; -- cgit v1.2.3 From 8e5c11963c5ca4af90b36147cabb4835e59990aa Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Wed, 26 Jun 2024 16:12:01 +0900 Subject: ASoC: dt-bindings: fsl_rpmsg: Add compatible string for i.MX95 Add compatible string for i.MX95 platform which supports audio function through rpmsg channel between Cortex-A and Cortex-M core. Signed-off-by: Chancel Liu Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240626071202.7149-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index 188f38baddec..3d5d435c765b 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -29,6 +29,7 @@ properties: - fsl,imx8mp-rpmsg-audio - fsl,imx8ulp-rpmsg-audio - fsl,imx93-rpmsg-audio + - fsl,imx95-rpmsg-audio clocks: items: -- cgit v1.2.3 From 19dec6650e3fd02de8752c4e0ab1c4894ce7fcb7 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Wed, 26 Jun 2024 16:12:02 +0900 Subject: ASoC: fsl_rpmsg: Add support for i.MX95 platform Add compatible string and specific soc data to support rpmsg sound card on i.MX95 platform. Signed-off-by: Chancel Liu Acked-by: Shengjiu Wang Link: https://patch.msgid.link/20240626071202.7149-2-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_rpmsg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index bc41a0666856..467d6bc9f956 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -175,6 +175,14 @@ static const struct fsl_rpmsg_soc_data imx93_data = { SNDRV_PCM_FMTBIT_S32_LE, }; +static const struct fsl_rpmsg_soc_data imx95_data = { + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, +}; + static const struct of_device_id fsl_rpmsg_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data}, { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data}, @@ -182,6 +190,7 @@ static const struct of_device_id fsl_rpmsg_ids[] = { { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data}, { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data}, { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data}, + { .compatible = "fsl,imx95-rpmsg-audio", .data = &imx95_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids); -- cgit v1.2.3 From 878f4c36f9235e8a15fe0c2ecde066d92c50c8ff Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Mon, 24 Jun 2024 18:59:44 +0530 Subject: ASoC: dt-bindings: realtek,rt5659: Convert to dtschema Convert the RT5659/RT5658 audio CODEC bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240624132949.124228-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/realtek,rt5659.yaml | 129 +++++++++++++++++++++ Documentation/devicetree/bindings/sound/rt5659.txt | 89 -------------- 2 files changed, 129 insertions(+), 89 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5659.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5659.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml new file mode 100644 index 000000000000..1100ffd9a7c0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5659.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5659/RT5658 audio CODEC + +maintainers: + - Animesh Agarwal + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes) for RT5659/RT5658: + * DMIC L1 + * DMIC R1 + * DMIC L2 + * DMIC R2 + * IN1P + * IN1N + * IN2P + * IN2N + * IN3P + * IN3N + * IN4P + * IN4N + * HPOL + * HPOR + * SPOL + * SPOR + * LOUTL + * LOUTR + * MONOOUT + * PDML + * PDMR + * SPDIF + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - realtek,rt5659 + - realtek,rt5658 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: mclk + + realtek,dmic1-data-pin: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic1 is not used + - 1 # using IN2N pin as dmic1 data pin + - 2 # using GPIO5 pin as dmic1 data pin + - 3 # using GPIO9 pin as dmic1 data pin + - 4 # using GPIO11 pin as dmic1 data pin + description: Specify which pin to be used as DMIC1 data pin. + default: 0 + + realtek,dmic2-data-pin: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic2 is not used + - 1 # using IN2P pin as dmic2 data pin + - 2 # using GPIO6 pin as dmic2 data pin + - 3 # using GPIO10 pin as dmic2 data pin + - 4 # using GPIO12 pin as dmic2 data pin + description: Specify which pin to be used as DMIC2 data pin. + default: 0 + + realtek,jd-src: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # No JD is used + - 1 # using JD3 as JD source + - 2 # JD source for Intel HDA header + description: Specify which JD source be used. + default: 0 + + realtek,ldo1-en-gpios: + maxItems: 1 + description: CODEC's LDO1_EN pin. + + realtek,reset-gpios: + maxItems: 1 + description: CODEC's RESET pin. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1b { + compatible = "realtek,rt5659"; + reg = <0x1b>; + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + realtek,ldo1-en-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5659.txt b/Documentation/devicetree/bindings/sound/rt5659.txt deleted file mode 100644 index 8f3f62c0226a..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5659.txt +++ /dev/null @@ -1,89 +0,0 @@ -RT5659/RT5658 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : One of "realtek,rt5659" or "realtek,rt5658". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC -- clock-names: Should be "mclk" - -- realtek,in1-differential -- realtek,in3-differential -- realtek,in4-differential - Boolean. Indicate MIC1/3/4 input are differential, rather than single-ended. - -- realtek,dmic1-data-pin - 0: dmic1 is not used - 1: using IN2N pin as dmic1 data pin - 2: using GPIO5 pin as dmic1 data pin - 3: using GPIO9 pin as dmic1 data pin - 4: using GPIO11 pin as dmic1 data pin - -- realtek,dmic2-data-pin - 0: dmic2 is not used - 1: using IN2P pin as dmic2 data pin - 2: using GPIO6 pin as dmic2 data pin - 3: using GPIO10 pin as dmic2 data pin - 4: using GPIO12 pin as dmic2 data pin - -- realtek,jd-src - 0: No JD is used - 1: using JD3 as JD source - 2: JD source for Intel HDA header - -- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. -- realtek,reset-gpios : The GPIO that controls the CODEC's RESET pin. - -- sound-name-prefix: Please refer to dai-common.yaml - -- ports: A Codec may have a single or multiple I2S interfaces. These - interfaces on Codec side can be described under 'ports' or 'port'. - When the SoC or host device is connected to multiple interfaces of - the Codec, the connectivity can be described using 'ports' property. - If a single interface is used, then 'port' can be used. The usage - depends on the platform or board design. - Please refer to Documentation/devicetree/bindings/graph.txt - -Pins on the device (for linking into audio routes) for RT5659/RT5658: - - * DMIC L1 - * DMIC R1 - * DMIC L2 - * DMIC R2 - * IN1P - * IN1N - * IN2P - * IN2N - * IN3P - * IN3N - * IN4P - * IN4N - * HPOL - * HPOR - * SPOL - * SPOR - * LOUTL - * LOUTR - * MONOOUT - * PDML - * PDMR - * SPDIF - -Example: - -rt5659 { - compatible = "realtek,rt5659"; - reg = <0x1b>; - interrupt-parent = <&gpio>; - interrupts = ; - realtek,ldo1-en-gpios = - <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; -}; -- cgit v1.2.3 From 3913ecb38833e8926467a17f1f1679ccd25a7230 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 1 Jul 2024 00:38:51 +0000 Subject: ASoC: simple-audio-mux: enable to select MUX names simple-audio-mux is designed to be used generally, thus "Input 1" or "Input 2" are used to selecting MUX input. This numbered inputs would work, but might be not user friendly in some case, for example in case of system hardware design has some clear labels. Adds new "state-labels" property and enable to select MUX by own state names. Original > amixer set "MUX" "Input 1" Use mux-names sound_mux: mux { compatible = "simple-audio-mux"; mux-gpios = <...>; state-labels = "Label_A", "Label_B"; }; > amixer set "MUX" "Label_A" Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87msn27xpg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/simple-mux.c | 55 +++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index bf67de12d20b..240af0563283 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -9,12 +9,21 @@ #include #include +#define MUX_TEXT_SIZE 2 +#define MUX_WIDGET_SIZE 4 +#define MUX_ROUTE_SIZE 3 struct simple_mux { struct gpio_desc *gpiod_mux; unsigned int mux; + const char *mux_texts[MUX_TEXT_SIZE]; + struct soc_enum mux_enum; + struct snd_kcontrol_new mux_mux; + struct snd_soc_dapm_widget mux_widgets[MUX_WIDGET_SIZE]; + struct snd_soc_dapm_route mux_routes[MUX_ROUTE_SIZE]; + struct snd_soc_component_driver mux_driver; }; -static const char * const simple_mux_texts[] = { +static const char * const simple_mux_texts[MUX_TEXT_SIZE] = { "Input 1", "Input 2" }; @@ -66,30 +75,23 @@ static unsigned int simple_mux_read(struct snd_soc_component *component, static const struct snd_kcontrol_new simple_mux_mux = SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put); -static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[] = { +static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[MUX_WIDGET_SIZE] = { SND_SOC_DAPM_INPUT("IN1"), SND_SOC_DAPM_INPUT("IN2"), - SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), + SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), // see simple_mux_probe() SND_SOC_DAPM_OUTPUT("OUT"), }; -static const struct snd_soc_dapm_route simple_mux_dapm_routes[] = { +static const struct snd_soc_dapm_route simple_mux_dapm_routes[MUX_ROUTE_SIZE] = { { "OUT", NULL, "MUX" }, - { "MUX", "Input 1", "IN1" }, - { "MUX", "Input 2", "IN2" }, -}; - -static const struct snd_soc_component_driver simple_mux_component_driver = { - .dapm_widgets = simple_mux_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(simple_mux_dapm_widgets), - .dapm_routes = simple_mux_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(simple_mux_dapm_routes), - .read = simple_mux_read, + { "MUX", "Input 1", "IN1" }, // see simple_mux_probe() + { "MUX", "Input 2", "IN2" }, // see simple_mux_probe() }; static int simple_mux_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct simple_mux *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -103,7 +105,30 @@ static int simple_mux_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), "Failed to get 'mux' gpio"); - return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); + /* Copy default settings */ + memcpy(&priv->mux_texts, &simple_mux_texts, sizeof(priv->mux_texts)); + memcpy(&priv->mux_enum, &simple_mux_enum, sizeof(priv->mux_enum)); + memcpy(&priv->mux_mux, &simple_mux_mux, sizeof(priv->mux_mux)); + memcpy(&priv->mux_widgets, &simple_mux_dapm_widgets, sizeof(priv->mux_widgets)); + memcpy(&priv->mux_routes, &simple_mux_dapm_routes, sizeof(priv->mux_routes)); + + priv->mux_driver.dapm_widgets = priv->mux_widgets; + priv->mux_driver.num_dapm_widgets = MUX_WIDGET_SIZE; + priv->mux_driver.dapm_routes = priv->mux_routes; + priv->mux_driver.num_dapm_routes = MUX_ROUTE_SIZE; + priv->mux_driver.read = simple_mux_read; + + /* Overwrite text ("Input 1", "Input 2") if property exists */ + of_property_read_string_array(np, "state-labels", priv->mux_texts, MUX_TEXT_SIZE); + + /* switch to use priv data instead of default */ + priv->mux_enum.texts = priv->mux_texts; + priv->mux_mux.private_value = (unsigned long)&priv->mux_enum; + priv->mux_widgets[2].kcontrol_news = &priv->mux_mux; + priv->mux_routes[1].control = priv->mux_texts[0]; // "Input 1" + priv->mux_routes[2].control = priv->mux_texts[1]; // "Input 2" + + return devm_snd_soc_register_component(dev, &priv->mux_driver, NULL, 0); } #ifdef CONFIG_OF -- cgit v1.2.3 From 9337f15f4fd6ebd35d911225c4a88ec82d27b2a2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 1 Jul 2024 00:38:59 +0000 Subject: ASoC: dt-bindings: simple-audio-mux: add state-labels property simple-audio-mux is designed to be used generally, thus "Input 1" or "Input 2" are used to selecting MUX input. This numbered inputs would work, but might be not user friendly in some case, for example in case of system hardware design has some clear labels. Adds new "state-labels" property and enable to select MUX by own state names. Signed-off-by: Kuninori Morimoto Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/87le2m7xp8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/simple-audio-mux.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml b/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml index 9f319caf3db7..194ac1d4f4f5 100644 --- a/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml +++ b/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml @@ -24,6 +24,11 @@ properties: description: | GPIOs used to select the input line. + state-labels: + description: State of input line. default is "Input 1", "Input 2" + $ref: /schemas/types.yaml#/definitions/string-array + maxItems: 2 + sound-name-prefix: true required: @@ -37,4 +42,5 @@ examples: mux { compatible = "simple-audio-mux"; mux-gpios = <&gpio 3 0>; + state-labels = "Label_A", "Label_B"; }; -- cgit v1.2.3 From 49beb4d2e85634ec1e1c82d76461d9552676045d Mon Sep 17 00:00:00 2001 From: Manikantan R Date: Thu, 27 Jun 2024 15:44:38 +0100 Subject: ASoC: dt-bindings: wsa883x: Document port mapping property Document port mapping property for wsa883x. Port mapping is required to be able map correct master ports for VI feedback. All the device ports are not mapped in same order as master ports, so there is a need for having static port mapping for WSA codecs. Signed-off-by: Manikantan R Reviewed-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20240626-port-map-v2-1-6cc1c5608cdd@linaro.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml index 8e462cdf0018..14d312f9c345 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml @@ -32,6 +32,14 @@ properties: vdd-supply: description: VDD Supply for the Codec + qcom,port-mapping: + description: | + Specifies static port mapping between slave and master ports. + In the order of slave port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + '#thermal-sensor-cells': const: 0 -- cgit v1.2.3 From 1cf3295bd108abbd7f128071ae9775fd18394ca9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 27 Jun 2024 15:44:39 +0100 Subject: ASoC: codecs: wsa883x: parse port-mapping information Add support to parse static master port map information from device tree. Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Srinivas Kandagatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240626-port-map-v2-2-6cc1c5608cdd@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 8abce2160d65..f5a15f0e891e 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1398,6 +1398,14 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->sconfig.direction = SDW_DATA_DIR_RX; wsa883x->sconfig.type = SDW_STREAM_PDM; + /** + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1], + WSA883X_MAX_SWR_PORTS)) + dev_dbg(dev, "Static Port mapping not specified\n"); + pdev->prop.sink_ports = GENMASK(WSA883X_MAX_SWR_PORTS, 0); pdev->prop.simple_clk_stop_capable = true; pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; -- cgit v1.2.3 From d47abee9fede21b19d5227061c5a8761ec1659fb Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 27 Jun 2024 15:44:40 +0100 Subject: ASoC: dt-bindings: wsa8840: Document port mapping property Document port mapping property for wsa884x. Port mapping is required to be able map correct master ports. All the device ports are not mapped in same order as master ports, so there is a need for having static port mapping for WSA codecs. Reviewed-by: Krzysztof Kozlowski Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20240626-port-map-v2-3-6cc1c5608cdd@linaro.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml index 22798d22d981..83e0360301e1 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml @@ -32,6 +32,14 @@ properties: description: Powerdown/Shutdown line to use (pin SD_N) maxItems: 1 + qcom,port-mapping: + description: | + Specifies static port mapping between slave and master ports. + In the order of slave port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 6 + maxItems: 6 + '#sound-dai-cells': const: 0 -- cgit v1.2.3 From e1bc5c324bcca3acdbe817ccbf9aa7992d89479d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 27 Jun 2024 15:44:41 +0100 Subject: ASoC: codecs: wsa884x: parse port-mapping information Add support to parse static master port map information from device tree. This is required for correct port mapping between soundwire device and master ports. Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Srinivas Kandagatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240626-port-map-v2-4-6cc1c5608cdd@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index a9767ef0e39d..a6034547b4f3 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1887,6 +1887,14 @@ static int wsa884x_probe(struct sdw_slave *pdev, wsa884x->sconfig.direction = SDW_DATA_DIR_RX; wsa884x->sconfig.type = SDW_STREAM_PDM; + /** + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1], + WSA884X_MAX_SWR_PORTS)) + dev_dbg(dev, "Static Port mapping not specified\n"); + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); pdev->prop.simple_clk_stop_capable = true; pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; -- cgit v1.2.3 From 55f0a1fa6ea4e339c797e9a3292ca0caa4ab3885 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Sat, 29 Jun 2024 18:11:10 +0800 Subject: ASoc: tas2781: Add name_prefix as the prefix name of DSP firmwares and calibrated data files Add name_prefix as the prefix name of DSP firmwares and calibrated data files which stored speaker calibrated impedance. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240629101112.628-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 4d1a0d836e77..cc765d45c6b5 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -394,8 +394,12 @@ static void tasdevice_fw_ready(const struct firmware *fmw, * failing to load DSP firmware is NOT an error. */ tas_priv->fw_state = TASDEVICE_RCA_FW_OK; - scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", - tas_priv->dev_name); + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%s_coef.bin", + tas_priv->name_prefix, tas_priv->dev_name); + else + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", @@ -418,8 +422,15 @@ static void tasdevice_fw_ready(const struct firmware *fmw, * calibrated data inside algo. */ for (i = 0; i < tas_priv->ndev; i++) { - scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", - tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + if (tas_priv->name_prefix) + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s-%s_cal_0x%02x.bin", tas_priv->name_prefix, + tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); + else + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s_cal_0x%02x.bin", tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); ret = tas2781_load_calibration(tas_priv, tas_priv->cal_binaryname[i], i); if (ret != 0) -- cgit v1.2.3 From 4381b88b24a2edf9ce130adc9acd53258ba44116 Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Thu, 27 Jun 2024 16:20:26 +0530 Subject: ASoC: dt-bindings: realtek,rt5677: Convert to dtschema Convert the RT5677 audio CODEC bindings to DT schema. Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240627105030.14360-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/realtek,rt5677.yaml | 135 +++++++++++++++++++++ Documentation/devicetree/bindings/sound/rt5677.txt | 78 ------------ 2 files changed, 135 insertions(+), 78 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5677.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5677.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml new file mode 100644 index 000000000000..9ce23e58e5ea --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5677.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5677 audio CODEC + +maintainers: + - Animesh Agarwal + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes): + * IN1P + * IN1N + * IN2P + * IN2N + * MICBIAS1 + * DMIC1 + * DMIC2 + * DMIC3 + * DMIC4 + * LOUT1 + * LOUT2 + * LOUT3 + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: realtek,rt5677 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + realtek,pow-ldo2-gpio: + maxItems: 1 + description: CODEC's POW_LDO2 pin. + + realtek,reset-gpio: + maxItems: 1 + description: CODEC's RESET pin. Active low. + + realtek,gpio-config: + description: | + Array of six 8bit elements that configures GPIO. + 0 - floating (reset value) + 1 - pull down + 2 - pull up + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 6 + maxItems: 6 + items: + maximum: 2 + + realtek,jd1-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO1 for jd1. + - 2 # GPIO2 for jd1. + - 3 # GPIO3 for jd1. + description: Configures GPIO Mic Jack detection 1. + + realtek,jd2-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO4 for jd2. + - 2 # GPIO5 for jd2. + - 3 # GPIO6 for jd2. + description: Configures GPIO Mic Jack detection 2. + + realtek,jd3-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO4 for jd3. + - 2 # GPIO5 for jd3. + - 3 # GPIO6 for jd3. + description: Configures GPIO Mic Jack detection 3. + +patternProperties: + '^realtek,in[1-2]-differential$': + type: boolean + description: Indicate MIC1/2 input are differential, rather than + single-ended. + + '^realtek,lout[1-3]-differential$': + type: boolean + description: Indicate LOUT1/2/3 outputs are differential, rather than + single-ended. + +required: + - compatible + - reg + - interrupts + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@2c { + compatible = "realtek,rt5677"; + reg = <0x2c>; + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + realtek,pow-ldo2-gpio = <&gpio 3 GPIO_ACTIVE_HIGH>; + realtek,reset-gpio = <&gpio 3 GPIO_ACTIVE_LOW>; + realtek,in1-differential; + realtek,gpio-config = <0 0 0 0 0 2>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt deleted file mode 100644 index da2430099181..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ /dev/null @@ -1,78 +0,0 @@ -RT5677 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : "realtek,rt5677". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -- gpio-controller : Indicates this device is a GPIO controller. - -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters (currently unused). - -Optional properties: - -- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin. -- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. Active low. - -- realtek,in1-differential -- realtek,in2-differential -- realtek,lout1-differential -- realtek,lout2-differential -- realtek,lout3-differential - Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential, - rather than single-ended. - -- realtek,gpio-config - Array of six 8bit elements that configures GPIO. - 0 - floating (reset value) - 1 - pull down - 2 - pull up - -- realtek,jd1-gpio - Configures GPIO Mic Jack detection 1. - Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively. - -- realtek,jd2-gpio -- realtek,jd3-gpio - Configures GPIO Mic Jack detection 2 and 3. - Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively. - -Pins on the device (for linking into audio routes): - - * IN1P - * IN1N - * IN2P - * IN2N - * MICBIAS1 - * DMIC1 - * DMIC2 - * DMIC3 - * DMIC4 - * LOUT1 - * LOUT2 - * LOUT3 - -Example: - -rt5677 { - compatible = "realtek,rt5677"; - reg = <0x2c>; - interrupt-parent = <&gpio>; - interrupts = ; - - gpio-controller; - #gpio-cells = <2>; - - realtek,pow-ldo2-gpio = - <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; - realtek,reset-gpio = <&gpio TEGRA_GPIO(BB, 3) GPIO_ACTIVE_LOW>; - realtek,in1-differential = "true"; - realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */ - realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */ -}; -- cgit v1.2.3 From f4662e6d51ffc0826f975429d3c1b6d1f1a295f1 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jul 2024 09:24:16 -0700 Subject: ASoC: fsl: lpc3xxx-i2s: Avoid using ret uninitialized in lpc32xx_i2s_probe() clang points out that ret may be used uninitialized in lpc32xx_i2s_probe() in an error pointer path (which becomes fatal with CONFIG_WERROR): sound/soc/fsl/lpc3xxx-i2s.c:326:47: error: variable 'ret' is uninitialized when used here [-Werror,-Wuninitialized] 326 | "failed to init register map: %d\n", ret); | ^~~ sound/soc/fsl/lpc3xxx-i2s.c:310:9: note: initialize the variable 'ret' to silence this warning 310 | int ret; | ^ | = 0 1 error generated. One solution would be a small refactoring of the second parameter in dev_err_probe(), PTR_ERR(i2s_info_p->regs), to be the value of ret in the if statement. However, a nicer solution for debugging purposes, which is the point of this statement, would be to use the '%pe' specifier to symbolically print the error pointer value. Do so, which eliminates the uninitialized use of ret, clearing up the warning. Fixes: 0959de657a10 ("ASoC: fsl: Add i2s and pcm drivers for LPC32xx CPUs") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20240701-lpc32xx-asoc-fix-uninitialized-ret-v1-1-985d86189739@kernel.org Signed-off-by: Mark Brown --- sound/soc/fsl/lpc3xxx-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c index 0e5b4d5202ff..af995ca081a3 100644 --- a/sound/soc/fsl/lpc3xxx-i2s.c +++ b/sound/soc/fsl/lpc3xxx-i2s.c @@ -323,7 +323,7 @@ static int lpc32xx_i2s_probe(struct platform_device *pdev) i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig); if (IS_ERR(i2s_info_p->regs)) return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs), - "failed to init register map: %d\n", ret); + "failed to init register map: %pe\n", i2s_info_p->regs); i2s_info_p->clk = devm_clk_get(dev, NULL); if (IS_ERR(i2s_info_p->clk)) -- cgit v1.2.3 From f1afb360b6f1f49e943ac85b26d4224ec67d22d3 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jul 2024 11:26:38 -0700 Subject: ASoC: fsl: lpc3xxx-i2s: Include bitfield.h for FIELD_PREP bitfield.h is not explicitly included but it is required for FIELD_PREP to be expanded by the preprocessor. If it is not implicitly included, there will be a compiler error (as seen with ARCH=hexagon allmodconfig): sound/soc/fsl/lpc3xxx-i2s.c:169:10: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 169 | tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP); | ^ sound/soc/fsl/lpc3xxx-i2s.h:42:30: note: expanded from macro 'LPC3XXX_I2S_WW8' 42 | #define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */ | ^ sound/soc/fsl/lpc3xxx-i2s.c:205:34: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 205 | LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4)); | ^ sound/soc/fsl/lpc3xxx-i2s.h:65:38: note: expanded from macro 'LPC3XXX_I2S_DMA0_TX_DEPTH' 65 | #define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */ | ^ sound/soc/fsl/lpc3xxx-i2s.c:210:34: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 210 | LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4)); | ^ sound/soc/fsl/lpc3xxx-i2s.h:70:38: note: expanded from macro 'LPC3XXX_I2S_DMA1_RX_DEPTH' 70 | #define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */ | ^ Include bitfield.h explicitly, so that FIELD_PREP is always expanded, clearing up the compiler error. Fixes: 0959de657a10 ("ASoC: fsl: Add i2s and pcm drivers for LPC32xx CPUs") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20240701-lpc32xx-asoc-fix-include-for-field_prep-v1-1-0c5d7f71921b@kernel.org Signed-off-by: Mark Brown --- sound/soc/fsl/lpc3xxx-i2s.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h index eec755448478..b6657853017a 100644 --- a/sound/soc/fsl/lpc3xxx-i2s.h +++ b/sound/soc/fsl/lpc3xxx-i2s.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_LPC3XXX_I2S_H #define __SOUND_SOC_LPC3XXX_I2S_H +#include #include #include -- cgit v1.2.3 From 2e068fe01ecbf677d414d4c1b02eea1636dd614c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 28 Jun 2024 11:58:30 +0200 Subject: ASoC: codecs: lpass-rx-macro: Fix using NULL pointer in probe() dev_err The 'rx->dev' is assigned closer to the end of the probe() function, so the dev_err() must not use it - it is still NULL at this point. Instead there is already a local 'dev' variable. Fixes: dbacef05898d ("ASoC: codec: lpass-rx-macro: prepare driver to accomdate new codec versions") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240628095831.207942-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 9c4f0763675d..d47c49c90de3 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3842,7 +3842,7 @@ static int rx_macro_probe(struct platform_device *pdev) rx_2_5_defaults, sizeof(rx_2_5_defaults)); break; default: - dev_err(rx->dev, "Unsupported Codec version (%d)\n", rx->codec_version); + dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version); ret = -EINVAL; goto err; } -- cgit v1.2.3 From 765d3a2aa195882e231835f5d9ac4d99affc8c07 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 28 Jun 2024 11:58:31 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Fix using NULL pointer in probe() dev_err The 'wsa->dev' is assigned closer to the end of the probe() function, so the dev_err() must not use it - it is still NULL at this point. Instead there is already a local 'dev' variable. Fixes: 727de4fbc546 ("ASoC: codecs: lpass-wsa-macro: Correct support for newer v2.5 version") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20240628095831.207942-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 8351f0974e6a..b4e7139bac61 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2800,7 +2800,7 @@ static int wsa_macro_probe(struct platform_device *pdev) break; default: - dev_err(wsa->dev, "Unsupported Codec version (%d)\n", wsa->codec_version); + dev_err(dev, "Unsupported Codec version (%d)\n", wsa->codec_version); return -EINVAL; } -- cgit v1.2.3 From fcad7177eb7854ce244ce863c82583d8a6ce5952 Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Tue, 2 Jul 2024 00:16:21 +0300 Subject: ASoC: dt-bindings: nau8824: Add master clock handling Use master clock "mclk" if provided through device tree. Signed-off-by: Maxim Kochetkov Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701211631.92384-2-fido_max@inbox.ru Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml index 3dbf438c3841..232dc16a94a3 100644 --- a/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml @@ -23,6 +23,14 @@ properties: '#sound-dai-cells': const: 0 + clocks: + items: + - description: The phandle of the master clock to the CODEC + + clock-names: + items: + - const: mclk + interrupts: maxItems: 1 -- cgit v1.2.3 From 1d3724c92dc20584f76033347384b8c1536480af Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Tue, 2 Jul 2024 00:16:22 +0300 Subject: ASoC: codecs: nau8824: Add master clock handling Use master clock "mclk" if provided through device tree. Signed-off-by: Maxim Kochetkov Link: https://patch.msgid.link/20240701211631.92384-3-fido_max@inbox.ru Signed-off-by: Mark Brown --- sound/soc/codecs/nau8824.c | 21 +++++++++++++++++++-- sound/soc/codecs/nau8824.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index f92b95b21cae..12540397fd4d 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -506,6 +506,7 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, struct regmap *regmap = nau8824->regmap; unsigned int value; bool clk_fll, error; + int ret; if (SND_SOC_DAPM_EVENT_OFF(event)) { dev_dbg(nau8824->dev, "system clock control : POWER OFF\n"); @@ -520,8 +521,15 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, } else { nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); } + + clk_disable_unprepare(nau8824->mclk); } else { dev_dbg(nau8824->dev, "system clock control : POWER ON\n"); + + ret = clk_prepare_enable(nau8824->mclk); + if (ret) + return ret; + /* Check the clock source setting is proper or not * no matter the source is from FLL or MCLK. */ @@ -563,16 +571,21 @@ static int dmic_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); int src; + unsigned int freq; + + freq = clk_get_rate(nau8824->mclk); + if (!freq) + freq = nau8824->fs * 256; /* The DMIC clock is gotten from system clock (256fs) divided by * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or * less than 3.072 MHz. */ for (src = 0; src < 5; src++) { - if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK) + if (freq / (0x1 << src) <= DMIC_CLK) break; } - dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256); + dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, freq); regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT)); @@ -1871,6 +1884,10 @@ static int nau8824_read_device_properties(struct device *dev, if (ret) nau8824->jack_eject_debounce = 1; + nau8824->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(nau8824->mclk)) + return PTR_ERR(nau8824->mclk); + return 0; } diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h index 5fcfc43dfc85..d8e19515133c 100644 --- a/sound/soc/codecs/nau8824.h +++ b/sound/soc/codecs/nau8824.h @@ -434,6 +434,7 @@ struct nau8824 { struct snd_soc_jack *jack; struct work_struct jdet_work; struct semaphore jd_sem; + struct clk *mclk; int fs; int irq; int resume_lock; -- cgit v1.2.3 From 62412a9357b16a4e39dc582deb2e2a682b92524c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 2 Jul 2024 12:08:09 +0100 Subject: firmware: cs_dsp: Don't allow writes to read-only controls Add a check to cs_dsp_coeff_write_ctrl() to abort if the control is not writeable. The cs_dsp code originated as an ASoC driver (wm_adsp) where all controls were exported as ALSA controls. It relied on ALSA to enforce the read-only permission. Now that the code has been separated from ALSA/ASoC it must perform its own permission check. This isn't currently causing any problems so there shouldn't be any need to backport this. If the client of cs_dsp exposes the control as an ALSA control, it should set permissions on that ALSA control to protect it. The few uses of cs_dsp_coeff_write_ctrl() inside drivers are for writable controls. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240702110809.16836-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 0d139e4de37c..d2aa0980ed78 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -796,6 +796,9 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, lockdep_assert_held(&ctl->dsp->pwr_lock); + if (ctl->flags && !(ctl->flags & WMFW_CTL_FLAG_WRITEABLE)) + return -EPERM; + if (len + off * sizeof(u32) > ctl->len) return -EINVAL; -- cgit v1.2.3 From 97a05cb4ab12f55e19cf28f6ceca47bb6ed8fea4 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:38 +0200 Subject: ASoC: topology: Simplify code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using goto, when there is no controls, just do a loop when there are. Overall the check seems to be a bit redundant as num_kcontrols will only be above 0 if kcontrols are set anyway, but let's keep it, while simplifying code. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-2-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 4b166294602f..dff83d49a8f6 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -394,13 +394,9 @@ static void soc_tplg_remove_widget(struct snd_soc_component *comp, if (dobj->unload) dobj->unload(comp, dobj); - if (!w->kcontrols) - goto free_news; - - for (i = 0; w->kcontrols && i < w->num_kcontrols; i++) - snd_ctl_remove(card, w->kcontrols[i]); - -free_news: + if (w->kcontrols) + for (i = 0; i < w->num_kcontrols; i++) + snd_ctl_remove(card, w->kcontrols[i]); list_del(&dobj->list); -- cgit v1.2.3 From 7aa129706b4d6f7a2c649b359eea0336eb640feb Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:39 +0200 Subject: ASoC: topology: Do not do unnecessary dobj management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Widget kcontrols do not need to be managed as topology dynamic objects with an index and a linked list. As they are always associated with a widget which is already a topology dynamic object, thus all addition/removals of a widget will by design manage the kcontrol. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-3-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index dff83d49a8f6..bd29b40acb99 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1134,8 +1134,6 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ sm->min = le32_to_cpu(mc->min); sm->invert = le32_to_cpu(mc->invert); sm->platform_max = le32_to_cpu(mc->platform_max); - sm->dobj.index = tplg->index; - INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); @@ -1198,7 +1196,6 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k se->items = le32_to_cpu(ec->items); se->mask = le32_to_cpu(ec->mask); - se->dobj.index = tplg->index; switch (le32_to_cpu(ec->hdr.ops.info)) { case SND_SOC_TPLG_CTL_ENUM_VALUE: @@ -1273,7 +1270,6 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ kc->access = le32_to_cpu(be->hdr.access); sbe->max = le32_to_cpu(be->max); - INIT_LIST_HEAD(&sbe->dobj.list); /* map standard io handlers and check for external handlers */ err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); -- cgit v1.2.3 From 8ec2a2643544ce352f012ad3d248163199d05dfc Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:40 +0200 Subject: ASoC: topology: Properly initialize soc_enum values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit soc_tplg_denum_create_values() should properly set its values field. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-4-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index bd29b40acb99..15c006c47d80 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -847,6 +847,8 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum * se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); } + se->items = le32_to_cpu(ec->items); + se->values = (const unsigned int *)se->dobj.control.dvalues; return 0; } -- cgit v1.2.3 From 52f4f7272c92d5be3988839a33be41b180bbc28e Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:41 +0200 Subject: ASoC: topology: Cleanup soc_tplg_dapm_widget_dbytes_create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor cleanups to increase readability. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-5-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 15c006c47d80..73e127975822 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1249,25 +1249,22 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ be = (struct snd_soc_tplg_bytes_control *)tplg->pos; /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); if (!sbe) return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + - le32_to_cpu(be->priv.size)); + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + le32_to_cpu(be->priv.size)); - dev_dbg(tplg->dev, - "ASoC: adding bytes kcontrol %s with access 0x%x\n", + dev_dbg(tplg->dev, "ASoC: adding bytes kcontrol %s with access 0x%x\n", be->hdr.name, be->hdr.access); - kc->private_value = (long)sbe; kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); if (!kc->name) return -ENOMEM; + kc->private_value = (long)sbe; kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kc->access = le32_to_cpu(be->hdr.access); @@ -1281,11 +1278,7 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &be->hdr); - if (err < 0) - return err; - - return 0; + return soc_tplg_control_load(tplg, kc, &be->hdr); } static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, -- cgit v1.2.3 From 40a16442a048c5f2b4336edd4d202f1930fb9df5 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:42 +0200 Subject: ASoC: topology: Cleanup soc_tplg_dapm_widget_dmixer_create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor cleanups to increase readability. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-6-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 73e127975822..242d8b9c2f03 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1094,43 +1094,37 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { - struct soc_mixer_control *sm; struct snd_soc_tplg_mixer_control *mc; + struct soc_mixer_control *sm; int err; mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); if (!sm) return -ENOMEM; - tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + - le32_to_cpu(mc->priv.size); + tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size); - dev_dbg(tplg->dev, " adding DAPM widget mixer control %s\n", - mc->hdr.name); + dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n", + mc->hdr.name, mc->hdr.access); - kc->private_value = (long)sm; kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); if (!kc->name) return -ENOMEM; + kc->private_value = (long)sm; kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kc->access = le32_to_cpu(mc->hdr.access); /* we only support FL/FR channel mapping atm */ - sm->reg = tplg_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rreg = tplg_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FR); - sm->shift = tplg_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rshift = tplg_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FR); + sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); + sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); sm->max = le32_to_cpu(mc->max); sm->min = le32_to_cpu(mc->min); @@ -1147,17 +1141,12 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ /* create any TLV data */ err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", - mc->hdr.name); + dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); return err; } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &mc->hdr); - if (err < 0) - return err; - - return 0; + return soc_tplg_control_load(tplg, kc, &mc->hdr); } static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -- cgit v1.2.3 From d1d0262a1f5c7a543271bd5899cdb4b3ddfe054b Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:43 +0200 Subject: ASoC: topology: Cleanup soc_tplg_dapm_widget_denum_create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor cleanups to increase readability. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-7-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 242d8b9c2f03..6a2a8249903d 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1156,36 +1156,31 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k int err; ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); if (!se) return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - le32_to_cpu(ec->priv.size)); + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + le32_to_cpu(ec->priv.size)); - dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", - ec->hdr.name); + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items); - kc->private_value = (long)se; kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); if (!kc->name) return -ENOMEM; + kc->private_value = (long)se; kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kc->access = le32_to_cpu(ec->hdr.access); /* we only support FL/FR channel mapping atm */ se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); - se->shift_r = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FR); + se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); - se->items = le32_to_cpu(ec->items); se->mask = le32_to_cpu(ec->mask); switch (le32_to_cpu(ec->hdr.ops.info)) { @@ -1193,8 +1188,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: err = soc_tplg_denum_create_values(tplg, se, ec); if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create values for %s\n", - ec->hdr.name); + dev_err(tplg->dev, "ASoC: could not create values for %s\n", ec->hdr.name); return err; } fallthrough; @@ -1203,8 +1197,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: err = soc_tplg_denum_create_texts(tplg, se, ec); if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create texts for %s\n", - ec->hdr.name); + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", ec->hdr.name); return err; } break; @@ -1222,11 +1215,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &ec->hdr); - if (err < 0) - return err; - - return 0; + return soc_tplg_control_load(tplg, kc, &ec->hdr); } static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -- cgit v1.2.3 From 963942f73b02b053f97e079ed6dae5f3602cc4b1 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:44 +0200 Subject: ASoC: topology: Correctly set shift_r in soc_tplg_denum_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This aligns soc_tplg_denum_create() with soc_tplg_dapm_widget_denum_create(), as there is no reason for difference in behavior. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-8-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6a2a8249903d..e6eb514de569 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -891,7 +891,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); se->shift_r = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); + SNDRV_CHMAP_FR); se->mask = le32_to_cpu(ec->mask); se->dobj.index = tplg->index; -- cgit v1.2.3 From f14a33fcadbacc1d9728e9c75253145ae6e09bbf Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:45 +0200 Subject: ASoC: topology: Align dynamic object initialization for controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit soc_tplg_dbytes_create() missed se->dobj.index initialization, so add it there. Additionally separate dynamic object initialization into separate logical block code. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-9-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index e6eb514de569..72bd3823a015 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -677,10 +677,12 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) kc.access = le32_to_cpu(be->hdr.access); sbe->max = le32_to_cpu(be->max); + + INIT_LIST_HEAD(&sbe->dobj.list); sbe->dobj.type = SND_SOC_DOBJ_BYTES; + sbe->dobj.index = tplg->index; if (tplg->ops) sbe->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&sbe->dobj.list); /* map io handlers */ ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); @@ -750,11 +752,12 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) sm->min = le32_to_cpu(mc->min); sm->invert = le32_to_cpu(mc->invert); sm->platform_max = le32_to_cpu(mc->platform_max); - sm->dobj.index = tplg->index; + + INIT_LIST_HEAD(&sm->dobj.list); sm->dobj.type = SND_SOC_DOBJ_MIXER; + sm->dobj.index = tplg->index; if (tplg->ops) sm->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); @@ -894,11 +897,12 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) SNDRV_CHMAP_FR); se->mask = le32_to_cpu(ec->mask); - se->dobj.index = tplg->index; + + INIT_LIST_HEAD(&se->dobj.list); se->dobj.type = SND_SOC_DOBJ_ENUM; + se->dobj.index = tplg->index; if (tplg->ops) se->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&se->dobj.list); switch (le32_to_cpu(ec->hdr.ops.info)) { case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: -- cgit v1.2.3 From 76d8aa0f727e327a0ba9bcb904865c40d256c292 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:46 +0200 Subject: ASoC: topology: Rename function creating widget kcontrol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In following patches it will be reused to also create standalone kcontrol, so it makes sense to name it in more generic way. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-10-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 72bd3823a015..f85cf67f74b1 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1096,7 +1096,7 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, return ret; } -static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { struct snd_soc_tplg_mixer_control *mc; struct soc_mixer_control *sm; @@ -1153,7 +1153,7 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ return soc_tplg_control_load(tplg, kc, &mc->hdr); } -static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { struct snd_soc_tplg_enum_control *ec; struct soc_enum *se; @@ -1222,7 +1222,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k return soc_tplg_control_load(tplg, kc, &ec->hdr); } -static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { struct snd_soc_tplg_bytes_control *be; struct soc_bytes_ext *sbe; @@ -1350,7 +1350,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = mixer_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER; mixer_count++; - ret = soc_tplg_dapm_widget_dmixer_create(tplg, &kc[i]); + ret = soc_tplg_control_dmixer_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; @@ -1363,7 +1363,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = enum_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM; enum_count++; - ret = soc_tplg_dapm_widget_denum_create(tplg, &kc[i]); + ret = soc_tplg_control_denum_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; @@ -1372,7 +1372,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = bytes_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES; bytes_count++; - ret = soc_tplg_dapm_widget_dbytes_create(tplg, &kc[i]); + ret = soc_tplg_control_dbytes_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; -- cgit v1.2.3 From c2dad0db01fcacd44616e9d4c7263b187c4ff530 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:47 +0200 Subject: ASoC: topology: Reshuffle function placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for following cleanups move functions around. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-11-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 466 +++++++++++++++++++++++------------------------ 1 file changed, 233 insertions(+), 233 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f85cf67f74b1..5aab1df7e260 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -640,6 +640,239 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg, return 0; } +static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +{ + struct snd_soc_tplg_mixer_control *mc; + struct soc_mixer_control *sm; + int err; + + mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); + if (!sm) + return -ENOMEM; + + tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size); + + dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n", + mc->hdr.name, mc->hdr.access); + + kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)sm; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(mc->hdr.access); + + /* we only support FL/FR channel mapping atm */ + sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); + sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); + + sm->max = le32_to_cpu(mc->max); + sm->min = le32_to_cpu(mc->min); + sm->invert = le32_to_cpu(mc->invert); + sm->platform_max = le32_to_cpu(mc->platform_max); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); + if (err) { + soc_control_err(tplg, &mc->hdr, mc->hdr.name); + return err; + } + + /* create any TLV data */ + err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); + return err; + } + + /* pass control to driver for optional further init */ + return soc_tplg_control_load(tplg, kc, &mc->hdr); +} + +static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se, + struct snd_soc_tplg_enum_control *ec) +{ + int i, ret; + + if (le32_to_cpu(ec->items) > ARRAY_SIZE(ec->texts)) + return -EINVAL; + + se->dobj.control.dtexts = + devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), sizeof(char *), GFP_KERNEL); + if (se->dobj.control.dtexts == NULL) + return -ENOMEM; + + for (i = 0; i < le32_to_cpu(ec->items); i++) { + + if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + ret = -EINVAL; + goto err; + } + + se->dobj.control.dtexts[i] = devm_kstrdup(tplg->dev, ec->texts[i], GFP_KERNEL); + if (!se->dobj.control.dtexts[i]) { + ret = -ENOMEM; + goto err; + } + } + + se->items = le32_to_cpu(ec->items); + se->texts = (const char * const *)se->dobj.control.dtexts; + return 0; + +err: + return ret; +} + +static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *se, + struct snd_soc_tplg_enum_control *ec) +{ + int i; + + /* + * Following "if" checks if we have at most SND_SOC_TPLG_NUM_TEXTS + * values instead of using ARRAY_SIZE(ec->values) due to the fact that + * it is oversized for its purpose. Additionally it is done so because + * it is defined in UAPI header where it can't be easily changed. + */ + if (le32_to_cpu(ec->items) > SND_SOC_TPLG_NUM_TEXTS) + return -EINVAL; + + se->dobj.control.dvalues = devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), + sizeof(*se->dobj.control.dvalues), + GFP_KERNEL); + if (!se->dobj.control.dvalues) + return -ENOMEM; + + /* convert from little-endian */ + for (i = 0; i < le32_to_cpu(ec->items); i++) { + se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); + } + + se->items = le32_to_cpu(ec->items); + se->values = (const unsigned int *)se->dobj.control.dvalues; + return 0; +} + +static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +{ + struct snd_soc_tplg_enum_control *ec; + struct soc_enum *se; + int err; + + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + le32_to_cpu(ec->priv.size)); + + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items); + + kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(ec->hdr.access); + + /* we only support FL/FR channel mapping atm */ + se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); + + se->mask = le32_to_cpu(ec->mask); + + switch (le32_to_cpu(ec->hdr.ops.info)) { + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(tplg, se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create values for %s\n", ec->hdr.name); + return err; + } + fallthrough; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + err = soc_tplg_denum_create_texts(tplg, se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", ec->hdr.name); + return err; + } + break; + default: + dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); + return -EINVAL; + } + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); + if (err) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + return err; + } + + /* pass control to driver for optional further init */ + return soc_tplg_control_load(tplg, kc, &ec->hdr); +} + +static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +{ + struct snd_soc_tplg_bytes_control *be; + struct soc_bytes_ext *sbe; + int err; + + be = (struct snd_soc_tplg_bytes_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); + if (!sbe) + return -ENOMEM; + + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + le32_to_cpu(be->priv.size)); + + dev_dbg(tplg->dev, "ASoC: adding bytes kcontrol %s with access 0x%x\n", + be->hdr.name, be->hdr.access); + + kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)sbe; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(be->hdr.access); + + sbe->max = le32_to_cpu(be->max); + + /* map standard io handlers and check for external handlers */ + err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); + if (err) { + soc_control_err(tplg, &be->hdr, be->hdr.name); + return err; + } + + /* pass control to driver for optional further init */ + return soc_tplg_control_load(tplg, kc, &be->hdr); +} + static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) { struct snd_soc_tplg_bytes_control *be; @@ -789,72 +1022,6 @@ err: return ret; } -static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se, - struct snd_soc_tplg_enum_control *ec) -{ - int i, ret; - - if (le32_to_cpu(ec->items) > ARRAY_SIZE(ec->texts)) - return -EINVAL; - - se->dobj.control.dtexts = - devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), sizeof(char *), GFP_KERNEL); - if (se->dobj.control.dtexts == NULL) - return -ENOMEM; - - for (i = 0; i < le32_to_cpu(ec->items); i++) { - - if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { - ret = -EINVAL; - goto err; - } - - se->dobj.control.dtexts[i] = devm_kstrdup(tplg->dev, ec->texts[i], GFP_KERNEL); - if (!se->dobj.control.dtexts[i]) { - ret = -ENOMEM; - goto err; - } - } - - se->items = le32_to_cpu(ec->items); - se->texts = (const char * const *)se->dobj.control.dtexts; - return 0; - -err: - return ret; -} - -static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *se, - struct snd_soc_tplg_enum_control *ec) -{ - int i; - - /* - * Following "if" checks if we have at most SND_SOC_TPLG_NUM_TEXTS - * values instead of using ARRAY_SIZE(ec->values) due to the fact that - * it is oversized for its purpose. Additionally it is done so because - * it is defined in UAPI header where it can't be easily changed. - */ - if (le32_to_cpu(ec->items) > SND_SOC_TPLG_NUM_TEXTS) - return -EINVAL; - - se->dobj.control.dvalues = devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), - sizeof(*se->dobj.control.dvalues), - GFP_KERNEL); - if (!se->dobj.control.dvalues) - return -ENOMEM; - - /* convert from little-endian */ - for (i = 0; i < le32_to_cpu(ec->items); i++) { - se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); - } - - se->items = le32_to_cpu(ec->items); - se->values = (const unsigned int *)se->dobj.control.dvalues; - return 0; -} - static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) { struct snd_soc_tplg_enum_control *ec; @@ -1096,173 +1263,6 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, return ret; } -static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct snd_soc_tplg_mixer_control *mc; - struct soc_mixer_control *sm; - int err; - - mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); - if (!sm) - return -ENOMEM; - - tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size); - - dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n", - mc->hdr.name, mc->hdr.access); - - kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->private_value = (long)sm; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(mc->hdr.access); - - /* we only support FL/FR channel mapping atm */ - sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); - sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); - - sm->max = le32_to_cpu(mc->max); - sm->min = le32_to_cpu(mc->min); - sm->invert = le32_to_cpu(mc->invert); - sm->platform_max = le32_to_cpu(mc->platform_max); - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &mc->hdr, mc->hdr.name); - return err; - } - - /* create any TLV data */ - err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - return soc_tplg_control_load(tplg, kc, &mc->hdr); -} - -static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct snd_soc_tplg_enum_control *ec; - struct soc_enum *se; - int err; - - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); - if (!se) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + le32_to_cpu(ec->priv.size)); - - dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items); - - kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->private_value = (long)se; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(ec->hdr.access); - - /* we only support FL/FR channel mapping atm */ - se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); - - se->mask = le32_to_cpu(ec->mask); - - switch (le32_to_cpu(ec->hdr.ops.info)) { - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - err = soc_tplg_denum_create_values(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create values for %s\n", ec->hdr.name); - return err; - } - fallthrough; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - err = soc_tplg_denum_create_texts(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create texts for %s\n", ec->hdr.name); - return err; - } - break; - default: - dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - return -EINVAL; - } - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - return soc_tplg_control_load(tplg, kc, &ec->hdr); -} - -static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct snd_soc_tplg_bytes_control *be; - struct soc_bytes_ext *sbe; - int err; - - be = (struct snd_soc_tplg_bytes_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); - if (!sbe) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + le32_to_cpu(be->priv.size)); - - dev_dbg(tplg->dev, "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); - - kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->private_value = (long)sbe; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(be->hdr.access); - - sbe->max = le32_to_cpu(be->max); - - /* map standard io handlers and check for external handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &be->hdr, be->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - return soc_tplg_control_load(tplg, kc, &be->hdr); -} - static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, struct snd_soc_tplg_dapm_widget *w) { -- cgit v1.2.3 From 0867278200f787817036419fc87b56fb04a448e4 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:48 +0200 Subject: ASoC: topology: Unify code for creating standalone and widget bytes control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code used to create standalone and widget bytes control is same, with exception that in standalone case dynamic object needs to be registered and control created directly. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-12-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 51 +++++++++--------------------------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 5aab1df7e260..b10a6be8f75b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -875,41 +875,21 @@ static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcon static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) { - struct snd_soc_tplg_bytes_control *be; + struct snd_kcontrol_new kc = {0}; struct soc_bytes_ext *sbe; - struct snd_kcontrol_new kc; - int ret = 0; + int ret; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_bytes_control), 1, size, "mixer bytes")) return -EINVAL; - be = (struct snd_soc_tplg_bytes_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); - if (sbe == NULL) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + - le32_to_cpu(be->priv.size)); - - dev_dbg(tplg->dev, - "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); - - memset(&kc, 0, sizeof(kc)); - kc.name = be->hdr.name; - kc.private_value = (long)sbe; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(be->hdr.access); + ret = soc_tplg_control_dbytes_create(tplg, &kc); + if (ret) + return ret; - sbe->max = le32_to_cpu(be->max); + /* register dynamic object */ + sbe = (struct soc_bytes_ext *)&kc.private_value; INIT_LIST_HEAD(&sbe->dobj.list); sbe->dobj.type = SND_SOC_DOBJ_BYTES; @@ -917,26 +897,13 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) if (tplg->ops) sbe->dobj.unload = tplg->ops->control_unload; - /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); - if (ret) { - soc_control_err(tplg, &be->hdr, be->hdr.name); - goto err; - } - - /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &be->hdr); - if (ret < 0) - goto err; - - /* register control here */ + /* create control directly */ ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); if (ret < 0) - goto err; + return ret; list_add(&sbe->dobj.list, &tplg->comp->dobj_list); -err: return ret; } -- cgit v1.2.3 From 4654ca7cc8d6b57c77436dd44b347cc7dd3f5f08 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:49 +0200 Subject: ASoC: topology: Unify code for creating standalone and widget mixer control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code used to create standalone and widget mixer control is same, with exception that in standalone case dynamic object needs to be registered and control created directly. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-13-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 66 +++++++----------------------------------------- 1 file changed, 9 insertions(+), 57 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index b10a6be8f75b..5fde44808246 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -909,49 +909,21 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) { - struct snd_soc_tplg_mixer_control *mc; + struct snd_kcontrol_new kc = {0}; struct soc_mixer_control *sm; - struct snd_kcontrol_new kc; - int ret = 0; + int ret; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_mixer_control), 1, size, "mixers")) return -EINVAL; - mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); - if (sm == NULL) - return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + - le32_to_cpu(mc->priv.size)); - - dev_dbg(tplg->dev, - "ASoC: adding mixer kcontrol %s with access 0x%x\n", - mc->hdr.name, mc->hdr.access); - - memset(&kc, 0, sizeof(kc)); - kc.name = mc->hdr.name; - kc.private_value = (long)sm; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(mc->hdr.access); - - /* we only support FL/FR channel mapping atm */ - sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); - sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); + ret = soc_tplg_control_dmixer_create(tplg, &kc); + if (ret) + return ret; - sm->max = le32_to_cpu(mc->max); - sm->min = le32_to_cpu(mc->min); - sm->invert = le32_to_cpu(mc->invert); - sm->platform_max = le32_to_cpu(mc->platform_max); + /* register dynamic object */ + sm = (struct soc_mixer_control *)&kc.private_value; INIT_LIST_HEAD(&sm->dobj.list); sm->dobj.type = SND_SOC_DOBJ_MIXER; @@ -959,33 +931,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) if (tplg->ops) sm->dobj.unload = tplg->ops->control_unload; - /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); - if (ret) { - soc_control_err(tplg, &mc->hdr, mc->hdr.name); - goto err; - } - - /* create any TLV data */ - ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); - if (ret < 0) { - dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); - goto err; - } - - /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &mc->hdr); - if (ret < 0) - goto err; - - /* register control here */ + /* create control directly */ ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); if (ret < 0) - goto err; + return ret; list_add(&sm->dobj.list, &tplg->comp->dobj_list); -err: return ret; } -- cgit v1.2.3 From 8f2942b9198c935862bc2400602780844b352667 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 27 Jun 2024 12:18:50 +0200 Subject: ASoC: topology: Unify code for creating standalone and widget enum control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code used to create standalone and widget enum control is same, with exception that in standalone case dynamic object needs to be registered and control created directly. Signed-off-by: Amadeusz Sławiński Link: https://patch.msgid.link/20240627101850.2191513-14-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 86 +++++------------------------------------------- 1 file changed, 9 insertions(+), 77 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 5fde44808246..af5d42b57be7 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -943,46 +943,21 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) { - struct snd_soc_tplg_enum_control *ec; + struct snd_kcontrol_new kc = {0}; struct soc_enum *se; - struct snd_kcontrol_new kc; - int ret = 0; + int ret; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_enum_control), 1, size, "enums")) return -EINVAL; - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL); - if (se == NULL) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - le32_to_cpu(ec->priv.size)); - - dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", - ec->hdr.name, ec->items); - - memset(&kc, 0, sizeof(kc)); - kc.name = ec->hdr.name; - kc.private_value = (long)se; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(ec->hdr.access); - - se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); - se->shift_r = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FR); + ret = soc_tplg_control_denum_create(tplg, &kc); + if (ret) + return ret; - se->mask = le32_to_cpu(ec->mask); + /* register dynamic object */ + se = (struct soc_enum *)kc.private_value; INIT_LIST_HEAD(&se->dobj.list); se->dobj.type = SND_SOC_DOBJ_ENUM; @@ -990,56 +965,13 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) if (tplg->ops) se->dobj.unload = tplg->ops->control_unload; - switch (le32_to_cpu(ec->hdr.ops.info)) { - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - case SND_SOC_TPLG_CTL_ENUM_VALUE: - ret = soc_tplg_denum_create_values(tplg, se, ec); - if (ret < 0) { - dev_err(tplg->dev, - "ASoC: could not create values for %s\n", - ec->hdr.name); - goto err; - } - fallthrough; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - ret = soc_tplg_denum_create_texts(tplg, se, ec); - if (ret < 0) { - dev_err(tplg->dev, - "ASoC: could not create texts for %s\n", - ec->hdr.name); - goto err; - } - break; - default: - ret = -EINVAL; - dev_err(tplg->dev, - "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - goto err; - } - - /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); - if (ret) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - goto err; - } - - /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &ec->hdr); - if (ret < 0) - goto err; - - /* register control here */ + /* create control directly */ ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol); if (ret < 0) - goto err; + return ret; list_add(&se->dobj.list, &tplg->comp->dobj_list); -err: return ret; } -- cgit v1.2.3 From 83d43ab0a1cb83b1f3d731885a5d800ad8e84524 Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Tue, 2 Jul 2024 17:30:58 +0530 Subject: ASoC: dt-bindings: realtek,rt5645: Convert to dtschema Convert the RT5650/RT5645 audio CODEC bindings to DT schema. Reviewed-by: Rob Herring (Arm) Signed-off-by: Animesh Agarwal Cc: Daniel Baluta Link: https://patch.msgid.link/20240702120106.17100-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/realtek,rt5645.yaml | 131 +++++++++++++++++++++ Documentation/devicetree/bindings/sound/rt5645.txt | 82 ------------- 2 files changed, 131 insertions(+), 82 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5645.yaml delete mode 100644 Documentation/devicetree/bindings/sound/rt5645.txt diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml new file mode 100644 index 000000000000..13f09f1bc800 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5645.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5650/RT5645 audio CODEC + +maintainers: + - Animesh Agarwal + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes) for RT5645/RT5650: + * DMIC L1 + * DMIC R1 + * DMIC L2 + * DMIC R2 + * IN1P + * IN1N + * IN2P + * IN2N + * Haptic Generator + * HPOL + * HPOR + * LOUTL + * LOUTR + * PDM1L + * PDM1R + * SPOL + * SPOR + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - realtek,rt5645 + - realtek,rt5650 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: The CODEC's interrupt output. + + avdd-supply: + description: Power supply for AVDD, providing 1.8V. + + cpvdd-supply: + description: Power supply for CPVDD, providing 3.5V. + + hp-detect-gpios: + description: + A GPIO spec for the external headphone detect pin. If jd-mode = 0, we + will get the JD status by getting the value of hp-detect-gpios. + maxItems: 1 + + cbj-sleeve-gpios: + description: + A GPIO spec to control the external combo jack circuit to tie the + sleeve/ring2 contacts to the ground or floating. It could avoid some + electric noise from the active speaker jacks. + maxItems: 1 + + realtek,in2-differential: + description: + Indicate MIC2 input are differential, rather than single-ended. + type: boolean + + realtek,dmic1-data-pin: + description: Specify which pin to be used as DMIC1 data pin. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic1 is not used + - 1 # using IN2P pin as dmic1 data pin + - 2 # using GPIO6 pin as dmic1 data pin + - 3 # using GPIO10 pin as dmic1 data pin + - 4 # using GPIO12 pin as dmic1 data pin + + realtek,dmic2-data-pin: + description: Specify which pin to be used as DMIC2 data pin. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic2 is not used + - 1 # using IN2N pin as dmic2 data pin + - 2 # using GPIO5 pin as dmic2 data pin + - 3 # using GPIO11 pin as dmic2 data pin + + realtek,jd-mode: + description: The JD mode of rt5645/rt5650. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # rt5645/rt5650 JD function is not used + - 1 # Mode-0 (VDD=3.3V), two port jack detection + - 2 # Mode-1 (VDD=3.3V), one port jack detection + - 3 # Mode-2 (VDD=1.8V), one port jack detection + +required: + - compatible + - reg + - interrupts + - avdd-supply + - cpvdd-supply + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "realtek,rt5650"; + reg = <0x1a>; + hp-detect-gpios = <&gpio 19 0>; + cbj-sleeve-gpios = <&gpio 20 0>; + interrupt-parent = <&gpio>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + avdd-supply = <&avdd_reg>; + cpvdd-supply = <&cpvdd_supply>; + realtek,jd-mode = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5645.txt b/Documentation/devicetree/bindings/sound/rt5645.txt deleted file mode 100644 index c1fa379f5f3e..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5645.txt +++ /dev/null @@ -1,82 +0,0 @@ -RT5650/RT5645 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : One of "realtek,rt5645" or "realtek,rt5650". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -- avdd-supply: Power supply for AVDD, providing 1.8V. - -- cpvdd-supply: Power supply for CPVDD, providing 3.5V. - -Optional properties: - -- hp-detect-gpios: - a GPIO spec for the external headphone detect pin. If jd-mode = 0, - we will get the JD status by getting the value of hp-detect-gpios. - -- cbj-sleeve-gpios: - a GPIO spec to control the external combo jack circuit to tie the sleeve/ring2 - contacts to the ground or floating. It could avoid some electric noise from the - active speaker jacks. - -- realtek,in2-differential - Boolean. Indicate MIC2 input are differential, rather than single-ended. - -- realtek,dmic1-data-pin - 0: dmic1 is not used - 1: using IN2P pin as dmic1 data pin - 2: using GPIO6 pin as dmic1 data pin - 3: using GPIO10 pin as dmic1 data pin - 4: using GPIO12 pin as dmic1 data pin - -- realtek,dmic2-data-pin - 0: dmic2 is not used - 1: using IN2N pin as dmic2 data pin - 2: using GPIO5 pin as dmic2 data pin - 3: using GPIO11 pin as dmic2 data pin - --- realtek,jd-mode : The JD mode of rt5645/rt5650 - 0 : rt5645/rt5650 JD function is not used - 1 : Mode-0 (VDD=3.3V), two port jack detection - 2 : Mode-1 (VDD=3.3V), one port jack detection - 3 : Mode-2 (VDD=1.8V), one port jack detection - -Pins on the device (for linking into audio routes) for RT5645/RT5650: - - * DMIC L1 - * DMIC R1 - * DMIC L2 - * DMIC R2 - * IN1P - * IN1N - * IN2P - * IN2N - * Haptic Generator - * HPOL - * HPOR - * LOUTL - * LOUTR - * PDM1L - * PDM1R - * SPOL - * SPOR - -Example: - -codec: rt5650@1a { - compatible = "realtek,rt5650"; - reg = <0x1a>; - hp-detect-gpios = <&gpio 19 0>; - cbj-sleeve-gpios = <&gpio 20 0>; - interrupt-parent = <&gpio>; - interrupts = <7 IRQ_TYPE_EDGE_FALLING>; - realtek,dmic-en = "true"; - realtek,en-jd-func = "true"; - realtek,jd-mode = <3>; -}; -- cgit v1.2.3 From 4aad1d9471108a5023e224422b2522323521174c Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 2 Jul 2024 15:53:49 -0600 Subject: ASoC: PCM6240: Use of_property_read_reg() Replace the open-coded parsing of "reg" with of_property_read_reg(). The #ifdef is also easily replaced with IS_ENABLED(). Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240702215349.839350-1-robh@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/pcm6240.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 8f7057e689fb..5faf5e97a105 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -2081,10 +2082,6 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) struct device_node *np; unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES]; int ret = 0, i = 0, ndev = 0; -#ifdef CONFIG_OF - const __be32 *reg, *reg_end; - int len, sw, aw; -#endif pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); if (!pcm_dev) @@ -2118,27 +2115,19 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, pcm_dev); mutex_init(&pcm_dev->codec_lock); np = pcm_dev->dev->of_node; -#ifdef CONFIG_OF - aw = of_n_addr_cells(np); - sw = of_n_size_cells(np); - if (sw == 0) { - reg = (const __be32 *)of_get_property(np, - "reg", &len); - reg_end = reg + len/sizeof(*reg); - ndev = 0; - do { - dev_addrs[ndev] = of_read_number(reg, aw); - reg += aw; - ndev++; - } while (reg < reg_end); + + if (IS_ENABLED(CONFIG_OF)) { + u64 addr; + + for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) { + if (of_property_read_reg(np, i, &addr, NULL)) + break; + dev_addrs[ndev++] = addr; + } } else { ndev = 1; dev_addrs[0] = i2c->addr; } -#else - ndev = 1; - dev_addrs[0] = i2c->addr; -#endif pcm_dev->irq_info.gpio = of_irq_get(np, 0); for (i = 0; i < ndev; i++) -- cgit v1.2.3 From 31a45f9190b5b4f5cd8cdec8471babd5215eee04 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 2 Jul 2024 15:54:01 -0600 Subject: ASoC: tas2781: Use of_property_read_reg() Replace the open-coded parsing of "reg" with of_property_read_reg(). The #ifdef is also easily replaced with IS_ENABLED(). Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240702215402.839673-1-robh@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index cc765d45c6b5..e0e630f66101 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -647,33 +648,20 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) tas_priv->irq_info.irq_gpio = acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); - } else { + } else if (IS_ENABLED(CONFIG_OF)) { struct device_node *np = tas_priv->dev->of_node; -#ifdef CONFIG_OF - const __be32 *reg, *reg_end; - int len, sw, aw; - - aw = of_n_addr_cells(np); - sw = of_n_size_cells(np); - if (sw == 0) { - reg = (const __be32 *)of_get_property(np, - "reg", &len); - reg_end = reg + len/sizeof(*reg); - ndev = 0; - do { - dev_addrs[ndev] = of_read_number(reg, aw); - reg += aw; - ndev++; - } while (reg < reg_end); - } else { - ndev = 1; - dev_addrs[0] = client->addr; + u64 addr; + + for (i = 0; i < TASDEVICE_MAX_CHANNELS; i++) { + if (of_property_read_reg(np, i, &addr, NULL)) + break; + dev_addrs[ndev++] = addr; } -#else + + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } else { ndev = 1; dev_addrs[0] = client->addr; -#endif - tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); } tas_priv->ndev = ndev; for (i = 0; i < ndev; i++) -- cgit v1.2.3 From c66995ae403073212f5ba60d2079003866c6e130 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 3 Jul 2024 10:55:16 +0100 Subject: ASoC: cs35l56: Use header defines for Speaker Volume control definition The "Speaker Volume" control was being defined using four hardcoded magic numbers. There are #defines in the cs35l56.h header for these numbers, so change the code to use the defined constants. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240703095517.208077-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 7cac9812f5b5..84c34f5b1a51 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -71,7 +71,11 @@ static const struct snd_kcontrol_new cs35l56_controls[] = { cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), SOC_SINGLE_S_EXT_TLV("Speaker Volume", CS35L56_MAIN_RENDER_USER_VOLUME, - 6, -400, 400, 9, 0, + CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT, + CS35L56_MAIN_RENDER_USER_VOLUME_MIN, + CS35L56_MAIN_RENDER_USER_VOLUME_MAX, + CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT, + 0, cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw, vol_tlv), -- cgit v1.2.3 From 244389bd42870640c4b5ef672a360da329b579ed Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 3 Jul 2024 10:55:17 +0100 Subject: ASoC: cs35l56: Limit Speaker Volume to +12dB maximum Change CS35L56_MAIN_RENDER_USER_VOLUME_MAX to 48, to limit the maximum value of the Speaker Volume control to +12dB. The minimum value is unchanged so that the default 0dB has the same integer control value. The original maximum of 400 (+100dB) was the largest value that can be mathematically handled by the DSP. The actual maximum amplification is +12dB. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20240703095517.208077-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 642ef690ebc2..a6aa112e5741 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -207,7 +207,7 @@ /* CS35L56_MAIN_RENDER_USER_VOLUME */ #define CS35L56_MAIN_RENDER_USER_VOLUME_MIN -400 -#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX 400 +#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX 48 #define CS35L56_MAIN_RENDER_USER_VOLUME_MASK 0x0000FFC0 #define CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT 6 #define CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT 9 -- cgit v1.2.3 From c288f0a1c08efa65f9e3bb7954eb3cefb966c97d Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 28 Jun 2024 18:43:54 +0900 Subject: ASoC: fsl_xcvr: Improve suspend/resume flow in fsl_xcvr_trigger() In the current flow all interrupts are disabled in runtime suspend phase. However interrupts enablement only exists in fsl_xcvr_prepare(). After resume fsl_xcvr_prepare() may not be called so it will cause all interrupts still disabled even if resume from suspend. Interrupts should be explictily enabled after resume. Also, DPATH reset setting only exists in fsl_xcvr_prepare(). After resume from suspend DPATH should be reset otherwise there'll be channel swap issue. Signed-off-by: Chancel Liu Acked-by: Shengjiu Wang Link: https://patch.msgid.link/20240628094354.780720-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 337da46a2f90..bf9a4e90978e 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -529,16 +529,6 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, break; } - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); - if (ret < 0) { - dev_err(dai->dev, "Error while setting IER0: %d\n", ret); - return ret; - } - - /* set DPATH RESET */ - m_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); - v_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, m_ctl, v_ctl); if (ret < 0) { dev_err(dai->dev, "Error while setting EXT_CTRL: %d\n", ret); @@ -679,6 +669,15 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* set DPATH RESET */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx), + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); + return ret; + } + if (tx) { switch (xcvr->mode) { case FSL_XCVR_MODE_EARC: @@ -711,6 +710,13 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); + if (ret < 0) { + dev_err(dai->dev, "Error while setting IER0: %d\n", ret); + return ret; + } + /* clear DPATH RESET */ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, FSL_XCVR_EXT_CTRL_DPTH_RESET(tx), @@ -733,6 +739,13 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); + return ret; + } + if (tx) { switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: @@ -1411,16 +1424,6 @@ static int fsl_xcvr_runtime_suspend(struct device *dev) struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; - /* - * Clear interrupts, when streams starts or resumes after - * suspend, interrupts are enabled in prepare(), so no need - * to enable interrupts in resume(). - */ - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, 0); - if (ret < 0) - dev_err(dev, "Failed to clear IER0: %d\n", ret); - if (!xcvr->soc_data->spdif_only) { /* Assert M0+ reset */ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, -- cgit v1.2.3 From cb148180125ef88a4c1c20ecf25337f1e45657bb Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:20:57 +0200 Subject: ASoC: cs35l34: Constify struct regmap_config `cs35l34_regmap` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-1-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l34.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index 4c517231d765..e63a518e3b8e 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -787,7 +787,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l34 = { .endianness = 1, }; -static struct regmap_config cs35l34_regmap = { +static const struct regmap_config cs35l34_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v1.2.3 From 306e0317bddfbb6bea1ad31c3daeecaec0304295 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:20:58 +0200 Subject: ASoC: cs35l35: Constify struct regmap_config `cs35l35_regmap` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-2-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l35.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index c39b3cfe9574..7a01b1d9fc9d 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1086,7 +1086,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l35 = { .endianness = 1, }; -static struct regmap_config cs35l35_regmap = { +static const struct regmap_config cs35l35_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v1.2.3 From 0271df05e6fe92b7000dcce5058a0ed6af127ef6 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:20:59 +0200 Subject: ASoC: cs35l36: Constify struct regmap_config `cs35l36_regmap` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-3-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l36.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index bc79990615e8..cbea79bd8980 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1300,7 +1300,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { .endianness = 1, }; -static struct regmap_config cs35l36_regmap = { +static const struct regmap_config cs35l36_regmap = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, -- cgit v1.2.3 From 52f0aa5fb9437013f7f35d61426de497a8927891 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:00 +0200 Subject: ASoC: cs53l30: Constify struct regmap_config `cs53l30_regmap` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-4-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs53l30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 2ee13d885fdc..bcbaf28a0b2d 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -900,7 +900,7 @@ static const struct snd_soc_component_driver cs53l30_driver = { .endianness = 1, }; -static struct regmap_config cs53l30_regmap = { +static const struct regmap_config cs53l30_regmap = { .reg_bits = 8, .val_bits = 8, -- cgit v1.2.3 From 8d9c0ede48f29c9fccd095952d657fc5696da9ac Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:01 +0200 Subject: ASoC: jz4760: Constify struct regmap_config `jz4760_codec_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-5-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/jz4760.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c index 9df58e23d360..6217e611259f 100644 --- a/sound/soc/codecs/jz4760.c +++ b/sound/soc/codecs/jz4760.c @@ -821,7 +821,7 @@ static const u8 jz4760_codec_reg_defaults[] = { 0x1F, 0x00, 0x00, 0x00 }; -static struct regmap_config jz4760_codec_regmap_config = { +static const struct regmap_config jz4760_codec_regmap_config = { .reg_bits = 7, .val_bits = 8, -- cgit v1.2.3 From 7abfa29ba6a43c5e25622de1ba1f1846b9c9b5e8 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:02 +0200 Subject: ASoC: jz4770: Constify struct regmap_config `jz4760_codec_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-6-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/jz4770.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 1d0c467ab57b..acb9eaa7ea1c 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -872,7 +872,7 @@ static const u8 jz4770_codec_reg_defaults[] = { 0x07, 0x44, 0x1F, 0x00 }; -static struct regmap_config jz4770_codec_regmap_config = { +static const struct regmap_config jz4770_codec_regmap_config = { .reg_bits = 7, .val_bits = 8, -- cgit v1.2.3 From 5ffab1d3f3f3281869b894070fe4438f307759ec Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:03 +0200 Subject: ASoC: wsa881x: Constify struct regmap_config `wsa881x_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-7-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 1253695bebd8..0478599d0f35 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -634,7 +634,7 @@ static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) } } -static struct regmap_config wsa881x_regmap_config = { +static const struct regmap_config wsa881x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, -- cgit v1.2.3 From 22c361dc7ce4d4d6a688febee57d6e4b130b96b3 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:04 +0200 Subject: ASoC: wsa883x: Constify struct regmap_config `wsa883x_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-8-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa883x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index f5a15f0e891e..d0ab4e2290b6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -934,7 +934,7 @@ static bool wsa883x_volatile_register(struct device *dev, unsigned int reg) return wsa883x_readonly_register(dev, reg); } -static struct regmap_config wsa883x_regmap_config = { +static const struct regmap_config wsa883x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, -- cgit v1.2.3 From e15cc906b9c5af5414bb6002b6a036550bca6bd8 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 3 Jul 2024 18:21:05 +0200 Subject: ASoC: wsa884x: Constify struct regmap_config `wsa884x_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20240703-sound-const-regmap_config-v1-9-2e379b0446a7@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index a6034547b4f3..d17ae17b2938 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1319,7 +1319,7 @@ static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) return wsa884x_readonly_register(dev, reg); } -static struct regmap_config wsa884x_regmap_config = { +static const struct regmap_config wsa884x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, -- cgit v1.2.3 From 3b628e617b21144579bbca806a26737e8485d93a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 1 Jul 2024 09:02:12 +0200 Subject: ASoC: tas5086: Convert to GPIO descriptors Switch the driver to use GPIO descriptors. Notice that we let the gpiolib handle line inversion for the active low reset line (nreset !reset). There are no upstream device trees using the tas5086 compatible string, if there were, we would need to ascertain that they all set the GPIO_ACTIVE_LOW flag on their GPIO lines. Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20240701-asoc-tas-gpios-v1-1-d69ec5d79939@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas5086.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 6d45df3b9ba4..4bc1fdd232bb 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -24,14 +24,13 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include #include #include #include @@ -246,7 +245,7 @@ struct tas5086_private { /* Current sample rate for de-emphasis control */ int rate; /* GPIO driving Reset pin, if any */ - int gpio_nreset; + struct gpio_desc *reset; struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; @@ -462,11 +461,11 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) static void tas5086_reset(struct tas5086_private *priv) { - if (gpio_is_valid(priv->gpio_nreset)) { + if (priv->reset) { /* Reset codec - minimum assertion time is 400ns */ - gpio_direction_output(priv->gpio_nreset, 0); + gpiod_direction_output(priv->reset, 1); udelay(1); - gpio_set_value(priv->gpio_nreset, 1); + gpiod_set_value(priv->reset, 0); /* Codec needs ~15ms to wake up */ msleep(15); @@ -867,9 +866,9 @@ static void tas5086_remove(struct snd_soc_component *component) { struct tas5086_private *priv = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(priv->gpio_nreset)) + if (priv->reset) /* Set codec to the reset state */ - gpio_set_value(priv->gpio_nreset, 0); + gpiod_set_value(priv->reset, 1); regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); }; @@ -914,7 +913,6 @@ static int tas5086_i2c_probe(struct i2c_client *i2c) { struct tas5086_private *priv; struct device *dev = &i2c->dev; - int gpio_nreset = -EINVAL; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -940,12 +938,11 @@ static int tas5086_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, priv); - gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_is_valid(gpio_nreset)) - if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) - gpio_nreset = -EINVAL; - - priv->gpio_nreset = gpio_nreset; + /* Request line asserted */ + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + gpiod_set_consumer_name(priv->reset, "TAS5086 Reset"); ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret < 0) { -- cgit v1.2.3 From 891168dc4a6c637ca76c64e7bde6917b96b9cd54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:33 +0200 Subject: ASoC: codecs: lpass-rx-macro: Simplify PDS cleanup with devm Eliminate PDS cleanup by using devm_add_action_or_reset() which results in one less error path and smaller cleanup in remove(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-1-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.h | 5 +++++ sound/soc/codecs/lpass-rx-macro.c | 30 ++++++++++++------------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index 3aa9737f2737..21cb30ab706d 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -41,6 +41,11 @@ void lpass_macro_pds_exit(struct lpass_macro *pds); void lpass_macro_set_codec_version(enum lpass_codec_version version); enum lpass_codec_version lpass_macro_get_codec_version(void); +static inline void lpass_macro_pds_exit_action(void *pds) +{ + lpass_macro_pds_exit(pds); +} + static inline const char *lpass_macro_get_codec_version_string(int version) { switch (version) { diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index d47c49c90de3..77e734ad1885 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3803,11 +3803,14 @@ static int rx_macro_probe(struct platform_device *pdev) if (IS_ERR(rx->pds)) return PTR_ERR(rx->pds); + ret = devm_add_action_or_reset(dev, lpass_macro_pds_exit_action, rx->pds); + if (ret) + return ret; + base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto err; - } + if (IS_ERR(base)) + return PTR_ERR(base); + rx->codec_version = lpass_macro_get_codec_version(); switch (rx->codec_version) { case LPASS_CODEC_VERSION_1_0: @@ -3818,10 +3821,8 @@ static int rx_macro_probe(struct platform_device *pdev) rx->rxn_reg_stride = 0x80; def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults); reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); - if (!reg_defaults) { - ret = -ENOMEM; - goto err; - } + if (!reg_defaults) + return -ENOMEM; memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults)); @@ -3833,18 +3834,15 @@ static int rx_macro_probe(struct platform_device *pdev) rx->rxn_reg_stride = 0xc0; def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults); reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); - if (!reg_defaults) { - ret = -ENOMEM; - goto err; - } + if (!reg_defaults) + return -ENOMEM; memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], rx_2_5_defaults, sizeof(rx_2_5_defaults)); break; default: dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version); - ret = -EINVAL; - goto err; + return -EINVAL; } rx_regmap_config.reg_defaults = reg_defaults; @@ -3927,8 +3925,6 @@ err_dcodec: clk_disable_unprepare(rx->macro); err_ver: kfree(reg_defaults); -err: - lpass_macro_pds_exit(rx->pds); return ret; } @@ -3942,8 +3938,6 @@ static void rx_macro_remove(struct platform_device *pdev) clk_disable_unprepare(rx->fsgen); clk_disable_unprepare(rx->macro); clk_disable_unprepare(rx->dcodec); - - lpass_macro_pds_exit(rx->pds); } static const struct of_device_id rx_macro_dt_match[] = { -- cgit v1.2.3 From ee5e13b2c92324938c2bffc44b36b5a29fc28087 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:34 +0200 Subject: ASoC: codecs: lpass-rx-macro: Simplify with cleanup.h Allocate the default register values array with scoped/cleanup.h to reduce number of error paths and make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-2-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 77e734ad1885..638f3995a364 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include #include #include #include @@ -3764,7 +3765,6 @@ static const struct snd_soc_component_driver rx_macro_component_drv = { static int rx_macro_probe(struct platform_device *pdev) { - struct reg_default *reg_defaults; struct device *dev = &pdev->dev; kernel_ulong_t flags; struct rx_macro *rx; @@ -3812,6 +3812,8 @@ static int rx_macro_probe(struct platform_device *pdev) return PTR_ERR(base); rx->codec_version = lpass_macro_get_codec_version(); + struct reg_default *reg_defaults __free(kfree) = NULL; + switch (rx->codec_version) { case LPASS_CODEC_VERSION_1_0: case LPASS_CODEC_VERSION_1_1: @@ -3849,10 +3851,8 @@ static int rx_macro_probe(struct platform_device *pdev) rx_regmap_config.num_reg_defaults = def_count; rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); - if (IS_ERR(rx->regmap)) { - ret = PTR_ERR(rx->regmap); - goto err_ver; - } + if (IS_ERR(rx->regmap)) + return PTR_ERR(rx->regmap); dev_set_drvdata(dev, rx); @@ -3864,7 +3864,7 @@ static int rx_macro_probe(struct platform_device *pdev) ret = clk_prepare_enable(rx->macro); if (ret) - goto err_ver; + return ret; ret = clk_prepare_enable(rx->dcodec); if (ret) @@ -3910,7 +3910,6 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) goto err_clkout; - kfree(reg_defaults); return 0; err_clkout: @@ -3923,8 +3922,6 @@ err_mclk: clk_disable_unprepare(rx->dcodec); err_dcodec: clk_disable_unprepare(rx->macro); -err_ver: - kfree(reg_defaults); return ret; } -- cgit v1.2.3 From 0c02cacf62fd90bf9f0c6c33e9a4862cfc50aab4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:35 +0200 Subject: ASoC: codecs: lpass-rx-macro: Keep static regmap_config as const The driver has static 'struct regmap_config', which is then customized depending on device version. This works fine, because there should not be two devices in a system simultaneously and even less likely that such two devices would have different versions, thus different regmap config. However code is cleaner and more obvious when static data in the driver is also const - it serves as a template. Mark the 'struct regmap_config' as const and duplicate it in the probe() with kmemdup to allow customizing per detected device variant. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-3-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 638f3995a364..d4d7e02db83f 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1663,7 +1663,7 @@ static bool rx_is_readable_register(struct device *dev, unsigned int reg) return rx_is_rw_register(dev, reg); } -static struct regmap_config rx_regmap_config = { +static const struct regmap_config rx_regmap_config = { .name = "rx_macro", .reg_bits = 16, .val_bits = 32, /* 8 but with 32 bit read/write */ @@ -3847,10 +3847,16 @@ static int rx_macro_probe(struct platform_device *pdev) return -EINVAL; } - rx_regmap_config.reg_defaults = reg_defaults; - rx_regmap_config.num_reg_defaults = def_count; + struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config, + sizeof(*reg_config), + GFP_KERNEL); + if (!reg_config) + return -ENOMEM; + + reg_config->reg_defaults = reg_defaults; + reg_config->num_reg_defaults = def_count; - rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); + rx->regmap = devm_regmap_init_mmio(dev, base, reg_config); if (IS_ERR(rx->regmap)) return PTR_ERR(rx->regmap); -- cgit v1.2.3 From bf95919fe1917efa8f5da83057ff9fc11130aa55 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:36 +0200 Subject: ASoC: dapm: Use unsigned for number of widgets in snd_soc_dapm_new_controls() Number of widgets in array passed to snd_soc_dapm_new_controls() cannot be negative, so make it explicit by using 'unsigned int', just like snd_soc_add_component_controls() is doing. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-4-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-dapm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 667ecd4daa68..12cd7b5a2202 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -457,7 +457,7 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uncontrol); int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget, int num); + const struct snd_soc_dapm_widget *widget, unsigned int num); struct snd_soc_dapm_widget *snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget); struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 16dad4a45443..32cc90d09bc2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3857,7 +3857,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); */ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget, - int num) + unsigned int num) { int i; int ret = 0; -- cgit v1.2.3 From c72585d79249fb07ca3e3c91022e312d21f20f40 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:37 +0200 Subject: ASoC: codecs: lpass-rx-macro: Use unsigned for number of widgets Driver uses ARRAY_SIZE() to get number of widgets later passed to snd_soc_dapm_new_controls(), which is an 'unsigned int'. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-5-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index d4d7e02db83f..ce42749660c8 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3612,8 +3612,8 @@ static int rx_macro_component_probe(struct snd_soc_component *component) struct rx_macro *rx = snd_soc_component_get_drvdata(component); const struct snd_soc_dapm_widget *widgets; const struct snd_kcontrol_new *controls; - unsigned int num_controls; - int ret, num_widgets; + unsigned int num_controls, num_widgets; + int ret; snd_soc_component_init_regmap(component, rx->regmap); -- cgit v1.2.3 From 67820eb9f4895791da46df42ff7942dfc1353bb2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 09:39:38 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Simplify with cleanup.h Driver's probe() has two allocations which are needed only within the probe() itself - for devm_regmap_init_mmio(). Usage of devm interface is a bit misleading here, because these can be freed right after each scope finishes. This makes the code a bit more obvious and self documenting. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701-b4-qcom-audio-lpass-codec-cleanups-v3-6-6d98d4dd1ef5@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index b4e7139bac61..73a588289408 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include #include #include #include @@ -2725,8 +2726,6 @@ static const struct snd_soc_component_driver wsa_macro_component_drv = { static int wsa_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct reg_default *reg_defaults; - struct regmap_config *reg_config; struct wsa_macro *wsa; kernel_ulong_t flags; void __iomem *base; @@ -2765,6 +2764,8 @@ static int wsa_macro_probe(struct platform_device *pdev) return PTR_ERR(base); wsa->codec_version = lpass_macro_get_codec_version(); + struct reg_default *reg_defaults __free(kfree) = NULL; + switch (wsa->codec_version) { case LPASS_CODEC_VERSION_1_0: case LPASS_CODEC_VERSION_1_1: @@ -2773,9 +2774,8 @@ static int wsa_macro_probe(struct platform_device *pdev) case LPASS_CODEC_VERSION_2_1: wsa->reg_layout = &wsa_codec_v2_1; def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1); - reg_defaults = devm_kmalloc_array(dev, def_count, - sizeof(*reg_defaults), - GFP_KERNEL); + reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults), + GFP_KERNEL); if (!reg_defaults) return -ENOMEM; memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); @@ -2789,9 +2789,8 @@ static int wsa_macro_probe(struct platform_device *pdev) case LPASS_CODEC_VERSION_2_8: wsa->reg_layout = &wsa_codec_v2_5; def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5); - reg_defaults = devm_kmalloc_array(dev, def_count, - sizeof(*reg_defaults), - GFP_KERNEL); + reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults), + GFP_KERNEL); if (!reg_defaults) return -ENOMEM; memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); @@ -2804,8 +2803,9 @@ static int wsa_macro_probe(struct platform_device *pdev) return -EINVAL; } - reg_config = devm_kmemdup(dev, &wsa_regmap_config, - sizeof(*reg_config), GFP_KERNEL); + struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config, + sizeof(*reg_config), + GFP_KERNEL); if (!reg_config) return -ENOMEM; @@ -2816,8 +2816,6 @@ static int wsa_macro_probe(struct platform_device *pdev) if (IS_ERR(wsa->regmap)) return PTR_ERR(wsa->regmap); - devm_kfree(dev, reg_config); - devm_kfree(dev, reg_defaults); dev_set_drvdata(dev, wsa); wsa->dev = dev; -- cgit v1.2.3 From e62599902327d27687693f6e5253a5d56583db58 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:28 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value devm_kasprintf() can return a NULL pointer on failure but this returned value is not checked. Fix this lack and check the returned value. Fixes: 075c7125b11c ("ASoC: fsl: Add support for QMC audio") Cc: stable@vger.kernel.org Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-2-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index bfaaa451735b..dd90ef16fa97 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -604,6 +604,8 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d", np->parent->name, qmc_dai->id); + if (!qmc_dai->name) + return -ENOMEM; qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np, "fsl,qmc-chan"); -- cgit v1.2.3 From 86dd725b57a88869acfe15b0405937450d0aef30 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:29 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch ./scripts/checkpatch.pl --strict --codespell detected several issues when running on the fsl_qmc_audio.c file: - CHECK: spaces preferred around that '*' (ctx:VxV) - CHECK: Alignment should match open parenthesis - CHECK: Comparison to NULL could be written "!prtd" - CHECK: spaces preferred around that '/' (ctx:VxV) - CHECK: Lines should not end with a '(' - CHECK: Please don't use multiple blank lines Some of them are present several times. Fix all of these issues without any functional changes. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-3-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 65 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index dd90ef16fa97..917a32389f3d 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -54,7 +54,7 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component, return ret; snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev, - 64*1024, 64*1024); + 64 * 1024, 64 * 1024); return 0; } @@ -89,8 +89,8 @@ static void qmc_audio_pcm_write_complete(void *context) prtd->period_ptr_submitted = prtd->dma_buffer_start; ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", ret); @@ -118,8 +118,8 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i prtd->period_ptr_submitted = prtd->dma_buffer_start; ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", ret); @@ -144,8 +144,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Submit first chunk ... */ ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); @@ -159,8 +159,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, /* ... and send it */ ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); @@ -169,8 +169,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, } else { /* Submit first chunk ... */ ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(component->dev, "read_submit failed %d\n", ret); @@ -184,8 +184,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, /* ... and send it */ ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); + prtd->period_ptr_submitted, prtd->period_size, + qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); @@ -220,8 +220,8 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone } static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component, - const struct of_phandle_args *args, - const char **dai_name) + const struct of_phandle_args *args, + const char **dai_name) { struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev); struct snd_soc_dai_driver *dai_driver; @@ -245,10 +245,10 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE, .period_bytes_min = 32, - .period_bytes_max = 64*1024, + .period_bytes_max = 64 * 1024, .periods_min = 2, - .periods_max = 2*1024, - .buffer_bytes_max = 64*1024, + .periods_max = 2 * 1024, + .buffer_bytes_max = 64 * 1024, }; static int qmc_audio_pcm_open(struct snd_soc_component *component, @@ -266,7 +266,7 @@ static int qmc_audio_pcm_open(struct snd_soc_component *component, return ret; prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (prtd == NULL) + if (!prtd) return -ENOMEM; runtime->private_data = prtd; @@ -329,13 +329,13 @@ static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai, ch.max = nb_ts; break; case 16: - ch.max = nb_ts/2; + ch.max = nb_ts / 2; break; case 32: - ch.max = nb_ts/4; + ch.max = nb_ts / 4; break; case 64: - ch.max = nb_ts/8; + ch.max = nb_ts / 8; break; default: dev_err(qmc_dai->dev, "format physical width %u not supported\n", @@ -356,9 +356,8 @@ static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts); } -static int qmc_dai_hw_rule_capture_channels_by_format( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; @@ -394,18 +393,16 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, return snd_mask_refine(f_old, &f_new); } -static int qmc_dai_hw_rule_playback_format_by_channels( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts); } -static int qmc_dai_hw_rule_capture_format_by_channels( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; @@ -413,7 +410,7 @@ static int qmc_dai_hw_rule_capture_format_by_channels( } static int qmc_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct qmc_dai_prtd *prtd = substream->runtime->private_data; snd_pcm_hw_rule_func_t hw_rule_channels_by_format; @@ -587,7 +584,8 @@ static u64 qmc_audio_formats(u8 nb_ts) } static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np, - struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver) + struct qmc_dai *qmc_dai, + struct snd_soc_dai_driver *qmc_soc_dai_driver) { struct qmc_chan_info info; u32 val; @@ -704,7 +702,6 @@ static int qmc_audio_probe(struct platform_device *pdev) i++; } - platform_set_drvdata(pdev, qmc_audio); ret = devm_snd_soc_register_component(qmc_audio->dev, -- cgit v1.2.3 From 42212b2ce8b1182d536452eee2880d2de7cce059 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:30 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling The driver mixes some internal values for channel DMA buffer handling and PCM pointer handling. In the currently supported interleaved mode, this mix does not lead to any issues but in order to prepare the support for the non-interleaved mode, having them clearly separated will ease the support and avoid additional computation to convert values used in channel DMA buffer management in values usable for PCM pointer. Use a specific set of variable for PCM pointer handling and an other set for channel DMA buffer. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-4-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 84 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 917a32389f3d..e8281e548746 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -35,11 +35,16 @@ struct qmc_audio { struct qmc_dai_prtd { struct qmc_dai *qmc_dai; - dma_addr_t dma_buffer_start; - dma_addr_t period_ptr_submitted; - dma_addr_t period_ptr_ended; - dma_addr_t dma_buffer_end; - size_t period_size; + + snd_pcm_uframes_t buffer_ended; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + + dma_addr_t ch_dma_addr_start; + dma_addr_t ch_dma_addr_current; + dma_addr_t ch_dma_addr_end; + size_t ch_dma_size; + struct snd_pcm_substream *substream; }; @@ -65,13 +70,17 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct qmc_dai_prtd *prtd = substream->runtime->private_data; - prtd->dma_buffer_start = runtime->dma_addr; - prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params); - prtd->period_size = params_period_bytes(params); - prtd->period_ptr_submitted = prtd->dma_buffer_start; - prtd->period_ptr_ended = prtd->dma_buffer_start; prtd->substream = substream; + prtd->buffer_ended = 0; + prtd->buffer_size = params_buffer_size(params); + prtd->period_size = params_period_size(params); + + prtd->ch_dma_addr_start = runtime->dma_addr; + prtd->ch_dma_addr_end = runtime->dma_addr + params_buffer_bytes(params); + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; + prtd->ch_dma_size = params_period_bytes(params); + return 0; } @@ -80,16 +89,16 @@ static void qmc_audio_pcm_write_complete(void *context) struct qmc_dai_prtd *prtd = context; int ret; - prtd->period_ptr_ended += prtd->period_size; - if (prtd->period_ptr_ended >= prtd->dma_buffer_end) - prtd->period_ptr_ended = prtd->dma_buffer_start; + prtd->buffer_ended += prtd->period_size; + if (prtd->buffer_ended >= prtd->buffer_size) + prtd->buffer_ended = 0; - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", @@ -104,21 +113,21 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i struct qmc_dai_prtd *prtd = context; int ret; - if (length != prtd->period_size) { + if (length != prtd->ch_dma_size) { dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", - length, prtd->period_size); + length, prtd->ch_dma_size); } - prtd->period_ptr_ended += prtd->period_size; - if (prtd->period_ptr_ended >= prtd->dma_buffer_end) - prtd->period_ptr_ended = prtd->dma_buffer_start; + prtd->buffer_ended += prtd->period_size; + if (prtd->buffer_ended >= prtd->buffer_size) + prtd->buffer_ended = 0; - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", @@ -144,7 +153,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Submit first chunk ... */ ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", @@ -153,13 +162,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, } /* ... prepare next one ... */ - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_write_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", @@ -169,7 +178,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, } else { /* Submit first chunk ... */ ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(component->dev, "read_submit failed %d\n", @@ -178,13 +187,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, } /* ... prepare next one ... */ - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, + prtd->ch_dma_addr_current, prtd->ch_dma_size, qmc_audio_pcm_read_complete, prtd); if (ret) { dev_err(component->dev, "write_submit failed %d\n", @@ -215,8 +224,7 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone { struct qmc_dai_prtd *prtd = substream->runtime->private_data; - return bytes_to_frames(substream->runtime, - prtd->period_ptr_ended - prtd->dma_buffer_start); + return prtd->buffer_ended; } static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component, -- cgit v1.2.3 From 5e51a1f9dfd90de6e44cfb5340d48263f9e8f8d8 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:31 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines The current QMC audio driver uses only one QMC channel per DAI. The context used by QMC channel transfer (read and write) completion routines does not contains any QMC channel and the only one available per DAI is used to schedule the next transfer. This works pretty well with only one QMC channel per DAI. The future support for non-inlerleave mode will use several QMC channel per DAI. In that case, QMC channel transfer completion routines need to identify the QMC channel related to the completion. In order to fill this lack, even if identifying the current QMC channel among several QMC channels is not needed for the current code, add one indirection level and introduce the qmc_dai_chan data structrure. This structure contains the QMC channel involved in the completion and refererences to the runtime context (capture and playback) used by the DAI. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-5-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 72 ++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index e8281e548746..b07770257bad 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -17,13 +17,19 @@ #include #include +struct qmc_dai_chan { + struct qmc_dai_prtd *prtd_tx; + struct qmc_dai_prtd *prtd_rx; + struct qmc_chan *qmc_chan; +}; + struct qmc_dai { char *name; int id; struct device *dev; - struct qmc_chan *qmc_chan; unsigned int nb_tx_ts; unsigned int nb_rx_ts; + struct qmc_dai_chan chan; }; struct qmc_audio { @@ -86,9 +92,12 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, static void qmc_audio_pcm_write_complete(void *context) { - struct qmc_dai_prtd *prtd = context; + struct qmc_dai_chan *chan = context; + struct qmc_dai_prtd *prtd; int ret; + prtd = chan->prtd_tx; + prtd->buffer_ended += prtd->period_size; if (prtd->buffer_ended >= prtd->buffer_size) prtd->buffer_ended = 0; @@ -97,9 +106,10 @@ static void qmc_audio_pcm_write_complete(void *context) if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, prtd); + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", ret); @@ -110,9 +120,12 @@ static void qmc_audio_pcm_write_complete(void *context) static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) { - struct qmc_dai_prtd *prtd = context; + struct qmc_dai_chan *chan = context; + struct qmc_dai_prtd *prtd; int ret; + prtd = chan->prtd_rx; + if (length != prtd->ch_dma_size) { dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", length, prtd->ch_dma_size); @@ -126,9 +139,10 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, prtd); + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", ret); @@ -151,10 +165,13 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + prtd->qmc_dai->chan.prtd_tx = prtd; + /* Submit first chunk ... */ - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, prtd); + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); @@ -167,19 +184,23 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, prtd); + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); return ret; } } else { + prtd->qmc_dai->chan.prtd_rx = prtd; + /* Submit first chunk ... */ - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, prtd); + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(component->dev, "read_submit failed %d\n", ret); @@ -192,9 +213,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, + ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, prtd); + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chan); if (ret) { dev_err(component->dev, "write_submit failed %d\n", ret); @@ -489,7 +511,7 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { chan_param.mode = QMC_TRANSPARENT; chan_param.transp.max_rx_buf_size = params_period_bytes(params); - ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param); + ret = qmc_chan_set_param(qmc_dai->chan.qmc_chan, &chan_param); if (ret) { dev_err(dai->dev, "set param failed %d\n", ret); @@ -520,23 +542,23 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = qmc_chan_start(qmc_dai->qmc_chan, direction); + ret = qmc_chan_start(qmc_dai->chan.qmc_chan, direction); if (ret) return ret; break; case SNDRV_PCM_TRIGGER_STOP: - ret = qmc_chan_stop(qmc_dai->qmc_chan, direction); + ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction); if (ret) return ret; - ret = qmc_chan_reset(qmc_dai->qmc_chan, direction); + ret = qmc_chan_reset(qmc_dai->chan.qmc_chan, direction); if (ret) return ret; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = qmc_chan_stop(qmc_dai->qmc_chan, direction); + ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction); if (ret) return ret; break; @@ -613,10 +635,10 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * if (!qmc_dai->name) return -ENOMEM; - qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np, - "fsl,qmc-chan"); - if (IS_ERR(qmc_dai->qmc_chan)) { - ret = PTR_ERR(qmc_dai->qmc_chan); + qmc_dai->chan.qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np, + "fsl,qmc-chan"); + if (IS_ERR(qmc_dai->chan.qmc_chan)) { + ret = PTR_ERR(qmc_dai->chan.qmc_chan); return dev_err_probe(qmc_audio->dev, ret, "dai %d get QMC channel failed\n", qmc_dai->id); } @@ -624,7 +646,7 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * qmc_soc_dai_driver->id = qmc_dai->id; qmc_soc_dai_driver->name = qmc_dai->name; - ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info); + ret = qmc_chan_get_info(qmc_dai->chan.qmc_chan, &info); if (ret) { dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n", qmc_dai->id, ret); -- cgit v1.2.3 From 33a6969fbc653f25d5204b17fb67d5a21e6295e6 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:32 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit() Submitting data to QMC channels is done in several places: transfer completions and DAI start. The operation done is simple and consist in one function call. With the future introduction of support for non-interleaved mode, submitting data will be more complex. To avoid copy/paste of code in several places, introduce qmc_audio_pcm_{read,write}_submit() whose goal is to handle this data submission. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-6-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 93 +++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index b07770257bad..36145f1ddbf1 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -90,11 +90,29 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, return 0; } +static void qmc_audio_pcm_write_complete(void *context); + +static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd) +{ + int ret; + + ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, + prtd->ch_dma_addr_current, prtd->ch_dma_size, + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chan); + if (ret) { + dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", + ret); + return ret; + } + + return 0; +} + static void qmc_audio_pcm_write_complete(void *context) { struct qmc_dai_chan *chan = context; struct qmc_dai_prtd *prtd; - int ret; prtd = chan->prtd_tx; @@ -106,23 +124,33 @@ static void qmc_audio_pcm_write_complete(void *context) if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; - ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, - &prtd->qmc_dai->chan); + qmc_audio_pcm_write_submit(prtd); + + snd_pcm_period_elapsed(prtd->substream); +} + +static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags); + +static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd) +{ + int ret; + + ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, + prtd->ch_dma_addr_current, prtd->ch_dma_size, + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chan); if (ret) { - dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", + dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", ret); } - snd_pcm_period_elapsed(prtd->substream); + return 0; } static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) { struct qmc_dai_chan *chan = context; struct qmc_dai_prtd *prtd; - int ret; prtd = chan->prtd_rx; @@ -139,14 +167,7 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; - ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", - ret); - } + qmc_audio_pcm_read_submit(prtd); snd_pcm_period_elapsed(prtd->substream); } @@ -168,15 +189,9 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, prtd->qmc_dai->chan.prtd_tx = prtd; /* Submit first chunk ... */ - ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_write_submit(prtd); + if (ret) return ret; - } /* ... prepare next one ... */ prtd->ch_dma_addr_current += prtd->ch_dma_size; @@ -184,28 +199,16 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_write_submit(prtd); + if (ret) return ret; - } } else { prtd->qmc_dai->chan.prtd_rx = prtd; /* Submit first chunk ... */ - ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(component->dev, "read_submit failed %d\n", - ret); + ret = qmc_audio_pcm_read_submit(prtd); + if (ret) return ret; - } /* ... prepare next one ... */ prtd->ch_dma_addr_current += prtd->ch_dma_size; @@ -213,15 +216,9 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_read_submit(prtd); + if (ret) return ret; - } } break; -- cgit v1.2.3 From b81cfa66435bdab896b4c24e11d24ec33bdb0601 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:33 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved() Constraints are set by qmc_dai_startup(). These constraints are specific to the interleaved mode. With the future introduction of support for non-interleaved mode, a new set of constraints will be set. To make the code clear and keep qmc_dai_startup() simple, extract the current interleaved mode constraints settings to a specific function. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-7-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 36145f1ddbf1..f70c6c8eec4a 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -436,24 +436,14 @@ static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params * return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts); } -static int qmc_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, + struct qmc_dai *qmc_dai) { - struct qmc_dai_prtd *prtd = substream->runtime->private_data; snd_pcm_hw_rule_func_t hw_rule_channels_by_format; snd_pcm_hw_rule_func_t hw_rule_format_by_channels; - struct qmc_dai *qmc_dai; unsigned int frame_bits; int ret; - qmc_dai = qmc_dai_get_data(dai); - if (!qmc_dai) { - dev_err(dai->dev, "Invalid dai\n"); - return -EINVAL; - } - - prtd->qmc_dai = qmc_dai; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format; hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels; @@ -468,7 +458,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, hw_rule_channels_by_format, qmc_dai, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) { - dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + dev_err(qmc_dai->dev, "Failed to add channels rule (%d)\n", ret); return ret; } @@ -476,7 +466,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, hw_rule_format_by_channels, qmc_dai, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret) { - dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + dev_err(qmc_dai->dev, "Failed to add format rule (%d)\n", ret); return ret; } @@ -484,13 +474,30 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_FRAME_BITS, frame_bits); if (ret < 0) { - dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); return ret; } return 0; } +static int qmc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qmc_dai_prtd *prtd = substream->runtime->private_data; + struct qmc_dai *qmc_dai; + + qmc_dai = qmc_dai_get_data(dai); + if (!qmc_dai) { + dev_err(dai->dev, "Invalid dai\n"); + return -EINVAL; + } + + prtd->qmc_dai = qmc_dai; + + return qmc_dai_constraints_interleaved(substream, qmc_dai); +} + static int qmc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) -- cgit v1.2.3 From 37797c605da33445adc112561695f70bfaa11133 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:34 +0200 Subject: soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list qmc_chan_get_byphandle() and the resource managed version retrieve a channel from a simple phandle. Extend the API and introduce qmc_chan_get_byphandles_index() and the resource managed version in order to retrieve a channel from a phandle list using the provided index to identify the phandle in the list. Also update qmc_chan_get_byphandle() and the resource managed version to use qmc_chan_get_byphandles_index() and so avoid code duplication. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-8-herve.codina@bootlin.com Signed-off-by: Mark Brown --- drivers/soc/fsl/qe/qmc.c | 19 +++++++++++-------- include/soc/fsl/qe/qmc.h | 25 ++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index f498db9abe35..e23d60018400 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -1777,13 +1777,15 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign return qmc_chan; } -struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) +struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, + const char *phandles_name, + int index) { struct of_phandle_args out_args; struct qmc_chan *qmc_chan; int ret; - ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, + ret = of_parse_phandle_with_fixed_args(np, phandles_name, 1, index, &out_args); if (ret < 0) return ERR_PTR(ret); @@ -1797,7 +1799,7 @@ struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phan of_node_put(out_args.np); return qmc_chan; } -EXPORT_SYMBOL(qmc_chan_get_byphandle); +EXPORT_SYMBOL(qmc_chan_get_byphandles_index); struct qmc_chan *qmc_chan_get_bychild(struct device_node *np) { @@ -1827,9 +1829,10 @@ static void devm_qmc_chan_release(struct device *dev, void *res) qmc_chan_put(*qmc_chan); } -struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, - struct device_node *np, - const char *phandle_name) +struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev, + struct device_node *np, + const char *phandles_name, + int index) { struct qmc_chan *qmc_chan; struct qmc_chan **dr; @@ -1838,7 +1841,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - qmc_chan = qmc_chan_get_byphandle(np, phandle_name); + qmc_chan = qmc_chan_get_byphandles_index(np, phandles_name, index); if (!IS_ERR(qmc_chan)) { *dr = qmc_chan; devres_add(dev, dr); @@ -1848,7 +1851,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, return qmc_chan; } -EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); +EXPORT_SYMBOL(devm_qmc_chan_get_byphandles_index); struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np) diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h index 2a333fc1ea81..0fa7205145ce 100644 --- a/include/soc/fsl/qe/qmc.h +++ b/include/soc/fsl/qe/qmc.h @@ -16,11 +16,30 @@ struct device_node; struct device; struct qmc_chan; -struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); +struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, + const char *phandles_name, + int index); +struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev, + struct device_node *np, + const char *phandles_name, + int index); + +static inline struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, + const char *phandle_name) +{ + return qmc_chan_get_byphandles_index(np, phandle_name, 0); +} + +static inline struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, + struct device_node *np, + const char *phandle_name) +{ + return devm_qmc_chan_get_byphandles_index(dev, np, phandle_name, 0); +} + struct qmc_chan *qmc_chan_get_bychild(struct device_node *np); void qmc_chan_put(struct qmc_chan *chan); -struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np, - const char *phandle_name); + struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np); enum qmc_mode { -- cgit v1.2.3 From af8432b2e41abc0a20bdc01a3b144ea7b2f1ee09 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:35 +0200 Subject: soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles() No function in the QMC API is available to get the number of phandles present in a phandle list. Fill this lack introducing qmc_chan_count_phandles(). Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-9-herve.codina@bootlin.com Signed-off-by: Mark Brown --- drivers/soc/fsl/qe/qmc.c | 13 +++++++++++++ include/soc/fsl/qe/qmc.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index e23d60018400..76bb496305a0 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -1777,6 +1777,19 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign return qmc_chan; } +int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name) +{ + int count; + + /* phandles are fixed args phandles with one arg */ + count = of_count_phandle_with_args(np, phandles_name, NULL); + if (count < 0) + return count; + + return count / 2; +} +EXPORT_SYMBOL(qmc_chan_count_phandles); + struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, const char *phandles_name, int index) diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h index 0fa7205145ce..294e42ea8d4c 100644 --- a/include/soc/fsl/qe/qmc.h +++ b/include/soc/fsl/qe/qmc.h @@ -16,6 +16,8 @@ struct device_node; struct device; struct qmc_chan; +int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name); + struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, const char *phandles_name, int index); -- cgit v1.2.3 From fb6013168fa94d5863ed6085b24eaeb89102ad74 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:36 +0200 Subject: dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI The QMC audio uses one QMC channel per DAI and uses this QMC channel to transmit interleaved audio channel samples. In order to work in non-interleave mode, a QMC audio DAI needs to use multiple QMC channels. In that case, the DAI maps each QMC channel to exactly one audio channel. Allow QMC audio DAIs with multiple QMC channels attached. Signed-off-by: Herve Codina Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240701113038.55144-10-herve.codina@bootlin.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,qmc-audio.yaml | 41 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml index b522ed7dcc51..a23e49198c37 100644 --- a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml @@ -12,7 +12,9 @@ maintainers: description: | The QMC audio is an ASoC component which uses QMC (QUICC Multichannel Controller) channels to transfer the audio data. - It provides as many DAI as the number of QMC channel used. + It provides several DAIs. For each DAI, the DAI is working in interleaved mode + if only one QMC channel is used by the DAI or it is working in non-interleaved + mode if several QMC channels are used by the DAI. allOf: - $ref: dai-common.yaml# @@ -45,12 +47,19 @@ patternProperties: fsl,qmc-chan: $ref: /schemas/types.yaml#/definitions/phandle-array items: - - items: - - description: phandle to QMC node - - description: Channel number + items: + - description: phandle to QMC node + - description: Channel number + minItems: 1 description: - Should be a phandle/number pair. The phandle to QMC node and the QMC - channel to use for this DAI. + Should be a phandle/number pair list. The list of phandle to QMC node + and the QMC channel pair to use for this DAI. + If only one phandle/number pair is provided, this DAI works in + interleaved mode, i.e. audio channels for this DAI are interleaved in + the QMC channel. If more than one pair is provided, this DAI works + in non-interleave mode. In that case the first audio channel uses the + the first QMC channel, the second audio channel uses the second QMC + channel, etc... required: - reg @@ -79,6 +88,11 @@ examples: reg = <17>; fsl,qmc-chan = <&qmc 17>; }; + dai@18 { + reg = <18>; + /* Non-interleaved mode */ + fsl,qmc-chan = <&qmc 18>, <&qmc 19>; + }; }; sound { @@ -115,4 +129,19 @@ examples: dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>; }; }; + simple-audio-card,dai-link@2 { + reg = <2>; + format = "dsp_b"; + cpu { + sound-dai = <&audio_controller 18>; + }; + codec { + sound-dai = <&codec3>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <8>; + /* TS 9, 10 */ + dai-tdm-slot-tx-mask = <0 0 0 0 0 0 0 0 0 1 1>; + dai-tdm-slot-rx-mask = <0 0 0 0 0 0 0 0 0 1 1>; + }; + }; }; -- cgit v1.2.3 From 188d9cae54388171d28bd632a2561863db4b9f8b Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 1 Jul 2024 13:30:37 +0200 Subject: ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode. The current fsl_qmc_audio works in interleaved mode. The audio samples are interleaved and all data are sent to (received from) one QMC channel. Using several QMC channels, non interleaved mode can be easily supported. In that case, data related to ch0 are sent to (received from) the first QMC channel, data related to ch1 use the next QMC channel and so on up to the last channel. In terms of constraints and settings, the two modes are slightly different: - Interleaved mode: - The sample size should fit in the number of time-slots available for the QMC channel. - The number of audio channels should fit in the number of time-slots (taking into account the sample size) available for the QMC channel. - Non-interleaved mode: - The number of audio channels is the number of available QMC channels. - Each QMC channel should have the same number of time-slots. - The sample size equals the number of time-slots of one QMC channel. Add support for the non-interleaved mode allowing multiple QMC channel per DAI. The DAI switches in non-interleaved mode when more that one QMC channel is available. Signed-off-by: Herve Codina Link: https://patch.msgid.link/20240701113038.55144-11-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_qmc_audio.c | 372 +++++++++++++++++++++++++++++++++--------- 1 file changed, 297 insertions(+), 75 deletions(-) diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index f70c6c8eec4a..8668abd35208 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -29,7 +29,11 @@ struct qmc_dai { struct device *dev; unsigned int nb_tx_ts; unsigned int nb_rx_ts; - struct qmc_dai_chan chan; + + unsigned int nb_chans_avail; + unsigned int nb_chans_used_tx; + unsigned int nb_chans_used_rx; + struct qmc_dai_chan *chans; }; struct qmc_audio { @@ -50,7 +54,10 @@ struct qmc_dai_prtd { dma_addr_t ch_dma_addr_current; dma_addr_t ch_dma_addr_end; size_t ch_dma_size; + size_t ch_dma_offset; + unsigned int channels; + DECLARE_BITMAP(chans_pending, 64); struct snd_pcm_substream *substream; }; @@ -69,6 +76,18 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component, return 0; } +static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access) +{ + switch (access) { + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: + case SNDRV_PCM_ACCESS_RW_INTERLEAVED: + return true; + default: + break; + } + return false; +} + static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -76,6 +95,14 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct qmc_dai_prtd *prtd = substream->runtime->private_data; + /* + * In interleaved mode, the driver uses one QMC channel for all audio + * channels whereas in non-interleaved mode, it uses one QMC channel per + * audio channel. + */ + prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ? + 1 : params_channels(params); + prtd->substream = substream; prtd->buffer_ended = 0; @@ -83,9 +110,10 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, prtd->period_size = params_period_size(params); prtd->ch_dma_addr_start = runtime->dma_addr; - prtd->ch_dma_addr_end = runtime->dma_addr + params_buffer_bytes(params); + prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels; + prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset; prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; - prtd->ch_dma_size = params_period_bytes(params); + prtd->ch_dma_size = params_period_bytes(params) / prtd->channels; return 0; } @@ -94,16 +122,23 @@ static void qmc_audio_pcm_write_complete(void *context); static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd) { + unsigned int i; int ret; - ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_write_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", - ret); - return ret; + for (i = 0; i < prtd->channels; i++) { + bitmap_set(prtd->chans_pending, i, 1); + + ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan, + prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, + prtd->ch_dma_size, + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chans[i]); + if (ret) { + dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n", + i, ret); + bitmap_clear(prtd->chans_pending, i, 1); + return ret; + } } return 0; @@ -116,6 +151,16 @@ static void qmc_audio_pcm_write_complete(void *context) prtd = chan->prtd_tx; + /* Mark the current channel as completed */ + bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); + + /* + * All QMC channels involved must have completed their transfer before + * submitting a new one. + */ + if (!bitmap_empty(prtd->chans_pending, 64)) + return; + prtd->buffer_ended += prtd->period_size; if (prtd->buffer_ended >= prtd->buffer_size) prtd->buffer_ended = 0; @@ -133,15 +178,23 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd) { + unsigned int i; int ret; - ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan, - prtd->ch_dma_addr_current, prtd->ch_dma_size, - qmc_audio_pcm_read_complete, - &prtd->qmc_dai->chan); - if (ret) { - dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", - ret); + for (i = 0; i < prtd->channels; i++) { + bitmap_set(prtd->chans_pending, i, 1); + + ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan, + prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, + prtd->ch_dma_size, + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chans[i]); + if (ret) { + dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n", + i, ret); + bitmap_clear(prtd->chans_pending, i, 1); + return ret; + } } return 0; @@ -154,11 +207,21 @@ static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned i prtd = chan->prtd_rx; + /* Mark the current channel as completed */ + bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); + if (length != prtd->ch_dma_size) { dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", length, prtd->ch_dma_size); } + /* + * All QMC channels involved must have completed their transfer before + * submitting a new one. + */ + if (!bitmap_empty(prtd->chans_pending, 64)) + return; + prtd->buffer_ended += prtd->period_size; if (prtd->buffer_ended >= prtd->buffer_size) prtd->buffer_ended = 0; @@ -176,6 +239,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct qmc_dai_prtd *prtd = substream->runtime->private_data; + unsigned int i; int ret; if (!prtd->qmc_dai) { @@ -185,8 +249,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, switch (cmd) { case SNDRV_PCM_TRIGGER_START: + bitmap_zero(prtd->chans_pending, 64); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - prtd->qmc_dai->chan.prtd_tx = prtd; + for (i = 0; i < prtd->channels; i++) + prtd->qmc_dai->chans[i].prtd_tx = prtd; /* Submit first chunk ... */ ret = qmc_audio_pcm_write_submit(prtd); @@ -203,7 +269,8 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, if (ret) return ret; } else { - prtd->qmc_dai->chan.prtd_rx = prtd; + for (i = 0; i < prtd->channels; i++) + prtd->qmc_dai->chans[i].prtd_rx = prtd; /* Submit first chunk ... */ ret = qmc_audio_pcm_read_submit(prtd); @@ -270,6 +337,7 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NONINTERLEAVED | SNDRV_PCM_INFO_PAUSE, .period_bytes_min = 32, .period_bytes_max = 64 * 1024, @@ -442,6 +510,7 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, snd_pcm_hw_rule_func_t hw_rule_channels_by_format; snd_pcm_hw_rule_func_t hw_rule_format_by_channels; unsigned int frame_bits; + u64 access; int ret; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { @@ -478,6 +547,44 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, return ret; } + access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | + 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED; + ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, + access); + if (ret) { + dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret); + return ret; + } + + return 0; +} + +static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream, + struct qmc_dai *qmc_dai) +{ + unsigned int frame_bits; + u64 access; + int ret; + + frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ? + qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8; + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + frame_bits); + if (ret < 0) { + dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | + 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, + access); + if (ret) { + dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret); + return ret; + } + return 0; } @@ -495,7 +602,9 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, prtd->qmc_dai = qmc_dai; - return qmc_dai_constraints_interleaved(substream, qmc_dai); + return qmc_dai->nb_chans_avail > 1 ? + qmc_dai_constraints_noninterleaved(substream, qmc_dai) : + qmc_dai_constraints_interleaved(substream, qmc_dai); } static int qmc_dai_hw_params(struct snd_pcm_substream *substream, @@ -503,7 +612,9 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct qmc_chan_param chan_param = {0}; + unsigned int nb_chans_used; struct qmc_dai *qmc_dai; + unsigned int i; int ret; qmc_dai = qmc_dai_get_data(dai); @@ -512,15 +623,34 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * In interleaved mode, the driver uses one QMC channel for all audio + * channels whereas in non-interleaved mode, it uses one QMC channel per + * audio channel. + */ + nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ? + 1 : params_channels(params); + + if (nb_chans_used > qmc_dai->nb_chans_avail) { + dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n", + nb_chans_used, qmc_dai->nb_chans_avail); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { chan_param.mode = QMC_TRANSPARENT; - chan_param.transp.max_rx_buf_size = params_period_bytes(params); - ret = qmc_chan_set_param(qmc_dai->chan.qmc_chan, &chan_param); - if (ret) { - dev_err(dai->dev, "set param failed %d\n", - ret); - return ret; + chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used; + for (i = 0; i < nb_chans_used; i++) { + ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param); + if (ret) { + dev_err(dai->dev, "chans[%u], set param failed %d\n", + i, ret); + return ret; + } } + qmc_dai->nb_chans_used_rx = nb_chans_used; + } else { + qmc_dai->nb_chans_used_tx = nb_chans_used; } return 0; @@ -529,9 +659,12 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + unsigned int nb_chans_used; struct qmc_dai *qmc_dai; + unsigned int i; int direction; - int ret; + int ret = 0; + int ret_tmp; qmc_dai = qmc_dai_get_data(dai); if (!qmc_dai) { @@ -539,30 +672,50 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return -EINVAL; } - direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - QMC_CHAN_WRITE : QMC_CHAN_READ; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = QMC_CHAN_WRITE; + nb_chans_used = qmc_dai->nb_chans_used_tx; + } else { + direction = QMC_CHAN_READ; + nb_chans_used = qmc_dai->nb_chans_used_rx; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = qmc_chan_start(qmc_dai->chan.qmc_chan, direction); - if (ret) - return ret; + for (i = 0; i < nb_chans_used; i++) { + ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction); + if (ret) + goto err_stop; + } break; case SNDRV_PCM_TRIGGER_STOP: - ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction); - if (ret) - return ret; - ret = qmc_chan_reset(qmc_dai->chan.qmc_chan, direction); + /* Stop and reset all QMC channels and return the first error encountered */ + for (i = 0; i < nb_chans_used; i++) { + ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + if (ret_tmp) + continue; + + ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + } if (ret) return ret; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction); + /* Stop all QMC channels and return the first error encountered */ + for (i = 0; i < nb_chans_used; i++) { + ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + } if (ret) return ret; break; @@ -572,6 +725,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, } return 0; + +err_stop: + while (i--) { + qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); + } + return ret; } static const struct snd_soc_dai_ops qmc_dai_ops = { @@ -580,7 +740,7 @@ static const struct snd_soc_dai_ops qmc_dai_ops = { .hw_params = qmc_dai_hw_params, }; -static u64 qmc_audio_formats(u8 nb_ts) +static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved) { unsigned int format_width; unsigned int chan_width; @@ -612,6 +772,13 @@ static u64 qmc_audio_formats(u8 nb_ts) if (format_width > chan_width || chan_width % format_width) continue; + /* + * In non interleaved mode, we can only support formats that + * can fit only 1 time in the channel + */ + if (is_noninterleaved && format_width != chan_width) + continue; + formats_mask |= pcm_format_to_bits(format); } return formats_mask; @@ -622,6 +789,12 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * struct snd_soc_dai_driver *qmc_soc_dai_driver) { struct qmc_chan_info info; + unsigned long rx_fs_rate; + unsigned long tx_fs_rate; + unsigned int nb_tx_ts; + unsigned int nb_rx_ts; + unsigned int i; + int count; u32 val; int ret; @@ -639,56 +812,105 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * if (!qmc_dai->name) return -ENOMEM; - qmc_dai->chan.qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np, - "fsl,qmc-chan"); - if (IS_ERR(qmc_dai->chan.qmc_chan)) { - ret = PTR_ERR(qmc_dai->chan.qmc_chan); - return dev_err_probe(qmc_audio->dev, ret, - "dai %d get QMC channel failed\n", qmc_dai->id); - } + count = qmc_chan_count_phandles(np, "fsl,qmc-chan"); + if (count < 0) + return dev_err_probe(qmc_audio->dev, count, + "dai %d get number of QMC channel failed\n", qmc_dai->id); + if (!count) + return dev_err_probe(qmc_audio->dev, -EINVAL, + "dai %d no QMC channel defined\n", qmc_dai->id); - qmc_soc_dai_driver->id = qmc_dai->id; - qmc_soc_dai_driver->name = qmc_dai->name; + qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL); + if (!qmc_dai->chans) + return -ENOMEM; - ret = qmc_chan_get_info(qmc_dai->chan.qmc_chan, &info); - if (ret) { - dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n", - qmc_dai->id, ret); - return ret; - } - dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n", - qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts); + for (i = 0; i < count; i++) { + qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, + "fsl,qmc-chan", i); + if (IS_ERR(qmc_dai->chans[i].qmc_chan)) { + return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan), + "dai %d get QMC channel %d failed\n", qmc_dai->id, i); + } - if (info.mode != QMC_TRANSPARENT) { - dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n", - qmc_dai->id, info.mode); - return -EINVAL; + ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info); + if (ret) { + dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n", + qmc_dai->id, i, ret); + return ret; + } + dev_info(qmc_audio->dev, "dai %d QMC channel %d mode %d, nb_tx_ts %u, nb_rx_ts %u\n", + qmc_dai->id, i, info.mode, info.nb_tx_ts, info.nb_rx_ts); + + if (info.mode != QMC_TRANSPARENT) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n", + qmc_dai->id, i, info.mode); + return -EINVAL; + } + + /* + * All channels must have the same number of Tx slots and the + * same numbers of Rx slots. + */ + if (i == 0) { + nb_tx_ts = info.nb_tx_ts; + nb_rx_ts = info.nb_rx_ts; + tx_fs_rate = info.tx_fs_rate; + rx_fs_rate = info.rx_fs_rate; + } else { + if (nb_tx_ts != info.nb_tx_ts) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n", + qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts); + return -EINVAL; + } + if (nb_rx_ts != info.nb_rx_ts) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n", + qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts); + return -EINVAL; + } + if (tx_fs_rate != info.tx_fs_rate) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n", + qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate); + return -EINVAL; + } + if (rx_fs_rate != info.rx_fs_rate) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n", + qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate); + return -EINVAL; + } + } } - qmc_dai->nb_tx_ts = info.nb_tx_ts; - qmc_dai->nb_rx_ts = info.nb_rx_ts; + + qmc_dai->nb_chans_avail = count; + qmc_dai->nb_tx_ts = nb_tx_ts * count; + qmc_dai->nb_rx_ts = nb_rx_ts * count; + + qmc_soc_dai_driver->id = qmc_dai->id; + qmc_soc_dai_driver->name = qmc_dai->name; qmc_soc_dai_driver->playback.channels_min = 0; qmc_soc_dai_driver->playback.channels_max = 0; - if (qmc_dai->nb_tx_ts) { + if (nb_tx_ts) { qmc_soc_dai_driver->playback.channels_min = 1; - qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts; + qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts; } - qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts); + qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts, + count > 1 ? true : false); qmc_soc_dai_driver->capture.channels_min = 0; qmc_soc_dai_driver->capture.channels_max = 0; - if (qmc_dai->nb_rx_ts) { + if (nb_rx_ts) { qmc_soc_dai_driver->capture.channels_min = 1; - qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts; + qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts; } - qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts); - - qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate); - qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate; - qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate; - qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate); - qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate; - qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate; + qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts, + count > 1 ? true : false); + + qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate); + qmc_soc_dai_driver->playback.rate_min = tx_fs_rate; + qmc_soc_dai_driver->playback.rate_max = tx_fs_rate; + qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate); + qmc_soc_dai_driver->capture.rate_min = rx_fs_rate; + qmc_soc_dai_driver->capture.rate_max = rx_fs_rate; qmc_soc_dai_driver->ops = &qmc_dai_ops; -- cgit v1.2.3 From 6558e8737af2067763e115ce3759372cd068b897 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 4 Jul 2024 10:59:44 +0200 Subject: ASoC: SOF: ipc4-topology: Use single token list for the copiers There is no need to keep separate token list for dai and 'common' copier token list when the 'common' list is actually the aif list, the SOF_COPIER_DEEP_BUFFER_TOKENS are not applicable for buffers. We could have separate lists for all types but it is probably simpler to just use a single list for all types of copiers. Function specific tokens will be only parsed by function specific code anyways. Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Signed-off-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20240704085944.371450-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 7755c01735f5..90f6856ee80c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3307,14 +3307,17 @@ static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link return 0; } -static enum sof_tokens common_copier_token_list[] = { +/* Tokens needed for different copier variants (aif, dai and buffer) */ +static enum sof_tokens copier_token_list[] = { SOF_COMP_TOKENS, + SOF_COPIER_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_DEEP_BUFFER_TOKENS, - SOF_COPIER_TOKENS, SOF_COMP_EXT_TOKENS, + + SOF_COPIER_DEEP_BUFFER_TOKENS, /* for AIF copier */ + SOF_DAI_TOKENS, /* for DAI copier */ }; static enum sof_tokens pipeline_token_list[] = { @@ -3322,16 +3325,6 @@ static enum sof_tokens pipeline_token_list[] = { SOF_PIPELINE_TOKENS, }; -static enum sof_tokens dai_token_list[] = { - SOF_COMP_TOKENS, - SOF_AUDIO_FMT_NUM_TOKENS, - SOF_IN_AUDIO_FORMAT_TOKENS, - SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_TOKENS, - SOF_DAI_TOKENS, - SOF_COMP_EXT_TOKENS, -}; - static enum sof_tokens pga_token_list[] = { SOF_COMP_TOKENS, SOF_GAIN_TOKENS, @@ -3368,23 +3361,23 @@ static enum sof_tokens process_token_list[] = { static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, - dai_token_list, ARRAY_SIZE(dai_token_list), NULL, + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, - dai_token_list, ARRAY_SIZE(dai_token_list), NULL, + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, -- cgit v1.2.3 From 1e0dff741b0a8af35645e5c3ffc4050893002a9c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 3 Jul 2024 20:26:15 +0800 Subject: ASoC: ak4458: remove "reset-gpios" property handler commit c721f189e89c0 ("reset: Instantiate reset GPIO controller for shared reset-gpios") check if there is no "resets" property will fallback to "reset-gpios". So don't need to handle "reset-gpios" separately in the driver, the "reset-gpios" handler is duplicated with "resets" control handler, remove it. Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/1720009575-11677-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4458.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 32cb802ad635..d472d9952628 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -45,7 +45,6 @@ struct ak4458_priv { const struct ak4458_drvdata *drvdata; struct device *dev; struct regmap *regmap; - struct gpio_desc *reset_gpiod; struct reset_control *reset; struct gpio_desc *mute_gpiod; int digfil; /* SSLOW, SD, SLOW bits */ @@ -631,10 +630,7 @@ static struct snd_soc_dai_driver ak4497_dai = { static void ak4458_reset(struct ak4458_priv *ak4458, bool active) { - if (ak4458->reset_gpiod) { - gpiod_set_value_cansleep(ak4458->reset_gpiod, active); - usleep_range(1000, 2000); - } else if (!IS_ERR_OR_NULL(ak4458->reset)) { + if (!IS_ERR_OR_NULL(ak4458->reset)) { if (active) reset_control_assert(ak4458->reset); else @@ -758,11 +754,6 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->reset)) return PTR_ERR(ak4458->reset); - ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ak4458->reset_gpiod)) - return PTR_ERR(ak4458->reset_gpiod); - ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute", GPIOD_OUT_LOW); if (IS_ERR(ak4458->mute_gpiod)) -- cgit v1.2.3 From 9f3ae72c5dbca9ba558c752f1ef969ed6908be01 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 14:26:16 +0200 Subject: ASoC: codecs: wcd939x: Fix typec mux and switch leak during device removal Driver does not unregister typec structures (typec_mux_dev and typec_switch_desc) during removal leading to leaks. Fix this by moving typec registering parts to separate function and using devm interface to release them. This also makes code a bit simpler: - Smaller probe() function with less error paths and no #ifdefs, - No need to store typec_mux_dev and typec_switch_desc in driver state container structure. Cc: stable@vger.kernel.org Fixes: 10f514bd172a ("ASoC: codecs: Add WCD939x Codec driver") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20240701122616.414158-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 113 ++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 47 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 66af035bd0e5..68fc591670dc 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -181,8 +181,6 @@ struct wcd939x_priv { /* typec handling */ bool typec_analog_mux; #if IS_ENABLED(CONFIG_TYPEC) - struct typec_mux_dev *typec_mux; - struct typec_switch_dev *typec_sw; enum typec_orientation typec_orientation; unsigned long typec_mode; struct typec_switch *typec_switch; @@ -3519,6 +3517,68 @@ static const struct component_master_ops wcd939x_comp_ops = { .unbind = wcd939x_unbind, }; +static void __maybe_unused wcd939x_typec_mux_unregister(void *data) +{ + struct typec_mux_dev *typec_mux = data; + + typec_mux_unregister(typec_mux); +} + +static void __maybe_unused wcd939x_typec_switch_unregister(void *data) +{ + struct typec_switch_dev *typec_sw = data; + + typec_switch_unregister(typec_sw); +} + +static int wcd939x_add_typec(struct wcd939x_priv *wcd939x, struct device *dev) +{ +#if IS_ENABLED(CONFIG_TYPEC) + int ret; + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; + struct typec_mux_desc mux_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_mux_set, + }; + struct typec_switch_desc sw_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_switch_set, + }; + + /* + * Is USBSS is used to mux analog lines, + * register a typec mux/switch to get typec events + */ + if (!wcd939x->typec_analog_mux) + return 0; + + typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(typec_mux)) + return dev_err_probe(dev, PTR_ERR(typec_mux), + "failed to register typec mux\n"); + + ret = devm_add_action_or_reset(dev, wcd939x_typec_mux_unregister, + typec_mux); + if (ret) + return ret; + + typec_sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(typec_sw)) + return dev_err_probe(dev, PTR_ERR(typec_sw), + "failed to register typec switch\n"); + + ret = devm_add_action_or_reset(dev, wcd939x_typec_switch_unregister, + typec_sw); + if (ret) + return ret; +#endif + + return 0; +} + static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, struct device *dev, struct component_match **matchptr) @@ -3567,42 +3627,13 @@ static int wcd939x_probe(struct platform_device *pdev) return -EINVAL; } -#if IS_ENABLED(CONFIG_TYPEC) - /* - * Is USBSS is used to mux analog lines, - * register a typec mux/switch to get typec events - */ - if (wcd939x->typec_analog_mux) { - struct typec_mux_desc mux_desc = { - .drvdata = wcd939x, - .fwnode = dev_fwnode(dev), - .set = wcd939x_typec_mux_set, - }; - struct typec_switch_desc sw_desc = { - .drvdata = wcd939x, - .fwnode = dev_fwnode(dev), - .set = wcd939x_typec_switch_set, - }; - - wcd939x->typec_mux = typec_mux_register(dev, &mux_desc); - if (IS_ERR(wcd939x->typec_mux)) { - ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_mux), - "failed to register typec mux\n"); - goto err_disable_regulators; - } - - wcd939x->typec_sw = typec_switch_register(dev, &sw_desc); - if (IS_ERR(wcd939x->typec_sw)) { - ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_sw), - "failed to register typec switch\n"); - goto err_unregister_typec_mux; - } - } -#endif /* CONFIG_TYPEC */ + ret = wcd939x_add_typec(wcd939x, dev); + if (ret) + goto err_disable_regulators; ret = wcd939x_add_slave_components(wcd939x, dev, &match); if (ret) - goto err_unregister_typec_switch; + goto err_disable_regulators; wcd939x_reset(wcd939x); @@ -3619,18 +3650,6 @@ static int wcd939x_probe(struct platform_device *pdev) return 0; -#if IS_ENABLED(CONFIG_TYPEC) -err_unregister_typec_mux: - if (wcd939x->typec_analog_mux) - typec_mux_unregister(wcd939x->typec_mux); -#endif /* CONFIG_TYPEC */ - -err_unregister_typec_switch: -#if IS_ENABLED(CONFIG_TYPEC) - if (wcd939x->typec_analog_mux) - typec_switch_unregister(wcd939x->typec_sw); -#endif /* CONFIG_TYPEC */ - err_disable_regulators: regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); -- cgit v1.2.3 From 5080808c3339de2220c602ab7c7fa23dc6c1a5a3 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Wed, 3 Jul 2024 22:10:07 +0300 Subject: ASoC: amd: Adjust error handling in case of absent codec device acpi_get_first_physical_node() can return NULL in several cases (no such device, ACPI table error, reference count drop to 0, etc). Existing check just emit error message, but doesn't perform return. Then this NULL pointer is passed to devm_acpi_dev_add_driver_gpios() where it is dereferenced. Adjust this error handling by adding error code return. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 02527c3f2300 ("ASoC: amd: add Machine driver for Jadeite platform") Signed-off-by: Aleksandr Mishin Link: https://patch.msgid.link/20240703191007.8524-1-amishin@t-argos.ru Signed-off-by: Mark Brown --- sound/soc/amd/acp-es8336.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c index e079b3218c6f..3756b8bef17b 100644 --- a/sound/soc/amd/acp-es8336.c +++ b/sound/soc/amd/acp-es8336.c @@ -203,8 +203,10 @@ static int st_es8336_late_probe(struct snd_soc_card *card) codec_dev = acpi_get_first_physical_node(adev); acpi_dev_put(adev); - if (!codec_dev) + if (!codec_dev) { dev_err(card->dev, "can not find codec dev\n"); + return -ENODEV; + } ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios); if (ret) -- cgit v1.2.3 From cabf0b0cff07bc8d6e80ab6a8f8a127a2708147b Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Thu, 4 Jul 2024 17:49:37 +0800 Subject: ASoc: tas2781: Set "Speaker Force Firmware Load" as the common kcontrol for both tas27871 and tas2563 Set "Speaker Force Firmware Load" as the common kcontrol for both tas27871 and tas2563 and move it into newly-created tasdevice_snd_controls, and keep the digital gain and analog gain in tas2781_snd_controls. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240704094939.1824-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index e0e630f66101..b412f8f2e5d5 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -104,7 +104,7 @@ static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, return tasdevice_amp_putvol(tas_priv, ucontrol, mc); } -static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -119,7 +119,7 @@ static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, return 0; } -static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -140,6 +140,11 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static const struct snd_kcontrol_new tasdevice_snd_controls[] = { + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tasdev_force_fwload_get, tasdev_force_fwload_put), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, @@ -147,8 +152,6 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, 0, 0, 200, 1, tas2781_digital_getvol, tas2781_digital_putvol, dvc_tlv), - SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, - tas2781_force_fwload_get, tas2781_force_fwload_put), }; static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, @@ -590,6 +593,18 @@ static struct snd_soc_dai_driver tasdevice_dai_driver[] = { static int tasdevice_codec_probe(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int rc; + + if (tas_priv->chip_id == TAS2781) { + rc = snd_soc_add_component_controls(codec, + tas2781_snd_controls, + ARRAY_SIZE(tas2781_snd_controls)); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: Add control err rc = %d", + __func__, rc); + return rc; + } + } tas_priv->name_prefix = codec->name_prefix; return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready); @@ -617,8 +632,8 @@ static const struct snd_soc_component_driver soc_codec_driver_tasdevice = { .probe = tasdevice_codec_probe, .remove = tasdevice_codec_remove, - .controls = tas2781_snd_controls, - .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .controls = tasdevice_snd_controls, + .num_controls = ARRAY_SIZE(tasdevice_snd_controls), .dapm_widgets = tasdevice_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), .dapm_routes = tasdevice_audio_map, @@ -759,7 +774,7 @@ MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); static struct i2c_driver tasdevice_i2c_driver = { .driver = { - .name = "tas2781-codec", + .name = "tasdev-codec", .of_match_table = of_match_ptr(tasdevice_of_match), #ifdef CONFIG_ACPI .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), -- cgit v1.2.3 From ccb367c5ab5814bb731eb517544a2ba6b7c3b689 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 5 Jul 2024 14:48:46 +0800 Subject: ASoc: pcm6240: Remove unnecessary name-prefix for all the controls Adding name-prefix for each audio controls is a redundant, because name-prefix will be automatically added behind the control name when creating a new control. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240705064846.1723-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/pcm6240.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 5faf5e97a105..6641e7c1ddf4 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -58,12 +58,6 @@ static const char *const pcmdev_ctrl_name[] = { "%s i2c%d Dev%d Ch%d Fine Volume", }; -static const char *const pcmdev_ctrl_name_with_prefix[] = { - "%s Dev%d Ch%d Ana Volume", - "%s Dev%d Ch%d Digi Volume", - "%s Dev%d Ch%d Fine Volume", -}; - static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = { { .shift = 1, @@ -1366,10 +1360,7 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev, name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id; - if (comp->name_prefix) - ctrl_name = pcmdev_ctrl_name_with_prefix[name_id]; - else - ctrl_name = pcmdev_ctrl_name[name_id]; + ctrl_name = pcmdev_ctrl_name[name_id]; for (chn = 1; chn <= nr_chn; chn++) { name = devm_kzalloc(pcm_dev->dev, @@ -1378,13 +1369,9 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev, ret = -ENOMEM; goto out; } - if (comp->name_prefix) - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - ctrl_name, comp->name_prefix, dev_no, chn); - else - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - ctrl_name, pcm_dev->upper_dev_name, adap->nr, - dev_no, chn); + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + ctrl_name, pcm_dev->upper_dev_name, adap->nr, + dev_no, chn); pcmdev_controls[mix_index].tlv.p = pcmdev_gain_ctl_info[id][ctl_id].gain; pcmdev_ctrl = devm_kmemdup(pcm_dev->dev, @@ -1438,13 +1425,8 @@ static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev) if (!name) return -ENOMEM; - if (comp->name_prefix) - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s Profile id", comp->name_prefix); - else - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s i2c%d Profile id", pcm_dev->upper_dev_name, - adap->nr); + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr); pcmdev_ctrl->name = name; pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; pcmdev_ctrl->info = pcmdevice_info_profile; -- cgit v1.2.3 From 408e49381750ca22fc584a37636f5035d2cd4c25 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:10:55 +0200 Subject: ASoC: codecs: audio-iio-aux: Simplify audio_iio_aux_add_dapms() with cleanup.h Allocate the memory with scoped/cleanup.h in audio_iio_aux_add_dapms() to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-1-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/audio-iio-aux.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 1e8e1effc2af..3969ee45f41e 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -6,6 +6,7 @@ // // Author: Herve Codina +#include #include #include #include @@ -131,33 +132,27 @@ static int audio_iio_aux_add_dapms(struct snd_soc_component *component, struct audio_iio_aux_chan *chan) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - char *output_name; - char *input_name; - char *pga_name; int ret; - input_name = kasprintf(GFP_KERNEL, "%s IN", chan->name); + /* Allocated names are not needed afterwards (duplicated in ASoC internals) */ + char *input_name __free(kfree) = kasprintf(GFP_KERNEL, "%s IN", chan->name); if (!input_name) return -ENOMEM; - output_name = kasprintf(GFP_KERNEL, "%s OUT", chan->name); - if (!output_name) { - ret = -ENOMEM; - goto out_free_input_name; - } + char *output_name __free(kfree) = kasprintf(GFP_KERNEL, "%s OUT", chan->name); + if (!output_name) + return -ENOMEM; - pga_name = kasprintf(GFP_KERNEL, "%s PGA", chan->name); - if (!pga_name) { - ret = -ENOMEM; - goto out_free_output_name; - } + char *pga_name __free(kfree) = kasprintf(GFP_KERNEL, "%s PGA", chan->name); + if (!pga_name) + return -ENOMEM; widgets[0] = SND_SOC_DAPM_INPUT(input_name); widgets[1] = SND_SOC_DAPM_OUTPUT(output_name); widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0); ret = snd_soc_dapm_new_controls(dapm, widgets, 3); if (ret) - goto out_free_pga_name; + return ret; routes[0].sink = pga_name; routes[0].control = NULL; @@ -165,17 +160,8 @@ static int audio_iio_aux_add_dapms(struct snd_soc_component *component, routes[1].sink = output_name; routes[1].control = NULL; routes[1].source = pga_name; - ret = snd_soc_dapm_add_routes(dapm, routes, 2); - /* Allocated names are no more needed (duplicated in ASoC internals) */ - -out_free_pga_name: - kfree(pga_name); -out_free_output_name: - kfree(output_name); -out_free_input_name: - kfree(input_name); - return ret; + return snd_soc_dapm_add_routes(dapm, routes, 2); } static int audio_iio_aux_component_probe(struct snd_soc_component *component) -- cgit v1.2.3 From f9cbfb66127bfc2a47dece3dfcdab2b79ab06c50 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:10:56 +0200 Subject: ASoC: codecs: audio-iio-aux: Simplify audio_iio_aux_probe() with cleanup.h Allocate the memory with scoped/cleanup.h in audio_iio_aux_probe() to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-2-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/audio-iio-aux.c | 47 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 3969ee45f41e..588e48044c13 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -230,8 +230,6 @@ static int audio_iio_aux_probe(struct platform_device *pdev) struct audio_iio_aux_chan *iio_aux_chan; struct device *dev = &pdev->dev; struct audio_iio_aux *iio_aux; - const char **names; - u32 *invert_ranges; int count; int ret; int i; @@ -248,22 +246,22 @@ static int audio_iio_aux_probe(struct platform_device *pdev) iio_aux->num_chans = count; - names = kcalloc(iio_aux->num_chans, sizeof(*names), GFP_KERNEL); + const char **names __free(kfree) = kcalloc(iio_aux->num_chans, + sizeof(*names), + GFP_KERNEL); if (!names) return -ENOMEM; - invert_ranges = kcalloc(iio_aux->num_chans, sizeof(*invert_ranges), GFP_KERNEL); - if (!invert_ranges) { - ret = -ENOMEM; - goto out_free_names; - } + u32 *invert_ranges __free(kfree) = kcalloc(iio_aux->num_chans, + sizeof(*invert_ranges), + GFP_KERNEL); + if (!invert_ranges) + return -ENOMEM; ret = device_property_read_string_array(dev, "io-channel-names", names, iio_aux->num_chans); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to read io-channel-names\n"); - goto out_free_invert_ranges; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read io-channel-names\n"); /* * snd-control-invert-range is optional and can contain fewer items @@ -274,10 +272,8 @@ static int audio_iio_aux_probe(struct platform_device *pdev) count = min_t(unsigned int, count, iio_aux->num_chans); ret = device_property_read_u32_array(dev, "snd-control-invert-range", invert_ranges, count); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); - goto out_free_invert_ranges; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); } for (i = 0; i < iio_aux->num_chans; i++) { @@ -286,23 +282,16 @@ static int audio_iio_aux_probe(struct platform_device *pdev) iio_aux_chan->is_invert_range = invert_ranges[i]; iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name); - if (IS_ERR(iio_aux_chan->iio_chan)) { - ret = PTR_ERR(iio_aux_chan->iio_chan); - dev_err_probe(dev, ret, "get IIO channel '%s' failed\n", - iio_aux_chan->name); - goto out_free_invert_ranges; - } + if (IS_ERR(iio_aux_chan->iio_chan)) + return dev_err_probe(dev, PTR_ERR(iio_aux_chan->iio_chan), + "get IIO channel '%s' failed\n", + iio_aux_chan->name); } platform_set_drvdata(pdev, iio_aux); - ret = devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, - NULL, 0); -out_free_invert_ranges: - kfree(invert_ranges); -out_free_names: - kfree(names); - return ret; + return devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, + NULL, 0); } static const struct of_device_id audio_iio_aux_ids[] = { -- cgit v1.2.3 From 6344ab5d0826640799e0c054ed4c0846b3f87ccb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:10:57 +0200 Subject: ASoC: codecs: wcd9335: Simplify with cleanup.h Allocate the memory with scoped/cleanup.h to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-3-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 1a20131e2a60..373a31ddccb2 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -2714,25 +2715,23 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); unsigned int decimator; char *dec_adc_mux_name = NULL; - char *widget_name = NULL; - char *wname; + char *widget_name; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; u16 tx_gain_ctl_reg; char *dec; u8 hpf_coff_freq; - widget_name = kmemdup_nul(w->name, 15, GFP_KERNEL); - if (!widget_name) + char *wname __free(kfree) = kmemdup_nul(w->name, 15, GFP_KERNEL); + if (!wname) return -ENOMEM; - wname = widget_name; + widget_name = wname; dec_adc_mux_name = strsep(&widget_name, " "); if (!dec_adc_mux_name) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, w->name); - ret = -EINVAL; - goto out; + return -EINVAL; } dec_adc_mux_name = widget_name; @@ -2740,16 +2739,14 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, if (!dec) { dev_err(comp->dev, "%s: decimator index not found\n", __func__); - ret = -EINVAL; - goto out; + return -EINVAL; } ret = kstrtouint(dec, 10, &decimator); if (ret < 0) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, wname); - ret = -EINVAL; - goto out; + return -EINVAL; } tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; @@ -2836,8 +2833,7 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); break; } -out: - kfree(wname); + return ret; } -- cgit v1.2.3 From 56d426f5525d1ad919e20663ad01a58238652df7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:10:58 +0200 Subject: ASoC: codecs: wcd934x: Simplify with cleanup.h Allocate the memory with scoped/cleanup.h to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-4-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 6c65b46e2dc9..291d0c80a6fc 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019, Linaro Limited +#include #include #include #include @@ -4973,25 +4974,23 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); unsigned int decimator; char *dec_adc_mux_name = NULL; - char *widget_name = NULL; - char *wname; + char *widget_name; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; u16 tx_gain_ctl_reg; char *dec; u8 hpf_coff_freq; - widget_name = kstrndup(w->name, 15, GFP_KERNEL); - if (!widget_name) + char *wname __free(kfree) = kstrndup(w->name, 15, GFP_KERNEL); + if (!wname) return -ENOMEM; - wname = widget_name; + widget_name = wname; dec_adc_mux_name = strsep(&widget_name, " "); if (!dec_adc_mux_name) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, w->name); - ret = -EINVAL; - goto out; + return -EINVAL; } dec_adc_mux_name = widget_name; @@ -4999,16 +4998,14 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, if (!dec) { dev_err(comp->dev, "%s: decimator index not found\n", __func__); - ret = -EINVAL; - goto out; + return -EINVAL; } ret = kstrtouint(dec, 10, &decimator); if (ret < 0) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, wname); - ret = -EINVAL; - goto out; + return -EINVAL; } tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; @@ -5101,8 +5098,7 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, WCD934X_DEC_PWR_LVL_DF); break; } -out: - kfree(wname); + return ret; } -- cgit v1.2.3 From 6440e7b2a058c50a05ebcc58f35693c50522fc1a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:10:59 +0200 Subject: ASoC: simple-card-utils: Simplify with cleanup.h Allocate the memory with scoped/cleanup.h to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-5-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a18de86b3c88..fedae7f6f70c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -5,6 +5,7 @@ // Copyright (c) 2016 Kuninori Morimoto #include +#include #include #include #include @@ -135,8 +136,8 @@ EXPORT_SYMBOL_GPL(simple_util_parse_daifmt); int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, struct simple_util_dai *dai) { - u32 *array_values, *p; int n, i, ret; + u32 *p; if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) return 0; @@ -151,14 +152,15 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, if (!dai->tdm_width_map) return -ENOMEM; - array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL); + u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), + GFP_KERNEL); if (!array_values) return -ENOMEM; ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n); if (ret < 0) { dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret); - goto out; + return ret; } p = array_values; @@ -169,11 +171,8 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, } dai->n_tdm_widths = i; - ret = 0; -out: - kfree(array_values); - return ret; + return 0; } EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map); -- cgit v1.2.3 From 5725c16af2678d334de0bcb85b42cfa50b32e04c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:00 +0200 Subject: ASoC: audio-graph-card: Use cleanup.h instead of devm_kfree() Allocate the memory with scoped/cleanup.h, instead of devm interface, to make the code more obvious that memory is not used outside this scope. Signed-off-by: Krzysztof Kozlowski Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-6-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index acf7d92d21e6..3425fbbcbd7e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -7,6 +7,7 @@ // // based on ${LINUX}/sound/soc/generic/simple-card.c +#include #include #include #include @@ -573,10 +574,9 @@ static int graph_get_dais_count(struct simple_util_priv *priv, int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) { struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; int ret; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -628,7 +628,6 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; - devm_kfree(dev, li); return 0; err: -- cgit v1.2.3 From b39f7713ece62b2b0a3cfad7a75a0eb0ab71aa4e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:01 +0200 Subject: ASoC: audio-graph-card2: Use cleanup.h instead of devm_kfree() Allocate the memory with scoped/cleanup.h, instead of devm interface, to make the code more obvious that memory is not used outside this scope. Signed-off-by: Krzysztof Kozlowski Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-7-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 93d9d045587b..56f7f946882e 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1350,10 +1350,9 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, struct graph2_custom_hooks *hooks) { struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; int ret; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -1417,8 +1416,6 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, ret = devm_snd_soc_register_card(dev, card); err: - devm_kfree(dev, li); - if (ret < 0) dev_err_probe(dev, ret, "parse error\n"); -- cgit v1.2.3 From 7d996c8a5fea700e816379e57f4983e2611519a0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:02 +0200 Subject: ASoC: simple-card: Use cleanup.h instead of devm_kfree() Allocate the memory with scoped/cleanup.h, instead of devm interface, to make the code more obvious that memory is not used outside this scope. Signed-off-by: Krzysztof Kozlowski Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-8-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index edbb6322e9be..d2588f1ea54e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -5,6 +5,7 @@ // Copyright (C) 2012 Renesas Solutions Corp. // Kuninori Morimoto +#include #include #include #include @@ -727,7 +728,6 @@ static int simple_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - struct link_info *li; int ret; /* Allocate the private data and the DAI link array */ @@ -741,7 +741,7 @@ static int simple_probe(struct platform_device *pdev) card->probe = simple_soc_probe; card->driver_name = "simple-card"; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -818,7 +818,6 @@ static int simple_probe(struct platform_device *pdev) if (ret < 0) goto err; - devm_kfree(dev, li); return 0; err: simple_util_clean_reference(card); -- cgit v1.2.3 From 1a7b846818210cbdf8994bfee1340c09342a5b3b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:03 +0200 Subject: ASoC: ops: Simplify with cleanup.h Allocate the memory with scoped/cleanup.h to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-9-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index b27e89ff6a16..19928f098d8d 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -11,6 +11,7 @@ // with code, comments and ideas from :- // Richard Purdie +#include #include #include #include @@ -727,14 +728,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes *params = (void *)kcontrol->private_value; int ret, len; unsigned int val, mask; - void *data; if (!component->regmap || !params->num_regs) return -EINVAL; len = params->num_regs * component->val_bytes; - data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + void *data __free(kfree) = kmemdup(ucontrol->value.bytes.data, len, + GFP_KERNEL | GFP_DMA); if (!data) return -ENOMEM; @@ -746,7 +747,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, if (params->mask) { ret = regmap_read(component->regmap, params->base, &val); if (ret != 0) - goto out; + return ret; val &= params->mask; @@ -760,14 +761,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) - goto out; + return ret; ((u16 *)data)[0] &= mask; ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) - goto out; + return ret; ((u16 *)data)[0] |= val; break; @@ -776,30 +777,23 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) - goto out; + return ret; ((u32 *)data)[0] &= mask; ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) - goto out; + return ret; ((u32 *)data)[0] |= val; break; default: - ret = -EINVAL; - goto out; + return -EINVAL; } } - ret = regmap_raw_write(component->regmap, params->base, - data, len); - -out: - kfree(data); - - return ret; + return regmap_raw_write(component->regmap, params->base, data, len); } EXPORT_SYMBOL_GPL(snd_soc_bytes_put); -- cgit v1.2.3 From 5b3cc85673bcc7bb961a3a6fa229cbc4fe0441ac Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:04 +0200 Subject: ASoC: dapm: Simplify dapm_cnew_widget() with cleanup.h Allocate the memory with scoped/cleanup.h in dapm_cnew_widget() to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-10-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 32cc90d09bc2..4b322bb634b6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -323,9 +324,9 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( const struct snd_soc_dapm_widget *_widget, const char *prefix) { - struct snd_soc_dapm_widget *w; - - w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); + struct snd_soc_dapm_widget *w __free(kfree) = kmemdup(_widget, + sizeof(*_widget), + GFP_KERNEL); if (!w) return NULL; @@ -333,20 +334,18 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, _widget->name); else w->name = kstrdup_const(_widget->name, GFP_KERNEL); - if (!w->name) { - kfree(w); + if (!w->name) return NULL; - } if (_widget->sname) { w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); if (!w->sname) { kfree_const(w->name); - kfree(w); return NULL; } } - return w; + + return_ptr(w); } struct dapm_kcontrol_data { -- cgit v1.2.3 From 522133d4401010d936b0588a5a975c2c965cb88e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jul 2024 14:11:05 +0200 Subject: ASoC: dapm: Simplify snd_soc_dai_link_event_pre_pmu() with cleanup.h Allocate the memory with scoped/cleanup.h in snd_soc_dai_link_event_pre_pmu() to reduce error handling (less error paths) and make the code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240703-asoc-cleanup-h-v1-11-71219dfd0aef@linaro.org Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4b322bb634b6..37dccd9c1ba0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3882,11 +3882,10 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_path *path; struct snd_soc_dai *source, *sink; struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_pcm_hw_params *params = NULL; const struct snd_soc_pcm_stream *config = NULL; struct snd_pcm_runtime *runtime = NULL; unsigned int fmt; - int ret = 0; + int ret; /* * NOTE @@ -3897,15 +3896,14 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, * stuff that increases stack usage. * So, we use kzalloc()/kfree() for params in this function. */ - params = kzalloc(sizeof(*params), GFP_KERNEL); + struct snd_pcm_hw_params *params __free(kfree) = kzalloc(sizeof(*params), + GFP_KERNEL); if (!params) return -ENOMEM; runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (!runtime) { - ret = -ENOMEM; - goto out; - } + if (!runtime) + return -ENOMEM; substream->runtime = runtime; @@ -3915,7 +3913,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_startup(source, substream); if (ret < 0) - goto out; + return ret; snd_soc_dai_activate(source, substream->stream); } @@ -3926,7 +3924,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_startup(sink, substream); if (ret < 0) - goto out; + return ret; snd_soc_dai_activate(sink, substream->stream); } @@ -3941,16 +3939,14 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, config = rtd->dai_link->c2c_params + rtd->c2c_params_select; if (!config) { dev_err(w->dapm->dev, "ASoC: link config missing\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } /* Be a little careful as we don't want to overflow the mask array */ if (!config->formats) { dev_warn(w->dapm->dev, "ASoC: Invalid format was specified\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } fmt = ffs(config->formats) - 1; @@ -3971,7 +3967,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_hw_params(source, substream, params); if (ret < 0) - goto out; + return ret; dapm_update_dai_unlocked(substream, params, source); } @@ -3982,7 +3978,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_hw_params(sink, substream, params); if (ret < 0) - goto out; + return ret; dapm_update_dai_unlocked(substream, params, sink); } @@ -3992,11 +3988,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, runtime->channels = params_channels(params); runtime->rate = params_rate(params); -out: - /* see above NOTE */ - kfree(params); - - return ret; + return 0; } static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, -- cgit v1.2.3 From 1cc509edbe23b61e8c245611bd15d88edb635a38 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Sun, 7 Jul 2024 16:30:07 +0800 Subject: ASoc: TAS2781: replace beXX_to_cpup with get_unaligned_beXX for potentially broken alignment Use get_unaligned_be16 instead of be16_to_cpup and get_unaligned_be32 instead of be32_to_cpup for potentially broken alignment. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240707083011.98-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-fmwlib.c | 71 +++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 838d29fead96..63626b982d04 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -21,7 +21,7 @@ #include #include #include - +#include #define ERROR_PRAM_CRCCHK 0x0000000 #define ERROR_YRAM_CRCCHK 0x0000001 @@ -187,8 +187,7 @@ static struct tasdevice_config_info *tasdevice_add_config( /* convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - cfg_info->nblocks = - be32_to_cpup((__be32 *)&config_data[config_offset]); + cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; /* Several kinds of dsp/algorithm firmwares can run on tas2781, @@ -232,14 +231,14 @@ static struct tasdevice_config_info *tasdevice_add_config( } bk_da[i]->yram_checksum = - be16_to_cpup((__be16 *)&config_data[config_offset]); + get_unaligned_be16(&config_data[config_offset]); config_offset += 2; bk_da[i]->block_size = - be32_to_cpup((__be32 *)&config_data[config_offset]); + get_unaligned_be32(&config_data[config_offset]); config_offset += 4; bk_da[i]->n_subblks = - be32_to_cpup((__be32 *)&config_data[config_offset]); + get_unaligned_be32(&config_data[config_offset]); config_offset += 4; @@ -289,7 +288,7 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) } buf = (unsigned char *)fmw->data; - fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->img_sz = get_unaligned_be32(&buf[offset]); offset += 4; if (fw_hdr->img_sz != fmw->size) { dev_err(tas_priv->dev, @@ -300,9 +299,9 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) goto out; } - fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->checksum = get_unaligned_be32(&buf[offset]); offset += 4; - fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]); if (fw_hdr->binary_version_num < 0x103) { dev_err(tas_priv->dev, "File version 0x%04x is too low", fw_hdr->binary_version_num); @@ -311,7 +310,7 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) goto out; } offset += 4; - fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]); offset += 8; fw_hdr->plat_type = buf[offset]; offset += 1; @@ -339,11 +338,11 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) fw_hdr->devs[i] = buf[offset]; - fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->nconfig = get_unaligned_be32(&buf[offset]); offset += 4; for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { - fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]); offset += 4; total_config_sz += fw_hdr->config_size[i]; } @@ -423,7 +422,7 @@ static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, /* convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - block->type = be32_to_cpup((__be32 *)&data[offset]); + block->type = get_unaligned_be32(&data[offset]); offset += 4; block->is_pchksum_present = data[offset]; @@ -438,10 +437,10 @@ static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, block->ychksum = data[offset]; offset++; - block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + block->blk_size = get_unaligned_be32(&data[offset]); offset += 4; - block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + block->nr_subblocks = get_unaligned_be32(&data[offset]); offset += 4; /* fixed m68k compiling issue: @@ -482,7 +481,7 @@ static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, offset = -EINVAL; goto out; } - img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + img_data->nr_blk = get_unaligned_be32(&data[offset]); offset += 4; img_data->dev_blks = kcalloc(img_data->nr_blk, @@ -578,14 +577,14 @@ static int fw_parse_variable_header_kernel( offset = -EINVAL; goto out; } - fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + fw_hdr->device_family = get_unaligned_be16(&buf[offset]); if (fw_hdr->device_family != 0) { dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); offset = -EINVAL; goto out; } offset += 2; - fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + fw_hdr->device = get_unaligned_be16(&buf[offset]); if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || fw_hdr->device == 6) { dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); @@ -603,7 +602,7 @@ static int fw_parse_variable_header_kernel( goto out; } - tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]); offset += 4; if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > @@ -622,14 +621,14 @@ static int fw_parse_variable_header_kernel( for (i = 0; i < tas_fmw->nr_programs; i++) { program = &(tas_fmw->programs[i]); - program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + program->prog_size = get_unaligned_be32(&buf[offset]); offset += 4; } /* Skip the unused prog_size */ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); - tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]); offset += 4; /* The max number of config in firmware greater than 4 pieces of @@ -661,7 +660,7 @@ static int fw_parse_variable_header_kernel( for (i = 0; i < tas_fmw->nr_programs; i++) { config = &(tas_fmw->configs[i]); - config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + config->cfg_size = get_unaligned_be32(&buf[offset]); offset += 4; } @@ -699,7 +698,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, switch (subblk_typ) { case TASDEVICE_CMD_SING_W: { int i; - unsigned short len = be16_to_cpup((__be16 *)&data[2]); + unsigned short len = get_unaligned_be16(&data[2]); subblk_offset += 2; if (subblk_offset + 4 * len > sublocksize) { @@ -725,7 +724,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, } break; case TASDEVICE_CMD_BURST: { - unsigned short len = be16_to_cpup((__be16 *)&data[2]); + unsigned short len = get_unaligned_be16(&data[2]); subblk_offset += 2; if (subblk_offset + 4 + len > sublocksize) { @@ -766,7 +765,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, is_err = true; break; } - sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + sleep_time = get_unaligned_be16(&data[2]) * 1000; usleep_range(sleep_time, sleep_time + 50); subblk_offset += 2; } @@ -910,7 +909,7 @@ static int fw_parse_variable_hdr(struct tasdevice_priv offset += len; - fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->device_family = get_unaligned_be32(&buf[offset]); if (fw_hdr->device_family != 0) { dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); offset = -EINVAL; @@ -918,7 +917,7 @@ static int fw_parse_variable_hdr(struct tasdevice_priv } offset += 4; - fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->device = get_unaligned_be32(&buf[offset]); if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || fw_hdr->device == 6) { dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); @@ -963,7 +962,7 @@ static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, offset = -EINVAL; goto out; } - block->type = be32_to_cpup((__be32 *)&data[offset]); + block->type = get_unaligned_be32(&data[offset]); offset += 4; if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { @@ -988,7 +987,7 @@ static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, block->is_ychksum_present = 0; } - block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + block->nr_cmds = get_unaligned_be32(&data[offset]); offset += 4; n = block->nr_cmds * 4; @@ -1039,7 +1038,7 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw, goto out; } offset += n; - img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + img_data->nr_blk = get_unaligned_be16(&data[offset]); offset += 2; img_data->dev_blks = kcalloc(img_data->nr_blk, @@ -1076,7 +1075,7 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv, offset = -EINVAL; goto out; } - tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]); offset += 2; if (tas_fmw->nr_programs == 0) { @@ -1143,7 +1142,7 @@ static int fw_parse_configuration_data( offset = -EINVAL; goto out; } - tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]); offset += 2; if (tas_fmw->nr_configurations == 0) { @@ -1775,7 +1774,7 @@ static int fw_parse_header(struct tasdevice_priv *tas_priv, /* Convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]); offset += 4; if (fw_fixed_hdr->fwsize != fmw->size) { dev_err(tas_priv->dev, "File size not match, %lu %u", @@ -1784,9 +1783,9 @@ static int fw_parse_header(struct tasdevice_priv *tas_priv, goto out; } offset += 4; - fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]); offset += 8; - fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]); offset += 72; out: @@ -1828,7 +1827,7 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, offset = -EINVAL; goto out; } - tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + tas_fmw->nr_calibrations = get_unaligned_be16(&data[offset]); offset += 2; if (tas_fmw->nr_calibrations != 1) { -- cgit v1.2.3 From a613b63db233b6e7d46ec72f009c1cbb7db8be66 Mon Sep 17 00:00:00 2001 From: Elinor Montmasson Date: Thu, 27 Jun 2024 10:30:58 +0200 Subject: ASoC: fsl-asoc-card: add support for dai links with multiple codecs Add support for dai links using multiple codecs for multi-codec use cases. Co-developed-by: Philip-Dylan Gleonec Signed-off-by: Philip-Dylan Gleonec Signed-off-by: Elinor Montmasson Link: https://patch.msgid.link/20240627083104.123357-2-elinor.montmasson@savoirfairelinux.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index eb67689dcd6e..e8003fbc8092 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -542,6 +542,7 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) static int fsl_asoc_card_probe(struct platform_device *pdev) { struct device_node *cpu_np, *codec_np, *asrc_np; + struct snd_soc_dai_link_component *codec_comp; struct device_node *np = pdev->dev.of_node; struct platform_device *asrc_pdev = NULL; struct device_node *bitclkprovider = NULL; @@ -552,6 +553,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) const char *codec_dai_name; const char *codec_dev_name; u32 asrc_fmt = 0; + int codec_idx; u32 width; int ret; @@ -816,10 +818,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpus->of_node = cpu_np; - priv->dai_link[0].codecs->dai_name = codec_dai_name; + priv->dai_link[0].codecs[0].dai_name = codec_dai_name; if (!fsl_asoc_card_is_ac97(priv)) - priv->dai_link[0].codecs->of_node = codec_np; + priv->dai_link[0].codecs[0].of_node = codec_np; else { u32 idx; @@ -830,11 +832,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } - priv->dai_link[0].codecs->name = + priv->dai_link[0].codecs[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); - if (!priv->dai_link[0].codecs->name) { + if (!priv->dai_link[0].codecs[0].name) { ret = -ENOMEM; goto asrc_fail; } @@ -848,10 +850,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* DPCM DAI Links only if ASRC exists */ priv->dai_link[1].cpus->of_node = asrc_np; priv->dai_link[1].platforms->of_node = asrc_np; - priv->dai_link[2].codecs->dai_name = codec_dai_name; - priv->dai_link[2].codecs->of_node = codec_np; - priv->dai_link[2].codecs->name = - priv->dai_link[0].codecs->name; + for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) { + codec_comp->dai_name = priv->dai_link[0].codecs[codec_idx].dai_name; + codec_comp->of_node = priv->dai_link[0].codecs[codec_idx].of_node; + codec_comp->name = priv->dai_link[0].codecs[codec_idx].name; + } priv->dai_link[2].cpus->of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; -- cgit v1.2.3 From c68fa0d9b0f8cc7c3ae7d29c02adbc97622a73f5 Mon Sep 17 00:00:00 2001 From: Elinor Montmasson Date: Thu, 27 Jun 2024 10:30:59 +0200 Subject: ASoC: fsl-asoc-card: add second dai link component for codecs Add a second dai link component for codecs that will be used for use cases with 2 codecs. It is needed for future integration of the SPDIF support, which will use spdif_receiver and spdif_transmitter drivers. To prevent deferring in use cases using only one codec, also set by default the number of codecs to 1 for the relevant dai links. Co-developed-by: Philip-Dylan Gleonec Signed-off-by: Philip-Dylan Gleonec Signed-off-by: Elinor Montmasson Link: https://patch.msgid.link/20240627083104.123357-3-elinor.montmasson@savoirfairelinux.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index e8003fbc8092..805e2030bde4 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -296,7 +296,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, SND_SOC_DAILINK_DEFS(hifi, DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(hifi_fe, @@ -306,7 +306,7 @@ SND_SOC_DAILINK_DEFS(hifi_fe, SND_SOC_DAILINK_DEFS(hifi_be, DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY())); static const struct snd_soc_dai_link fsl_asoc_card_dai[] = { /* Default ASoC DAI Link*/ @@ -622,6 +622,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + priv->dai_link[0].num_codecs = 1; + priv->dai_link[2].num_codecs = 1; priv->card.dapm_routes = audio_map; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); -- cgit v1.2.3 From fcc6ace84f1f6ce2211af25c3c8fb30a0fb2bb2c Mon Sep 17 00:00:00 2001 From: Elinor Montmasson Date: Thu, 27 Jun 2024 10:31:00 +0200 Subject: ASoC: fsl-asoc-card: add compatibility to use 2 codecs in dai-links Adapt the driver to work with configurations using two codecs or more. Modify fsl_asoc_card_probe() to handle use cases where 2 codecs are given in the device tree. This will be needed to add support for the SPDIF. Use cases using one codec will ignore any given codecs other than the first. Co-developed-by: Philip-Dylan Gleonec Signed-off-by: Philip-Dylan Gleonec Signed-off-by: Elinor Montmasson Link: https://patch.msgid.link/20240627083104.123357-4-elinor.montmasson@savoirfairelinux.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 279 ++++++++++++++++++++++++------------------ 1 file changed, 161 insertions(+), 118 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 805e2030bde4..87329731e02d 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -99,7 +99,7 @@ struct fsl_asoc_card_priv { struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; struct platform_device *pdev; - struct codec_priv codec_priv; + struct codec_priv codec_priv[2]; struct cpu_priv cpu_priv; struct snd_soc_card card; u8 streams; @@ -172,10 +172,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - struct codec_priv *codec_priv = &priv->codec_priv; + struct codec_priv *codec_priv; + struct snd_soc_dai *codec_dai; struct cpu_priv *cpu_priv = &priv->cpu_priv; struct device *dev = rtd->card->dev; unsigned int pll_out; + int codec_idx; int ret; priv->sample_rate = params_rate(params); @@ -208,28 +210,32 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, } /* Specific configuration for PLL */ - if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { - if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) - pll_out = priv->sample_rate * 384; - else - pll_out = priv->sample_rate * 256; + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->pll_id, - codec_priv->mclk_id, - codec_priv->mclk_freq, pll_out); - if (ret) { - dev_err(dev, "failed to start FLL: %d\n", ret); - goto fail; - } + if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { + if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) + pll_out = priv->sample_rate * 384; + else + pll_out = priv->sample_rate * 256; - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->fll_id, - pll_out, SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_pll(codec_dai, + codec_priv->pll_id, + codec_priv->mclk_id, + codec_priv->mclk_freq, pll_out); + if (ret) { + dev_err(dev, "failed to start FLL: %d\n", ret); + goto fail; + } - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set SYSCLK: %d\n", ret); - goto fail; + ret = snd_soc_dai_set_sysclk(codec_dai, + codec_priv->fll_id, + pll_out, SND_SOC_CLOCK_IN); + + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set SYSCLK: %d\n", ret); + goto fail; + } } } @@ -244,28 +250,34 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct codec_priv *codec_priv = &priv->codec_priv; + struct codec_priv *codec_priv; + struct snd_soc_dai *codec_dai; struct device *dev = rtd->card->dev; + int codec_idx; int ret; priv->streams &= ~BIT(substream->stream); - if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { - /* Force freq to be free_freq to avoid error message in codec */ - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->mclk_id, - codec_priv->free_freq, - SND_SOC_CLOCK_IN); - if (ret) { - dev_err(dev, "failed to switch away from FLL: %d\n", ret); - return ret; - } + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->pll_id, 0, 0, 0); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to stop FLL: %d\n", ret); - return ret; + if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { + /* Force freq to be free_freq to avoid error message in codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, + codec_priv->mclk_id, + codec_priv->free_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "failed to switch away from FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, + codec_priv->pll_id, 0, 0, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to stop FLL: %d\n", ret); + return ret; + } } } @@ -504,9 +516,10 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_runtime *rtd = list_first_entry( &card->rtd_list, struct snd_soc_pcm_runtime, list); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct codec_priv *codec_priv = &priv->codec_priv; + struct snd_soc_dai *codec_dai; + struct codec_priv *codec_priv; struct device *dev = card->dev; + int codec_idx; int ret; if (fsl_asoc_card_is_ac97(priv)) { @@ -526,32 +539,37 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) return 0; } - ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, - codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set sysclk in %s\n", __func__); - return ret; - } + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - if (!IS_ERR_OR_NULL(codec_priv->mclk)) - clk_prepare_enable(codec_priv->mclk); + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set sysclk in %s\n", __func__); + return ret; + } + + if (!IS_ERR_OR_NULL(codec_priv->mclk)) + clk_prepare_enable(codec_priv->mclk); + } return 0; } static int fsl_asoc_card_probe(struct platform_device *pdev) { - struct device_node *cpu_np, *codec_np, *asrc_np; + struct device_node *cpu_np, *asrc_np; struct snd_soc_dai_link_component *codec_comp; + struct device_node *codec_np[2]; struct device_node *np = pdev->dev.of_node; struct platform_device *asrc_pdev = NULL; struct device_node *bitclkprovider = NULL; struct device_node *frameprovider = NULL; struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; - struct device *codec_dev = NULL; - const char *codec_dai_name; - const char *codec_dev_name; + struct device *codec_dev[2] = { NULL, NULL }; + const char *codec_dai_name[2]; + const char *codec_dev_name[2]; u32 asrc_fmt = 0; int codec_idx; u32 width; @@ -580,21 +598,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (codec_np) { - struct platform_device *codec_pdev; - struct i2c_client *codec_i2c; + codec_np[0] = of_parse_phandle(np, "audio-codec", 0); + codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - codec_i2c = of_find_i2c_device_by_node(codec_np); - if (codec_i2c) { - codec_dev = &codec_i2c->dev; - codec_dev_name = codec_i2c->name; - } - if (!codec_dev) { - codec_pdev = of_find_device_by_node(codec_np); - if (codec_pdev) { - codec_dev = &codec_pdev->dev; - codec_dev_name = codec_pdev->name; + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + if (codec_np[codec_idx]) { + struct platform_device *codec_pdev; + struct i2c_client *codec_i2c; + + codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); + if (codec_i2c) { + codec_dev[codec_idx] = &codec_i2c->dev; + codec_dev_name[codec_idx] = codec_i2c->name; + } + if (!codec_dev[codec_idx]) { + codec_pdev = of_find_device_by_node(codec_np[codec_idx]); + if (codec_pdev) { + codec_dev[codec_idx] = &codec_pdev->dev; + codec_dev_name[codec_idx] = codec_pdev->name; + } } } } @@ -604,12 +626,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - if (codec_dev) { - struct clk *codec_clk = clk_get(codec_dev, NULL); + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + if (codec_dev[codec_idx]) { + struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); - if (!IS_ERR(codec_clk)) { - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); + if (!IS_ERR(codec_clk)) { + priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } } } @@ -629,31 +653,33 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); priv->card.driver_name = DRIVER_NAME; - priv->codec_priv.fll_id = -1; - priv->codec_priv.pll_id = -1; + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + priv->codec_priv[codec_idx].fll_id = -1; + priv->codec_priv[codec_idx].pll_id = -1; + } /* Diversify the card configurations */ if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { - codec_dai_name = "cs42888"; - priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; - priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; + codec_dai_name[0] = "cs42888"; + priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; + priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { - codec_dai_name = "cs4271-hifi"; - priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; + codec_dai_name[0] = "cs4271-hifi"; + priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { - codec_dai_name = "sgtl5000"; - priv->codec_priv.mclk_id = SGTL5000_SYSCLK; + codec_dai_name[0] = "sgtl5000"; + priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { - codec_dai_name = "tlv320aic32x4-hifi"; + codec_dai_name[0] = "tlv320aic32x4-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { - codec_dai_name = "tlv320dac31xx-hifi"; + codec_dai_name[0] = "tlv320dac31xx-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; @@ -662,23 +688,23 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { - codec_dai_name = "wm8962"; - priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; - priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; - priv->codec_priv.pll_id = WM8962_FLL; + codec_dai_name[0] = "wm8962"; + priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; + priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; + priv->codec_priv[0].pll_id = WM8962_FLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { - codec_dai_name = "wm8960-hifi"; - priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; - priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; + codec_dai_name[0] = "wm8960-hifi"; + priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; + priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { - codec_dai_name = "ac97-hifi"; + codec_dai_name[0] = "ac97-hifi"; priv->dai_fmt = SND_SOC_DAIFMT_AC97; priv->card.dapm_routes = audio_map_ac97; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { - codec_dai_name = "fsl-mqs-dai"; + codec_dai_name[0] = "fsl-mqs-dai"; priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_NB_NF; @@ -687,7 +713,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { - codec_dai_name = "wm8524-hifi"; + codec_dai_name[0] = "wm8524-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; @@ -695,32 +721,32 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { - codec_dai_name = "si476x-codec"; + codec_dai_name[0] = "si476x-codec"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->card.dapm_routes = audio_map_rx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { - codec_dai_name = "wm8994-aif1"; + codec_dai_name[0] = "wm8994-aif1"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1; - priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1; - priv->codec_priv.pll_id = WM8994_FLL1; - priv->codec_priv.free_freq = priv->codec_priv.mclk_freq; + priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; + priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; + priv->codec_priv[0].pll_id = WM8994_FLL1; + priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; priv->card.dapm_routes = NULL; priv->card.num_dapm_routes = 0; } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { - codec_dai_name = "nau8822-hifi"; - priv->codec_priv.mclk_id = NAU8822_CLK_MCLK; - priv->codec_priv.fll_id = NAU8822_CLK_PLL; - priv->codec_priv.pll_id = NAU8822_CLK_PLL; + codec_dai_name[0] = "nau8822-hifi"; + priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; + priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; + priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - if (codec_dev) - priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL); + if (codec_dev[0]) + priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { - codec_dai_name = "wm8904-hifi"; - priv->codec_priv.mclk_id = WM8904_FLL_MCLK; - priv->codec_priv.fll_id = WM8904_CLK_FLL; - priv->codec_priv.pll_id = WM8904_FLL_MCLK; + codec_dai_name[0] = "wm8904-hifi"; + priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; + priv->codec_priv[0].fll_id = WM8904_CLK_FLL; + priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); @@ -732,18 +758,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) * Allow setting mclk-id from the device-tree node. Otherwise, the * default value for each card configuration is used. */ - of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id); + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + of_property_read_u32_index(np, "mclk-id", codec_idx, + &priv->codec_priv[codec_idx].mclk_id); + } /* Format info from DT is optional. */ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider); if (bitclkprovider || frameprovider) { unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL); + bool codec_bitclkprovider = false; + bool codec_frameprovider = false; + + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + if (bitclkprovider && codec_np[codec_idx] == bitclkprovider) + codec_bitclkprovider = true; + if (frameprovider && codec_np[codec_idx] == frameprovider) + codec_frameprovider = true; + } - if (codec_np == bitclkprovider) - daifmt |= (codec_np == frameprovider) ? + if (codec_bitclkprovider) + daifmt |= (codec_frameprovider) ? SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC; else - daifmt |= (codec_np == frameprovider) ? + daifmt |= (codec_frameprovider) ? SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC; /* Override dai_fmt with value from DT */ @@ -759,7 +797,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) of_node_put(bitclkprovider); of_node_put(frameprovider); - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0]) { dev_dbg(&pdev->dev, "failed to find codec device\n"); ret = -EPROBE_DEFER; goto asrc_fail; @@ -798,7 +836,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = snd_soc_of_parse_card_name(&priv->card, "model"); if (ret) { snprintf(priv->name, sizeof(priv->name), "%s-audio", - fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name); + fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); priv->card.name = priv->name; } priv->card.dai_link = priv->dai_link; @@ -820,11 +858,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpus->of_node = cpu_np; - priv->dai_link[0].codecs[0].dai_name = codec_dai_name; + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + codec_comp->dai_name = codec_dai_name[codec_idx]; + } - if (!fsl_asoc_card_is_ac97(priv)) - priv->dai_link[0].codecs[0].of_node = codec_np; - else { + if (!fsl_asoc_card_is_ac97(priv)) { + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + codec_comp->of_node = codec_np[codec_idx]; + } + } else { u32 idx; ret = of_property_read_u32(cpu_np, "cell-index", &idx); @@ -926,7 +968,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); - of_node_put(codec_np); + of_node_put(codec_np[0]); + of_node_put(codec_np[1]); put_device(&cpu_pdev->dev); fail: of_node_put(cpu_np); -- cgit v1.2.3 From 6d174cc4f22461ad3fe383570527e86bf1948a2e Mon Sep 17 00:00:00 2001 From: Elinor Montmasson Date: Thu, 27 Jun 2024 10:31:01 +0200 Subject: ASoC: fsl-asoc-card: merge spdif support from imx-spdif.c The imx-spdif machine driver creates audio card to directly use an S/PDIF device. However, it doesn't support interacting with an ASRC. fsl-asoc-card already has the support to create audio card which can use the ASRC. Merge the S/PDIF support from imx-spdif into driver fsl-asoc-card to extend the support of S/PDIF audio card with the use of ASRC devices. fsl-asoc-card uses slightly different DT properties than imx-spdif: * the "spdif-controller" property from imx-spdif is named "audio-cpu" in fsl-asoc-card. * fsl-asoc-card uses codecs explicitly declared in DT with "audio-codec". With an SPDIF, codec drivers spdif_transmitter and spdif_receiver should be used. Driver imx-spdif used instead the dummy codec and a pair of boolean properties, "spdif-in" and "spdif-out". To keep backward compatibility, support for "spdif-controller", "spdif-in" and "spdif-out" is also added to fsl-asoc-card. However, it is recommended to use the new properties if possible. It is better to declare transmitter and/or receiver in DT than using the dummy codec. DTs using compatible "fsl,imx-audio-spdif" are still compatible, and fsl-asoc-card will behave the same as imx-spdif for these DTs. Signed-off-by: Elinor Montmasson Link: https://patch.msgid.link/20240627083104.123357-5-elinor.montmasson@savoirfairelinux.com Signed-off-by: Mark Brown --- arch/arm/configs/imx_v6_v7_defconfig | 1 - arch/arm64/configs/defconfig | 1 - sound/soc/fsl/Kconfig | 10 +--- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/fsl-asoc-card.c | 85 ++++++++++++++++++++++++++++- sound/soc/fsl/imx-spdif.c | 103 ----------------------------------- 6 files changed, 84 insertions(+), 118 deletions(-) delete mode 100644 sound/soc/fsl/imx-spdif.c diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index cf2480dce285..ac5ae621b2af 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -311,7 +311,6 @@ CONFIG_SND_IMX_SOC=y CONFIG_SND_SOC_EUKREA_TLV320=y CONFIG_SND_SOC_IMX_ES8328=y CONFIG_SND_SOC_IMX_SGTL5000=y -CONFIG_SND_SOC_IMX_SPDIF=y CONFIG_SND_SOC_FSL_ASOC_CARD=y CONFIG_SND_SOC_AC97_CODEC=y CONFIG_SND_SOC_CS42XX8_I2C=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 57a9abe78ee4..a6c9688fee0e 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -940,7 +940,6 @@ CONFIG_SND_SOC_FSL_MICFIL=m CONFIG_SND_SOC_FSL_EASRC=m CONFIG_SND_IMX_SOC=m CONFIG_SND_SOC_IMX_SGTL5000=m -CONFIG_SND_SOC_IMX_SPDIF=m CONFIG_SND_SOC_FSL_ASOC_CARD=m CONFIG_SND_SOC_IMX_AUDMIX=m CONFIG_SND_SOC_MT8183=m diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8b7088623b6a..e283751abfef 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -303,15 +303,6 @@ config SND_SOC_IMX_SGTL5000 SND_SOC_FSL_ASOC_CARD and SND_SOC_SGTL5000 to use the newer driver. -config SND_SOC_IMX_SPDIF - tristate "SoC Audio support for i.MX boards with S/PDIF" - select SND_SOC_IMX_PCM_DMA - select SND_SOC_FSL_SPDIF - help - SoC Audio support for i.MX boards with S/PDIF - Say Y if you want to add support for SoC audio on an i.MX board with - a S/DPDIF. - config SND_SOC_FSL_ASOC_CARD tristate "Generic ASoC Sound Card with ASRC support" depends on OF && I2C @@ -323,6 +314,7 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_ESAI select SND_SOC_FSL_SAI select SND_SOC_FSL_SSI + select SND_SOC_FSL_SPDIF select SND_SOC_TLV320AIC31XX select SND_SOC_WM8994 select MFD_WM8994 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 2a61e2f96438..ad97244b5cc3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -67,7 +67,6 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o snd-soc-eukrea-tlv320-y := eukrea-tlv320.o snd-soc-imx-es8328-y := imx-es8328.o snd-soc-imx-sgtl5000-y := imx-sgtl5000.o -snd-soc-imx-spdif-y := imx-spdif.o snd-soc-imx-audmix-y := imx-audmix.o snd-soc-imx-hdmi-y := imx-hdmi.o snd-soc-imx-rpmsg-y := imx-rpmsg.o @@ -76,7 +75,6 @@ snd-soc-imx-card-y := imx-card.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o -obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 87329731e02d..82df887b3af5 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -477,6 +477,75 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return 0; } +static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], + struct device_node *cpu_np, + const char *codec_dai_name[], + struct fsl_asoc_card_priv *priv) +{ + struct device *dev = &priv->pdev->dev; + struct device_node *np = dev->of_node; + + if (!of_node_name_eq(cpu_np, "spdif")) { + dev_err(dev, "CPU phandle invalid, should be an SPDIF device\n"); + return -EINVAL; + } + + priv->dai_link[0].playback_only = true; + priv->dai_link[0].capture_only = true; + + for (int i = 0; i < 2; i++) { + if (!codec_np[i]) + break; + + if (of_device_is_compatible(codec_np[i], "linux,spdif-dit")) { + priv->dai_link[0].capture_only = false; + codec_dai_name[i] = "dit-hifi"; + } else if (of_device_is_compatible(codec_np[i], "linux,spdif-dir")) { + priv->dai_link[0].playback_only = false; + codec_dai_name[i] = "dir-hifi"; + } + } + + // Old SPDIF DT binding + if (!codec_np[0]) { + codec_dai_name[0] = snd_soc_dummy_dlc.dai_name; + if (of_property_read_bool(np, "spdif-out")) + priv->dai_link[0].capture_only = false; + if (of_property_read_bool(np, "spdif-in")) + priv->dai_link[0].playback_only = false; + } + + if (priv->dai_link[0].playback_only && priv->dai_link[0].capture_only) { + dev_err(dev, "no enabled S/PDIF DAI link\n"); + return -EINVAL; + } + + if (priv->dai_link[0].playback_only) { + priv->dai_link[1].dpcm_capture = false; + priv->dai_link[2].dpcm_capture = false; + priv->card.dapm_routes = audio_map_tx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); + } else if (priv->dai_link[0].capture_only) { + priv->dai_link[1].dpcm_playback = false; + priv->dai_link[2].dpcm_playback = false; + priv->card.dapm_routes = audio_map_rx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); + } + + // No DAPM routes with old bindings and dummy codec + if (!codec_np[0]) { + priv->card.dapm_routes = NULL; + priv->card.num_dapm_routes = 0; + } + + if (codec_np[0] && codec_np[1]) { + priv->dai_link[0].num_codecs = 2; + priv->dai_link[2].num_codecs = 2; + } + + return 0; +} + static int hp_jack_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -582,9 +651,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->pdev = pdev; cpu_np = of_parse_phandle(np, "audio-cpu", 0); - /* Give a chance to old DT binding */ + /* Give a chance to old DT bindings */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); + if (!cpu_np) + cpu_np = of_parse_phandle(np, "spdif-controller", 0); if (!cpu_np) { dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; @@ -748,6 +819,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv[0].fll_id = WM8904_CLK_FLL; priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; + } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { + ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); + if (ret) + goto asrc_fail; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; @@ -797,7 +872,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) of_node_put(bitclkprovider); of_node_put(frameprovider); - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0]) { + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] + && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { dev_dbg(&pdev->dev, "failed to find codec device\n"); ret = -EPROBE_DEFER; goto asrc_fail; @@ -862,6 +938,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) codec_comp->dai_name = codec_dai_name[codec_idx]; } + // Old SPDIF DT binding support + if (codec_dai_name[0] == snd_soc_dummy_dlc.dai_name) + priv->dai_link[0].codecs[0].name = snd_soc_dummy_dlc.name; + if (!fsl_asoc_card_is_ac97(priv)) { for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { codec_comp->of_node = codec_np[codec_idx]; @@ -992,6 +1072,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8958", }, { .compatible = "fsl,imx-audio-nau8822", }, { .compatible = "fsl,imx-audio-wm8904", }, + { .compatible = "fsl,imx-audio-spdif", }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c deleted file mode 100644 index 1e57939a7e29..000000000000 --- a/sound/soc/fsl/imx-spdif.c +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// Copyright (C) 2013 Freescale Semiconductor, Inc. - -#include -#include -#include - -struct imx_spdif_data { - struct snd_soc_dai_link dai; - struct snd_soc_card card; -}; - -static int imx_spdif_audio_probe(struct platform_device *pdev) -{ - struct device_node *spdif_np, *np = pdev->dev.of_node; - struct imx_spdif_data *data; - struct snd_soc_dai_link_component *comp; - int ret = 0; - - spdif_np = of_parse_phandle(np, "spdif-controller", 0); - if (!spdif_np) { - dev_err(&pdev->dev, "failed to find spdif-controller\n"); - ret = -EINVAL; - goto end; - } - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL); - if (!data || !comp) { - ret = -ENOMEM; - goto end; - } - - /* - * CPU == Platform - * platform is using soc-generic-dmaengine-pcm - */ - data->dai.cpus = - data->dai.platforms = comp; - data->dai.codecs = &snd_soc_dummy_dlc; - - data->dai.num_cpus = 1; - data->dai.num_codecs = 1; - data->dai.num_platforms = 1; - - data->dai.name = "S/PDIF PCM"; - data->dai.stream_name = "S/PDIF PCM"; - data->dai.cpus->of_node = spdif_np; - data->dai.playback_only = true; - data->dai.capture_only = true; - - if (of_property_read_bool(np, "spdif-out")) - data->dai.capture_only = false; - - if (of_property_read_bool(np, "spdif-in")) - data->dai.playback_only = false; - - if (data->dai.playback_only && data->dai.capture_only) { - dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); - goto end; - } - - data->card.dev = &pdev->dev; - data->card.dai_link = &data->dai; - data->card.num_links = 1; - data->card.owner = THIS_MODULE; - - ret = snd_soc_of_parse_card_name(&data->card, "model"); - if (ret) - goto end; - - ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) - dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); - -end: - of_node_put(spdif_np); - - return ret; -} - -static const struct of_device_id imx_spdif_dt_ids[] = { - { .compatible = "fsl,imx-audio-spdif", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); - -static struct platform_driver imx_spdif_driver = { - .driver = { - .name = "imx-spdif", - .pm = &snd_soc_pm_ops, - .of_match_table = imx_spdif_dt_ids, - }, - .probe = imx_spdif_audio_probe, -}; - -module_platform_driver(imx_spdif_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx-spdif"); -- cgit v1.2.3 From 4359caadd17fbde64d656c10bd6f2dc91b675a11 Mon Sep 17 00:00:00 2001 From: Elinor Montmasson Date: Thu, 27 Jun 2024 10:31:02 +0200 Subject: ASoC: dt-bindings: update fsl-asoc-card bindings after imx-spdif merge The S/PDIF audio card support with compatible "fsl,imx-audio-spdif" was merged from imx-spdif into the fsl-asoc-card driver. It makes possible to use an S/PDIF with an ASRC. This merge introduces new DT bindings to use with compatible "fsl,imx-audio-spdif" to follow the way fsl-asoc-card works: * the "spdif-controller" property from imx-spdif is named "audio-cpu" in fsl-asoc-card. * fsl-asoc-card uses codecs explicitly declared in DT with "audio-codec". With an SPDIF, codec drivers spdif_transmitter and spdif_receiver should be used. Driver imx-spdif used instead the dummy codec and a pair of boolean properties, "spdif-in" and "spdif-out". In an upcoming commit, in-tree DTs will be modified to follow these new properties: * Property "spdif-controller" will be renamed "audio-cpu". * spdif_transmitter and spdif_receiver nodes will be declared and linked to the fsl-asoc-card node with the property "audio-codec". To keep backward compatibility with other DTs, support for "spdif-controller", "spdif-in" and "spdif-out" properties is kept. However, it is recommended to use the new properties if possible. It is better to declare transmitter and/or receiver in DT than using the dummy codec. DTs using compatible "fsl,imx-audio-spdif" are still supported, and fsl-asoc-card will behave the same as imx-spdif for these DTs. Signed-off-by: Elinor Montmasson Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240627083104.123357-6-elinor.montmasson@savoirfairelinux.com Signed-off-by: Mark Brown --- .../bindings/sound/fsl,imx-audio-spdif.yaml | 66 ---------------------- .../devicetree/bindings/sound/fsl-asoc-card.yaml | 53 +++++++++++++++-- 2 files changed, 49 insertions(+), 70 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml deleted file mode 100644 index 5fc543d02ecb..000000000000 --- a/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/fsl,imx-audio-spdif.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Freescale i.MX audio complex with S/PDIF transceiver - -maintainers: - - Shengjiu Wang - -properties: - compatible: - oneOf: - - items: - - enum: - - fsl,imx-sabreauto-spdif - - fsl,imx6sx-sdb-spdif - - const: fsl,imx-audio-spdif - - enum: - - fsl,imx-audio-spdif - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - - spdif-controller: - $ref: /schemas/types.yaml#/definitions/phandle - description: The phandle of the i.MX S/PDIF controller - - spdif-out: - type: boolean - description: - If present, the transmitting function of S/PDIF will be enabled, - indicating there's a physical S/PDIF out connector or jack on the - board or it's connecting to some other IP block, such as an HDMI - encoder or display-controller. - - spdif-in: - type: boolean - description: - If present, the receiving function of S/PDIF will be enabled, - indicating there is a physical S/PDIF in connector/jack on the board. - -required: - - compatible - - model - - spdif-controller - -anyOf: - - required: - - spdif-in - - required: - - spdif-out - -additionalProperties: false - -examples: - - | - sound-spdif { - compatible = "fsl,imx-audio-spdif"; - model = "imx-spdif"; - spdif-controller = <&spdif>; - spdif-out; - spdif-in; - }; diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml index 9922664d5ccc..92aa47ec72c7 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml @@ -65,6 +65,11 @@ properties: - fsl,imx-audio-sgtl5000 - fsl,imx-audio-wm8960 - fsl,imx-audio-wm8962 + - items: + - enum: + - fsl,imx-sabreauto-spdif + - fsl,imx6sx-sdb-spdif + - const: fsl,imx-audio-spdif - items: - enum: - fsl,imx-audio-ac97 @@ -81,6 +86,7 @@ properties: - fsl,imx-audio-wm8960 - fsl,imx-audio-wm8962 - fsl,imx-audio-wm8958 + - fsl,imx-audio-spdif model: $ref: /schemas/types.yaml#/definitions/string @@ -93,8 +99,15 @@ properties: need to add ASRC support via DPCM. audio-codec: - $ref: /schemas/types.yaml#/definitions/phandle - description: The phandle of an audio codec + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + The phandle of an audio codec. + With "fsl,imx-audio-spdif", either SPDIF audio codec spdif_transmitter, + spdif_receiver or both. + minItems: 1 + maxItems: 2 + items: + maxItems: 1 audio-cpu: $ref: /schemas/types.yaml#/definitions/phandle @@ -150,8 +163,10 @@ properties: description: dai-link uses bit clock inversion. mclk-id: - $ref: /schemas/types.yaml#/definitions/uint32 - description: main clock id, specific for each card configuration. + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Main clock id for each codec, specific for each card configuration. + minItems: 1 + maxItems: 2 mux-int-port: $ref: /schemas/types.yaml#/definitions/uint32 @@ -167,6 +182,27 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of an CPU DAI controller + spdif-controller: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + description: The phandle of an S/PDIF CPU DAI controller. + + spdif-out: + type: boolean + deprecated: true + description: | + If present, the transmitting function of S/PDIF will be enabled, + indicating there's a physical S/PDIF out connector or jack on the + board or it's connecting to some other IP block, such as an HDMI + encoder or display-controller. + + spdif-in: + type: boolean + deprecated: true + description: | + If present, the receiving function of S/PDIF will be enabled, + indicating there is a physical S/PDIF in connector/jack on the board. + required: - compatible - model @@ -195,3 +231,12 @@ examples: "AIN2L", "Line In Jack", "AIN2R", "Line In Jack"; }; + + - | + sound-spdif-asrc { + compatible = "fsl,imx-audio-spdif"; + model = "spdif-asrc-audio"; + audio-cpu = <&spdif>; + audio-asrc = <&easrc>; + audio-codec = <&spdifdit>, <&spdifdir>; + }; -- cgit v1.2.3 From be5db7581f59621ed9cb9cbf6bebccda38263eb5 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Tue, 9 Jul 2024 12:33:40 +0800 Subject: ASoc: TAS2781: rename the tas2781_reset as tasdevice_reset Rename the tas2781_reset as tasdevice_reset in case of misunderstanding. RESET register for both tas2563 and tas2781 is same and the use of reset pin is also same. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240709043342.946-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781.h | 8 ++++---- sound/pci/hda/tas2781_hda_i2c.c | 4 ++-- sound/soc/codecs/tas2781-comlib.c | 8 ++++---- sound/soc/codecs/tas2781-i2c.c | 2 ++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index cd8ce522b78e..a43ad6dcb7c7 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated // https://www.ti.com // // The TAS2563/TAS2781 driver implements a flexible and configurable @@ -43,8 +43,8 @@ (page * 128)) + reg) /*Software Reset */ -#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01) -#define TAS2781_REG_SWRESET_RESET BIT(0) +#define TASDEVICE_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01) +#define TASDEVICE_REG_SWRESET_RESET BIT(0) /*I2C Checksum */ #define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E) @@ -140,7 +140,7 @@ struct tasdevice_priv { void (*apply_calibration)(struct tasdevice_priv *tas_priv); }; -void tas2781_reset(struct tasdevice_priv *tas_dev); +void tasdevice_reset(struct tasdevice_priv *tas_dev); int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, struct module *module, void (*cont)(const struct firmware *fw, void *context)); diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 75f7674c66ee..10420360b214 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -834,7 +834,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) pm_runtime_set_active(tas_hda->dev); pm_runtime_enable(tas_hda->dev); - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops); if (ret) { @@ -929,7 +929,7 @@ static int tas2781_system_resume(struct device *dev) tas_hda->priv->tasdevice[i].cur_prog = -1; tas_hda->priv->tasdevice[i].cur_conf = -1; } - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog); /* If calibrated data occurs error, dsp will still work with default diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 6db1a260da82..1fbf4560f5cc 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -243,7 +243,7 @@ struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) } EXPORT_SYMBOL_GPL(tasdevice_kzalloc); -void tas2781_reset(struct tasdevice_priv *tas_dev) +void tasdevice_reset(struct tasdevice_priv *tas_dev) { int ret, i; @@ -254,8 +254,8 @@ void tas2781_reset(struct tasdevice_priv *tas_dev) } else { for (i = 0; i < tas_dev->ndev; i++) { ret = tasdevice_dev_write(tas_dev, i, - TAS2781_REG_SWRESET, - TAS2781_REG_SWRESET_RESET); + TASDEVICE_REG_SWRESET, + TASDEVICE_REG_SWRESET_RESET); if (ret < 0) dev_err(tas_dev->dev, "dev %d swreset fail, %d\n", @@ -264,7 +264,7 @@ void tas2781_reset(struct tasdevice_priv *tas_dev) } usleep_range(1000, 1050); } -EXPORT_SYMBOL_GPL(tas2781_reset); +EXPORT_SYMBOL_GPL(tasdevice_reset); int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, struct module *module, diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index b412f8f2e5d5..d5d95ae57c73 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -742,6 +742,8 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) if (ret) goto err; + tasdevice_reset(tas_priv); + ret = devm_snd_soc_register_component(tas_priv->dev, &soc_codec_driver_tasdevice, tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); -- cgit v1.2.3 From e52a73e694522c7c94369a7741717a9f50baca0b Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 9 Jul 2024 11:09:21 +0800 Subject: ASoC: Intel: sof_sdw: Convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20240709030921.585740-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index e94849b84a6b..e5feaef669d1 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -2136,9 +2136,9 @@ static int mc_probe(struct platform_device *pdev) card = &ctx->card; card->dev = &pdev->dev; - card->name = "soundwire", - card->owner = THIS_MODULE, - card->late_probe = sof_sdw_card_late_probe, + card->name = "soundwire"; + card->owner = THIS_MODULE; + card->late_probe = sof_sdw_card_late_probe; snd_soc_card_set_drvdata(card, ctx); -- cgit v1.2.3 From d0f4ce8a28dd992579949b8ea2df810c4b5fe9b5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 19:19:16 +0200 Subject: ASoC: codecs: aw88395: Simplify with cleanup.h Allocate memory, which is being freed at end of the scope, with scoped/cleanup.h to reduce number of error paths and make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701171917.596173-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/aw88395/aw88395_lib.c | 51 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index f25f6e0d4428..769ca32a5c8e 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -7,6 +7,7 @@ // Author: Bruce zhao // +#include #include #include #include "aw88395_lib.h" @@ -361,11 +362,11 @@ static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { - struct aw_bin *aw_bin; int ret; int i; - aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(struct aw_bin), + GFP_KERNEL); if (!aw_bin) return -ENOMEM; @@ -375,7 +376,7 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->dev, "parse bin failed"); - goto parse_bin_failed; + return ret; } for (i = 0; i < aw_bin->all_bin_parse_num; i++) { @@ -387,10 +388,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * data + aw_bin->header_info[i].valid_data_addr; break; case DATA_TYPE_DSP_REG: - if (aw_bin->header_info[i].valid_data_len & 0x01) { - ret = -EINVAL; - goto parse_bin_failed; - } + if (aw_bin->header_info[i].valid_data_len & 0x01) + return -EINVAL; swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); @@ -402,10 +401,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * break; case DATA_TYPE_DSP_FW: case DATA_TYPE_SOC_APP: - if (aw_bin->header_info[i].valid_data_len & 0x01) { - ret = -EINVAL; - goto parse_bin_failed; - } + if (aw_bin->header_info[i].valid_data_len & 0x01) + return -EINVAL; swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); @@ -422,20 +419,17 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * } } prof_desc->prof_st = AW88395_PROFILE_OK; - ret = 0; -parse_bin_failed: - devm_kfree(aw_dev->dev, aw_bin); - return ret; + return 0; } static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) { - struct aw_bin *aw_bin; int ret; - aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); + struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(*aw_bin), + GFP_KERNEL); if (!aw_bin) return -ENOMEM; @@ -445,14 +439,13 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->dev, "parse bin failed"); - goto parse_bin_failed; + return ret; } if ((aw_bin->all_bin_parse_num != 1) || (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { dev_err(aw_dev->dev, "bin num or type error"); - ret = -EINVAL; - goto parse_bin_failed; + return -EINVAL; } prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = @@ -461,15 +454,7 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, aw_bin->header_info[0].valid_data_len; prof_desc->prof_st = AW88395_PROFILE_OK; - devm_kfree(aw_dev->dev, aw_bin); - aw_bin = NULL; - return 0; - -parse_bin_failed: - devm_kfree(aw_dev->dev, aw_bin); - aw_bin = NULL; - return ret; } static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, @@ -678,21 +663,21 @@ static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev, static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr) { - struct aw_all_prof_info *all_prof_info; int ret; - all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*all_prof_info), + GFP_KERNEL); if (!all_prof_info) return -ENOMEM; ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) { - goto exit; + return ret; } else if (ret == AW88395_DEV_TYPE_NONE) { dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) - goto exit; + return ret; } switch (aw_dev->prof_data_type) { @@ -710,8 +695,6 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, if (!ret) aw_dev->prof_info.prof_name_list = profile_name; -exit: - devm_kfree(aw_dev->dev, all_prof_info); return ret; } -- cgit v1.2.3 From a8915e2fe86c29304a3038f269f4667b371b87d2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 1 Jul 2024 19:19:17 +0200 Subject: ASoC: qcom: topology: Simplify with cleanup.h Allocate memory, which is being freed at end of the scope, with scoped/cleanup.h to reduce number of error paths and make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20240701171917.596173-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/topology.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index f4d62ea60baa..83319a928f29 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020, Linaro Limited +#include #include #include #include @@ -1288,18 +1289,19 @@ int audioreach_tplg_init(struct snd_soc_component *component) struct snd_soc_card *card = component->card; struct device *dev = component->dev; const struct firmware *fw; - char *tplg_fw_name; int ret; /* Inline with Qualcomm UCM configs and linux-firmware path */ - tplg_fw_name = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", card->driver_name, card->name); + char *tplg_fw_name __free(kfree) = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", + card->driver_name, + card->name); if (!tplg_fw_name) return -ENOMEM; ret = request_firmware(&fw, tplg_fw_name, dev); if (ret < 0) { dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); - goto err; + return ret; } ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw); @@ -1309,8 +1311,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) } release_firmware(fw); -err: - kfree(tplg_fw_name); return ret; } -- cgit v1.2.3 From 054ac9c97c7d9eb55f471fc6f019b654d17332a7 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Wed, 10 Jul 2024 12:36:06 +0100 Subject: ASoC: dt-bindings: qcom,msm8916-wcd-digital-codec: convert to dtschema Convert the Qualcomm MSM8916 WCD Digital Audio Codec bindings from text to yaml dt schema format. Make bindings complete by adding #sound-dai-cells. Signed-off-by: Rayyan Ansari Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240710113833.39859-2-rayyan.ansari@linaro.org Signed-off-by: Mark Brown --- .../sound/qcom,msm8916-wcd-digital-codec.yaml | 55 ++++++++++++++++++++++ .../bindings/sound/qcom,msm8916-wcd-digital.txt | 20 -------- 2 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml delete mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml new file mode 100644 index 000000000000..a899c4e7c1c9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,msm8916-wcd-digital-codec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8916 WCD Digital Audio Codec + +maintainers: + - Srinivas Kandagatla + +description: + The digital WCD audio codec found on Qualcomm MSM8916 LPASS. + +properties: + compatible: + const: qcom,msm8916-wcd-digital-codec + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: ahbix-clk + - const: mclk + + '#sound-dai-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#sound-dai-cells' + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + audio-codec@771c000 { + compatible = "qcom,msm8916-wcd-digital-codec"; + reg = <0x0771c000 0x400>; + clocks = <&gcc GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK>, + <&gcc GCC_CODEC_DIGCODEC_CLK>; + clock-names = "ahbix-clk", "mclk"; + #sound-dai-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt deleted file mode 100644 index 1c8e4cb25176..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt +++ /dev/null @@ -1,20 +0,0 @@ -msm8916 digital audio CODEC - -## Bindings for codec core in lpass: - -Required properties - - compatible = "qcom,msm8916-wcd-digital-codec"; - - reg: address space for lpass codec. - - clocks: Handle to mclk and ahbclk - - clock-names: should be "mclk", "ahbix-clk". - -Example: - -audio-codec@771c000{ - compatible = "qcom,msm8916-wcd-digital-codec"; - reg = <0x0771c000 0x400>; - clocks = <&gcc GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK>, - <&gcc GCC_CODEC_DIGCODEC_CLK>; - clock-names = "ahbix-clk", "mclk"; - #sound-dai-cells = <1>; -}; -- cgit v1.2.3 From 3cbda25e2db28b4352c73d4167f0dab10049ea54 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Wed, 10 Jul 2024 12:36:08 +0100 Subject: ASoC: dt-bindings: qcom,apq8096-sndcard: use dtschema Remove old txt bindings and add apq8096 soundcard entry to existing dt schema. Signed-off-by: Rayyan Ansari Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240710113833.39859-4-rayyan.ansari@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,apq8096.txt | 128 --------------------- .../devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 2 files changed, 1 insertion(+), 128 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt deleted file mode 100644 index e1b9fa8a5bf8..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt +++ /dev/null @@ -1,128 +0,0 @@ -* Qualcomm Technologies APQ8096 ASoC sound card driver - -This binding describes the APQ8096 sound card, which uses qdsp for audio. - -- compatible: - Usage: required - Value type: - Definition: must be "qcom,apq8096-sndcard" - -- audio-routing: - Usage: Optional - Value type: - Definition: A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names could be power supplies, MicBias - of codec and the jacks on the board: - Valid names include: - - Board Connectors: - "Headphone Left" - "Headphone Right" - "Earphone" - "Line Out1" - "Line Out2" - "Line Out3" - "Line Out4" - "Analog Mic1" - "Analog Mic2" - "Analog Mic3" - "Analog Mic4" - "Analog Mic5" - "Analog Mic6" - "Digital Mic2" - "Digital Mic3" - - Audio pins and MicBias on WCD9335 Codec: - "MIC_BIAS1" - "MIC_BIAS2" - "MIC_BIAS3" - "MIC_BIAS4" - "AMIC1" - "AMIC2" - "AMIC3" - "AMIC4" - "AMIC5" - "AMIC6" - "AMIC6" - "DMIC1" - "DMIC2" - "DMIC3" - -- model: - Usage: required - Value type: - Definition: The user-visible name of this sound card. - -- aux-devs - Usage: optional - Value type: - Definition: A list of phandles for auxiliary devices (e.g. analog - amplifiers) that do not appear directly within the DAI - links. Should be connected to another audio component - using "audio-routing". - -= dailinks -Each subnode of sndcard represents either a dailink, and subnodes of each -dailinks would be cpu/codec/platform dais. - -- link-name: - Usage: required - Value type: - Definition: User friendly name for dai link - -= CPU, PLATFORM, CODEC dais subnodes -- cpu: - Usage: required - Value type: - Definition: cpu dai sub-node - -- codec: - Usage: Optional - Value type: - Definition: codec dai sub-node - -- platform: - Usage: Optional - Value type: - Definition: platform dai sub-node - -- sound-dai: - Usage: required - Value type: - Definition: dai phandle/s and port of CPU/CODEC/PLATFORM node. - -Obsolete: - qcom,model: String for soundcard name (Use model instead) - qcom,audio-routing: A list of the connections between audio components. - (Use audio-routing instead) - -Example: - -audio { - compatible = "qcom,apq8096-sndcard"; - model = "DB820c"; - - mm1-dai-link { - link-name = "MultiMedia1"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; - }; - }; - - hdmi-dai-link { - link-name = "HDMI Playback"; - cpu { - sound-dai = <&q6afe HDMI_RX>; - }; - - platform { - sound-dai = <&q6adm>; - }; - - codec { - sound-dai = <&hdmi 0>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index b2e15ebbd1bc..c9076dcd44c1 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -28,6 +28,7 @@ properties: - const: qcom,sm8450-sndcard - enum: - qcom,apq8016-sbc-sndcard + - qcom,apq8096-sndcard - qcom,msm8916-qdsp6-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs6490-rb3gen2-sndcard -- cgit v1.2.3 From bff92858dfb9897e4c06c11aab0322e1aab822f7 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jul 2024 11:36:37 +0100 Subject: firmware: cs_dsp: Don't allocate temporary buffer for info text Don't allocate a temporary buffer to hold a NUL-terminated copy of the NAME/INFO string from the wmfw/bin. It can be printed directly to the log. Also limit the maximum number of characters that will be logged from this string. The NAME/INFO blocks in the firmware files are an array of characters with a length, not a NUL-terminated C string. The original code allocated a temporary buffer to make a NUL-terminated copy of the string and then passed that to dev_info(). There's no need for this: printf formatting can use "%.*s" to print a character array of a given length. Signed-off-by: Richard Fitzgerald Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240710103640.78197-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 8a347b938406..9bd3f78396ea 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1470,7 +1471,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, const struct wmfw_region *region; const struct cs_dsp_region *mem; const char *region_name; - char *text = NULL; struct cs_dsp_buf *buf; unsigned int reg; int regions = 0; @@ -1542,15 +1542,15 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, region_name = "Unknown"; reg = 0; - text = NULL; offset = le32_to_cpu(region->offset) & 0xffffff; type = be32_to_cpu(region->type) & 0xff; switch (type) { + case WMFW_INFO_TEXT: case WMFW_NAME_TEXT: - region_name = "Firmware name"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); + region_name = "Info/Name"; + cs_dsp_info(dsp, "%s: %.*s\n", file, + min(le32_to_cpu(region->len), 100), region->data); break; case WMFW_ALGORITHM_DATA: region_name = "Algorithm"; @@ -1558,11 +1558,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, if (ret != 0) goto out_fw; break; - case WMFW_INFO_TEXT: - region_name = "Information"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); - break; case WMFW_ABSOLUTE: region_name = "Absolute"; reg = offset; @@ -1596,13 +1591,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, regions, le32_to_cpu(region->len), offset, region_name); - if (text) { - memcpy(text, region->data, le32_to_cpu(region->len)); - cs_dsp_info(dsp, "%s: %s\n", file, text); - kfree(text); - text = NULL; - } - if (reg) { buf = cs_dsp_buf_alloc(region->data, le32_to_cpu(region->len), @@ -1644,7 +1632,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); - kfree(text); if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); @@ -2177,7 +2164,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware struct cs_dsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg, version; - char *text = NULL; struct cs_dsp_buf *buf; if (!firmware) @@ -2246,7 +2232,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware region_name = "Unknown"; switch (type) { case (WMFW_NAME_TEXT << 8): - text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); + cs_dsp_info(dsp, "%s: %.*s\n", dsp->fw_name, + min(le32_to_cpu(blk->len), 100), blk->data); break; case (WMFW_INFO_TEXT << 8): case (WMFW_METADATA << 8): @@ -2318,13 +2305,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware break; } - if (text) { - memcpy(text, blk->data, le32_to_cpu(blk->len)); - cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); - kfree(text); - text = NULL; - } - if (reg) { buf = cs_dsp_buf_alloc(blk->data, le32_to_cpu(blk->len), @@ -2364,7 +2344,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); - kfree(text); if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); -- cgit v1.2.3 From 584e86e14c59d36688633002613792923620d8c0 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jul 2024 11:36:38 +0100 Subject: firmware: cs_dsp: Make wmfw and bin filename arguments const char * The wmfw_filename and bin_filename strings passed into cs_dsp_power_up() and cs_dsp_adsp1_power_up() should be const char *. Signed-off-by: Richard Fitzgerald Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240710103640.78197-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 8 ++++---- include/linux/firmware/cirrus/cs_dsp.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 9bd3f78396ea..fda6faa8c835 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -2410,8 +2410,8 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP); * Return: Zero for success, a negative number on error. */ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name) { unsigned int val; @@ -2704,8 +2704,8 @@ static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) * Return: Zero for success, a negative number on error. */ int cs_dsp_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name) { int ret; diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 82687e07a7c2..8078dc377948 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -213,13 +213,13 @@ int cs_dsp_adsp2_init(struct cs_dsp *dsp); int cs_dsp_halo_init(struct cs_dsp *dsp); int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name); void cs_dsp_adsp1_power_down(struct cs_dsp *dsp); int cs_dsp_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name); void cs_dsp_power_down(struct cs_dsp *dsp); int cs_dsp_run(struct cs_dsp *dsp); -- cgit v1.2.3 From a4939119055d5c97aff2cea040f1af8c4606bfa1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jul 2024 11:36:39 +0100 Subject: firmware: cs_dsp: Clarify wmfw format version log message Change the log message of the wmfw format version to include the file name, and change the message to say "format" instead of "Firmware version". Merge this with the message that logs the timestamp. The wmfw format version is information that is useful to have logged because the behaviour of firmware controls depends on the wmfw format. So "unexpected" behaviour could be caused by having expectations based on one format of wmfw when a different format has been loaded. But the original message was confusing. It reported the file format version but didn't actually log the name of the file it referred to. It also called it "Firmware version", which is confusing when a later message also logs a firmware version that is the version of the actual firmware within the wmfw. The logging of the firmware timestamp has been merged into this. That was originally a dbg-only message, but as we are already logging a line of info, we might as well add a few extra characters to log the timestamp. The timestamp is now logged in hexadecimal - it's not particularly useful as a decimal value. Signed-off-by: Richard Fitzgerald Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240710103640.78197-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index fda6faa8c835..865db8defc56 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1499,7 +1499,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, goto out_fw; } - cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); dsp->fw_ver = header->ver; if (header->core != dsp->type) { @@ -1523,8 +1522,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, goto out_fw; } - cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, - le64_to_cpu(footer->timestamp)); + cs_dsp_info(dsp, "%s: format %d timestamp %#llx\n", file, header->ver, + le64_to_cpu(footer->timestamp)); while (pos < firmware->size) { /* Is there enough data for a complete block header? */ -- cgit v1.2.3 From dc0e5ca8856dc6a97e3b117879dfb2b52bda06b6 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jul 2024 11:36:40 +0100 Subject: firmware: cs_dsp: Rename fw_ver to wmfw_ver Rename the confusingly named struct member fw_ver to wmfw_ver. It contains the wmfw format version of the loaded wmfw file. This commit also contains an update to wm_adsp for the new name. Signed-off-by: Richard Fitzgerald Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20240710103640.78197-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 20 ++++++++++---------- include/linux/firmware/cirrus/cs_dsp.h | 2 +- sound/soc/codecs/wm_adsp.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 865db8defc56..66c2f09b0be7 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1048,7 +1048,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp, ctl->fw_name = dsp->fw_name; ctl->alg_region = *alg_region; - if (subname && dsp->fw_ver >= 2) { + if (subname && dsp->wmfw_ver >= 2) { ctl->subname_len = subname_len; ctl->subname = kasprintf(GFP_KERNEL, "%.*s", subname_len, subname); if (!ctl->subname) { @@ -1175,7 +1175,7 @@ static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, raw = (const struct wmfw_adsp_alg_data *)region->data; - switch (dsp->fw_ver) { + switch (dsp->wmfw_ver) { case 0: case 1: if (sizeof(*raw) > data_len) @@ -1252,7 +1252,7 @@ static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, blk->offset = le16_to_cpu(raw->hdr.offset); blk->mem_type = le16_to_cpu(raw->hdr.type); - switch (dsp->fw_ver) { + switch (dsp->wmfw_ver) { case 0: case 1: if (sizeof(*raw) > (data_len - pos)) @@ -1499,7 +1499,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, goto out_fw; } - dsp->fw_ver = header->ver; + dsp->wmfw_ver = header->ver; if (header->core != dsp->type) { cs_dsp_err(dsp, "%s: invalid core %d != %d\n", @@ -1779,7 +1779,7 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, list_add_tail(&alg_region->list, &dsp->alg_regions); - if (dsp->fw_ver > 0) + if (dsp->wmfw_ver > 0) cs_dsp_ctl_fixup_base(dsp, alg_region); return alg_region; @@ -1902,7 +1902,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp1_alg[i + 1].dm); len -= be32_to_cpu(adsp1_alg[i].dm); @@ -1924,7 +1924,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp1_alg[i + 1].zm); len -= be32_to_cpu(adsp1_alg[i].zm); @@ -2015,7 +2015,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].xm); len -= be32_to_cpu(adsp2_alg[i].xm); @@ -2037,7 +2037,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].ym); len -= be32_to_cpu(adsp2_alg[i].ym); @@ -2059,7 +2059,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].zm); len -= be32_to_cpu(adsp2_alg[i].zm); diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 8078dc377948..2e649810169f 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -167,7 +167,7 @@ struct cs_dsp { const struct cs_dsp_region *mem; int num_mems; - int fw_ver; + int wmfw_ver; bool booted; bool running; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 68d2d6444533..9f8549b34e30 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -601,7 +601,7 @@ static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) return -EINVAL; } - switch (cs_dsp->fw_ver) { + switch (cs_dsp->wmfw_ver) { case 0: case 1: ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, -- cgit v1.2.3 From e021e0eecba99d7ec68f6e7972a72175f54ff6e3 Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Wed, 10 Jul 2024 00:12:25 +0530 Subject: ASoC: dt-bindings: cirrus,cs4270: Convert to dtschema Convert the Cirrus Logic CS4270 audio CODEC bindings to DT schema. Add missing va-supply, vd-supply and vlc-supply properties, because they are already being used in the DTS and the driver for this device. Cc: Daniel Baluta Signed-off-by: Animesh Agarwal Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240709184231.125207-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cirrus,cs4270.yaml | 59 ++++++++++++++++++++++ Documentation/devicetree/bindings/sound/cs4270.txt | 21 -------- 2 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml delete mode 100644 Documentation/devicetree/bindings/sound/cs4270.txt diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml new file mode 100644 index 000000000000..336e11773694 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs4270.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS4270 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: + The CS4270 is a stereo audio codec. The driver for this device currently only + supports I2C. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: cirrus,cs4270 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + reset-gpios: + description: + This pin will be deasserted before communication to the codec starts. + maxItems: 1 + + va-supply: + description: Analog power supply. + + vd-supply: + description: Digital power supply. + + vlc-supply: + description: Serial Control Port power supply. + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@48 { + compatible = "cirrus,cs4270"; + reg = <0x48>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs4270.txt b/Documentation/devicetree/bindings/sound/cs4270.txt deleted file mode 100644 index c33770ec4c3c..000000000000 --- a/Documentation/devicetree/bindings/sound/cs4270.txt +++ /dev/null @@ -1,21 +0,0 @@ -CS4270 audio CODEC - -The driver for this device currently only supports I2C. - -Required properties: - - - compatible : "cirrus,cs4270" - - - reg : the I2C address of the device for I2C - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the codec starts. - -Example: - -codec: cs4270@48 { - compatible = "cirrus,cs4270"; - reg = <0x48>; -}; -- cgit v1.2.3 From c62bec0fe36873ab6c87c9ae84a6b661d09244c4 Mon Sep 17 00:00:00 2001 From: Paul Handrigan Date: Wed, 10 Jul 2024 11:04:15 -0500 Subject: ASoC: cs530x: Calculate proper bclk rate using TDM Calculate the proper bclk rate using the number of tdm slots and the width of the tdm data. Fixes: 2884c29152c0 ("ASoC: cs530x: Support for cs530x ADCs") Signed-off-by: Paul Handrigan Link: https://lore.kernel.org/r/20240710160416.2617173-1-paulha@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs530x.c | 15 ++++++++++----- sound/soc/codecs/cs530x.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 036d0f45e3ba..195739657f4e 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -526,13 +526,15 @@ static int cs530x_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(regmap, CS530X_CLK_CFG_1, CS530X_SAMPLE_RATE_MASK, fs_val); - if (cs530x->tdm_slots) { + + if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_TDM_EN_MASK)) { dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", cs530x->tdm_slots, cs530x->tdm_width); - cs530x->bclk = snd_soc_calc_bclk(cs530x->fs, - cs530x->tdm_width, - params_channels(params), - cs530x->tdm_slots); + cs530x->bclk = snd_soc_tdm_params_to_bclk(params, + cs530x->tdm_width, + cs530x->tdm_slots, + 1); } else { cs530x->bclk = snd_soc_params_to_bclk(params); } @@ -655,6 +657,9 @@ static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, return -EINVAL; } + cs530x->tdm_width = slot_width; + cs530x->tdm_slots = slots; + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, CS530X_ASP_TDM_SLOT_MASK, val << CS530X_ASP_TDM_SLOT_SHIFT); diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1c85310a5d03..1915a0efe829 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -96,6 +96,7 @@ #define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3) #define CS530X_ASP_TDM_SLOT_SHIFT 3 #define CS530X_ASP_CH_REVERSE BIT(9) +#define CS530X_TDM_EN_MASK BIT(2) #define CS530X_ASP_FMT_I2S 0 #define CS530X_ASP_FMT_LJ 1 #define CS530X_ASP_FMT_DSP_A 0x6 -- cgit v1.2.3 From 0d1ebba74fad7b6b3eb0e2bc08d649d20c7aa7c9 Mon Sep 17 00:00:00 2001 From: Paul Handrigan Date: Wed, 10 Jul 2024 11:04:16 -0500 Subject: ASoC: cs530x: Remove bclk from private structure Remove the bclk int from the private structure and pass it into the set_bclk function. Signed-off-by: Paul Handrigan Link: https://lore.kernel.org/r/20240710160416.2617173-2-paulha@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs530x.c | 24 ++++++++++++------------ sound/soc/codecs/cs530x.h | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 195739657f4e..25a86a32e936 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -415,13 +415,13 @@ static void cs530x_add_34_adc_widgets(struct snd_soc_component *component) ARRAY_SIZE(adc_ch3_4_routes)); } -static int cs530x_set_bclk(struct snd_soc_component *component) +static int cs530x_set_bclk(struct snd_soc_component *component, const int freq) { struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); struct regmap *regmap = cs530x->regmap; unsigned int bclk_val; - switch (cs530x->bclk) { + switch (freq) { case 2822400: case 3072000: bclk_val = CS530X_BCLK_2P822_3P072; @@ -439,11 +439,11 @@ static int cs530x_set_bclk(struct snd_soc_component *component) bclk_val = CS530X_BCLK_24P5792_24P576; break; default: - dev_err(component->dev, "Invalid BCLK %d\n", cs530x->bclk); + dev_err(component->dev, "Invalid BCLK frequency %d\n", freq); return -EINVAL; } - dev_dbg(component->dev, "BCLK is %d\n", cs530x->bclk); + dev_dbg(component->dev, "BCLK frequency is %d\n", freq); return regmap_update_bits(regmap, CS530X_ASP_CFG, CS530X_ASP_BCLK_FREQ_MASK, bclk_val); @@ -489,7 +489,7 @@ static int cs530x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); struct regmap *regmap = cs530x->regmap; - int ret = 0, fs = params_rate(params); + int ret = 0, fs = params_rate(params), bclk; unsigned int fs_val; @@ -531,22 +531,22 @@ static int cs530x_hw_params(struct snd_pcm_substream *substream, CS530X_TDM_EN_MASK)) { dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", cs530x->tdm_slots, cs530x->tdm_width); - cs530x->bclk = snd_soc_tdm_params_to_bclk(params, - cs530x->tdm_width, - cs530x->tdm_slots, - 1); + bclk = snd_soc_tdm_params_to_bclk(params, + cs530x->tdm_width, + cs530x->tdm_slots, + 1); } else { - cs530x->bclk = snd_soc_params_to_bclk(params); + bclk = snd_soc_params_to_bclk(params); } if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0, CS530X_PLL_REFCLK_SRC_MASK)) { - ret = cs530x_set_pll_refclk(component, cs530x->bclk); + ret = cs530x_set_pll_refclk(component, bclk); if (ret) return ret; } - return cs530x_set_bclk(component); + return cs530x_set_bclk(component, bclk); } static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1915a0efe829..f473e33eb835 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -211,7 +211,6 @@ struct cs530x_priv { int tdm_width; int tdm_slots; - int bclk; int fs; int adc_pairs_count; -- cgit v1.2.3 From e3fff693da9fa0337d98dab496db3a04c5227aae Mon Sep 17 00:00:00 2001 From: Animesh Agarwal Date: Wed, 10 Jul 2024 12:57:52 +0530 Subject: ASoC: dt-bindings: cirrus,cs42xx8: Convert to dtschema Convert the Cirrus Logic CS42448/CS42888 audio CODEC bindings to DT schema format. Set power supply properties to required only for CS42888. Cc: Daniel Baluta Signed-off-by: Animesh Agarwal Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20240710072756.99765-1-animeshagarwal28@gmail.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cirrus,cs42xx8.yaml | 81 ++++++++++++++++++++++ .../devicetree/bindings/sound/cs42xx8.txt | 34 --------- 2 files changed, 81 insertions(+), 34 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml delete mode 100644 Documentation/devicetree/bindings/sound/cs42xx8.txt diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml new file mode 100644 index 000000000000..725b47e82062 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42xx8.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42448/CS42888 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + enum: + - cirrus,cs42448 + - cirrus,cs42888 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + const: mclk + + VA-supply: + description: Analog power supply. + + VD-supply: + description: Digital power supply. + + VLC-supply: + description: Control port power supply + + VLS-supply: + description: Serial port interface power supply. + + reset-gpios: + description: This pin is connected to the chip's RESET pin. + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + +if: + properties: + compatible: + contains: + const: cirrus,cs42888 +then: + required: + - VA-supply + - VD-supply + - VLC-supply + - VLS-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@48 { + compatible = "cirrus,cs42888"; + reg = <0x48>; + clocks = <&codec_mclk 0>; + clock-names = "mclk"; + VA-supply = <®_audio>; + VD-supply = <®_audio>; + VLS-supply = <®_audio>; + VLC-supply = <®_audio>; + reset-gpios = <&gpio 1>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs42xx8.txt b/Documentation/devicetree/bindings/sound/cs42xx8.txt deleted file mode 100644 index bbfe39347c20..000000000000 --- a/Documentation/devicetree/bindings/sound/cs42xx8.txt +++ /dev/null @@ -1,34 +0,0 @@ -CS42448/CS42888 audio CODEC - -Required properties: - - - compatible : must contain one of "cirrus,cs42448" and "cirrus,cs42888" - - - reg : the I2C address of the device for I2C - - - clocks : a list of phandles + clock-specifiers, one for each entry in - clock-names - - - clock-names : must contain "mclk" - - - VA-supply, VD-supply, VLS-supply, VLC-supply: power supplies for the device, - as covered in Documentation/devicetree/bindings/regulator/regulator.txt - -Optional properties: - - - reset-gpios : a GPIO spec to define which pin is connected to the chip's - !RESET pin - -Example: - -cs42888: codec@48 { - compatible = "cirrus,cs42888"; - reg = <0x48>; - clocks = <&codec_mclk 0>; - clock-names = "mclk"; - VA-supply = <®_audio>; - VD-supply = <®_audio>; - VLS-supply = <®_audio>; - VLC-supply = <®_audio>; - reset-gpios = <&pca9557_b 1 GPIO_ACTIVE_LOW>; -}; -- cgit v1.2.3 From 0ffc5a40ad21996c1c4b1d8a636c9051ea41a76e Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 4 Jul 2024 13:47:21 +0530 Subject: ASoC: codecs: wcd937x-sdw: Fix Unbalanced pm_runtime_enable Fix the unbalanced pm_runtime_enable! in wcd937x-sdw soundwire slave. Fixes: c99a515ff153 ("ASoC: codecs: wcd937x-sdw: add SoundWire driver") Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20240704081723.3394153-2-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x-sdw.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index a24d6c620dba..3abc8041406a 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -1067,13 +1067,15 @@ static int wcd9370_probe(struct sdw_slave *pdev, wcd->ch_info = &wcd937x_sdw_rx_ch_info[0]; } - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - return component_add(dev, &wcd937x_sdw_component_ops); + ret = component_add(dev, &wcd937x_sdw_component_ops); + if (ret) + return ret; + + /* Set suspended until aggregate device is bind */ + pm_runtime_set_suspended(dev); + + return 0; } static int wcd9370_remove(struct sdw_slave *pdev) @@ -1082,10 +1084,6 @@ static int wcd9370_remove(struct sdw_slave *pdev) component_del(dev, &wcd937x_sdw_component_ops); - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_dont_use_autosuspend(dev); - return 0; } -- cgit v1.2.3 From a5f727f423101efd60a27bccd7680df429cef38a Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 4 Jul 2024 13:47:22 +0530 Subject: ASoC: codecs: wcd937x: Remove the string compare in MIC BIAS widget settings Remove the string compare with widget name in MIC BIAS settings, instead of string compare use the MIC BIAS id's. Reported-by: Srinivas Kandagatla Closes: https://lore.kernel.org/linux-sound/ba911ebd-aef5-46af-ace1-84d13bee6876@linaro.org/ Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20240704081723.3394153-3-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 4beb70bc4d8c..94282499fa33 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -1164,16 +1164,7 @@ static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - int micb_num; - - if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) - micb_num = MIC_BIAS_1; - else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) - micb_num = MIC_BIAS_2; - else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) - micb_num = MIC_BIAS_3; - else - return -EINVAL; + int micb_num = w->shift; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1203,16 +1194,7 @@ static int __wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - int micb_num; - - if (strnstr(w->name, "VA MIC BIAS1", sizeof("VA MIC BIAS1"))) - micb_num = MIC_BIAS_1; - else if (strnstr(w->name, "VA MIC BIAS2", sizeof("VA MIC BIAS2"))) - micb_num = MIC_BIAS_2; - else if (strnstr(w->name, "VA MIC BIAS3", sizeof("VA MIC BIAS3"))) - micb_num = MIC_BIAS_3; - else - return -EINVAL; + int micb_num = w->shift; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -2231,15 +2213,15 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_POST_PMD), /* MIC_BIAS widgets */ - SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, wcd937x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, wcd937x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, wcd937x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -2320,15 +2302,15 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("HPHR"), /* MIC_BIAS pull up widgets */ - SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, wcd937x_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, wcd937x_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, wcd937x_codec_enable_micbias_pullup, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), -- cgit v1.2.3 From 216d04139a6d0ecaea9432178225b29d367da886 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 4 Jul 2024 13:47:23 +0530 Subject: ASoC: codecs: wcd937x: Remove separate handling for vdd-buck supply Remove separate handling for vdd-buck regulator supply which is not required. The vdd-buck regulator supply enabled using bulk enable. Add the error handling in wcd937x_probe() and disable the regulators in error case. Reported-by: Christophe JAILLET Closes: https://lore.kernel.org/linux-sound/834d31cc-f4bc-4db7-a25b-f9869e550eb6@wanadoo.fr/ Signed-off-by: Mohammad Rafi Shaik Link: https://patch.msgid.link/20240704081723.3394153-4-quic_mohs@quicinc.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 61 ++++++++++------------------------------------ sound/soc/codecs/wcd937x.h | 2 +- 2 files changed, 14 insertions(+), 49 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 94282499fa33..13926f4b0d9f 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -869,37 +869,6 @@ static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w, return 0; } -static int wcd937x_codec_enable_vdd_buck(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - int ret = 0; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (test_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask)) { - dev_err(component->dev, "buck already in enabled state\n"); - clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); - return 0; - } - ret = regulator_enable(wcd937x->buck_supply); - if (ret) { - dev_err(component->dev, "VDD_BUCK is not enabled\n"); - return ret; - } - clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); - usleep_range(200, 250); - break; - case SND_SOC_DAPM_POST_PMD: - set_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); - break; - } - - return 0; -} - static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) { if (micb_mv < 1000 || micb_mv > 2850) { @@ -2226,10 +2195,7 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, - wcd937x_codec_enable_vdd_buck, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), /* RX widgets */ @@ -2915,24 +2881,17 @@ static int wcd937x_probe(struct platform_device *pdev) wcd937x->supplies[0].supply = "vdd-rxtx"; wcd937x->supplies[1].supply = "vdd-px"; wcd937x->supplies[2].supply = "vdd-mic-bias"; + wcd937x->supplies[3].supply = "vdd-buck"; ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); if (ret) return dev_err_probe(dev, ret, "Failed to get supplies\n"); ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); - if (ret) + if (ret) { + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); return dev_err_probe(dev, ret, "Failed to enable supplies\n"); - - /* Get the buck separately, as it needs special handling */ - wcd937x->buck_supply = devm_regulator_get(dev, "vdd-buck"); - if (IS_ERR(wcd937x->buck_supply)) - return dev_err_probe(dev, PTR_ERR(wcd937x->buck_supply), - "Failed to get buck supply\n"); - - ret = regulator_enable(wcd937x->buck_supply); - if (ret) - return dev_err_probe(dev, ret, "Failed to enable buck supply\n"); + } wcd937x_dt_parse_micbias_info(dev, wcd937x); @@ -2949,13 +2908,13 @@ static int wcd937x_probe(struct platform_device *pdev) ret = wcd937x_add_slave_components(wcd937x, dev, &match); if (ret) - return ret; + goto err_disable_regulators; wcd937x_reset(wcd937x); ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match); if (ret) - return ret; + goto err_disable_regulators; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -2964,6 +2923,12 @@ static int wcd937x_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_idle(dev); + return 0; + +err_disable_regulators: + regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + return ret; } diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index f267c66ca959..37bff16e88dd 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -483,7 +483,7 @@ #define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31) #define WCD937X_MAX_MICBIAS 3 -#define WCD937X_MAX_BULK_SUPPLY 3 +#define WCD937X_MAX_BULK_SUPPLY 4 #define WCD937X_MAX_TX_SWR_PORTS 4 #define WCD937X_MAX_SWR_PORTS 5 #define WCD937X_MAX_SWR_CH_IDS 15 -- cgit v1.2.3 From 75ed63a5ab5d1d2872c735bc7edf8fef0e2fa2ea Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Wed, 10 Jul 2024 14:42:37 +0800 Subject: ASoC: tas2781: Add new Kontrol to set tas2563 digital Volume Requriment from customer to add new kcontrol to set tas2563 digital Volume Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20240710064238.1480-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781-tlv.h | 262 ++++++++++++++++++++++++++++++++++++++++- include/sound/tas2781.h | 1 + sound/soc/codecs/tas2781-i2c.c | 129 ++++++++++++++++++-- 3 files changed, 382 insertions(+), 10 deletions(-) diff --git a/include/sound/tas2781-tlv.h b/include/sound/tas2781-tlv.h index 1dc59005d241..99c41bfc7827 100644 --- a/include/sound/tas2781-tlv.h +++ b/include/sound/tas2781-tlv.h @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated // https://www.ti.com // // The TAS2781 driver implements a flexible and configurable @@ -17,5 +17,265 @@ static const __maybe_unused DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0); static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0); +static const DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1); +/* pow(10, db/20) * pow(2,30) */ +static const unsigned char tas2563_dvc_table[][4] = { + { 0X00, 0X00, 0X00, 0X00 }, /* -121.5db */ + { 0X00, 0X00, 0X03, 0XBC }, /* -121.0db */ + { 0X00, 0X00, 0X03, 0XF5 }, /* -120.5db */ + { 0X00, 0X00, 0X04, 0X31 }, /* -120.0db */ + { 0X00, 0X00, 0X04, 0X71 }, /* -119.5db */ + { 0X00, 0X00, 0X04, 0XB4 }, /* -119.0db */ + { 0X00, 0X00, 0X04, 0XFC }, /* -118.5db */ + { 0X00, 0X00, 0X05, 0X47 }, /* -118.0db */ + { 0X00, 0X00, 0X05, 0X97 }, /* -117.5db */ + { 0X00, 0X00, 0X05, 0XEC }, /* -117.0db */ + { 0X00, 0X00, 0X06, 0X46 }, /* -116.5db */ + { 0X00, 0X00, 0X06, 0XA5 }, /* -116.0db */ + { 0X00, 0X00, 0X07, 0X0A }, /* -115.5db */ + { 0X00, 0X00, 0X07, 0X75 }, /* -115.0db */ + { 0X00, 0X00, 0X07, 0XE6 }, /* -114.5db */ + { 0X00, 0X00, 0X08, 0X5E }, /* -114.0db */ + { 0X00, 0X00, 0X08, 0XDD }, /* -113.5db */ + { 0X00, 0X00, 0X09, 0X63 }, /* -113.0db */ + { 0X00, 0X00, 0X09, 0XF2 }, /* -112.5db */ + { 0X00, 0X00, 0X0A, 0X89 }, /* -112.0db */ + { 0X00, 0X00, 0X0B, 0X28 }, /* -111.5db */ + { 0X00, 0X00, 0X0B, 0XD2 }, /* -111.0db */ + { 0X00, 0X00, 0X0C, 0X85 }, /* -110.5db */ + { 0X00, 0X00, 0X0D, 0X43 }, /* -110.0db */ + { 0X00, 0X00, 0X0E, 0X0C }, /* -109.5db */ + { 0X00, 0X00, 0X0E, 0XE1 }, /* -109.0db */ + { 0X00, 0X00, 0X0F, 0XC3 }, /* -108.5db */ + { 0X00, 0X00, 0X10, 0XB2 }, /* -108.0db */ + { 0X00, 0X00, 0X11, 0XAF }, /* -107.5db */ + { 0X00, 0X00, 0X12, 0XBC }, /* -107.0db */ + { 0X00, 0X00, 0X13, 0XD8 }, /* -106.5db */ + { 0X00, 0X00, 0X15, 0X05 }, /* -106.0db */ + { 0X00, 0X00, 0X16, 0X44 }, /* -105.5db */ + { 0X00, 0X00, 0X17, 0X96 }, /* -105.0db */ + { 0X00, 0X00, 0X18, 0XFB }, /* -104.5db */ + { 0X00, 0X00, 0X1A, 0X76 }, /* -104.0db */ + { 0X00, 0X00, 0X1C, 0X08 }, /* -103.5db */ + { 0X00, 0X00, 0X1D, 0XB1 }, /* -103.0db */ + { 0X00, 0X00, 0X1F, 0X73 }, /* -102.5db */ + { 0X00, 0X00, 0X21, 0X51 }, /* -102.0db */ + { 0X00, 0X00, 0X23, 0X4A }, /* -101.5db */ + { 0X00, 0X00, 0X25, 0X61 }, /* -101.0db */ + { 0X00, 0X00, 0X27, 0X98 }, /* -100.5db */ + { 0X00, 0X00, 0X29, 0XF1 }, /* -100.0db */ + { 0X00, 0X00, 0X2C, 0X6D }, /* -99.5db */ + { 0X00, 0X00, 0X2F, 0X0F }, /* -99.0db */ + { 0X00, 0X00, 0X31, 0XD9 }, /* -98.5db */ + { 0X00, 0X00, 0X34, 0XCD }, /* -98.0db */ + { 0X00, 0X00, 0X37, 0XEE }, /* -97.5db */ + { 0X00, 0X00, 0X3B, 0X3F }, /* -97.0db */ + { 0X00, 0X00, 0X3E, 0XC1 }, /* -96.5db */ + { 0X00, 0X00, 0X42, 0X79 }, /* -96.0db */ + { 0X00, 0X00, 0X46, 0X6A }, /* -95.5db */ + { 0X00, 0X00, 0X4A, 0X96 }, /* -95.0db */ + { 0X00, 0X00, 0X4F, 0X01 }, /* -94.5db */ + { 0X00, 0X00, 0X53, 0XAF }, /* -94.0db */ + { 0X00, 0X00, 0X58, 0XA5 }, /* -93.5db */ + { 0X00, 0X00, 0X5D, 0XE6 }, /* -93.0db */ + { 0X00, 0X00, 0X63, 0X76 }, /* -92.5db */ + { 0X00, 0X00, 0X69, 0X5B }, /* -92.0db */ + { 0X00, 0X00, 0X6F, 0X99 }, /* -91.5db */ + { 0X00, 0X00, 0X76, 0X36 }, /* -91.0db */ + { 0X00, 0X00, 0X7D, 0X37 }, /* -90.5db */ + { 0X00, 0X00, 0X84, 0XA2 }, /* -90.0db */ + { 0X00, 0X00, 0X8C, 0X7E }, /* -89.5db */ + { 0X00, 0X00, 0X94, 0XD1 }, /* -89.0db */ + { 0X00, 0X00, 0X9D, 0XA3 }, /* -88.5db */ + { 0X00, 0X00, 0XA6, 0XFA }, /* -88.0db */ + { 0X00, 0X00, 0XB0, 0XDF }, /* -87.5db */ + { 0X00, 0X00, 0XBB, 0X5A }, /* -87.0db */ + { 0X00, 0X00, 0XC6, 0X74 }, /* -86.5db */ + { 0X00, 0X00, 0XD2, 0X36 }, /* -86.0db */ + { 0X00, 0X00, 0XDE, 0XAB }, /* -85.5db */ + { 0X00, 0X00, 0XEB, 0XDC }, /* -85.0db */ + { 0X00, 0X00, 0XF9, 0XD6 }, /* -84.5db */ + { 0X00, 0X01, 0X08, 0XA4 }, /* -84.0db */ + { 0X00, 0X01, 0X18, 0X52 }, /* -83.5db */ + { 0X00, 0X01, 0X28, 0XEF }, /* -83.0db */ + { 0X00, 0X01, 0X3A, 0X87 }, /* -82.5db */ + { 0X00, 0X01, 0X4D, 0X2A }, /* -82.0db */ + { 0X00, 0X01, 0X60, 0XE8 }, /* -81.5db */ + { 0X00, 0X01, 0X75, 0XD1 }, /* -81.0db */ + { 0X00, 0X01, 0X8B, 0XF7 }, /* -80.5db */ + { 0X00, 0X01, 0XA3, 0X6E }, /* -80.0db */ + { 0X00, 0X01, 0XBC, 0X48 }, /* -79.5db */ + { 0X00, 0X01, 0XD6, 0X9B }, /* -79.0db */ + { 0X00, 0X01, 0XF2, 0X7E }, /* -78.5db */ + { 0X00, 0X02, 0X10, 0X08 }, /* -78.0db */ + { 0X00, 0X02, 0X2F, 0X51 }, /* -77.5db */ + { 0X00, 0X02, 0X50, 0X76 }, /* -77.0db */ + { 0X00, 0X02, 0X73, 0X91 }, /* -76.5db */ + { 0X00, 0X02, 0X98, 0XC0 }, /* -76.0db */ + { 0X00, 0X02, 0XC0, 0X24 }, /* -75.5db */ + { 0X00, 0X02, 0XE9, 0XDD }, /* -75.0db */ + { 0X00, 0X03, 0X16, 0X0F }, /* -74.5db */ + { 0X00, 0X03, 0X44, 0XDF }, /* -74.0db */ + { 0X00, 0X03, 0X76, 0X76 }, /* -73.5db */ + { 0X00, 0X03, 0XAA, 0XFC }, /* -73.0db */ + { 0X00, 0X03, 0XE2, 0XA0 }, /* -72.5db */ + { 0X00, 0X04, 0X1D, 0X8F }, /* -72.0db */ + { 0X00, 0X04, 0X5B, 0XFD }, /* -71.5db */ + { 0X00, 0X04, 0X9E, 0X1D }, /* -71.0db */ + { 0X00, 0X04, 0XE4, 0X29 }, /* -70.5db */ + { 0X00, 0X05, 0X2E, 0X5A }, /* -70.0db */ + { 0X00, 0X05, 0X7C, 0XF2 }, /* -69.5db */ + { 0X00, 0X05, 0XD0, 0X31 }, /* -69.0db */ + { 0X00, 0X06, 0X28, 0X60 }, /* -68.5db */ + { 0X00, 0X06, 0X85, 0XC8 }, /* -68.0db */ + { 0X00, 0X06, 0XE8, 0XB9 }, /* -67.5db */ + { 0X00, 0X07, 0X51, 0X86 }, /* -67.0db */ + { 0X00, 0X07, 0XC0, 0X8A }, /* -66.5db */ + { 0X00, 0X08, 0X36, 0X21 }, /* -66.0db */ + { 0X00, 0X08, 0XB2, 0XB0 }, /* -65.5db */ + { 0X00, 0X09, 0X36, 0XA1 }, /* -65.0db */ + { 0X00, 0X09, 0XC2, 0X63 }, /* -64.5db */ + { 0X00, 0X0A, 0X56, 0X6D }, /* -64.0db */ + { 0X00, 0X0A, 0XF3, 0X3C }, /* -63.5db */ + { 0X00, 0X0B, 0X99, 0X56 }, /* -63.0db */ + { 0X00, 0X0C, 0X49, 0X48 }, /* -62.5db */ + { 0X00, 0X0D, 0X03, 0XA7 }, /* -62.0db */ + { 0X00, 0X0D, 0XC9, 0X11 }, /* -61.5db */ + { 0X00, 0X0E, 0X9A, 0X2D }, /* -61.0db */ + { 0X00, 0X0F, 0X77, 0XAD }, /* -60.5db */ + { 0X00, 0X10, 0X62, 0X4D }, /* -60.0db */ + { 0X00, 0X11, 0X5A, 0XD5 }, /* -59.5db */ + { 0X00, 0X12, 0X62, 0X16 }, /* -59.0db */ + { 0X00, 0X13, 0X78, 0XF0 }, /* -58.5db */ + { 0X00, 0X14, 0XA0, 0X50 }, /* -58.0db */ + { 0X00, 0X15, 0XD9, 0X31 }, /* -57.5db */ + { 0X00, 0X17, 0X24, 0X9C }, /* -57.0db */ + { 0X00, 0X18, 0X83, 0XAA }, /* -56.5db */ + { 0X00, 0X19, 0XF7, 0X86 }, /* -56.0db */ + { 0X00, 0X1B, 0X81, 0X6A }, /* -55.5db */ + { 0X00, 0X1D, 0X22, 0XA4 }, /* -55.0db */ + { 0X00, 0X1E, 0XDC, 0X98 }, /* -54.5db */ + { 0X00, 0X20, 0XB0, 0XBC }, /* -54.0db */ + { 0X00, 0X22, 0XA0, 0X9D }, /* -53.5db */ + { 0X00, 0X24, 0XAD, 0XE0 }, /* -53.0db */ + { 0X00, 0X26, 0XDA, 0X43 }, /* -52.5db */ + { 0X00, 0X29, 0X27, 0X9D }, /* -52.0db */ + { 0X00, 0X2B, 0X97, 0XE3 }, /* -51.5db */ + { 0X00, 0X2E, 0X2D, 0X27 }, /* -51.0db */ + { 0X00, 0X30, 0XE9, 0X9A }, /* -50.5db */ + { 0X00, 0X33, 0XCF, 0X8D }, /* -50.0db */ + { 0X00, 0X36, 0XE1, 0X78 }, /* -49.5db */ + { 0X00, 0X3A, 0X21, 0XF3 }, /* -49.0db */ + { 0X00, 0X3D, 0X93, 0XC3 }, /* -48.5db */ + { 0X00, 0X41, 0X39, 0XD3 }, /* -48.0db */ + { 0X00, 0X45, 0X17, 0X3B }, /* -47.5db */ + { 0X00, 0X49, 0X2F, 0X44 }, /* -47.0db */ + { 0X00, 0X4D, 0X85, 0X66 }, /* -46.5db */ + { 0X00, 0X52, 0X1D, 0X50 }, /* -46.0db */ + { 0X00, 0X56, 0XFA, 0XE8 }, /* -45.5db */ + { 0X00, 0X5C, 0X22, 0X4E }, /* -45.0db */ + { 0X00, 0X61, 0X97, 0XE1 }, /* -44.5db */ + { 0X00, 0X67, 0X60, 0X44 }, /* -44.0db */ + { 0X00, 0X6D, 0X80, 0X60 }, /* -43.5db */ + { 0X00, 0X73, 0XFD, 0X65 }, /* -43.0db */ + { 0X00, 0X7A, 0XDC, 0XD7 }, /* -42.5db */ + { 0X00, 0X82, 0X24, 0X8A }, /* -42.0db */ + { 0X00, 0X89, 0XDA, 0XAB }, /* -41.5db */ + { 0X00, 0X92, 0X05, 0XC6 }, /* -41.0db */ + { 0X00, 0X9A, 0XAC, 0XC8 }, /* -40.5db */ + { 0X00, 0XA3, 0XD7, 0X0A }, /* -40.0db */ + { 0X00, 0XAD, 0X8C, 0X52 }, /* -39.5db */ + { 0X00, 0XB7, 0XD4, 0XDD }, /* -39.0db */ + { 0X00, 0XC2, 0XB9, 0X65 }, /* -38.5db */ + { 0X00, 0XCE, 0X43, 0X28 }, /* -38.0db */ + { 0X00, 0XDA, 0X7B, 0XF1 }, /* -37.5db */ + { 0X00, 0XE7, 0X6E, 0X1E }, /* -37.0db */ + { 0X00, 0XF5, 0X24, 0XAC }, /* -36.5db */ + { 0X01, 0X03, 0XAB, 0X3D }, /* -36.0db */ + { 0X01, 0X13, 0X0E, 0X24 }, /* -35.5db */ + { 0X01, 0X23, 0X5A, 0X71 }, /* -35.0db */ + { 0X01, 0X34, 0X9D, 0XF8 }, /* -34.5db */ + { 0X01, 0X46, 0XE7, 0X5D }, /* -34.0db */ + { 0X01, 0X5A, 0X46, 0X27 }, /* -33.5db */ + { 0X01, 0X6E, 0XCA, 0XC5 }, /* -33.0db */ + { 0X01, 0X84, 0X86, 0X9F }, /* -32.5db */ + { 0X01, 0X9B, 0X8C, 0X27 }, /* -32.0db */ + { 0X01, 0XB3, 0XEE, 0XE5 }, /* -31.5db */ + { 0X01, 0XCD, 0XC3, 0X8C }, /* -31.0db */ + { 0X01, 0XE9, 0X20, 0X05 }, /* -30.5db */ + { 0X02, 0X06, 0X1B, 0X89 }, /* -30.0db */ + { 0X02, 0X24, 0XCE, 0XB0 }, /* -29.5db */ + { 0X02, 0X45, 0X53, 0X85 }, /* -29.0db */ + { 0X02, 0X67, 0XC5, 0XA2 }, /* -28.5db */ + { 0X02, 0X8C, 0X42, 0X3F }, /* -28.0db */ + { 0X02, 0XB2, 0XE8, 0X55 }, /* -27.5db */ + { 0X02, 0XDB, 0XD8, 0XAD }, /* -27.0db */ + { 0X03, 0X07, 0X36, 0X05 }, /* -26.5db */ + { 0X03, 0X35, 0X25, 0X29 }, /* -26.0db */ + { 0X03, 0X65, 0XCD, 0X13 }, /* -25.5db */ + { 0X03, 0X99, 0X57, 0X0C }, /* -25.0db */ + { 0X03, 0XCF, 0XEE, 0XCF }, /* -24.5db */ + { 0X04, 0X09, 0XC2, 0XB0 }, /* -24.0db */ + { 0X04, 0X47, 0X03, 0XC1 }, /* -23.5db */ + { 0X04, 0X87, 0XE5, 0XFB }, /* -23.0db */ + { 0X04, 0XCC, 0XA0, 0X6D }, /* -22.5db */ + { 0X05, 0X15, 0X6D, 0X68 }, /* -22.0db */ + { 0X05, 0X62, 0X8A, 0XB3 }, /* -21.5db */ + { 0X05, 0XB4, 0X39, 0XBC }, /* -21.0db */ + { 0X06, 0X0A, 0XBF, 0XD4 }, /* -20.5db */ + { 0X06, 0X66, 0X66, 0X66 }, /* -20.0db */ + { 0X06, 0XC7, 0X7B, 0X36 }, /* -19.5db */ + { 0X07, 0X2E, 0X50, 0XA6 }, /* -19.0db */ + { 0X07, 0X9B, 0X3D, 0XF6 }, /* -18.5db */ + { 0X08, 0X0E, 0X9F, 0X96 }, /* -18.0db */ + { 0X08, 0X88, 0XD7, 0X6D }, /* -17.5db */ + { 0X09, 0X0A, 0X4D, 0X2F }, /* -17.0db */ + { 0X09, 0X93, 0X6E, 0XB8 }, /* -16.5db */ + { 0X0A, 0X24, 0XB0, 0X62 }, /* -16.0db */ + { 0X0A, 0XBE, 0X8D, 0X70 }, /* -15.5db */ + { 0X0B, 0X61, 0X88, 0X71 }, /* -15.0db */ + { 0X0C, 0X0E, 0X2B, 0XB0 }, /* -14.5db */ + { 0X0C, 0XC5, 0X09, 0XAB }, /* -14.0db */ + { 0X0D, 0X86, 0XBD, 0X8D }, /* -13.5db */ + { 0X0E, 0X53, 0XEB, 0XB3 }, /* -13.0db */ + { 0X0F, 0X2D, 0X42, 0X38 }, /* -12.5db */ + { 0X10, 0X13, 0X79, 0X87 }, /* -12.0db */ + { 0X11, 0X07, 0X54, 0XF9 }, /* -11.5db */ + { 0X12, 0X09, 0XA3, 0X7A }, /* -11.0db */ + { 0X13, 0X1B, 0X40, 0X39 }, /* -10.5db */ + { 0X14, 0X3D, 0X13, 0X62 }, /* -10.0db */ + { 0X15, 0X70, 0X12, 0XE1 }, /* -9.5db */ + { 0X16, 0XB5, 0X43, 0X37 }, /* -9.0db */ + { 0X18, 0X0D, 0XB8, 0X54 }, /* -8.5db */ + { 0X19, 0X7A, 0X96, 0X7F }, /* -8.0db */ + { 0X1A, 0XFD, 0X13, 0X54 }, /* -7.5db */ + { 0X1C, 0X96, 0X76, 0XC6 }, /* -7.0db */ + { 0X1E, 0X48, 0X1C, 0X37 }, /* -6.5db */ + { 0X20, 0X13, 0X73, 0X9E }, /* -6.0db */ + { 0X21, 0XFA, 0X02, 0XBF }, /* -5.5db */ + { 0X23, 0XFD, 0X66, 0X78 }, /* -5.0db */ + { 0X26, 0X1F, 0X54, 0X1C }, /* -4.5db */ + { 0X28, 0X61, 0X9A, 0XE9 }, /* -4.0db */ + { 0X2A, 0XC6, 0X25, 0X91 }, /* -3.5db */ + { 0X2D, 0X4E, 0XFB, 0XD5 }, /* -3.0db */ + { 0X2F, 0XFE, 0X44, 0X48 }, /* -2.5db */ + { 0X32, 0XD6, 0X46, 0X17 }, /* -2.0db */ + { 0X35, 0XD9, 0X6B, 0X02 }, /* -1.5db */ + { 0X39, 0X0A, 0X41, 0X5F }, /* -1.0db */ + { 0X3C, 0X6B, 0X7E, 0X4F }, /* -0.5db */ + { 0X40, 0X00, 0X00, 0X00 }, /* 0.0db */ + { 0X43, 0XCA, 0XD0, 0X22 }, /* 0.5db */ + { 0X47, 0XCF, 0X26, 0X7D }, /* 1.0db */ + { 0X4C, 0X10, 0X6B, 0XA5 }, /* 1.5db */ + { 0X50, 0X92, 0X3B, 0XE3 }, /* 2.0db */ + { 0X55, 0X58, 0X6A, 0X46 }, /* 2.5db */ + { 0X5A, 0X67, 0X03, 0XDF }, /* 3.0db */ + { 0X5F, 0XC2, 0X53, 0X32 }, /* 3.5db */ + { 0X65, 0X6E, 0XE3, 0XDB }, /* 4.0db */ + { 0X6B, 0X71, 0X86, 0X68 }, /* 4.5db */ + { 0X71, 0XCF, 0X54, 0X71 }, /* 5.0db */ + { 0X78, 0X8D, 0XB4, 0XE9 }, /* 5.5db */ + { 0XFF, 0XFF, 0XFF, 0XFF }, /* 6.0db */ +}; #endif diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index a43ad6dcb7c7..18161d02a96f 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -50,6 +50,7 @@ #define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E) /* Volume control */ +#define TAS2563_DVC_LVL TASDEVICE_REG(0x00, 0x02, 0x0C) #define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A) #define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) #define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index d5d95ae57c73..e79d613745b4 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -31,6 +31,7 @@ #include #include #include +#include static const struct i2c_device_id tasdevice_id[] = { { "tas2563", TAS2563 }, @@ -140,6 +141,101 @@ static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static int tas2563_digital_gain_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + unsigned int l = 0, r = mc->max; + unsigned int target, ar_mid, mid, ar_l, ar_r; + unsigned int reg = mc->reg; + unsigned char data[4]; + int ret; + + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + target = get_unaligned_be32(&data[0]); + + while (r > 1 + l) { + mid = (l + r) / 2; + ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]); + if (target < ar_mid) + r = mid; + else + l = mid; + } + + ar_l = get_unaligned_be32(tas2563_dvc_table[l]); + ar_r = get_unaligned_be32(tas2563_dvc_table[r]); + + /* find out the member same as or closer to the current volume */ + ucontrol->value.integer.value[0] = + abs(target - ar_l) <= abs(target - ar_r) ? l : r; +out: + mutex_unlock(&tas_dev->codec_lock); + return 0; +} + +static int tas2563_digital_gain_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int vol = ucontrol->value.integer.value[0]; + int status = 0, max = mc->max, rc = 1; + int i, ret; + unsigned int reg = mc->reg; + unsigned int volrd, volwr; + unsigned char data[4]; + + vol = clamp(vol, 0, max); + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + rc = -1; + goto out; + } + + volrd = get_unaligned_be32(&data[0]); + volwr = get_unaligned_be32(tas2563_dvc_table[vol]); + + if (volrd == volwr) { + rc = 0; + goto out; + } + + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_bulk_write(tas_dev, i, reg, + (unsigned char *)tas2563_dvc_table[vol], 4); + if (ret) { + dev_err(tas_dev->dev, + "%s, set digital vol error in dev %d\n", + __func__, i); + status |= BIT(i); + } + } + + if (status) + rc = -1; +out: + mutex_unlock(&tas_dev->codec_lock); + return rc; +} + static const struct snd_kcontrol_new tasdevice_snd_controls[] = { SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, tasdev_force_fwload_get, tasdev_force_fwload_put), @@ -154,6 +250,13 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { tas2781_digital_putvol, dvc_tlv), }; +static const struct snd_kcontrol_new tas2563_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0, + 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0, + tas2563_digital_gain_get, tas2563_digital_gain_put, + tas2563_dvc_tlv), +}; + static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -593,17 +696,25 @@ static struct snd_soc_dai_driver tasdevice_dai_driver[] = { static int tasdevice_codec_probe(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct snd_kcontrol_new *p; + unsigned int size; int rc; - if (tas_priv->chip_id == TAS2781) { - rc = snd_soc_add_component_controls(codec, - tas2781_snd_controls, - ARRAY_SIZE(tas2781_snd_controls)); - if (rc < 0) { - dev_err(tas_priv->dev, "%s: Add control err rc = %d", - __func__, rc); - return rc; - } + switch (tas_priv->chip_id) { + case TAS2781: + p = (struct snd_kcontrol_new *)tas2781_snd_controls; + size = ARRAY_SIZE(tas2781_snd_controls); + break; + default: + p = (struct snd_kcontrol_new *)tas2563_snd_controls; + size = ARRAY_SIZE(tas2563_snd_controls); + } + + rc = snd_soc_add_component_controls(codec, p, size); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: Add control err rc = %d", + __func__, rc); + return rc; } tas_priv->name_prefix = codec->name_prefix; -- cgit v1.2.3