162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PMac DACA lowlevel functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) by Takashi Iwai <tiwai@suse.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/kmod.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <sound/core.h>
1462306a36Sopenharmony_ci#include "pmac.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* i2c address */
1762306a36Sopenharmony_ci#define DACA_I2C_ADDR	0x4d
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* registers */
2062306a36Sopenharmony_ci#define DACA_REG_SR	0x01
2162306a36Sopenharmony_ci#define DACA_REG_AVOL	0x02
2262306a36Sopenharmony_ci#define DACA_REG_GCFG	0x03
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* maximum volume value */
2562306a36Sopenharmony_ci#define DACA_VOL_MAX	0x38
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct pmac_daca {
2962306a36Sopenharmony_ci	struct pmac_keywest i2c;
3062306a36Sopenharmony_ci	int left_vol, right_vol;
3162306a36Sopenharmony_ci	unsigned int deemphasis : 1;
3262306a36Sopenharmony_ci	unsigned int amp_on : 1;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * initialize / detect DACA
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic int daca_init_client(struct pmac_keywest *i2c)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned short wdata = 0x00;
4262306a36Sopenharmony_ci	/* SR: no swap, 1bit delay, 32-48kHz */
4362306a36Sopenharmony_ci	/* GCFG: power amp inverted, DAC on */
4462306a36Sopenharmony_ci	if (i2c_smbus_write_byte_data(i2c->client, DACA_REG_SR, 0x08) < 0 ||
4562306a36Sopenharmony_ci	    i2c_smbus_write_byte_data(i2c->client, DACA_REG_GCFG, 0x05) < 0)
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci	return i2c_smbus_write_block_data(i2c->client, DACA_REG_AVOL,
4862306a36Sopenharmony_ci					  2, (unsigned char*)&wdata);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * update volume
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic int daca_set_volume(struct pmac_daca *mix)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	unsigned char data[2];
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (! mix->i2c.client)
5962306a36Sopenharmony_ci		return -ENODEV;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (mix->left_vol > DACA_VOL_MAX)
6262306a36Sopenharmony_ci		data[0] = DACA_VOL_MAX;
6362306a36Sopenharmony_ci	else
6462306a36Sopenharmony_ci		data[0] = mix->left_vol;
6562306a36Sopenharmony_ci	if (mix->right_vol > DACA_VOL_MAX)
6662306a36Sopenharmony_ci		data[1] = DACA_VOL_MAX;
6762306a36Sopenharmony_ci	else
6862306a36Sopenharmony_ci		data[1] = mix->right_vol;
6962306a36Sopenharmony_ci	data[1] |= mix->deemphasis ? 0x40 : 0;
7062306a36Sopenharmony_ci	if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL,
7162306a36Sopenharmony_ci				       2, data) < 0) {
7262306a36Sopenharmony_ci		snd_printk(KERN_ERR "failed to set volume \n");
7362306a36Sopenharmony_ci		return -EINVAL;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* deemphasis switch */
8062306a36Sopenharmony_ci#define daca_info_deemphasis		snd_ctl_boolean_mono_info
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int daca_get_deemphasis(struct snd_kcontrol *kcontrol,
8362306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
8662306a36Sopenharmony_ci	struct pmac_daca *mix;
8762306a36Sopenharmony_ci	mix = chip->mixer_data;
8862306a36Sopenharmony_ci	if (!mix)
8962306a36Sopenharmony_ci		return -ENODEV;
9062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0;
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
9562306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
9862306a36Sopenharmony_ci	struct pmac_daca *mix;
9962306a36Sopenharmony_ci	int change;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	mix = chip->mixer_data;
10262306a36Sopenharmony_ci	if (!mix)
10362306a36Sopenharmony_ci		return -ENODEV;
10462306a36Sopenharmony_ci	change = mix->deemphasis != ucontrol->value.integer.value[0];
10562306a36Sopenharmony_ci	if (change) {
10662306a36Sopenharmony_ci		mix->deemphasis = !!ucontrol->value.integer.value[0];
10762306a36Sopenharmony_ci		daca_set_volume(mix);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	return change;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* output volume */
11362306a36Sopenharmony_cistatic int daca_info_volume(struct snd_kcontrol *kcontrol,
11462306a36Sopenharmony_ci			    struct snd_ctl_elem_info *uinfo)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
11762306a36Sopenharmony_ci	uinfo->count = 2;
11862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
11962306a36Sopenharmony_ci	uinfo->value.integer.max = DACA_VOL_MAX;
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int daca_get_volume(struct snd_kcontrol *kcontrol,
12462306a36Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
12762306a36Sopenharmony_ci	struct pmac_daca *mix;
12862306a36Sopenharmony_ci	mix = chip->mixer_data;
12962306a36Sopenharmony_ci	if (!mix)
13062306a36Sopenharmony_ci		return -ENODEV;
13162306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = mix->left_vol;
13262306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = mix->right_vol;
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int daca_put_volume(struct snd_kcontrol *kcontrol,
13762306a36Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
14062306a36Sopenharmony_ci	struct pmac_daca *mix;
14162306a36Sopenharmony_ci	unsigned int vol[2];
14262306a36Sopenharmony_ci	int change;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	mix = chip->mixer_data;
14562306a36Sopenharmony_ci	if (!mix)
14662306a36Sopenharmony_ci		return -ENODEV;
14762306a36Sopenharmony_ci	vol[0] = ucontrol->value.integer.value[0];
14862306a36Sopenharmony_ci	vol[1] = ucontrol->value.integer.value[1];
14962306a36Sopenharmony_ci	if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX)
15062306a36Sopenharmony_ci		return -EINVAL;
15162306a36Sopenharmony_ci	change = mix->left_vol != vol[0] ||
15262306a36Sopenharmony_ci		mix->right_vol != vol[1];
15362306a36Sopenharmony_ci	if (change) {
15462306a36Sopenharmony_ci		mix->left_vol = vol[0];
15562306a36Sopenharmony_ci		mix->right_vol = vol[1];
15662306a36Sopenharmony_ci		daca_set_volume(mix);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	return change;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/* amplifier switch */
16262306a36Sopenharmony_ci#define daca_info_amp	daca_info_deemphasis
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int daca_get_amp(struct snd_kcontrol *kcontrol,
16562306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
16862306a36Sopenharmony_ci	struct pmac_daca *mix;
16962306a36Sopenharmony_ci	mix = chip->mixer_data;
17062306a36Sopenharmony_ci	if (!mix)
17162306a36Sopenharmony_ci		return -ENODEV;
17262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0;
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int daca_put_amp(struct snd_kcontrol *kcontrol,
17762306a36Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
18062306a36Sopenharmony_ci	struct pmac_daca *mix;
18162306a36Sopenharmony_ci	int change;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	mix = chip->mixer_data;
18462306a36Sopenharmony_ci	if (!mix)
18562306a36Sopenharmony_ci		return -ENODEV;
18662306a36Sopenharmony_ci	change = mix->amp_on != ucontrol->value.integer.value[0];
18762306a36Sopenharmony_ci	if (change) {
18862306a36Sopenharmony_ci		mix->amp_on = !!ucontrol->value.integer.value[0];
18962306a36Sopenharmony_ci		i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
19062306a36Sopenharmony_ci					  mix->amp_on ? 0x05 : 0x04);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	return change;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic const struct snd_kcontrol_new daca_mixers[] = {
19662306a36Sopenharmony_ci	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
19762306a36Sopenharmony_ci	  .name = "Deemphasis Switch",
19862306a36Sopenharmony_ci	  .info = daca_info_deemphasis,
19962306a36Sopenharmony_ci	  .get = daca_get_deemphasis,
20062306a36Sopenharmony_ci	  .put = daca_put_deemphasis
20162306a36Sopenharmony_ci	},
20262306a36Sopenharmony_ci	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
20362306a36Sopenharmony_ci	  .name = "Master Playback Volume",
20462306a36Sopenharmony_ci	  .info = daca_info_volume,
20562306a36Sopenharmony_ci	  .get = daca_get_volume,
20662306a36Sopenharmony_ci	  .put = daca_put_volume
20762306a36Sopenharmony_ci	},
20862306a36Sopenharmony_ci	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
20962306a36Sopenharmony_ci	  .name = "Power Amplifier Switch",
21062306a36Sopenharmony_ci	  .info = daca_info_amp,
21162306a36Sopenharmony_ci	  .get = daca_get_amp,
21262306a36Sopenharmony_ci	  .put = daca_put_amp
21362306a36Sopenharmony_ci	},
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci#ifdef CONFIG_PM
21862306a36Sopenharmony_cistatic void daca_resume(struct snd_pmac *chip)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct pmac_daca *mix = chip->mixer_data;
22162306a36Sopenharmony_ci	i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_SR, 0x08);
22262306a36Sopenharmony_ci	i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
22362306a36Sopenharmony_ci				  mix->amp_on ? 0x05 : 0x04);
22462306a36Sopenharmony_ci	daca_set_volume(mix);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci#endif /* CONFIG_PM */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void daca_cleanup(struct snd_pmac *chip)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct pmac_daca *mix = chip->mixer_data;
23262306a36Sopenharmony_ci	if (! mix)
23362306a36Sopenharmony_ci		return;
23462306a36Sopenharmony_ci	snd_pmac_keywest_cleanup(&mix->i2c);
23562306a36Sopenharmony_ci	kfree(mix);
23662306a36Sopenharmony_ci	chip->mixer_data = NULL;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* exported */
24062306a36Sopenharmony_ciint snd_pmac_daca_init(struct snd_pmac *chip)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	int i, err;
24362306a36Sopenharmony_ci	struct pmac_daca *mix;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	request_module("i2c-powermac");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
24862306a36Sopenharmony_ci	if (! mix)
24962306a36Sopenharmony_ci		return -ENOMEM;
25062306a36Sopenharmony_ci	chip->mixer_data = mix;
25162306a36Sopenharmony_ci	chip->mixer_free = daca_cleanup;
25262306a36Sopenharmony_ci	mix->amp_on = 1; /* default on */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	mix->i2c.addr = DACA_I2C_ADDR;
25562306a36Sopenharmony_ci	mix->i2c.init_client = daca_init_client;
25662306a36Sopenharmony_ci	mix->i2c.name = "DACA";
25762306a36Sopenharmony_ci	err = snd_pmac_keywest_init(&mix->i2c);
25862306a36Sopenharmony_ci	if (err < 0)
25962306a36Sopenharmony_ci		return err;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * build mixers
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	strcpy(chip->card->mixername, "PowerMac DACA");
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) {
26762306a36Sopenharmony_ci		err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip));
26862306a36Sopenharmony_ci		if (err < 0)
26962306a36Sopenharmony_ci			return err;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#ifdef CONFIG_PM
27362306a36Sopenharmony_ci	chip->resume = daca_resume;
27462306a36Sopenharmony_ci#endif
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
278