diff options
author | Howard Yen <howardyen@google.com> | 2021-06-30 15:03:21 +0800 |
---|---|---|
committer | Howard Yen <howardyen@google.com> | 2021-07-09 02:53:46 +0000 |
commit | a3e1882fe92c403777328ecca4bdd8edd3bcc65a (patch) | |
tree | 774c6afb66a0d1b6f37c9996dd053e43f360e7fd | |
parent | 8bdcf7e55ea861c753ea5eae5993837caae0a59b (diff) | |
download | aoc-a3e1882fe92c403777328ecca4bdd8edd3bcc65a.tar.gz |
aoc: usb: use DRAM for usb audio offload
Bug: 192326094
Test: build and boot pass
Change-Id: Ie3d333a0494f0131fffd0e013a17941b06ab0f1c
Signed-off-by: Howard Yen <howardyen@google.com>
-rw-r--r-- | usb/aoc_usb.h | 5 | ||||
-rw-r--r-- | usb/aoc_usb_dev.c | 72 | ||||
-rw-r--r-- | usb/xhci_hooks_impl_whi.c | 97 |
3 files changed, 156 insertions, 18 deletions
diff --git a/usb/aoc_usb.h b/usb/aoc_usb.h index b3115bd..89bfb52 100644 --- a/usb/aoc_usb.h +++ b/usb/aoc_usb.h @@ -23,9 +23,11 @@ extern bool aoc_usb_probe_done; enum aoc_usb_msg { SYNC_DEVICE_CONTEXT, GET_DCBAA_PTR, + SET_DCBAA_PTR, GET_TR_DEQUEUE_PTR, SETUP_DONE, GET_ISOC_TR_INFO, + SET_ISOC_TR_INFO, SYNC_CONN_STAT }; @@ -36,7 +38,8 @@ enum aoc_usb_state { enum usb_offload_op_mode { USB_OFFLOAD_STOP, - USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY + USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY, + USB_OFFLOAD_DRAM }; enum usb_recover_state { diff --git a/usb/aoc_usb_dev.c b/usb/aoc_usb_dev.c index 9e331e7..ccfae9c 100644 --- a/usb/aoc_usb_dev.c +++ b/usb/aoc_usb_dev.c @@ -44,6 +44,8 @@ static ssize_t aoc_usb_send_command(struct aoc_usb_drvdata *drvdata, if (aoc_service_flush_read_data(adev)) dev_err(&drvdata->adev->dev ,"Previous response left in channel\n"); + dev_dbg(&drvdata->adev->dev, "send cmd id [%u]\n", ((struct CMD_CORE_GENERIC *)in_cmd)->parent.id); + ret = aoc_service_write_timeout(adev, in_cmd, in_size, drvdata->service_timeout); if (ret != in_size) { ret = -EIO; @@ -118,6 +120,33 @@ static int aoc_usb_get_dcbaa_ptr(struct aoc_usb_drvdata *drvdata, return 0; } +static int aoc_usb_set_dcbaa_ptr(struct aoc_usb_drvdata *drvdata, + u64 *aoc_dcbaa_ptr) +{ + int ret = 0; + struct CMD_USB_CONTROL_GET_DCBAA_PTR *cmd; + + // TODO(b/192858107): Create a CMD_USB_CONTROL_SET_DCBAA_PTR instead. + cmd = kzalloc(sizeof(struct CMD_USB_CONTROL_GET_DCBAA_PTR), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + AocCmdHdrSet(&cmd->parent, + CMD_USB_CONTROL_GET_DCBAA_PTR_ID, + sizeof(*cmd)); + + cmd->aoc_dcbaa_ptr = *aoc_dcbaa_ptr; + ret = aoc_usb_send_command(drvdata, cmd, sizeof(*cmd), cmd, sizeof(*cmd)); + if (ret < 0) { + kfree(cmd); + return ret; + } + + kfree(cmd); + + return 0; +} + static int aoc_usb_setup_done(struct aoc_usb_drvdata *drvdata) { int ret; @@ -214,6 +243,43 @@ static int aoc_usb_get_isoc_tr_info(struct aoc_usb_drvdata *drvdata, void *args) return 0; } +static int aoc_usb_set_isoc_tr_info(struct aoc_usb_drvdata *drvdata, void *args) +{ + int ret; + struct get_isoc_tr_info_args *tr_info_args = + (struct get_isoc_tr_info_args *)args; + struct CMD_USB_CONTROL_GET_ISOC_TR_INFO *cmd; + + // TODO(b/192858107): Create a CMD_USB_CONTROL_SET_ISOC_TR_INFO instead. + cmd = kzalloc(sizeof(struct CMD_USB_CONTROL_GET_ISOC_TR_INFO), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + AocCmdHdrSet(&cmd->parent, + CMD_USB_CONTROL_GET_ISOC_TR_INFO_ID, + sizeof(*cmd)); + + cmd->ep_id = tr_info_args->ep_id; + cmd->dir = tr_info_args->dir; + cmd->type = tr_info_args->type; + cmd->num_segs = tr_info_args->num_segs; + cmd->seg_ptr = tr_info_args->seg_ptr; + cmd->max_packet = tr_info_args->max_packet; + cmd->cycle_state = tr_info_args->cycle_state; + cmd->num_trbs_free = tr_info_args->num_trbs_free; + + dev_dbg(&drvdata->adev->dev, "%s: ep_id=%u, dir=%u\n", __func__, cmd->ep_id, cmd->dir); + ret = aoc_usb_send_command(drvdata, cmd, sizeof(*cmd), cmd, sizeof(*cmd)); + if (ret < 0) { + kfree(cmd); + return ret; + } + + kfree(cmd); + + return 0; +} + static int aoc_usb_notify(struct notifier_block *this, unsigned long code, void *data) { @@ -232,12 +298,18 @@ static int aoc_usb_notify(struct notifier_block *this, case GET_DCBAA_PTR: ret = aoc_usb_get_dcbaa_ptr(drvdata, data); break; + case SET_DCBAA_PTR: + ret = aoc_usb_set_dcbaa_ptr(drvdata, data); + break; case SETUP_DONE: ret = aoc_usb_setup_done(drvdata); break; case GET_ISOC_TR_INFO: ret = aoc_usb_get_isoc_tr_info(drvdata, data); break; + case SET_ISOC_TR_INFO: + ret = aoc_usb_set_isoc_tr_info(drvdata, data); + break; case SYNC_CONN_STAT: ret = aoc_usb_notify_conn_stat(drvdata, data); break; diff --git a/usb/xhci_hooks_impl_whi.c b/usb/xhci_hooks_impl_whi.c index 2336a0f..d74a18f 100644 --- a/usb/xhci_hooks_impl_whi.c +++ b/usb/xhci_hooks_impl_whi.c @@ -100,6 +100,13 @@ static int xhci_get_dcbaa_ptr(u64 *aoc_dcbaa_ptr) return 0; } +static int xhci_set_dcbaa_ptr(u64 aoc_dcbaa_ptr) +{ + blocking_notifier_call_chain(&aoc_usb_notifier_list, SET_DCBAA_PTR, + &aoc_dcbaa_ptr); + return 0; +} + static int xhci_setup_done(void) { blocking_notifier_call_chain(&aoc_usb_notifier_list, SETUP_DONE, NULL); @@ -131,6 +138,25 @@ static int xhci_get_isoc_tr_info(u16 ep_id, u16 dir, struct xhci_ring *ep_ring) return 0; } +static int xhci_set_isoc_tr_info(u16 ep_id, u16 dir, struct xhci_ring *ep_ring) +{ + struct get_isoc_tr_info_args tr_info; + + tr_info.ep_id = ep_id; + tr_info.dir = dir; + tr_info.num_segs = ep_ring->num_segs; + tr_info.max_packet = ep_ring->bounce_buf_len; + tr_info.type = ep_ring->type; + tr_info.seg_ptr = ep_ring->first_seg->dma; + tr_info.cycle_state = ep_ring->cycle_state; + tr_info.num_trbs_free = ep_ring->num_trbs_free; + + blocking_notifier_call_chain(&aoc_usb_notifier_list, SET_ISOC_TR_INFO, + &tr_info); + + return 0; +} + static bool is_compatible_with_usb_audio_offload(struct usb_device *udev) { struct usb_endpoint_descriptor *epd; @@ -183,7 +209,7 @@ static int sync_dev_ctx(struct xhci_hcd *xhci, unsigned int slot_id) struct xhci_vendor_data *vendor_data = xhci_to_priv(xhci)->vendor_data; int ret = 0; - if (vendor_data->op_mode != USB_OFFLOAD_STOP) + if (vendor_data->op_mode == USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) ret = xhci_sync_dev_ctx(xhci, slot_id); return ret; @@ -284,7 +310,9 @@ static int xhci_udev_notify(struct notifier_block *self, unsigned long action, "Compatible with usb audio offload\n"); xhci_reset_for_usb_audio_offload(udev); if (vendor_data->op_mode == - USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) { + USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY || + vendor_data->op_mode == + USB_OFFLOAD_DRAM) { xhci_sync_conn_stat(USB_CONNECTED); } } @@ -292,8 +320,10 @@ static int xhci_udev_notify(struct notifier_block *self, unsigned long action, break; case USB_DEVICE_REMOVE: if (is_compatible_with_usb_audio_offload(udev) && - vendor_data->op_mode == - USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) { + (vendor_data->op_mode == + USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY || + vendor_data->op_mode == + USB_OFFLOAD_DRAM)) { xhci_sync_conn_stat(USB_DISCONNECTED); } vendor_data->usb_accessory_enabled = false; @@ -389,16 +419,14 @@ static int xhci_vendor_init_irq_workqueue(struct xhci_vendor_data *vendor_data) { vendor_data->irq_wq = alloc_workqueue("xhci_vendor_irq_work", WQ_UNBOUND, 0); - if (!vendor_data->irq_wq) { + if (!vendor_data->irq_wq) return -ENOMEM; - } INIT_WORK(&vendor_data->xhci_vendor_irq_work, xhci_vendor_irq_work); return 0; } - static struct xhci_ring * xhci_initialize_ring_info_for_remote_isoc(struct xhci_hcd *xhci, u32 endpoint_type, @@ -477,7 +505,7 @@ static int usb_audio_offload_init(struct xhci_hcd *xhci) mutex_init(&vendor_data->lock); INIT_WORK(&vendor_data->xhci_vendor_reset_ws, xhci_reset_work); usb_register_notify(&xhci_udev_nb); - vendor_data->op_mode = USB_OFFLOAD_STOP; + vendor_data->op_mode = USB_OFFLOAD_DRAM; vendor_data->xhci = xhci; xhci_to_priv(xhci)->vendor_data = vendor_data; @@ -548,8 +576,13 @@ static bool is_usb_offload_enabled(struct xhci_hcd *xhci, if (global_enabled) { ep_ring = vdev->eps[ep_index].ring; - if (is_dma_for_offload(ep_ring->first_seg->dma)) - return true; + if (vendor_data->op_mode == USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) { + if (is_dma_for_offload(ep_ring->first_seg->dma)) + return true; + } else if (vendor_data->op_mode == USB_OFFLOAD_DRAM) { + if (ep_ring->type == TYPE_ISOC) + return true; + } } return false; @@ -590,6 +623,9 @@ static irqreturn_t queue_irq_work(struct xhci_hcd *xhci) struct xhci_transfer_event *event; u32 trb_comp_code; + if (vendor_data->op_mode != USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) + return IRQ_NONE; + if (is_usb_offload_enabled(xhci, NULL, 0)) { event = &xhci->event_ring->dequeue->trans_event; trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); @@ -623,7 +659,21 @@ static struct xhci_device_context_array *alloc_dcbaa(struct xhci_hcd *xhci, } xhci_setup_done(); - xhci_dbg(xhci, "write dcbaa_ptr=%llx\n", xhci->dcbaa->dma); + xhci_dbg(xhci, "Get dcbaa_ptr=%llx\n", xhci->dcbaa->dma); + } else if (vendor_data->op_mode == USB_OFFLOAD_DRAM) { + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), + &dma, flags); + if (!xhci->dcbaa) + return NULL; + + xhci->dcbaa->dma = dma; + if (xhci_set_dcbaa_ptr(xhci->dcbaa->dma) != 0) { + xhci_err(xhci, "Set DCBAA pointer failed\n"); + return NULL; + } + xhci_setup_done(); + + xhci_dbg(xhci, "Set dcbaa_ptr=%llx to AoC\n", xhci->dcbaa->dma); } else { xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags); @@ -658,9 +708,22 @@ static struct xhci_ring *alloc_transfer_ring(struct xhci_hcd *xhci, u32 endpoint_type, enum xhci_ring_type ring_type, unsigned int max_packet, gfp_t mem_flags) { - return xhci_initialize_ring_info_for_remote_isoc(xhci, endpoint_type, + struct xhci_vendor_data *vendor_data = xhci_to_priv(xhci)->vendor_data; + struct xhci_ring *ep_ring; + u16 dir; + + if (vendor_data->op_mode == USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY) { + ep_ring = xhci_initialize_ring_info_for_remote_isoc(xhci, endpoint_type, ring_type, max_packet, mem_flags); + } else { + ep_ring = xhci_ring_alloc(xhci, 1, 1, ring_type, max_packet, mem_flags); + dir = endpoint_type == ISOC_IN_EP ? 0 : 1; + + xhci_set_isoc_tr_info(0, dir, ep_ring); + } + + return ep_ring; } static void free_transfer_ring(struct xhci_hcd *xhci, @@ -677,10 +740,10 @@ static void free_transfer_ring(struct xhci_hcd *xhci, ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); if (ring) { - xhci_dbg(xhci, "ep_index=%u, ep_type=%u, ring type=%u\n", ep_index, + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%u, ring type=%u\n", __func__, ep_index, ep_type, ring->type); - if (vendor_data->op_mode != USB_OFFLOAD_STOP && + if (vendor_data->op_mode == USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY && ring->type == TYPE_ISOC) { kfree(ring->first_seg); kfree(virt_dev->eps[ep_index].ring); @@ -694,10 +757,10 @@ static void free_transfer_ring(struct xhci_hcd *xhci, } if (new_ring) { - xhci_dbg(xhci, "ep_index=%u, ep_type=%u, new_ring type=%u\n", ep_index, + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%u, new_ring type=%u\n", __func__, ep_index, ep_type, new_ring->type); - if (vendor_data->op_mode != USB_OFFLOAD_STOP && + if (vendor_data->op_mode == USB_OFFLOAD_SIMPLE_AUDIO_ACCESSORY && new_ring->type == TYPE_ISOC) { kfree(new_ring->first_seg); kfree(virt_dev->eps[ep_index].new_ring); @@ -723,7 +786,7 @@ static bool usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb) ep_index = (unsigned int)(usb_endpoint_num(desc)*2) + (usb_endpoint_dir_in(desc) ? 1 : 0) - 1; - xhci_dbg(xhci, "ep_index=%u, ep_type=%d\n", ep_index, ep_type); + xhci_dbg(xhci, "%s: ep_index=%u, ep_type=%d\n", __func__, ep_index, ep_type); if (is_usb_offload_enabled(xhci, vdev, ep_index)) return true; |