18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Sound Core PDAudioCF soundcard
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <sound/core.h>
98c2ecf20Sopenharmony_ci#include "pdaudiocf.h"
108c2ecf20Sopenharmony_ci#include <sound/initval.h>
118c2ecf20Sopenharmony_ci#include <asm/irq_regs.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ciirqreturn_t pdacf_interrupt(int irq, void *dev)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = dev;
198c2ecf20Sopenharmony_ci	unsigned short stat;
208c2ecf20Sopenharmony_ci	bool wake_thread = false;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|
238c2ecf20Sopenharmony_ci				  PDAUDIOCF_STAT_IS_CONFIGURED|
248c2ecf20Sopenharmony_ci				  PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
258c2ecf20Sopenharmony_ci		return IRQ_HANDLED;	/* IRQ_NONE here? */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	stat = inw(chip->port + PDAUDIOCF_REG_ISR);
288c2ecf20Sopenharmony_ci	if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) {
298c2ecf20Sopenharmony_ci		if (stat & PDAUDIOCF_IRQOVR)	/* should never happen */
308c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
318c2ecf20Sopenharmony_ci		if (chip->pcm_substream)
328c2ecf20Sopenharmony_ci			wake_thread = true;
338c2ecf20Sopenharmony_ci		if (!(stat & PDAUDIOCF_IRQAKM))
348c2ecf20Sopenharmony_ci			stat |= PDAUDIOCF_IRQAKM;	/* check rate */
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	if (get_irq_regs() != NULL)
378c2ecf20Sopenharmony_ci		snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
388c2ecf20Sopenharmony_ci	return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	while (size-- > 0) {
448c2ecf20Sopenharmony_ci		*dst++ = inw(rdp_port) ^ xor;
458c2ecf20Sopenharmony_ci		inw(rdp_port);
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	register u16 val1, val2;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	while (size-- > 0) {
548c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
558c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
568c2ecf20Sopenharmony_ci		inw(rdp_port);
578c2ecf20Sopenharmony_ci		*dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	while (size-- > 0) {
648c2ecf20Sopenharmony_ci		*dst++ = inw(rdp_port) ^ xor;
658c2ecf20Sopenharmony_ci		*dst++ = inw(rdp_port) ^ xor;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	register u16 val1, val2, val3;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	while (size-- > 0) {
748c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
758c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
768c2ecf20Sopenharmony_ci		val3 = inw(rdp_port);
778c2ecf20Sopenharmony_ci		*dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
788c2ecf20Sopenharmony_ci		*dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	while (size-- > 0) {
858c2ecf20Sopenharmony_ci		*dst++ = swab16(inw(rdp_port) ^ xor);
868c2ecf20Sopenharmony_ci		inw(rdp_port);
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	register u16 val1, val2;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	while (size-- > 0) {
958c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
968c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
978c2ecf20Sopenharmony_ci		inw(rdp_port);
988c2ecf20Sopenharmony_ci		*dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	while (size-- > 0) {
1058c2ecf20Sopenharmony_ci		*dst++ = swab16(inw(rdp_port) ^ xor);
1068c2ecf20Sopenharmony_ci		*dst++ = swab16(inw(rdp_port) ^ xor);
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	register u16 val1, val2, val3;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	while (size-- > 0) {
1158c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
1168c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
1178c2ecf20Sopenharmony_ci		val3 = inw(rdp_port);
1188c2ecf20Sopenharmony_ci		*dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
1198c2ecf20Sopenharmony_ci		*dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	register u16 val1, val2;
1268c2ecf20Sopenharmony_ci	register u32 xval1;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	while (size-- > 0) {
1298c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
1308c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
1318c2ecf20Sopenharmony_ci		inw(rdp_port);
1328c2ecf20Sopenharmony_ci		xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
1338c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 8);
1348c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 16);
1358c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 24);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	register u16 val1, val2;
1428c2ecf20Sopenharmony_ci	register u32 xval1;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	while (size-- > 0) {
1458c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
1468c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
1478c2ecf20Sopenharmony_ci		inw(rdp_port);
1488c2ecf20Sopenharmony_ci		xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
1498c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 24);
1508c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 16);
1518c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 8);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	register u16 val1, val2, val3;
1588c2ecf20Sopenharmony_ci	register u32 xval1, xval2;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	while (size-- > 0) {
1618c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
1628c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
1638c2ecf20Sopenharmony_ci		val3 = inw(rdp_port);
1648c2ecf20Sopenharmony_ci		xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
1658c2ecf20Sopenharmony_ci		xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
1668c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 8);
1678c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 16);
1688c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 24);
1698c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 8);
1708c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 16);
1718c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 24);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	register u16 val1, val2, val3;
1788c2ecf20Sopenharmony_ci	register u32 xval1, xval2;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	while (size-- > 0) {
1818c2ecf20Sopenharmony_ci		val1 = inw(rdp_port);
1828c2ecf20Sopenharmony_ci		val2 = inw(rdp_port);
1838c2ecf20Sopenharmony_ci		val3 = inw(rdp_port);
1848c2ecf20Sopenharmony_ci		xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
1858c2ecf20Sopenharmony_ci		xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
1868c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 24);
1878c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 16);
1888c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval1 >> 8);
1898c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 24);
1908c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 16);
1918c2ecf20Sopenharmony_ci		*dst++ = (u8)(xval2 >> 8);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned int off)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
1988c2ecf20Sopenharmony_ci	unsigned int xor = chip->pcm_xor;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (chip->pcm_sample == 3) {
2018c2ecf20Sopenharmony_ci		if (chip->pcm_little) {
2028c2ecf20Sopenharmony_ci			if (chip->pcm_channels == 1) {
2038c2ecf20Sopenharmony_ci				pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
2048c2ecf20Sopenharmony_ci			} else {
2058c2ecf20Sopenharmony_ci				pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
2068c2ecf20Sopenharmony_ci			}
2078c2ecf20Sopenharmony_ci		} else {
2088c2ecf20Sopenharmony_ci			if (chip->pcm_channels == 1) {
2098c2ecf20Sopenharmony_ci				pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
2108c2ecf20Sopenharmony_ci			} else {
2118c2ecf20Sopenharmony_ci				pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
2128c2ecf20Sopenharmony_ci			}
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	if (chip->pcm_swab == 0) {
2178c2ecf20Sopenharmony_ci		if (chip->pcm_channels == 1) {
2188c2ecf20Sopenharmony_ci			if (chip->pcm_frame == 2) {
2198c2ecf20Sopenharmony_ci				pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port);
2208c2ecf20Sopenharmony_ci			} else {
2218c2ecf20Sopenharmony_ci				pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port);
2228c2ecf20Sopenharmony_ci			}
2238c2ecf20Sopenharmony_ci		} else {
2248c2ecf20Sopenharmony_ci			if (chip->pcm_frame == 2) {
2258c2ecf20Sopenharmony_ci				pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2268c2ecf20Sopenharmony_ci			} else {
2278c2ecf20Sopenharmony_ci				pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2288c2ecf20Sopenharmony_ci			}
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci	} else {
2318c2ecf20Sopenharmony_ci		if (chip->pcm_channels == 1) {
2328c2ecf20Sopenharmony_ci			if (chip->pcm_frame == 2) {
2338c2ecf20Sopenharmony_ci				pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port);
2348c2ecf20Sopenharmony_ci			} else {
2358c2ecf20Sopenharmony_ci				pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port);
2368c2ecf20Sopenharmony_ci			}
2378c2ecf20Sopenharmony_ci		} else {
2388c2ecf20Sopenharmony_ci			if (chip->pcm_frame == 2) {
2398c2ecf20Sopenharmony_ci				pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2408c2ecf20Sopenharmony_ci			} else {
2418c2ecf20Sopenharmony_ci				pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2428c2ecf20Sopenharmony_ci			}
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ciirqreturn_t pdacf_threaded_irq(int irq, void *dev)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct snd_pdacf *chip = dev;
2508c2ecf20Sopenharmony_ci	int size, off, cont, rdp, wdp;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
2538c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
2568c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
2598c2ecf20Sopenharmony_ci	wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
2608c2ecf20Sopenharmony_ci	/* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */
2618c2ecf20Sopenharmony_ci	size = wdp - rdp;
2628c2ecf20Sopenharmony_ci	if (size < 0)
2638c2ecf20Sopenharmony_ci		size += 0x10000;
2648c2ecf20Sopenharmony_ci	if (size == 0)
2658c2ecf20Sopenharmony_ci		size = 0x10000;
2668c2ecf20Sopenharmony_ci	size /= chip->pcm_frame;
2678c2ecf20Sopenharmony_ci	if (size > 64)
2688c2ecf20Sopenharmony_ci		size -= 32;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci#if 0
2718c2ecf20Sopenharmony_ci	chip->pcm_hwptr += size;
2728c2ecf20Sopenharmony_ci	chip->pcm_hwptr %= chip->pcm_size;
2738c2ecf20Sopenharmony_ci	chip->pcm_tdone += size;
2748c2ecf20Sopenharmony_ci	if (chip->pcm_frame == 2) {
2758c2ecf20Sopenharmony_ci		unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
2768c2ecf20Sopenharmony_ci		while (size-- > 0) {
2778c2ecf20Sopenharmony_ci			inw(rdp_port);
2788c2ecf20Sopenharmony_ci			inw(rdp_port);
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci	} else {
2818c2ecf20Sopenharmony_ci		unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
2828c2ecf20Sopenharmony_ci		while (size-- > 0) {
2838c2ecf20Sopenharmony_ci			inw(rdp_port);
2848c2ecf20Sopenharmony_ci			inw(rdp_port);
2858c2ecf20Sopenharmony_ci			inw(rdp_port);
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci#else
2898c2ecf20Sopenharmony_ci	off = chip->pcm_hwptr + chip->pcm_tdone;
2908c2ecf20Sopenharmony_ci	off %= chip->pcm_size;
2918c2ecf20Sopenharmony_ci	chip->pcm_tdone += size;
2928c2ecf20Sopenharmony_ci	while (size > 0) {
2938c2ecf20Sopenharmony_ci		cont = chip->pcm_size - off;
2948c2ecf20Sopenharmony_ci		if (cont > size)
2958c2ecf20Sopenharmony_ci			cont = size;
2968c2ecf20Sopenharmony_ci		pdacf_transfer(chip, cont, off);
2978c2ecf20Sopenharmony_ci		off += cont;
2988c2ecf20Sopenharmony_ci		off %= chip->pcm_size;
2998c2ecf20Sopenharmony_ci		size -= cont;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci#endif
3028c2ecf20Sopenharmony_ci	mutex_lock(&chip->reg_lock);
3038c2ecf20Sopenharmony_ci	while (chip->pcm_tdone >= chip->pcm_period) {
3048c2ecf20Sopenharmony_ci		chip->pcm_hwptr += chip->pcm_period;
3058c2ecf20Sopenharmony_ci		chip->pcm_hwptr %= chip->pcm_size;
3068c2ecf20Sopenharmony_ci		chip->pcm_tdone -= chip->pcm_period;
3078c2ecf20Sopenharmony_ci		mutex_unlock(&chip->reg_lock);
3088c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->pcm_substream);
3098c2ecf20Sopenharmony_ci		mutex_lock(&chip->reg_lock);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	mutex_unlock(&chip->reg_lock);
3128c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3138c2ecf20Sopenharmony_ci}
314