Re: [PATCH] usb: cdnsp: : add scatter gather support for ISOC endpoint

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 22-12-22 04:09:34, Pawel Laszczak wrote:
> Patch implements scatter gather support for isochronous endpoint.
> This fix is forced by 'commit e81e7f9a0eb9
> ("usb: gadget: uvc: add scatter gather support")'.
> After this fix CDNSP driver stop working with UVC class.
> 
> cc: <stable@xxxxxxxxxxxxxxx>
> Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
> Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx>
> ---
>  drivers/usb/cdns3/cdnsp-gadget.c |   2 +-
>  drivers/usb/cdns3/cdnsp-gadget.h |   4 +-
>  drivers/usb/cdns3/cdnsp-ring.c   | 110 +++++++++++++++++--------------
>  3 files changed, 63 insertions(+), 53 deletions(-)
> 
> diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
> index a8640516c895..e81dca0e62a8 100644
> --- a/drivers/usb/cdns3/cdnsp-gadget.c
> +++ b/drivers/usb/cdns3/cdnsp-gadget.c
> @@ -382,7 +382,7 @@ int cdnsp_ep_enqueue(struct cdnsp_ep *pep, struct cdnsp_request *preq)
>  		ret = cdnsp_queue_bulk_tx(pdev, preq);
>  		break;
>  	case USB_ENDPOINT_XFER_ISOC:
> -		ret = cdnsp_queue_isoc_tx_prepare(pdev, preq);
> +		ret = cdnsp_queue_isoc_tx(pdev, preq);
>  	}
>  
>  	if (ret)
> diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h
> index f740fa6089d8..e1b5801fdddf 100644
> --- a/drivers/usb/cdns3/cdnsp-gadget.h
> +++ b/drivers/usb/cdns3/cdnsp-gadget.h
> @@ -1532,8 +1532,8 @@ void cdnsp_queue_stop_endpoint(struct cdnsp_device *pdev,
>  			       unsigned int ep_index);
>  int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq);
>  int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq);
> -int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev,
> -				struct cdnsp_request *preq);
> +int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
> +			struct cdnsp_request *preq);

Why you re-name this function?

Other changes are ok for me.

Peter

