blob: 02ae6e7f2256789e2359b2be82600eef6ce55c9b [file] [log] [blame]
/*
* ak4954_amb.c -- audio driver for AK4954
*
* Copyright 2014 Ambarella Ltd.
*
* Author: Diao Chengdong <cddiao@ambarella.com>
*
* History:
* 2014/03/27 - created
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ak4954_amb.h"
//#define PLL_32BICK_MODE
//#define PLL_64BICK_MODE
//#define AK4954_DEBUG //used at debug mode
//#define AK4954_CONTIF_DEBUG //used at debug mode
#ifdef AK4954_DEBUG
#define akdbgprt printk
#else
#define akdbgprt(format, arg...) do {} while (0)
#endif
#define LINEIN1_MIC_BIAS_CONNECT
#define LINEIN2_MIC_BIAS_CONNECT
/* AK4954 Codec Private Data */
struct ak4954_priv {
unsigned int rst_pin;
unsigned int rst_active;
unsigned int sysclk;
unsigned int clkid;
struct snd_soc_codec codec;
u8 reg_cache[AK4954_MAX_REGISTERS];
int onDrc;
int onStereo;
int mic;
};
static struct snd_soc_codec *ak4954_codec;
static struct ak4954_priv *ak4954_data;
/* ak4954 register cache & default register settings */
static const u8 ak4954_reg[AK4954_MAX_REGISTERS] = {
0x00, /* 0x00 AK4954_00_POWER_MANAGEMENT1 */
0x30, /* 0x01 AK4954_01_POWER_MANAGEMENT2 */
0x0a, /* 0x02 AK4954_02_SIGNAL_SELECT1 */
0x00, /* 0x03 AK4954_03_SIGNAL_SELECT2 */
0x34, /* 0x04 AK4954_04_SIGNAL_SELECT3 */
0x02, /* 0x05 AK4954_05_MODE_CONTROL1 */
0x09, /* 0x06 AK4954_06_MODE_CONTROL2 */
0x14, /* 0x07 AK4954_07_MODE_CONTROL3 */
0x00, /* 0x08 AK4954_08_DIGITL_MIC */
0x3A, /* 0x09 AK4954_09_TIMER_SELECT */
0x48, /* 0x0A AK4954_0A_ALC_TIMER_SELECT */
0x01, /* 0x0B AK4954_0B_ALC_MODE_CONTROL1 */
0xE1, /* 0x0C AK4954_0C_ALC_MODE_CONTROL2 */
0x91, /* 0x0D AK4954_0D_LCH_INPUT_VOLUME_CONTROL */
0x91, /* 0x0E AK4954_0E_RCH_INPUT_VOLUME_CONTROL */
0x00, /* 0x0F AK4954_0F_RESERVEDL */
0x00, /* 0x10 AK4954_10_RESERVED */
0x00, /* 0x11 AK4954_11_RESERVED */
0x00, /* 0x12 AK4954_12_HP_OUTPUT_CONTROL */
0x0C, /* 0x13 AK4954_13_LCH_DIGITAL_VOLUME_CONTROL */
0x0C, /* 0x14 AK4954_14_RCH_DIGITAL_VOLUME_CONTROL */
0x00, /* 0x15 AK4954_15_BEEP_FREQUENCY */
0x00, /* 0x16 AK4954_16_BEEP_ON_TIME */
0x00, /* 0x17 AK4954_17_BEEP_OFF_TIME */
0x00, /* 0x18 AK4954_18_BEEP_REPEAT_COUNT */
0x00, /* 0x19 AK4954_19_BEEP_VOLUME_CONTROL */
0x00, /* 0x1A AK4954_1A_RESERVED */
0x01, /* 0x1B AK4954_1B_DIGITAL_FILTER_SELECT1 */
0x00, /* 0x1C AK4954_1C_DIGITAL_FILTER_SELECT2 */
0x03, /* 0x1D AK4954_1D_DIGITAL_FILTER_MODE */
0xA9, /* 0x1E AK4954_1E_HPF2_COEFFICIENT0 */
0x1F, /* 0x1F AK4954_1F_HPF2_COEFFICIENT1 */
0xAD, /* 0x20 AK4954_20_HPF2_COEFFICIENT2 */
0x20, /* 0x21 AK4954_21_HPF2_COEFFICIENT3 */
0x7F, /* 0x22 AK4954_22_LPF_COEFFICIENT0 */
0x0C, /* 0x23 AK4954_23_LPF_COEFFICIENT1 */
0xFF, /* 0x24 AK4954_24_LPF_COEFFICIENT2 */
0x38, /* 0x25 AK4954_25_LPF_COEFFICIENT3 */
0xA2, /* 0x26 AK4954_26_FIL3_COEFFICIENT0 */
0x83, /* 0x27 AK4954_27_FIL3_COEFFICIENT1 */
0x80, /* 0x28 AK4954_28_FIL3_COEFFICIENT2 */
0x2E, /* 0x29 AK4954_29_FIL3_COEFFICIENT3 */
0x5B, /* 0x2A AK4954_2A_EQ_COEFFICIENT0 */
0x23, /* 0x2B AK4954_2B_EQ_COEFFICIENT1 */
0x07, /* 0x2C AK4954_2C_EQ_COEFFICIENT2 */
0x28, /* 0x2D AK4954_2D_EQ_COEFFICIENT3 */
0xAA, /* 0x2E AK4954_2E_EQ_COEFFICIENT4 */
0xEC, /* 0x2F AK4954_2F_EQ_COEFFICIENT5 */
0x00, /* 0x30 AK4954_30_DIGITAL_FILTER_SELECT3 */
0x00, /* 0x31 AK4954_31_RESERVED */
0x9F, /* 0x32 AK4954_32_E1_COEFFICIENT0 */
0x00, /* 0x33 AK4954_33_E1_COEFFICIENT1 */
0x2B, /* 0x34 AK4954_34_E1_COEFFICIENT2 */
0x3F, /* 0x35 AK4954_35_E1_COEFFICIENT3 */
0xD4, /* 0x36 AK4954_36_E1_COEFFICIENT4 */
0xE0, /* 0x37 AK4954_37_E1_COEFFICIENT5 */
0x6A, /* 0x38 AK4954_38_E2_COEFFICIENT0 */
0x00, /* 0x39 AK4954_39_E2_COEFFICIENT1 */
0x1B, /* 0x3A AK4954_3A_E2_COEFFICIENT2 */
0x3F, /* 0x3B AK4954_3B_E2_COEFFICIENT3 */
0xD4, /* 0x3C AK4954_3C_E2_COEFFICIENT4 */
0xE0, /* 0x3D AK4954_3D_E2_COEFFICIENT5 */
0x6A, /* 0x3E AK4954_3E_E3_COEFFICIENT0 */
0x00, /* 0x3F AK4954_3F_E3_COEFFICIENT1 */
0xA2, /* 0x40 AK4954_40_E3_COEFFICIENT2 */
0x3E, /* 0x41 AK4954_41_E3_COEFFICIENT3 */
0xD4, /* 0x42 AK4954_42_E3_COEFFICIENT4 */
0xE0, /* 0x43 AK4954_43_E3_COEFFICIENT5 */
0x6A, /* 0x44 AK4954_44_E4_COEFFICIENT0 */
0x00, /* 0x45 AK4954_45_E4_COEFFICIENT1 */
0xA8, /* 0x46 AK4954_46_E4_COEFFICIENT2 */
0x38, /* 0x47 AK4954_47_E4_COEFFICIENT3 */
0xD4, /* 0x48 AK4954_48_E4_COEFFICIENT4 */
0xE0, /* 0x49 AK4954_49_E4_COEFFICIENT5 */
0x6A, /* 0x4A AK4954_4A_E5_COEFFICIENT0 */
0x00, /* 0x4B AK4954_4B_E5_COEFFICIENT1 */
0x96, /* 0x4C AK4954_4C_E5_COEFFICIENT2 */
0x1F, /* 0x4D AK4954_4D_E5_COEFFICIENT3 */
0xD4, /* 0x4E AK4954_4E_E5_COEFFICIENT4 */
0xE0, /* 0x4F AK4954_4F_E5_COEFFICIENT5 */
0x00, /* 0x50 AK4954_50_DRC_MODE_CONTROL */
0x00, /* 0x51 AK4954_51_NS_CONTROL */
0x11, /* 0x52 AK4954_52_NS_GAIN_ATT_CONTROL */
0x90, /* 0x53 AK4954_53_NS_ON_LEVEL */
0x8A, /* 0x54 AK4954_54_NS_OFF_LEVEL */
0x07, /* 0x55 AK4954_55_NS_REFERENCE_SELECT */
0x40, /* 0x56 AK4954_56_NS_LPF_COEFFICIENT0 */
0x07, /* 0x57 AK4954_57_NS_LPF_COEFFICIENT1 */
0x80, /* 0x58 AK4954_58_NS_LPF_COEFFICIENT2 */
0x2E, /* 0x59 AK4954_59_NS_LPF_COEFFICIENT3 */
0xA9, /* 0x5A AK4954_5A_NS_HPF_COEFFICIENT0 */
0x1F, /* 0x5B AK4954_5B_NS_HPF_COEFFICIENT1 */
0xAD, /* 0x5C AK4954_5C_NS_HPF_COEFFICIENT2 */
0x20, /* 0x5D AK4954_5D_NS_HPF_COEFFICIENT3 */
0x00, /* 0x5E AK4954_5E_RESERVED */
0x00, /* 0x5F AK4954_5F_RESERVED */
0x00, /* 0x60 AK4954_60_DVLC_FILTER_SELECT */
0x6F, /* 0x61 AK4954_61_DVLC_MODE_CONTROL */
0x18, /* 0x62 AK4954_62_DVLCL_CURVE_X1 */
0x0C, /* 0x63 AK4954_63_DVLCL_CURVE_Y1 */
0x10, /* 0x64 AK4954_64_DVLCL_CURVE_X2 */
0x09, /* 0x65 AK4954_65_DVLCL_CURVE_Y2 */
0x08, /* 0x66 AK4954_66_DVLCL_CURVE_X3 */
0x08, /* 0x67 AK4954_67_DVLCL_CURVE_Y3 */
0x7F, /* 0x68 AK4954_68_DVLCL_SLOPE1 */
0x1D, /* 0x69 AK4954_69_DVLCL_SLOPE2 */
0x03, /* 0x6A AK4954_6A_DVLCL_SLOPE3 */
0x00, /* 0x6B AK4954_6B_DVLCL_SLOPE4 */
0x18, /* 0x6C AK4954_6C_DVLCM_CURVE_X1 */
0x0C, /* 0x6D AK4954_6D_DVLCM_CURVE_Y1 */
0x10, /* 0x6E AK4954_6E_DVLCM_CURVE_X2 */
0x06, /* 0x6F AK4954_6F_DVLCM_CURVE_Y2 */
0x08, /* 0x70 AK4954_70_DVLCM_CURVE_X3 */
0x04, /* 0x71 AK4954_71_DVLCM_CURVE_Y3 */
0x7F, /* ox72 AK4954_72_DVLCM_SLOPE1 */
0x4E, /* 0x73 AK4954_73_DVLCM_SLOPE2 */
0x0C, /* 0x74 AK4954_74_DVLCM_SLOPE3 */
0x00, /* 0x75 AK4954_75_DVLCM_SLOPE4 */
0x1C, /* 0x76 AK4954_76_DVLCH_CURVE_X1 */
0x10, /* 0x77 AK4954_77_DVLCH_CURVE_Y1 */
0x10, /* 0x78 AK4954_78_DVLCH_CURVE_X2 */
0x0C, /* 0x79 AK4954_79_DVLCH_CURVE_Y2 */
0x08, /* 0x7A AK4954_7A_DVLCH_CURVE_X3 */
0x09, /* 0x7B AK4954_7B_DVLCH_CURVE_Y3 */
0x7F, /* 0x7C AK4954_7C_DVLCH_SLOPE1 */
0x12, /* 0x7D AK4954_7D_DVLCH_SLOPE2 */
0x07, /* 0x7E AK4954_7E_DVLCH_SLOPE3 */
0x01, /* 0x7F AK4954_7F_DVLCH_SLOPE4 */
0x50, /* 0x80 AK4954_80_DVLCL_LPF_COEFFICIENT0 */
0x01, /* 0x81 AK4954_81_DVLCL_LPF_COEFFICIENT1 */
0xA0, /* 0x82 AK4954_82_DVLCL_LPF_COEFFICIENT2 */
0x22, /* 0x83 AK4954_83_DVLCL_LPF_COEFFICIENT3 */
0xA9, /* 0x84 AK4954_84_DVLCM_HPF_COEFFICIENT0 */
0x1F, /* 0x85 AK4954_85_DVLCM_HPF_COEFFICIENT1 */
0xAD, /* 0x86 AK4954_86_DVLCM_HPF_COEFFICIENT2 */
0x20, /* 0x87 AK4954_87_DVLCM_HPF_COEFFICIENT3 */
0x04, /* 0x88 AK4954_88_DVLCM_LPF_COEFFICIENT0 */
0x0A, /* 0x89 AK4954_89_DVLCM_LPF_COEFFICIENT1 */
0x07, /* 0x8A AK4954_8A_DVLCM_LPF_COEFFICIENT2 */
0x34, /* 0x8B AK4954_8B_DVLCM_LPF_COEFFICIENT3 */
0xE6, /* 0x8C AK4954_8C_DVLCH_HPF_COEFFICIENT0 */
0x1C, /* 0x8D AK4954_8D_DVLCH_HPF_COEFFICIENT1 */
0x33, /* 0x8E AK4954_8E_DVLCH_HPF_COEFFICIENT2 */
0x26, /* 0x8F AK4954_8F_DVLCH_HPF_COEFFICIENT3 */
};
static const struct {
int readable; /* Mask of readable bits */
int writable; /* Mask of writable bits */
} ak4954_access_masks[] = {
{ 0xEF, 0xEF }, //0x00
{ 0x3F, 0x3F }, //0x01
{ 0xBF, 0xBF }, //0x02
{ 0xCF, 0xCF }, //0x03
{ 0x3F, 0x3F }, //0x04
{ 0x7F, 0x7F }, //0x05
{ 0xCF, 0xCF }, //0x06
{ 0xF7, 0xB7 }, //0x07
{ 0x7B, 0x7B }, //0x08
{ 0xFF, 0xFF }, //0x09
{ 0xCF, 0xCF }, //0x0A
{ 0xBF, 0xBF }, //0x0B
{ 0xFF, 0xFF }, //0x0C
{ 0xFF, 0xFF }, //0x0D
{ 0xFF, 0xFF }, //0x0E
{ 0x00, 0x00 }, //0x0F
{ 0x00, 0x00 }, //0x10
{ 0x00, 0x00 }, //0x11
{ 0x80, 0x80 }, //0x12
{ 0xFF, 0xFF }, //0x13
{ 0xFF, 0xFF }, //0x14
{ 0x83, 0x83 }, //0x15
{ 0xFF, 0xFF }, //0x16
{ 0xFF, 0xFF }, //0x17
{ 0x7F, 0x7F }, //0x18
{ 0x9F, 0x9F }, //0x19
{ 0x00, 0x00 }, //0x1A
{ 0x0F, 0x0F }, //0x1B
{ 0xF3, 0xF3 }, //0x1C
{ 0x87, 0x87 }, //0x1D
{ 0xFF, 0xFF }, //0x1E
{ 0xFF, 0xFF }, //0x1F
{ 0xFF, 0xFF }, //0x20
{ 0xFF, 0xFF }, //0x21
{ 0xFF, 0xFF }, //0x22
{ 0xFF, 0xFF }, //0x23
{ 0xFF, 0xFF }, //0x24
{ 0xFF, 0xFF }, //0x25
{ 0xFF, 0xFF }, //0x26
{ 0xFF, 0xFF }, //0x27
{ 0xFF, 0xFF }, //0x28
{ 0xFF, 0xFF }, //0x29
{ 0xFF, 0xFF }, //0x2A
{ 0xFF, 0xFF }, //0x2B
{ 0xFF, 0xFF }, //0x2C
{ 0xFF, 0xFF }, //0x2D
{ 0xFF, 0xFF }, //0x2E
{ 0xFF, 0xFF }, //0x2F
{ 0x1F, 0x1F }, //0x30
{ 0x00, 0x00 }, //0x31
{ 0xFF, 0xFF }, //0x32
{ 0xFF, 0xFF }, //0x33
{ 0xFF, 0xFF }, //0x34
{ 0xFF, 0xFF }, //0x35
{ 0xFF, 0xFF }, //0x36
{ 0xFF, 0xFF }, //0x37
{ 0xFF, 0xFF }, //0x38
{ 0xFF, 0xFF }, //0x39
{ 0xFF, 0xFF }, //0x3A
{ 0xFF, 0xFF }, //0x3B
{ 0xFF, 0xFF }, //0x3C
{ 0xFF, 0xFF }, //0x3D
{ 0xFF, 0xFF }, //0x3E
{ 0xFF, 0xFF }, //0x3F
{ 0xFF, 0xFF }, //0x40
{ 0xFF, 0xFF }, //0x41
{ 0xFF, 0xFF }, //0x42
{ 0xFF, 0xFF }, //0x43
{ 0xFF, 0xFF }, //0x44
{ 0xFF, 0xFF }, //0x45
{ 0xFF, 0xFF }, //0x46
{ 0xFF, 0xFF }, //0x47
{ 0xFF, 0xFF }, //0x48
{ 0xFF, 0xFF }, //0x49
{ 0xFF, 0xFF }, //0x4A
{ 0xFF, 0xFF }, //0x4B
{ 0xFF, 0xFF }, //0x4C
{ 0xFF, 0xFF }, //0x4D
{ 0xFF, 0xFF }, //0x4E
{ 0xFF, 0xFF }, //0x4F
{ 0x7F, 0x7F }, //0x50
{ 0x37, 0x37 }, //0x51
{ 0x77, 0x77 }, //0x52
{ 0xDF, 0xDF }, //0x53
{ 0xDF, 0xDF }, //0x54
{ 0x0F, 0x0F }, //0x55
{ 0xFF, 0xFF }, //0x56
{ 0xFF, 0xFF }, //0x57
{ 0xFF, 0xFF }, //0x58
{ 0xFF, 0xFF }, //0x59
{ 0xFF, 0xFF }, //0x5A
{ 0xFF, 0xFF }, //0x5B
{ 0xFF, 0xFF }, //0x5C
{ 0xFF, 0xFF }, //0x5D
{ 0x00, 0x00 }, //0x5E
{ 0x00, 0x00 }, //0x5F
{ 0xFF, 0xFF }, //0x60
{ 0xFF, 0xFF }, //0x61
{ 0x7F, 0x7F }, //0x62
{ 0x7F, 0x7F }, //0x63
{ 0x7F, 0x7F }, //0x64
{ 0x7F, 0x7F }, //0x65
{ 0x7F, 0x7F }, //0x66
{ 0x7F, 0x7F }, //0x67
{ 0x7F, 0x7F }, //0x68
{ 0x7F, 0x7F }, //0x69
{ 0x7F, 0x7F }, //0x6A
{ 0x7F, 0x7F }, //0x6B
{ 0x7F, 0x7F }, //0x6C
{ 0x7F, 0x7F }, //0x6D
{ 0x7F, 0x7F }, //0x6E
{ 0x7F, 0x7F }, //0x6F
{ 0x7F, 0x7F }, //0x70
{ 0x7F, 0x7F }, //0x71
{ 0x7F, 0x7F }, //0x72
{ 0x7F, 0x7F }, //0x73
{ 0x7F, 0x7F }, //0x74
{ 0x7F, 0x7F }, //0x75
{ 0x7F, 0x7F }, //0x76
{ 0x7F, 0x7F }, //0x77
{ 0x7F, 0x7F }, //0x78
{ 0x7F, 0x7F }, //0x79
{ 0x7F, 0x7F }, //0x7A
{ 0x7F, 0x7F }, //0x7B
{ 0x7F, 0x7F }, //0x7C
{ 0x7F, 0x7F }, //0x7D
{ 0x7F, 0x7F }, //0x7E
{ 0x7F, 0x7F }, //0x7F
{ 0xFF, 0xFF }, //0x80
{ 0xFF, 0xFF }, //0x81
{ 0xFF, 0xFF }, //0x82
{ 0xFF, 0xFF }, //0x83
{ 0xFF, 0xFF }, //0x84
{ 0xFF, 0xFF }, //0x85
{ 0xFF, 0xFF }, //0x86
{ 0xFF, 0xFF }, //0x87
{ 0xFF, 0xFF }, //0x88
{ 0xFF, 0xFF }, //0x89
{ 0xFF, 0xFF }, //0x8A
{ 0xFF, 0xFF }, //0x8B
{ 0xFF, 0xFF }, //0x8C
{ 0xFF, 0xFF }, //0x8D
{ 0xFF, 0xFF }, //0x8E
{ 0xFF, 0xFF } //0x8F
};
/*
* MIC Gain control:
* from 6 to 26 dB in 6.5 dB steps
*/
static DECLARE_TLV_DB_SCALE(mgain_tlv, 600, 650, 0);
/*
* Input Digital volume control:
* from -54.375 to 36 dB in 0.375 dB steps mute instead of -54.375 dB)
*/
static DECLARE_TLV_DB_SCALE(ivol_tlv, -5437, 37, 1);
/*
* Speaker output volume control:
* from -33 to 12 dB in 3 dB steps (mute instead of -33 dB)
*/
static DECLARE_TLV_DB_SCALE(spkout_tlv, 426, 200, 0);
/*
* Output Digital volume control: (DATT-A)
* (This can be used as Bluetooth I/F output volume)
* from -57.5 to 6 dB in 0.5 dB steps (mute instead of -57.5 dB)
*/
static DECLARE_TLV_DB_SCALE(dvol_tlv, -6600, 50, 1);
static const char *drc_on_select[] =
{
"DRC OFF",
"DRC ON",
};
static const struct soc_enum ak4954_drc_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(drc_on_select), drc_on_select),
};
/*For our Board -- cddiao*/
static const char *mic_and_lin_select[] =
{
"LIN",
"MIC",
};
static const struct soc_enum ak4954_micswitch_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mic_and_lin_select), mic_and_lin_select),
};
static int get_micstatus(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = ak4954->mic;
return 0;
}
static int set_micstatus(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ak4954->mic = ucontrol->value.enumerated.item[0];
if ( ak4954->mic ) {
snd_soc_update_bits(codec,AK4954_03_SIGNAL_SELECT2,0x0f,0x05);// LIN2 RIN2
}else {
snd_soc_update_bits(codec,AK4954_03_SIGNAL_SELECT2,0x0f,0x0a);// LIN3 RIN3
}
return 0;
}
static const char *stereo_on_select[] =
{
"Stereo Enphasis Filter OFF",
"Stereo Enphasis Filter ON",
};
static const struct soc_enum ak4954_stereo_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(stereo_on_select), stereo_on_select),
};
static int ak4954_writeMask(struct snd_soc_codec *, u16, u16, u16);
static inline u32 ak4954_read_reg_cache(struct snd_soc_codec *, u16);
static int get_ondrc(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = ak4954->onDrc;
return 0;
}
static int set_ondrc(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ak4954->onDrc = ucontrol->value.enumerated.item[0];
if ( ak4954->onDrc ) {
ak4954_writeMask(codec, AK4954_50_DRC_MODE_CONTROL, 0x3, 0x3);
ak4954_writeMask(codec, AK4954_60_DVLC_FILTER_SELECT, 0, 0x55);
}
else {
ak4954_writeMask(codec, AK4954_50_DRC_MODE_CONTROL, 0x3, 0x0);
ak4954_writeMask(codec, AK4954_60_DVLC_FILTER_SELECT, 0, 0x0);
}
return 0;
}
static int get_onstereo(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = ak4954->onStereo;
return 0;
}
static int set_onstereo(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
ak4954->onStereo = ucontrol->value.enumerated.item[0];
if ( ak4954->onStereo ) {
ak4954_writeMask(codec, AK4954_1C_DIGITAL_FILTER_SELECT2, 0x30, 0x30);
}
else {
ak4954_writeMask(codec, AK4954_1C_DIGITAL_FILTER_SELECT2, 0x30, 0x00);
}
return 0;
}
#ifdef AK4954_DEBUG
static const char *test_reg_select[] =
{
"read AK4954 Reg 00:2F",
"read AK4954 Reg 30:5F",
"read AK4954 Reg 60:8F",
};
static const struct soc_enum ak4954_enum[] =
{
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(test_reg_select), test_reg_select),
};
static int nTestRegNo = 0;
static int get_test_reg(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/* Get the current output routing */
ucontrol->value.enumerated.item[0] = nTestRegNo;
return 0;
}
static int set_test_reg(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
u32 currMode = ucontrol->value.enumerated.item[0];
int i, value;
int regs, rege;
nTestRegNo = currMode;
switch(nTestRegNo) {
case 1:
regs = 0x30;
rege = 0x5F;
break;
case 2:
regs = 0x60;
rege = 0x8F;
break;
default:
regs = 0x00;
rege = 0x2F;
break;
}
for ( i = regs ; i <= rege ; i++ ){
value = snd_soc_read(codec, i);
printk("***AK4954 Addr,Reg=(%x, %x)\n", i, value);
}
return(0);
}
#endif
static const struct snd_kcontrol_new ak4954_snd_controls[] = {
SOC_SINGLE_TLV("Mic Gain Control",
AK4954_02_SIGNAL_SELECT1, 0, 0x07, 0, mgain_tlv),
SOC_SINGLE_TLV("Input Digital Volume",
AK4954_0D_LCH_INPUT_VOLUME_CONTROL, 0, 0xF1, 0, ivol_tlv),
SOC_SINGLE_TLV("Speaker Output Volume",
AK4954_03_SIGNAL_SELECT2, 6, 0x03, 0, spkout_tlv),
SOC_SINGLE_TLV("Digital Output Volume",
AK4954_13_LCH_DIGITAL_VOLUME_CONTROL, 0, 0x90, 1, dvol_tlv),
SOC_SINGLE("High Path Filter 1", AK4954_1B_DIGITAL_FILTER_SELECT1, 1, 3, 0),
SOC_SINGLE("High Path Filter 2", AK4954_1C_DIGITAL_FILTER_SELECT2, 0, 1, 0),
SOC_SINGLE("Low Path Filter", AK4954_1C_DIGITAL_FILTER_SELECT2, 1, 1, 0),
SOC_SINGLE("5 Band Equalizer 1", AK4954_30_DIGITAL_FILTER_SELECT3, 0, 1, 0),
SOC_SINGLE("5 Band Equalizer 2", AK4954_30_DIGITAL_FILTER_SELECT3, 1, 1, 0),
SOC_SINGLE("5 Band Equalizer 3", AK4954_30_DIGITAL_FILTER_SELECT3, 2, 1, 0),
SOC_SINGLE("5 Band Equalizer 4", AK4954_30_DIGITAL_FILTER_SELECT3, 3, 1, 0),
SOC_SINGLE("5 Band Equalizer 5", AK4954_30_DIGITAL_FILTER_SELECT3, 4, 1, 0),
SOC_SINGLE("Auto Level Control 1", AK4954_0B_ALC_MODE_CONTROL1, 5, 1, 0),
SOC_SINGLE("Soft Mute Control", AK4954_07_MODE_CONTROL3, 5, 1, 0),
SOC_ENUM_EXT("DRC Control", ak4954_drc_enum[0], get_ondrc, set_ondrc),
SOC_ENUM_EXT("Stereo Enphasis Filter Control", ak4954_stereo_enum[0], get_onstereo, set_onstereo),
SOC_ENUM_EXT("Mic and Lin Switch",ak4954_micswitch_enum[0],get_micstatus,set_micstatus),
#ifdef AK4954_DEBUG
SOC_ENUM_EXT("Reg Read", ak4954_enum[0], get_test_reg, set_test_reg),
#endif
};
static const char *ak4954_lin_select_texts[] =
{"LIN1", "LIN2", "LIN3"};
static const struct soc_enum ak4954_lin_mux_enum =
SOC_ENUM_SINGLE(AK4954_03_SIGNAL_SELECT2, 2,
ARRAY_SIZE(ak4954_lin_select_texts), ak4954_lin_select_texts);
static const struct snd_kcontrol_new ak4954_lin_mux_control =
SOC_DAPM_ENUM("LIN Select", ak4954_lin_mux_enum);
static const char *ak4954_rin_select_texts[] =
{"RIN1", "RIN2", "RIN3"};
static const struct soc_enum ak4954_rin_mux_enum =
SOC_ENUM_SINGLE(AK4954_03_SIGNAL_SELECT2, 0,
ARRAY_SIZE(ak4954_rin_select_texts), ak4954_rin_select_texts);
static const struct snd_kcontrol_new ak4954_rin_mux_control =
SOC_DAPM_ENUM("RIN Select", ak4954_rin_mux_enum);
static const char *ak4954_lin1_select_texts[] =
{"LIN1", "Mic Bias"};
static const struct soc_enum ak4954_lin1_mux_enum =
SOC_ENUM_SINGLE(0, 0,
ARRAY_SIZE(ak4954_lin1_select_texts), ak4954_lin1_select_texts);
static const struct snd_kcontrol_new ak4954_lin1_mux_control =
SOC_DAPM_ENUM_VIRT("LIN1 Switch", ak4954_lin1_mux_enum);
static const char *ak4954_micbias_select_texts[] =
{"LIN1", "LIN2"};
static const struct soc_enum ak4954_micbias_mux_enum =
SOC_ENUM_SINGLE(AK4954_02_SIGNAL_SELECT1, 4,
ARRAY_SIZE(ak4954_micbias_select_texts), ak4954_micbias_select_texts);
static const struct snd_kcontrol_new ak4954_micbias_mux_control =
SOC_DAPM_ENUM("MIC bias Select", ak4954_micbias_mux_enum);
static const char *ak4954_spklo_select_texts[] =
{"Speaker", "Line"};
static const struct soc_enum ak4954_spklo_mux_enum =
SOC_ENUM_SINGLE(AK4954_01_POWER_MANAGEMENT2, 0,
ARRAY_SIZE(ak4954_spklo_select_texts), ak4954_spklo_select_texts);
static const struct snd_kcontrol_new ak4954_spklo_mux_control =
SOC_DAPM_ENUM("SPKLO Select", ak4954_spklo_mux_enum);
static const char *ak4954_adcpf_select_texts[] =
{"SDTI", "ADC"};
static const struct soc_enum ak4954_adcpf_mux_enum =
SOC_ENUM_SINGLE(AK4954_1D_DIGITAL_FILTER_MODE, 1,
ARRAY_SIZE(ak4954_adcpf_select_texts), ak4954_adcpf_select_texts);
static const struct snd_kcontrol_new ak4954_adcpf_mux_control =
SOC_DAPM_ENUM("ADCPF Select", ak4954_adcpf_mux_enum);
static const char *ak4954_pfsdo_select_texts[] =
{"ADC", "PFIL"};
static const struct soc_enum ak4954_pfsdo_mux_enum =
SOC_ENUM_SINGLE(AK4954_1D_DIGITAL_FILTER_MODE, 0,
ARRAY_SIZE(ak4954_pfsdo_select_texts), ak4954_pfsdo_select_texts);
static const struct snd_kcontrol_new ak4954_pfsdo_mux_control =
SOC_DAPM_ENUM("PFSDO Select", ak4954_pfsdo_mux_enum);
static const char *ak4954_pfdac_select_texts[] =
{"SDTI", "PFIL"};
static const struct soc_enum ak4954_pfdac_mux_enum =
SOC_ENUM_SINGLE(AK4954_1D_DIGITAL_FILTER_MODE, 2,
ARRAY_SIZE(ak4954_pfdac_select_texts), ak4954_pfdac_select_texts);
static const struct snd_kcontrol_new ak4954_pfdac_mux_control =
SOC_DAPM_ENUM("PFDAC Select", ak4954_pfdac_mux_enum);
static const char *ak4954_dac_select_texts[] =
{"PFDAC", "DRC"};
static const struct soc_enum ak4954_dac_mux_enum =
SOC_ENUM_SINGLE(AK4954_1D_DIGITAL_FILTER_MODE, 7,
ARRAY_SIZE(ak4954_dac_select_texts), ak4954_dac_select_texts);
static const struct snd_kcontrol_new ak4954_dac_mux_control =
SOC_DAPM_ENUM("DAC Select", ak4954_dac_mux_enum);
static const char *ak4954_mic_select_texts[] =
{"AMIC", "DMIC"};
static const struct soc_enum ak4954_mic_mux_enum =
SOC_ENUM_SINGLE(AK4954_08_DIGITL_MIC, 0,
ARRAY_SIZE(ak4954_mic_select_texts), ak4954_mic_select_texts);
static const struct snd_kcontrol_new ak4954_mic_mux_control =
SOC_DAPM_ENUM("MIC Select", ak4954_mic_mux_enum);
static const struct snd_kcontrol_new ak4954_dacsl_mixer_controls[] = {
SOC_DAPM_SINGLE("DACSL", AK4954_02_SIGNAL_SELECT1, 5, 1, 0),
};
static int ak4954_spklo_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) //CONFIG_LINF
{
struct snd_soc_codec *codec = w->codec;
u32 reg, nLOSEL;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
reg = ak4954_read_reg_cache(codec, AK4954_01_POWER_MANAGEMENT2);
nLOSEL = (0x1 & reg);
switch (event) {
case SND_SOC_DAPM_PRE_PMU: /* before widget power up */
break;
case SND_SOC_DAPM_POST_PMU: /* after widget power up */
if ( nLOSEL ) {
akdbgprt("\t[AK4954] %s wait=300msec\n",__FUNCTION__);
mdelay(300);
}
else {
akdbgprt("\t[AK4954] %s wait=1msec\n",__FUNCTION__);
mdelay(1);
}
snd_soc_update_bits(codec, AK4954_02_SIGNAL_SELECT1, 0x80,0x80);
break;
case SND_SOC_DAPM_PRE_PMD: /* before widget power down */
snd_soc_update_bits(codec, AK4954_02_SIGNAL_SELECT1, 0x80,0x00);
mdelay(1);
break;
case SND_SOC_DAPM_POST_PMD: /* after widget power down */
if ( nLOSEL ) {
akdbgprt("\t[AK4954] %s wait=300msec\n",__FUNCTION__);
mdelay(300);
}
break;
}
return 0;
}
static const struct snd_soc_dapm_widget ak4954_dapm_widgets[] = {
// ADC, DAC
SND_SOC_DAPM_ADC("ADC Left", "NULL", AK4954_00_POWER_MANAGEMENT1, 0, 0),
SND_SOC_DAPM_ADC("ADC Right", "NULL", AK4954_00_POWER_MANAGEMENT1, 1, 0),
SND_SOC_DAPM_DAC("DAC", "NULL", AK4954_00_POWER_MANAGEMENT1, 2, 0),
#ifdef PLL_32BICK_MODE
SND_SOC_DAPM_SUPPLY("PMPLL", AK4954_01_POWER_MANAGEMENT2, 2, 0, NULL, 0),
#else
#ifdef PLL_64BICK_MODE
SND_SOC_DAPM_SUPPLY("PMPLL", AK4954_01_POWER_MANAGEMENT2, 2, 0, NULL, 0),
#endif
#endif
SND_SOC_DAPM_ADC("PFIL", "NULL", AK4954_00_POWER_MANAGEMENT1, 7, 0),
SND_SOC_DAPM_DAC("DRC", "NULL", AK4954_1D_DIGITAL_FILTER_MODE, 7, 0),
SND_SOC_DAPM_ADC("DMICL", "NULL", AK4954_08_DIGITL_MIC, 4, 0),
SND_SOC_DAPM_ADC("DMICR", "NULL", AK4954_08_DIGITL_MIC, 5, 0),
SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0),
// Analog Output
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKLO"),
SND_SOC_DAPM_PGA("HPL Amp", AK4954_01_POWER_MANAGEMENT2, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPR Amp", AK4954_01_POWER_MANAGEMENT2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPK Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER_E("SPKLO Mixer", AK4954_01_POWER_MANAGEMENT2, 1, 0,
&ak4954_dacsl_mixer_controls[0], ARRAY_SIZE(ak4954_dacsl_mixer_controls),
ak4954_spklo_event, (SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_PRE_PMD
|SND_SOC_DAPM_PRE_PMU |SND_SOC_DAPM_POST_PMD)),
// Analog Input
SND_SOC_DAPM_INPUT("LIN1"),
SND_SOC_DAPM_INPUT("RIN1"),
SND_SOC_DAPM_INPUT("LIN2"),
SND_SOC_DAPM_INPUT("RIN2"),
SND_SOC_DAPM_INPUT("LIN3"),
SND_SOC_DAPM_INPUT("RIN3"),
SND_SOC_DAPM_MUX("LIN MUX", SND_SOC_NOPM, 0, 0, &ak4954_lin_mux_control),
SND_SOC_DAPM_MUX("RIN MUX", SND_SOC_NOPM, 0, 0, &ak4954_rin_mux_control),
// MIC Bias
SND_SOC_DAPM_MICBIAS("Mic Bias", AK4954_02_SIGNAL_SELECT1, 3, 0),
SND_SOC_DAPM_VIRT_MUX("LIN1 MUX", SND_SOC_NOPM, 0, 0, &ak4954_lin1_mux_control),
SND_SOC_DAPM_MUX("Mic Bias MUX", SND_SOC_NOPM, 0, 0, &ak4954_micbias_mux_control),
SND_SOC_DAPM_MUX("SPKLO MUX", SND_SOC_NOPM, 0, 0, &ak4954_spklo_mux_control),
// PFIL
SND_SOC_DAPM_MUX("PFIL MUX", SND_SOC_NOPM, 0, 0, &ak4954_adcpf_mux_control),
SND_SOC_DAPM_MUX("PFSDO MUX", SND_SOC_NOPM, 0, 0, &ak4954_pfsdo_mux_control),
SND_SOC_DAPM_MUX("PFDAC MUX", SND_SOC_NOPM, 0, 0, &ak4954_pfdac_mux_control),
SND_SOC_DAPM_MUX("DAC MUX", SND_SOC_NOPM, 0, 0, &ak4954_dac_mux_control),
// Digital Mic
SND_SOC_DAPM_INPUT("DMICLIN"),
SND_SOC_DAPM_INPUT("DMICRIN"),
SND_SOC_DAPM_VIRT_MUX("MIC MUX", SND_SOC_NOPM, 0, 0, &ak4954_mic_mux_control),
};
static const struct snd_soc_dapm_route ak4954_intercon[] = {
#ifdef PLL_32BICK_MODE
{"ADC Left", "NULL", "PMPLL"},
{"ADC Right", "NULL", "PMPLL"},
{"DAC", "NULL", "PMPLL"},
#else
#ifdef PLL_64BICK_MODE
{"ADC Left", "NULL", "PMPLL"},
{"ADC Right", "NULL", "PMPLL"},
{"DAC", "NULL", "PMPLL"},
#endif
#endif
{"Mic Bias MUX", "LIN1", "LIN1"},
{"Mic Bias MUX", "LIN2", "LIN2"},
{"Mic Bias", "NULL", "Mic Bias MUX"},
{"LIN1 MUX", "LIN1", "LIN1"},
{"LIN1 MUX", "Mic Bias", "Mic Bias"},
{"LIN MUX", "LIN1", "LIN1 MUX"},
{"LIN MUX", "LIN2", "Mic Bias"},
{"LIN MUX", "LIN3", "LIN3"},
{"RIN MUX", "RIN1", "RIN1"},
{"RIN MUX", "RIN2", "RIN2"},
{"RIN MUX", "RIN3", "RIN3"},
{"ADC Left", "NULL", "LIN MUX"},
{"ADC Right", "NULL", "RIN MUX"},
{"DMICL", "NULL", "DMICLIN"},
{"DMICR", "NULL", "DMICRIN"},
{"MIC MUX", "AMIC", "ADC Left"},
{"MIC MUX", "AMIC", "ADC Right"},
{"MIC MUX", "DMIC", "DMICL"},
{"MIC MUX", "DMIC", "DMICR"},
{"PFIL MUX", "SDTI", "SDTI"},
{"PFIL MUX", "ADC", "MIC MUX"},
{"PFIL", "NULL", "PFIL MUX"},
{"PFSDO MUX", "ADC", "MIC MUX"},
{"PFSDO MUX", "PFIL", "PFIL"},
{"SDTO", "NULL", "PFSDO MUX"},
{"PFDAC MUX", "SDTI", "SDTI"},
{"PFDAC MUX", "PFIL", "PFIL"},
{"DAC MUX", "PFDAC", "PFDAC MUX"},
{"DRC", "NULL", "PFDAC MUX"},
{"DAC MUX", "DRC", "DRC"},
{"DAC", "NULL", "DAC MUX"},
{"HPL Amp", "NULL", "DAC"},
{"HPR Amp", "NULL", "DAC"},
{"HPL", "NULL", "HPL Amp"},
{"HPR", "NULL", "HPR Amp"},
{"SPKLO Mixer", "DACSL", "DAC"},
{"SPK Amp", "NULL", "SPKLO Mixer"},
{"Line Amp", "NULL", "SPKLO Mixer"},
{"SPKLO MUX", "Speaker", "SPK Amp"},
{"SPKLO MUX", "Line", "Line Amp"},
{"SPKLO", "NULL", "SPKLO MUX"},
};
static int ak4954_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
int rate = params_rate(params);
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
int oversample = 0;
u8 fs = 0;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
oversample = ak4954->sysclk / rate;
switch (oversample) {
case 256:
fs &= (~AK4954_FS_CM1);
fs &= (~AK4954_FS_CM0);
break;
case 384:
fs &= (~AK4954_FS_CM1);
fs |= AK4954_FS_CM0;
break;
case 512:
fs |= AK4954_FS_CM1;
fs &= (~AK4954_FS_CM0);
break;
case 1024:
fs |= AK4954_FS_CM1;
fs |= AK4954_FS_CM0;
break;
default:
break;
}
switch (rate) {
case 8000:
fs |= AK4954_FS_8KHZ;
break;
case 11025:
fs |= AK4954_FS_11_025KHZ;
break;
case 12000:
fs |= AK4954_FS_12KHZ;
break;
case 16000:
fs |= AK4954_FS_16KHZ;
break;
case 22050:
fs |= AK4954_FS_22_05KHZ;
break;
case 24000:
fs |= AK4954_FS_24KHZ;
break;
case 32000:
fs |= AK4954_FS_32KHZ;
break;
case 44100:
fs |= AK4954_FS_44_1KHZ;
break;
case 48000:
fs |= AK4954_FS_48KHZ;
break;
case 64000:
fs |= AK4954_FS_64KHZ;
break;
case 88000:
fs |= AK4954_FS_88_2KHZ;
break;
case 96000:
fs |= AK4954_FS_96KHZ;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, AK4954_06_MODE_CONTROL2, fs);
return 0;
}
static int ak4954_set_pll(u8 *pll, int clk_id,int freq)
{
if (clk_id == AK4954_MCLK_IN_BCLK_OUT){
switch (freq) {
case 11289600:
*pll |= (2 << 4);
break;
case 12288000:
*pll |= (3 << 4);
break;
case 12000000:
*pll |= (4 << 4);
break;
case 24000000:
*pll |= (5 << 4);
break;
case 13500000:
*pll |= (6 << 4);
break;
case 27000000:
*pll |= (7 << 4);
break;
default:
break;
}
}else if (clk_id == AK4954_BCLK_IN) {
*pll |= (0 << 4);
}
return 0;
}
static int ak4954_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
u8 pllpwr = 0, pll = 0;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
pll= snd_soc_read(codec, AK4954_05_MODE_CONTROL1);
pll &=(~0x70);
pllpwr = snd_soc_read(codec,AK4954_01_POWER_MANAGEMENT2);
pllpwr &=(~0x0c);
if (clk_id == AK4954_MCLK_IN) {
pllpwr &= (~AK4954_PMPLL);
pllpwr &= (~AK4954_M_S);
}else if (clk_id == AK4954_BCLK_IN) {
pllpwr |= AK4954_PMPLL;
pllpwr &= (~AK4954_M_S);
ak4954_set_pll(&pll, clk_id, freq);
}else if (clk_id == AK4954_MCLK_IN_BCLK_OUT) {
pllpwr |= AK4954_PMPLL;
pllpwr |= AK4954_M_S;
ak4954_set_pll(&pll, clk_id, freq);
}
snd_soc_write(codec, AK4954_05_MODE_CONTROL1, pll);
snd_soc_write(codec, AK4954_01_POWER_MANAGEMENT2, pllpwr);
ak4954->sysclk = freq;
ak4954->clkid = clk_id;
return 0;
}
static int ak4954_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
u8 mode;
u8 format;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
/* set master/slave audio interface */
mode = snd_soc_read(codec, AK4954_01_POWER_MANAGEMENT2);
format = snd_soc_read(codec, AK4954_05_MODE_CONTROL1);
format &= ~AK4954_DIF;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
akdbgprt("\t[AK4954] %s(Slave)\n",__FUNCTION__);
mode &= ~(AK4954_M_S);
//format &= ~(AK4954_BCKO);
break;
case SND_SOC_DAIFMT_CBM_CFM:
akdbgprt("\t[AK4954] %s(Master)\n",__FUNCTION__);
mode |= (AK4954_M_S);
//format |= (AK4954_BCKO);
break;
case SND_SOC_DAIFMT_CBS_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
default:
dev_err(codec->dev, "Clock mode unsupported");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
format |= AK4954_DIF_24_16_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
format |= AK4954_DIF_24MSB_MODE;
break;
default:
return -EINVAL;
}
/* set mode and format */
snd_soc_write(codec, AK4954_01_POWER_MANAGEMENT2, mode);
snd_soc_write(codec, AK4954_05_MODE_CONTROL1, format);
return 0;
}
static int ak4954_volatile(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
switch (reg) {
// case :
// ret = 1;
default:
ret = 0;
break;
}
return(ret);
}
static int ak4954_readable(struct snd_soc_codec *codec, unsigned int reg)
{
if (reg >= ARRAY_SIZE(ak4954_access_masks))
return 0;
return ak4954_access_masks[reg].readable != 0;
}
/*
* Read ak4954 register cache
*/
static inline u32 ak4954_read_reg_cache(struct snd_soc_codec *codec, u16 reg)
{
u8 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(ak4954_reg));
return (u32)cache[reg];
}
#ifdef AK4954_CONTIF_DEBUG
/*
* Write ak4954 register cache
*/
static inline void ak4954_write_reg_cache(
struct snd_soc_codec *codec,
u16 reg,
u16 value)
{
u8 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(ak4954_reg));
cache[reg] = (u8)value;
}
unsigned int ak4954_i2c_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
if ( reg == AK4954_10_RESERVED ) { // Dummy Register
ret = ak4954_read_reg_cache(codec, reg);
return ret;
}
ret = i2c_smbus_read_byte_data(codec->control_data, (u8)(reg & 0xFF));
// ret = ak4954_read_reg_cache(codec, reg);
// if ( reg < 3 )
// akdbgprt("\t[ak4954] %s: (addr,data)=(%x, %x)\n",__FUNCTION__, reg, ret);
if (ret < 0)
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
return ret;
}
static int ak4954_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
ak4954_write_reg_cache(codec, reg, value);
akdbgprt("\t[ak4954] %s: (addr,data)=(%x, %x)\n",__FUNCTION__, reg, value);
if ( reg == AK4954_10_RESERVED ) return 0; // Dummy Register
if(i2c_smbus_write_byte_data(codec->control_data, (u8)(reg & 0xFF), (u8)(value & 0xFF))<0) {
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
return EIO;
}
return 0;
}
#endif
/*
* Write with Mask to AK4954 register space
*/
static int ak4954_writeMask(
struct snd_soc_codec *codec,
u16 reg,
u16 mask,
u16 value)
{
u16 olddata;
u16 newdata;
if ( (mask == 0) || (mask == 0xFF) ) {
newdata = value;
}
else {
olddata = ak4954_read_reg_cache(codec, reg);
newdata = (olddata & ~(mask)) | value;
}
snd_soc_write(codec, (unsigned int)reg, (unsigned int)newdata);
akdbgprt("\t[ak4954_writeMask] %s(%d): (addr,data)=(%x, %x)\n",__FUNCTION__,__LINE__, reg, newdata);
return(0);
}
// * for AK4954
static int ak4954_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *codec_dai)
{
int ret = 0;
// struct snd_soc_codec *codec = codec_dai->codec;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
return ret;
}
static int ak4954_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u8 reg;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
reg = snd_soc_read(codec, AK4954_00_POWER_MANAGEMENT1); // * for AK4954
snd_soc_write(codec, AK4954_00_POWER_MANAGEMENT1, // * for AK4954
reg | AK4954_PMVCM);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, AK4954_00_POWER_MANAGEMENT1, 0x00); // * for AK4954
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AK4954_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000)
#define AK4954_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static struct snd_soc_dai_ops ak4954_dai_ops = {
.hw_params = ak4954_hw_params,
.set_sysclk = ak4954_set_dai_sysclk,
.set_fmt = ak4954_set_dai_fmt,
.trigger = ak4954_trigger,
};
struct snd_soc_dai_driver ak4954_dai[] = {
{
.name = "ak4954-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4954_RATES,
.formats = AK4954_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4954_RATES,
.formats = AK4954_FORMATS,
},
.ops = &ak4954_dai_ops,
},
};
static int ak4954_write_cache_reg(
struct snd_soc_codec *codec,
u16 regs,
u16 rege)
{
u32 reg, cache_data;
reg = regs;
do {
cache_data = ak4954_read_reg_cache(codec, reg);
snd_soc_write(codec, (unsigned int)reg, (unsigned int)cache_data);
reg ++;
} while (reg <= rege);
return(0);
}
static int ak4954_set_reg_digital_effect(struct snd_soc_codec *codec)
{
ak4954_write_cache_reg(codec, AK4954_0A_ALC_TIMER_SELECT, AK4954_0D_LCH_INPUT_VOLUME_CONTROL);
ak4954_write_cache_reg(codec, AK4954_22_LPF_COEFFICIENT0, AK4954_2F_EQ_COEFFICIENT5);
ak4954_write_cache_reg(codec, AK4954_32_E1_COEFFICIENT0, AK4954_4F_E5_COEFFICIENT5);
ak4954_write_cache_reg(codec, AK4954_50_DRC_MODE_CONTROL, AK4954_8F_DVLCH_HPF_COEFFICIENT3);
return(0);
}
static int ak4954_probe(struct snd_soc_codec *codec)
{
struct ak4954_priv *ak4954 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
#ifdef AK4954_CONTIF_DEBUG
codec->write = ak4954_i2c_write;
codec->read = ak4954_i2c_read;
#endif
ak4954_codec = codec;
akdbgprt("\t[AK4954] %s(%d) ak4954=%x\n",__FUNCTION__,__LINE__, (int)ak4954);
snd_soc_write(codec, AK4954_00_POWER_MANAGEMENT1, 0x00);
snd_soc_write(codec, AK4954_00_POWER_MANAGEMENT1, 0x00);
ak4954_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
akdbgprt("\t[AK4954 bias] %s(%d)\n",__FUNCTION__,__LINE__);
ak4954_set_reg_digital_effect(codec);
akdbgprt("\t[AK4954 Effect] %s(%d)\n",__FUNCTION__,__LINE__);
snd_soc_add_codec_controls(codec, ak4954_snd_controls,
ARRAY_SIZE(ak4954_snd_controls));
ak4954->onDrc = 0;
ak4954->onStereo = 0;
ak4954->mic = 1;
/*Enable Line out */
snd_soc_update_bits(codec,AK4954_01_POWER_MANAGEMENT2,0x02,0x02);
snd_soc_update_bits(codec,AK4954_02_SIGNAL_SELECT1, 0xa0,0xa0);
/*Enable LIN2*/
snd_soc_update_bits(codec,AK4954_02_SIGNAL_SELECT1,0x18,0x08);//MPWR1
snd_soc_update_bits(codec,AK4954_03_SIGNAL_SELECT2,0x0f,0x05);// LIN2 RIN2
snd_soc_update_bits(codec,AK4954_08_DIGITL_MIC,0x01,0x00);//AMIC
snd_soc_update_bits(codec,AK4954_1D_DIGITAL_FILTER_MODE,0x02,0x02);//ADC output
snd_soc_update_bits(codec,AK4954_1D_DIGITAL_FILTER_MODE,0x01,0x01);//ALC output
snd_soc_update_bits(codec,AK4954_02_SIGNAL_SELECT1,0x07,0x3);//Mic Gain
snd_soc_update_bits(codec,AK4954_0D_LCH_INPUT_VOLUME_CONTROL,0xff,0xb0);//Lch gain
snd_soc_update_bits(codec,AK4954_0E_RCH_INPUT_VOLUME_CONTROL,0xff,0xb0);//Lch gain
/*Enable LIN3*/
//snd_soc_update_bits(codec,AK4954_03_SIGNAL_SELECT2,0x0f,0x0a);// LIN3 RIN3
return ret;
}
static int ak4954_remove(struct snd_soc_codec *codec)
{
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
ak4954_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ak4954_suspend(struct snd_soc_codec *codec)
{
ak4954_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ak4954_resume(struct snd_soc_codec *codec)
{
ak4954_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
struct snd_soc_codec_driver soc_codec_dev_ak4954 = {
.probe = ak4954_probe,
.remove = ak4954_remove,
.suspend = ak4954_suspend,
.resume = ak4954_resume,
.set_bias_level = ak4954_set_bias_level,
.reg_cache_size = ARRAY_SIZE(ak4954_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4954_reg,
.readable_register = ak4954_readable,
.volatile_register = ak4954_volatile,
.dapm_widgets = ak4954_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4954_dapm_widgets),
.dapm_routes = ak4954_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4954_intercon),
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ak4954);
static int ak4954_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device_node *np = i2c->dev.of_node;
struct ak4954_priv *ak4954;
enum of_gpio_flags flags;
int rst_pin;
struct snd_soc_codec *codec;
int ret=0;
akdbgprt("\t[AK4954] %s(%d)\n",__FUNCTION__,__LINE__);
ak4954 = devm_kzalloc(&i2c->dev, sizeof(struct ak4954_priv), GFP_KERNEL);
if (ak4954 == NULL)
return -ENOMEM;
rst_pin = of_get_gpio_flags(np, 0, &flags);
if (rst_pin < 0 || !gpio_is_valid(rst_pin))
return -ENXIO;
ak4954->rst_pin = rst_pin;
ak4954->rst_active = !!(flags & OF_GPIO_ACTIVE_LOW);
codec = &ak4954->codec;
i2c_set_clientdata(i2c, ak4954);
codec->control_data = i2c;
ak4954_data = ak4954;
codec->dev = &i2c->dev;
snd_soc_codec_set_drvdata(codec, ak4954);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ak4954, &ak4954_dai[0], ARRAY_SIZE(ak4954_dai));
if (ret < 0){
kfree(ak4954);
akdbgprt("\t[AK4954 Error!] %s(%d)\n",__FUNCTION__,__LINE__);
}
return ret;
}
static int ak4954_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static struct of_device_id ak4954_of_match[] = {
{ .compatible = "ambarella,ak4954",},
{},
};
MODULE_DEVICE_TABLE(of, ak4954_of_match);
static const struct i2c_device_id ak4954_i2c_id[] = {
{ "ak4954", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4954_i2c_id);
static struct i2c_driver ak4954_i2c_driver = {
.driver = {
.name = "ak4954-codec",
.owner = THIS_MODULE,
.of_match_table = ak4954_of_match,
},
.probe = ak4954_i2c_probe,
.remove = ak4954_i2c_remove,
.id_table = ak4954_i2c_id,
};
static int __init ak4954_modinit(void)
{
int ret;
akdbgprt("\t[AK4954] %s(%d)\n", __FUNCTION__,__LINE__);
ret = i2c_add_driver(&ak4954_i2c_driver);
if (ret != 0)
pr_err("Failed to register ak4954 I2C driver: %d\n",ret);
return ret;
}
module_init(ak4954_modinit);
static void __exit ak4954_exit(void)
{
i2c_del_driver(&ak4954_i2c_driver);
}
module_exit(ak4954_exit);
MODULE_DESCRIPTION("Soc AK4954 driver");
MODULE_AUTHOR("Diao Chengdong<cddiao@ambarella.com>");
MODULE_LICENSE("GPL");