18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Sound Core PDAudioCF soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * PCM part 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 138c2ecf20Sopenharmony_ci#include "pdaudiocf.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * clear the SRAM contents 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_cistatic int pdacf_pcm_clear_sram(struct snd_pdacf *chip) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci int max_loop = 64 * 1024; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { 248c2ecf20Sopenharmony_ci if (max_loop-- < 0) 258c2ecf20Sopenharmony_ci return -EIO; 268c2ecf20Sopenharmony_ci inw(chip->port + PDAUDIOCF_REG_MD); 278c2ecf20Sopenharmony_ci } 288c2ecf20Sopenharmony_ci return 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * pdacf_pcm_trigger - trigger callback for capture 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistatic int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct snd_pdacf *chip = snd_pcm_substream_chip(subs); 378c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 388c2ecf20Sopenharmony_ci int inc, ret = 0, rate; 398c2ecf20Sopenharmony_ci unsigned short mask, val, tmp; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) 428c2ecf20Sopenharmony_ci return -EBUSY; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci switch (cmd) { 458c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 468c2ecf20Sopenharmony_ci chip->pcm_hwptr = 0; 478c2ecf20Sopenharmony_ci chip->pcm_tdone = 0; 488c2ecf20Sopenharmony_ci fallthrough; 498c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 508c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 518c2ecf20Sopenharmony_ci mask = 0; 528c2ecf20Sopenharmony_ci val = PDAUDIOCF_RECORD; 538c2ecf20Sopenharmony_ci inc = 1; 548c2ecf20Sopenharmony_ci rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 588c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 598c2ecf20Sopenharmony_ci mask = PDAUDIOCF_RECORD; 608c2ecf20Sopenharmony_ci val = 0; 618c2ecf20Sopenharmony_ci inc = -1; 628c2ecf20Sopenharmony_ci rate = 0; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci default: 658c2ecf20Sopenharmony_ci return -EINVAL; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci mutex_lock(&chip->reg_lock); 688c2ecf20Sopenharmony_ci chip->pcm_running += inc; 698c2ecf20Sopenharmony_ci tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); 708c2ecf20Sopenharmony_ci if (chip->pcm_running) { 718c2ecf20Sopenharmony_ci if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { 728c2ecf20Sopenharmony_ci chip->pcm_running -= inc; 738c2ecf20Sopenharmony_ci ret = -EIO; 748c2ecf20Sopenharmony_ci goto __end; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci tmp &= ~mask; 788c2ecf20Sopenharmony_ci tmp |= val; 798c2ecf20Sopenharmony_ci pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); 808c2ecf20Sopenharmony_ci __end: 818c2ecf20Sopenharmony_ci mutex_unlock(&chip->reg_lock); 828c2ecf20Sopenharmony_ci snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * pdacf_pcm_prepare - prepare callback for playback and capture 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int pdacf_pcm_prepare(struct snd_pcm_substream *subs) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct snd_pdacf *chip = snd_pcm_substream_chip(subs); 928c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 938c2ecf20Sopenharmony_ci u16 val, nval, aval; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) 968c2ecf20Sopenharmony_ci return -EBUSY; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci chip->pcm_channels = runtime->channels; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; 1018c2ecf20Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN 1028c2ecf20Sopenharmony_ci chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; 1038c2ecf20Sopenharmony_ci#else 1048c2ecf20Sopenharmony_ci chip->pcm_swab = chip->pcm_little; 1058c2ecf20Sopenharmony_ci#endif 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (snd_pcm_format_unsigned(runtime->format)) 1088c2ecf20Sopenharmony_ci chip->pcm_xor = 0x80008000; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (pdacf_pcm_clear_sram(chip) < 0) 1118c2ecf20Sopenharmony_ci return -EIO; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); 1148c2ecf20Sopenharmony_ci nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); 1158c2ecf20Sopenharmony_ci switch (runtime->format) { 1168c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 1178c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci default: /* 24-bit */ 1208c2ecf20Sopenharmony_ci nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci aval = 0; 1248c2ecf20Sopenharmony_ci chip->pcm_sample = 4; 1258c2ecf20Sopenharmony_ci switch (runtime->format) { 1268c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 1278c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 1288c2ecf20Sopenharmony_ci aval = AK4117_DIF_16R; 1298c2ecf20Sopenharmony_ci chip->pcm_frame = 2; 1308c2ecf20Sopenharmony_ci chip->pcm_sample = 2; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3LE: 1338c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_3BE: 1348c2ecf20Sopenharmony_ci chip->pcm_sample = 3; 1358c2ecf20Sopenharmony_ci fallthrough; 1368c2ecf20Sopenharmony_ci default: /* 24-bit */ 1378c2ecf20Sopenharmony_ci aval = AK4117_DIF_24R; 1388c2ecf20Sopenharmony_ci chip->pcm_frame = 3; 1398c2ecf20Sopenharmony_ci chip->pcm_xor &= 0xffff0000; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (val != nval) { 1448c2ecf20Sopenharmony_ci snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); 1458c2ecf20Sopenharmony_ci pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); 1498c2ecf20Sopenharmony_ci val &= ~(PDAUDIOCF_IRQLVLEN1); 1508c2ecf20Sopenharmony_ci val |= PDAUDIOCF_IRQLVLEN0; 1518c2ecf20Sopenharmony_ci pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci chip->pcm_size = runtime->buffer_size; 1548c2ecf20Sopenharmony_ci chip->pcm_period = runtime->period_size; 1558c2ecf20Sopenharmony_ci chip->pcm_area = runtime->dma_area; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * capture hw information 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware pdacf_pcm_capture_hw = { 1668c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 1678c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | 1688c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 1698c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH), 1708c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 1718c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | 1728c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, 1738c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | 1748c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100 | 1758c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | 1768c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | 1778c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000 | 1788c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_176400 | 1798c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_192000, 1808c2ecf20Sopenharmony_ci .rate_min = 32000, 1818c2ecf20Sopenharmony_ci .rate_max = 192000, 1828c2ecf20Sopenharmony_ci .channels_min = 1, 1838c2ecf20Sopenharmony_ci .channels_max = 2, 1848c2ecf20Sopenharmony_ci .buffer_bytes_max = (512*1024), 1858c2ecf20Sopenharmony_ci .period_bytes_min = 8*1024, 1868c2ecf20Sopenharmony_ci .period_bytes_max = (64*1024), 1878c2ecf20Sopenharmony_ci .periods_min = 2, 1888c2ecf20Sopenharmony_ci .periods_max = 128, 1898c2ecf20Sopenharmony_ci .fifo_size = 0, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * pdacf_pcm_capture_open - open callback for capture 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic int pdacf_pcm_capture_open(struct snd_pcm_substream *subs) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 1998c2ecf20Sopenharmony_ci struct snd_pdacf *chip = snd_pcm_substream_chip(subs); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) 2028c2ecf20Sopenharmony_ci return -EBUSY; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci runtime->hw = pdacf_pcm_capture_hw; 2058c2ecf20Sopenharmony_ci runtime->private_data = chip; 2068c2ecf20Sopenharmony_ci chip->pcm_substream = subs; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * pdacf_pcm_capture_close - close callback for capture 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic int pdacf_pcm_capture_close(struct snd_pcm_substream *subs) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct snd_pdacf *chip = snd_pcm_substream_chip(subs); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!chip) 2198c2ecf20Sopenharmony_ci return -EINVAL; 2208c2ecf20Sopenharmony_ci pdacf_reinit(chip, 0); 2218c2ecf20Sopenharmony_ci chip->pcm_substream = NULL; 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* 2278c2ecf20Sopenharmony_ci * pdacf_pcm_capture_pointer - pointer callback for capture 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pdacf_pcm_capture_pointer(struct snd_pcm_substream *subs) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct snd_pdacf *chip = snd_pcm_substream_chip(subs); 2328c2ecf20Sopenharmony_ci return chip->pcm_hwptr; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * operators for PCM capture 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops pdacf_pcm_capture_ops = { 2398c2ecf20Sopenharmony_ci .open = pdacf_pcm_capture_open, 2408c2ecf20Sopenharmony_ci .close = pdacf_pcm_capture_close, 2418c2ecf20Sopenharmony_ci .prepare = pdacf_pcm_prepare, 2428c2ecf20Sopenharmony_ci .trigger = pdacf_pcm_trigger, 2438c2ecf20Sopenharmony_ci .pointer = pdacf_pcm_capture_pointer, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * snd_pdacf_pcm_new - create and initialize a pcm 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ciint snd_pdacf_pcm_new(struct snd_pdacf *chip) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2538c2ecf20Sopenharmony_ci int err; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm); 2568c2ecf20Sopenharmony_ci if (err < 0) 2578c2ecf20Sopenharmony_ci return err; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops); 2608c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, 2618c2ecf20Sopenharmony_ci snd_dma_continuous_data(GFP_KERNEL | GFP_DMA32), 2628c2ecf20Sopenharmony_ci 0, 0); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci pcm->private_data = chip; 2658c2ecf20Sopenharmony_ci pcm->info_flags = 0; 2668c2ecf20Sopenharmony_ci pcm->nonatomic = true; 2678c2ecf20Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 2688c2ecf20Sopenharmony_ci chip->pcm = pcm; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); 2718c2ecf20Sopenharmony_ci if (err < 0) 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 276