/*++ * linux/sound/soc/codecs/vt1603.c * WonderMedia audio driver for ALSA * * Copyright c 2010 WonderMedia Technologies, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * WonderMedia Technologies, Inc. * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vt1603.h" #include #define VT1603_TIMER_INTERVAL 100 // ms static struct snd_soc_codec *gvt1603_codec = NULL; // struct vt1603_priv { unsigned int sysclk; struct switch_dev hp_switch; // state of headphone insert or not struct work_struct work; struct snd_soc_codec *codec; struct timer_list timer; // timer for detecting headphone struct proc_dir_entry *proc; struct notifier_block reboot_notifier; }; struct vt1603_boardinfo { int hp_level; // 0: headset level low; 1: high int ignore_hp_event; int rec_src; // 0: AMIC-IN; 1: DMIC-IN int hpd_rm; //add 2013-11-26 int hp_debounce; //add 2014-1-14 int timer_del; //2014-1-20 tvbox int out_on; //2014-4-29 av out on for tvbox noisy. hw not good! }; static struct vt1603_boardinfo vt1603_boardinfo; extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); static const u16 vt1603_regs[] = { 0x0020, 0x00D7, 0x00D7, 0x0004, /* 0-3 */ 0x0000, 0x0040, 0x0000, 0x00c0, /* 4-7 */ 0x00c0, 0x0010, 0x0001, 0x0041, /* 8-11 */ 0x0000, 0x0000, 0x000b, 0x0093, /* 12-15 */ 0x0004, 0x0016, 0x0026, 0x0060, /* 16-19 */ 0x001a, 0x0000, 0x0002, 0x0000, /* 20-23 */ 0x0000, 0x000a, 0x0000, 0x0010, /* 24-27 */ 0x0000, 0x00e2, 0x0000, 0x0000, /* 28-31 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 32-35 */ 0x0000, 0x000c, 0x0054, 0x0000, /* 36-39 */ 0x0001, 0x000c, 0x000c, 0x000c, /* 40-43 */ 0x000c, 0x000c, 0x00c0, 0x00d5, /* 44-47 */ 0x00c5, 0x0012, 0x0085, 0x002b, /* 48-51 */ 0x00cd, 0x00cd, 0x008e, 0x008d, /* 52-55 */ 0x00e0, 0x00a6, 0x00a5, 0x0050, /* 56-59 */ 0x00e9, 0x00cf, 0x0040, 0x0000, /* 60-63 */ 0x0000, 0x0008, 0x0004, 0x0000, /* 64-67 */ 0x0040, 0x0000, 0x0040, 0x0000, /* 68-71 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 72-75 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 76-79 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 80-83 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 84-87 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 88-91 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 92-95 */ 0x00c4, 0x0079, 0x000c, 0x0024, /* 96-99 */ 0x0016, 0x0016, 0x0060, 0x0002, /* 100-103 */ 0x005b, 0x0003, 0x0039, 0x0039, /* 104-107 */ 0x00fe, 0x0012, 0x0034, 0x0000, /* 108-111 */ 0x0004, 0x00f0, 0x0000, 0x0000, /* 112-115 */ 0x0000, 0x0000, 0x0003, 0x0000, /* 116-119 */ 0x0000, 0x0000, 0x0000, 0x0000, /* 120-123 */ 0x00f2, 0x0020, 0x0023, 0x006e, /* 124-127 */ 0x0019, 0x0040, 0x0000, 0x0004, /* 128-131 */ 0x000a, 0x0075, 0x0035, 0x00b0, /* 132-135 */ 0x0040, 0x0000, 0x0000, 0x0000, /* 136-139 */ 0x0088, 0x0013, 0x000c, 0x0003, /* 140-143 */ 0x0000, 0x0000, 0x0008, 0x0000, /* 144-147 */ 0x0003, 0x0020, 0x0030, 0x0018, /* 148-151 */ 0x001b, }; /*----------------------------------------------------------------------*/ /*---------------------Control Interface Functions----------------------*/ static int vt1603_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret = vt1603_reg_write(codec->control_data, reg, value); if (ret == 0) ((u16 *)codec->reg_cache)[reg] = value; return ret; } static unsigned int vt1603_read(struct snd_soc_codec *codec, unsigned int reg) { return ((u16 *)codec->reg_cache)[reg]; } static int vt1603_hw_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { return vt1603_reg_write(codec->control_data, reg, value); } static unsigned int vt1603_hw_read(struct snd_soc_codec *codec, unsigned int reg) { u8 val; int ret = vt1603_reg_read(codec->control_data, reg, &val); if (ret == 0) return val; else return ((u16 *)codec->reg_cache)[reg]; } /*----------------------------------------------------------------------*/ /*----------------------IRQ handle Functions----------------------------*/ static void vt1603_set_switch_state(struct snd_soc_codec *codec) { /* Just report hp state, don't switch hw device, app will handle it. */ struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); unsigned int val = vt1603_hw_read(codec, VT1603_R21); if (vt1603_boardinfo.hp_level) { if (val & BIT1) { val &= ~BIT1; snd_soc_write(codec, VT1603_R21, val); if (!vt1603_boardinfo.ignore_hp_event) switch_set_state(&vt1603->hp_switch, 0); pr_info("<<hp_switch, 1); pr_info("<<hp_switch, 1); pr_info("<<hp_switch, 0); pr_info("<<codec; unsigned int val; static unsigned int count = 0; int hp_state = 0; //switch_get_state(&priv->hp_switch); if (!vt1603_boardinfo.hpd_rm) hp_state = switch_get_state(&priv->hp_switch); unsigned int debounce = (hp_state == 0) ? 3 : 1; // sw-debounce for headset plugin if (vt1603_boardinfo.hp_debounce > 0) debounce = (hp_state == 0) ? vt1603_boardinfo.hp_debounce : 1; val = vt1603_hw_read(codec, VT1603_R51); if (val & BIT1) { val = vt1603_hw_read(codec, VT1603_R1b); val |= BIT1; snd_soc_write(codec, VT1603_R1b, val); val &= ~BIT1; snd_soc_write(codec, VT1603_R1b, val); count++; } else { count = 0; } if (count == debounce) { pr_info("vt1603: hpdetect\n"); vt1603_set_switch_state(codec); // clear headphone irq mask val = vt1603_hw_read(codec,VT1603_R1b); val |= BIT1; snd_soc_write(codec, VT1603_R1b, val); val &= ~BIT1; snd_soc_write(codec, VT1603_R1b, val); count = 0; } val = vt1603_hw_read(codec, VT1603_R52); if (val & BIT4) { pr_info("vt1603: left cld oc\n"); val = vt1603_hw_read(codec,VT1603_R1c); val |= BIT4; snd_soc_write(codec, VT1603_R1c, val); val &= ~BIT4; snd_soc_write(codec, VT1603_R1c, val); } if (val & BIT3) { pr_info("vt1603: right cld oc\n"); val = vt1603_hw_read(codec,VT1603_R1c); val |= BIT3; snd_soc_write(codec, VT1603_R1c, val); val &= ~BIT3; snd_soc_write(codec, VT1603_R1c, val); } } static void vt1603_timer_handler(unsigned long data) { struct vt1603_priv *vt1603 = (struct vt1603_priv *)data; schedule_work(&vt1603->work); mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(VT1603_TIMER_INTERVAL)); } /*----------------------------------------------------------------------*/ /*--------------------Mixer controls & dapm-----------------------------*/ static const DECLARE_TLV_DB_SCALE(digital_tlv, -9550, 50, 1); static const DECLARE_TLV_DB_SCALE(digital_clv, -4350, 50, 1); static const DECLARE_TLV_DB_SCALE(adc_boost_clv, 0, 1000, 1); static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(adc_pga_clv, -1650, 150, 1); static const DECLARE_TLV_DB_SCALE(analog_tlv, -2100, 300, 1); static const DECLARE_TLV_DB_SCALE(hp_mixer_clv, -5700, 100, 1); static const DECLARE_TLV_DB_SCALE(cld_mixer_clv, -300, 300, 1); static const char *adc_cutoff_freq_txt[] = { "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3", }; static const struct soc_enum adc_cutoff_freq = SOC_ENUM_SINGLE(VT1603_R00, 3, 4, adc_cutoff_freq_txt); static const char *lr_txt[] = { "Left", "Right" }; static const char *clamper_limit_voltage_txt[] = { "Vih=2.40V, Vil=0.90V, P=0.63W", "Vih=2.45V, Vil=0.85V, P=0.72W", "Vih=2.50V, ViL=0.80V, P=0.81W", "Vih=2.55V, Vil=0.75V, P=0.91W", "Vih=2.60V, Vil=0.70V, P=1.02W", "Vih=2.65V, Vil=0.65V, P=1.12W", "Vih=2.70V, Vil=0.60V, P=1.24W", "Vih=2.75V, Vil=0.55V, P=1.36W", "Vih=2.80V, Vil=0.50V, P=1.49W", "Vih=2.85V, Vil=0.45V, P=1.62W", "Vih=2.90V, Vil=0.40V, P=1.76W", "Vih=2.95V, Vil=0.35V, P=1.90W", "Vih=3.00V, Vil=0.30V, P=2.05W", "Vih=3.05V, Vil=0.25V, P=2.20W", "Vih=3.10V, Vil=0.20V, P=2.37W", "Vih=3.15V, Vil=0.15V, P=2.53W", }; static const char *clamper_work_voltage_txt[] = { "Voh=3.10V, Vol=1.90V, P=0.36W", "Voh=3.25V, Vol=1.75V, P=0.56W", "Voh=3.40V, Vol=1.60V, P=0.81W", "Voh=3.55V, Vol=1.45V, P=1.10W", "Voh=3.70V, Vol=1.30V, P=1.44W", "Voh=3.85V, Vol=1.15V, P=1.82W", "Voh=4.00V, Vol=1.00V, P=2.25W", "Voh=4.15V, Vol=0.85V, P=2.72W", }; static const struct soc_enum dacl_src = SOC_ENUM_SINGLE(VT1603_R0b, 7, 2, lr_txt); static const struct soc_enum dacr_src = SOC_ENUM_SINGLE(VT1603_R0b, 6, 2, lr_txt); static const struct soc_enum adcl_src = SOC_ENUM_SINGLE(VT1603_R03, 3, 2, lr_txt); static const struct soc_enum adcr_src = SOC_ENUM_SINGLE(VT1603_R03, 2, 2, lr_txt); static const struct soc_enum clamper_limit_voltage = SOC_ENUM_SINGLE(VT1603_R87, 4, 16, clamper_limit_voltage_txt); static const struct soc_enum clamper_work_voltage = SOC_ENUM_SINGLE(VT1603_R87, 0, 8, clamper_work_voltage_txt); static const struct snd_kcontrol_new vt1603_snd_controls[] = { /* Volume */ SOC_DOUBLE_R_TLV("Digital Capture Volume", VT1603_R01, VT1603_R02, 0, 0x7f, 0, digital_clv), SOC_DOUBLE_R_TLV("Digital Playback Volume", VT1603_R07, VT1603_R08, 0, 0xc0, 0, digital_tlv), SOC_DOUBLE_R_TLV("Analog Playback Volume", VT1603_R72, VT1603_R73, 0, 0x07, 1, analog_tlv), SOC_DOUBLE_R_TLV("Input Boost Volume", VT1603_R64, VT1603_R65, 6, 4, 0, adc_boost_clv), SOC_DOUBLE_R_TLV("Input PGA Volume", VT1603_R64, VT1603_R65, 1, 0x1f, 0, adc_pga_clv), SOC_DOUBLE_TLV("Output Boost Volume", VT1603_R06, 0, 3, 7, 0, dac_boost_tlv), SOC_DOUBLE_TLV("Output CLD Volume", VT1603_R91, 5, 2, 7, 0, cld_mixer_clv), SOC_DOUBLE_R_TLV("Output HP Volume", VT1603_R6a, VT1603_R6b, 0, 0x3f, 0, hp_mixer_clv), /* Switch */ SOC_SINGLE("EQ Switch", VT1603_R28, 0, 1, 0), SOC_SINGLE("DAC Mono Mix Switch", VT1603_R0d, 4, 1, 0), SOC_SINGLE("Left DAC Switch", VT1603_R62, 5, 1, 0), SOC_SINGLE("Right DAC Switch", VT1603_R62, 4, 1, 0), SOC_ENUM("Left DAC Source", dacl_src), SOC_ENUM("Right DAC Source", dacr_src), SOC_ENUM("Digital Capture Voice Mode", adc_cutoff_freq), SOC_ENUM("Left ADC Source", adcl_src), SOC_ENUM("Right ADC Source", adcr_src), SOC_DOUBLE("Analog Playback Switch", VT1603_R71, 7, 6, 1, 1), SOC_SINGLE("Digital Playback Switch", VT1603_R0b, 0, 1, 1), SOC_DOUBLE("Digital Capture Switch", VT1603_R63, 7, 6, 1, 0), SOC_SINGLE("Headset Switch", VT1603_R68, 4, 1, 1), SOC_SINGLE("Headfree Switch", VT1603_R25, 1, 1, 0), SOC_SINGLE("Left CLD Switch", VT1603_R82, 3, 1, 1), SOC_SINGLE("Right CLD Switch", VT1603_R82, 4, 1, 1), SOC_ENUM("Limit Voltage of Power Clamper", clamper_limit_voltage), SOC_ENUM("Voltage of Clamper Work", clamper_work_voltage), }; /* DAPM Controls */ static const struct snd_kcontrol_new linmix_controls[] = { SOC_DAPM_SINGLE("Left Mic Switch", VT1603_R8e, 5, 1, 0), SOC_DAPM_SINGLE("Left Linein Switch", VT1603_R8e, 7, 1, 0), }; static const struct snd_kcontrol_new rinmix_controls[] = { SOC_DAPM_SINGLE("Right Mic Switch", VT1603_R8e, 4, 1, 0), SOC_DAPM_SINGLE("Right Linein Switch", VT1603_R8e, 6, 1, 0), }; static const struct snd_kcontrol_new loutmix_controls[] = { SOC_DAPM_SINGLE("Left Input Mixer Switch", VT1603_R71, 3, 1, 0), SOC_DAPM_SINGLE("DACL Switch", VT1603_R71, 1, 1, 0), }; static const struct snd_kcontrol_new routmix_controls[] = { SOC_DAPM_SINGLE("Right Input Mixer Switch", VT1603_R71, 2, 1, 0), SOC_DAPM_SINGLE("DACR Switch", VT1603_R71, 0, 1, 0), }; static const struct snd_kcontrol_new lcld_controls[] = { SOC_DAPM_SINGLE("Left Output Mixer Switch", VT1603_R90, 5, 1, 0), SOC_DAPM_SINGLE("DACL Switch", VT1603_R90, 4, 1, 0), }; static const struct snd_kcontrol_new rcld_controls[] = { SOC_DAPM_SINGLE("Right Output Mixer Switch", VT1603_R90, 3, 1, 0), SOC_DAPM_SINGLE("DACR Switch", VT1603_R90, 2, 1, 0), }; static const struct snd_kcontrol_new lhp_controls[] = { SOC_DAPM_SINGLE("Left Output Mixer Switch", VT1603_R69, 5, 1, 0), SOC_DAPM_SINGLE("DACL Switch", VT1603_R69, 7, 1, 0), }; static const struct snd_kcontrol_new rhp_controls[] = { SOC_DAPM_SINGLE("Right Output Mixer Switch", VT1603_R69, 2, 1, 0), SOC_DAPM_SINGLE("DACR Switch", VT1603_R69, 4, 1, 0), }; static const struct snd_soc_dapm_widget vt1603_dapm_widgets[] = { SND_SOC_DAPM_INPUT("Mic"), SND_SOC_DAPM_INPUT("Linein"), SND_SOC_DAPM_INPUT("Internal ADC Source"), SND_SOC_DAPM_PGA("Left Input Boost", VT1603_R67, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Input Boost", VT1603_R67, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("Left Input PGA", VT1603_R67, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Input PGA", VT1603_R67, 4, 0, NULL, 0), SND_SOC_DAPM_MIXER("Left Input Mixer", VT1603_R66, 6, 1, linmix_controls, ARRAY_SIZE(linmix_controls)), SND_SOC_DAPM_MIXER("Right Input Mixer", VT1603_R66, 5, 1, rinmix_controls, ARRAY_SIZE(rinmix_controls)), SND_SOC_DAPM_ADC("ADCL", "Left Capture", VT1603_R01, 7, 1), SND_SOC_DAPM_ADC("ADCR", "Right Capture", VT1603_R02, 7, 1), //SND_SOC_DAPM_DAC("DACL", "Left Playback", VT1603_R62, 5, 0), //SND_SOC_DAPM_DAC("DACR", "Right Playback", VT1603_R62, 4, 0), SND_SOC_DAPM_INPUT("DACL"), SND_SOC_DAPM_INPUT("DACR"), SND_SOC_DAPM_MIXER("Left Output Mixer", VT1603_R72, 6, 0, loutmix_controls, ARRAY_SIZE(loutmix_controls)), SND_SOC_DAPM_MIXER("Right Output Mixer", VT1603_R73, 6, 0, routmix_controls, ARRAY_SIZE(routmix_controls)), SND_SOC_DAPM_MIXER("Left CLD Mixer", VT1603_R91, 1, 0, lcld_controls, ARRAY_SIZE(lcld_controls)), SND_SOC_DAPM_MIXER("Right CLD Mixer", VT1603_R91, 0, 0, rcld_controls, ARRAY_SIZE(rcld_controls)), SND_SOC_DAPM_PGA("Left CLD PGA", VT1603_R82, 3, 1, NULL, 0), SND_SOC_DAPM_PGA("Right CLD PGA", VT1603_R82, 4, 1, NULL, 0), SND_SOC_DAPM_MIXER("Left HP Mixer", VT1603_R68, 1, 1, lhp_controls, ARRAY_SIZE(lhp_controls)), SND_SOC_DAPM_MIXER("Right HP Mixer", VT1603_R68, 0, 1, rhp_controls, ARRAY_SIZE(rhp_controls)), SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), SND_SOC_DAPM_OUTPUT("Left HP"), SND_SOC_DAPM_OUTPUT("Right HP"), SND_SOC_DAPM_OUTPUT("Left SPK"), SND_SOC_DAPM_OUTPUT("Right SPK"), }; static const struct snd_soc_dapm_route audio_map[] = { /* Make DACs turn on when playing even if not mixed into any outputs */ //{"Internal DAC Sink", NULL, "DACL"}, //{"Internal DAC Sink", NULL, "DACR"}, /* Make ADCs turn on when recording even if not mixed from any inputs */ //{"ADCL", NULL, "Internal ADC Source"}, //{"ADCR", NULL, "Internal ADC Source"}, /* Capture route */ { "Left Input Mixer", "Left Linein Switch", "Linein" }, { "Left Input Mixer", "Left Mic Switch", "Mic" }, { "Right Input Mixer", "Right Linein Switch", "Linein" }, { "Right Input Mixer", "Right Mic Switch", "Mic" }, { "Left Input Boost", NULL, "Left Input Mixer"}, { "Right Input Boost", NULL, "Right Input Mixer"}, { "Left Input PGA", NULL, "Left Input Boost"}, { "Right Input PGA", NULL, "Right Input Boost"}, { "ADCL", NULL, "Left Input PGA" }, { "ADCR", NULL, "Right Input PGA" }, //{ "Left Input Mixer", NULL, "Left Input PGA" }, //{ "Right Input Mixer", NULL, "Right Input PGA" }, /* Playback route */ //{ "Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer" }, { "Left Output Mixer", "Left Input Mixer Switch", "Left Input PGA"}, { "Left Output Mixer", "DACL Switch", "DACL" }, //{ "Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer" }, { "Right Output Mixer", "Right Input Mixer Switch", "Right Input PGA"}, { "Right Output Mixer", "DACR Switch", "DACR" }, { "Left CLD Mixer", "Left Output Mixer Switch", "Left Output Mixer" }, { "Left CLD Mixer", "DACL Switch", "DACL" }, { "Right CLD Mixer", "Right Output Mixer Switch", "Right Output Mixer" }, { "Right CLD Mixer", "DACR Switch", "DACR" }, { "Left HP Mixer", "Left Output Mixer Switch", "Left Output Mixer" }, { "Left HP Mixer", "DACL Switch", "DACL" }, { "Right HP Mixer", "Right Output Mixer Switch", "Right Output Mixer" }, { "Right HP Mixer", "DACR Switch", "DACR" }, { "Left CLD PGA", NULL, "Left CLD Mixer" }, { "Right CLD PGA", NULL, "Right CLD Mixer" }, { "Left HP", NULL, "Left HP Mixer" }, { "Right HP", NULL, "Right HP Mixer" }, { "Left SPK", NULL, "Left CLD PGA" }, { "Right SPK", NULL, "Right CLD PGA" }, }; /*----------------------------------------------------------------------*/ /*----------------------codec dai functions-----------------------------*/ struct _coeff_div { u32 mclk; u32 rate; u16 fs; u8 sr; u8 bclk_div; }; /* codec hifi mclk clock divider coefficients */ static const struct _coeff_div coeff_div[] = { /* 8k */ {12288000, 8000, 1536, 0x4, 0x0}, /* 11.025k */ {11289600, 11025, 1024, 0x8, 0x0}, /* 16k */ {12288000, 16000, 768, 0x5, 0x0}, /* 22.05k */ {11289600, 22050, 512, 0x9, 0x0}, /* 32k */ {12288000, 32000, 384, 0x7, 0x0}, /* 44.1k */ {11289600, 44100, 256, 0x6, 0x07}, /* 48k */ {12288000, 48000, 256, 0x0, 0x07}, /* 96k */ {12288000, 96000, 128, 0x1, 0x04}, }; static inline int get_coeff(int mclk, int rate) { int i; for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) return i; } return -EINVAL; } static int vt1603_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); switch (freq) { case 11289600: case 12000000: case 12288000: case 16934400: case 18432000: vt1603->sysclk = freq; return 0; } return -EINVAL; } static int vt1603_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; u16 iface = snd_soc_read(codec, VT1603_R19); /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: iface |= BIT5 ; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: return -EINVAL; } /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x0002; break; case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_LEFT_J: iface |= 0x0001; break; case SND_SOC_DAIFMT_DSP_A: iface |= 0x0003; break; case SND_SOC_DAIFMT_DSP_B: iface |= 0x0013; break; default: return -EINVAL; } /* clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: iface |= 0x0090; break; case SND_SOC_DAIFMT_IB_NF: iface |= 0x0080; break; case SND_SOC_DAIFMT_NB_IF: iface |= 0x0010; break; default: return -EINVAL; } snd_soc_write(codec, VT1603_R19, iface); return 0; } static int vt1603_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); u16 val; u16 iface = snd_soc_read(codec, VT1603_R19) & 0x73; int coeff = get_coeff(vt1603->sysclk, params_rate(params)); /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: iface |= 0x04; break; case SNDRV_PCM_FORMAT_S24_LE: iface |= 0x08; break; case SNDRV_PCM_FORMAT_S32_LE: iface |= 0x0c; break; } /* set iface & srate */ snd_soc_write(codec, VT1603_R19, iface); if (coeff >= 0) { val = snd_soc_read(codec, VT1603_R42); val &= 0xf0; val |= coeff_div[coeff].bclk_div; snd_soc_write(codec, VT1603_R42, val); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { val = snd_soc_read(codec, VT1603_R05); val &= 0xf0; val |= coeff_div[coeff].sr; snd_soc_write(codec, VT1603_R05, val); } if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { val = snd_soc_read(codec, VT1603_R03); val &= 0x0f; val |= ((coeff_div[coeff].sr<<4) & 0xf0); snd_soc_write(codec, VT1603_R03, val); } } return 0; } static int vt1603_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { u16 val; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: val = snd_soc_read(codec, VT1603_R96); val |= BIT4+BIT5; snd_soc_write(codec, VT1603_R96, val); break; case SND_SOC_BIAS_STANDBY: break; case SND_SOC_BIAS_OFF: break; } codec->dapm.bias_level = level; return 0; } static struct snd_soc_dai_ops vt1603_dai_ops = { .hw_params = vt1603_pcm_hw_params, .set_fmt = vt1603_set_dai_fmt, .set_sysclk = vt1603_set_dai_sysclk, }; struct snd_soc_dai_driver vt1603_dai = { .name = "VT1603", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &vt1603_dai_ops, }; /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ static void vt1603_get_boardinfo(void) { int ret = 0; unsigned char buf[64]; int varlen = sizeof(buf); memset(buf, 0x0, sizeof(buf)); ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen); if (ret == 0) { sscanf(buf, "vt1603:%d:%d:%d", &vt1603_boardinfo.hp_level, &vt1603_boardinfo.ignore_hp_event, &vt1603_boardinfo.rec_src); } else { vt1603_boardinfo.hp_level = 0; vt1603_boardinfo.ignore_hp_event = 0; vt1603_boardinfo.rec_src = 0; } memset(buf, 0x0, sizeof(buf)); vt1603_boardinfo.hpd_rm = 0; ret = wmt_getsyspara("wmt.vt1603.hpd.rm", buf, &varlen); if (ret == 0) { sscanf(buf, "%d", &vt1603_boardinfo.hpd_rm); } else { vt1603_boardinfo.hpd_rm = 0; } printk("<<<<%s hpd_rm %d\n", __func__, vt1603_boardinfo.hpd_rm); memset(buf, 0x0, sizeof(buf)); vt1603_boardinfo.hp_debounce = 0; ret = wmt_getsyspara("wmt.vt1603.hp.debounce", buf, &varlen); if (ret == 0) { sscanf(buf, "%d", &vt1603_boardinfo.hp_debounce); } else { vt1603_boardinfo.hp_debounce = 0; } printk("<<<<%s hp_debounce %d\n", __func__, vt1603_boardinfo.hp_debounce); memset(buf, 0x0, sizeof(buf)); vt1603_boardinfo.timer_del = 0; ret = wmt_getsyspara("wmt.vt1603.timer.del", buf, &varlen); if (ret == 0) { sscanf(buf, "%d", &vt1603_boardinfo.timer_del); } else { vt1603_boardinfo.timer_del = 0; } printk("<<<<%s timer_del %d\n", __func__, vt1603_boardinfo.timer_del); memset(buf, 0x0, sizeof(buf)); vt1603_boardinfo.out_on = 0; ret = wmt_getsyspara("wmt.vt1603.out_on", buf, &varlen); if (ret == 0) { sscanf(buf, "%d", &vt1603_boardinfo.out_on); } else { vt1603_boardinfo.out_on = 0; } printk("<<<<%s out_on %d\n", __func__, vt1603_boardinfo.out_on); } static int vt1603_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) { struct vt1603_priv *vt1603 = data; pr_info("\nvt1603 regs dump:\n"); vt1603_regs_dump(vt1603->codec->control_data); return 0; } static void vt1603_codec_reset(struct snd_soc_codec *codec) { unsigned int val; vt1603_hw_write(codec, 0xc2, 0x01); // reset all registers to default settings vt1603_hw_write(codec, VT1603_REG_RESET, 0xff); vt1603_hw_write(codec, VT1603_REG_RESET, 0x00); val = vt1603_hw_read(codec, VT1603_R60); val &= ~(BIT6 + BIT7); vt1603_hw_write(codec, VT1603_R60, val); val |= BIT6 + BIT7; vt1603_hw_write(codec, VT1603_R60, val); } static void vt1603_codec_init(struct snd_soc_codec *codec) { unsigned int val; // vt1603 touch uses irq low active??? if (vt1603_boardinfo.hp_level) { //hp high snd_soc_write(codec, VT1603_R21, 0xfd); } else { //hp low snd_soc_write(codec, VT1603_R21, 0xff); } // vt1603 codec irq settings snd_soc_write(codec, VT1603_R1b, 0xff); snd_soc_write(codec, VT1603_R1b, 0xfd); snd_soc_write(codec, VT1603_R1c, 0xff); snd_soc_write(codec, VT1603_R1d, 0xff); snd_soc_write(codec, VT1603_R24, 0x04); snd_soc_write(codec, VT1603_R95, 0x00); snd_soc_write(codec, VT1603_R60, 0xc7); snd_soc_write(codec, VT1603_R93, 0x02); snd_soc_write(codec, VT1603_R7b, 0x10); snd_soc_write(codec, VT1603_R92, 0x2c); // r4 is reserved, I don't know more about it. snd_soc_write(codec, VT1603_R04, 0x00); // r9[5] is reserved, I don't know more about it. val = snd_soc_read(codec, VT1603_R09); val |= BIT5; snd_soc_write(codec, VT1603_R09, val); // r79 is reserved, I don't know more about it. val = snd_soc_read(codec, VT1603_R79); val |= BIT2; val &= ~(BIT0+BIT1); snd_soc_write(codec, VT1603_R79, val); // r7a is reserved, I don't know more about it. val = snd_soc_read(codec, VT1603_R7a); val |= BIT3+BIT4+BIT7; snd_soc_write(codec, VT1603_R7a, val); // r87 is reserved, I don't know more about it. snd_soc_write(codec, VT1603_R87, 0x90); // r88 is reserved, I don't know more about it. snd_soc_write(codec, VT1603_R88, 0x28); // r8a is reserved, I don't know more about it. val = snd_soc_read(codec, VT1603_R8a); val |= BIT1; snd_soc_write(codec, VT1603_R8a, val); // enable CLK_SYS, CLK_DSP, CLK_AIF val = snd_soc_read(codec, VT1603_R40); val |= BIT4+BIT5+BIT6; snd_soc_write(codec, VT1603_R40, val); // set DAC sample rate divier=CLK_SYS/1, DAC clock divider=DAC_CLK_512X val = snd_soc_read(codec, VT1603_R41); val &= ~(BIT3+BIT2+BIT1+BIT0); val |= BIT1;//add for mclk 24.576M, 10Khz 17kHz sine distortion 2014-6-12 snd_soc_write(codec, VT1603_R41, val); // set BCLK rate=CLK_SYS/8 val = snd_soc_read(codec, VT1603_R42); val |= BIT0+BIT1+BIT2; val &= ~BIT3; snd_soc_write(codec, VT1603_R42, val); // enable VREF_SC_DA buffer val = snd_soc_read(codec, VT1603_R61); val |= BIT7; snd_soc_write(codec, VT1603_R61, val); // ADC VREF_SC offset voltage snd_soc_write(codec, VT1603_R93, 0x20); // set mic bias voltage = 90% * AVDD val = snd_soc_read(codec, VT1603_R92); val |= BIT2; snd_soc_write(codec, VT1603_R92, val); // enable bypass for AOW0 parameterized HPF val = snd_soc_read(codec, VT1603_R0a); val |= BIT6; snd_soc_write(codec, VT1603_R0a, val); // CPVEE=2.2uF, the ripple frequency ON CPVEE above 22kHz, that will reduce HP noise val = snd_soc_read(codec, VT1603_R7a); val |= BIT6; val &= ~BIT5; snd_soc_write(codec, VT1603_R7a, val); // set DAC soft mute mode val = snd_soc_read(codec, VT1603_R0b); val |= BIT1+BIT2; snd_soc_write(codec, VT1603_R0b, val); // DAC High Voltage Switch Control Signal enable??? val = snd_soc_read(codec, VT1603_R62); val |= BIT7+BIT6; val &= ~BIT3; snd_soc_write(codec, VT1603_R62, val); // enable microphone bias val = snd_soc_read(codec, VT1603_R60); val |= 0x0f; snd_soc_write(codec, VT1603_R60, val); // capture pga settings?? val = snd_soc_read(codec, VT1603_R8e); val |= 0x0f; snd_soc_write(codec, VT1603_R8e, val); // capture pga settings?? val = snd_soc_read(codec, VT1603_R66); val |= BIT1+BIT2+BIT3+BIT4; // snd_soc_write(codec, VT1603_R66, val); // PGA Zero Cross Detector Enable, Change gain on zero cross only val = snd_soc_read(codec, VT1603_R64); val |= BIT0; snd_soc_write(codec, VT1603_R64, val); val = snd_soc_read(codec, VT1603_R65); val |= BIT0; snd_soc_write(codec, VT1603_R65, val); // enable hp output mode val = snd_soc_read(codec, VT1603_R68); val |= BIT2; snd_soc_write(codec, VT1603_R68, val); // enable class-d, unmute channels snd_soc_write(codec, VT1603_R7c, 0xe0); // set DAC gain update val = snd_soc_read(codec, VT1603_R05); val |= BIT6+BIT7; snd_soc_write(codec, VT1603_R05, val); // set class-d AC boost gain=1.6 snd_soc_write(codec, VT1603_R97, 0x1c); val = snd_soc_read(codec, VT1603_R97); val = (val & (~0x07)) + 0x04; snd_soc_write(codec, VT1603_R97, val); // set ADC gain update, enable ADCDAT output val = snd_soc_read(codec, VT1603_R00); val |= BIT5+BIT6+BIT7; snd_soc_write(codec, VT1603_R00, val); if (vt1603_boardinfo.out_on) { val = snd_soc_read(codec, VT1603_R68); val &= ~(1<<4); snd_soc_write(codec, VT1603_R68, val); val = snd_soc_read(codec, VT1603_R69); val |= BIT2 +BIT5; snd_soc_write(codec, VT1603_R69, val); val = snd_soc_read(codec, VT1603_R25); val |= BIT1; snd_soc_write(codec, VT1603_R25, val); val = snd_soc_read(codec, VT1603_R90); val |= BIT5 +BIT3; snd_soc_write(codec, VT1603_R90, val); } // DA DRC settings //snd_soc_write(codec, VT1603_R0e, 0x0b); //snd_soc_write(codec, VT1603_R0f, 0x94); //snd_soc_write(codec, VT1603_R10, 0x02); //snd_soc_write(codec, VT1603_R11, 0x00); //snd_soc_write(codec, VT1603_R12, 0x60); } static int vt1603_reboot_notify(struct notifier_block *this, unsigned long code, void *unused) { unsigned int val; struct vt1603_priv *priv = container_of(this, struct vt1603_priv, reboot_notifier); struct snd_soc_codec *codec = priv->codec; printk("vt1603_reboot_notify [%lu]\n", code); // power down headphone val = snd_soc_read(codec, VT1603_R68); val |= BIT4; snd_soc_write(codec, VT1603_R68, val); // power down class-d val = snd_soc_read(codec, VT1603_R25); val &= ~BIT1; snd_soc_write(codec, VT1603_R25, val); return NOTIFY_DONE; } /*----------------------------------------------------------------------*/ /*---------------------codec driver functions---------------------------*/ static int vt1603_codec_suspend(struct snd_soc_codec *codec) { struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); if (!vt1603_boardinfo.timer_del) { //add 2014-1-22 tvbox del_timer_sync(&vt1603->timer); } return 0; } static int vt1603_codec_resume(struct snd_soc_codec *codec) { struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); int reg; u16 *cache = codec->reg_cache; unsigned int val; int state = 0;//switch_get_state(&vt1603->hp_switch); if (!vt1603_boardinfo.hpd_rm) state = switch_get_state(&vt1603->hp_switch); // reset codec vt1603_codec_reset(codec); // restore codec registers for (reg = 0; reg < ARRAY_SIZE(vt1603_regs); reg++) { if (reg == VT1603_REG_RESET) continue; snd_soc_write(codec, reg, cache[reg]); } // restore headphone irq status val = vt1603_hw_read(codec, VT1603_R21); if ((vt1603_boardinfo.hp_level && state) || (!vt1603_boardinfo.hp_level && !state)) { val |= BIT1; snd_soc_write(codec, VT1603_R21, val); } else { val &= ~BIT1; snd_soc_write(codec, VT1603_R21, val); } // open headphone detect val = vt1603_hw_read(codec,VT1603_R1b); val |= BIT1; snd_soc_write(codec, VT1603_R1b, val); val &= ~BIT1; snd_soc_write(codec, VT1603_R1b, val); // clear touch interrupt status??? why here??? snd_soc_write(codec, 0xca, 0x0f); if (!vt1603_boardinfo.timer_del) { //add 2014-1-22 tvbox mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(50)); } return 0; } static int vt1603_codec_probe(struct snd_soc_codec *codec) { struct vt1603_priv *vt1603; int ret; vt1603 = kzalloc(sizeof(struct vt1603_priv), GFP_KERNEL); if (vt1603 == NULL) return -ENOMEM; gvt1603_codec = codec;//add 2013-9-2 snd_soc_codec_set_drvdata(codec, vt1603); codec->control_data = dev_get_platdata(codec->dev); vt1603->codec = codec; // need close to use wmt-switch.c 2013-11-27 if (!vt1603_boardinfo.hpd_rm) { vt1603->hp_switch.name = "h2w"; switch_dev_register(&vt1603->hp_switch); } //need close vt1603_codec_reset(codec); vt1603_codec_init(codec); vt1603_set_bias_level(codec, SND_SOC_BIAS_PREPARE); INIT_WORK(&vt1603->work, vt1603_codec_irq_handle); if (!vt1603_boardinfo.timer_del) { //add 2014-1-20 tvbox init_timer(&vt1603->timer); vt1603->timer.data = (unsigned long)vt1603; vt1603->timer.function = vt1603_timer_handler; mod_timer(&vt1603->timer, jiffies + msecs_to_jiffies(2000)); } vt1603->proc = create_proc_read_entry("vt1603_dumpregs", 0666, NULL, vt1603_readproc, vt1603); // register reboot notifier event vt1603->reboot_notifier.notifier_call = vt1603_reboot_notify; ret = register_reboot_notifier(&vt1603->reboot_notifier); if (ret != 0) pr_err("cannot register reboot notifier (err=%d)\n", ret); return 0; } static int vt1603_codec_remove(struct snd_soc_codec *codec) { struct vt1603_priv *vt1603 = snd_soc_codec_get_drvdata(codec); remove_proc_entry("vt1603_dumpregs", NULL); if (!vt1603_boardinfo.hpd_rm) { switch_dev_unregister(&vt1603->hp_switch); } del_timer_sync(&vt1603->timer); vt1603_set_bias_level(codec, SND_SOC_BIAS_OFF); kfree(vt1603); return 0; } static struct snd_soc_codec_driver soc_codec_dev_vt1603 = { .probe = vt1603_codec_probe, .remove = vt1603_codec_remove, .suspend = vt1603_codec_suspend, .resume = vt1603_codec_resume, .read = vt1603_read, .write = vt1603_write, .set_bias_level = vt1603_set_bias_level, .reg_cache_size = ARRAY_SIZE(vt1603_regs), .reg_word_size = sizeof(u16), .reg_cache_default = vt1603_regs, .controls = vt1603_snd_controls, .num_controls = ARRAY_SIZE(vt1603_snd_controls), .dapm_widgets = vt1603_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(vt1603_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), }; static int __devinit vt1603_probe(struct platform_device *pdev) { pr_info("<<<<%s \n", __FUNCTION__); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_vt1603, &vt1603_dai, 1); } static int __devexit vt1603_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; } static struct platform_driver vt1603_codec_driver = { .driver = { .name = "vt1603-codec", .owner = THIS_MODULE, }, .probe = vt1603_probe, .remove = __devexit_p(vt1603_remove), }; static __init int vt1603_init(void) { vt1603_get_boardinfo(); return platform_driver_register(&vt1603_codec_driver); } module_init(vt1603_init); static __exit void vt1603_exit(void) { platform_driver_unregister(&vt1603_codec_driver); } module_exit(vt1603_exit); int vt1603_hwdep_ioctl(u8 rw_flag, u16 offset, u16 value) { struct snd_soc_codec *codec = gvt1603_codec; if (!codec){ printk("%s NULL!\n", __FUNCTION__); return -1; } //info("rw_flag=%d, offset=0x%x, value=0x%x", rw_flag, offset, value); if (rw_flag) { if ((offset >= VT1603_R00) && (offset <= VT1603_R97)) { snd_soc_write(codec, offset, value); } else { printk("write to offset 0x%x is not allowed", offset); } return 0; } else { return snd_soc_read(codec, offset); } } EXPORT_SYMBOL(vt1603_hwdep_ioctl); MODULE_DESCRIPTION("WMT [ALSA SoC/codec] driver"); MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:vt1603-codec");