From: Vahram Aharonyan <vahr...@synopsys.com>

Add dwc2_gadget_start_isoc_ddma() function calls in NAK and OUTTknEPDis
interrupts to start isochronous transfer once Host has sent a token.

Add call of dwc2_gadget_fill_isoc_desc() function in
dwc2_hsotg_ep_queue() to fill descriptor chain's half that is under SW
control by queued usb requests if isochronous transfer has started
before.

Add return in dwc2_hsotg_complete_request() for DDMA isochronous
transfers not to proceed with next request starting routine.

Add call of functions dwc2_gadget_complete_isoc_request_ddma() and
dwc2_gadget_start_next_isoc_ddma() in XferCompl interrupt handler.

Add call of dwc2_gadget_start_next_isoc_ddma() in BNA interrupt handler
to restart OUT endpoint once it gets disabled due to BNA interrupt
assertion.

Signed-off-by: Vahram Aharonyan <vahr...@synopsys.com>
Signed-off-by: John Youn <johny...@synopsys.com>
---
 drivers/usb/dwc2/gadget.c | 70 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 64 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index e353147..0f1a3a8 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1280,6 +1280,22 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct 
usb_request *req,
        first = list_empty(&hs_ep->queue);
        list_add_tail(&hs_req->queue, &hs_ep->queue);
 
+       /*
+        * Handle DDMA isochronous transfers separately - just add new entry
+        * to the half of descriptor chain that is not processed by HW.
+        * Transfer will be started once SW gets either one of NAK or
+        * OutTknEpDis interrupts.
+        */
+       if (using_desc_dma(hs) && hs_ep->isochronous &&
+           hs_ep->target_frame != TARGET_FRAME_INITIAL) {
+               ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma,
+                                                hs_req->req.length);
+               if (ret)
+                       dev_dbg(hs->dev, "%s: ISO desc chain full\n", __func__);
+
+               return 0;
+       }
+
        if (first) {
                if (!hs_ep->isochronous) {
                        dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
@@ -1921,6 +1937,10 @@ static void dwc2_hsotg_complete_request(struct 
dwc2_hsotg *hsotg,
                spin_lock(&hsotg->lock);
        }
 
+       /* In DDMA don't need to proceed to starting of next ISOC request */
+       if (using_desc_dma(hsotg) && hs_ep->isochronous)
+               return;
+
        /*
         * Look to see if there is anything else to do. Note, the completion
         * of the previous request may have caused a new request to be started
@@ -2685,12 +2705,28 @@ static void 
dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
        struct dwc2_hsotg *hsotg = ep->parent;
        int dir_in = ep->dir_in;
        u32 doepmsk;
+       u32 tmp;
 
        if (dir_in || !ep->isochronous)
                return;
 
+       /*
+        * Store frame in which irq was asserted here, as
+        * it can change while completing request below.
+        */
+       tmp = dwc2_hsotg_read_frameno(hsotg);
+
        dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), -ENODATA);
 
+       if (using_desc_dma(hsotg)) {
+               if (ep->target_frame == TARGET_FRAME_INITIAL) {
+                       /* Start first ISO Out */
+                       ep->target_frame = tmp;
+                       dwc2_gadget_start_isoc_ddma(ep);
+               }
+               return;
+       }
+
        if (ep->interval > 1 &&
            ep->target_frame == TARGET_FRAME_INITIAL) {
                u32 dsts;
@@ -2739,6 +2775,12 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep 
*hs_ep)
 
        if (hs_ep->target_frame == TARGET_FRAME_INITIAL) {
                hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
+
+               if (using_desc_dma(hsotg)) {
+                       dwc2_gadget_start_isoc_ddma(hs_ep);
+                       return;
+               }
+
                if (hs_ep->interval > 1) {
                        u32 ctrl = dwc2_readl(hsotg->regs +
                                              DIEPCTL(hs_ep->index));
@@ -2800,11 +2842,17 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, 
unsigned int idx,
                        __func__, dwc2_readl(hsotg->regs + epctl_reg),
                        dwc2_readl(hsotg->regs + epsiz_reg));
 
-               /*
-                * we get OutDone from the FIFO, so we only need to look
-                * at completing IN requests here
-                */
-               if (dir_in) {
+               /* In DDMA handle isochronous requests separately */
+               if (using_desc_dma(hsotg) && hs_ep->isochronous) {
+                       dwc2_gadget_complete_isoc_request_ddma(hs_ep);
+                       /* Try to start next isoc request */
+                       dwc2_gadget_start_next_isoc_ddma(hs_ep);
+               } else if (dir_in) {
+                       /*
+                        * We get OutDone from the FIFO, so we only
+                        * need to look at completing IN requests here
+                        * if operating slave mode
+                        */
                        if (hs_ep->isochronous && hs_ep->interval > 1)
                                dwc2_gadget_incr_frame_num(hs_ep);
 
@@ -2867,9 +2915,19 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, 
unsigned int idx,
        if (ints & DXEPINT_BACK2BACKSETUP)
                dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
 
-       if (ints & DXEPINT_BNAINTR)
+       if (ints & DXEPINT_BNAINTR) {
                dev_dbg(hsotg->dev, "%s: BNA interrupt\n", __func__);
 
+               /*
+                * Try to start next isoc request, if any.
+                * Sometimes the endpoint remains enabled after BNA interrupt
+                * assertion, which is not expected, hence we can enter here
+                * couple of times.
+                */
+               if (hs_ep->isochronous)
+                       dwc2_gadget_start_next_isoc_ddma(hs_ep);
+       }
+
        if (dir_in && !hs_ep->isochronous) {
                /* not sure if this is important, but we'll clear it anyway */
                if (ints & DXEPINT_INTKNTXFEMP) {
-- 
2.10.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to