18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Routines for control of the AK4114 via I2C and 4-wire serial interface 48c2ecf20Sopenharmony_ci * IEC958 (S/PDIF) receiver by Asahi Kasei 58c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/control.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm.h> 148c2ecf20Sopenharmony_ci#include <sound/ak4114.h> 158c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 168c2ecf20Sopenharmony_ci#include <sound/info.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei"); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define AK4114_ADDR 0x00 /* fixed address */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void ak4114_stats(struct work_struct *work); 258c2ecf20Sopenharmony_cistatic void ak4114_init_regs(struct ak4114 *chip); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char val) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci ak4114->write(ak4114->private_data, reg, val); 308c2ecf20Sopenharmony_ci if (reg <= AK4114_REG_INT1_MASK) 318c2ecf20Sopenharmony_ci ak4114->regmap[reg] = val; 328c2ecf20Sopenharmony_ci else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4) 338c2ecf20Sopenharmony_ci ak4114->txcsb[reg-AK4114_REG_TXCSB0] = val; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return ak4114->read(ak4114->private_data, reg); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#if 0 428c2ecf20Sopenharmony_cistatic void reg_dump(struct ak4114 *ak4114) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int i; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "AK4114 REG DUMP:\n"); 478c2ecf20Sopenharmony_ci for (i = 0; i < 0x20; i++) 488c2ecf20Sopenharmony_ci printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void snd_ak4114_free(struct ak4114 *chip) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci atomic_inc(&chip->wq_processing); /* don't schedule new work */ 558c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&chip->work); 568c2ecf20Sopenharmony_ci kfree(chip); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int snd_ak4114_dev_free(struct snd_device *device) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct ak4114 *chip = device->device_data; 628c2ecf20Sopenharmony_ci snd_ak4114_free(chip); 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ciint snd_ak4114_create(struct snd_card *card, 678c2ecf20Sopenharmony_ci ak4114_read_t *read, ak4114_write_t *write, 688c2ecf20Sopenharmony_ci const unsigned char pgm[6], const unsigned char txcsb[5], 698c2ecf20Sopenharmony_ci void *private_data, struct ak4114 **r_ak4114) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct ak4114 *chip; 728c2ecf20Sopenharmony_ci int err = 0; 738c2ecf20Sopenharmony_ci unsigned char reg; 748c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 758c2ecf20Sopenharmony_ci .dev_free = snd_ak4114_dev_free, 768c2ecf20Sopenharmony_ci }; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 798c2ecf20Sopenharmony_ci if (chip == NULL) 808c2ecf20Sopenharmony_ci return -ENOMEM; 818c2ecf20Sopenharmony_ci spin_lock_init(&chip->lock); 828c2ecf20Sopenharmony_ci chip->card = card; 838c2ecf20Sopenharmony_ci chip->read = read; 848c2ecf20Sopenharmony_ci chip->write = write; 858c2ecf20Sopenharmony_ci chip->private_data = private_data; 868c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&chip->work, ak4114_stats); 878c2ecf20Sopenharmony_ci atomic_set(&chip->wq_processing, 0); 888c2ecf20Sopenharmony_ci mutex_init(&chip->reinit_mutex); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci for (reg = 0; reg < 6; reg++) 918c2ecf20Sopenharmony_ci chip->regmap[reg] = pgm[reg]; 928c2ecf20Sopenharmony_ci for (reg = 0; reg < 5; reg++) 938c2ecf20Sopenharmony_ci chip->txcsb[reg] = txcsb[reg]; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ak4114_init_regs(chip); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT); 988c2ecf20Sopenharmony_ci chip->rcs1 = reg_read(chip, AK4114_REG_RCS1); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0) 1018c2ecf20Sopenharmony_ci goto __fail; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (r_ak4114) 1048c2ecf20Sopenharmony_ci *r_ak4114 = chip; 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci __fail: 1088c2ecf20Sopenharmony_ci snd_ak4114_free(chip); 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_create); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char mask, unsigned char val) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (reg <= AK4114_REG_INT1_MASK) 1168c2ecf20Sopenharmony_ci reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); 1178c2ecf20Sopenharmony_ci else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4) 1188c2ecf20Sopenharmony_ci reg_write(chip, reg, 1198c2ecf20Sopenharmony_ci (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_reg_write); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void ak4114_init_regs(struct ak4114 *chip) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* bring the chip to reset state and powerdown state */ 1288c2ecf20Sopenharmony_ci reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN)); 1298c2ecf20Sopenharmony_ci udelay(200); 1308c2ecf20Sopenharmony_ci /* release reset, but leave powerdown */ 1318c2ecf20Sopenharmony_ci reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN); 1328c2ecf20Sopenharmony_ci udelay(200); 1338c2ecf20Sopenharmony_ci for (reg = 1; reg < 6; reg++) 1348c2ecf20Sopenharmony_ci reg_write(chip, reg, chip->regmap[reg]); 1358c2ecf20Sopenharmony_ci for (reg = 0; reg < 5; reg++) 1368c2ecf20Sopenharmony_ci reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); 1378c2ecf20Sopenharmony_ci /* release powerdown, everything is initialized now */ 1388c2ecf20Sopenharmony_ci reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_civoid snd_ak4114_reinit(struct ak4114 *chip) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (atomic_inc_return(&chip->wq_processing) == 1) 1448c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&chip->work); 1458c2ecf20Sopenharmony_ci mutex_lock(&chip->reinit_mutex); 1468c2ecf20Sopenharmony_ci ak4114_init_regs(chip); 1478c2ecf20Sopenharmony_ci mutex_unlock(&chip->reinit_mutex); 1488c2ecf20Sopenharmony_ci /* bring up statistics / event queing */ 1498c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&chip->wq_processing)) 1508c2ecf20Sopenharmony_ci schedule_delayed_work(&chip->work, HZ / 10); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_reinit); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic unsigned int external_rate(unsigned char rcs1) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci switch (rcs1 & (AK4114_FS0|AK4114_FS1|AK4114_FS2|AK4114_FS3)) { 1578c2ecf20Sopenharmony_ci case AK4114_FS_32000HZ: return 32000; 1588c2ecf20Sopenharmony_ci case AK4114_FS_44100HZ: return 44100; 1598c2ecf20Sopenharmony_ci case AK4114_FS_48000HZ: return 48000; 1608c2ecf20Sopenharmony_ci case AK4114_FS_88200HZ: return 88200; 1618c2ecf20Sopenharmony_ci case AK4114_FS_96000HZ: return 96000; 1628c2ecf20Sopenharmony_ci case AK4114_FS_176400HZ: return 176400; 1638c2ecf20Sopenharmony_ci case AK4114_FS_192000HZ: return 192000; 1648c2ecf20Sopenharmony_ci default: return 0; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int snd_ak4114_in_error_info(struct snd_kcontrol *kcontrol, 1698c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1728c2ecf20Sopenharmony_ci uinfo->count = 1; 1738c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 1748c2ecf20Sopenharmony_ci uinfo->value.integer.max = LONG_MAX; 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol, 1798c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_lock_irq(&chip->lock); 1848c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 1858c2ecf20Sopenharmony_ci chip->errors[kcontrol->private_value]; 1868c2ecf20Sopenharmony_ci chip->errors[kcontrol->private_value] = 0; 1878c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->lock); 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci#define snd_ak4114_in_bit_info snd_ctl_boolean_mono_info 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int snd_ak4114_in_bit_get(struct snd_kcontrol *kcontrol, 1948c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 1978c2ecf20Sopenharmony_ci unsigned char reg = kcontrol->private_value & 0xff; 1988c2ecf20Sopenharmony_ci unsigned char bit = (kcontrol->private_value >> 8) & 0xff; 1998c2ecf20Sopenharmony_ci unsigned char inv = (kcontrol->private_value >> 31) & 1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv; 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int snd_ak4114_rate_info(struct snd_kcontrol *kcontrol, 2068c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2098c2ecf20Sopenharmony_ci uinfo->count = 1; 2108c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 2118c2ecf20Sopenharmony_ci uinfo->value.integer.max = 192000; 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int snd_ak4114_rate_get(struct snd_kcontrol *kcontrol, 2168c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4114_REG_RCS1)); 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 2278c2ecf20Sopenharmony_ci uinfo->count = 1; 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_get(struct snd_kcontrol *kcontrol, 2328c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 2358c2ecf20Sopenharmony_ci unsigned i; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (i = 0; i < AK4114_REG_RXCSB_SIZE; i++) 2388c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[i] = reg_read(chip, AK4114_REG_RXCSB0 + i); 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_playback_get(struct snd_kcontrol *kcontrol, 2438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 2468c2ecf20Sopenharmony_ci unsigned i; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++) 2498c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[i] = chip->txcsb[i]; 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_playback_put(struct snd_kcontrol *kcontrol, 2548c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 2578c2ecf20Sopenharmony_ci unsigned i; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++) 2608c2ecf20Sopenharmony_ci reg_write(chip, AK4114_REG_TXCSB0 + i, ucontrol->value.iec958.status[i]); 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 2678c2ecf20Sopenharmony_ci uinfo->count = 1; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_mask_get(struct snd_kcontrol *kcontrol, 2728c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci memset(ucontrol->value.iec958.status, 0xff, AK4114_REG_RXCSB_SIZE); 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_pinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2818c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 2828c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0xffff; 2838c2ecf20Sopenharmony_ci uinfo->count = 4; 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_pget(struct snd_kcontrol *kcontrol, 2888c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 2918c2ecf20Sopenharmony_ci unsigned short tmp; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 0xf8f2; 2948c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = 0x4e1f; 2958c2ecf20Sopenharmony_ci tmp = reg_read(chip, AK4114_REG_Pc0) | (reg_read(chip, AK4114_REG_Pc1) << 8); 2968c2ecf20Sopenharmony_ci ucontrol->value.integer.value[2] = tmp; 2978c2ecf20Sopenharmony_ci tmp = reg_read(chip, AK4114_REG_Pd0) | (reg_read(chip, AK4114_REG_Pd1) << 8); 2988c2ecf20Sopenharmony_ci ucontrol->value.integer.value[3] = tmp; 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 3058c2ecf20Sopenharmony_ci uinfo->count = AK4114_REG_QSUB_SIZE; 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int snd_ak4114_spdif_qget(struct snd_kcontrol *kcontrol, 3108c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct ak4114 *chip = snd_kcontrol_chip(kcontrol); 3138c2ecf20Sopenharmony_ci unsigned i; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci for (i = 0; i < AK4114_REG_QSUB_SIZE; i++) 3168c2ecf20Sopenharmony_ci ucontrol->value.bytes.data[i] = reg_read(chip, AK4114_REG_QSUB_ADDR + i); 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* Don't forget to change AK4114_CONTROLS define!!! */ 3218c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3248c2ecf20Sopenharmony_ci .name = "IEC958 Parity Errors", 3258c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3268c2ecf20Sopenharmony_ci .info = snd_ak4114_in_error_info, 3278c2ecf20Sopenharmony_ci .get = snd_ak4114_in_error_get, 3288c2ecf20Sopenharmony_ci .private_value = AK4114_PARITY_ERRORS, 3298c2ecf20Sopenharmony_ci}, 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3328c2ecf20Sopenharmony_ci .name = "IEC958 V-Bit Errors", 3338c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3348c2ecf20Sopenharmony_ci .info = snd_ak4114_in_error_info, 3358c2ecf20Sopenharmony_ci .get = snd_ak4114_in_error_get, 3368c2ecf20Sopenharmony_ci .private_value = AK4114_V_BIT_ERRORS, 3378c2ecf20Sopenharmony_ci}, 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3408c2ecf20Sopenharmony_ci .name = "IEC958 C-CRC Errors", 3418c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3428c2ecf20Sopenharmony_ci .info = snd_ak4114_in_error_info, 3438c2ecf20Sopenharmony_ci .get = snd_ak4114_in_error_get, 3448c2ecf20Sopenharmony_ci .private_value = AK4114_CCRC_ERRORS, 3458c2ecf20Sopenharmony_ci}, 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3488c2ecf20Sopenharmony_ci .name = "IEC958 Q-CRC Errors", 3498c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3508c2ecf20Sopenharmony_ci .info = snd_ak4114_in_error_info, 3518c2ecf20Sopenharmony_ci .get = snd_ak4114_in_error_get, 3528c2ecf20Sopenharmony_ci .private_value = AK4114_QCRC_ERRORS, 3538c2ecf20Sopenharmony_ci}, 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3568c2ecf20Sopenharmony_ci .name = "IEC958 External Rate", 3578c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3588c2ecf20Sopenharmony_ci .info = snd_ak4114_rate_info, 3598c2ecf20Sopenharmony_ci .get = snd_ak4114_rate_get, 3608c2ecf20Sopenharmony_ci}, 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3638c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 3648c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 3658c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_mask_info, 3668c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_mask_get, 3678c2ecf20Sopenharmony_ci}, 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3708c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 3718c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3728c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_info, 3738c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_playback_get, 3748c2ecf20Sopenharmony_ci .put = snd_ak4114_spdif_playback_put, 3758c2ecf20Sopenharmony_ci}, 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3788c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK), 3798c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 3808c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_mask_info, 3818c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_mask_get, 3828c2ecf20Sopenharmony_ci}, 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3858c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), 3868c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3878c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_info, 3888c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_get, 3898c2ecf20Sopenharmony_ci}, 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3928c2ecf20Sopenharmony_ci .name = "IEC958 Preamble Capture Default", 3938c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 3948c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_pinfo, 3958c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_pget, 3968c2ecf20Sopenharmony_ci}, 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 3998c2ecf20Sopenharmony_ci .name = "IEC958 Q-subcode Capture Default", 4008c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 4018c2ecf20Sopenharmony_ci .info = snd_ak4114_spdif_qinfo, 4028c2ecf20Sopenharmony_ci .get = snd_ak4114_spdif_qget, 4038c2ecf20Sopenharmony_ci}, 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 4068c2ecf20Sopenharmony_ci .name = "IEC958 Audio", 4078c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 4088c2ecf20Sopenharmony_ci .info = snd_ak4114_in_bit_info, 4098c2ecf20Sopenharmony_ci .get = snd_ak4114_in_bit_get, 4108c2ecf20Sopenharmony_ci .private_value = (1<<31) | (1<<8) | AK4114_REG_RCS0, 4118c2ecf20Sopenharmony_ci}, 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 4148c2ecf20Sopenharmony_ci .name = "IEC958 Non-PCM Bitstream", 4158c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 4168c2ecf20Sopenharmony_ci .info = snd_ak4114_in_bit_info, 4178c2ecf20Sopenharmony_ci .get = snd_ak4114_in_bit_get, 4188c2ecf20Sopenharmony_ci .private_value = (6<<8) | AK4114_REG_RCS0, 4198c2ecf20Sopenharmony_ci}, 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 4228c2ecf20Sopenharmony_ci .name = "IEC958 DTS Bitstream", 4238c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 4248c2ecf20Sopenharmony_ci .info = snd_ak4114_in_bit_info, 4258c2ecf20Sopenharmony_ci .get = snd_ak4114_in_bit_get, 4268c2ecf20Sopenharmony_ci .private_value = (3<<8) | AK4114_REG_RCS0, 4278c2ecf20Sopenharmony_ci}, 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 4308c2ecf20Sopenharmony_ci .name = "IEC958 PPL Lock Status", 4318c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, 4328c2ecf20Sopenharmony_ci .info = snd_ak4114_in_bit_info, 4338c2ecf20Sopenharmony_ci .get = snd_ak4114_in_bit_get, 4348c2ecf20Sopenharmony_ci .private_value = (1<<31) | (4<<8) | AK4114_REG_RCS0, 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void snd_ak4114_proc_regs_read(struct snd_info_entry *entry, 4408c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct ak4114 *ak4114 = entry->private_data; 4438c2ecf20Sopenharmony_ci int reg, val; 4448c2ecf20Sopenharmony_ci /* all ak4114 registers 0x00 - 0x1f */ 4458c2ecf20Sopenharmony_ci for (reg = 0; reg < 0x20; reg++) { 4468c2ecf20Sopenharmony_ci val = reg_read(ak4114, reg); 4478c2ecf20Sopenharmony_ci snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void snd_ak4114_proc_init(struct ak4114 *ak4114) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci snd_card_ro_proc_new(ak4114->card, "ak4114", ak4114, 4548c2ecf20Sopenharmony_ci snd_ak4114_proc_regs_read); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciint snd_ak4114_build(struct ak4114 *ak4114, 4588c2ecf20Sopenharmony_ci struct snd_pcm_substream *ply_substream, 4598c2ecf20Sopenharmony_ci struct snd_pcm_substream *cap_substream) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 4628c2ecf20Sopenharmony_ci unsigned int idx; 4638c2ecf20Sopenharmony_ci int err; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (snd_BUG_ON(!cap_substream)) 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci ak4114->playback_substream = ply_substream; 4688c2ecf20Sopenharmony_ci ak4114->capture_substream = cap_substream; 4698c2ecf20Sopenharmony_ci for (idx = 0; idx < AK4114_CONTROLS; idx++) { 4708c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&snd_ak4114_iec958_controls[idx], ak4114); 4718c2ecf20Sopenharmony_ci if (kctl == NULL) 4728c2ecf20Sopenharmony_ci return -ENOMEM; 4738c2ecf20Sopenharmony_ci if (strstr(kctl->id.name, "Playback")) { 4748c2ecf20Sopenharmony_ci if (ply_substream == NULL) { 4758c2ecf20Sopenharmony_ci snd_ctl_free_one(kctl); 4768c2ecf20Sopenharmony_ci ak4114->kctls[idx] = NULL; 4778c2ecf20Sopenharmony_ci continue; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci kctl->id.device = ply_substream->pcm->device; 4808c2ecf20Sopenharmony_ci kctl->id.subdevice = ply_substream->number; 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci kctl->id.device = cap_substream->pcm->device; 4838c2ecf20Sopenharmony_ci kctl->id.subdevice = cap_substream->number; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci err = snd_ctl_add(ak4114->card, kctl); 4868c2ecf20Sopenharmony_ci if (err < 0) 4878c2ecf20Sopenharmony_ci return err; 4888c2ecf20Sopenharmony_ci ak4114->kctls[idx] = kctl; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci snd_ak4114_proc_init(ak4114); 4918c2ecf20Sopenharmony_ci /* trigger workq */ 4928c2ecf20Sopenharmony_ci schedule_delayed_work(&ak4114->work, HZ / 10); 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_build); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* notify kcontrols if any parameters are changed */ 4988c2ecf20Sopenharmony_cistatic void ak4114_notify(struct ak4114 *ak4114, 4998c2ecf20Sopenharmony_ci unsigned char rcs0, unsigned char rcs1, 5008c2ecf20Sopenharmony_ci unsigned char c0, unsigned char c1) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci if (!ak4114->kctls[0]) 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (rcs0 & AK4114_PAR) 5068c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5078c2ecf20Sopenharmony_ci &ak4114->kctls[0]->id); 5088c2ecf20Sopenharmony_ci if (rcs0 & AK4114_V) 5098c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5108c2ecf20Sopenharmony_ci &ak4114->kctls[1]->id); 5118c2ecf20Sopenharmony_ci if (rcs1 & AK4114_CCRC) 5128c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5138c2ecf20Sopenharmony_ci &ak4114->kctls[2]->id); 5148c2ecf20Sopenharmony_ci if (rcs1 & AK4114_QCRC) 5158c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5168c2ecf20Sopenharmony_ci &ak4114->kctls[3]->id); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* rate change */ 5198c2ecf20Sopenharmony_ci if (c1 & 0xf0) 5208c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5218c2ecf20Sopenharmony_ci &ak4114->kctls[4]->id); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT)) 5248c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5258c2ecf20Sopenharmony_ci &ak4114->kctls[9]->id); 5268c2ecf20Sopenharmony_ci if (c0 & AK4114_QINT) 5278c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5288c2ecf20Sopenharmony_ci &ak4114->kctls[10]->id); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (c0 & AK4114_AUDION) 5318c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5328c2ecf20Sopenharmony_ci &ak4114->kctls[11]->id); 5338c2ecf20Sopenharmony_ci if (c0 & AK4114_AUTO) 5348c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5358c2ecf20Sopenharmony_ci &ak4114->kctls[12]->id); 5368c2ecf20Sopenharmony_ci if (c0 & AK4114_DTSCD) 5378c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5388c2ecf20Sopenharmony_ci &ak4114->kctls[13]->id); 5398c2ecf20Sopenharmony_ci if (c0 & AK4114_UNLCK) 5408c2ecf20Sopenharmony_ci snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, 5418c2ecf20Sopenharmony_ci &ak4114->kctls[14]->id); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciint snd_ak4114_external_rate(struct ak4114 *ak4114) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci unsigned char rcs1; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci rcs1 = reg_read(ak4114, AK4114_REG_RCS1); 5498c2ecf20Sopenharmony_ci return external_rate(rcs1); 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_external_rate); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciint snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = ak4114->capture_substream ? ak4114->capture_substream->runtime : NULL; 5568c2ecf20Sopenharmony_ci unsigned long _flags; 5578c2ecf20Sopenharmony_ci int res = 0; 5588c2ecf20Sopenharmony_ci unsigned char rcs0, rcs1; 5598c2ecf20Sopenharmony_ci unsigned char c0, c1; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci rcs1 = reg_read(ak4114, AK4114_REG_RCS1); 5628c2ecf20Sopenharmony_ci if (flags & AK4114_CHECK_NO_STAT) 5638c2ecf20Sopenharmony_ci goto __rate; 5648c2ecf20Sopenharmony_ci rcs0 = reg_read(ak4114, AK4114_REG_RCS0); 5658c2ecf20Sopenharmony_ci spin_lock_irqsave(&ak4114->lock, _flags); 5668c2ecf20Sopenharmony_ci if (rcs0 & AK4114_PAR) 5678c2ecf20Sopenharmony_ci ak4114->errors[AK4114_PARITY_ERRORS]++; 5688c2ecf20Sopenharmony_ci if (rcs1 & AK4114_V) 5698c2ecf20Sopenharmony_ci ak4114->errors[AK4114_V_BIT_ERRORS]++; 5708c2ecf20Sopenharmony_ci if (rcs1 & AK4114_CCRC) 5718c2ecf20Sopenharmony_ci ak4114->errors[AK4114_CCRC_ERRORS]++; 5728c2ecf20Sopenharmony_ci if (rcs1 & AK4114_QCRC) 5738c2ecf20Sopenharmony_ci ak4114->errors[AK4114_QCRC_ERRORS]++; 5748c2ecf20Sopenharmony_ci c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^ 5758c2ecf20Sopenharmony_ci (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)); 5768c2ecf20Sopenharmony_ci c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0); 5778c2ecf20Sopenharmony_ci ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT); 5788c2ecf20Sopenharmony_ci ak4114->rcs1 = rcs1; 5798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ak4114->lock, _flags); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci ak4114_notify(ak4114, rcs0, rcs1, c0, c1); 5828c2ecf20Sopenharmony_ci if (ak4114->change_callback && (c0 | c1) != 0) 5838c2ecf20Sopenharmony_ci ak4114->change_callback(ak4114, c0, c1); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci __rate: 5868c2ecf20Sopenharmony_ci /* compare rate */ 5878c2ecf20Sopenharmony_ci res = external_rate(rcs1); 5888c2ecf20Sopenharmony_ci if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) { 5898c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags); 5908c2ecf20Sopenharmony_ci if (snd_pcm_running(ak4114->capture_substream)) { 5918c2ecf20Sopenharmony_ci // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res); 5928c2ecf20Sopenharmony_ci snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING); 5938c2ecf20Sopenharmony_ci res = 1; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci return res; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_check_rate_and_errors); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void ak4114_stats(struct work_struct *work) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct ak4114 *chip = container_of(work, struct ak4114, work.work); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (atomic_inc_return(&chip->wq_processing) == 1) 6068c2ecf20Sopenharmony_ci snd_ak4114_check_rate_and_errors(chip, chip->check_flags); 6078c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&chip->wq_processing)) 6088c2ecf20Sopenharmony_ci schedule_delayed_work(&chip->work, HZ / 10); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6128c2ecf20Sopenharmony_civoid snd_ak4114_suspend(struct ak4114 *chip) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci atomic_inc(&chip->wq_processing); /* don't schedule new work */ 6158c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&chip->work); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_suspend); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_civoid snd_ak4114_resume(struct ak4114 *chip) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci atomic_dec(&chip->wq_processing); 6228c2ecf20Sopenharmony_ci snd_ak4114_reinit(chip); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_ak4114_resume); 6258c2ecf20Sopenharmony_ci#endif 626