| /* | 
 |  * Apple Onboard Audio driver for Onyx codec | 
 |  * | 
 |  * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | 
 |  * | 
 |  * GPL v2, can be found in COPYING. | 
 |  * | 
 |  * | 
 |  * This is a driver for the pcm3052 codec chip (codenamed Onyx) | 
 |  * that is present in newer Apple hardware (with digital output). | 
 |  * | 
 |  * The Onyx codec has the following connections (listed by the bit | 
 |  * to be used in aoa_codec.connected): | 
 |  *  0: analog output | 
 |  *  1: digital output | 
 |  *  2: line input | 
 |  *  3: microphone input | 
 |  * Note that even though I know of no machine that has for example | 
 |  * the digital output connected but not the analog, I have handled | 
 |  * all the different cases in the code so that this driver may serve | 
 |  * as a good example of what to do. | 
 |  * | 
 |  * NOTE: This driver assumes that there's at most one chip to be | 
 |  * 	 used with one alsa card, in form of creating all kinds | 
 |  *	 of mixer elements without regard for their existence. | 
 |  *	 But snd-aoa assumes that there's at most one card, so | 
 |  *	 this means you can only have one onyx on a system. This | 
 |  *	 should probably be fixed by changing the assumption of | 
 |  *	 having just a single card on a system, and making the | 
 |  *	 'card' pointer accessible to anyone who needs it instead | 
 |  *	 of hiding it in the aoa_snd_* functions... | 
 |  * | 
 |  */ | 
 | #include <linux/delay.h> | 
 | #include <linux/module.h> | 
 | #include <linux/slab.h> | 
 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | 
 | MODULE_LICENSE("GPL"); | 
 | MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); | 
 |  | 
 | #include "onyx.h" | 
 | #include "../aoa.h" | 
 | #include "../soundbus/soundbus.h" | 
 |  | 
 |  | 
 | #define PFX "snd-aoa-codec-onyx: " | 
 |  | 
 | struct onyx { | 
 | 	/* cache registers 65 to 80, they are write-only! */ | 
 | 	u8			cache[16]; | 
 | 	struct i2c_client	*i2c; | 
 | 	struct aoa_codec	codec; | 
 | 	u32			initialised:1, | 
 | 				spdif_locked:1, | 
 | 				analog_locked:1, | 
 | 				original_mute:2; | 
 | 	int			open_count; | 
 | 	struct codec_info	*codec_info; | 
 |  | 
 | 	/* mutex serializes concurrent access to the device | 
 | 	 * and this structure. | 
 | 	 */ | 
 | 	struct mutex mutex; | 
 | }; | 
 | #define codec_to_onyx(c) container_of(c, struct onyx, codec) | 
 |  | 
 | /* both return 0 if all ok, else on error */ | 
 | static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) | 
 | { | 
 | 	s32 v; | 
 |  | 
 | 	if (reg != ONYX_REG_CONTROL) { | 
 | 		*value = onyx->cache[reg-FIRSTREGISTER]; | 
 | 		return 0; | 
 | 	} | 
 | 	v = i2c_smbus_read_byte_data(onyx->i2c, reg); | 
 | 	if (v < 0) | 
 | 		return -1; | 
 | 	*value = (u8)v; | 
 | 	onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value) | 
 | { | 
 | 	int result; | 
 |  | 
 | 	result = i2c_smbus_write_byte_data(onyx->i2c, reg, value); | 
 | 	if (!result) | 
 | 		onyx->cache[reg-FIRSTREGISTER] = value; | 
 | 	return result; | 
 | } | 
 |  | 
 | /* alsa stuff */ | 
 |  | 
 | static int onyx_dev_register(struct snd_device *dev) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct snd_device_ops ops = { | 
 | 	.dev_register = onyx_dev_register, | 
 | }; | 
 |  | 
 | /* this is necessary because most alsa mixer programs | 
 |  * can't properly handle the negative range */ | 
 | #define VOLUME_RANGE_SHIFT	128 | 
 |  | 
 | static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_info *uinfo) | 
 | { | 
 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 	uinfo->count = 2; | 
 | 	uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT; | 
 | 	uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	s8 l, r; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT; | 
 | 	ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	s8 l, r; | 
 |  | 
 | 	if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT || | 
 | 	    ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT) | 
 | 		return -EINVAL; | 
 | 	if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT || | 
 | 	    ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT) | 
 | 		return -EINVAL; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); | 
 |  | 
 | 	if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] && | 
 | 	    r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) { | 
 | 		mutex_unlock(&onyx->mutex); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, | 
 | 			    ucontrol->value.integer.value[0] | 
 | 			     - VOLUME_RANGE_SHIFT); | 
 | 	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, | 
 | 			    ucontrol->value.integer.value[1] | 
 | 			     - VOLUME_RANGE_SHIFT); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new volume_control = { | 
 | 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 
 | 	.name = "Master Playback Volume", | 
 | 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | 
 | 	.info = onyx_snd_vol_info, | 
 | 	.get = onyx_snd_vol_get, | 
 | 	.put = onyx_snd_vol_put, | 
 | }; | 
 |  | 
 | /* like above, this is necessary because a lot | 
 |  * of alsa mixer programs don't handle ranges | 
 |  * that don't start at 0 properly. | 
 |  * even alsamixer is one of them... */ | 
 | #define INPUTGAIN_RANGE_SHIFT	(-3) | 
 |  | 
 | static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_info *uinfo) | 
 | { | 
 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 	uinfo->count = 1; | 
 | 	uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT; | 
 | 	uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 ig; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	ucontrol->value.integer.value[0] = | 
 | 		(ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 v, n; | 
 |  | 
 | 	if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT || | 
 | 	    ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT) | 
 | 		return -EINVAL; | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | 
 | 	n = v; | 
 | 	n &= ~ONYX_ADC_PGA_GAIN_MASK; | 
 | 	n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT) | 
 | 		& ONYX_ADC_PGA_GAIN_MASK; | 
 | 	onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return n != v; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new inputgain_control = { | 
 | 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 
 | 	.name = "Master Capture Volume", | 
 | 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | 
 | 	.info = onyx_snd_inputgain_info, | 
 | 	.get = onyx_snd_inputgain_get, | 
 | 	.put = onyx_snd_inputgain_put, | 
 | }; | 
 |  | 
 | static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_info *uinfo) | 
 | { | 
 | 	static const char * const texts[] = { "Line-In", "Microphone" }; | 
 |  | 
 | 	return snd_ctl_enum_info(uinfo, 1, 2, texts); | 
 | } | 
 |  | 
 | static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	s8 v; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void onyx_set_capture_source(struct onyx *onyx, int mic) | 
 | { | 
 | 	s8 v; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | 
 | 	v &= ~ONYX_ADC_INPUT_MIC; | 
 | 	if (mic) | 
 | 		v |= ONYX_ADC_INPUT_MIC; | 
 | 	onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v); | 
 | 	mutex_unlock(&onyx->mutex); | 
 | } | 
 |  | 
 | static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	if (ucontrol->value.enumerated.item[0] > 1) | 
 | 		return -EINVAL; | 
 | 	onyx_set_capture_source(snd_kcontrol_chip(kcontrol), | 
 | 				ucontrol->value.enumerated.item[0]); | 
 | 	return 1; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new capture_source_control = { | 
 | 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 
 | 	/* If we name this 'Input Source', it properly shows up in | 
 | 	 * alsamixer as a selection, * but it's shown under the | 
 | 	 * 'Playback' category. | 
 | 	 * If I name it 'Capture Source', it shows up in strange | 
 | 	 * ways (two bools of which one can be selected at a | 
 | 	 * time) but at least it's shown in the 'Capture' | 
 | 	 * category. | 
 | 	 * I was told that this was due to backward compatibility, | 
 | 	 * but I don't understand then why the mangling is *not* | 
 | 	 * done when I name it "Input Source"..... | 
 | 	 */ | 
 | 	.name = "Capture Source", | 
 | 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | 
 | 	.info = onyx_snd_capture_source_info, | 
 | 	.get = onyx_snd_capture_source_get, | 
 | 	.put = onyx_snd_capture_source_put, | 
 | }; | 
 |  | 
 | #define onyx_snd_mute_info	snd_ctl_boolean_stereo_info | 
 |  | 
 | static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 c; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT); | 
 | 	ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 v = 0, c = 0; | 
 | 	int err = -EBUSY; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	if (onyx->analog_locked) | 
 | 		goto out_unlock; | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | 
 | 	c = v; | 
 | 	c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT); | 
 | 	if (!ucontrol->value.integer.value[0]) | 
 | 		c |= ONYX_MUTE_LEFT; | 
 | 	if (!ucontrol->value.integer.value[1]) | 
 | 		c |= ONYX_MUTE_RIGHT; | 
 | 	err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c); | 
 |  | 
 |  out_unlock: | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return !err ? (v != c) : err; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new mute_control = { | 
 | 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 
 | 	.name = "Master Playback Switch", | 
 | 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | 
 | 	.info = onyx_snd_mute_info, | 
 | 	.get = onyx_snd_mute_get, | 
 | 	.put = onyx_snd_mute_put, | 
 | }; | 
 |  | 
 |  | 
 | #define onyx_snd_single_bit_info	snd_ctl_boolean_mono_info | 
 |  | 
 | #define FLAG_POLARITY_INVERT	1 | 
 | #define FLAG_SPDIFLOCK		2 | 
 |  | 
 | static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 c; | 
 | 	long int pv = kcontrol->private_value; | 
 | 	u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; | 
 | 	u8 address = (pv >> 8) & 0xff; | 
 | 	u8 mask = pv & 0xff; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, address, &c); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 v = 0, c = 0; | 
 | 	int err; | 
 | 	long int pv = kcontrol->private_value; | 
 | 	u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; | 
 | 	u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK; | 
 | 	u8 address = (pv >> 8) & 0xff; | 
 | 	u8 mask = pv & 0xff; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	if (spdiflock && onyx->spdif_locked) { | 
 | 		/* even if alsamixer doesn't care.. */ | 
 | 		err = -EBUSY; | 
 | 		goto out_unlock; | 
 | 	} | 
 | 	onyx_read_register(onyx, address, &v); | 
 | 	c = v; | 
 | 	c &= ~(mask); | 
 | 	if (!!ucontrol->value.integer.value[0] ^ polarity) | 
 | 		c |= mask; | 
 | 	err = onyx_write_register(onyx, address, c); | 
 |  | 
 |  out_unlock: | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return !err ? (v != c) : err; | 
 | } | 
 |  | 
 | #define SINGLE_BIT(n, type, description, address, mask, flags)	 	\ | 
 | static struct snd_kcontrol_new n##_control = {				\ | 
 | 	.iface = SNDRV_CTL_ELEM_IFACE_##type,				\ | 
 | 	.name = description,						\ | 
 | 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,			\ | 
 | 	.info = onyx_snd_single_bit_info,				\ | 
 | 	.get = onyx_snd_single_bit_get,					\ | 
 | 	.put = onyx_snd_single_bit_put,					\ | 
 | 	.private_value = (flags << 16) | (address << 8) | mask		\ | 
 | } | 
 |  | 
 | SINGLE_BIT(spdif, | 
 | 	   MIXER, | 
 | 	   SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), | 
 | 	   ONYX_REG_DIG_INFO4, | 
 | 	   ONYX_SPDIF_ENABLE, | 
 | 	   FLAG_SPDIFLOCK); | 
 | SINGLE_BIT(ovr1, | 
 | 	   MIXER, | 
 | 	   "Oversampling Rate", | 
 | 	   ONYX_REG_DAC_CONTROL, | 
 | 	   ONYX_OVR1, | 
 | 	   0); | 
 | SINGLE_BIT(flt0, | 
 | 	   MIXER, | 
 | 	   "Fast Digital Filter Rolloff", | 
 | 	   ONYX_REG_DAC_FILTER, | 
 | 	   ONYX_ROLLOFF_FAST, | 
 | 	   FLAG_POLARITY_INVERT); | 
 | SINGLE_BIT(hpf, | 
 | 	   MIXER, | 
 | 	   "Highpass Filter", | 
 | 	   ONYX_REG_ADC_HPF_BYPASS, | 
 | 	   ONYX_HPF_DISABLE, | 
 | 	   FLAG_POLARITY_INVERT); | 
 | SINGLE_BIT(dm12, | 
 | 	   MIXER, | 
 | 	   "Digital De-Emphasis", | 
 | 	   ONYX_REG_DAC_DEEMPH, | 
 | 	   ONYX_DIGDEEMPH_CTRL, | 
 | 	   0); | 
 |  | 
 | static int onyx_spdif_info(struct snd_kcontrol *kcontrol, | 
 | 			   struct snd_ctl_elem_info *uinfo) | 
 | { | 
 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | 
 | 	uinfo->count = 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol, | 
 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	/* datasheet page 30, all others are 0 */ | 
 | 	ucontrol->value.iec958.status[0] = 0x3e; | 
 | 	ucontrol->value.iec958.status[1] = 0xff; | 
 |  | 
 | 	ucontrol->value.iec958.status[3] = 0x3f; | 
 | 	ucontrol->value.iec958.status[4] = 0x0f; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new onyx_spdif_mask = { | 
 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READ, | 
 | 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), | 
 | 	.info =		onyx_spdif_info, | 
 | 	.get =		onyx_spdif_mask_get, | 
 | }; | 
 |  | 
 | static int onyx_spdif_get(struct snd_kcontrol *kcontrol, | 
 | 			  struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 v; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); | 
 | 	ucontrol->value.iec958.status[0] = v & 0x3e; | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v); | 
 | 	ucontrol->value.iec958.status[1] = v; | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); | 
 | 	ucontrol->value.iec958.status[3] = v & 0x3f; | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | 
 | 	ucontrol->value.iec958.status[4] = v & 0x0f; | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_spdif_put(struct snd_kcontrol *kcontrol, | 
 | 			  struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct onyx *onyx = snd_kcontrol_chip(kcontrol); | 
 | 	u8 v; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); | 
 | 	v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e); | 
 | 	onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v); | 
 |  | 
 | 	v = ucontrol->value.iec958.status[1]; | 
 | 	onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v); | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); | 
 | 	v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f); | 
 | 	onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v); | 
 |  | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | 
 | 	v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f); | 
 | 	onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | static const struct snd_kcontrol_new onyx_spdif_ctrl = { | 
 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE, | 
 | 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | 
 | 	.info =		onyx_spdif_info, | 
 | 	.get =		onyx_spdif_get, | 
 | 	.put =		onyx_spdif_put, | 
 | }; | 
 |  | 
 | /* our registers */ | 
 |  | 
 | static u8 register_map[] = { | 
 | 	ONYX_REG_DAC_ATTEN_LEFT, | 
 | 	ONYX_REG_DAC_ATTEN_RIGHT, | 
 | 	ONYX_REG_CONTROL, | 
 | 	ONYX_REG_DAC_CONTROL, | 
 | 	ONYX_REG_DAC_DEEMPH, | 
 | 	ONYX_REG_DAC_FILTER, | 
 | 	ONYX_REG_DAC_OUTPHASE, | 
 | 	ONYX_REG_ADC_CONTROL, | 
 | 	ONYX_REG_ADC_HPF_BYPASS, | 
 | 	ONYX_REG_DIG_INFO1, | 
 | 	ONYX_REG_DIG_INFO2, | 
 | 	ONYX_REG_DIG_INFO3, | 
 | 	ONYX_REG_DIG_INFO4 | 
 | }; | 
 |  | 
 | static u8 initial_values[ARRAY_SIZE(register_map)] = { | 
 | 	0x80, 0x80, /* muted */ | 
 | 	ONYX_MRST | ONYX_SRST, /* but handled specially! */ | 
 | 	ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT, | 
 | 	0, /* no deemphasis */ | 
 | 	ONYX_DAC_FILTER_ALWAYS, | 
 | 	ONYX_OUTPHASE_INVERTED, | 
 | 	(-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/ | 
 | 	ONYX_ADC_HPF_ALWAYS, | 
 | 	(1<<2),	/* pcm audio */ | 
 | 	2,	/* category: pcm coder */ | 
 | 	0,	/* sampling frequency 44.1 kHz, clock accuracy level II */ | 
 | 	1	/* 24 bit depth */ | 
 | }; | 
 |  | 
 | /* reset registers of chip, either to initial or to previous values */ | 
 | static int onyx_register_init(struct onyx *onyx) | 
 | { | 
 | 	int i; | 
 | 	u8 val; | 
 | 	u8 regs[sizeof(initial_values)]; | 
 |  | 
 | 	if (!onyx->initialised) { | 
 | 		memcpy(regs, initial_values, sizeof(initial_values)); | 
 | 		if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val)) | 
 | 			return -1; | 
 | 		val &= ~ONYX_SILICONVERSION; | 
 | 		val |= initial_values[3]; | 
 | 		regs[3] = val; | 
 | 	} else { | 
 | 		for (i=0; i<sizeof(register_map); i++) | 
 | 			regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER]; | 
 | 	} | 
 |  | 
 | 	for (i=0; i<sizeof(register_map); i++) { | 
 | 		if (onyx_write_register(onyx, register_map[i], regs[i])) | 
 | 			return -1; | 
 | 	} | 
 | 	onyx->initialised = 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct transfer_info onyx_transfers[] = { | 
 | 	/* this is first so we can skip it if no input is present... | 
 | 	 * No hardware exists with that, but it's here as an example | 
 | 	 * of what to do :) */ | 
 | 	{ | 
 | 		/* analog input */ | 
 | 		.formats = SNDRV_PCM_FMTBIT_S8 | | 
 | 			   SNDRV_PCM_FMTBIT_S16_BE | | 
 | 			   SNDRV_PCM_FMTBIT_S24_BE, | 
 | 		.rates = SNDRV_PCM_RATE_8000_96000, | 
 | 		.transfer_in = 1, | 
 | 		.must_be_clock_source = 0, | 
 | 		.tag = 0, | 
 | 	}, | 
 | 	{ | 
 | 		/* if analog and digital are currently off, anything should go, | 
 | 		 * so this entry describes everything we can do... */ | 
 | 		.formats = SNDRV_PCM_FMTBIT_S8 | | 
 | 			   SNDRV_PCM_FMTBIT_S16_BE | | 
 | 			   SNDRV_PCM_FMTBIT_S24_BE | 
 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | 
 | 			   | SNDRV_PCM_FMTBIT_COMPRESSED_16BE | 
 | #endif | 
 | 		, | 
 | 		.rates = SNDRV_PCM_RATE_8000_96000, | 
 | 		.tag = 0, | 
 | 	}, | 
 | 	{ | 
 | 		/* analog output */ | 
 | 		.formats = SNDRV_PCM_FMTBIT_S8 | | 
 | 			   SNDRV_PCM_FMTBIT_S16_BE | | 
 | 			   SNDRV_PCM_FMTBIT_S24_BE, | 
 | 		.rates = SNDRV_PCM_RATE_8000_96000, | 
 | 		.transfer_in = 0, | 
 | 		.must_be_clock_source = 0, | 
 | 		.tag = 1, | 
 | 	}, | 
 | 	{ | 
 | 		/* digital pcm output, also possible for analog out */ | 
 | 		.formats = SNDRV_PCM_FMTBIT_S8 | | 
 | 			   SNDRV_PCM_FMTBIT_S16_BE | | 
 | 			   SNDRV_PCM_FMTBIT_S24_BE, | 
 | 		.rates = SNDRV_PCM_RATE_32000 | | 
 | 			 SNDRV_PCM_RATE_44100 | | 
 | 			 SNDRV_PCM_RATE_48000, | 
 | 		.transfer_in = 0, | 
 | 		.must_be_clock_source = 0, | 
 | 		.tag = 2, | 
 | 	}, | 
 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | 
 | 	/* Once alsa gets supports for this kind of thing we can add it... */ | 
 | 	{ | 
 | 		/* digital compressed output */ | 
 | 		.formats =  SNDRV_PCM_FMTBIT_COMPRESSED_16BE, | 
 | 		.rates = SNDRV_PCM_RATE_32000 | | 
 | 			 SNDRV_PCM_RATE_44100 | | 
 | 			 SNDRV_PCM_RATE_48000, | 
 | 		.tag = 2, | 
 | 	}, | 
 | #endif | 
 | 	{} | 
 | }; | 
 |  | 
 | static int onyx_usable(struct codec_info_item *cii, | 
 | 		       struct transfer_info *ti, | 
 | 		       struct transfer_info *out) | 
 | { | 
 | 	u8 v; | 
 | 	struct onyx *onyx = cii->codec_data; | 
 | 	int spdif_enabled, analog_enabled; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | 
 | 	spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); | 
 | 	onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | 
 | 	analog_enabled = | 
 | 		(v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) | 
 | 		 != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	switch (ti->tag) { | 
 | 	case 0: return 1; | 
 | 	case 1:	return analog_enabled; | 
 | 	case 2: return spdif_enabled; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 | static int onyx_prepare(struct codec_info_item *cii, | 
 | 			struct bus_info *bi, | 
 | 			struct snd_pcm_substream *substream) | 
 | { | 
 | 	u8 v; | 
 | 	struct onyx *onyx = cii->codec_data; | 
 | 	int err = -EBUSY; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 |  | 
 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | 
 | 	if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { | 
 | 		/* mute and lock analog output */ | 
 | 		onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | 
 | 		if (onyx_write_register(onyx, | 
 | 					ONYX_REG_DAC_CONTROL, | 
 | 					v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) | 
 | 			goto out_unlock; | 
 | 		onyx->analog_locked = 1; | 
 | 		err = 0; | 
 | 		goto out_unlock; | 
 | 	} | 
 | #endif | 
 | 	switch (substream->runtime->rate) { | 
 | 	case 32000: | 
 | 	case 44100: | 
 | 	case 48000: | 
 | 		/* these rates are ok for all outputs */ | 
 | 		/* FIXME: program spdif channel control bits here so that | 
 | 		 *	  userspace doesn't have to if it only plays pcm! */ | 
 | 		err = 0; | 
 | 		goto out_unlock; | 
 | 	default: | 
 | 		/* got some rate that the digital output can't do, | 
 | 		 * so disable and lock it */ | 
 | 		onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v); | 
 | 		if (onyx_write_register(onyx, | 
 | 					ONYX_REG_DIG_INFO4, | 
 | 					v & ~ONYX_SPDIF_ENABLE)) | 
 | 			goto out_unlock; | 
 | 		onyx->spdif_locked = 1; | 
 | 		err = 0; | 
 | 		goto out_unlock; | 
 | 	} | 
 |  | 
 |  out_unlock: | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static int onyx_open(struct codec_info_item *cii, | 
 | 		     struct snd_pcm_substream *substream) | 
 | { | 
 | 	struct onyx *onyx = cii->codec_data; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx->open_count++; | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_close(struct codec_info_item *cii, | 
 | 		      struct snd_pcm_substream *substream) | 
 | { | 
 | 	struct onyx *onyx = cii->codec_data; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	onyx->open_count--; | 
 | 	if (!onyx->open_count) | 
 | 		onyx->spdif_locked = onyx->analog_locked = 0; | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int onyx_switch_clock(struct codec_info_item *cii, | 
 | 			     enum clock_switch what) | 
 | { | 
 | 	struct onyx *onyx = cii->codec_data; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	/* this *MUST* be more elaborate later... */ | 
 | 	switch (what) { | 
 | 	case CLOCK_SWITCH_PREPARE_SLAVE: | 
 | 		onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio); | 
 | 		break; | 
 | 	case CLOCK_SWITCH_SLAVE: | 
 | 		onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio); | 
 | 		break; | 
 | 	default: /* silence warning */ | 
 | 		break; | 
 | 	} | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifdef CONFIG_PM | 
 |  | 
 | static int onyx_suspend(struct codec_info_item *cii, pm_message_t state) | 
 | { | 
 | 	struct onyx *onyx = cii->codec_data; | 
 | 	u8 v; | 
 | 	int err = -ENXIO; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 | 	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) | 
 | 		goto out_unlock; | 
 | 	onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV); | 
 | 	/* Apple does a sleep here but the datasheet says to do it on resume */ | 
 | 	err = 0; | 
 |  out_unlock: | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static int onyx_resume(struct codec_info_item *cii) | 
 | { | 
 | 	struct onyx *onyx = cii->codec_data; | 
 | 	u8 v; | 
 | 	int err = -ENXIO; | 
 |  | 
 | 	mutex_lock(&onyx->mutex); | 
 |  | 
 | 	/* reset codec */ | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | 
 | 	msleep(1); | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); | 
 | 	msleep(1); | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | 
 | 	msleep(1); | 
 |  | 
 | 	/* take codec out of suspend (if it still is after reset) */ | 
 | 	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) | 
 | 		goto out_unlock; | 
 | 	onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); | 
 | 	/* FIXME: should divide by sample rate, but 8k is the lowest we go */ | 
 | 	msleep(2205000/8000); | 
 | 	/* reset all values */ | 
 | 	onyx_register_init(onyx); | 
 | 	err = 0; | 
 |  out_unlock: | 
 | 	mutex_unlock(&onyx->mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | #endif /* CONFIG_PM */ | 
 |  | 
 | static struct codec_info onyx_codec_info = { | 
 | 	.transfers = onyx_transfers, | 
 | 	.sysclock_factor = 256, | 
 | 	.bus_factor = 64, | 
 | 	.owner = THIS_MODULE, | 
 | 	.usable = onyx_usable, | 
 | 	.prepare = onyx_prepare, | 
 | 	.open = onyx_open, | 
 | 	.close = onyx_close, | 
 | 	.switch_clock = onyx_switch_clock, | 
 | #ifdef CONFIG_PM | 
 | 	.suspend = onyx_suspend, | 
 | 	.resume = onyx_resume, | 
 | #endif | 
 | }; | 
 |  | 
 | static int onyx_init_codec(struct aoa_codec *codec) | 
 | { | 
 | 	struct onyx *onyx = codec_to_onyx(codec); | 
 | 	struct snd_kcontrol *ctl; | 
 | 	struct codec_info *ci = &onyx_codec_info; | 
 | 	u8 v; | 
 | 	int err; | 
 |  | 
 | 	if (!onyx->codec.gpio || !onyx->codec.gpio->methods) { | 
 | 		printk(KERN_ERR PFX "gpios not assigned!!\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | 
 | 	msleep(1); | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); | 
 | 	msleep(1); | 
 | 	onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | 
 | 	msleep(1); | 
 |  | 
 | 	if (onyx_register_init(onyx)) { | 
 | 		printk(KERN_ERR PFX "failed to initialise onyx registers\n"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) { | 
 | 		printk(KERN_ERR PFX "failed to create onyx snd device!\n"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	/* nothing connected? what a joke! */ | 
 | 	if ((onyx->codec.connected & 0xF) == 0) | 
 | 		return -ENOTCONN; | 
 |  | 
 | 	/* if no inputs are present... */ | 
 | 	if ((onyx->codec.connected & 0xC) == 0) { | 
 | 		if (!onyx->codec_info) | 
 | 			onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); | 
 | 		if (!onyx->codec_info) | 
 | 			return -ENOMEM; | 
 | 		ci = onyx->codec_info; | 
 | 		*ci = onyx_codec_info; | 
 | 		ci->transfers++; | 
 | 	} | 
 |  | 
 | 	/* if no outputs are present... */ | 
 | 	if ((onyx->codec.connected & 3) == 0) { | 
 | 		if (!onyx->codec_info) | 
 | 			onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); | 
 | 		if (!onyx->codec_info) | 
 | 			return -ENOMEM; | 
 | 		ci = onyx->codec_info; | 
 | 		/* this is fine as there have to be inputs | 
 | 		 * if we end up in this part of the code */ | 
 | 		*ci = onyx_codec_info; | 
 | 		ci->transfers[1].formats = 0; | 
 | 	} | 
 |  | 
 | 	if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev, | 
 | 						   aoa_get_card(), | 
 | 						   ci, onyx)) { | 
 | 		printk(KERN_ERR PFX "error creating onyx pcm\n"); | 
 | 		return -ENODEV; | 
 | 	} | 
 | #define ADDCTL(n)							\ | 
 | 	do {								\ | 
 | 		ctl = snd_ctl_new1(&n, onyx);				\ | 
 | 		if (ctl) {						\ | 
 | 			ctl->id.device =				\ | 
 | 				onyx->codec.soundbus_dev->pcm->device;	\ | 
 | 			err = aoa_snd_ctl_add(ctl);			\ | 
 | 			if (err)					\ | 
 | 				goto error;				\ | 
 | 		}							\ | 
 | 	} while (0) | 
 |  | 
 | 	if (onyx->codec.soundbus_dev->pcm) { | 
 | 		/* give the user appropriate controls | 
 | 		 * depending on what inputs are connected */ | 
 | 		if ((onyx->codec.connected & 0xC) == 0xC) | 
 | 			ADDCTL(capture_source_control); | 
 | 		else if (onyx->codec.connected & 4) | 
 | 			onyx_set_capture_source(onyx, 0); | 
 | 		else | 
 | 			onyx_set_capture_source(onyx, 1); | 
 | 		if (onyx->codec.connected & 0xC) | 
 | 			ADDCTL(inputgain_control); | 
 |  | 
 | 		/* depending on what output is connected, | 
 | 		 * give the user appropriate controls */ | 
 | 		if (onyx->codec.connected & 1) { | 
 | 			ADDCTL(volume_control); | 
 | 			ADDCTL(mute_control); | 
 | 			ADDCTL(ovr1_control); | 
 | 			ADDCTL(flt0_control); | 
 | 			ADDCTL(hpf_control); | 
 | 			ADDCTL(dm12_control); | 
 | 			/* spdif control defaults to off */ | 
 | 		} | 
 | 		if (onyx->codec.connected & 2) { | 
 | 			ADDCTL(onyx_spdif_mask); | 
 | 			ADDCTL(onyx_spdif_ctrl); | 
 | 		} | 
 | 		if ((onyx->codec.connected & 3) == 3) | 
 | 			ADDCTL(spdif_control); | 
 | 		/* if only S/PDIF is connected, enable it unconditionally */ | 
 | 		if ((onyx->codec.connected & 3) == 2) { | 
 | 			onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | 
 | 			v |= ONYX_SPDIF_ENABLE; | 
 | 			onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); | 
 | 		} | 
 | 	} | 
 | #undef ADDCTL | 
 | 	printk(KERN_INFO PFX "attached to onyx codec via i2c\n"); | 
 |  | 
 | 	return 0; | 
 |  error: | 
 | 	onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); | 
 | 	snd_device_free(aoa_get_card(), onyx); | 
 | 	return err; | 
 | } | 
 |  | 
 | static void onyx_exit_codec(struct aoa_codec *codec) | 
 | { | 
 | 	struct onyx *onyx = codec_to_onyx(codec); | 
 |  | 
 | 	if (!onyx->codec.soundbus_dev) { | 
 | 		printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n"); | 
 | 		return; | 
 | 	} | 
 | 	onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); | 
 | } | 
 |  | 
 | static int onyx_i2c_probe(struct i2c_client *client, | 
 | 			  const struct i2c_device_id *id) | 
 | { | 
 | 	struct device_node *node = client->dev.of_node; | 
 | 	struct onyx *onyx; | 
 | 	u8 dummy; | 
 |  | 
 | 	onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL); | 
 |  | 
 | 	if (!onyx) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	mutex_init(&onyx->mutex); | 
 | 	onyx->i2c = client; | 
 | 	i2c_set_clientdata(client, onyx); | 
 |  | 
 | 	/* we try to read from register ONYX_REG_CONTROL | 
 | 	 * to check if the codec is present */ | 
 | 	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) { | 
 | 		printk(KERN_ERR PFX "failed to read control register\n"); | 
 | 		goto fail; | 
 | 	} | 
 |  | 
 | 	strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN); | 
 | 	onyx->codec.owner = THIS_MODULE; | 
 | 	onyx->codec.init = onyx_init_codec; | 
 | 	onyx->codec.exit = onyx_exit_codec; | 
 | 	onyx->codec.node = of_node_get(node); | 
 |  | 
 | 	if (aoa_codec_register(&onyx->codec)) { | 
 | 		goto fail; | 
 | 	} | 
 | 	printk(KERN_DEBUG PFX "created and attached onyx instance\n"); | 
 | 	return 0; | 
 |  fail: | 
 | 	kfree(onyx); | 
 | 	return -ENODEV; | 
 | } | 
 |  | 
 | static int onyx_i2c_remove(struct i2c_client *client) | 
 | { | 
 | 	struct onyx *onyx = i2c_get_clientdata(client); | 
 |  | 
 | 	aoa_codec_unregister(&onyx->codec); | 
 | 	of_node_put(onyx->codec.node); | 
 | 	kfree(onyx->codec_info); | 
 | 	kfree(onyx); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct i2c_device_id onyx_i2c_id[] = { | 
 | 	{ "MAC,pcm3052", 0 }, | 
 | 	{ } | 
 | }; | 
 | MODULE_DEVICE_TABLE(i2c,onyx_i2c_id); | 
 |  | 
 | static struct i2c_driver onyx_driver = { | 
 | 	.driver = { | 
 | 		.name = "aoa_codec_onyx", | 
 | 	}, | 
 | 	.probe = onyx_i2c_probe, | 
 | 	.remove = onyx_i2c_remove, | 
 | 	.id_table = onyx_i2c_id, | 
 | }; | 
 |  | 
 | module_i2c_driver(onyx_driver); |