18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Apple Onboard Audio driver for Onyx codec
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This is a driver for the pcm3052 codec chip (codenamed Onyx)
88c2ecf20Sopenharmony_ci * that is present in newer Apple hardware (with digital output).
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * The Onyx codec has the following connections (listed by the bit
118c2ecf20Sopenharmony_ci * to be used in aoa_codec.connected):
128c2ecf20Sopenharmony_ci *  0: analog output
138c2ecf20Sopenharmony_ci *  1: digital output
148c2ecf20Sopenharmony_ci *  2: line input
158c2ecf20Sopenharmony_ci *  3: microphone input
168c2ecf20Sopenharmony_ci * Note that even though I know of no machine that has for example
178c2ecf20Sopenharmony_ci * the digital output connected but not the analog, I have handled
188c2ecf20Sopenharmony_ci * all the different cases in the code so that this driver may serve
198c2ecf20Sopenharmony_ci * as a good example of what to do.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * NOTE: This driver assumes that there's at most one chip to be
228c2ecf20Sopenharmony_ci * 	 used with one alsa card, in form of creating all kinds
238c2ecf20Sopenharmony_ci *	 of mixer elements without regard for their existence.
248c2ecf20Sopenharmony_ci *	 But snd-aoa assumes that there's at most one card, so
258c2ecf20Sopenharmony_ci *	 this means you can only have one onyx on a system. This
268c2ecf20Sopenharmony_ci *	 should probably be fixed by changing the assumption of
278c2ecf20Sopenharmony_ci *	 having just a single card on a system, and making the
288c2ecf20Sopenharmony_ci *	 'card' pointer accessible to anyone who needs it instead
298c2ecf20Sopenharmony_ci *	 of hiding it in the aoa_snd_* functions...
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <linux/module.h>
338c2ecf20Sopenharmony_ci#include <linux/slab.h>
348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "onyx.h"
398c2ecf20Sopenharmony_ci#include "../aoa.h"
408c2ecf20Sopenharmony_ci#include "../soundbus/soundbus.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define PFX "snd-aoa-codec-onyx: "
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct onyx {
468c2ecf20Sopenharmony_ci	/* cache registers 65 to 80, they are write-only! */
478c2ecf20Sopenharmony_ci	u8			cache[16];
488c2ecf20Sopenharmony_ci	struct i2c_client	*i2c;
498c2ecf20Sopenharmony_ci	struct aoa_codec	codec;
508c2ecf20Sopenharmony_ci	u32			initialised:1,
518c2ecf20Sopenharmony_ci				spdif_locked:1,
528c2ecf20Sopenharmony_ci				analog_locked:1,
538c2ecf20Sopenharmony_ci				original_mute:2;
548c2ecf20Sopenharmony_ci	int			open_count;
558c2ecf20Sopenharmony_ci	struct codec_info	*codec_info;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* mutex serializes concurrent access to the device
588c2ecf20Sopenharmony_ci	 * and this structure.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	struct mutex mutex;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci#define codec_to_onyx(c) container_of(c, struct onyx, codec)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* both return 0 if all ok, else on error */
658c2ecf20Sopenharmony_cistatic int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	s32 v;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (reg != ONYX_REG_CONTROL) {
708c2ecf20Sopenharmony_ci		*value = onyx->cache[reg-FIRSTREGISTER];
718c2ecf20Sopenharmony_ci		return 0;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	v = i2c_smbus_read_byte_data(onyx->i2c, reg);
748c2ecf20Sopenharmony_ci	if (v < 0) {
758c2ecf20Sopenharmony_ci		*value = 0;
768c2ecf20Sopenharmony_ci		return -1;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	*value = (u8)v;
798c2ecf20Sopenharmony_ci	onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int onyx_write_register(struct onyx *onyx, u8 reg, u8 value)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int result;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	result = i2c_smbus_write_byte_data(onyx->i2c, reg, value);
888c2ecf20Sopenharmony_ci	if (!result)
898c2ecf20Sopenharmony_ci		onyx->cache[reg-FIRSTREGISTER] = value;
908c2ecf20Sopenharmony_ci	return result;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* alsa stuff */
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int onyx_dev_register(struct snd_device *dev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic const struct snd_device_ops ops = {
1018c2ecf20Sopenharmony_ci	.dev_register = onyx_dev_register,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* this is necessary because most alsa mixer programs
1058c2ecf20Sopenharmony_ci * can't properly handle the negative range */
1068c2ecf20Sopenharmony_ci#define VOLUME_RANGE_SHIFT	128
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int onyx_snd_vol_info(struct snd_kcontrol *kcontrol,
1098c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info *uinfo)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1128c2ecf20Sopenharmony_ci	uinfo->count = 2;
1138c2ecf20Sopenharmony_ci	uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT;
1148c2ecf20Sopenharmony_ci	uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT;
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
1198c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
1228c2ecf20Sopenharmony_ci	s8 l, r;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
1258c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
1268c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
1278c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
1308c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
1368c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
1398c2ecf20Sopenharmony_ci	s8 l, r;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT ||
1428c2ecf20Sopenharmony_ci	    ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT)
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT ||
1458c2ecf20Sopenharmony_ci	    ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
1468c2ecf20Sopenharmony_ci		return -EINVAL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
1498c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
1508c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
1538c2ecf20Sopenharmony_ci	    r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
1548c2ecf20Sopenharmony_ci		mutex_unlock(&onyx->mutex);
1558c2ecf20Sopenharmony_ci		return 0;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
1598c2ecf20Sopenharmony_ci			    ucontrol->value.integer.value[0]
1608c2ecf20Sopenharmony_ci			     - VOLUME_RANGE_SHIFT);
1618c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
1628c2ecf20Sopenharmony_ci			    ucontrol->value.integer.value[1]
1638c2ecf20Sopenharmony_ci			     - VOLUME_RANGE_SHIFT);
1648c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 1;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new volume_control = {
1708c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1718c2ecf20Sopenharmony_ci	.name = "Master Playback Volume",
1728c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
1738c2ecf20Sopenharmony_ci	.info = onyx_snd_vol_info,
1748c2ecf20Sopenharmony_ci	.get = onyx_snd_vol_get,
1758c2ecf20Sopenharmony_ci	.put = onyx_snd_vol_put,
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* like above, this is necessary because a lot
1798c2ecf20Sopenharmony_ci * of alsa mixer programs don't handle ranges
1808c2ecf20Sopenharmony_ci * that don't start at 0 properly.
1818c2ecf20Sopenharmony_ci * even alsamixer is one of them... */
1828c2ecf20Sopenharmony_ci#define INPUTGAIN_RANGE_SHIFT	(-3)
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol,
1858c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info *uinfo)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1888c2ecf20Sopenharmony_ci	uinfo->count = 1;
1898c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT;
1908c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT;
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
1958c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
1988c2ecf20Sopenharmony_ci	u8 ig;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
2018c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
2028c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
2058c2ecf20Sopenharmony_ci		(ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return 0;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
2118c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
2148c2ecf20Sopenharmony_ci	u8 v, n;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
2178c2ecf20Sopenharmony_ci	    ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
2208c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
2218c2ecf20Sopenharmony_ci	n = v;
2228c2ecf20Sopenharmony_ci	n &= ~ONYX_ADC_PGA_GAIN_MASK;
2238c2ecf20Sopenharmony_ci	n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
2248c2ecf20Sopenharmony_ci		& ONYX_ADC_PGA_GAIN_MASK;
2258c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
2268c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return n != v;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new inputgain_control = {
2328c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2338c2ecf20Sopenharmony_ci	.name = "Master Capture Volume",
2348c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
2358c2ecf20Sopenharmony_ci	.info = onyx_snd_inputgain_info,
2368c2ecf20Sopenharmony_ci	.get = onyx_snd_inputgain_get,
2378c2ecf20Sopenharmony_ci	.put = onyx_snd_inputgain_put,
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
2418c2ecf20Sopenharmony_ci	struct snd_ctl_elem_info *uinfo)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	static const char * const texts[] = { "Line-In", "Microphone" };
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, texts);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
2498c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
2528c2ecf20Sopenharmony_ci	s8 v;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
2558c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
2568c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void onyx_set_capture_source(struct onyx *onyx, int mic)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	s8 v;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
2688c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
2698c2ecf20Sopenharmony_ci	v &= ~ONYX_ADC_INPUT_MIC;
2708c2ecf20Sopenharmony_ci	if (mic)
2718c2ecf20Sopenharmony_ci		v |= ONYX_ADC_INPUT_MIC;
2728c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
2738c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
2778c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 1)
2808c2ecf20Sopenharmony_ci		return -EINVAL;
2818c2ecf20Sopenharmony_ci	onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
2828c2ecf20Sopenharmony_ci				ucontrol->value.enumerated.item[0]);
2838c2ecf20Sopenharmony_ci	return 1;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new capture_source_control = {
2878c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2888c2ecf20Sopenharmony_ci	/* If we name this 'Input Source', it properly shows up in
2898c2ecf20Sopenharmony_ci	 * alsamixer as a selection, * but it's shown under the
2908c2ecf20Sopenharmony_ci	 * 'Playback' category.
2918c2ecf20Sopenharmony_ci	 * If I name it 'Capture Source', it shows up in strange
2928c2ecf20Sopenharmony_ci	 * ways (two bools of which one can be selected at a
2938c2ecf20Sopenharmony_ci	 * time) but at least it's shown in the 'Capture'
2948c2ecf20Sopenharmony_ci	 * category.
2958c2ecf20Sopenharmony_ci	 * I was told that this was due to backward compatibility,
2968c2ecf20Sopenharmony_ci	 * but I don't understand then why the mangling is *not*
2978c2ecf20Sopenharmony_ci	 * done when I name it "Input Source".....
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	.name = "Capture Source",
3008c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
3018c2ecf20Sopenharmony_ci	.info = onyx_snd_capture_source_info,
3028c2ecf20Sopenharmony_ci	.get = onyx_snd_capture_source_get,
3038c2ecf20Sopenharmony_ci	.put = onyx_snd_capture_source_put,
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci#define onyx_snd_mute_info	snd_ctl_boolean_stereo_info
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
3098c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
3128c2ecf20Sopenharmony_ci	u8 c;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
3158c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
3168c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
3198c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return 0;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
3258c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
3288c2ecf20Sopenharmony_ci	u8 v = 0, c = 0;
3298c2ecf20Sopenharmony_ci	int err = -EBUSY;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
3328c2ecf20Sopenharmony_ci	if (onyx->analog_locked)
3338c2ecf20Sopenharmony_ci		goto out_unlock;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
3368c2ecf20Sopenharmony_ci	c = v;
3378c2ecf20Sopenharmony_ci	c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT);
3388c2ecf20Sopenharmony_ci	if (!ucontrol->value.integer.value[0])
3398c2ecf20Sopenharmony_ci		c |= ONYX_MUTE_LEFT;
3408c2ecf20Sopenharmony_ci	if (!ucontrol->value.integer.value[1])
3418c2ecf20Sopenharmony_ci		c |= ONYX_MUTE_RIGHT;
3428c2ecf20Sopenharmony_ci	err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci out_unlock:
3458c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return !err ? (v != c) : err;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mute_control = {
3518c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3528c2ecf20Sopenharmony_ci	.name = "Master Playback Switch",
3538c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
3548c2ecf20Sopenharmony_ci	.info = onyx_snd_mute_info,
3558c2ecf20Sopenharmony_ci	.get = onyx_snd_mute_get,
3568c2ecf20Sopenharmony_ci	.put = onyx_snd_mute_put,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci#define onyx_snd_single_bit_info	snd_ctl_boolean_mono_info
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci#define FLAG_POLARITY_INVERT	1
3638c2ecf20Sopenharmony_ci#define FLAG_SPDIFLOCK		2
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
3668c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
3698c2ecf20Sopenharmony_ci	u8 c;
3708c2ecf20Sopenharmony_ci	long int pv = kcontrol->private_value;
3718c2ecf20Sopenharmony_ci	u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
3728c2ecf20Sopenharmony_ci	u8 address = (pv >> 8) & 0xff;
3738c2ecf20Sopenharmony_ci	u8 mask = pv & 0xff;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
3768c2ecf20Sopenharmony_ci	onyx_read_register(onyx, address, &c);
3778c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
3858c2ecf20Sopenharmony_ci	struct snd_ctl_elem_value *ucontrol)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
3888c2ecf20Sopenharmony_ci	u8 v = 0, c = 0;
3898c2ecf20Sopenharmony_ci	int err;
3908c2ecf20Sopenharmony_ci	long int pv = kcontrol->private_value;
3918c2ecf20Sopenharmony_ci	u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
3928c2ecf20Sopenharmony_ci	u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK;
3938c2ecf20Sopenharmony_ci	u8 address = (pv >> 8) & 0xff;
3948c2ecf20Sopenharmony_ci	u8 mask = pv & 0xff;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
3978c2ecf20Sopenharmony_ci	if (spdiflock && onyx->spdif_locked) {
3988c2ecf20Sopenharmony_ci		/* even if alsamixer doesn't care.. */
3998c2ecf20Sopenharmony_ci		err = -EBUSY;
4008c2ecf20Sopenharmony_ci		goto out_unlock;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	onyx_read_register(onyx, address, &v);
4038c2ecf20Sopenharmony_ci	c = v;
4048c2ecf20Sopenharmony_ci	c &= ~(mask);
4058c2ecf20Sopenharmony_ci	if (!!ucontrol->value.integer.value[0] ^ polarity)
4068c2ecf20Sopenharmony_ci		c |= mask;
4078c2ecf20Sopenharmony_ci	err = onyx_write_register(onyx, address, c);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci out_unlock:
4108c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return !err ? (v != c) : err;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci#define SINGLE_BIT(n, type, description, address, mask, flags)	 	\
4168c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new n##_control = {			\
4178c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_##type,				\
4188c2ecf20Sopenharmony_ci	.name = description,						\
4198c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,			\
4208c2ecf20Sopenharmony_ci	.info = onyx_snd_single_bit_info,				\
4218c2ecf20Sopenharmony_ci	.get = onyx_snd_single_bit_get,					\
4228c2ecf20Sopenharmony_ci	.put = onyx_snd_single_bit_put,					\
4238c2ecf20Sopenharmony_ci	.private_value = (flags << 16) | (address << 8) | mask		\
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ciSINGLE_BIT(spdif,
4278c2ecf20Sopenharmony_ci	   MIXER,
4288c2ecf20Sopenharmony_ci	   SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
4298c2ecf20Sopenharmony_ci	   ONYX_REG_DIG_INFO4,
4308c2ecf20Sopenharmony_ci	   ONYX_SPDIF_ENABLE,
4318c2ecf20Sopenharmony_ci	   FLAG_SPDIFLOCK);
4328c2ecf20Sopenharmony_ciSINGLE_BIT(ovr1,
4338c2ecf20Sopenharmony_ci	   MIXER,
4348c2ecf20Sopenharmony_ci	   "Oversampling Rate",
4358c2ecf20Sopenharmony_ci	   ONYX_REG_DAC_CONTROL,
4368c2ecf20Sopenharmony_ci	   ONYX_OVR1,
4378c2ecf20Sopenharmony_ci	   0);
4388c2ecf20Sopenharmony_ciSINGLE_BIT(flt0,
4398c2ecf20Sopenharmony_ci	   MIXER,
4408c2ecf20Sopenharmony_ci	   "Fast Digital Filter Rolloff",
4418c2ecf20Sopenharmony_ci	   ONYX_REG_DAC_FILTER,
4428c2ecf20Sopenharmony_ci	   ONYX_ROLLOFF_FAST,
4438c2ecf20Sopenharmony_ci	   FLAG_POLARITY_INVERT);
4448c2ecf20Sopenharmony_ciSINGLE_BIT(hpf,
4458c2ecf20Sopenharmony_ci	   MIXER,
4468c2ecf20Sopenharmony_ci	   "Highpass Filter",
4478c2ecf20Sopenharmony_ci	   ONYX_REG_ADC_HPF_BYPASS,
4488c2ecf20Sopenharmony_ci	   ONYX_HPF_DISABLE,
4498c2ecf20Sopenharmony_ci	   FLAG_POLARITY_INVERT);
4508c2ecf20Sopenharmony_ciSINGLE_BIT(dm12,
4518c2ecf20Sopenharmony_ci	   MIXER,
4528c2ecf20Sopenharmony_ci	   "Digital De-Emphasis",
4538c2ecf20Sopenharmony_ci	   ONYX_REG_DAC_DEEMPH,
4548c2ecf20Sopenharmony_ci	   ONYX_DIGDEEMPH_CTRL,
4558c2ecf20Sopenharmony_ci	   0);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic int onyx_spdif_info(struct snd_kcontrol *kcontrol,
4588c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_info *uinfo)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
4618c2ecf20Sopenharmony_ci	uinfo->count = 1;
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
4668c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	/* datasheet page 30, all others are 0 */
4698c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0x3e;
4708c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = 0x3f;
4738c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[4] = 0x0f;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new onyx_spdif_mask = {
4798c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
4808c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
4818c2ecf20Sopenharmony_ci	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
4828c2ecf20Sopenharmony_ci	.info =		onyx_spdif_info,
4838c2ecf20Sopenharmony_ci	.get =		onyx_spdif_mask_get,
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int onyx_spdif_get(struct snd_kcontrol *kcontrol,
4878c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
4908c2ecf20Sopenharmony_ci	u8 v;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
4938c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
4948c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = v & 0x3e;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v);
4978c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = v;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
5008c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = v & 0x3f;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
5038c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[4] = v & 0x0f;
5048c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int onyx_spdif_put(struct snd_kcontrol *kcontrol,
5108c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
5138c2ecf20Sopenharmony_ci	u8 v;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
5168c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
5178c2ecf20Sopenharmony_ci	v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
5188c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	v = ucontrol->value.iec958.status[1];
5218c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
5248c2ecf20Sopenharmony_ci	v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f);
5258c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
5288c2ecf20Sopenharmony_ci	v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
5298c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
5308c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 1;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new onyx_spdif_ctrl = {
5368c2ecf20Sopenharmony_ci	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
5378c2ecf20Sopenharmony_ci	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
5388c2ecf20Sopenharmony_ci	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
5398c2ecf20Sopenharmony_ci	.info =		onyx_spdif_info,
5408c2ecf20Sopenharmony_ci	.get =		onyx_spdif_get,
5418c2ecf20Sopenharmony_ci	.put =		onyx_spdif_put,
5428c2ecf20Sopenharmony_ci};
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/* our registers */
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic const u8 register_map[] = {
5478c2ecf20Sopenharmony_ci	ONYX_REG_DAC_ATTEN_LEFT,
5488c2ecf20Sopenharmony_ci	ONYX_REG_DAC_ATTEN_RIGHT,
5498c2ecf20Sopenharmony_ci	ONYX_REG_CONTROL,
5508c2ecf20Sopenharmony_ci	ONYX_REG_DAC_CONTROL,
5518c2ecf20Sopenharmony_ci	ONYX_REG_DAC_DEEMPH,
5528c2ecf20Sopenharmony_ci	ONYX_REG_DAC_FILTER,
5538c2ecf20Sopenharmony_ci	ONYX_REG_DAC_OUTPHASE,
5548c2ecf20Sopenharmony_ci	ONYX_REG_ADC_CONTROL,
5558c2ecf20Sopenharmony_ci	ONYX_REG_ADC_HPF_BYPASS,
5568c2ecf20Sopenharmony_ci	ONYX_REG_DIG_INFO1,
5578c2ecf20Sopenharmony_ci	ONYX_REG_DIG_INFO2,
5588c2ecf20Sopenharmony_ci	ONYX_REG_DIG_INFO3,
5598c2ecf20Sopenharmony_ci	ONYX_REG_DIG_INFO4
5608c2ecf20Sopenharmony_ci};
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic const u8 initial_values[ARRAY_SIZE(register_map)] = {
5638c2ecf20Sopenharmony_ci	0x80, 0x80, /* muted */
5648c2ecf20Sopenharmony_ci	ONYX_MRST | ONYX_SRST, /* but handled specially! */
5658c2ecf20Sopenharmony_ci	ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
5668c2ecf20Sopenharmony_ci	0, /* no deemphasis */
5678c2ecf20Sopenharmony_ci	ONYX_DAC_FILTER_ALWAYS,
5688c2ecf20Sopenharmony_ci	ONYX_OUTPHASE_INVERTED,
5698c2ecf20Sopenharmony_ci	(-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/
5708c2ecf20Sopenharmony_ci	ONYX_ADC_HPF_ALWAYS,
5718c2ecf20Sopenharmony_ci	(1<<2),	/* pcm audio */
5728c2ecf20Sopenharmony_ci	2,	/* category: pcm coder */
5738c2ecf20Sopenharmony_ci	0,	/* sampling frequency 44.1 kHz, clock accuracy level II */
5748c2ecf20Sopenharmony_ci	1	/* 24 bit depth */
5758c2ecf20Sopenharmony_ci};
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/* reset registers of chip, either to initial or to previous values */
5788c2ecf20Sopenharmony_cistatic int onyx_register_init(struct onyx *onyx)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	int i;
5818c2ecf20Sopenharmony_ci	u8 val;
5828c2ecf20Sopenharmony_ci	u8 regs[sizeof(initial_values)];
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (!onyx->initialised) {
5858c2ecf20Sopenharmony_ci		memcpy(regs, initial_values, sizeof(initial_values));
5868c2ecf20Sopenharmony_ci		if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val))
5878c2ecf20Sopenharmony_ci			return -1;
5888c2ecf20Sopenharmony_ci		val &= ~ONYX_SILICONVERSION;
5898c2ecf20Sopenharmony_ci		val |= initial_values[3];
5908c2ecf20Sopenharmony_ci		regs[3] = val;
5918c2ecf20Sopenharmony_ci	} else {
5928c2ecf20Sopenharmony_ci		for (i=0; i<sizeof(register_map); i++)
5938c2ecf20Sopenharmony_ci			regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER];
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	for (i=0; i<sizeof(register_map); i++) {
5978c2ecf20Sopenharmony_ci		if (onyx_write_register(onyx, register_map[i], regs[i]))
5988c2ecf20Sopenharmony_ci			return -1;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	onyx->initialised = 1;
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic struct transfer_info onyx_transfers[] = {
6058c2ecf20Sopenharmony_ci	/* this is first so we can skip it if no input is present...
6068c2ecf20Sopenharmony_ci	 * No hardware exists with that, but it's here as an example
6078c2ecf20Sopenharmony_ci	 * of what to do :) */
6088c2ecf20Sopenharmony_ci	{
6098c2ecf20Sopenharmony_ci		/* analog input */
6108c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S8 |
6118c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S16_BE |
6128c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_BE,
6138c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_96000,
6148c2ecf20Sopenharmony_ci		.transfer_in = 1,
6158c2ecf20Sopenharmony_ci		.must_be_clock_source = 0,
6168c2ecf20Sopenharmony_ci		.tag = 0,
6178c2ecf20Sopenharmony_ci	},
6188c2ecf20Sopenharmony_ci	{
6198c2ecf20Sopenharmony_ci		/* if analog and digital are currently off, anything should go,
6208c2ecf20Sopenharmony_ci		 * so this entry describes everything we can do... */
6218c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S8 |
6228c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S16_BE |
6238c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_BE
6248c2ecf20Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
6258c2ecf20Sopenharmony_ci			   | SNDRV_PCM_FMTBIT_COMPRESSED_16BE
6268c2ecf20Sopenharmony_ci#endif
6278c2ecf20Sopenharmony_ci		,
6288c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_96000,
6298c2ecf20Sopenharmony_ci		.tag = 0,
6308c2ecf20Sopenharmony_ci	},
6318c2ecf20Sopenharmony_ci	{
6328c2ecf20Sopenharmony_ci		/* analog output */
6338c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S8 |
6348c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S16_BE |
6358c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_BE,
6368c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_8000_96000,
6378c2ecf20Sopenharmony_ci		.transfer_in = 0,
6388c2ecf20Sopenharmony_ci		.must_be_clock_source = 0,
6398c2ecf20Sopenharmony_ci		.tag = 1,
6408c2ecf20Sopenharmony_ci	},
6418c2ecf20Sopenharmony_ci	{
6428c2ecf20Sopenharmony_ci		/* digital pcm output, also possible for analog out */
6438c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S8 |
6448c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S16_BE |
6458c2ecf20Sopenharmony_ci			   SNDRV_PCM_FMTBIT_S24_BE,
6468c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_32000 |
6478c2ecf20Sopenharmony_ci			 SNDRV_PCM_RATE_44100 |
6488c2ecf20Sopenharmony_ci			 SNDRV_PCM_RATE_48000,
6498c2ecf20Sopenharmony_ci		.transfer_in = 0,
6508c2ecf20Sopenharmony_ci		.must_be_clock_source = 0,
6518c2ecf20Sopenharmony_ci		.tag = 2,
6528c2ecf20Sopenharmony_ci	},
6538c2ecf20Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
6548c2ecf20Sopenharmony_ci	/* Once alsa gets supports for this kind of thing we can add it... */
6558c2ecf20Sopenharmony_ci	{
6568c2ecf20Sopenharmony_ci		/* digital compressed output */
6578c2ecf20Sopenharmony_ci		.formats =  SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
6588c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_32000 |
6598c2ecf20Sopenharmony_ci			 SNDRV_PCM_RATE_44100 |
6608c2ecf20Sopenharmony_ci			 SNDRV_PCM_RATE_48000,
6618c2ecf20Sopenharmony_ci		.tag = 2,
6628c2ecf20Sopenharmony_ci	},
6638c2ecf20Sopenharmony_ci#endif
6648c2ecf20Sopenharmony_ci	{}
6658c2ecf20Sopenharmony_ci};
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic int onyx_usable(struct codec_info_item *cii,
6688c2ecf20Sopenharmony_ci		       struct transfer_info *ti,
6698c2ecf20Sopenharmony_ci		       struct transfer_info *out)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	u8 v;
6728c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
6738c2ecf20Sopenharmony_ci	int spdif_enabled, analog_enabled;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
6768c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
6778c2ecf20Sopenharmony_ci	spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
6788c2ecf20Sopenharmony_ci	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
6798c2ecf20Sopenharmony_ci	analog_enabled =
6808c2ecf20Sopenharmony_ci		(v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
6818c2ecf20Sopenharmony_ci		 != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
6828c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	switch (ti->tag) {
6858c2ecf20Sopenharmony_ci	case 0: return 1;
6868c2ecf20Sopenharmony_ci	case 1:	return analog_enabled;
6878c2ecf20Sopenharmony_ci	case 2: return spdif_enabled;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci	return 1;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int onyx_prepare(struct codec_info_item *cii,
6938c2ecf20Sopenharmony_ci			struct bus_info *bi,
6948c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	u8 v;
6978c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
6988c2ecf20Sopenharmony_ci	int err = -EBUSY;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
7038c2ecf20Sopenharmony_ci	if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
7048c2ecf20Sopenharmony_ci		/* mute and lock analog output */
7058c2ecf20Sopenharmony_ci		onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
7068c2ecf20Sopenharmony_ci		if (onyx_write_register(onyx,
7078c2ecf20Sopenharmony_ci					ONYX_REG_DAC_CONTROL,
7088c2ecf20Sopenharmony_ci					v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
7098c2ecf20Sopenharmony_ci			goto out_unlock;
7108c2ecf20Sopenharmony_ci		onyx->analog_locked = 1;
7118c2ecf20Sopenharmony_ci		err = 0;
7128c2ecf20Sopenharmony_ci		goto out_unlock;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci#endif
7158c2ecf20Sopenharmony_ci	switch (substream->runtime->rate) {
7168c2ecf20Sopenharmony_ci	case 32000:
7178c2ecf20Sopenharmony_ci	case 44100:
7188c2ecf20Sopenharmony_ci	case 48000:
7198c2ecf20Sopenharmony_ci		/* these rates are ok for all outputs */
7208c2ecf20Sopenharmony_ci		/* FIXME: program spdif channel control bits here so that
7218c2ecf20Sopenharmony_ci		 *	  userspace doesn't have to if it only plays pcm! */
7228c2ecf20Sopenharmony_ci		err = 0;
7238c2ecf20Sopenharmony_ci		goto out_unlock;
7248c2ecf20Sopenharmony_ci	default:
7258c2ecf20Sopenharmony_ci		/* got some rate that the digital output can't do,
7268c2ecf20Sopenharmony_ci		 * so disable and lock it */
7278c2ecf20Sopenharmony_ci		onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v);
7288c2ecf20Sopenharmony_ci		if (onyx_write_register(onyx,
7298c2ecf20Sopenharmony_ci					ONYX_REG_DIG_INFO4,
7308c2ecf20Sopenharmony_ci					v & ~ONYX_SPDIF_ENABLE))
7318c2ecf20Sopenharmony_ci			goto out_unlock;
7328c2ecf20Sopenharmony_ci		onyx->spdif_locked = 1;
7338c2ecf20Sopenharmony_ci		err = 0;
7348c2ecf20Sopenharmony_ci		goto out_unlock;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci out_unlock:
7388c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	return err;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int onyx_open(struct codec_info_item *cii,
7448c2ecf20Sopenharmony_ci		     struct snd_pcm_substream *substream)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
7498c2ecf20Sopenharmony_ci	onyx->open_count++;
7508c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic int onyx_close(struct codec_info_item *cii,
7568c2ecf20Sopenharmony_ci		      struct snd_pcm_substream *substream)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
7618c2ecf20Sopenharmony_ci	onyx->open_count--;
7628c2ecf20Sopenharmony_ci	if (!onyx->open_count)
7638c2ecf20Sopenharmony_ci		onyx->spdif_locked = onyx->analog_locked = 0;
7648c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	return 0;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic int onyx_switch_clock(struct codec_info_item *cii,
7708c2ecf20Sopenharmony_ci			     enum clock_switch what)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
7758c2ecf20Sopenharmony_ci	/* this *MUST* be more elaborate later... */
7768c2ecf20Sopenharmony_ci	switch (what) {
7778c2ecf20Sopenharmony_ci	case CLOCK_SWITCH_PREPARE_SLAVE:
7788c2ecf20Sopenharmony_ci		onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio);
7798c2ecf20Sopenharmony_ci		break;
7808c2ecf20Sopenharmony_ci	case CLOCK_SWITCH_SLAVE:
7818c2ecf20Sopenharmony_ci		onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio);
7828c2ecf20Sopenharmony_ci		break;
7838c2ecf20Sopenharmony_ci	default: /* silence warning */
7848c2ecf20Sopenharmony_ci		break;
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	return 0;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
7968c2ecf20Sopenharmony_ci	u8 v;
7978c2ecf20Sopenharmony_ci	int err = -ENXIO;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
8008c2ecf20Sopenharmony_ci	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
8018c2ecf20Sopenharmony_ci		goto out_unlock;
8028c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
8038c2ecf20Sopenharmony_ci	/* Apple does a sleep here but the datasheet says to do it on resume */
8048c2ecf20Sopenharmony_ci	err = 0;
8058c2ecf20Sopenharmony_ci out_unlock:
8068c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return err;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic int onyx_resume(struct codec_info_item *cii)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct onyx *onyx = cii->codec_data;
8148c2ecf20Sopenharmony_ci	u8 v;
8158c2ecf20Sopenharmony_ci	int err = -ENXIO;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	mutex_lock(&onyx->mutex);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	/* reset codec */
8208c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
8218c2ecf20Sopenharmony_ci	msleep(1);
8228c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
8238c2ecf20Sopenharmony_ci	msleep(1);
8248c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
8258c2ecf20Sopenharmony_ci	msleep(1);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* take codec out of suspend (if it still is after reset) */
8288c2ecf20Sopenharmony_ci	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
8298c2ecf20Sopenharmony_ci		goto out_unlock;
8308c2ecf20Sopenharmony_ci	onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
8318c2ecf20Sopenharmony_ci	/* FIXME: should divide by sample rate, but 8k is the lowest we go */
8328c2ecf20Sopenharmony_ci	msleep(2205000/8000);
8338c2ecf20Sopenharmony_ci	/* reset all values */
8348c2ecf20Sopenharmony_ci	onyx_register_init(onyx);
8358c2ecf20Sopenharmony_ci	err = 0;
8368c2ecf20Sopenharmony_ci out_unlock:
8378c2ecf20Sopenharmony_ci	mutex_unlock(&onyx->mutex);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return err;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic struct codec_info onyx_codec_info = {
8458c2ecf20Sopenharmony_ci	.transfers = onyx_transfers,
8468c2ecf20Sopenharmony_ci	.sysclock_factor = 256,
8478c2ecf20Sopenharmony_ci	.bus_factor = 64,
8488c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
8498c2ecf20Sopenharmony_ci	.usable = onyx_usable,
8508c2ecf20Sopenharmony_ci	.prepare = onyx_prepare,
8518c2ecf20Sopenharmony_ci	.open = onyx_open,
8528c2ecf20Sopenharmony_ci	.close = onyx_close,
8538c2ecf20Sopenharmony_ci	.switch_clock = onyx_switch_clock,
8548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
8558c2ecf20Sopenharmony_ci	.suspend = onyx_suspend,
8568c2ecf20Sopenharmony_ci	.resume = onyx_resume,
8578c2ecf20Sopenharmony_ci#endif
8588c2ecf20Sopenharmony_ci};
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic int onyx_init_codec(struct aoa_codec *codec)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct onyx *onyx = codec_to_onyx(codec);
8638c2ecf20Sopenharmony_ci	struct snd_kcontrol *ctl;
8648c2ecf20Sopenharmony_ci	struct codec_info *ci = &onyx_codec_info;
8658c2ecf20Sopenharmony_ci	u8 v;
8668c2ecf20Sopenharmony_ci	int err;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (!onyx->codec.gpio || !onyx->codec.gpio->methods) {
8698c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "gpios not assigned!!\n");
8708c2ecf20Sopenharmony_ci		return -EINVAL;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
8748c2ecf20Sopenharmony_ci	msleep(1);
8758c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
8768c2ecf20Sopenharmony_ci	msleep(1);
8778c2ecf20Sopenharmony_ci	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
8788c2ecf20Sopenharmony_ci	msleep(1);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (onyx_register_init(onyx)) {
8818c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "failed to initialise onyx registers\n");
8828c2ecf20Sopenharmony_ci		return -ENODEV;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) {
8868c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "failed to create onyx snd device!\n");
8878c2ecf20Sopenharmony_ci		return -ENODEV;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* nothing connected? what a joke! */
8918c2ecf20Sopenharmony_ci	if ((onyx->codec.connected & 0xF) == 0)
8928c2ecf20Sopenharmony_ci		return -ENOTCONN;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* if no inputs are present... */
8958c2ecf20Sopenharmony_ci	if ((onyx->codec.connected & 0xC) == 0) {
8968c2ecf20Sopenharmony_ci		if (!onyx->codec_info)
8978c2ecf20Sopenharmony_ci			onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
8988c2ecf20Sopenharmony_ci		if (!onyx->codec_info)
8998c2ecf20Sopenharmony_ci			return -ENOMEM;
9008c2ecf20Sopenharmony_ci		ci = onyx->codec_info;
9018c2ecf20Sopenharmony_ci		*ci = onyx_codec_info;
9028c2ecf20Sopenharmony_ci		ci->transfers++;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* if no outputs are present... */
9068c2ecf20Sopenharmony_ci	if ((onyx->codec.connected & 3) == 0) {
9078c2ecf20Sopenharmony_ci		if (!onyx->codec_info)
9088c2ecf20Sopenharmony_ci			onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
9098c2ecf20Sopenharmony_ci		if (!onyx->codec_info)
9108c2ecf20Sopenharmony_ci			return -ENOMEM;
9118c2ecf20Sopenharmony_ci		ci = onyx->codec_info;
9128c2ecf20Sopenharmony_ci		/* this is fine as there have to be inputs
9138c2ecf20Sopenharmony_ci		 * if we end up in this part of the code */
9148c2ecf20Sopenharmony_ci		*ci = onyx_codec_info;
9158c2ecf20Sopenharmony_ci		ci->transfers[1].formats = 0;
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev,
9198c2ecf20Sopenharmony_ci						   aoa_get_card(),
9208c2ecf20Sopenharmony_ci						   ci, onyx)) {
9218c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "error creating onyx pcm\n");
9228c2ecf20Sopenharmony_ci		return -ENODEV;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci#define ADDCTL(n)							\
9258c2ecf20Sopenharmony_ci	do {								\
9268c2ecf20Sopenharmony_ci		ctl = snd_ctl_new1(&n, onyx);				\
9278c2ecf20Sopenharmony_ci		if (ctl) {						\
9288c2ecf20Sopenharmony_ci			ctl->id.device =				\
9298c2ecf20Sopenharmony_ci				onyx->codec.soundbus_dev->pcm->device;	\
9308c2ecf20Sopenharmony_ci			err = aoa_snd_ctl_add(ctl);			\
9318c2ecf20Sopenharmony_ci			if (err)					\
9328c2ecf20Sopenharmony_ci				goto error;				\
9338c2ecf20Sopenharmony_ci		}							\
9348c2ecf20Sopenharmony_ci	} while (0)
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (onyx->codec.soundbus_dev->pcm) {
9378c2ecf20Sopenharmony_ci		/* give the user appropriate controls
9388c2ecf20Sopenharmony_ci		 * depending on what inputs are connected */
9398c2ecf20Sopenharmony_ci		if ((onyx->codec.connected & 0xC) == 0xC)
9408c2ecf20Sopenharmony_ci			ADDCTL(capture_source_control);
9418c2ecf20Sopenharmony_ci		else if (onyx->codec.connected & 4)
9428c2ecf20Sopenharmony_ci			onyx_set_capture_source(onyx, 0);
9438c2ecf20Sopenharmony_ci		else
9448c2ecf20Sopenharmony_ci			onyx_set_capture_source(onyx, 1);
9458c2ecf20Sopenharmony_ci		if (onyx->codec.connected & 0xC)
9468c2ecf20Sopenharmony_ci			ADDCTL(inputgain_control);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		/* depending on what output is connected,
9498c2ecf20Sopenharmony_ci		 * give the user appropriate controls */
9508c2ecf20Sopenharmony_ci		if (onyx->codec.connected & 1) {
9518c2ecf20Sopenharmony_ci			ADDCTL(volume_control);
9528c2ecf20Sopenharmony_ci			ADDCTL(mute_control);
9538c2ecf20Sopenharmony_ci			ADDCTL(ovr1_control);
9548c2ecf20Sopenharmony_ci			ADDCTL(flt0_control);
9558c2ecf20Sopenharmony_ci			ADDCTL(hpf_control);
9568c2ecf20Sopenharmony_ci			ADDCTL(dm12_control);
9578c2ecf20Sopenharmony_ci			/* spdif control defaults to off */
9588c2ecf20Sopenharmony_ci		}
9598c2ecf20Sopenharmony_ci		if (onyx->codec.connected & 2) {
9608c2ecf20Sopenharmony_ci			ADDCTL(onyx_spdif_mask);
9618c2ecf20Sopenharmony_ci			ADDCTL(onyx_spdif_ctrl);
9628c2ecf20Sopenharmony_ci		}
9638c2ecf20Sopenharmony_ci		if ((onyx->codec.connected & 3) == 3)
9648c2ecf20Sopenharmony_ci			ADDCTL(spdif_control);
9658c2ecf20Sopenharmony_ci		/* if only S/PDIF is connected, enable it unconditionally */
9668c2ecf20Sopenharmony_ci		if ((onyx->codec.connected & 3) == 2) {
9678c2ecf20Sopenharmony_ci			onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
9688c2ecf20Sopenharmony_ci			v |= ONYX_SPDIF_ENABLE;
9698c2ecf20Sopenharmony_ci			onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci#undef ADDCTL
9738c2ecf20Sopenharmony_ci	printk(KERN_INFO PFX "attached to onyx codec via i2c\n");
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	return 0;
9768c2ecf20Sopenharmony_ci error:
9778c2ecf20Sopenharmony_ci	onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
9788c2ecf20Sopenharmony_ci	snd_device_free(aoa_get_card(), onyx);
9798c2ecf20Sopenharmony_ci	return err;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_cistatic void onyx_exit_codec(struct aoa_codec *codec)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct onyx *onyx = codec_to_onyx(codec);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (!onyx->codec.soundbus_dev) {
9878c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n");
9888c2ecf20Sopenharmony_ci		return;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci	onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cistatic int onyx_i2c_probe(struct i2c_client *client,
9948c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	struct device_node *node = client->dev.of_node;
9978c2ecf20Sopenharmony_ci	struct onyx *onyx;
9988c2ecf20Sopenharmony_ci	u8 dummy;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (!onyx)
10038c2ecf20Sopenharmony_ci		return -ENOMEM;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	mutex_init(&onyx->mutex);
10068c2ecf20Sopenharmony_ci	onyx->i2c = client;
10078c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, onyx);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* we try to read from register ONYX_REG_CONTROL
10108c2ecf20Sopenharmony_ci	 * to check if the codec is present */
10118c2ecf20Sopenharmony_ci	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) {
10128c2ecf20Sopenharmony_ci		printk(KERN_ERR PFX "failed to read control register\n");
10138c2ecf20Sopenharmony_ci		goto fail;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
10178c2ecf20Sopenharmony_ci	onyx->codec.owner = THIS_MODULE;
10188c2ecf20Sopenharmony_ci	onyx->codec.init = onyx_init_codec;
10198c2ecf20Sopenharmony_ci	onyx->codec.exit = onyx_exit_codec;
10208c2ecf20Sopenharmony_ci	onyx->codec.node = of_node_get(node);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (aoa_codec_register(&onyx->codec)) {
10238c2ecf20Sopenharmony_ci		goto fail;
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci	printk(KERN_DEBUG PFX "created and attached onyx instance\n");
10268c2ecf20Sopenharmony_ci	return 0;
10278c2ecf20Sopenharmony_ci fail:
10288c2ecf20Sopenharmony_ci	kfree(onyx);
10298c2ecf20Sopenharmony_ci	return -ENODEV;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic int onyx_i2c_remove(struct i2c_client *client)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	struct onyx *onyx = i2c_get_clientdata(client);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	aoa_codec_unregister(&onyx->codec);
10378c2ecf20Sopenharmony_ci	of_node_put(onyx->codec.node);
10388c2ecf20Sopenharmony_ci	kfree(onyx->codec_info);
10398c2ecf20Sopenharmony_ci	kfree(onyx);
10408c2ecf20Sopenharmony_ci	return 0;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic const struct i2c_device_id onyx_i2c_id[] = {
10448c2ecf20Sopenharmony_ci	{ "MAC,pcm3052", 0 },
10458c2ecf20Sopenharmony_ci	{ }
10468c2ecf20Sopenharmony_ci};
10478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c,onyx_i2c_id);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic struct i2c_driver onyx_driver = {
10508c2ecf20Sopenharmony_ci	.driver = {
10518c2ecf20Sopenharmony_ci		.name = "aoa_codec_onyx",
10528c2ecf20Sopenharmony_ci	},
10538c2ecf20Sopenharmony_ci	.probe = onyx_i2c_probe,
10548c2ecf20Sopenharmony_ci	.remove = onyx_i2c_remove,
10558c2ecf20Sopenharmony_ci	.id_table = onyx_i2c_id,
10568c2ecf20Sopenharmony_ci};
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_cimodule_i2c_driver(onyx_driver);
1059