162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Apple Onboard Audio driver for Onyx codec 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This is a driver for the pcm3052 codec chip (codenamed Onyx) 862306a36Sopenharmony_ci * that is present in newer Apple hardware (with digital output). 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The Onyx codec has the following connections (listed by the bit 1162306a36Sopenharmony_ci * to be used in aoa_codec.connected): 1262306a36Sopenharmony_ci * 0: analog output 1362306a36Sopenharmony_ci * 1: digital output 1462306a36Sopenharmony_ci * 2: line input 1562306a36Sopenharmony_ci * 3: microphone input 1662306a36Sopenharmony_ci * Note that even though I know of no machine that has for example 1762306a36Sopenharmony_ci * the digital output connected but not the analog, I have handled 1862306a36Sopenharmony_ci * all the different cases in the code so that this driver may serve 1962306a36Sopenharmony_ci * as a good example of what to do. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * NOTE: This driver assumes that there's at most one chip to be 2262306a36Sopenharmony_ci * used with one alsa card, in form of creating all kinds 2362306a36Sopenharmony_ci * of mixer elements without regard for their existence. 2462306a36Sopenharmony_ci * But snd-aoa assumes that there's at most one card, so 2562306a36Sopenharmony_ci * this means you can only have one onyx on a system. This 2662306a36Sopenharmony_ci * should probably be fixed by changing the assumption of 2762306a36Sopenharmony_ci * having just a single card on a system, and making the 2862306a36Sopenharmony_ci * 'card' pointer accessible to anyone who needs it instead 2962306a36Sopenharmony_ci * of hiding it in the aoa_snd_* functions... 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ciMODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "onyx.h" 3962306a36Sopenharmony_ci#include "../aoa.h" 4062306a36Sopenharmony_ci#include "../soundbus/soundbus.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define PFX "snd-aoa-codec-onyx: " 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct onyx { 4662306a36Sopenharmony_ci /* cache registers 65 to 80, they are write-only! */ 4762306a36Sopenharmony_ci u8 cache[16]; 4862306a36Sopenharmony_ci struct i2c_client *i2c; 4962306a36Sopenharmony_ci struct aoa_codec codec; 5062306a36Sopenharmony_ci u32 initialised:1, 5162306a36Sopenharmony_ci spdif_locked:1, 5262306a36Sopenharmony_ci analog_locked:1, 5362306a36Sopenharmony_ci original_mute:2; 5462306a36Sopenharmony_ci int open_count; 5562306a36Sopenharmony_ci struct codec_info *codec_info; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* mutex serializes concurrent access to the device 5862306a36Sopenharmony_ci * and this structure. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci struct mutex mutex; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci#define codec_to_onyx(c) container_of(c, struct onyx, codec) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* both return 0 if all ok, else on error */ 6562306a36Sopenharmony_cistatic int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci s32 v; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (reg != ONYX_REG_CONTROL) { 7062306a36Sopenharmony_ci *value = onyx->cache[reg-FIRSTREGISTER]; 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci v = i2c_smbus_read_byte_data(onyx->i2c, reg); 7462306a36Sopenharmony_ci if (v < 0) { 7562306a36Sopenharmony_ci *value = 0; 7662306a36Sopenharmony_ci return -1; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci *value = (u8)v; 7962306a36Sopenharmony_ci onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int onyx_write_register(struct onyx *onyx, u8 reg, u8 value) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int result; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci result = i2c_smbus_write_byte_data(onyx->i2c, reg, value); 8862306a36Sopenharmony_ci if (!result) 8962306a36Sopenharmony_ci onyx->cache[reg-FIRSTREGISTER] = value; 9062306a36Sopenharmony_ci return result; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* alsa stuff */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int onyx_dev_register(struct snd_device *dev) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const struct snd_device_ops ops = { 10162306a36Sopenharmony_ci .dev_register = onyx_dev_register, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* this is necessary because most alsa mixer programs 10562306a36Sopenharmony_ci * can't properly handle the negative range */ 10662306a36Sopenharmony_ci#define VOLUME_RANGE_SHIFT 128 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int onyx_snd_vol_info(struct snd_kcontrol *kcontrol, 10962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 11262306a36Sopenharmony_ci uinfo->count = 2; 11362306a36Sopenharmony_ci uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT; 11462306a36Sopenharmony_ci uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT; 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int onyx_snd_vol_get(struct snd_kcontrol *kcontrol, 11962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 12262306a36Sopenharmony_ci s8 l, r; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 12562306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); 12662306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); 12762306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT; 13062306a36Sopenharmony_ci ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, 13662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 13962306a36Sopenharmony_ci s8 l, r; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT || 14262306a36Sopenharmony_ci ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT || 14562306a36Sopenharmony_ci ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT) 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 14962306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); 15062306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] && 15362306a36Sopenharmony_ci r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) { 15462306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, 15962306a36Sopenharmony_ci ucontrol->value.integer.value[0] 16062306a36Sopenharmony_ci - VOLUME_RANGE_SHIFT); 16162306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, 16262306a36Sopenharmony_ci ucontrol->value.integer.value[1] 16362306a36Sopenharmony_ci - VOLUME_RANGE_SHIFT); 16462306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 1; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct snd_kcontrol_new volume_control = { 17062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 17162306a36Sopenharmony_ci .name = "Master Playback Volume", 17262306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 17362306a36Sopenharmony_ci .info = onyx_snd_vol_info, 17462306a36Sopenharmony_ci .get = onyx_snd_vol_get, 17562306a36Sopenharmony_ci .put = onyx_snd_vol_put, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* like above, this is necessary because a lot 17962306a36Sopenharmony_ci * of alsa mixer programs don't handle ranges 18062306a36Sopenharmony_ci * that don't start at 0 properly. 18162306a36Sopenharmony_ci * even alsamixer is one of them... */ 18262306a36Sopenharmony_ci#define INPUTGAIN_RANGE_SHIFT (-3) 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol, 18562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 18862306a36Sopenharmony_ci uinfo->count = 1; 18962306a36Sopenharmony_ci uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT; 19062306a36Sopenharmony_ci uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT; 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol, 19562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 19862306a36Sopenharmony_ci u8 ig; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 20162306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig); 20262306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 20562306a36Sopenharmony_ci (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, 21162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 21462306a36Sopenharmony_ci u8 v, n; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT || 21762306a36Sopenharmony_ci ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT) 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 22062306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); 22162306a36Sopenharmony_ci n = v; 22262306a36Sopenharmony_ci n &= ~ONYX_ADC_PGA_GAIN_MASK; 22362306a36Sopenharmony_ci n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT) 22462306a36Sopenharmony_ci & ONYX_ADC_PGA_GAIN_MASK; 22562306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n); 22662306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return n != v; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic const struct snd_kcontrol_new inputgain_control = { 23262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 23362306a36Sopenharmony_ci .name = "Master Capture Volume", 23462306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 23562306a36Sopenharmony_ci .info = onyx_snd_inputgain_info, 23662306a36Sopenharmony_ci .get = onyx_snd_inputgain_get, 23762306a36Sopenharmony_ci .put = onyx_snd_inputgain_put, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, 24162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci static const char * const texts[] = { "Line-In", "Microphone" }; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, 24962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 25262306a36Sopenharmony_ci s8 v; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 25562306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); 25662306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void onyx_set_capture_source(struct onyx *onyx, int mic) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci s8 v; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 26862306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); 26962306a36Sopenharmony_ci v &= ~ONYX_ADC_INPUT_MIC; 27062306a36Sopenharmony_ci if (mic) 27162306a36Sopenharmony_ci v |= ONYX_ADC_INPUT_MIC; 27262306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v); 27362306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, 27762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] > 1) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci onyx_set_capture_source(snd_kcontrol_chip(kcontrol), 28262306a36Sopenharmony_ci ucontrol->value.enumerated.item[0]); 28362306a36Sopenharmony_ci return 1; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct snd_kcontrol_new capture_source_control = { 28762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 28862306a36Sopenharmony_ci /* If we name this 'Input Source', it properly shows up in 28962306a36Sopenharmony_ci * alsamixer as a selection, * but it's shown under the 29062306a36Sopenharmony_ci * 'Playback' category. 29162306a36Sopenharmony_ci * If I name it 'Capture Source', it shows up in strange 29262306a36Sopenharmony_ci * ways (two bools of which one can be selected at a 29362306a36Sopenharmony_ci * time) but at least it's shown in the 'Capture' 29462306a36Sopenharmony_ci * category. 29562306a36Sopenharmony_ci * I was told that this was due to backward compatibility, 29662306a36Sopenharmony_ci * but I don't understand then why the mangling is *not* 29762306a36Sopenharmony_ci * done when I name it "Input Source"..... 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci .name = "Capture Source", 30062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 30162306a36Sopenharmony_ci .info = onyx_snd_capture_source_info, 30262306a36Sopenharmony_ci .get = onyx_snd_capture_source_get, 30362306a36Sopenharmony_ci .put = onyx_snd_capture_source_put, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci#define onyx_snd_mute_info snd_ctl_boolean_stereo_info 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, 30962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 31262306a36Sopenharmony_ci u8 c; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 31562306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c); 31662306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT); 31962306a36Sopenharmony_ci ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int onyx_snd_mute_put(struct snd_kcontrol *kcontrol, 32562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 32862306a36Sopenharmony_ci u8 v = 0, c = 0; 32962306a36Sopenharmony_ci int err = -EBUSY; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 33262306a36Sopenharmony_ci if (onyx->analog_locked) 33362306a36Sopenharmony_ci goto out_unlock; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); 33662306a36Sopenharmony_ci c = v; 33762306a36Sopenharmony_ci c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT); 33862306a36Sopenharmony_ci if (!ucontrol->value.integer.value[0]) 33962306a36Sopenharmony_ci c |= ONYX_MUTE_LEFT; 34062306a36Sopenharmony_ci if (!ucontrol->value.integer.value[1]) 34162306a36Sopenharmony_ci c |= ONYX_MUTE_RIGHT; 34262306a36Sopenharmony_ci err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci out_unlock: 34562306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return !err ? (v != c) : err; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic const struct snd_kcontrol_new mute_control = { 35162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 35262306a36Sopenharmony_ci .name = "Master Playback Switch", 35362306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 35462306a36Sopenharmony_ci .info = onyx_snd_mute_info, 35562306a36Sopenharmony_ci .get = onyx_snd_mute_get, 35662306a36Sopenharmony_ci .put = onyx_snd_mute_put, 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci#define onyx_snd_single_bit_info snd_ctl_boolean_mono_info 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci#define FLAG_POLARITY_INVERT 1 36362306a36Sopenharmony_ci#define FLAG_SPDIFLOCK 2 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol, 36662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 36962306a36Sopenharmony_ci u8 c; 37062306a36Sopenharmony_ci long int pv = kcontrol->private_value; 37162306a36Sopenharmony_ci u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; 37262306a36Sopenharmony_ci u8 address = (pv >> 8) & 0xff; 37362306a36Sopenharmony_ci u8 mask = pv & 0xff; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 37662306a36Sopenharmony_ci onyx_read_register(onyx, address, &c); 37762306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol, 38562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 38862306a36Sopenharmony_ci u8 v = 0, c = 0; 38962306a36Sopenharmony_ci int err; 39062306a36Sopenharmony_ci long int pv = kcontrol->private_value; 39162306a36Sopenharmony_ci u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; 39262306a36Sopenharmony_ci u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK; 39362306a36Sopenharmony_ci u8 address = (pv >> 8) & 0xff; 39462306a36Sopenharmony_ci u8 mask = pv & 0xff; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 39762306a36Sopenharmony_ci if (spdiflock && onyx->spdif_locked) { 39862306a36Sopenharmony_ci /* even if alsamixer doesn't care.. */ 39962306a36Sopenharmony_ci err = -EBUSY; 40062306a36Sopenharmony_ci goto out_unlock; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci onyx_read_register(onyx, address, &v); 40362306a36Sopenharmony_ci c = v; 40462306a36Sopenharmony_ci c &= ~(mask); 40562306a36Sopenharmony_ci if (!!ucontrol->value.integer.value[0] ^ polarity) 40662306a36Sopenharmony_ci c |= mask; 40762306a36Sopenharmony_ci err = onyx_write_register(onyx, address, c); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci out_unlock: 41062306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return !err ? (v != c) : err; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci#define SINGLE_BIT(n, type, description, address, mask, flags) \ 41662306a36Sopenharmony_cistatic const struct snd_kcontrol_new n##_control = { \ 41762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_##type, \ 41862306a36Sopenharmony_ci .name = description, \ 41962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 42062306a36Sopenharmony_ci .info = onyx_snd_single_bit_info, \ 42162306a36Sopenharmony_ci .get = onyx_snd_single_bit_get, \ 42262306a36Sopenharmony_ci .put = onyx_snd_single_bit_put, \ 42362306a36Sopenharmony_ci .private_value = (flags << 16) | (address << 8) | mask \ 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciSINGLE_BIT(spdif, 42762306a36Sopenharmony_ci MIXER, 42862306a36Sopenharmony_ci SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), 42962306a36Sopenharmony_ci ONYX_REG_DIG_INFO4, 43062306a36Sopenharmony_ci ONYX_SPDIF_ENABLE, 43162306a36Sopenharmony_ci FLAG_SPDIFLOCK); 43262306a36Sopenharmony_ciSINGLE_BIT(ovr1, 43362306a36Sopenharmony_ci MIXER, 43462306a36Sopenharmony_ci "Oversampling Rate", 43562306a36Sopenharmony_ci ONYX_REG_DAC_CONTROL, 43662306a36Sopenharmony_ci ONYX_OVR1, 43762306a36Sopenharmony_ci 0); 43862306a36Sopenharmony_ciSINGLE_BIT(flt0, 43962306a36Sopenharmony_ci MIXER, 44062306a36Sopenharmony_ci "Fast Digital Filter Rolloff", 44162306a36Sopenharmony_ci ONYX_REG_DAC_FILTER, 44262306a36Sopenharmony_ci ONYX_ROLLOFF_FAST, 44362306a36Sopenharmony_ci FLAG_POLARITY_INVERT); 44462306a36Sopenharmony_ciSINGLE_BIT(hpf, 44562306a36Sopenharmony_ci MIXER, 44662306a36Sopenharmony_ci "Highpass Filter", 44762306a36Sopenharmony_ci ONYX_REG_ADC_HPF_BYPASS, 44862306a36Sopenharmony_ci ONYX_HPF_DISABLE, 44962306a36Sopenharmony_ci FLAG_POLARITY_INVERT); 45062306a36Sopenharmony_ciSINGLE_BIT(dm12, 45162306a36Sopenharmony_ci MIXER, 45262306a36Sopenharmony_ci "Digital De-Emphasis", 45362306a36Sopenharmony_ci ONYX_REG_DAC_DEEMPH, 45462306a36Sopenharmony_ci ONYX_DIGDEEMPH_CTRL, 45562306a36Sopenharmony_ci 0); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int onyx_spdif_info(struct snd_kcontrol *kcontrol, 45862306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 46162306a36Sopenharmony_ci uinfo->count = 1; 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol, 46662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci /* datasheet page 30, all others are 0 */ 46962306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = 0x3e; 47062306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = 0x3f; 47362306a36Sopenharmony_ci ucontrol->value.iec958.status[4] = 0x0f; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic const struct snd_kcontrol_new onyx_spdif_mask = { 47962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 48062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 48162306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), 48262306a36Sopenharmony_ci .info = onyx_spdif_info, 48362306a36Sopenharmony_ci .get = onyx_spdif_mask_get, 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int onyx_spdif_get(struct snd_kcontrol *kcontrol, 48762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 49062306a36Sopenharmony_ci u8 v; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 49362306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); 49462306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = v & 0x3e; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v); 49762306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = v; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); 50062306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = v & 0x3f; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); 50362306a36Sopenharmony_ci ucontrol->value.iec958.status[4] = v & 0x0f; 50462306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int onyx_spdif_put(struct snd_kcontrol *kcontrol, 51062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct onyx *onyx = snd_kcontrol_chip(kcontrol); 51362306a36Sopenharmony_ci u8 v; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 51662306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); 51762306a36Sopenharmony_ci v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e); 51862306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci v = ucontrol->value.iec958.status[1]; 52162306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); 52462306a36Sopenharmony_ci v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f); 52562306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); 52862306a36Sopenharmony_ci v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f); 52962306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); 53062306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 1; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic const struct snd_kcontrol_new onyx_spdif_ctrl = { 53662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 53762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 53862306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 53962306a36Sopenharmony_ci .info = onyx_spdif_info, 54062306a36Sopenharmony_ci .get = onyx_spdif_get, 54162306a36Sopenharmony_ci .put = onyx_spdif_put, 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* our registers */ 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic const u8 register_map[] = { 54762306a36Sopenharmony_ci ONYX_REG_DAC_ATTEN_LEFT, 54862306a36Sopenharmony_ci ONYX_REG_DAC_ATTEN_RIGHT, 54962306a36Sopenharmony_ci ONYX_REG_CONTROL, 55062306a36Sopenharmony_ci ONYX_REG_DAC_CONTROL, 55162306a36Sopenharmony_ci ONYX_REG_DAC_DEEMPH, 55262306a36Sopenharmony_ci ONYX_REG_DAC_FILTER, 55362306a36Sopenharmony_ci ONYX_REG_DAC_OUTPHASE, 55462306a36Sopenharmony_ci ONYX_REG_ADC_CONTROL, 55562306a36Sopenharmony_ci ONYX_REG_ADC_HPF_BYPASS, 55662306a36Sopenharmony_ci ONYX_REG_DIG_INFO1, 55762306a36Sopenharmony_ci ONYX_REG_DIG_INFO2, 55862306a36Sopenharmony_ci ONYX_REG_DIG_INFO3, 55962306a36Sopenharmony_ci ONYX_REG_DIG_INFO4 56062306a36Sopenharmony_ci}; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic const u8 initial_values[ARRAY_SIZE(register_map)] = { 56362306a36Sopenharmony_ci 0x80, 0x80, /* muted */ 56462306a36Sopenharmony_ci ONYX_MRST | ONYX_SRST, /* but handled specially! */ 56562306a36Sopenharmony_ci ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT, 56662306a36Sopenharmony_ci 0, /* no deemphasis */ 56762306a36Sopenharmony_ci ONYX_DAC_FILTER_ALWAYS, 56862306a36Sopenharmony_ci ONYX_OUTPHASE_INVERTED, 56962306a36Sopenharmony_ci (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/ 57062306a36Sopenharmony_ci ONYX_ADC_HPF_ALWAYS, 57162306a36Sopenharmony_ci (1<<2), /* pcm audio */ 57262306a36Sopenharmony_ci 2, /* category: pcm coder */ 57362306a36Sopenharmony_ci 0, /* sampling frequency 44.1 kHz, clock accuracy level II */ 57462306a36Sopenharmony_ci 1 /* 24 bit depth */ 57562306a36Sopenharmony_ci}; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* reset registers of chip, either to initial or to previous values */ 57862306a36Sopenharmony_cistatic int onyx_register_init(struct onyx *onyx) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci int i; 58162306a36Sopenharmony_ci u8 val; 58262306a36Sopenharmony_ci u8 regs[sizeof(initial_values)]; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (!onyx->initialised) { 58562306a36Sopenharmony_ci memcpy(regs, initial_values, sizeof(initial_values)); 58662306a36Sopenharmony_ci if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val)) 58762306a36Sopenharmony_ci return -1; 58862306a36Sopenharmony_ci val &= ~ONYX_SILICONVERSION; 58962306a36Sopenharmony_ci val |= initial_values[3]; 59062306a36Sopenharmony_ci regs[3] = val; 59162306a36Sopenharmony_ci } else { 59262306a36Sopenharmony_ci for (i=0; i<sizeof(register_map); i++) 59362306a36Sopenharmony_ci regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER]; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci for (i=0; i<sizeof(register_map); i++) { 59762306a36Sopenharmony_ci if (onyx_write_register(onyx, register_map[i], regs[i])) 59862306a36Sopenharmony_ci return -1; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci onyx->initialised = 1; 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic struct transfer_info onyx_transfers[] = { 60562306a36Sopenharmony_ci /* this is first so we can skip it if no input is present... 60662306a36Sopenharmony_ci * No hardware exists with that, but it's here as an example 60762306a36Sopenharmony_ci * of what to do :) */ 60862306a36Sopenharmony_ci { 60962306a36Sopenharmony_ci /* analog input */ 61062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 61162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | 61262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE, 61362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 61462306a36Sopenharmony_ci .transfer_in = 1, 61562306a36Sopenharmony_ci .must_be_clock_source = 0, 61662306a36Sopenharmony_ci .tag = 0, 61762306a36Sopenharmony_ci }, 61862306a36Sopenharmony_ci { 61962306a36Sopenharmony_ci /* if analog and digital are currently off, anything should go, 62062306a36Sopenharmony_ci * so this entry describes everything we can do... */ 62162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 62262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | 62362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE 62462306a36Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE 62562306a36Sopenharmony_ci | SNDRV_PCM_FMTBIT_COMPRESSED_16BE 62662306a36Sopenharmony_ci#endif 62762306a36Sopenharmony_ci , 62862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 62962306a36Sopenharmony_ci .tag = 0, 63062306a36Sopenharmony_ci }, 63162306a36Sopenharmony_ci { 63262306a36Sopenharmony_ci /* analog output */ 63362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 63462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | 63562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE, 63662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 63762306a36Sopenharmony_ci .transfer_in = 0, 63862306a36Sopenharmony_ci .must_be_clock_source = 0, 63962306a36Sopenharmony_ci .tag = 1, 64062306a36Sopenharmony_ci }, 64162306a36Sopenharmony_ci { 64262306a36Sopenharmony_ci /* digital pcm output, also possible for analog out */ 64362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | 64462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | 64562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE, 64662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | 64762306a36Sopenharmony_ci SNDRV_PCM_RATE_44100 | 64862306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 64962306a36Sopenharmony_ci .transfer_in = 0, 65062306a36Sopenharmony_ci .must_be_clock_source = 0, 65162306a36Sopenharmony_ci .tag = 2, 65262306a36Sopenharmony_ci }, 65362306a36Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE 65462306a36Sopenharmony_ci /* Once alsa gets supports for this kind of thing we can add it... */ 65562306a36Sopenharmony_ci { 65662306a36Sopenharmony_ci /* digital compressed output */ 65762306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, 65862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | 65962306a36Sopenharmony_ci SNDRV_PCM_RATE_44100 | 66062306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 66162306a36Sopenharmony_ci .tag = 2, 66262306a36Sopenharmony_ci }, 66362306a36Sopenharmony_ci#endif 66462306a36Sopenharmony_ci {} 66562306a36Sopenharmony_ci}; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int onyx_usable(struct codec_info_item *cii, 66862306a36Sopenharmony_ci struct transfer_info *ti, 66962306a36Sopenharmony_ci struct transfer_info *out) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci u8 v; 67262306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 67362306a36Sopenharmony_ci int spdif_enabled, analog_enabled; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 67662306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); 67762306a36Sopenharmony_ci spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); 67862306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); 67962306a36Sopenharmony_ci analog_enabled = 68062306a36Sopenharmony_ci (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) 68162306a36Sopenharmony_ci != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); 68262306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci switch (ti->tag) { 68562306a36Sopenharmony_ci case 0: return 1; 68662306a36Sopenharmony_ci case 1: return analog_enabled; 68762306a36Sopenharmony_ci case 2: return spdif_enabled; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci return 1; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int onyx_prepare(struct codec_info_item *cii, 69362306a36Sopenharmony_ci struct bus_info *bi, 69462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci u8 v; 69762306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 69862306a36Sopenharmony_ci int err = -EBUSY; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE 70362306a36Sopenharmony_ci if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { 70462306a36Sopenharmony_ci /* mute and lock analog output */ 70562306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); 70662306a36Sopenharmony_ci if (onyx_write_register(onyx, 70762306a36Sopenharmony_ci ONYX_REG_DAC_CONTROL, 70862306a36Sopenharmony_ci v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) 70962306a36Sopenharmony_ci goto out_unlock; 71062306a36Sopenharmony_ci onyx->analog_locked = 1; 71162306a36Sopenharmony_ci err = 0; 71262306a36Sopenharmony_ci goto out_unlock; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci#endif 71562306a36Sopenharmony_ci switch (substream->runtime->rate) { 71662306a36Sopenharmony_ci case 32000: 71762306a36Sopenharmony_ci case 44100: 71862306a36Sopenharmony_ci case 48000: 71962306a36Sopenharmony_ci /* these rates are ok for all outputs */ 72062306a36Sopenharmony_ci /* FIXME: program spdif channel control bits here so that 72162306a36Sopenharmony_ci * userspace doesn't have to if it only plays pcm! */ 72262306a36Sopenharmony_ci err = 0; 72362306a36Sopenharmony_ci goto out_unlock; 72462306a36Sopenharmony_ci default: 72562306a36Sopenharmony_ci /* got some rate that the digital output can't do, 72662306a36Sopenharmony_ci * so disable and lock it */ 72762306a36Sopenharmony_ci onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v); 72862306a36Sopenharmony_ci if (onyx_write_register(onyx, 72962306a36Sopenharmony_ci ONYX_REG_DIG_INFO4, 73062306a36Sopenharmony_ci v & ~ONYX_SPDIF_ENABLE)) 73162306a36Sopenharmony_ci goto out_unlock; 73262306a36Sopenharmony_ci onyx->spdif_locked = 1; 73362306a36Sopenharmony_ci err = 0; 73462306a36Sopenharmony_ci goto out_unlock; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci out_unlock: 73862306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return err; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int onyx_open(struct codec_info_item *cii, 74462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 74962306a36Sopenharmony_ci onyx->open_count++; 75062306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int onyx_close(struct codec_info_item *cii, 75662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 76162306a36Sopenharmony_ci onyx->open_count--; 76262306a36Sopenharmony_ci if (!onyx->open_count) 76362306a36Sopenharmony_ci onyx->spdif_locked = onyx->analog_locked = 0; 76462306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int onyx_switch_clock(struct codec_info_item *cii, 77062306a36Sopenharmony_ci enum clock_switch what) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 77562306a36Sopenharmony_ci /* this *MUST* be more elaborate later... */ 77662306a36Sopenharmony_ci switch (what) { 77762306a36Sopenharmony_ci case CLOCK_SWITCH_PREPARE_SLAVE: 77862306a36Sopenharmony_ci onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio); 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case CLOCK_SWITCH_SLAVE: 78162306a36Sopenharmony_ci onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio); 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci default: /* silence warning */ 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci#ifdef CONFIG_PM 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic int onyx_suspend(struct codec_info_item *cii, pm_message_t state) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 79662306a36Sopenharmony_ci u8 v; 79762306a36Sopenharmony_ci int err = -ENXIO; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 80062306a36Sopenharmony_ci if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) 80162306a36Sopenharmony_ci goto out_unlock; 80262306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV); 80362306a36Sopenharmony_ci /* Apple does a sleep here but the datasheet says to do it on resume */ 80462306a36Sopenharmony_ci err = 0; 80562306a36Sopenharmony_ci out_unlock: 80662306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return err; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int onyx_resume(struct codec_info_item *cii) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct onyx *onyx = cii->codec_data; 81462306a36Sopenharmony_ci u8 v; 81562306a36Sopenharmony_ci int err = -ENXIO; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci mutex_lock(&onyx->mutex); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* reset codec */ 82062306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); 82162306a36Sopenharmony_ci msleep(1); 82262306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); 82362306a36Sopenharmony_ci msleep(1); 82462306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); 82562306a36Sopenharmony_ci msleep(1); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* take codec out of suspend (if it still is after reset) */ 82862306a36Sopenharmony_ci if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) 82962306a36Sopenharmony_ci goto out_unlock; 83062306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); 83162306a36Sopenharmony_ci /* FIXME: should divide by sample rate, but 8k is the lowest we go */ 83262306a36Sopenharmony_ci msleep(2205000/8000); 83362306a36Sopenharmony_ci /* reset all values */ 83462306a36Sopenharmony_ci onyx_register_init(onyx); 83562306a36Sopenharmony_ci err = 0; 83662306a36Sopenharmony_ci out_unlock: 83762306a36Sopenharmony_ci mutex_unlock(&onyx->mutex); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return err; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic struct codec_info onyx_codec_info = { 84562306a36Sopenharmony_ci .transfers = onyx_transfers, 84662306a36Sopenharmony_ci .sysclock_factor = 256, 84762306a36Sopenharmony_ci .bus_factor = 64, 84862306a36Sopenharmony_ci .owner = THIS_MODULE, 84962306a36Sopenharmony_ci .usable = onyx_usable, 85062306a36Sopenharmony_ci .prepare = onyx_prepare, 85162306a36Sopenharmony_ci .open = onyx_open, 85262306a36Sopenharmony_ci .close = onyx_close, 85362306a36Sopenharmony_ci .switch_clock = onyx_switch_clock, 85462306a36Sopenharmony_ci#ifdef CONFIG_PM 85562306a36Sopenharmony_ci .suspend = onyx_suspend, 85662306a36Sopenharmony_ci .resume = onyx_resume, 85762306a36Sopenharmony_ci#endif 85862306a36Sopenharmony_ci}; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int onyx_init_codec(struct aoa_codec *codec) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct onyx *onyx = codec_to_onyx(codec); 86362306a36Sopenharmony_ci struct snd_kcontrol *ctl; 86462306a36Sopenharmony_ci struct codec_info *ci = &onyx_codec_info; 86562306a36Sopenharmony_ci u8 v; 86662306a36Sopenharmony_ci int err; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (!onyx->codec.gpio || !onyx->codec.gpio->methods) { 86962306a36Sopenharmony_ci printk(KERN_ERR PFX "gpios not assigned!!\n"); 87062306a36Sopenharmony_ci return -EINVAL; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); 87462306a36Sopenharmony_ci msleep(1); 87562306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); 87662306a36Sopenharmony_ci msleep(1); 87762306a36Sopenharmony_ci onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); 87862306a36Sopenharmony_ci msleep(1); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (onyx_register_init(onyx)) { 88162306a36Sopenharmony_ci printk(KERN_ERR PFX "failed to initialise onyx registers\n"); 88262306a36Sopenharmony_ci return -ENODEV; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) { 88662306a36Sopenharmony_ci printk(KERN_ERR PFX "failed to create onyx snd device!\n"); 88762306a36Sopenharmony_ci return -ENODEV; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* nothing connected? what a joke! */ 89162306a36Sopenharmony_ci if ((onyx->codec.connected & 0xF) == 0) 89262306a36Sopenharmony_ci return -ENOTCONN; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* if no inputs are present... */ 89562306a36Sopenharmony_ci if ((onyx->codec.connected & 0xC) == 0) { 89662306a36Sopenharmony_ci if (!onyx->codec_info) 89762306a36Sopenharmony_ci onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); 89862306a36Sopenharmony_ci if (!onyx->codec_info) 89962306a36Sopenharmony_ci return -ENOMEM; 90062306a36Sopenharmony_ci ci = onyx->codec_info; 90162306a36Sopenharmony_ci *ci = onyx_codec_info; 90262306a36Sopenharmony_ci ci->transfers++; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* if no outputs are present... */ 90662306a36Sopenharmony_ci if ((onyx->codec.connected & 3) == 0) { 90762306a36Sopenharmony_ci if (!onyx->codec_info) 90862306a36Sopenharmony_ci onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); 90962306a36Sopenharmony_ci if (!onyx->codec_info) 91062306a36Sopenharmony_ci return -ENOMEM; 91162306a36Sopenharmony_ci ci = onyx->codec_info; 91262306a36Sopenharmony_ci /* this is fine as there have to be inputs 91362306a36Sopenharmony_ci * if we end up in this part of the code */ 91462306a36Sopenharmony_ci *ci = onyx_codec_info; 91562306a36Sopenharmony_ci ci->transfers[1].formats = 0; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev, 91962306a36Sopenharmony_ci aoa_get_card(), 92062306a36Sopenharmony_ci ci, onyx)) { 92162306a36Sopenharmony_ci printk(KERN_ERR PFX "error creating onyx pcm\n"); 92262306a36Sopenharmony_ci return -ENODEV; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci#define ADDCTL(n) \ 92562306a36Sopenharmony_ci do { \ 92662306a36Sopenharmony_ci ctl = snd_ctl_new1(&n, onyx); \ 92762306a36Sopenharmony_ci if (ctl) { \ 92862306a36Sopenharmony_ci ctl->id.device = \ 92962306a36Sopenharmony_ci onyx->codec.soundbus_dev->pcm->device; \ 93062306a36Sopenharmony_ci err = aoa_snd_ctl_add(ctl); \ 93162306a36Sopenharmony_ci if (err) \ 93262306a36Sopenharmony_ci goto error; \ 93362306a36Sopenharmony_ci } \ 93462306a36Sopenharmony_ci } while (0) 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (onyx->codec.soundbus_dev->pcm) { 93762306a36Sopenharmony_ci /* give the user appropriate controls 93862306a36Sopenharmony_ci * depending on what inputs are connected */ 93962306a36Sopenharmony_ci if ((onyx->codec.connected & 0xC) == 0xC) 94062306a36Sopenharmony_ci ADDCTL(capture_source_control); 94162306a36Sopenharmony_ci else if (onyx->codec.connected & 4) 94262306a36Sopenharmony_ci onyx_set_capture_source(onyx, 0); 94362306a36Sopenharmony_ci else 94462306a36Sopenharmony_ci onyx_set_capture_source(onyx, 1); 94562306a36Sopenharmony_ci if (onyx->codec.connected & 0xC) 94662306a36Sopenharmony_ci ADDCTL(inputgain_control); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* depending on what output is connected, 94962306a36Sopenharmony_ci * give the user appropriate controls */ 95062306a36Sopenharmony_ci if (onyx->codec.connected & 1) { 95162306a36Sopenharmony_ci ADDCTL(volume_control); 95262306a36Sopenharmony_ci ADDCTL(mute_control); 95362306a36Sopenharmony_ci ADDCTL(ovr1_control); 95462306a36Sopenharmony_ci ADDCTL(flt0_control); 95562306a36Sopenharmony_ci ADDCTL(hpf_control); 95662306a36Sopenharmony_ci ADDCTL(dm12_control); 95762306a36Sopenharmony_ci /* spdif control defaults to off */ 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci if (onyx->codec.connected & 2) { 96062306a36Sopenharmony_ci ADDCTL(onyx_spdif_mask); 96162306a36Sopenharmony_ci ADDCTL(onyx_spdif_ctrl); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci if ((onyx->codec.connected & 3) == 3) 96462306a36Sopenharmony_ci ADDCTL(spdif_control); 96562306a36Sopenharmony_ci /* if only S/PDIF is connected, enable it unconditionally */ 96662306a36Sopenharmony_ci if ((onyx->codec.connected & 3) == 2) { 96762306a36Sopenharmony_ci onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); 96862306a36Sopenharmony_ci v |= ONYX_SPDIF_ENABLE; 96962306a36Sopenharmony_ci onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci#undef ADDCTL 97362306a36Sopenharmony_ci printk(KERN_INFO PFX "attached to onyx codec via i2c\n"); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci error: 97762306a36Sopenharmony_ci onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); 97862306a36Sopenharmony_ci snd_device_free(aoa_get_card(), onyx); 97962306a36Sopenharmony_ci return err; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic void onyx_exit_codec(struct aoa_codec *codec) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct onyx *onyx = codec_to_onyx(codec); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!onyx->codec.soundbus_dev) { 98762306a36Sopenharmony_ci printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n"); 98862306a36Sopenharmony_ci return; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int onyx_i2c_probe(struct i2c_client *client) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct device_node *node = client->dev.of_node; 99662306a36Sopenharmony_ci struct onyx *onyx; 99762306a36Sopenharmony_ci u8 dummy; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (!onyx) 100262306a36Sopenharmony_ci return -ENOMEM; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci mutex_init(&onyx->mutex); 100562306a36Sopenharmony_ci onyx->i2c = client; 100662306a36Sopenharmony_ci i2c_set_clientdata(client, onyx); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* we try to read from register ONYX_REG_CONTROL 100962306a36Sopenharmony_ci * to check if the codec is present */ 101062306a36Sopenharmony_ci if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) { 101162306a36Sopenharmony_ci printk(KERN_ERR PFX "failed to read control register\n"); 101262306a36Sopenharmony_ci goto fail; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci strscpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN); 101662306a36Sopenharmony_ci onyx->codec.owner = THIS_MODULE; 101762306a36Sopenharmony_ci onyx->codec.init = onyx_init_codec; 101862306a36Sopenharmony_ci onyx->codec.exit = onyx_exit_codec; 101962306a36Sopenharmony_ci onyx->codec.node = of_node_get(node); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (aoa_codec_register(&onyx->codec)) { 102262306a36Sopenharmony_ci goto fail; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci printk(KERN_DEBUG PFX "created and attached onyx instance\n"); 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci fail: 102762306a36Sopenharmony_ci kfree(onyx); 102862306a36Sopenharmony_ci return -ENODEV; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic void onyx_i2c_remove(struct i2c_client *client) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct onyx *onyx = i2c_get_clientdata(client); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci aoa_codec_unregister(&onyx->codec); 103662306a36Sopenharmony_ci of_node_put(onyx->codec.node); 103762306a36Sopenharmony_ci kfree(onyx->codec_info); 103862306a36Sopenharmony_ci kfree(onyx); 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic const struct i2c_device_id onyx_i2c_id[] = { 104262306a36Sopenharmony_ci { "MAC,pcm3052", 0 }, 104362306a36Sopenharmony_ci { } 104462306a36Sopenharmony_ci}; 104562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c,onyx_i2c_id); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic struct i2c_driver onyx_driver = { 104862306a36Sopenharmony_ci .driver = { 104962306a36Sopenharmony_ci .name = "aoa_codec_onyx", 105062306a36Sopenharmony_ci }, 105162306a36Sopenharmony_ci .probe = onyx_i2c_probe, 105262306a36Sopenharmony_ci .remove = onyx_i2c_remove, 105362306a36Sopenharmony_ci .id_table = onyx_i2c_id, 105462306a36Sopenharmony_ci}; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cimodule_i2c_driver(onyx_driver); 1057