162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021 Cirrus Logic, Inc. and 662306a36Sopenharmony_ci * Cirrus Logic International Semiconductor Ltd. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <sound/core.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/iopoll.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "patch_cs8409.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/****************************************************************************** 1962306a36Sopenharmony_ci * CS8409 Specific Functions 2062306a36Sopenharmony_ci ******************************************************************************/ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int cs8409_parse_auto_config(struct hda_codec *codec) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 2562306a36Sopenharmony_ci int err; 2662306a36Sopenharmony_ci int i; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 2962306a36Sopenharmony_ci if (err < 0) 3062306a36Sopenharmony_ci return err; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 3362306a36Sopenharmony_ci if (err < 0) 3462306a36Sopenharmony_ci return err; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* keep the ADCs powered up when it's dynamically switchable */ 3762306a36Sopenharmony_ci if (spec->gen.dyn_adc_switch) { 3862306a36Sopenharmony_ci unsigned int done = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < spec->gen.input_mux.num_items; i++) { 4162306a36Sopenharmony_ci int idx = spec->gen.dyn_adc_idx[i]; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (done & (1 << idx)) 4462306a36Sopenharmony_ci continue; 4562306a36Sopenharmony_ci snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]); 4662306a36Sopenharmony_ci done |= 1 << idx; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void cs8409_disable_i2c_clock_worker(struct work_struct *work); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct cs8409_spec *spec; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 6062306a36Sopenharmony_ci if (!spec) 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci codec->spec = spec; 6362306a36Sopenharmony_ci spec->codec = codec; 6462306a36Sopenharmony_ci codec->power_save_node = 1; 6562306a36Sopenharmony_ci mutex_init(&spec->i2c_mux); 6662306a36Sopenharmony_ci INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); 6762306a36Sopenharmony_ci snd_hda_gen_spec_init(&spec->gen); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return spec; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); 7562306a36Sopenharmony_ci return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, 7962306a36Sopenharmony_ci unsigned int coef) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); 8262306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * cs8409_enable_i2c_clock - Disable I2C clocks 8762306a36Sopenharmony_ci * @codec: the codec instance 8862306a36Sopenharmony_ci * Disable I2C clocks. 8962306a36Sopenharmony_ci * This must be called when the i2c mutex is unlocked. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic void cs8409_disable_i2c_clock(struct hda_codec *codec) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mutex_lock(&spec->i2c_mux); 9662306a36Sopenharmony_ci if (spec->i2c_clck_enabled) { 9762306a36Sopenharmony_ci cs8409_vendor_coef_set(spec->codec, 0x0, 9862306a36Sopenharmony_ci cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7); 9962306a36Sopenharmony_ci spec->i2c_clck_enabled = 0; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic void cs8409_disable_i2c_clock_worker(struct work_struct *work) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci cs8409_disable_i2c_clock(spec->codec); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * cs8409_enable_i2c_clock - Enable I2C clocks 11662306a36Sopenharmony_ci * @codec: the codec instance 11762306a36Sopenharmony_ci * Enable I2C clocks. 11862306a36Sopenharmony_ci * This must be called when the i2c mutex is locked. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic void cs8409_enable_i2c_clock(struct hda_codec *codec) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Cancel the disable timer, but do not wait for any running disable functions to finish. 12562306a36Sopenharmony_ci * If the disable timer runs out before cancel, the delayed work thread will be blocked, 12662306a36Sopenharmony_ci * waiting for the mutex to become unlocked. This mutex will be locked for the duration of 12762306a36Sopenharmony_ci * any i2c transaction, so the disable function will run to completion immediately 12862306a36Sopenharmony_ci * afterwards in the scenario. The next enable call will re-enable the clock, regardless. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci cancel_delayed_work(&spec->i2c_clk_work); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!spec->i2c_clck_enabled) { 13362306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8); 13462306a36Sopenharmony_ci spec->i2c_clck_enabled = 1; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25)); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * cs8409_i2c_wait_complete - Wait for I2C transaction 14162306a36Sopenharmony_ci * @codec: the codec instance 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Wait for I2C transaction to complete. 14462306a36Sopenharmony_ci * Return -ETIMEDOUT if transaction wait times out. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic int cs8409_i2c_wait_complete(struct hda_codec *codec) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned int retval; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18, 15162306a36Sopenharmony_ci CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * cs8409_set_i2c_dev_addr - Set i2c address for transaction 15662306a36Sopenharmony_ci * @codec: the codec instance 15762306a36Sopenharmony_ci * @addr: I2C Address 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (spec->dev_addr != addr) { 16462306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr); 16562306a36Sopenharmony_ci spec->dev_addr = addr; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/** 17062306a36Sopenharmony_ci * cs8409_i2c_set_page - CS8409 I2C set page register. 17162306a36Sopenharmony_ci * @scodec: the codec instance 17262306a36Sopenharmony_ci * @i2c_reg: Page register 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * Returns negative on error. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct hda_codec *codec = scodec->codec; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) { 18162306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8); 18262306a36Sopenharmony_ci if (cs8409_i2c_wait_complete(codec) < 0) 18362306a36Sopenharmony_ci return -EIO; 18462306a36Sopenharmony_ci scodec->last_page = i2c_reg >> 8; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * cs8409_i2c_read - CS8409 I2C Read. 19262306a36Sopenharmony_ci * @scodec: the codec instance 19362306a36Sopenharmony_ci * @addr: Register to read 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * Returns negative on error, otherwise returns read value in bits 0-7. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct hda_codec *codec = scodec->codec; 20062306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 20162306a36Sopenharmony_ci unsigned int i2c_reg_data; 20262306a36Sopenharmony_ci unsigned int read_data; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (scodec->suspended) 20562306a36Sopenharmony_ci return -EPERM; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci mutex_lock(&spec->i2c_mux); 20862306a36Sopenharmony_ci cs8409_enable_i2c_clock(codec); 20962306a36Sopenharmony_ci cs8409_set_i2c_dev_addr(codec, scodec->addr); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (cs8409_i2c_set_page(scodec, addr)) 21262306a36Sopenharmony_ci goto error; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci i2c_reg_data = (addr << 8) & 0x0ffff; 21562306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); 21662306a36Sopenharmony_ci if (cs8409_i2c_wait_complete(codec) < 0) 21762306a36Sopenharmony_ci goto error; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Register in bits 15-8 and the data in 7-0 */ 22062306a36Sopenharmony_ci read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return read_data & 0x0ff; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cierror: 22762306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 22862306a36Sopenharmony_ci codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); 22962306a36Sopenharmony_ci return -EIO; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence. 23462306a36Sopenharmony_ci * @scodec: the codec instance 23562306a36Sopenharmony_ci * @seq: Register Sequence to read 23662306a36Sopenharmony_ci * @count: Number of registeres to read 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * Returns negative on error, values are read into value element of cs8409_i2c_param sequence. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistatic int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct hda_codec *codec = scodec->codec; 24362306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 24462306a36Sopenharmony_ci unsigned int i2c_reg_data; 24562306a36Sopenharmony_ci int i; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (scodec->suspended) 24862306a36Sopenharmony_ci return -EPERM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci mutex_lock(&spec->i2c_mux); 25162306a36Sopenharmony_ci cs8409_set_i2c_dev_addr(codec, scodec->addr); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < count; i++) { 25462306a36Sopenharmony_ci cs8409_enable_i2c_clock(codec); 25562306a36Sopenharmony_ci if (cs8409_i2c_set_page(scodec, seq[i].addr)) 25662306a36Sopenharmony_ci goto error; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci i2c_reg_data = (seq[i].addr << 8) & 0x0ffff; 25962306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (cs8409_i2c_wait_complete(codec) < 0) 26262306a36Sopenharmony_ci goto error; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cierror: 27262306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 27362306a36Sopenharmony_ci codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); 27462306a36Sopenharmony_ci return -EIO; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/** 27862306a36Sopenharmony_ci * cs8409_i2c_write - CS8409 I2C Write. 27962306a36Sopenharmony_ci * @scodec: the codec instance 28062306a36Sopenharmony_ci * @addr: Register to write to 28162306a36Sopenharmony_ci * @value: Data to write 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Returns negative on error, otherwise returns 0. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct hda_codec *codec = scodec->codec; 28862306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 28962306a36Sopenharmony_ci unsigned int i2c_reg_data; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (scodec->suspended) 29262306a36Sopenharmony_ci return -EPERM; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&spec->i2c_mux); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci cs8409_enable_i2c_clock(codec); 29762306a36Sopenharmony_ci cs8409_set_i2c_dev_addr(codec, scodec->addr); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (cs8409_i2c_set_page(scodec, addr)) 30062306a36Sopenharmony_ci goto error; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff); 30362306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (cs8409_i2c_wait_complete(codec) < 0) 30662306a36Sopenharmony_ci goto error; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cierror: 31262306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 31362306a36Sopenharmony_ci codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); 31462306a36Sopenharmony_ci return -EIO; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/** 31862306a36Sopenharmony_ci * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence. 31962306a36Sopenharmony_ci * @scodec: the codec instance 32062306a36Sopenharmony_ci * @seq: Register Sequence to write 32162306a36Sopenharmony_ci * @count: Number of registeres to write 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Returns negative on error. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_cistatic int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq, 32662306a36Sopenharmony_ci int count) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct hda_codec *codec = scodec->codec; 32962306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 33062306a36Sopenharmony_ci unsigned int i2c_reg_data; 33162306a36Sopenharmony_ci int i; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (scodec->suspended) 33462306a36Sopenharmony_ci return -EPERM; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci mutex_lock(&spec->i2c_mux); 33762306a36Sopenharmony_ci cs8409_set_i2c_dev_addr(codec, scodec->addr); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < count; i++) { 34062306a36Sopenharmony_ci cs8409_enable_i2c_clock(codec); 34162306a36Sopenharmony_ci if (cs8409_i2c_set_page(scodec, seq[i].addr)) 34262306a36Sopenharmony_ci goto error; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff); 34562306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (cs8409_i2c_wait_complete(codec) < 0) 34862306a36Sopenharmony_ci goto error; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cierror: 35662306a36Sopenharmony_ci mutex_unlock(&spec->i2c_mux); 35762306a36Sopenharmony_ci codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); 35862306a36Sopenharmony_ci return -EIO; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int cs8409_init(struct hda_codec *codec) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci int ret = snd_hda_gen_init(codec); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!ret) 36662306a36Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int cs8409_build_controls(struct hda_codec *codec) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci int err; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci err = snd_hda_gen_build_controls(codec); 37662306a36Sopenharmony_ci if (err < 0) 37762306a36Sopenharmony_ci return err; 37862306a36Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* Enable/Disable Unsolicited Response */ 38462306a36Sopenharmony_cistatic void cs8409_enable_ur(struct hda_codec *codec, int flag) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 38762306a36Sopenharmony_ci unsigned int ur_gpios = 0; 38862306a36Sopenharmony_ci int i; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) 39162306a36Sopenharmony_ci ur_gpios |= spec->scodecs[i]->irq_mask; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 39462306a36Sopenharmony_ci flag ? ur_gpios : 0); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE, 39762306a36Sopenharmony_ci flag ? AC_UNSOL_ENABLED : 0); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int caps; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* CS8409 is simple HDA bridge and intended to be used with a remote 40562306a36Sopenharmony_ci * companion codec. Most of input/output PIN(s) have only basic 40662306a36Sopenharmony_ci * capabilities. Receive and Transmit NID(s) have only OUTC and INC 40762306a36Sopenharmony_ci * capabilities and no presence detect capable (PDC) and call to 40862306a36Sopenharmony_ci * snd_hda_gen_build_controls() will mark them as non detectable 40962306a36Sopenharmony_ci * phantom jacks. However, a companion codec may be 41062306a36Sopenharmony_ci * connected to these pins which supports jack detect 41162306a36Sopenharmony_ci * capabilities. We have to override pin capabilities, 41262306a36Sopenharmony_ci * otherwise they will not be created as input devices. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); 41562306a36Sopenharmony_ci if (caps >= 0) 41662306a36Sopenharmony_ci snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, 41762306a36Sopenharmony_ci (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol, 42362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 42662306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio); 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol, 43362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 43662306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 43762306a36Sopenharmony_ci unsigned int gpio_data; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) | 44062306a36Sopenharmony_ci (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0); 44162306a36Sopenharmony_ci if (gpio_data == spec->gpio_data) 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci spec->gpio_data = gpio_data; 44462306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); 44562306a36Sopenharmony_ci return 1; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic const struct snd_kcontrol_new cs8409_spk_sw_ctrl = { 44962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 45062306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 45162306a36Sopenharmony_ci .get = cs8409_spk_sw_gpio_get, 45262306a36Sopenharmony_ci .put = cs8409_spk_sw_gpio_put, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/****************************************************************************** 45662306a36Sopenharmony_ci * CS42L42 Specific Functions 45762306a36Sopenharmony_ci ******************************************************************************/ 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciint cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci unsigned int ofs = get_amp_offset(kctrl); 46262306a36Sopenharmony_ci u8 chs = get_amp_channels(kctrl); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 46562306a36Sopenharmony_ci uinfo->value.integer.step = 1; 46662306a36Sopenharmony_ci uinfo->count = chs == 3 ? 2 : 1; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci switch (ofs) { 46962306a36Sopenharmony_ci case CS42L42_VOL_DAC: 47062306a36Sopenharmony_ci uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN; 47162306a36Sopenharmony_ci uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case CS42L42_VOL_ADC: 47462306a36Sopenharmony_ci uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN; 47562306a36Sopenharmony_ci uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX; 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci default: 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciint cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kctrl); 48762306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 48862306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; 48962306a36Sopenharmony_ci int chs = get_amp_channels(kctrl); 49062306a36Sopenharmony_ci unsigned int ofs = get_amp_offset(kctrl); 49162306a36Sopenharmony_ci long *valp = uctrl->value.integer.value; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci switch (ofs) { 49462306a36Sopenharmony_ci case CS42L42_VOL_DAC: 49562306a36Sopenharmony_ci if (chs & BIT(0)) 49662306a36Sopenharmony_ci *valp++ = cs42l42->vol[ofs]; 49762306a36Sopenharmony_ci if (chs & BIT(1)) 49862306a36Sopenharmony_ci *valp = cs42l42->vol[ofs+1]; 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci case CS42L42_VOL_ADC: 50162306a36Sopenharmony_ci if (chs & BIT(0)) 50262306a36Sopenharmony_ci *valp = cs42l42->vol[ofs]; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci default: 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void cs42l42_mute(struct sub_codec *cs42l42, int vol_type, 51262306a36Sopenharmony_ci unsigned int chs, bool mute) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci if (mute) { 51562306a36Sopenharmony_ci if (vol_type == CS42L42_VOL_DAC) { 51662306a36Sopenharmony_ci if (chs & BIT(0)) 51762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f); 51862306a36Sopenharmony_ci if (chs & BIT(1)) 51962306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f); 52062306a36Sopenharmony_ci } else if (vol_type == CS42L42_VOL_ADC) { 52162306a36Sopenharmony_ci if (chs & BIT(0)) 52262306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci if (vol_type == CS42L42_VOL_DAC) { 52662306a36Sopenharmony_ci if (chs & BIT(0)) 52762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 52862306a36Sopenharmony_ci -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET]) 52962306a36Sopenharmony_ci & CS42L42_MIXER_CH_VOL_MASK); 53062306a36Sopenharmony_ci if (chs & BIT(1)) 53162306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 53262306a36Sopenharmony_ci -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET]) 53362306a36Sopenharmony_ci & CS42L42_MIXER_CH_VOL_MASK); 53462306a36Sopenharmony_ci } else if (vol_type == CS42L42_VOL_ADC) { 53562306a36Sopenharmony_ci if (chs & BIT(0)) 53662306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 53762306a36Sopenharmony_ci cs42l42->vol[CS42L42_ADC_VOL_OFFSET] 53862306a36Sopenharmony_ci & CS42L42_REG_AMIC_VOL_MASK); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciint cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kctrl); 54662306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 54762306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; 54862306a36Sopenharmony_ci int chs = get_amp_channels(kctrl); 54962306a36Sopenharmony_ci unsigned int ofs = get_amp_offset(kctrl); 55062306a36Sopenharmony_ci long *valp = uctrl->value.integer.value; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci switch (ofs) { 55362306a36Sopenharmony_ci case CS42L42_VOL_DAC: 55462306a36Sopenharmony_ci if (chs & BIT(0)) 55562306a36Sopenharmony_ci cs42l42->vol[ofs] = *valp; 55662306a36Sopenharmony_ci if (chs & BIT(1)) { 55762306a36Sopenharmony_ci valp++; 55862306a36Sopenharmony_ci cs42l42->vol[ofs + 1] = *valp; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci if (spec->playback_started) 56162306a36Sopenharmony_ci cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci case CS42L42_VOL_ADC: 56462306a36Sopenharmony_ci if (chs & BIT(0)) 56562306a36Sopenharmony_ci cs42l42->vol[ofs] = *valp; 56662306a36Sopenharmony_ci if (spec->capture_started) 56762306a36Sopenharmony_ci cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false); 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci default: 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo, 57762306a36Sopenharmony_ci struct hda_codec *codec, 57862306a36Sopenharmony_ci struct snd_pcm_substream *substream, 57962306a36Sopenharmony_ci int action) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 58262306a36Sopenharmony_ci struct sub_codec *cs42l42; 58362306a36Sopenharmony_ci int i; 58462306a36Sopenharmony_ci bool mute; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci switch (action) { 58762306a36Sopenharmony_ci case HDA_GEN_PCM_ACT_PREPARE: 58862306a36Sopenharmony_ci mute = false; 58962306a36Sopenharmony_ci spec->playback_started = 1; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case HDA_GEN_PCM_ACT_CLEANUP: 59262306a36Sopenharmony_ci mute = true; 59362306a36Sopenharmony_ci spec->playback_started = 0; 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci default: 59662306a36Sopenharmony_ci return; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) { 60062306a36Sopenharmony_ci cs42l42 = spec->scodecs[i]; 60162306a36Sopenharmony_ci cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo, 60662306a36Sopenharmony_ci struct hda_codec *codec, 60762306a36Sopenharmony_ci struct snd_pcm_substream *substream, 60862306a36Sopenharmony_ci int action) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 61162306a36Sopenharmony_ci struct sub_codec *cs42l42; 61262306a36Sopenharmony_ci int i; 61362306a36Sopenharmony_ci bool mute; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci switch (action) { 61662306a36Sopenharmony_ci case HDA_GEN_PCM_ACT_PREPARE: 61762306a36Sopenharmony_ci mute = false; 61862306a36Sopenharmony_ci spec->capture_started = 1; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case HDA_GEN_PCM_ACT_CLEANUP: 62162306a36Sopenharmony_ci mute = true; 62262306a36Sopenharmony_ci spec->capture_started = 0; 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci default: 62562306a36Sopenharmony_ci return; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) { 62962306a36Sopenharmony_ci cs42l42 = spec->scodecs[i]; 63062306a36Sopenharmony_ci cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* Configure CS42L42 slave codec for jack autodetect */ 63562306a36Sopenharmony_cistatic void cs42l42_enable_jack_detect(struct sub_codec *cs42l42) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz); 63862306a36Sopenharmony_ci /* Clear WAKE# */ 63962306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1); 64062306a36Sopenharmony_ci /* Wait ~2.5ms */ 64162306a36Sopenharmony_ci usleep_range(2500, 3000); 64262306a36Sopenharmony_ci /* Set mode WAKE# output follows the combination logic directly */ 64362306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0); 64462306a36Sopenharmony_ci /* Clear interrupts status */ 64562306a36Sopenharmony_ci cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); 64662306a36Sopenharmony_ci /* Enable interrupt */ 64762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* Enable and run CS42L42 slave codec jack auto detect */ 65162306a36Sopenharmony_cistatic void cs42l42_run_jack_detect(struct sub_codec *cs42l42) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci /* Clear interrupts */ 65462306a36Sopenharmony_ci cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); 65562306a36Sopenharmony_ci cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1); 65662306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF); 65762306a36Sopenharmony_ci cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87); 66062306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86); 66162306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07); 66262306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD); 66362306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); 66462306a36Sopenharmony_ci /* Wait ~20ms*/ 66562306a36Sopenharmony_ci usleep_range(20000, 25000); 66662306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77); 66762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int cs42l42_manual_hs_det(struct sub_codec *cs42l42) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci unsigned int hs_det_status; 67362306a36Sopenharmony_ci unsigned int hs_det_comp1; 67462306a36Sopenharmony_ci unsigned int hs_det_comp2; 67562306a36Sopenharmony_ci unsigned int hs_det_sw; 67662306a36Sopenharmony_ci unsigned int hs_type; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Set hs detect to manual, active mode */ 67962306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 68062306a36Sopenharmony_ci (1 << CS42L42_HSDET_CTRL_SHIFT) | 68162306a36Sopenharmony_ci (0 << CS42L42_HSDET_SET_SHIFT) | 68262306a36Sopenharmony_ci (0 << CS42L42_HSBIAS_REF_SHIFT) | 68362306a36Sopenharmony_ci (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* Configure HS DET comparator reference levels. */ 68662306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 68762306a36Sopenharmony_ci (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | 68862306a36Sopenharmony_ci (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ 69162306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci msleep(100); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> 69862306a36Sopenharmony_ci CS42L42_HSDET_COMP1_OUT_SHIFT; 69962306a36Sopenharmony_ci hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> 70062306a36Sopenharmony_ci CS42L42_HSDET_COMP2_OUT_SHIFT; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ 70362306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci msleep(100); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> 71062306a36Sopenharmony_ci CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; 71162306a36Sopenharmony_ci hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> 71262306a36Sopenharmony_ci CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* Use Comparator 1 with 1.25V Threshold. */ 71562306a36Sopenharmony_ci switch (hs_det_comp1) { 71662306a36Sopenharmony_ci case CS42L42_HSDET_COMP_TYPE1: 71762306a36Sopenharmony_ci hs_type = CS42L42_PLUG_CTIA; 71862306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE1; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci case CS42L42_HSDET_COMP_TYPE2: 72162306a36Sopenharmony_ci hs_type = CS42L42_PLUG_OMTP; 72262306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE2; 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci default: 72562306a36Sopenharmony_ci /* Fallback to Comparator 2 with 1.75V Threshold. */ 72662306a36Sopenharmony_ci switch (hs_det_comp2) { 72762306a36Sopenharmony_ci case CS42L42_HSDET_COMP_TYPE1: 72862306a36Sopenharmony_ci hs_type = CS42L42_PLUG_CTIA; 72962306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE1; 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci case CS42L42_HSDET_COMP_TYPE2: 73262306a36Sopenharmony_ci hs_type = CS42L42_PLUG_OMTP; 73362306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE2; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci case CS42L42_HSDET_COMP_TYPE3: 73662306a36Sopenharmony_ci hs_type = CS42L42_PLUG_HEADPHONE; 73762306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE3; 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci default: 74062306a36Sopenharmony_ci hs_type = CS42L42_PLUG_INVALID; 74162306a36Sopenharmony_ci hs_det_sw = CS42L42_HSDET_SW_TYPE4; 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Set Switches */ 74762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Set HSDET mode to Manual—Disabled */ 75062306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 75162306a36Sopenharmony_ci (0 << CS42L42_HSDET_CTRL_SHIFT) | 75262306a36Sopenharmony_ci (0 << CS42L42_HSDET_SET_SHIFT) | 75362306a36Sopenharmony_ci (0 << CS42L42_HSBIAS_REF_SHIFT) | 75462306a36Sopenharmony_ci (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Configure HS DET comparator reference levels. */ 75762306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 75862306a36Sopenharmony_ci (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | 75962306a36Sopenharmony_ci (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return hs_type; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci int status_changed = 0; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* TIP_SENSE INSERT/REMOVE */ 76962306a36Sopenharmony_ci switch (reg_ts_status) { 77062306a36Sopenharmony_ci case CS42L42_TS_PLUG: 77162306a36Sopenharmony_ci if (cs42l42->no_type_dect) { 77262306a36Sopenharmony_ci status_changed = 1; 77362306a36Sopenharmony_ci cs42l42->hp_jack_in = 1; 77462306a36Sopenharmony_ci cs42l42->mic_jack_in = 0; 77562306a36Sopenharmony_ci } else { 77662306a36Sopenharmony_ci cs42l42_run_jack_detect(cs42l42); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci case CS42L42_TS_UNPLUG: 78162306a36Sopenharmony_ci status_changed = 1; 78262306a36Sopenharmony_ci cs42l42->hp_jack_in = 0; 78362306a36Sopenharmony_ci cs42l42->mic_jack_in = 0; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci default: 78662306a36Sopenharmony_ci /* jack in transition */ 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return status_changed; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int cs42l42_jack_unsol_event(struct sub_codec *cs42l42) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci int current_plug_status; 79862306a36Sopenharmony_ci int status_changed = 0; 79962306a36Sopenharmony_ci int reg_cdc_status; 80062306a36Sopenharmony_ci int reg_hs_status; 80162306a36Sopenharmony_ci int reg_ts_status; 80262306a36Sopenharmony_ci int type; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Read jack detect status registers */ 80562306a36Sopenharmony_ci reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); 80662306a36Sopenharmony_ci reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); 80762306a36Sopenharmony_ci reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* If status values are < 0, read error has occurred. */ 81062306a36Sopenharmony_ci if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0) 81162306a36Sopenharmony_ci return -EIO; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) 81462306a36Sopenharmony_ci >> CS42L42_TS_PLUG_SHIFT; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* HSDET_AUTO_DONE */ 81762306a36Sopenharmony_ci if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) { 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* Disable HSDET_AUTO_DONE */ 82062306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* Configure the HSDET mode. */ 82562306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (cs42l42->no_type_dect) { 82862306a36Sopenharmony_ci status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); 82962306a36Sopenharmony_ci } else { 83062306a36Sopenharmony_ci if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) { 83162306a36Sopenharmony_ci codec_dbg(cs42l42->codec, 83262306a36Sopenharmony_ci "Auto detect value not valid (%d), running manual det\n", 83362306a36Sopenharmony_ci type); 83462306a36Sopenharmony_ci type = cs42l42_manual_hs_det(cs42l42); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci switch (type) { 83862306a36Sopenharmony_ci case CS42L42_PLUG_CTIA: 83962306a36Sopenharmony_ci case CS42L42_PLUG_OMTP: 84062306a36Sopenharmony_ci status_changed = 1; 84162306a36Sopenharmony_ci cs42l42->hp_jack_in = 1; 84262306a36Sopenharmony_ci cs42l42->mic_jack_in = 1; 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci case CS42L42_PLUG_HEADPHONE: 84562306a36Sopenharmony_ci status_changed = 1; 84662306a36Sopenharmony_ci cs42l42->hp_jack_in = 1; 84762306a36Sopenharmony_ci cs42l42->mic_jack_in = 0; 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci default: 85062306a36Sopenharmony_ci status_changed = 1; 85162306a36Sopenharmony_ci cs42l42->hp_jack_in = 0; 85262306a36Sopenharmony_ci cs42l42->mic_jack_in = 0; 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci codec_dbg(cs42l42->codec, "Detection done (%d)\n", type); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Enable the HPOUT ground clamp and configure the HP pull-down */ 85962306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02); 86062306a36Sopenharmony_ci /* Re-Enable Tip Sense Interrupt */ 86162306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); 86262306a36Sopenharmony_ci } else { 86362306a36Sopenharmony_ci status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return status_changed; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void cs42l42_resume(struct sub_codec *cs42l42) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct hda_codec *codec = cs42l42->codec; 87262306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 87362306a36Sopenharmony_ci struct cs8409_i2c_param irq_regs[] = { 87462306a36Sopenharmony_ci { CS42L42_CODEC_STATUS, 0x00 }, 87562306a36Sopenharmony_ci { CS42L42_DET_INT_STATUS1, 0x00 }, 87662306a36Sopenharmony_ci { CS42L42_DET_INT_STATUS2, 0x00 }, 87762306a36Sopenharmony_ci { CS42L42_TSRS_PLUG_STATUS, 0x00 }, 87862306a36Sopenharmony_ci }; 87962306a36Sopenharmony_ci int fsv_old, fsv_new; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Bring CS42L42 out of Reset */ 88262306a36Sopenharmony_ci spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); 88362306a36Sopenharmony_ci spec->gpio_data |= cs42l42->reset_gpio; 88462306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); 88562306a36Sopenharmony_ci usleep_range(10000, 15000); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci cs42l42->suspended = 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* Initialize CS42L42 companion codec */ 89062306a36Sopenharmony_ci cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); 89162306a36Sopenharmony_ci msleep(CS42L42_INIT_TIMEOUT_MS); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* Clear interrupts, by reading interrupt status registers */ 89462306a36Sopenharmony_ci cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); 89762306a36Sopenharmony_ci if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB) 89862306a36Sopenharmony_ci fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK; 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK; 90162306a36Sopenharmony_ci if (fsv_new != fsv_old) 90262306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* we have to explicitly allow unsol event handling even during the 90562306a36Sopenharmony_ci * resume phase so that the jack event is processed properly 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci snd_hda_codec_allow_unsol_events(cs42l42->codec); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci cs42l42_enable_jack_detect(cs42l42); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci#ifdef CONFIG_PM 91362306a36Sopenharmony_cistatic void cs42l42_suspend(struct sub_codec *cs42l42) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct hda_codec *codec = cs42l42->codec; 91662306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 91762306a36Sopenharmony_ci int reg_cdc_status = 0; 91862306a36Sopenharmony_ci const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = { 91962306a36Sopenharmony_ci { CS42L42_DAC_CTL2, 0x02 }, 92062306a36Sopenharmony_ci { CS42L42_HS_CLAMP_DISABLE, 0x00 }, 92162306a36Sopenharmony_ci { CS42L42_MIXER_CHA_VOL, 0x3F }, 92262306a36Sopenharmony_ci { CS42L42_MIXER_ADC_VOL, 0x3F }, 92362306a36Sopenharmony_ci { CS42L42_MIXER_CHB_VOL, 0x3F }, 92462306a36Sopenharmony_ci { CS42L42_HP_CTL, 0x0F }, 92562306a36Sopenharmony_ci { CS42L42_ASP_RX_DAI0_EN, 0x00 }, 92662306a36Sopenharmony_ci { CS42L42_ASP_CLK_CFG, 0x00 }, 92762306a36Sopenharmony_ci { CS42L42_PWR_CTL1, 0xFE }, 92862306a36Sopenharmony_ci { CS42L42_PWR_CTL2, 0x8C }, 92962306a36Sopenharmony_ci { CS42L42_PWR_CTL1, 0xFF }, 93062306a36Sopenharmony_ci }; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq)); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status, 93562306a36Sopenharmony_ci (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US, 93662306a36Sopenharmony_ci true, cs42l42, CS42L42_CODEC_STATUS) < 0) 93762306a36Sopenharmony_ci codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n"); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Power down CS42L42 ASP/EQ/MIX/HP */ 94062306a36Sopenharmony_ci cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C); 94162306a36Sopenharmony_ci cs42l42->suspended = 1; 94262306a36Sopenharmony_ci cs42l42->last_page = 0; 94362306a36Sopenharmony_ci cs42l42->hp_jack_in = 0; 94462306a36Sopenharmony_ci cs42l42->mic_jack_in = 0; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Put CS42L42 into Reset */ 94762306a36Sopenharmony_ci spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); 94862306a36Sopenharmony_ci spec->gpio_data &= ~cs42l42->reset_gpio; 94962306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci#endif 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void cs8409_free(struct hda_codec *codec) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Cancel i2c clock disable timer, and disable clock if left enabled */ 95862306a36Sopenharmony_ci cancel_delayed_work_sync(&spec->i2c_clk_work); 95962306a36Sopenharmony_ci cs8409_disable_i2c_clock(codec); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci snd_hda_gen_free(codec); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/****************************************************************************** 96562306a36Sopenharmony_ci * BULLSEYE / WARLOCK / CYBORG Specific Functions 96662306a36Sopenharmony_ci * CS8409/CS42L42 96762306a36Sopenharmony_ci ******************************************************************************/ 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci/* 97062306a36Sopenharmony_ci * In the case of CS8409 we do not have unsolicited events from NID's 0x24 97162306a36Sopenharmony_ci * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will 97262306a36Sopenharmony_ci * generate interrupt via gpio 4 to notify jack events. We have to overwrite 97362306a36Sopenharmony_ci * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers 97462306a36Sopenharmony_ci * and then notify status via generic snd_hda_jack_unsol_event() call. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_cistatic void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 97962306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; 98062306a36Sopenharmony_ci struct hda_jack_tbl *jk; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* jack_unsol_event() will be called every time gpio line changing state. 98362306a36Sopenharmony_ci * In this case gpio4 line goes up as a result of reading interrupt status 98462306a36Sopenharmony_ci * registers in previous cs8409_jack_unsol_event() call. 98562306a36Sopenharmony_ci * We don't need to handle this event, ignoring... 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci if (res & cs42l42->irq_mask) 98862306a36Sopenharmony_ci return; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (cs42l42_jack_unsol_event(cs42l42)) { 99162306a36Sopenharmony_ci snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, 99262306a36Sopenharmony_ci cs42l42->hp_jack_in ? 0 : PIN_OUT); 99362306a36Sopenharmony_ci /* Report jack*/ 99462306a36Sopenharmony_ci jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); 99562306a36Sopenharmony_ci if (jk) 99662306a36Sopenharmony_ci snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & 99762306a36Sopenharmony_ci AC_UNSOL_RES_TAG); 99862306a36Sopenharmony_ci /* Report jack*/ 99962306a36Sopenharmony_ci jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); 100062306a36Sopenharmony_ci if (jk) 100162306a36Sopenharmony_ci snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & 100262306a36Sopenharmony_ci AC_UNSOL_RES_TAG); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci#ifdef CONFIG_PM 100762306a36Sopenharmony_ci/* Manage PDREF, when transition to D3hot */ 100862306a36Sopenharmony_cistatic int cs8409_cs42l42_suspend(struct hda_codec *codec) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 101162306a36Sopenharmony_ci int i; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci spec->init_done = 0; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci cs8409_enable_ur(codec, 0); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) 101862306a36Sopenharmony_ci cs42l42_suspend(spec->scodecs[i]); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Cancel i2c clock disable timer, and disable clock if left enabled */ 102162306a36Sopenharmony_ci cancel_delayed_work_sync(&spec->i2c_clk_work); 102262306a36Sopenharmony_ci cs8409_disable_i2c_clock(codec); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci snd_hda_shutup_pins(codec); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci#endif 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci/* Vendor specific HW configuration 103162306a36Sopenharmony_ci * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_cistatic void cs8409_cs42l42_hw_init(struct hda_codec *codec) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg; 103662306a36Sopenharmony_ci const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn; 103762306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 103862306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (spec->gpio_mask) { 104162306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, 104262306a36Sopenharmony_ci spec->gpio_mask); 104362306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, 104462306a36Sopenharmony_ci spec->gpio_dir); 104562306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 104662306a36Sopenharmony_ci spec->gpio_data); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci for (; seq->nid; seq++) 105062306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (codec->fixup_id == CS8409_BULLSEYE) { 105362306a36Sopenharmony_ci for (; seq_bullseye->nid; seq_bullseye++) 105462306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci switch (codec->fixup_id) { 105862306a36Sopenharmony_ci case CS8409_CYBORG: 105962306a36Sopenharmony_ci case CS8409_WARLOCK_MLK_DUAL_MIC: 106062306a36Sopenharmony_ci /* DMIC1_MO=00b, DMIC1/2_SR=1 */ 106162306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003); 106262306a36Sopenharmony_ci break; 106362306a36Sopenharmony_ci case CS8409_ODIN: 106462306a36Sopenharmony_ci /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */ 106562306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00); 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci default: 106862306a36Sopenharmony_ci break; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci cs42l42_resume(cs42l42); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Enable Unsolicited Response */ 107462306a36Sopenharmony_ci cs8409_enable_ur(codec, 1); 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic const struct hda_codec_ops cs8409_cs42l42_patch_ops = { 107862306a36Sopenharmony_ci .build_controls = cs8409_build_controls, 107962306a36Sopenharmony_ci .build_pcms = snd_hda_gen_build_pcms, 108062306a36Sopenharmony_ci .init = cs8409_init, 108162306a36Sopenharmony_ci .free = cs8409_free, 108262306a36Sopenharmony_ci .unsol_event = cs8409_cs42l42_jack_unsol_event, 108362306a36Sopenharmony_ci#ifdef CONFIG_PM 108462306a36Sopenharmony_ci .suspend = cs8409_cs42l42_suspend, 108562306a36Sopenharmony_ci#endif 108662306a36Sopenharmony_ci}; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, 108962306a36Sopenharmony_ci unsigned int *res) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 109262306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 109362306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci unsigned int nid = ((cmd >> 20) & 0x07f); 109662306a36Sopenharmony_ci unsigned int verb = ((cmd >> 8) & 0x0fff); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* CS8409 pins have no AC_PINSENSE_PRESENCE 109962306a36Sopenharmony_ci * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 110062306a36Sopenharmony_ci * and return correct pin sense values for read_pin_sense() call from 110162306a36Sopenharmony_ci * hda_jack based on CS42L42 jack detect status. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci switch (nid) { 110462306a36Sopenharmony_ci case CS8409_CS42L42_HP_PIN_NID: 110562306a36Sopenharmony_ci if (verb == AC_VERB_GET_PIN_SENSE) { 110662306a36Sopenharmony_ci *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci break; 111062306a36Sopenharmony_ci case CS8409_CS42L42_AMIC_PIN_NID: 111162306a36Sopenharmony_ci if (verb == AC_VERB_GET_PIN_SENSE) { 111262306a36Sopenharmony_ci *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; 111362306a36Sopenharmony_ci return 0; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci break; 111662306a36Sopenharmony_ci default: 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci return spec->exec_verb(dev, cmd, flags, res); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_civoid cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci switch (action) { 112862306a36Sopenharmony_ci case HDA_FIXUP_ACT_PRE_PROBE: 112962306a36Sopenharmony_ci snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs); 113062306a36Sopenharmony_ci /* verb exec op override */ 113162306a36Sopenharmony_ci spec->exec_verb = codec->core.exec_verb; 113262306a36Sopenharmony_ci codec->core.exec_verb = cs8409_cs42l42_exec_verb; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; 113562306a36Sopenharmony_ci spec->num_scodecs = 1; 113662306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->codec = codec; 113762306a36Sopenharmony_ci codec->patch_ops = cs8409_cs42l42_patch_ops; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci spec->gen.suppress_auto_mute = 1; 114062306a36Sopenharmony_ci spec->gen.no_primary_hp = 1; 114162306a36Sopenharmony_ci spec->gen.suppress_vmaster = 1; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci spec->speaker_pdn_gpio = 0; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* GPIO 5 out, 3,4 in */ 114662306a36Sopenharmony_ci spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; 114762306a36Sopenharmony_ci spec->gpio_data = 0; 114862306a36Sopenharmony_ci spec->gpio_mask = 0x03f; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* Basic initial sequence for specific hw configuration */ 115162306a36Sopenharmony_ci snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID); 115462306a36Sopenharmony_ci cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci switch (codec->fixup_id) { 115962306a36Sopenharmony_ci case CS8409_CYBORG: 116062306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->full_scale_vol = 116162306a36Sopenharmony_ci CS42L42_FULL_SCALE_VOL_MINUS6DB; 116262306a36Sopenharmony_ci spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci case CS8409_ODIN: 116562306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; 116662306a36Sopenharmony_ci spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; 116762306a36Sopenharmony_ci break; 116862306a36Sopenharmony_ci case CS8409_WARLOCK_MLK: 116962306a36Sopenharmony_ci case CS8409_WARLOCK_MLK_DUAL_MIC: 117062306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; 117162306a36Sopenharmony_ci spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; 117262306a36Sopenharmony_ci break; 117362306a36Sopenharmony_ci default: 117462306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->full_scale_vol = 117562306a36Sopenharmony_ci CS42L42_FULL_SCALE_VOL_MINUS6DB; 117662306a36Sopenharmony_ci spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (spec->speaker_pdn_gpio > 0) { 118162306a36Sopenharmony_ci spec->gpio_dir |= spec->speaker_pdn_gpio; 118262306a36Sopenharmony_ci spec->gpio_data |= spec->speaker_pdn_gpio; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci case HDA_FIXUP_ACT_PROBE: 118762306a36Sopenharmony_ci /* Fix Sample Rate to 48kHz */ 118862306a36Sopenharmony_ci spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; 118962306a36Sopenharmony_ci spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; 119062306a36Sopenharmony_ci /* add hooks */ 119162306a36Sopenharmony_ci spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; 119262306a36Sopenharmony_ci spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; 119362306a36Sopenharmony_ci if (codec->fixup_id != CS8409_ODIN) 119462306a36Sopenharmony_ci /* Set initial DMIC volume to -26 dB */ 119562306a36Sopenharmony_ci snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, 119662306a36Sopenharmony_ci HDA_INPUT, 0, 0xff, 0x19); 119762306a36Sopenharmony_ci snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", 119862306a36Sopenharmony_ci &cs42l42_dac_volume_mixer); 119962306a36Sopenharmony_ci snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", 120062306a36Sopenharmony_ci &cs42l42_adc_volume_mixer); 120162306a36Sopenharmony_ci if (spec->speaker_pdn_gpio > 0) 120262306a36Sopenharmony_ci snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", 120362306a36Sopenharmony_ci &cs8409_spk_sw_ctrl); 120462306a36Sopenharmony_ci /* Disable Unsolicited Response during boot */ 120562306a36Sopenharmony_ci cs8409_enable_ur(codec, 0); 120662306a36Sopenharmony_ci snd_hda_codec_set_name(codec, "CS8409/CS42L42"); 120762306a36Sopenharmony_ci break; 120862306a36Sopenharmony_ci case HDA_FIXUP_ACT_INIT: 120962306a36Sopenharmony_ci cs8409_cs42l42_hw_init(codec); 121062306a36Sopenharmony_ci spec->init_done = 1; 121162306a36Sopenharmony_ci if (spec->init_done && spec->build_ctrl_done 121262306a36Sopenharmony_ci && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) 121362306a36Sopenharmony_ci cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci case HDA_FIXUP_ACT_BUILD: 121662306a36Sopenharmony_ci spec->build_ctrl_done = 1; 121762306a36Sopenharmony_ci /* Run jack auto detect first time on boot 121862306a36Sopenharmony_ci * after controls have been added, to check if jack has 121962306a36Sopenharmony_ci * been already plugged in. 122062306a36Sopenharmony_ci * Run immediately after init. 122162306a36Sopenharmony_ci */ 122262306a36Sopenharmony_ci if (spec->init_done && spec->build_ctrl_done 122362306a36Sopenharmony_ci && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) 122462306a36Sopenharmony_ci cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci default: 122762306a36Sopenharmony_ci break; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/****************************************************************************** 123262306a36Sopenharmony_ci * Dolphin Specific Functions 123362306a36Sopenharmony_ci * CS8409/ 2 X CS42L42 123462306a36Sopenharmony_ci ******************************************************************************/ 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* 123762306a36Sopenharmony_ci * In the case of CS8409 we do not have unsolicited events when 123862306a36Sopenharmony_ci * hs mic and hp are connected. Companion codec CS42L42 will 123962306a36Sopenharmony_ci * generate interrupt via irq_mask to notify jack events. We have to overwrite 124062306a36Sopenharmony_ci * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers 124162306a36Sopenharmony_ci * and then notify status via generic snd_hda_jack_unsol_event() call. 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_cistatic void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 124662306a36Sopenharmony_ci struct sub_codec *cs42l42; 124762306a36Sopenharmony_ci struct hda_jack_tbl *jk; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci cs42l42 = spec->scodecs[CS8409_CODEC0]; 125062306a36Sopenharmony_ci if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && 125162306a36Sopenharmony_ci cs42l42_jack_unsol_event(cs42l42)) { 125262306a36Sopenharmony_ci jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0); 125362306a36Sopenharmony_ci if (jk) 125462306a36Sopenharmony_ci snd_hda_jack_unsol_event(codec, 125562306a36Sopenharmony_ci (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & 125662306a36Sopenharmony_ci AC_UNSOL_RES_TAG); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0); 125962306a36Sopenharmony_ci if (jk) 126062306a36Sopenharmony_ci snd_hda_jack_unsol_event(codec, 126162306a36Sopenharmony_ci (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & 126262306a36Sopenharmony_ci AC_UNSOL_RES_TAG); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci cs42l42 = spec->scodecs[CS8409_CODEC1]; 126662306a36Sopenharmony_ci if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && 126762306a36Sopenharmony_ci cs42l42_jack_unsol_event(cs42l42)) { 126862306a36Sopenharmony_ci jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0); 126962306a36Sopenharmony_ci if (jk) 127062306a36Sopenharmony_ci snd_hda_jack_unsol_event(codec, 127162306a36Sopenharmony_ci (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & 127262306a36Sopenharmony_ci AC_UNSOL_RES_TAG); 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci/* Vendor specific HW configuration 127762306a36Sopenharmony_ci * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_cistatic void dolphin_hw_init(struct hda_codec *codec) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci const struct cs8409_cir_param *seq = dolphin_hw_cfg; 128262306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 128362306a36Sopenharmony_ci struct sub_codec *cs42l42; 128462306a36Sopenharmony_ci int i; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (spec->gpio_mask) { 128762306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, 128862306a36Sopenharmony_ci spec->gpio_mask); 128962306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, 129062306a36Sopenharmony_ci spec->gpio_dir); 129162306a36Sopenharmony_ci snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 129262306a36Sopenharmony_ci spec->gpio_data); 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci for (; seq->nid; seq++) 129662306a36Sopenharmony_ci cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) { 129962306a36Sopenharmony_ci cs42l42 = spec->scodecs[i]; 130062306a36Sopenharmony_ci cs42l42_resume(cs42l42); 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* Enable Unsolicited Response */ 130462306a36Sopenharmony_ci cs8409_enable_ur(codec, 1); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic const struct hda_codec_ops cs8409_dolphin_patch_ops = { 130862306a36Sopenharmony_ci .build_controls = cs8409_build_controls, 130962306a36Sopenharmony_ci .build_pcms = snd_hda_gen_build_pcms, 131062306a36Sopenharmony_ci .init = cs8409_init, 131162306a36Sopenharmony_ci .free = cs8409_free, 131262306a36Sopenharmony_ci .unsol_event = dolphin_jack_unsol_event, 131362306a36Sopenharmony_ci#ifdef CONFIG_PM 131462306a36Sopenharmony_ci .suspend = cs8409_cs42l42_suspend, 131562306a36Sopenharmony_ci#endif 131662306a36Sopenharmony_ci}; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, 131962306a36Sopenharmony_ci unsigned int *res) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 132262306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 132362306a36Sopenharmony_ci struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci unsigned int nid = ((cmd >> 20) & 0x07f); 132662306a36Sopenharmony_ci unsigned int verb = ((cmd >> 8) & 0x0fff); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* CS8409 pins have no AC_PINSENSE_PRESENCE 132962306a36Sopenharmony_ci * capabilities. We have to intercept calls for CS42L42 pins 133062306a36Sopenharmony_ci * and return correct pin sense values for read_pin_sense() call from 133162306a36Sopenharmony_ci * hda_jack based on CS42L42 jack detect status. 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci switch (nid) { 133462306a36Sopenharmony_ci case DOLPHIN_HP_PIN_NID: 133562306a36Sopenharmony_ci case DOLPHIN_LO_PIN_NID: 133662306a36Sopenharmony_ci if (nid == DOLPHIN_LO_PIN_NID) 133762306a36Sopenharmony_ci cs42l42 = spec->scodecs[CS8409_CODEC1]; 133862306a36Sopenharmony_ci if (verb == AC_VERB_GET_PIN_SENSE) { 133962306a36Sopenharmony_ci *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; 134062306a36Sopenharmony_ci return 0; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci break; 134362306a36Sopenharmony_ci case DOLPHIN_AMIC_PIN_NID: 134462306a36Sopenharmony_ci if (verb == AC_VERB_GET_PIN_SENSE) { 134562306a36Sopenharmony_ci *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; 134662306a36Sopenharmony_ci return 0; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci default: 135062306a36Sopenharmony_ci break; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return spec->exec_verb(dev, cmd, flags, res); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_civoid dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct cs8409_spec *spec = codec->spec; 135962306a36Sopenharmony_ci struct snd_kcontrol_new *kctrl; 136062306a36Sopenharmony_ci int i; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci switch (action) { 136362306a36Sopenharmony_ci case HDA_FIXUP_ACT_PRE_PROBE: 136462306a36Sopenharmony_ci snd_hda_add_verbs(codec, dolphin_init_verbs); 136562306a36Sopenharmony_ci /* verb exec op override */ 136662306a36Sopenharmony_ci spec->exec_verb = codec->core.exec_verb; 136762306a36Sopenharmony_ci codec->core.exec_verb = dolphin_exec_verb; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0; 137062306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->codec = codec; 137162306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1; 137262306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC1]->codec = codec; 137362306a36Sopenharmony_ci spec->num_scodecs = 2; 137462306a36Sopenharmony_ci spec->gen.suppress_vmaster = 1; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci codec->patch_ops = cs8409_dolphin_patch_ops; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* GPIO 1,5 out, 0,4 in */ 137962306a36Sopenharmony_ci spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | 138062306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC1]->reset_gpio; 138162306a36Sopenharmony_ci spec->gpio_data = 0; 138262306a36Sopenharmony_ci spec->gpio_mask = 0x03f; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* Basic initial sequence for specific hw configuration */ 138562306a36Sopenharmony_ci snd_hda_sequence_write(codec, dolphin_init_verbs); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true, 138862306a36Sopenharmony_ci SND_JACK_HEADPHONE, NULL); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true, 139162306a36Sopenharmony_ci SND_JACK_MICROPHONE, NULL); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID); 139462306a36Sopenharmony_ci cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID); 139562306a36Sopenharmony_ci cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; 139862306a36Sopenharmony_ci spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci case HDA_FIXUP_ACT_PROBE: 140262306a36Sopenharmony_ci /* Fix Sample Rate to 48kHz */ 140362306a36Sopenharmony_ci spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; 140462306a36Sopenharmony_ci spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; 140562306a36Sopenharmony_ci /* add hooks */ 140662306a36Sopenharmony_ci spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; 140762306a36Sopenharmony_ci spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; 140862306a36Sopenharmony_ci snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", 140962306a36Sopenharmony_ci &cs42l42_dac_volume_mixer); 141062306a36Sopenharmony_ci snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer); 141162306a36Sopenharmony_ci kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume", 141262306a36Sopenharmony_ci &cs42l42_dac_volume_mixer); 141362306a36Sopenharmony_ci /* Update Line Out kcontrol template */ 141462306a36Sopenharmony_ci kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, 141562306a36Sopenharmony_ci HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; 141662306a36Sopenharmony_ci cs8409_enable_ur(codec, 0); 141762306a36Sopenharmony_ci snd_hda_codec_set_name(codec, "CS8409/CS42L42"); 141862306a36Sopenharmony_ci break; 141962306a36Sopenharmony_ci case HDA_FIXUP_ACT_INIT: 142062306a36Sopenharmony_ci dolphin_hw_init(codec); 142162306a36Sopenharmony_ci spec->init_done = 1; 142262306a36Sopenharmony_ci if (spec->init_done && spec->build_ctrl_done) { 142362306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) { 142462306a36Sopenharmony_ci if (!spec->scodecs[i]->hp_jack_in) 142562306a36Sopenharmony_ci cs42l42_run_jack_detect(spec->scodecs[i]); 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci case HDA_FIXUP_ACT_BUILD: 143062306a36Sopenharmony_ci spec->build_ctrl_done = 1; 143162306a36Sopenharmony_ci /* Run jack auto detect first time on boot 143262306a36Sopenharmony_ci * after controls have been added, to check if jack has 143362306a36Sopenharmony_ci * been already plugged in. 143462306a36Sopenharmony_ci * Run immediately after init. 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ci if (spec->init_done && spec->build_ctrl_done) { 143762306a36Sopenharmony_ci for (i = 0; i < spec->num_scodecs; i++) { 143862306a36Sopenharmony_ci if (!spec->scodecs[i]->hp_jack_in) 143962306a36Sopenharmony_ci cs42l42_run_jack_detect(spec->scodecs[i]); 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci default: 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic int patch_cs8409(struct hda_codec *codec) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci int err; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!cs8409_alloc_spec(codec)) 145362306a36Sopenharmony_ci return -ENOMEM; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, 145862306a36Sopenharmony_ci codec->bus->pci->subsystem_vendor, 145962306a36Sopenharmony_ci codec->bus->pci->subsystem_device); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci err = cs8409_parse_auto_config(codec); 146462306a36Sopenharmony_ci if (err < 0) { 146562306a36Sopenharmony_ci cs8409_free(codec); 146662306a36Sopenharmony_ci return err; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 147062306a36Sopenharmony_ci return 0; 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic const struct hda_device_id snd_hda_id_cs8409[] = { 147462306a36Sopenharmony_ci HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), 147562306a36Sopenharmony_ci {} /* terminator */ 147662306a36Sopenharmony_ci}; 147762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic struct hda_codec_driver cs8409_driver = { 148062306a36Sopenharmony_ci .id = snd_hda_id_cs8409, 148162306a36Sopenharmony_ci}; 148262306a36Sopenharmony_cimodule_hda_codec_driver(cs8409_driver); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 148562306a36Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic HDA bridge"); 1486