/*++
* 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");