162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Routines for control of the AK4117 via 4-wire serial interface
462306a36Sopenharmony_ci *  IEC958 (S/PDIF) receiver by Asahi Kasei
562306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <sound/core.h>
1262306a36Sopenharmony_ci#include <sound/control.h>
1362306a36Sopenharmony_ci#include <sound/pcm.h>
1462306a36Sopenharmony_ci#include <sound/ak4117.h>
1562306a36Sopenharmony_ci#include <sound/asoundef.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
1862306a36Sopenharmony_ciMODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei");
1962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define AK4117_ADDR			0x00 /* fixed address */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void snd_ak4117_timer(struct timer_list *t);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	ak4117->write(ak4117->private_data, reg, val);
2862306a36Sopenharmony_ci	if (reg < sizeof(ak4117->regmap))
2962306a36Sopenharmony_ci		ak4117->regmap[reg] = val;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic inline unsigned char reg_read(struct ak4117 *ak4117, unsigned char reg)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return ak4117->read(ak4117->private_data, reg);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#if 0
3862306a36Sopenharmony_cistatic void reg_dump(struct ak4117 *ak4117)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int i;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	printk(KERN_DEBUG "AK4117 REG DUMP:\n");
4362306a36Sopenharmony_ci	for (i = 0; i < 0x1b; i++)
4462306a36Sopenharmony_ci		printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void snd_ak4117_free(struct ak4117 *chip)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	timer_shutdown_sync(&chip->timer);
5162306a36Sopenharmony_ci	kfree(chip);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int snd_ak4117_dev_free(struct snd_device *device)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct ak4117 *chip = device->device_data;
5762306a36Sopenharmony_ci	snd_ak4117_free(chip);
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciint snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
6262306a36Sopenharmony_ci		      const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct ak4117 *chip;
6562306a36Sopenharmony_ci	int err = 0;
6662306a36Sopenharmony_ci	unsigned char reg;
6762306a36Sopenharmony_ci	static const struct snd_device_ops ops = {
6862306a36Sopenharmony_ci		.dev_free =     snd_ak4117_dev_free,
6962306a36Sopenharmony_ci	};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
7262306a36Sopenharmony_ci	if (chip == NULL)
7362306a36Sopenharmony_ci		return -ENOMEM;
7462306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
7562306a36Sopenharmony_ci	chip->card = card;
7662306a36Sopenharmony_ci	chip->read = read;
7762306a36Sopenharmony_ci	chip->write = write;
7862306a36Sopenharmony_ci	chip->private_data = private_data;
7962306a36Sopenharmony_ci	timer_setup(&chip->timer, snd_ak4117_timer, 0);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (reg = 0; reg < 5; reg++)
8262306a36Sopenharmony_ci		chip->regmap[reg] = pgm[reg];
8362306a36Sopenharmony_ci	snd_ak4117_reinit(chip);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	chip->rcs0 = reg_read(chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
8662306a36Sopenharmony_ci	chip->rcs1 = reg_read(chip, AK4117_REG_RCS1);
8762306a36Sopenharmony_ci	chip->rcs2 = reg_read(chip, AK4117_REG_RCS2);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
9062306a36Sopenharmony_ci	if (err < 0)
9162306a36Sopenharmony_ci		goto __fail;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (r_ak4117)
9462306a36Sopenharmony_ci		*r_ak4117 = chip;
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci      __fail:
9862306a36Sopenharmony_ci	snd_ak4117_free(chip);
9962306a36Sopenharmony_ci	return err;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	if (reg >= 5)
10562306a36Sopenharmony_ci		return;
10662306a36Sopenharmony_ci	reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_civoid snd_ak4117_reinit(struct ak4117 *chip)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	del_timer(&chip->timer);
11462306a36Sopenharmony_ci	chip->init = 1;
11562306a36Sopenharmony_ci	/* bring the chip to reset state and powerdown state */
11662306a36Sopenharmony_ci	reg_write(chip, AK4117_REG_PWRDN, 0);
11762306a36Sopenharmony_ci	udelay(200);
11862306a36Sopenharmony_ci	/* release reset, but leave powerdown */
11962306a36Sopenharmony_ci	reg_write(chip, AK4117_REG_PWRDN, (old | AK4117_RST) & ~AK4117_PWN);
12062306a36Sopenharmony_ci	udelay(200);
12162306a36Sopenharmony_ci	for (reg = 1; reg < 5; reg++)
12262306a36Sopenharmony_ci		reg_write(chip, reg, chip->regmap[reg]);
12362306a36Sopenharmony_ci	/* release powerdown, everything is initialized now */
12462306a36Sopenharmony_ci	reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN);
12562306a36Sopenharmony_ci	chip->init = 0;
12662306a36Sopenharmony_ci	mod_timer(&chip->timer, 1 + jiffies);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic unsigned int external_rate(unsigned char rcs1)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) {
13262306a36Sopenharmony_ci	case AK4117_FS_32000HZ: return 32000;
13362306a36Sopenharmony_ci	case AK4117_FS_44100HZ: return 44100;
13462306a36Sopenharmony_ci	case AK4117_FS_48000HZ: return 48000;
13562306a36Sopenharmony_ci	case AK4117_FS_88200HZ: return 88200;
13662306a36Sopenharmony_ci	case AK4117_FS_96000HZ: return 96000;
13762306a36Sopenharmony_ci	case AK4117_FS_176400HZ: return 176400;
13862306a36Sopenharmony_ci	case AK4117_FS_192000HZ: return 192000;
13962306a36Sopenharmony_ci	default:		return 0;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int snd_ak4117_in_error_info(struct snd_kcontrol *kcontrol,
14462306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
14762306a36Sopenharmony_ci	uinfo->count = 1;
14862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
14962306a36Sopenharmony_ci	uinfo->value.integer.max = LONG_MAX;
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
15462306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
15962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
16062306a36Sopenharmony_ci		       chip->errors[kcontrol->private_value];
16162306a36Sopenharmony_ci	chip->errors[kcontrol->private_value] = 0;
16262306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci#define snd_ak4117_in_bit_info		snd_ctl_boolean_mono_info
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol,
16962306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
17262306a36Sopenharmony_ci	unsigned char reg = kcontrol->private_value & 0xff;
17362306a36Sopenharmony_ci	unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
17462306a36Sopenharmony_ci	unsigned char inv = (kcontrol->private_value >> 31) & 1;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int snd_ak4117_rx_info(struct snd_kcontrol *kcontrol,
18162306a36Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
18462306a36Sopenharmony_ci	uinfo->count = 1;
18562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
18662306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int snd_ak4117_rx_get(struct snd_kcontrol *kcontrol,
19162306a36Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0;
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int snd_ak4117_rx_put(struct snd_kcontrol *kcontrol,
20062306a36Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
20362306a36Sopenharmony_ci	int change;
20462306a36Sopenharmony_ci	u8 old_val;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
20762306a36Sopenharmony_ci	old_val = chip->regmap[AK4117_REG_IO];
20862306a36Sopenharmony_ci	change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
20962306a36Sopenharmony_ci	if (change)
21062306a36Sopenharmony_ci		reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
21162306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
21262306a36Sopenharmony_ci	return change;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int snd_ak4117_rate_info(struct snd_kcontrol *kcontrol,
21662306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
21962306a36Sopenharmony_ci	uinfo->count = 1;
22062306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
22162306a36Sopenharmony_ci	uinfo->value.integer.max = 192000;
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int snd_ak4117_rate_get(struct snd_kcontrol *kcontrol,
22662306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4117_REG_RCS1));
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int snd_ak4117_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
23762306a36Sopenharmony_ci	uinfo->count = 1;
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int snd_ak4117_spdif_get(struct snd_kcontrol *kcontrol,
24262306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
24562306a36Sopenharmony_ci	unsigned i;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++)
24862306a36Sopenharmony_ci		ucontrol->value.iec958.status[i] = reg_read(chip, AK4117_REG_RXCSB0 + i);
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int snd_ak4117_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
25562306a36Sopenharmony_ci	uinfo->count = 1;
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int snd_ak4117_spdif_mask_get(struct snd_kcontrol *kcontrol,
26062306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE);
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int snd_ak4117_spdif_pinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
27062306a36Sopenharmony_ci	uinfo->value.integer.max = 0xffff;
27162306a36Sopenharmony_ci	uinfo->count = 4;
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int snd_ak4117_spdif_pget(struct snd_kcontrol *kcontrol,
27662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
27962306a36Sopenharmony_ci	unsigned short tmp;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = 0xf8f2;
28262306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = 0x4e1f;
28362306a36Sopenharmony_ci	tmp = reg_read(chip, AK4117_REG_Pc0) | (reg_read(chip, AK4117_REG_Pc1) << 8);
28462306a36Sopenharmony_ci	ucontrol->value.integer.value[2] = tmp;
28562306a36Sopenharmony_ci	tmp = reg_read(chip, AK4117_REG_Pd0) | (reg_read(chip, AK4117_REG_Pd1) << 8);
28662306a36Sopenharmony_ci	ucontrol->value.integer.value[3] = tmp;
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int snd_ak4117_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
29362306a36Sopenharmony_ci	uinfo->count = AK4117_REG_QSUB_SIZE;
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int snd_ak4117_spdif_qget(struct snd_kcontrol *kcontrol,
29862306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
30162306a36Sopenharmony_ci	unsigned i;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	for (i = 0; i < AK4117_REG_QSUB_SIZE; i++)
30462306a36Sopenharmony_ci		ucontrol->value.bytes.data[i] = reg_read(chip, AK4117_REG_QSUB_ADDR + i);
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/* Don't forget to change AK4117_CONTROLS define!!! */
30962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
31262306a36Sopenharmony_ci	.name =		"IEC958 Parity Errors",
31362306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
31462306a36Sopenharmony_ci	.info =		snd_ak4117_in_error_info,
31562306a36Sopenharmony_ci	.get =		snd_ak4117_in_error_get,
31662306a36Sopenharmony_ci	.private_value = AK4117_PARITY_ERRORS,
31762306a36Sopenharmony_ci},
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
32062306a36Sopenharmony_ci	.name =		"IEC958 V-Bit Errors",
32162306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
32262306a36Sopenharmony_ci	.info =		snd_ak4117_in_error_info,
32362306a36Sopenharmony_ci	.get =		snd_ak4117_in_error_get,
32462306a36Sopenharmony_ci	.private_value = AK4117_V_BIT_ERRORS,
32562306a36Sopenharmony_ci},
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
32862306a36Sopenharmony_ci	.name =		"IEC958 C-CRC Errors",
32962306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
33062306a36Sopenharmony_ci	.info =		snd_ak4117_in_error_info,
33162306a36Sopenharmony_ci	.get =		snd_ak4117_in_error_get,
33262306a36Sopenharmony_ci	.private_value = AK4117_CCRC_ERRORS,
33362306a36Sopenharmony_ci},
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
33662306a36Sopenharmony_ci	.name =		"IEC958 Q-CRC Errors",
33762306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
33862306a36Sopenharmony_ci	.info =		snd_ak4117_in_error_info,
33962306a36Sopenharmony_ci	.get =		snd_ak4117_in_error_get,
34062306a36Sopenharmony_ci	.private_value = AK4117_QCRC_ERRORS,
34162306a36Sopenharmony_ci},
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
34462306a36Sopenharmony_ci	.name =		"IEC958 External Rate",
34562306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
34662306a36Sopenharmony_ci	.info =		snd_ak4117_rate_info,
34762306a36Sopenharmony_ci	.get =		snd_ak4117_rate_get,
34862306a36Sopenharmony_ci},
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
35162306a36Sopenharmony_ci	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
35262306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
35362306a36Sopenharmony_ci	.info =		snd_ak4117_spdif_mask_info,
35462306a36Sopenharmony_ci	.get =		snd_ak4117_spdif_mask_get,
35562306a36Sopenharmony_ci},
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
35862306a36Sopenharmony_ci	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
35962306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
36062306a36Sopenharmony_ci	.info =		snd_ak4117_spdif_info,
36162306a36Sopenharmony_ci	.get =		snd_ak4117_spdif_get,
36262306a36Sopenharmony_ci},
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
36562306a36Sopenharmony_ci	.name =		"IEC958 Preamble Capture Default",
36662306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
36762306a36Sopenharmony_ci	.info =		snd_ak4117_spdif_pinfo,
36862306a36Sopenharmony_ci	.get =		snd_ak4117_spdif_pget,
36962306a36Sopenharmony_ci},
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
37262306a36Sopenharmony_ci	.name =		"IEC958 Q-subcode Capture Default",
37362306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
37462306a36Sopenharmony_ci	.info =		snd_ak4117_spdif_qinfo,
37562306a36Sopenharmony_ci	.get =		snd_ak4117_spdif_qget,
37662306a36Sopenharmony_ci},
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
37962306a36Sopenharmony_ci	.name =		"IEC958 Audio",
38062306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
38162306a36Sopenharmony_ci	.info =		snd_ak4117_in_bit_info,
38262306a36Sopenharmony_ci	.get =		snd_ak4117_in_bit_get,
38362306a36Sopenharmony_ci	.private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0,
38462306a36Sopenharmony_ci},
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
38762306a36Sopenharmony_ci	.name =		"IEC958 Non-PCM Bitstream",
38862306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
38962306a36Sopenharmony_ci	.info =		snd_ak4117_in_bit_info,
39062306a36Sopenharmony_ci	.get =		snd_ak4117_in_bit_get,
39162306a36Sopenharmony_ci	.private_value = (5<<8) | AK4117_REG_RCS1,
39262306a36Sopenharmony_ci},
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
39562306a36Sopenharmony_ci	.name =		"IEC958 DTS Bitstream",
39662306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
39762306a36Sopenharmony_ci	.info =		snd_ak4117_in_bit_info,
39862306a36Sopenharmony_ci	.get =		snd_ak4117_in_bit_get,
39962306a36Sopenharmony_ci	.private_value = (6<<8) | AK4117_REG_RCS1,
40062306a36Sopenharmony_ci},
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
40362306a36Sopenharmony_ci	.name =		"AK4117 Input Select",
40462306a36Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
40562306a36Sopenharmony_ci	.info =		snd_ak4117_rx_info,
40662306a36Sopenharmony_ci	.get =		snd_ak4117_rx_get,
40762306a36Sopenharmony_ci	.put =		snd_ak4117_rx_put,
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ciint snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substream)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
41462306a36Sopenharmony_ci	unsigned int idx;
41562306a36Sopenharmony_ci	int err;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (snd_BUG_ON(!cap_substream))
41862306a36Sopenharmony_ci		return -EINVAL;
41962306a36Sopenharmony_ci	ak4117->substream = cap_substream;
42062306a36Sopenharmony_ci	for (idx = 0; idx < AK4117_CONTROLS; idx++) {
42162306a36Sopenharmony_ci		kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
42262306a36Sopenharmony_ci		if (kctl == NULL)
42362306a36Sopenharmony_ci			return -ENOMEM;
42462306a36Sopenharmony_ci		kctl->id.device = cap_substream->pcm->device;
42562306a36Sopenharmony_ci		kctl->id.subdevice = cap_substream->number;
42662306a36Sopenharmony_ci		err = snd_ctl_add(ak4117->card, kctl);
42762306a36Sopenharmony_ci		if (err < 0)
42862306a36Sopenharmony_ci			return err;
42962306a36Sopenharmony_ci		ak4117->kctls[idx] = kctl;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciint snd_ak4117_external_rate(struct ak4117 *ak4117)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	unsigned char rcs1;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
43962306a36Sopenharmony_ci	return external_rate(rcs1);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciint snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = ak4117->substream ? ak4117->substream->runtime : NULL;
44562306a36Sopenharmony_ci	unsigned long _flags;
44662306a36Sopenharmony_ci	int res = 0;
44762306a36Sopenharmony_ci	unsigned char rcs0, rcs1, rcs2;
44862306a36Sopenharmony_ci	unsigned char c0, c1;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
45162306a36Sopenharmony_ci	if (flags & AK4117_CHECK_NO_STAT)
45262306a36Sopenharmony_ci		goto __rate;
45362306a36Sopenharmony_ci	rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
45462306a36Sopenharmony_ci	rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
45562306a36Sopenharmony_ci	// printk(KERN_DEBUG "AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2);
45662306a36Sopenharmony_ci	spin_lock_irqsave(&ak4117->lock, _flags);
45762306a36Sopenharmony_ci	if (rcs0 & AK4117_PAR)
45862306a36Sopenharmony_ci		ak4117->errors[AK4117_PARITY_ERRORS]++;
45962306a36Sopenharmony_ci	if (rcs0 & AK4117_V)
46062306a36Sopenharmony_ci		ak4117->errors[AK4117_V_BIT_ERRORS]++;
46162306a36Sopenharmony_ci	if (rcs2 & AK4117_CCRC)
46262306a36Sopenharmony_ci		ak4117->errors[AK4117_CCRC_ERRORS]++;
46362306a36Sopenharmony_ci	if (rcs2 & AK4117_QCRC)
46462306a36Sopenharmony_ci		ak4117->errors[AK4117_QCRC_ERRORS]++;
46562306a36Sopenharmony_ci	c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
46662306a36Sopenharmony_ci                     (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
46762306a36Sopenharmony_ci	c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
46862306a36Sopenharmony_ci	             (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
46962306a36Sopenharmony_ci	ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
47062306a36Sopenharmony_ci	ak4117->rcs1 = rcs1;
47162306a36Sopenharmony_ci	ak4117->rcs2 = rcs2;
47262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ak4117->lock, _flags);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (rcs0 & AK4117_PAR)
47562306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id);
47662306a36Sopenharmony_ci	if (rcs0 & AK4117_V)
47762306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[1]->id);
47862306a36Sopenharmony_ci	if (rcs2 & AK4117_CCRC)
47962306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[2]->id);
48062306a36Sopenharmony_ci	if (rcs2 & AK4117_QCRC)
48162306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[3]->id);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* rate change */
48462306a36Sopenharmony_ci	if (c1 & 0x0f)
48562306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[4]->id);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT))
48862306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[6]->id);
48962306a36Sopenharmony_ci	if (c0 & AK4117_QINT)
49062306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[8]->id);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (c0 & AK4117_AUDION)
49362306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[9]->id);
49462306a36Sopenharmony_ci	if (c1 & AK4117_NPCM)
49562306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[10]->id);
49662306a36Sopenharmony_ci	if (c1 & AK4117_DTSCD)
49762306a36Sopenharmony_ci		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[11]->id);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (ak4117->change_callback && (c0 | c1) != 0)
50062306a36Sopenharmony_ci		ak4117->change_callback(ak4117, c0, c1);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci      __rate:
50362306a36Sopenharmony_ci	/* compare rate */
50462306a36Sopenharmony_ci	res = external_rate(rcs1);
50562306a36Sopenharmony_ci	if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
50662306a36Sopenharmony_ci		snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
50762306a36Sopenharmony_ci		if (snd_pcm_running(ak4117->substream)) {
50862306a36Sopenharmony_ci			// printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
50962306a36Sopenharmony_ci			snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING);
51062306a36Sopenharmony_ci			wake_up(&runtime->sleep);
51162306a36Sopenharmony_ci			res = 1;
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci		snd_pcm_stream_unlock_irqrestore(ak4117->substream, _flags);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	return res;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic void snd_ak4117_timer(struct timer_list *t)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct ak4117 *chip = from_timer(chip, t, timer);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (chip->init)
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci	snd_ak4117_check_rate_and_errors(chip, 0);
52562306a36Sopenharmony_ci	mod_timer(&chip->timer, 1 + jiffies);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_create);
52962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_reg_write);
53062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_reinit);
53162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_build);
53262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_external_rate);
53362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ak4117_check_rate_and_errors);
534