Rtwo/kernel/motorola/sm8550/sound/soc/codecs/add2010.c
2025-09-30 19:22:48 -05:00

1456 lines
50 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* add2010.c -- add2010 ALSA SoC Audio driver
*
* Copyright 1998 Elite Semiconductor Memory Technology
*
* Author: ESMT Audio/Power Product BU Team
*/
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/device.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/tlv.h>
#include <sound/initval.h>
#include <linux/regmap.h>
#include "add2010.h"
// after enable,you can use "tinymix" or "amixer" cmd to change eq mode.
//#define ADD2010_CHANGE_EQ_MODE_EN
static int add2010_codec_probe(struct snd_soc_component *component);
#ifdef ADD2010_CHANGE_EQ_MODE_EN
// eq file, add Parameter file in "codec\add2010_eq", and then include the header file.
#include "add2010_eq/add2010_eq_mode_1.h"
#include "add2010_eq/add2010_eq_mode_2.h"
// the value range of eq mode, modify them according to your request.
#define ADD2010_EQ_MODE_MIN 1
#define ADD2010_EQ_MODE_MAX 2
#endif
/* Define how often to check (and clear) the fault status register (in ms) */
#define ADD2010_FAULT_CHECK_INTERVAL 500
// you can read out the register and ram data, and check them.
//#define ADD2010_REG_RAM_CHECK
enum add2010_type {
ADD2010,
};
static const char * const add2010_supply_names[] = {
"dvdd", /* Digital power supply. Connect to 3.3-V supply. */
"pvdd", /* Class-D amp and analog power supply (connected). */
};
#define ADD2010_NUM_SUPPLIES ARRAY_SIZE(add2010_supply_names)
static int m_reg_tab[ADD2010_REGISTER_COUNT][2] = {
{0x00, 0x00},//##State_Control_1
{0x01, 0x91},//##State_Control_2
{0x02, 0x00},//##State_Control_3
{0x03, 0x1c},//##Master_volume_control
{0x04, 0x18},//##Channel_1_volume_control
{0x05, 0x18},//##Channel_2_volume_control
{0x06, 0x18},//##Channel_3_volume_control
{0x07, 0x18},//##Channel_4_volume_control
{0x08, 0x18},//##Channel_5_volume_control
{0x09, 0x18},//##Channel_6_volume_control
{0x0a, 0x10},//##Bass_Tone_Boost_and_Cut
{0x0b, 0x10},//##Treble_Tone_Boost_and_Cut
{0x0c, 0x90},//##State_Control_4
{0x0d, 0x00},//##Channel_1_configuration_registers
{0x0e, 0x00},//##Channel_2_configuration_registers
{0x0f, 0x00},//##Channel_3_configuration_registers
{0x10, 0x00},//##Channel_4_configuration_registers
{0x11, 0x00},//##Channel_5_configuration_registers
{0x12, 0x00},//##Channel_6_configuration_registers
{0x13, 0x00},//##Channel_7_configuration_registers
{0x14, 0x00},//##Channel_8_configuration_registers
{0x15, 0x6a},//##DRC1_limiter_attack/release_rate
{0x16, 0x6a},//##DRC2_limiter_attack/release_rate
{0x17, 0x6a},//##DRC3_limiter_attack/release_rate
{0x18, 0x6a},//##DRC4_limiter_attack/release_rate
{0x19, 0x10},//##State_Control_6
{0x1a, 0x70},//##State_Control_7
{0x1b, 0x00},//##State_Control_8
{0x1c, 0x00},//##State_Control_9
{0x1d, 0x00},//##Coefficient_RAM_Base_Address
{0x1e, 0x00},//##Top_8-bits_of_coefficients_A1
{0x1f, 0x00},//##Middle_8-bits_of_coefficients_A1
{0x20, 0x00},//##Bottom_8-bits_of_coefficients_A1
{0x21, 0x00},//##Top_8-bits_of_coefficients_A2
{0x22, 0x00},//##Middle_8-bits_of_coefficients_A2
{0x23, 0x00},//##Bottom_8-bits_of_coefficients_A2
{0x24, 0x00},//##Top_8-bits_of_coefficients_B1
{0x25, 0x00},//##Middle_8-bits_of_coefficients_B1
{0x26, 0x00},//##Bottom_8-bits_of_coefficients_B1
{0x27, 0x00},//##Top_8-bits_of_coefficients_B2
{0x28, 0x00},//##Middle_8-bits_of_coefficients_B2
{0x29, 0x00},//##Bottom_8-bits_of_coefficients_B2
{0x2a, 0x40},//##Top_8-bits_of_coefficients_A0
{0x2b, 0x00},//##Middle_8-bits_of_coefficients_A0
{0x2c, 0x00},//##Bottom_8-bits_of_coefficients_A0
{0x2d, 0x00},//##Coefficient_RAM_R/W_control
{0x2e, 0x00},//##Protection_Enable/Disable
{0x2f, 0x00},//##Memory_BIST_status
{0x30, 0x00},//##Memory_Test_Tor_Sensing_Time
{0x31, 0x00},//##Scan_key
{0x32, 0x00},//##Test_Mode_Control_Reg
{0x33, 0x00},//####Test_Mode_2_Control_Reg
{0x34, 0x00},//##Volume_Fine_tune
{0x35, 0x00},//##Volume_Fine_tune
{0x36, 0x40},//##HP_Control
{0x37, 0xc8},//##Device_ID_register
{0x38, 0x00},//##RAM1_test_register_address
{0x39, 0x00},//##Top_8-bits_of_RAM1_Data
{0x3a, 0x00},//##Middle_8-bits_of_RAM1_Data
{0x3b, 0x00},//##Bottom_8-bits_of_RAM1_Data
{0x3c, 0x00},//##RAM1_test_r/w_control
{0x3d, 0x00},//##RAM3_test_register_address
{0x3e, 0x00},//##Top_8-bits_of_RAM3_Data
{0x3f, 0x00},//##Middle_8-bits_of_RAM3_Data
{0x40, 0x00},//##Bottom_8-bits_of_RAM3_Data
{0x41, 0x00},//##RAM3_test_r/w_control
{0x42, 0x00},//##Level_Meter_Clear
{0x43, 0x00},//##Power_Meter_Clear
{0x44, 0x00},//##TOP_of_C1_Level_Meter
{0x45, 0x31},//##Middle_of_C1_Level_Meter
{0x46, 0xf6},//##Bottom_of_C1_Level_Meter
{0x47, 0x00},//##TOP_of_C2_Level_Meter
{0x48, 0x32},//##Middle_of_C2_Level_Meter
{0x49, 0x46},//##Bottom_of_C2_Level_Meter
{0x4a, 0x00},//##TOP_of_C3_Level_Meter
{0x4b, 0x00},//##Middle_of_C3_Level_Meter
{0x4c, 0x00},//##Bottom_of_C3_Level_Meter
{0x4d, 0x00},//##TOP_of_C4_Level_Meter
{0x4e, 0x00},//##Middle_of_C4_Level_Meter
{0x4f, 0x00},//##Bottom_of_C4_Level_Meter
{0x50, 0x00},//##TOP_of_C5_Level_Meter
{0x51, 0x00},//##Middle_of_C5_Level_Meter
{0x52, 0x00},//##Bottom_of_C5_Level_Meter
{0x53, 0x00},//##TOP_of_C6_Level_Meter
{0x54, 0x00},//##Middle_of_C6_Level_Meter
{0x55, 0x00},//##Bottom_of_C6_Level_Meter
{0x56, 0x00},//##TOP_of_C7_Level_Meter
{0x57, 0x00},//##Middle_of_C7_Level_Meter
{0x58, 0x00},//##Bottom_of_C7_Level_Meter
{0x59, 0x00},//##TOP_of_C8_Level_Meter
{0x5a, 0x00},//##Middle_of_C8_Level_Meter
{0x5b, 0x00},//##Bottom_of_C8_Level_Meter
{0x5c, 0x05},//##I2S_Data_Output_Selection_Register
{0x5d, 0x00},//##Check_Status
{0x5e, 0x00},//##Top_8_bits_of_DRC_CHK_set_value
{0x5f, 0x00},//##Middle_8_bits_of_DRC_CHK_set_value
{0x60, 0x00},//##Bottom_8_bits_of_DRC_CHK_set_value
{0x61, 0x00},//##Top_8_bits_of_BEQ_CHK_set_value
{0x62, 0x00},//##Middle_8_bits_of_BEQ_CHK_set_value
{0x63, 0x00},//##Bottom_8_bits_of_BEQ_CHK_set_value
{0x64, 0x00},//##Top_8_bits_of_DRC_CHK_result
{0x65, 0x00},//##Middle_8_bits_of_DRC_CHK_result
{0x66, 0x00},//##Bottom_8_bits_of_DRC_CHK_result
{0x67, 0x00},//##Top_8_bits_of_BEQ_CHK_result
{0x68, 0x00},//##Middle_8_bits_of_BEQ_CHK_result
{0x69, 0x00},//##Bottom_8_bits_of_BEQ_CHK_result
{0x6a, 0x00},//##Reserve
{0x6b, 0x00},//##Reserve
{0x6c, 0x00},//##Reserve
{0x6d, 0x00},//##Reserve
{0x6e, 0x00},//##Reserve
{0x6f, 0x00},//##Reserve
{0x70, 0x78},//##Dither_Signal_Setting
{0x71, 0x00},//##Reserve
{0x72, 0x00},//##Reserve
{0x73, 0x00},//##Reserve
{0x74, 0x00},//##Mono_Key_High_Byte
{0x75, 0x00},//##Mono_Key_Low_Byte
{0x76, 0x00},//##Reserve
{0x77, 0x07},//##Hi-res_Item
{0x78, 0xfc},//##Test_Mode_register
{0x79, 0x58},//##VOS_Control_and_IDAC_VN_LPF_Setting
{0x7a, 0x00},//##Reserve
{0x7b, 0x55},//##MBIST_User_Program_Top_Byte_Even
{0x7c, 0x55},//##MBIST_User_Program_Middle_Byte_Even
{0x7d, 0x55},//##MBIST_User_Program_Bottom_Byte_Even
{0x7e, 0x55},//##MBIST_User_Program_Top_Byte_Odd
{0x7f, 0x55},//##MBIST_User_Program_Middle_Byte_Odd
{0x80, 0x55},//##MBIST_User_Program_Bottom_Byte_Odd
{0x81, 0x00},//##GPIO0_Control
{0x82, 0x00},//##GPIO1_Control
{0x83, 0x00},//##GPIO2_Control
{0x84, 0xc4},//##ERROR_Register_Read_only
{0x85, 0xc4},//##ERROR_Latch_Register_Read_only
{0x86, 0x00},//##ERROR_Clear_Register
{0x87, 0xff},//##HP_Master_Volume
{0x88, 0x18},//##HP_Channel_1_Volume
{0x89, 0x18},//##HP_Channel_2_Volume
{0x8a, 0x18},//##HP_Channel_3_Volume
{0x8b, 0x18},//##HP_Channel_4_Volume
{0x8c, 0x18},//##HP_Channel_5_Volume
{0x8d, 0x18},//##HP_Channel_6_Volume
{0x8e, 0x00},//##HP_Volume_Fine_Tune_1
{0x8f, 0x00},//##HP_Volume_Fine_Tune_2
{0x90, 0x6a},//##SMB_Left_DRC_A/R_rate
{0x91, 0x6a},//##SMB_Right_DRC_A/R_rate
{0x92, 0x00},//##RAM2_Test_egister_Address
{0x93, 0x00},//##Top_8-bits_of_RAM2_Data
{0x94, 0x00},//##Middle_8-bits_of_RAM2_Data
{0x95, 0x00},//##Bottom_8-bits_of_RAM2_Data
{0x96, 0x00},//##RAM2_test_r/w_control
{0x97, 0x00},//##RAM4_Test_egister_Address
{0x98, 0x00},//##Top_8-bits_of_RAM4_Data
{0x99, 0x00},//##Middle_8-bits_of_RAM4_Data
{0x9a, 0x00},//##Bottom_8-bits_of_RAM4_Data
{0x9b, 0x00},//##RAM4_test_r/w_control
};
static int m_ram1_tab[][4] = {
{0x00, 0x00, 0x00, 0x00},//##Channel_1_EQ1_A1
{0x01, 0x00, 0x00, 0x00},//##Channel_1_EQ1_A2
{0x02, 0x00, 0x00, 0x00},//##Channel_1_EQ1_B1
{0x03, 0x00, 0x00, 0x00},//##Channel_1_EQ1_B2
{0x04, 0x20, 0x00, 0x00},//##Channel_1_EQ1_A0
{0x05, 0x00, 0x00, 0x00},//##Channel_1_EQ2_A1
{0x06, 0x00, 0x00, 0x00},//##Channel_1_EQ2_A2
{0x07, 0x00, 0x00, 0x00},//##Channel_1_EQ2_B1
{0x08, 0x00, 0x00, 0x00},//##Channel_1_EQ2_B2
{0x09, 0x20, 0x00, 0x00},//##Channel_1_EQ2_A0
{0x0a, 0x00, 0x00, 0x00},//##Channel_1_EQ3_A1
{0x0b, 0x00, 0x00, 0x00},//##Channel_1_EQ3_A2
{0x0c, 0x00, 0x00, 0x00},//##Channel_1_EQ3_B1
{0x0d, 0x00, 0x00, 0x00},//##Channel_1_EQ3_B2
{0x0e, 0x20, 0x00, 0x00},//##Channel_1_EQ3_A0
{0x0f, 0x00, 0x00, 0x00},//##Channel_1_EQ4_A1
{0x10, 0x00, 0x00, 0x00},//##Channel_1_EQ4_A2
{0x11, 0x00, 0x00, 0x00},//##Channel_1_EQ4_B1
{0x12, 0x00, 0x00, 0x00},//##Channel_1_EQ4_B2
{0x13, 0x20, 0x00, 0x00},//##Channel_1_EQ4_A0
{0x14, 0x00, 0x00, 0x00},//##Channel_1_EQ5_A1
{0x15, 0x00, 0x00, 0x00},//##Channel_1_EQ5_A2
{0x16, 0x00, 0x00, 0x00},//##Channel_1_EQ5_B1
{0x17, 0x00, 0x00, 0x00},//##Channel_1_EQ5_B2
{0x18, 0x20, 0x00, 0x00},//##Channel_1_EQ5_A0
{0x19, 0x00, 0x00, 0x00},//##Channel_1_EQ6_A1
{0x1a, 0x00, 0x00, 0x00},//##Channel_1_EQ6_A2
{0x1b, 0x00, 0x00, 0x00},//##Channel_1_EQ6_B1
{0x1c, 0x00, 0x00, 0x00},//##Channel_1_EQ6_B2
{0x1d, 0x20, 0x00, 0x00},//##Channel_1_EQ6_A0
{0x1e, 0x00, 0x00, 0x00},//##Channel_1_EQ7_A1
{0x1f, 0x00, 0x00, 0x00},//##Channel_1_EQ7_A2
{0x20, 0x00, 0x00, 0x00},//##Channel_1_EQ7_B1
{0x21, 0x00, 0x00, 0x00},//##Channel_1_EQ7_B2
{0x22, 0x20, 0x00, 0x00},//##Channel_1_EQ7_A0
{0x23, 0x00, 0x00, 0x00},//##Channel_1_EQ8_A1
{0x24, 0x00, 0x00, 0x00},//##Channel_1_EQ8_A2
{0x25, 0x00, 0x00, 0x00},//##Channel_1_EQ8_B1
{0x26, 0x00, 0x00, 0x00},//##Channel_1_EQ8_B2
{0x27, 0x20, 0x00, 0x00},//##Channel_1_EQ8_A0
{0x28, 0x00, 0x00, 0x00},//##Channel_1_EQ9_A1
{0x29, 0x00, 0x00, 0x00},//##Channel_1_EQ9_A2
{0x2a, 0x00, 0x00, 0x00},//##Channel_1_EQ9_B1
{0x2b, 0x00, 0x00, 0x00},//##Channel_1_EQ9_B2
{0x2c, 0x20, 0x00, 0x00},//##Channel_1_EQ9_A0
{0x2d, 0x00, 0x00, 0x00},//##Channel_1_EQ10_A1
{0x2e, 0x00, 0x00, 0x00},//##Channel_1_EQ10_A2
{0x2f, 0x00, 0x00, 0x00},//##Channel_1_EQ10_B1
{0x30, 0x00, 0x00, 0x00},//##Channel_1_EQ10_B2
{0x31, 0x20, 0x00, 0x00},//##Channel_1_EQ10_A0
{0x32, 0x00, 0x00, 0x00},//##Channel_1_EQ11_A1
{0x33, 0x00, 0x00, 0x00},//##Channel_1_EQ11_A2
{0x34, 0x00, 0x00, 0x00},//##Channel_1_EQ11_B1
{0x35, 0x00, 0x00, 0x00},//##Channel_1_EQ11_B2
{0x36, 0x20, 0x00, 0x00},//##Channel_1_EQ11_A0
{0x37, 0x00, 0x00, 0x00},//##Channel_1_EQ12_A1
{0x38, 0x00, 0x00, 0x00},//##Channel_1_EQ12_A2
{0x39, 0x00, 0x00, 0x00},//##Channel_1_EQ12_B1
{0x3a, 0x00, 0x00, 0x00},//##Channel_1_EQ12_B2
{0x3b, 0x20, 0x00, 0x00},//##Channel_1_EQ12_A0
{0x3c, 0x00, 0x00, 0x00},//##Channel_1_EQ13_A1
{0x3d, 0x00, 0x00, 0x00},//##Channel_1_EQ13_A2
{0x3e, 0x00, 0x00, 0x00},//##Channel_1_EQ13_B1
{0x3f, 0x00, 0x00, 0x00},//##Channel_1_EQ13_B2
{0x40, 0x20, 0x00, 0x00},//##Channel_1_EQ13_A0
{0x41, 0x00, 0x00, 0x00},//##Channel_1_EQ14_A1
{0x42, 0x00, 0x00, 0x00},//##Channel_1_EQ14_A2
{0x43, 0x00, 0x00, 0x00},//##Channel_1_EQ14_B1
{0x44, 0x00, 0x00, 0x00},//##Channel_1_EQ14_B2
{0x45, 0x20, 0x00, 0x00},//##Channel_1_EQ14_A0
{0x46, 0x00, 0x00, 0x00},//##Channel_1_EQ15_A1
{0x47, 0x00, 0x00, 0x00},//##Channel_1_EQ15_A2
{0x48, 0x00, 0x00, 0x00},//##Channel_1_EQ15_B1
{0x49, 0x00, 0x00, 0x00},//##Channel_1_EQ15_B2
{0x4a, 0x20, 0x00, 0x00},//##Channel_1_EQ15_A0
{0x4b, 0x7f, 0xff, 0xff},//##Channel_1_Mixer1
{0x4c, 0x00, 0x00, 0x00},//##Channel_1_Mixer2
{0x4d, 0x07, 0xe8, 0x8e},//##Channel_1_Prescale
{0x4e, 0x20, 0x00, 0x00},//##Channel_1_Postscale
{0x4f, 0xc7, 0xb6, 0x91},//##A0_of_L_channel_SRS_HPF
{0x50, 0x38, 0x49, 0x6e},//##A1_of_L_channel_SRS_HPF
{0x51, 0x0c, 0x46, 0xf8},//##B1_of_L_channel_SRS_HPF
{0x52, 0x0e, 0x81, 0xb9},//##A0_of_L_channel_SRS_LPF
{0x53, 0xf2, 0x2c, 0x12},//##A1_of_L_channel_SRS_LPF
{0x54, 0x0f, 0xca, 0xbb},//##B1_of_L_channel_SRS_LPF
{0x55, 0x20, 0x00, 0x00},//##CH1.2_Power_Clipping
{0x56, 0x20, 0x00, 0x00},//##CCH1.2_DRC1_Attack_threshold
{0x57, 0x08, 0x00, 0x00},//##CH1.2_DRC1_Release_threshold
{0x58, 0x20, 0x00, 0x00},//##CH3.4_DRC2_Attack_threshold
{0x59, 0x08, 0x00, 0x00},//##CH3.4_DRC2_Release_threshold
{0x5a, 0x20, 0x00, 0x00},//##CH5.6_DRC3_Attack_threshold
{0x5b, 0x08, 0x00, 0x00},//##CH5.6_DRC3_Release_threshold
{0x5c, 0x20, 0x00, 0x00},//##CH7.8_DRC4_Attack_threshold
{0x5d, 0x08, 0x00, 0x00},//##CH7.8_DRC4_Release_threshold
{0x5e, 0x00, 0x00, 0x1a},//##Noise_Gate_Attack_Level
{0x5f, 0x00, 0x00, 0x53},//##Noise_Gate_Release_Level
{0x60, 0x00, 0x80, 0x00},//##DRC1_Energy_Coefficient
{0x61, 0x00, 0x20, 0x00},//##DRC2_Energy_Coefficient
{0x62, 0x00, 0x80, 0x00},//##DRC3_Energy_Coefficient
{0x63, 0x00, 0x80, 0x00},//##DRC4_Energy_Coefficient
{0x64, 0x00, 0x17, 0x78},//DRC1_Power_Meter
{0x65, 0x00, 0x00, 0x00},//DRC3_Power_Mete
{0x66, 0x00, 0x00, 0x00},//DRC5_Power_Meter
{0x67, 0x00, 0x00, 0x00},//DRC7_Power_Meter
{0x68, 0x00, 0x00, 0x00},//##Channel_1_DEQ1_A1
{0x69, 0x00, 0x00, 0x00},//##Channel_1_DEQ1_A2
{0x6a, 0x00, 0x00, 0x00},//##Channel_1_DEQ1_B1
{0x6b, 0x00, 0x00, 0x00},//##Channel_1_DEQ1_B2
{0x6c, 0x20, 0x00, 0x00},//##Channel_1_DEQ1_A0
{0x6d, 0x00, 0x00, 0x00},//##Channel_1_DEQ2_A1
{0x6e, 0x00, 0x00, 0x00},//##Channel_1_DEQ2_A2
{0x6f, 0x00, 0x00, 0x00},//##Channel_1_DEQ2_B1
{0x70, 0x00, 0x00, 0x00},//##Channel_1_DEQ2_B2
{0x71, 0x20, 0x00, 0x00},//##Channel_1_DEQ2_A0
{0x72, 0x00, 0x00, 0x00},//##Channel_1_DEQ3_A1
{0x73, 0x00, 0x00, 0x00},//##Channel_1_DEQ3_A2
{0x74, 0x00, 0x00, 0x00},//##Channel_1_DEQ3_B1
{0x75, 0x00, 0x00, 0x00},//##Channel_1_DEQ3_B2
{0x76, 0x20, 0x00, 0x00},//##Channel_1_DEQ3_A0
{0x77, 0x00, 0x00, 0x00},//##Channel_1_DEQ4_A1
{0x78, 0x00, 0x00, 0x00},//##Channel_1_DEQ4_A2
{0x79, 0x00, 0x00, 0x00},//##Channel_1_DEQ4_B1
{0x7a, 0x00, 0x00, 0x00},//##Channel_1_DEQ4_B2
{0x7b, 0x20, 0x00, 0x00},//##Channel_1_DEQ4_A0
{0x7c, 0x00, 0x00, 0x00},//##Reserve
{0x7d, 0x00, 0x00, 0x00},//##Reserve
{0x7e, 0x00, 0x00, 0x00},//##Reserve
{0x7f, 0x00, 0x00, 0x00},//##Reserve
{0x80, 0x00, 0x00, 0x2d},//##Channel_1_MF_LPF1_A1
{0x81, 0x00, 0x00, 0x16},//##Channel_1_MF_LPF1_A2
{0x82, 0x3f, 0xb3, 0x6a},//##Channel_1_MF_LPF1_B1
{0x83, 0xe0, 0x4c, 0x3d},//##Channel_1_MF_LPF1_B2
{0x84, 0x00, 0x00, 0x16},//##Channel_1_MF_LPF1_A0
{0x85, 0x00, 0x00, 0x2d},//##Channel_1_MF_LPF2_A1
{0x86, 0x00, 0x00, 0x16},//##Channel_1_MF_LPF2_A2
{0x87, 0x3f, 0xb3, 0x6a},//##Channel_1_MF_LPF2_B1
{0x88, 0xe0, 0x4c, 0x3d},//##Channel_1_MF_LPF2_B2
{0x89, 0x00, 0x00, 0x16},//##Channel_1_MF_LPF2_A0
{0x8a, 0x00, 0x00, 0x00},//##Channel_1_MF_BPF1_A1
{0x8b, 0xff, 0xe5, 0x51},//##Channel_1_MF_BPF1_A2
{0x8c, 0x3f, 0xb3, 0x6a},//##Channel_1_MF_BPF1_B1
{0x8d, 0xe0, 0x4c, 0x3d},//##Channel_1_MF_BPF1_B2
{0x8e, 0x00, 0x1a, 0xaf},//##Channel_1_MF_BPF1_A0
{0x8f, 0x00, 0x00, 0x00},//##Channel_1_MF_BPF2_A1
{0x90, 0xff, 0xe5, 0x51},//##Channel_1_MF_BPF2_A2
{0x91, 0x3f, 0xb3, 0x6a},//##Channel_1_MF_BPF2_B1
{0x92, 0xe0, 0x4c, 0x3d},//##Channel_1_MF_BPF2_B2
{0x93, 0x00, 0x1a, 0xaf},//##Channel_1_MF_BPF2_A0
{0x94, 0x08, 0x00, 0x00},//##Channel_1_MF_CLIP
{0x95, 0x01, 0x9a, 0xfd},//##Channel_1_MF_Gain1
{0x96, 0x08, 0x00, 0x00},//##Channel_1_MF_Gain2
{0x97, 0x0b, 0x4c, 0xe0},//##Channel_1_MF_Gain3
{0x98, 0x08, 0x00, 0x00},//##Reserve
{0x99, 0x08, 0x00, 0x00},//##Reserve
{0x9a, 0x00, 0x00, 0x00},//##Reserve
{0x9b, 0x00, 0x00, 0x00},//##Reserve
{0x9c, 0x00, 0x00, 0x00},//##Reserve
{0x9d, 0x00, 0x00, 0x00},//##Reserve
{0x9e, 0x00, 0x00, 0x00},//##Reserve
{0x9f, 0x00, 0x00, 0x00},//##Reserve
{0xa0, 0x00, 0x00, 0x00},//##Channel_1_EQ16_A1
{0xa1, 0x00, 0x00, 0x00},//##Channel_1_EQ16_A2
{0xa2, 0x00, 0x00, 0x00},//##Channel_1_EQ16_B1
{0xa3, 0x00, 0x00, 0x00},//##Channel_1_EQ16_B2
{0xa4, 0x20, 0x00, 0x00},//##Channel_1_EQ16_A0
{0xa5, 0x00, 0x00, 0x00},//##Channel_1_EQ17_A1
{0xa6, 0x00, 0x00, 0x00},//##Channel_1_EQ17_A2
{0xa7, 0x00, 0x00, 0x00},//##Channel_1_EQ17_B1
{0xa8, 0x00, 0x00, 0x00},//##Channel_1_EQ17_B2
{0xa9, 0x20, 0x00, 0x00},//##Channel_1_EQ17_A0
{0xaa, 0x00, 0x00, 0x00},//##Channel_1_EQ18_A1
{0xab, 0x00, 0x00, 0x00},//##Channel_1_EQ18_A2
{0xac, 0x00, 0x00, 0x00},//##Channel_1_EQ18_B1
{0xad, 0x00, 0x00, 0x00},//##Channel_1_EQ18_B2
{0xae, 0x20, 0x00, 0x00},//##Channel_1_EQ18_A0
{0xaf, 0x20, 0x00, 0x00},//##Channel_1_SMB_ATH
{0xb0, 0x08, 0x00, 0x00},//##Channel_1_SMB_RTH
{0xb1, 0x02, 0x00, 0x00},//##Channel_1_Boost_CTRL1
{0xb2, 0x00, 0x80, 0x00},//##Channel_1_Boost_CTRL2
{0xb3, 0x00, 0x20, 0x00},//##Channel_1_Boost_CTRL3
};
static int m_ram2_tab[][4] = {
{0x00, 0x00, 0x00, 0x00},//##Channel_2_EQ1_A1
{0x01, 0x00, 0x00, 0x00},//##Channel_2_EQ1_A2
{0x02, 0x00, 0x00, 0x00},//##Channel_2_EQ1_B1
{0x03, 0x00, 0x00, 0x00},//##Channel_2_EQ1_B2
{0x04, 0x20, 0x00, 0x00},//##Channel_2_EQ1_A0
{0x05, 0x00, 0x00, 0x00},//##Channel_2_EQ2_A1
{0x06, 0x00, 0x00, 0x00},//##Channel_2_EQ2_A2
{0x07, 0x00, 0x00, 0x00},//##Channel_2_EQ2_B1
{0x08, 0x00, 0x00, 0x00},//##Channel_2_EQ2_B2
{0x09, 0x20, 0x00, 0x00},//##Channel_2_EQ2_A0
{0x0a, 0x00, 0x00, 0x00},//##Channel_2_EQ3_A1
{0x0b, 0x00, 0x00, 0x00},//##Channel_2_EQ3_A2
{0x0c, 0x00, 0x00, 0x00},//##Channel_2_EQ3_B1
{0x0d, 0x00, 0x00, 0x00},//##Channel_2_EQ3_B2
{0x0e, 0x20, 0x00, 0x00},//##Channel_2_EQ3_A0
{0x0f, 0x00, 0x00, 0x00},//##Channel_2_EQ4_A1
{0x10, 0x00, 0x00, 0x00},//##Channel_2_EQ4_A2
{0x11, 0x00, 0x00, 0x00},//##Channel_2_EQ4_B1
{0x12, 0x00, 0x00, 0x00},//##Channel_2_EQ4_B2
{0x13, 0x20, 0x00, 0x00},//##Channel_2_EQ4_A0
{0x14, 0x00, 0x00, 0x00},//##Channel_2_EQ5_A1
{0x15, 0x00, 0x00, 0x00},//##Channel_2_EQ5_A2
{0x16, 0x00, 0x00, 0x00},//##Channel_2_EQ5_B1
{0x17, 0x00, 0x00, 0x00},//##Channel_2_EQ5_B2
{0x18, 0x20, 0x00, 0x00},//##Channel_2_EQ5_A0
{0x19, 0x00, 0x00, 0x00},//##Channel_2_EQ6_A1
{0x1a, 0x00, 0x00, 0x00},//##Channel_2_EQ6_A2
{0x1b, 0x00, 0x00, 0x00},//##Channel_2_EQ6_B1
{0x1c, 0x00, 0x00, 0x00},//##Channel_2_EQ6_B2
{0x1d, 0x20, 0x00, 0x00},//##Channel_2_EQ6_A0
{0x1e, 0x00, 0x00, 0x00},//##Channel_2_EQ7_A1
{0x1f, 0x00, 0x00, 0x00},//##Channel_2_EQ7_A2
{0x20, 0x00, 0x00, 0x00},//##Channel_2_EQ7_B1
{0x21, 0x00, 0x00, 0x00},//##Channel_2_EQ7_B2
{0x22, 0x20, 0x00, 0x00},//##Channel_2_EQ7_A0
{0x23, 0x00, 0x00, 0x00},//##Channel_2_EQ8_A1
{0x24, 0x00, 0x00, 0x00},//##Channel_2_EQ8_A2
{0x25, 0x00, 0x00, 0x00},//##Channel_2_EQ8_B1
{0x26, 0x00, 0x00, 0x00},//##Channel_2_EQ8_B2
{0x27, 0x20, 0x00, 0x00},//##Channel_2_EQ8_A0
{0x28, 0x00, 0x00, 0x00},//##Channel_2_EQ9_A1
{0x29, 0x00, 0x00, 0x00},//##Channel_2_EQ9_A2
{0x2a, 0x00, 0x00, 0x00},//##Channel_2_EQ9_B1
{0x2b, 0x00, 0x00, 0x00},//##Channel_2_EQ9_B2
{0x2c, 0x20, 0x00, 0x00},//##Channel_2_EQ9_A0
{0x2d, 0x00, 0x00, 0x00},//##Channel_2_EQ10_A1
{0x2e, 0x00, 0x00, 0x00},//##Channel_2_EQ10_A2
{0x2f, 0x00, 0x00, 0x00},//##Channel_2_EQ10_B1
{0x30, 0x00, 0x00, 0x00},//##Channel_2_EQ10_B2
{0x31, 0x20, 0x00, 0x00},//##Channel_2_EQ10_A0
{0x32, 0x00, 0x00, 0x00},//##Channel_2_EQ11_A1
{0x33, 0x00, 0x00, 0x00},//##Channel_2_EQ11_A2
{0x34, 0x00, 0x00, 0x00},//##Channel_2_EQ11_B1
{0x35, 0x00, 0x00, 0x00},//##Channel_2_EQ11_B2
{0x36, 0x20, 0x00, 0x00},//##Channel_2_EQ11_A0
{0x37, 0x00, 0x00, 0x00},//##Channel_2_EQ12_A1
{0x38, 0x00, 0x00, 0x00},//##Channel_2_EQ12_A2
{0x39, 0x00, 0x00, 0x00},//##Channel_2_EQ12_B1
{0x3a, 0x00, 0x00, 0x00},//##Channel_2_EQ12_B2
{0x3b, 0x20, 0x00, 0x00},//##Channel_2_EQ12_A0
{0x3c, 0x00, 0x00, 0x00},//##Channel_2_EQ13_A1
{0x3d, 0x00, 0x00, 0x00},//##Channel_2_EQ13_A2
{0x3e, 0x00, 0x00, 0x00},//##Channel_2_EQ13_B1
{0x3f, 0x00, 0x00, 0x00},//##Channel_2_EQ13_B2
{0x40, 0x20, 0x00, 0x00},//##Channel_2_EQ13_A0
{0x41, 0x00, 0x00, 0x00},//##Channel_2_EQ14_A1
{0x42, 0x00, 0x00, 0x00},//##Channel_2_EQ14_A2
{0x43, 0x00, 0x00, 0x00},//##Channel_2_EQ14_B1
{0x44, 0x00, 0x00, 0x00},//##Channel_2_EQ14_B2
{0x45, 0x20, 0x00, 0x00},//##Channel_2_EQ14_A0
{0x46, 0x00, 0x00, 0x00},//##Channel_2_EQ15_A1
{0x47, 0x00, 0x00, 0x00},//##Channel_2_EQ15_A2
{0x48, 0x00, 0x00, 0x00},//##Channel_2_EQ15_B1
{0x49, 0x00, 0x00, 0x00},//##Channel_2_EQ15_B2
{0x4a, 0x20, 0x00, 0x00},//##Channel_2_EQ15_A0
{0x4b, 0x00, 0x00, 0x00},//##Channel_2_Mixer1
{0x4c, 0x7f, 0xff, 0xff},//##Channel_2_Mixer2
{0x4d, 0x07, 0xe8, 0x8e},//##Channel_2_Prescale
{0x4e, 0x20, 0x00, 0x00},//##Channel_2_Postscale
{0x4f, 0xc7, 0xb6, 0x91},//##A0_of_R_channel_SRS_HPF
{0x50, 0x38, 0x49, 0x6e},//##A1_of_R_channel_SRS_HPF
{0x51, 0x0c, 0x46, 0xf8},//##B1_of_R_channel_SRS_HPF
{0x52, 0x0e, 0x81, 0xb9},//##A0_of_R_channel_SRS_LPF
{0x53, 0xf2, 0x2c, 0x12},//##A1_of_R_channel_SRS_LPF
{0x54, 0x0f, 0xca, 0xbb},//##B1_of_R_channel_SRS_LPF
{0x55, 0x00, 0x00, 0x00},//##Reserve
{0x56, 0x00, 0x00, 0x00},//##Reserve
{0x57, 0x00, 0x00, 0x00},//##Reserve
{0x58, 0x00, 0x00, 0x00},//##Reserve
{0x59, 0x00, 0x00, 0x00},//##Reserve
{0x5a, 0x00, 0x00, 0x00},//##Reserve
{0x5b, 0x00, 0x00, 0x00},//##Reserve
{0x5c, 0x00, 0x00, 0x00},//##Reserve
{0x5d, 0x00, 0x00, 0x00},//##Reserve
{0x5e, 0x00, 0x00, 0x00},//##Reserve
{0x5f, 0x00, 0x00, 0x00},//##Reserve
{0x60, 0x00, 0x00, 0x00},//##Reserve
{0x61, 0x00, 0x00, 0x00},//##Reserve
{0x62, 0x00, 0x00, 0x00},//##Reserve
{0x63, 0x00, 0x00, 0x00},//##Reserve
{0x64, 0x00, 0x17, 0x7c},//DRC2_Power_Meter
{0x65, 0x00, 0x00, 0x00},//DRC4_Power_Mete
{0x66, 0x00, 0x00, 0x00},//DRC6_Power_Meter
{0x67, 0x00, 0x00, 0x00},//DRC8_Power_Meter
{0x68, 0x00, 0x00, 0x00},//##Channel_2_DEQ1_A1
{0x69, 0x00, 0x00, 0x00},//##Channel_2_DEQ1_A2
{0x6a, 0x00, 0x00, 0x00},//##Channel_2_DEQ1_B1
{0x6b, 0x00, 0x00, 0x00},//##Channel_2_DEQ1_B2
{0x6c, 0x20, 0x00, 0x00},//##Channel_2_DEQ1_A0
{0x6d, 0x00, 0x00, 0x00},//##Channel_2_DEQ2_A1
{0x6e, 0x00, 0x00, 0x00},//##Channel_2_DEQ2_A2
{0x6f, 0x00, 0x00, 0x00},//##Channel_2_DEQ2_B1
{0x70, 0x00, 0x00, 0x00},//##Channel_2_DEQ2_B2
{0x71, 0x20, 0x00, 0x00},//##Channel_2_DEQ2_A0
{0x72, 0x00, 0x00, 0x00},//##Channel_2_DEQ3_A1
{0x73, 0x00, 0x00, 0x00},//##Channel_2_DEQ3_A2
{0x74, 0x00, 0x00, 0x00},//##Channel_2_DEQ3_B1
{0x75, 0x00, 0x00, 0x00},//##Channel_2_DEQ3_B2
{0x76, 0x20, 0x00, 0x00},//##Channel_2_DEQ3_A0
{0x77, 0x00, 0x00, 0x00},//##Channel_2_DEQ4_A1
{0x78, 0x00, 0x00, 0x00},//##Channel_2_DEQ4_A2
{0x79, 0x00, 0x00, 0x00},//##Channel_2_DEQ4_B1
{0x7a, 0x00, 0x00, 0x00},//##Channel_2_DEQ4_B2
{0x7b, 0x20, 0x00, 0x00},//##Channel_2_DEQ4_A0
{0x7c, 0x00, 0x00, 0x00},//##Reserve
{0x7d, 0x00, 0x00, 0x00},//##Reserve
{0x7e, 0x00, 0x00, 0x00},//##Reserve
{0x7f, 0x00, 0x00, 0x00},//##Reserve
{0x80, 0x00, 0x00, 0x2d},//##Channel_2_MF_LPF1_A1
{0x81, 0x00, 0x00, 0x16},//##Channel_2_MF_LPF1_A2
{0x82, 0x3f, 0xb3, 0x6a},//##Channel_2_MF_LPF1_B1
{0x83, 0xe0, 0x4c, 0x3d},//##Channel_2_MF_LPF1_B2
{0x84, 0x00, 0x00, 0x16},//##Channel_2_MF_LPF1_A0
{0x85, 0x00, 0x00, 0x2d},//##Channel_2_MF_LPF2_A1
{0x86, 0x00, 0x00, 0x16},//##Channel_2_MF_LPF2_A2
{0x87, 0x3f, 0xb3, 0x6a},//##Channel_2_MF_LPF2_B1
{0x88, 0xe0, 0x4c, 0x3d},//##Channel_2_MF_LPF2_B2
{0x89, 0x00, 0x00, 0x16},//##Channel_2_MF_LPF2_A0
{0x8a, 0x00, 0x00, 0x00},//##Channel_2_MF_BPF1_A1
{0x8b, 0xff, 0xe5, 0x51},//##Channel_2_MF_BPF1_A2
{0x8c, 0x3f, 0xb3, 0x6a},//##Channel_2_MF_BPF1_B1
{0x8d, 0xe0, 0x4c, 0x3d},//##Channel_2_MF_BPF1_B2
{0x8e, 0x00, 0x1a, 0xaf},//##Channel_2_MF_BPF1_A0
{0x8f, 0x00, 0x00, 0x00},//##Channel_2_MF_BPF2_A1
{0x90, 0xff, 0xe5, 0x51},//##Channel_2_MF_BPF2_A2
{0x91, 0x3f, 0xb3, 0x6a},//##Channel_2_MF_BPF2_B1
{0x92, 0xe0, 0x4c, 0x3d},//##Channel_2_MF_BPF2_B2
{0x93, 0x00, 0x1a, 0xaf},//##Channel_2_MF_BPF2_A0
{0x94, 0x08, 0x00, 0x00},//##Channel_2_MF_CLIP
{0x95, 0x01, 0x9a, 0xfd},//##Channel_2_MF_Gain1
{0x96, 0x08, 0x00, 0x00},//##Channel_2_MF_Gain2
{0x97, 0x0b, 0x4c, 0xe0},//##Channel_2_MF_Gain3
{0x98, 0x08, 0x00, 0x00},//##Reserve
{0x99, 0x08, 0x00, 0x00},//##Reserve
{0x9a, 0x00, 0x00, 0x00},//##Reserve
{0x9b, 0x00, 0x00, 0x00},//##Reserve
{0x9c, 0x00, 0x00, 0x00},//##Reserve
{0x9d, 0x00, 0x00, 0x00},//##Reserve
{0x9e, 0x00, 0x00, 0x00},//##Reserve
{0x9f, 0x00, 0x00, 0x00},//##Reserve
{0xa0, 0x00, 0x00, 0x00},//##Channel_2_EQ16_A1
{0xa1, 0x00, 0x00, 0x00},//##Channel_2_EQ16_A2
{0xa2, 0x00, 0x00, 0x00},//##Channel_2_EQ16_B1
{0xa3, 0x00, 0x00, 0x00},//##Channel_2_EQ16_B2
{0xa4, 0x20, 0x00, 0x00},//##Channel_2_EQ16_A0
{0xa5, 0x00, 0x00, 0x00},//##Channel_2_EQ17_A1
{0xa6, 0x00, 0x00, 0x00},//##Channel_2_EQ17_A2
{0xa7, 0x00, 0x00, 0x00},//##Channel_2_EQ17_B1
{0xa8, 0x00, 0x00, 0x00},//##Channel_2_EQ17_B2
{0xa9, 0x20, 0x00, 0x00},//##Channel_2_EQ17_A0
{0xaa, 0x00, 0x00, 0x00},//##Channel_2_EQ18_A1
{0xab, 0x00, 0x00, 0x00},//##Channel_2_EQ18_A2
{0xac, 0x00, 0x00, 0x00},//##Channel_2_EQ18_B1
{0xad, 0x00, 0x00, 0x00},//##Channel_2_EQ18_B2
{0xae, 0x20, 0x00, 0x00},//##Channel_2_EQ18_A0
{0xaf, 0x20, 0x00, 0x00},//##Channel_2_SMB_ATH
{0xb0, 0x08, 0x00, 0x00},//##Channel_2_SMB_RTH
{0xb1, 0x01, 0x00, 0x00},//##Reserve
{0xb2, 0x00, 0x40, 0x00},//##Reserve
{0xb3, 0x00, 0x10, 0x00},//##Reserve
};
struct add2010_data {
struct snd_soc_component *component;
struct regmap *regmap;
struct i2c_client *add2010_client;
enum add2010_type devtype;
struct regulator_bulk_data supplies[ADD2010_NUM_SUPPLIES];
struct delayed_work fault_check_work;
unsigned int last_fault;
int ssz_ds;
int mute;
int init_done;
#ifdef ADD2010_CHANGE_EQ_MODE_EN
unsigned int eq_mode;
unsigned char (*m_ram_tab)[4];
#endif
};
static int add2010_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
unsigned int rate = params_rate(params);
int ssz_ds;
switch (rate) {
case 44100:
case 48000:
ssz_ds = ADD2010_FS_48K;
break;
case 88200:
case 96000:
ssz_ds = ADD2010_FS_96K;
break;
default:
dev_err(component->dev, "unsupported sample rate: %u\n", rate);
return -EINVAL;
}
add2010->ssz_ds = ssz_ds;
return 0;
}
static int add2010_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
u8 serial_format;
int ret;
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
dev_vdbg(component->dev, "DAI Format master is not found\n");
return -EINVAL;
}
switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
SND_SOC_DAIFMT_INV_MASK)) {
case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
/* 1st data bit occur one BCLK cycle after the frame sync */
serial_format = ADD2010_SAIF_I2S;
break;
case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):
/*
* Note that although the ADD2010 does not have a dedicated DSP
* mode it doesn't care about the LRCLK duty cycle during TDM
* operation. Therefore we can use the device's I2S mode with
* its delaying of the 1st data bit to receive DSP_A formatted
* data. See device datasheet for additional details.
*/
serial_format = ADD2010_SAIF_I2S;
break;
case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):
/*
* Similar to DSP_A, we can use the fact that the ADD2010 does
* not care about the LRCLK duty cycle during TDM to receive
* DSP_B formatted data in LEFTJ mode (no delaying of the 1st
* data bit).
*/
serial_format = ADD2010_SAIF_LEFTJ;
break;
case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
/* No delay after the frame sync */
serial_format = ADD2010_SAIF_LEFTJ;
break;
default:
dev_vdbg(component->dev, "DAI Format is not found\n");
return -EINVAL;
}
dev_info(component->dev, "setting add2010 dai format ----- thj\n");
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL1_REG,
ADD2010_SAIF_FORMAT_MASK,
serial_format);
if (ret < 0) {
dev_err(component->dev, "error setting SAIF format: %d\n", ret);
return ret;
}
return 0;
}
#ifdef ADD2010_CHANGE_EQ_MODE_EN
static int add2010_change_eq_mode(struct snd_soc_component *component, int channel)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int eq_seg = 0;
int i = 0;
int cmd = 0;
for (i = 0; i < 15; i++) {
// ram addr
regmap_write(add2010->regmap, 0x1d, add2010->m_ram_tab[eq_seg][0]);
// write A1
regmap_write(add2010->regmap, 0x1e, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, 0x1f, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, 0x20, add2010->m_ram_tab[eq_seg][3]);
eq_seg += 1;
// write A2
regmap_write(add2010->regmap, 0x21, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, 0x22, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, 0x23, add2010->m_ram_tab[eq_seg][3]);
eq_seg += 1;
// write B1
regmap_write(add2010->regmap, 0x24, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, 0x25, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, 0x26, add2010->m_ram_tab[eq_seg][3]);
eq_seg += 1;
// write B2
regmap_write(add2010->regmap, 0x27, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, 0x28, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, 0x29, add2010->m_ram_tab[eq_seg][3]);
eq_seg += 1;
// write A0
regmap_write(add2010->regmap, 0x2a, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, 0x2b, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, 0x2c, add2010->m_ram_tab[eq_seg][3]);
if (channel == 1)
cmd = 0x02;
else if (channel == 2)
cmd = 0x42;
regmap_write(add2010->regmap, 0x2d, cmd);
eq_seg += 1;
if (eq_seg > 0x4a)
break;
}
for (eq_seg = 0x4b; eq_seg <= 0xb3; eq_seg++) {
if ((eq_seg >= 0x7c) && (eq_seg <= 0x7f))
continue;
if ((eq_seg >= 0x9a) && (eq_seg <= 0x9f))
continue;
regmap_write(add2010->regmap, CFADDR, add2010->m_ram_tab[eq_seg][0]);
regmap_write(add2010->regmap, A1CF1, add2010->m_ram_tab[eq_seg][1]);
regmap_write(add2010->regmap, A1CF2, add2010->m_ram_tab[eq_seg][2]);
regmap_write(add2010->regmap, A1CF3, add2010->m_ram_tab[eq_seg][3]);
if (channel == 1)
cmd = 0x01;
if (channel == 2)
cmd = 0x41;
regmap_write(add2010->regmap, CFUD, cmd);
}
return 0;
}
static int add2010_eq_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
uinfo->count = 1;
uinfo->value.integer.min = ADD2010_EQ_MODE_MIN;
uinfo->value.integer.max = ADD2010_EQ_MODE_MAX;
uinfo->value.integer.step = 1;
return 0;
}
static int add2010_eq_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = add2010->eq_mode;
return 0;
}
static int add2010_eq_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int id_reg = 0;
if ((ucontrol->value.integer.value[0] > ADD2010_EQ_MODE_MAX) ||
(ucontrol->value.integer.value[0] < ADD2010_EQ_MODE_MIN)) {
dev_err(component->dev, "error mode value setting, please check!\n");
return -1;
}
id_reg = snd_soc_component_read(component, ADD2010_DEVICE_ID_REG);
if ((id_reg&0xf0) != ADD2010_DEVICE_ID) { // amp i2c have not ack ,i2c error
dev_err(component->dev, "error device id 0x%02x, please check!\n", id_reg);
return -1;
}
add2010->eq_mode = ucontrol->value.integer.value[0];
dev_info(component->dev, "change add2010 eq mode = %d\n", add2010->eq_mode);
if (add2010->eq_mode == 1) {
add2010->m_ram_tab = eq_mode_1_ram1_tab;
add2010_change_eq_mode(component, 1);
#ifdef CONFIG_SND_SOC_ADD2010_2CHANNEL
add2010->m_ram_tab = eq_mode_1_ram2_tab;
add2010_change_eq_mode(component, 2);
#endif
}
if (add2010->eq_mode == 2) {
add2010->m_ram_tab = eq_mode_2_ram1_tab;
add2010_change_eq_mode(component, 1);
#ifdef CONFIG_SND_SOC_ADD2010_2CHANNEL
add2010->m_ram_tab = eq_mode_2_ram2_tab;
add2010_change_eq_mode(component, 2);
#endif
}
// add your other eq mode here
// ...
return 0;
}
static const struct snd_kcontrol_new add2010_eq_mode_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADD2010 EQ Mode", // Just fake the name
.info = add2010_eq_mode_info,
.get = add2010_eq_mode_get,
.put = add2010_eq_mode_put,
},
};
#endif
static int add2010_mute(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_component *component = dai->component;
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int ret = 0;
add2010->mute = mute;
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, add2010->mute);
if (ret < 0) {
dev_err(component->dev, "error (un-)muting device: %d\n", ret);
return ret;
}
return 0;
}
static int ADD2010_get_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uinfo)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
struct device *dev = &add2010->add2010_client->dev;
if (add2010->init_done) {
// add2010->mute = ;
dev_err(dev, "in %s %X\n", __func__, add2010->mute);
}
return 0;
}
static int ADD2010_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
struct device *dev = &add2010->add2010_client->dev;
int ret = 0;
if (add2010->init_done) {
add2010->mute = (ucontrol->value.integer.value[0]) ? 0x40 : 0x00;
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, add2010->mute);
if (ret < 0) {
dev_err(dev, "error (un-)muting device: %d\n", ret);
return ret;
}
dev_err(dev, "in %s %X\n", __func__, add2010->mute);
}
return 0;
}
static int add2010_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
struct snd_soc_component *component = dai->component;
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
(void)substream;
dev_dbg(component->dev,
"%s: init_done %d, mute %X\n", __func__, add2010->init_done,
add2010->mute);
if (!add2010->init_done) {
add2010_codec_probe(component);
ret = snd_soc_component_update_bits(component,
ADD2010_STATE_CTRL2_REG,
ADD2010_SAMPLE_FREQUENCY_MASK,
add2010->ssz_ds);
if (ret < 0) {
dev_err(component->dev,
"error setting sample rate: %d\n", ret);
return ret;
}
add2010->init_done = 1;
}
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, add2010->mute);
if (ret < 0) {
dev_err(component->dev, "error (un-)muting device: %d\n", ret);
return ret;
}
return 0;
}
static void add2010_fault_check_work(struct work_struct *work)
{
struct add2010_data *add2010 = container_of(work, struct add2010_data,
fault_check_work.work);
struct device *dev = add2010->component->dev;
struct snd_soc_component *component = add2010->component;
unsigned int curr_fault;
int ret;
ret = regmap_read(add2010->regmap, ADD2010_FAULT_REG, &curr_fault);
if (ret < 0) {
dev_err(dev, "failed to read FAULT register: %d\n", ret);
goto out;
}
/* Check/handle all errors except SAIF clock errors */
curr_fault &= ADD2010_LVDET | ADD2010_OTE | ADD2010_CLKE;
/*
* Only flag errors once for a given occurrence. This is needed as
* the ADD2010 will take time clearing the fault condition internally
* during which we don't want to bombard the system with the same
* error message over and over.
*/
if (!(curr_fault & ADD2010_LVDET) && (add2010->last_fault & ADD2010_LVDET))
dev_crit(dev, "experienced an LVDET fault\n");
if (!(curr_fault & ADD2010_CLKE) && (add2010->last_fault & ADD2010_CLKE))
dev_crit(dev, "experienced an CLK ERROR fault\n");
if (!(curr_fault & ADD2010_OTE) && (add2010->last_fault & ADD2010_OTE))
dev_crit(dev, "experienced an over temperature fault\n");
/* Store current fault value so we can detect any changes next time */
add2010->last_fault = curr_fault;
if ((curr_fault&ADD2010_LVDET) && (curr_fault&ADD2010_OTE))
goto out;
// MUTE
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, ADD2010_MUTE);
if (ret < 0) {
dev_err(dev, "failed to MUTE AMP: %d\n", ret);
goto out;
}
msleep(20);
// UNMUTE
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, 0);
if (ret < 0) {
dev_err(dev, "failed to UNMUTE AMP: %d\n", ret);
goto out;
}
out:
/* Schedule the next fault check at the specified interval */
schedule_delayed_work(&add2010->fault_check_work,
msecs_to_jiffies(ADD2010_FAULT_CHECK_INTERVAL));
}
static int add2010_codec_probe_fake(struct snd_soc_component *component)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
add2010->component = component;
return 0;
}
static int add2010_codec_probe(struct snd_soc_component *component)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
//unsigned int device_id, expected_device_id;
int ret;
int i;
int reg_data;
#ifdef ADD2010_REG_RAM_CHECK
int ram_h8_data;
int ram_m8_data;
int ram_l8_data;
#endif
add2010->component = component;
dev_info(component->dev, "shx add2010 i2c address = %p, %s!\n", component, __func__);
ret = regulator_bulk_enable(ARRAY_SIZE(add2010->supplies),
add2010->supplies);
if (ret != 0) {
dev_err(component->dev, "failed to enable supplies: %d\n", ret);
return ret;
}
msleep(20);
// software reset amp
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL5_REG,
ADD2010_SW_RESET, 0);
if (ret < 0)
goto error_snd_soc_component_update_bits;
usleep_range(5000, 5050);
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL5_REG,
ADD2010_SW_RESET, ADD2010_SW_RESET);
if (ret < 0)
goto error_snd_soc_component_update_bits;
msleep(20); // wait 20ms
/* Set device to mute */
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, ADD2010_MUTE);
if (ret < 0)
goto error_snd_soc_component_update_bits;
// Write register table
for (i = 0; i < ADD2010_REGISTER_COUNT; i++) {
reg_data = m_reg_tab[i][1];
if (m_reg_tab[i][0] == 0x02)
continue;
ret = regmap_write(add2010->regmap, m_reg_tab[i][0], reg_data);
if (ret < 0)
goto error_snd_soc_component_update_bits;
}
// Write ram1
for (i = 0; i < ADD2010_RAM_TABLE_COUNT; i++) {
regmap_write(add2010->regmap, CFADDR, m_ram1_tab[i][0]);
regmap_write(add2010->regmap, A1CF1, m_ram1_tab[i][1]);
regmap_write(add2010->regmap, A1CF2, m_ram1_tab[i][2]);
regmap_write(add2010->regmap, A1CF3, m_ram1_tab[i][3]);
regmap_write(add2010->regmap, CFUD, 0x01);
}
// Write ram2
for (i = 0; i < ADD2010_RAM_TABLE_COUNT; i++) {
regmap_write(add2010->regmap, CFADDR, m_ram2_tab[i][0]);
regmap_write(add2010->regmap, A1CF1, m_ram2_tab[i][1]);
regmap_write(add2010->regmap, A1CF2, m_ram2_tab[i][2]);
regmap_write(add2010->regmap, A1CF3, m_ram2_tab[i][3]);
regmap_write(add2010->regmap, CFUD, 0x41);
}
usleep_range(2000, 2050);
/* Set device to unmute */
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, 0);
if (ret < 0)
goto error_snd_soc_component_update_bits;
INIT_DELAYED_WORK(&add2010->fault_check_work, add2010_fault_check_work);
dev_info(component->dev, "shx %s success!\n", __func__);
#ifdef ADD2010_CHANGE_EQ_MODE_EN
ret = snd_soc_add_component_controls(component, add2010_eq_mode_control, 1);
if (ret != 0)
dev_err(component->dev, "Failed to register add2010_eq_mode_control (%d)\n", ret);
#endif
#ifdef ADD2010_REG_RAM_CHECK
msleep(1000);
for (i = 0; i < ADD2010_REGISTER_COUNT; i++) {
reg_data = snd_soc_component_read(component, m_reg_tab[i][0]);
dev_info(component->dev,
"read add2010 register {addr, data} = {0x%02x, 0x%02x}\n",
m_reg_tab[i][0], reg_data);
}
for (i = 0; i < ADD2010_RAM_TABLE_COUNT; i++) {
regmap_write(add2010->regmap, CFADDR, m_ram1_tab[i][0]); // write ram addr
regmap_write(add2010->regmap, CFUD, 0x04); // write read a single ram data cmd
ram_h8_data = snd_soc_component_read(component, A1CF1);
ram_m8_data = snd_soc_component_read(component, A1CF2);
ram_l8_data = snd_soc_component_read(component, A1CF3);
dev_info(component->dev,
"read add2010 ram1 {addr, H8, M8, L8} = {0x%02x, 0x%02x, 0x%02x, 0x%02x}\n",
m_ram1_tab[i][0], ram_h8_data, ram_m8_data, ram_l8_data);
}
#endif
return 0;
error_snd_soc_component_update_bits:
dev_err(component->dev, "error configuring device registers: %d\n", ret);
//probe_fail:
//regulator_bulk_disable(ARRAY_SIZE(add2010->supplies),
//add2010->supplies);
return ret;
}
static void add2010_codec_remove(struct snd_soc_component *component)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int ret;
dev_info(component->dev, "add2010 codec remove ---- thj\n");
cancel_delayed_work_sync(&add2010->fault_check_work);
ret = regulator_bulk_disable(ARRAY_SIZE(add2010->supplies),
add2010->supplies);
if (ret < 0)
dev_err(component->dev, "failed to disable supplies: %d\n", ret);
};
static int add2010_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int ret;
if (event & SND_SOC_DAPM_POST_PMU) {
dev_info(component->dev, "add2010 dac event post PMU ---- thj\n");
/* Take ADD2010 out of shutdown mode */
/*
* Observe codec shutdown-to-active time. The datasheet only
* lists a nominal value however just use-it as-is without
* additional padding to minimize the delay introduced in
* starting to play audio (actually there is other setup done
* by the ASoC framework that will provide additional delays,
* so we should always be safe).
*/
msleep(25);
add2010->mute = 0;
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, 0);
if (ret < 0)
dev_err(component->dev, "failed to write MUTE register: %d\n", ret);
/* Turn on ADD2010 periodic fault checking/handling */
add2010->last_fault = 0xFE;
schedule_delayed_work(&add2010->fault_check_work,
msecs_to_jiffies(ADD2010_FAULT_CHECK_INTERVAL));
} else if (event & SND_SOC_DAPM_PRE_PMD) {
dev_info(component->dev, "add2010 dac event pre PMD ----- thj\n");
/* Disable ADD2010 periodic fault checking/handling */
cancel_delayed_work_sync(&add2010->fault_check_work);
/* Place ADD2010 in shutdown mode to minimize current draw */
add2010->mute = ADD2010_MUTE;
ret = snd_soc_component_update_bits(component, ADD2010_STATE_CTRL3_REG,
ADD2010_MUTE, ADD2010_MUTE);
if (ret < 0)
dev_err(component->dev, "failed to write MUTE register: %d\n", ret);
}
return 0;
}
#ifdef CONFIG_PM
static int add2010_suspend(struct snd_soc_component *component)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int ret;
regcache_cache_only(add2010->regmap, true);
regcache_mark_dirty(add2010->regmap);
ret = regulator_bulk_disable(ARRAY_SIZE(add2010->supplies),
add2010->supplies);
if (ret < 0)
dev_err(component->dev, "failed to disable supplies: %d\n", ret);
return ret;
}
static int add2010_resume(struct snd_soc_component *component)
{
struct add2010_data *add2010 = snd_soc_component_get_drvdata(component);
int ret;
dev_info(component->dev, "add2010 resume ---- thj\n");
ret = regulator_bulk_enable(ARRAY_SIZE(add2010->supplies),
add2010->supplies);
if (ret < 0) {
dev_err(component->dev, "failed to enable supplies: %d\n", ret);
return ret;
}
regcache_cache_only(add2010->regmap, false);
ret = regcache_sync(add2010->regmap);
if (ret < 0) {
dev_err(component->dev, "failed to sync regcache: %d\n", ret);
return ret;
}
return 0;
}
#else
#define add2010_suspend NULL
#define add2010_resume NULL
#endif
static bool add2010_is_volatile_reg(struct device *dev, unsigned int reg)
{
#ifdef ADD2010_REG_RAM_CHECK
if (reg < ADD2010_MAX_REG)
return true;
else
return false;
#else
switch (reg) {
case ADD2010_FAULT_REG:
case ADD2010_STATE_CTRL1_REG:
case ADD2010_STATE_CTRL2_REG:
case ADD2010_STATE_CTRL3_REG:
case ADD2010_STATE_CTRL5_REG:
case ADD2010_DEVICE_ID_REG:
return true;
default:
return false;
}
#endif
}
static const struct regmap_config add2010_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ADD2010_MAX_REG,
//.reg_defaults = m_reg_tab,
//
.cache_type = REGCACHE_RBTREE,
.volatile_reg = add2010_is_volatile_reg,
};
/*
* DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
* setting the gain below -100 dB (register value <0x7) is effectively a MUTE
* as per device datasheet.
*/
static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
static const struct snd_kcontrol_new add2010_snd_controls[] = {
SOC_SINGLE_TLV("PhoneJack Playback Volume",
ADD2010_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv),
SOC_SINGLE_EXT("PhoneJack mute",
ADD2010_VOLUME_CTRL_REG, 0, 0xff, 0,
ADD2010_get_mute, ADD2010_put_mute),
};
static const struct snd_soc_dapm_widget add2010_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, add2010_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUTPUT("OUT")
};
static const struct snd_soc_dapm_route add2010_audio_map[] = {
{ "DAC", NULL, "DAC IN" },
{ "OUT", NULL, "DAC" },
};
static const struct snd_soc_component_driver soc_component_dev_add2010 = {
.probe = add2010_codec_probe_fake,
.remove = add2010_codec_remove,
.suspend = add2010_suspend,
.resume = add2010_resume,
.controls = add2010_snd_controls,
.num_controls = ARRAY_SIZE(add2010_snd_controls),
.dapm_widgets = add2010_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(add2010_dapm_widgets),
.dapm_routes = add2010_audio_map,
.num_dapm_routes = ARRAY_SIZE(add2010_audio_map),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
/* PCM rates supported by the ADD2010 driver */
#define ADD2010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
/* Formats supported by ADD2010 driver */
/*
* #define ADD2010_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_LE |\
SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE)
*/
#define ADD2010_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops add2010_speaker_dai_ops = {
.hw_params = add2010_hw_params,
.set_fmt = add2010_set_dai_fmt,
.mute_stream = add2010_mute,
.prepare = add2010_prepare,
};
/*
* ADD2010 DAI structure
*
* Note that were are advertising .playback.channels_max = 2 despite this being
* a mono amplifier. The reason for that is that some serial ports such as ESMT's
* McASP module have a minimum number of channels (2) that they can output.
* Advertising more channels than we have will allow us to interface with such
* a serial port without really any negative side effects as the ADD2010 will
* simply ignore any extra channel(s) asides from the one channel that is
* configured to be played back.
*/
static struct snd_soc_dai_driver add2010_dai[] = {
{
.name = "add2010",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = ADD2010_RATES,
.formats = ADD2010_FORMATS,
},
.ops = &add2010_speaker_dai_ops,
},
};
static int add2010_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct add2010_data *data;
const struct regmap_config *regmap_config;
int ret;
int i;
dev_info(dev, "shx goin %s 222 start\n", __func__); //add
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->add2010_client = client;
data->devtype = id->driver_data;
switch (id->driver_data) {
case ADD2010:
regmap_config = &add2010_regmap_config;
break;
default:
dev_err(dev, "unexpected private driver data\n");
return -EINVAL;
}
data->regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(data->regmap)) {
ret = PTR_ERR(data->regmap);
dev_err(dev, "failed to allocate register map: %d\n", ret);
return ret;
}
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = add2010_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
data->supplies);
if (ret != 0) {
dev_err(dev, "failed to request supplies: %d\n", ret);
return ret;
}
dev_set_drvdata(dev, data);
ret = devm_snd_soc_register_component(&client->dev,
&soc_component_dev_add2010,
add2010_dai, ARRAY_SIZE(add2010_dai));
if (ret < 0) {
dev_err(dev, "failed to register component: %d\n", ret);
return ret;
}
dev_info(dev, "shx goin %s 222 end\n", __func__); //add
return 0;
}
static const struct i2c_device_id add2010_id[] = {
{ "add2010", ADD2010 },
{ }
};
MODULE_DEVICE_TABLE(i2c, add2010_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id add2010_of_match[] = {
{ .compatible = "esmt,add2010", },
{ },
};
MODULE_DEVICE_TABLE(of, add2010_of_match);
#endif
static struct i2c_driver add2010_i2c_driver = {
.driver = {
.name = "add2010",
.of_match_table = of_match_ptr(add2010_of_match),
},
.probe = add2010_probe,
.id_table = add2010_id,
};
module_i2c_driver(add2010_i2c_driver);
MODULE_DESCRIPTION("ADD2010 Audio amplifier driver");
MODULE_LICENSE("GPL");