Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8743216 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "sound: usb: Add support to handle QMI client disconnect"

parents b6d823c8 f054b81f
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -1789,6 +1789,8 @@ void xhci_free_command(struct xhci_hcd *xhci,
int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
{
	int size;
	u32 iman_reg;
	u64 erdp_reg;
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct device	*dev = xhci_to_hcd(xhci)->self.controller;

@@ -1800,14 +1802,38 @@ int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)

	size =
	sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries);
	if (xhci->sec_erst[intr_num].entries)
	if (xhci->sec_erst[intr_num].entries) {
		/*
		 * disable irq, ack pending interrupt and clear EHB for xHC to
		 * generate interrupt again when new event ring is setup
		 */
		iman_reg =
			readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
		iman_reg &= ~IMAN_IE;
		writel_relaxed(iman_reg,
				&xhci->sec_ir_set[intr_num]->irq_pending);
		iman_reg =
			readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
		if (iman_reg & IMAN_IP)
			writel_relaxed(iman_reg,
				&xhci->sec_ir_set[intr_num]->irq_pending);
		/* make sure IP gets cleared before clearing EHB */
		mb();

		erdp_reg = xhci_read_64(xhci,
				&xhci->sec_ir_set[intr_num]->erst_dequeue);
		xhci_write_64(xhci, erdp_reg | ERST_EHB,
				&xhci->sec_ir_set[intr_num]->erst_dequeue);

		dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries,
				xhci->sec_erst[intr_num].erst_dma_addr);
		xhci->sec_erst[intr_num].entries = NULL;
	}
	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d",
		intr_num);
	if (xhci->sec_event_ring[intr_num])
		xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]);

	xhci->sec_event_ring[intr_num] = NULL;
	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
		"Freed sec event ring");
+124 −87
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@ struct intf_info {
	size_t xfer_buf_size;
	phys_addr_t xfer_buf_pa;
	u8 *xfer_buf;
	u8 pcm_card_num;
	u8 pcm_dev_num;
	u8 direction;
	bool in_use;
};

@@ -115,6 +118,7 @@ struct uaudio_qmi_svc {
	struct qmi_handle *uaudio_svc_hdl;
	void *curr_conn;
	struct work_struct recv_msg_work;
	struct work_struct qmi_disconnect_work;
	struct workqueue_struct *uaudio_wq;
	ktime_t t_request_recvd;
	ktime_t t_resp_sent;
@@ -385,7 +389,7 @@ static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,

static int prepare_qmi_response(struct snd_usb_substream *subs,
		struct qmi_uaudio_stream_resp_msg_v01 *resp, u32 xfer_buf_len,
		int card_num)
		int card_num, int pcm_dev_num)
{
	int ret = -ENODEV;
	struct usb_interface *iface;
@@ -411,6 +415,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
		goto err;
	}

	if (uadev[card_num].info &&
			uadev[card_num].info[subs->interface].in_use) {
		pr_err("%s interface# %d already in use card# %d\n", __func__,
			subs->interface, card_num);
		ret = -EBUSY;
		goto err;
	}

	alts = &iface->altsetting[subs->altset_idx];
	altsd = get_iface_desc(alts);
	protocol = altsd->bInterfaceProtocol;
@@ -627,12 +639,6 @@ skip_sync:
		kref_get(&uadev[card_num].kref);
	}

	if (uadev[card_num].info[subs->interface].in_use) {
		pr_err("%s interface# %d already in use card# %d\n", __func__,
			subs->interface, card_num);
		goto unmap_xfer_buf;
	}

	uadev[card_num].card_num = card_num;

	/* cache intf specific info to use it for unmap and free xfer buf */
@@ -644,6 +650,9 @@ skip_sync:
	uadev[card_num].info[subs->interface].xfer_buf_pa = xfer_buf_pa;
	uadev[card_num].info[subs->interface].xfer_buf_size = len;
	uadev[card_num].info[subs->interface].xfer_buf = xfer_buf;
	uadev[card_num].info[subs->interface].pcm_card_num = card_num;
	uadev[card_num].info[subs->interface].pcm_dev_num = pcm_dev_num;
	uadev[card_num].info[subs->interface].direction = subs->direction;
	uadev[card_num].info[subs->interface].in_use = true;

	set_bit(card_num, &uaudio_qdev->card_slot);