>  void cdnsp_queue_configure_endpoint(struct cdnsp_device *pdev,
>  				    dma_addr_t in_ctx_ptr);
>  void cdnsp_queue_reset_ep(struct cdnsp_device *pdev, unsigned int ep_index);
> diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
> index b23e543b3a3d..07f6068342d4 100644
> --- a/drivers/usb/cdns3/cdnsp-ring.c
> +++ b/drivers/usb/cdns3/cdnsp-ring.c
> @@ -1333,6 +1333,20 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
>  					 ep_ring->dequeue, td->last_trb,
>  					 ep_trb_dma);
>  
> +		desc = td->preq->pep->endpoint.desc;
> +
> +		if (ep_seg) {
> +			ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
> +					       / sizeof(*ep_trb)];
> +
> +			trace_cdnsp_handle_transfer(ep_ring,
> +					(struct cdnsp_generic_trb *)ep_trb);
> +
> +			if (pep->skip && usb_endpoint_xfer_isoc(desc) &&
> +			    td->last_trb != ep_trb)
> +				return -EAGAIN;
> +		}
> +
>  		/*
>  		 * Skip the Force Stopped Event. The event_trb(ep_trb_dma)
>  		 * of FSE is not in the current TD pointed by ep_ring->dequeue
> @@ -1347,7 +1361,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
>  			goto cleanup;
>  		}
>  
> -		desc = td->preq->pep->endpoint.desc;
>  		if (!ep_seg) {
>  			if (!pep->skip || !usb_endpoint_xfer_isoc(desc)) {
>  				/* Something is busted, give up! */
> @@ -1374,12 +1387,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
>  			goto cleanup;
>  		}
>  
> -		ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
> -				       / sizeof(*ep_trb)];
> -
> -		trace_cdnsp_handle_transfer(ep_ring,
> -					    (struct cdnsp_generic_trb *)ep_trb);
> -
>  		if (cdnsp_trb_is_noop(ep_trb))
>  			goto cleanup;
>  
> @@ -1726,11 +1733,6 @@ static unsigned int count_sg_trbs_needed(struct cdnsp_request *preq)
>  	return num_trbs;
>  }
>  
> -static unsigned int count_isoc_trbs_needed(struct cdnsp_request *preq)
> -{
> -	return cdnsp_count_trbs(preq->request.dma, preq->request.length);
> -}
> -
>  static void cdnsp_check_trb_math(struct cdnsp_request *preq, int running_total)
>  {
>  	if (running_total != preq->request.length)
> @@ -2192,28 +2194,48 @@ static unsigned int
>  }
>  
>  /* Queue function isoc transfer */
> -static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
> -			       struct cdnsp_request *preq)
> +int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
> +			struct cdnsp_request *preq)
>  {
> -	int trb_buff_len, td_len, td_remain_len, ret;
> +	unsigned int trb_buff_len, td_len, td_remain_len, block_len;
>  	unsigned int burst_count, last_burst_pkt;
>  	unsigned int total_pkt_count, max_pkt;
>  	struct cdnsp_generic_trb *start_trb;
> +	struct scatterlist *sg = NULL;
>  	bool more_trbs_coming = true;
>  	struct cdnsp_ring *ep_ring;
> +	unsigned int num_sgs = 0;
>  	int running_total = 0;
>  	u32 field, length_field;
> +	u64 addr, send_addr;
>  	int start_cycle;
>  	int trbs_per_td;
> -	u64 addr;
> -	int i;
> +	int i, sent_len, ret;
>  
>  	ep_ring = preq->pep->ring;
> +
> +	td_len = preq->request.length;
> +
> +	if (preq->request.num_sgs) {
> +		num_sgs = preq->request.num_sgs;
> +		sg = preq->request.sg;
> +		addr = (u64)sg_dma_address(sg);
> +		block_len = sg_dma_len(sg);
> +		trbs_per_td = count_sg_trbs_needed(preq);
> +	} else {
> +		addr = (u64)preq->request.dma;
> +		block_len = td_len;
> +		trbs_per_td = count_trbs_needed(preq);
> +	}
> +
> +	ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
> +	if (ret)
> +		return ret;
> +
>  	start_trb = &ep_ring->enqueue->generic;
>  	start_cycle = ep_ring->cycle_state;
> -	td_len = preq->request.length;
> -	addr = (u64)preq->request.dma;
>  	td_remain_len = td_len;
> +	send_addr = addr;
>  
>  	max_pkt = usb_endpoint_maxp(preq->pep->endpoint.desc);
>  	total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
> @@ -2225,11 +2247,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
>  	burst_count = cdnsp_get_burst_count(pdev, preq, total_pkt_count);
>  	last_burst_pkt = cdnsp_get_last_burst_packet_count(pdev, preq,
>  							   total_pkt_count);
> -	trbs_per_td = count_isoc_trbs_needed(preq);
> -
> -	ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
> -	if (ret)
> -		goto cleanup;
>  
>  	/*
>  	 * Set isoc specific data for the first TRB in a TD.
> @@ -2248,6 +2265,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
>  
>  		/* Calculate TRB length. */
>  		trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
> +		trb_buff_len = min(trb_buff_len, block_len);
>  		if (trb_buff_len > td_remain_len)
>  			trb_buff_len = td_remain_len;
>  
> @@ -2256,7 +2274,8 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
>  					       trb_buff_len, td_len, preq,
>  					       more_trbs_coming, 0);
>  
> -		length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
> +		length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) |
> +			TRB_INTR_TARGET(0);
>  
>  		/* Only first TRB is isoc, overwrite otherwise. */
>  		if (i) {
> @@ -2281,12 +2300,27 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
>  		}
>  
>  		cdnsp_queue_trb(pdev, ep_ring, more_trbs_coming,
> -				lower_32_bits(addr), upper_32_bits(addr),
> +				lower_32_bits(send_addr), upper_32_bits(send_addr),
>  				length_field, field);
>  
>  		running_total += trb_buff_len;
>  		addr += trb_buff_len;
>  		td_remain_len -= trb_buff_len;
> +
> +		sent_len = trb_buff_len;
> +		while (sg && sent_len >= block_len) {
> +			/* New sg entry */
> +			--num_sgs;
> +			sent_len -= block_len;
> +			if (num_sgs != 0) {
> +				sg = sg_next(sg);
> +				block_len = sg_dma_len(sg);
> +				addr = (u64)sg_dma_address(sg);
> +				addr += sent_len;
> +			}
> +		}
> +		block_len -= sent_len;
> +		send_addr = addr;
>  	}
>  
>  	/* Check TD length */
> @@ -2324,30 +2358,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
>  	return ret;
>  }
>  
> -int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev,
> -				struct cdnsp_request *preq)
> -{
> -	struct cdnsp_ring *ep_ring;
> -	u32 ep_state;
> -	int num_trbs;
> -	int ret;
> -
> -	ep_ring = preq->pep->ring;
> -	ep_state = GET_EP_CTX_STATE(preq->pep->out_ctx);
> -	num_trbs = count_isoc_trbs_needed(preq);
> -
> -	/*
> -	 * Check the ring to guarantee there is enough room for the whole
> -	 * request. Do not insert any td of the USB Request to the ring if the
> -	 * check failed.
> -	 */
> -	ret = cdnsp_prepare_ring(pdev, ep_ring, ep_state, num_trbs, GFP_ATOMIC);
> -	if (ret)
> -		return ret;
> -
> -	return cdnsp_queue_isoc_tx(pdev, preq);
> -}
> -
>  /****		Command Ring Operations		****/
>  /*
>   * Generic function for queuing a command TRB on the command ring.
> -- 
> 2.25.1
> 

-- 

Thanks,
Peter Chen



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux