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