@@ -665,9 +674,71 @@ err:
	return ret;
}

void uaudio_disconnect_cb(struct snd_usb_audio *chip)
static void uaudio_dev_intf_cleanup(struct usb_device *udev,
	struct intf_info *info)
{
	uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
		info->data_xfer_ring_size);
	info->data_xfer_ring_va = 0;
	info->data_xfer_ring_size = 0;

	uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
		info->sync_xfer_ring_size);
	info->sync_xfer_ring_va = 0;
	info->sync_xfer_ring_size = 0;

	uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_va,
		info->xfer_buf_size);
	info->xfer_buf_va = 0;

	usb_free_coherent(udev, info->xfer_buf_size,
		info->xfer_buf, info->xfer_buf_pa);
	info->xfer_buf_size = 0;
	info->xfer_buf = NULL;
	info->xfer_buf_pa = 0;

	info->in_use = false;
}

static void uaudio_dev_cleanup(struct uaudio_dev *dev)
{
	int if_idx;

	/* free xfer buffer and unmap xfer ring and buf per interface */
	for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
		if (!dev->info[if_idx].in_use)
			continue;
		uaudio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
		pr_debug("%s: release resources: intf# %d card# %d\n", __func__,
			if_idx, dev->card_num);
	}

	/* iommu_unmap dcba iova for a usb device */
	uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size);

	dev->dcba_iova = 0;
	dev->dcba_size = 0;
	dev->num_intf = 0;

	/* free interface info */
	kfree(dev->info);
	dev->info = NULL;

	clear_bit(dev->card_num, &uaudio_qdev->card_slot);

	/* all audio devices are disconnected */
	if (!uaudio_qdev->card_slot) {
		uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
		usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
		pr_debug("%s: all audio devices disconnected\n", __func__);
	}

	dev->udev = NULL;
}

static void uaudio_disconnect_cb(struct snd_usb_audio *chip)
{
	int ret, if_idx;
	int ret;
	struct uaudio_dev *dev;
	int card_num = chip->card_num;
	struct uaudio_qmi_svc *svc = uaudio_svc;
@@ -713,57 +784,7 @@ void uaudio_disconnect_cb(struct snd_usb_audio *chip)
		mutex_lock(&chip->dev_lock);
	}

	/* free xfer buffer and unmap xfer ring and buf per interface */
	for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
		if (!dev->info[if_idx].in_use)
			continue;
		usb_free_coherent(dev->udev,
		dev->info[if_idx].xfer_buf_size,
		dev->info[if_idx].xfer_buf,
		dev->info[if_idx].xfer_buf_pa);

		uaudio_iommu_unmap(MEM_XFER_RING,
		dev->info[if_idx].data_xfer_ring_va,
		dev->info[if_idx].data_xfer_ring_size);
		dev->info[if_idx].data_xfer_ring_va = 0;
		dev->info[if_idx].data_xfer_ring_size = 0;

		uaudio_iommu_unmap(MEM_XFER_RING,
		dev->info[if_idx].sync_xfer_ring_va,
		dev->info[if_idx].sync_xfer_ring_size);
		dev->info[if_idx].sync_xfer_ring_va = 0;
		dev->info[if_idx].sync_xfer_ring_size = 0;

		uaudio_iommu_unmap(MEM_XFER_BUF,
		dev->info[if_idx].xfer_buf_va,
		dev->info[if_idx].xfer_buf_size);
		dev->info[if_idx].xfer_buf_va = 0;
		dev->info[if_idx].xfer_buf_size = 0;
		pr_debug("%s: release resources: intf# %d card# %d\n", __func__,
			if_idx, card_num);
	}

	/* iommu_unmap dcba iova for a usb device */
	uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size);

	dev->dcba_iova = 0;
	dev->dcba_size = 0;
	dev->num_intf = 0;

	/* free interface info */
	kfree(dev->info);
	dev->info = NULL;

	clear_bit(card_num, &uaudio_qdev->card_slot);

	/* all audio devices are disconnected */
	if (!uaudio_qdev->card_slot) {
		uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
		usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num);
		pr_debug("%s: all audio devices disconnected\n", __func__);
	}

	dev->udev = NULL;
	uaudio_dev_cleanup(dev);
done:
	mutex_unlock(&chip->dev_lock);
}
@@ -789,7 +810,7 @@ static void uaudio_dev_release(struct kref *kref)
}

/* maps audio format received over QMI to asound.h based pcm format */
int map_pcm_format(unsigned int fmt_received)
static int map_pcm_format(unsigned int fmt_received)
{
	switch (fmt_received) {
	case USB_QMI_PCM_FORMAT_S8:
@@ -903,7 +924,7 @@ static int handle_uaudio_stream_req(void *req_h, void *req)

	if (!ret && req_msg->enable)
		ret = prepare_qmi_response(subs, &resp, req_msg->xfer_buff_size,
			pcm_card_num);
			pcm_card_num, pcm_dev_num);

	mutex_unlock(&chip->dev_lock);

@@ -912,31 +933,7 @@ response:
		if (intf_num >= 0) {
			mutex_lock(&chip->dev_lock);
			info = &uadev[pcm_card_num].info[intf_num];
			uaudio_iommu_unmap(MEM_XFER_RING,
			info->data_xfer_ring_va,
			info->data_xfer_ring_size);
			info->data_xfer_ring_va = 0;
			info->data_xfer_ring_size = 0;

			uaudio_iommu_unmap(MEM_XFER_RING,
			info->sync_xfer_ring_va,
			info->sync_xfer_ring_size);
			info->sync_xfer_ring_va = 0;
			info->sync_xfer_ring_size = 0;

			uaudio_iommu_unmap(MEM_XFER_BUF,
			info->xfer_buf_va,
			info->xfer_buf_size);
			info->xfer_buf_va = 0;

			usb_free_coherent(uadev[pcm_card_num].udev,
				info->xfer_buf_size,
				info->xfer_buf,
				info->xfer_buf_pa);
			info->xfer_buf_size = 0;
			info->xfer_buf = NULL;
			info->xfer_buf_pa = 0;
			info->in_use = false;
			uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev, info);
			pr_debug("%s:release resources: intf# %d card# %d\n",
				__func__, intf_num, pcm_card_num);
			mutex_unlock(&chip->dev_lock);
@@ -980,6 +977,43 @@ static int uaudio_qmi_svc_connect_cb(struct qmi_handle *handle,
	return 0;
}

static void uaudio_qmi_disconnect_work(struct work_struct *w)
{
	struct intf_info *info;
	int idx, if_idx;
	struct snd_usb_substream *subs;
	struct snd_usb_audio *chip = NULL;

	/* find all active intf for set alt 0 and cleanup usb audio dev */
	for (idx = 0; idx < SNDRV_CARDS; idx++) {
		if (!atomic_read(&uadev[idx].in_use))
			continue;

		for (if_idx = 0; if_idx < uadev[idx].num_intf; if_idx++) {
			if (!uadev[idx].info || !uadev[idx].info[if_idx].in_use)
				continue;
			info = &uadev[idx].info[if_idx];
			subs = find_snd_usb_substream(info->pcm_card_num,
							info->pcm_dev_num,
							info->direction,
							&chip,
							uaudio_disconnect_cb);
			if (!subs || !chip || atomic_read(&chip->shutdown)) {
				pr_debug("%s:no subs for c#%u, dev#%u dir%u\n",
					__func__, info->pcm_card_num,
					info->pcm_dev_num,
					info->direction);
				continue;
			}
			snd_usb_enable_audio_stream(subs, 0);
		}
		atomic_set(&uadev[idx].in_use, 0);
		mutex_lock(&chip->dev_lock);
		uaudio_dev_cleanup(&uadev[idx]);
		mutex_unlock(&chip->dev_lock);
	}
}

static int uaudio_qmi_svc_disconnect_cb(struct qmi_handle *handle,
				  void *conn_h)
{
@@ -991,6 +1025,8 @@ static int uaudio_qmi_svc_disconnect_cb(struct qmi_handle *handle,
	}

	svc->curr_conn = NULL;
	queue_work(svc->uaudio_wq, &svc->qmi_disconnect_work);

	return 0;
}

@@ -1195,6 +1231,7 @@ static int uaudio_qmi_svc_init(void)
	}

	INIT_WORK(&svc->recv_msg_work, uaudio_qmi_svc_recv_msg);
	INIT_WORK(&svc->qmi_disconnect_work, uaudio_qmi_disconnect_work);

	uaudio_svc = svc;