| /* |
| * max98090.c -- MAX98090 ALSA SoC Audio driver |
| * based on Rev0p8 datasheet |
| * |
| * Copyright (C) 2012 Renesas Solutions Corp. |
| * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| * |
| * Based on |
| * |
| * max98095.c |
| * Copyright 2011 Maxim Integrated Products |
| * |
| * https://github.com/hardkernel/linux/commit/\ |
| * 3417d7166b17113b3b33b0a337c74d1c7cc313df#sound/soc/codecs/max98090.c |
| * Copyright 2011 Maxim Integrated Products |
| * |
| * 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/i2c.h> |
| #include <linux/module.h> |
| #include <linux/regmap.h> |
| #include <sound/soc.h> |
| #include <sound/tlv.h> |
| |
| /* |
| * |
| * MAX98090 Registers Definition |
| * |
| */ |
| |
| /* RESET / STATUS / INTERRUPT REGISTERS */ |
| #define MAX98090_0x00_SW_RESET 0x00 |
| #define MAX98090_0x01_INT_STS 0x01 |
| #define MAX98090_0x02_JACK_STS 0x02 |
| #define MAX98090_0x03_INT_MASK 0x03 |
| |
| /* QUICK SETUP REGISTERS */ |
| #define MAX98090_0x04_SYS_CLK 0x04 |
| #define MAX98090_0x05_SAMPLE_RATE 0x05 |
| #define MAX98090_0x06_DAI_IF 0x06 |
| #define MAX98090_0x07_DAC_PATH 0x07 |
| #define MAX98090_0x08_MIC_TO_ADC 0x08 |
| #define MAX98090_0x09_LINE_TO_ADC 0x09 |
| #define MAX98090_0x0A_ANALOG_MIC_LOOP 0x0A |
| #define MAX98090_0x0B_ANALOG_LINE_LOOP 0x0B |
| |
| /* ANALOG INPUT CONFIGURATION REGISTERS */ |
| #define MAX98090_0x0D_INPUT_CONFIG 0x0D |
| #define MAX98090_0x0E_LINE_IN_LVL 0x0E |
| #define MAX98090_0x0F_LINI_IN_CFG 0x0F |
| #define MAX98090_0x10_MIC1_IN_LVL 0x10 |
| #define MAX98090_0x11_MIC2_IN_LVL 0x11 |
| |
| /* MICROPHONE CONFIGURATION REGISTERS */ |
| #define MAX98090_0x12_MIC_BIAS_VOL 0x12 |
| #define MAX98090_0x13_DIGITAL_MIC_CFG 0x13 |
| #define MAX98090_0x14_DIGITAL_MIC_MODE 0x14 |
| |
| /* ADC PATH AND CONFIGURATION REGISTERS */ |
| #define MAX98090_0x15_L_ADC_MIX 0x15 |
| #define MAX98090_0x16_R_ADC_MIX 0x16 |
| #define MAX98090_0x17_L_ADC_LVL 0x17 |
| #define MAX98090_0x18_R_ADC_LVL 0x18 |
| #define MAX98090_0x19_ADC_BIQUAD_LVL 0x19 |
| #define MAX98090_0x1A_ADC_SIDETONE 0x1A |
| |
| /* CLOCK CONFIGURATION REGISTERS */ |
| #define MAX98090_0x1B_SYS_CLK 0x1B |
| #define MAX98090_0x1C_CLK_MODE 0x1C |
| #define MAX98090_0x1D_ANY_CLK1 0x1D |
| #define MAX98090_0x1E_ANY_CLK2 0x1E |
| #define MAX98090_0x1F_ANY_CLK3 0x1F |
| #define MAX98090_0x20_ANY_CLK4 0x20 |
| #define MAX98090_0x21_MASTER_MODE 0x21 |
| |
| /* INTERFACE CONTROL REGISTERS */ |
| #define MAX98090_0x22_DAI_IF_FMT 0x22 |
| #define MAX98090_0x23_DAI_TDM_FMT1 0x23 |
| #define MAX98090_0x24_DAI_TDM_FMT2 0x24 |
| #define MAX98090_0x25_DAI_IO_CFG 0x25 |
| #define MAX98090_0x26_FILTER_CFG 0x26 |
| #define MAX98090_0x27_DAI_PLAYBACK_LVL 0x27 |
| #define MAX98090_0x28_EQ_PLAYBACK_LVL 0x28 |
| |
| /* HEADPHONE CONTROL REGISTERS */ |
| #define MAX98090_0x29_L_HP_MIX 0x29 |
| #define MAX98090_0x2A_R_HP_MIX 0x2A |
| #define MAX98090_0x2B_HP_CTR 0x2B |
| #define MAX98090_0x2C_L_HP_VOL 0x2C |
| #define MAX98090_0x2D_R_HP_VOL 0x2D |
| |
| /* SPEAKER CONFIGURATION REGISTERS */ |
| #define MAX98090_0x2E_L_SPK_MIX 0x2E |
| #define MAX98090_0x2F_R_SPK_MIX 0x2F |
| #define MAX98090_0x30_SPK_CTR 0x30 |
| #define MAX98090_0x31_L_SPK_VOL 0x31 |
| #define MAX98090_0x32_R_SPK_VOL 0x32 |
| |
| /* ALC CONFIGURATION REGISTERS */ |
| #define MAX98090_0x33_ALC_TIMING 0x33 |
| #define MAX98090_0x34_ALC_COMPRESSOR 0x34 |
| #define MAX98090_0x35_ALC_EXPANDER 0x35 |
| #define MAX98090_0x36_ALC_GAIN 0x36 |
| |
| /* RECEIVER AND LINE_OUTPUT REGISTERS */ |
| #define MAX98090_0x37_RCV_LOUT_L_MIX 0x37 |
| #define MAX98090_0x38_RCV_LOUT_L_CNTL 0x38 |
| #define MAX98090_0x39_RCV_LOUT_L_VOL 0x39 |
| #define MAX98090_0x3A_LOUT_R_MIX 0x3A |
| #define MAX98090_0x3B_LOUT_R_CNTL 0x3B |
| #define MAX98090_0x3C_LOUT_R_VOL 0x3C |
| |
| /* JACK DETECT AND ENABLE REGISTERS */ |
| #define MAX98090_0x3D_JACK_DETECT 0x3D |
| #define MAX98090_0x3E_IN_ENABLE 0x3E |
| #define MAX98090_0x3F_OUT_ENABLE 0x3F |
| #define MAX98090_0x40_LVL_CTR 0x40 |
| #define MAX98090_0x41_DSP_FILTER_ENABLE 0x41 |
| |
| /* BIAS AND POWER MODE CONFIGURATION REGISTERS */ |
| #define MAX98090_0x42_BIAS_CTR 0x42 |
| #define MAX98090_0x43_DAC_CTR 0x43 |
| #define MAX98090_0x44_ADC_CTR 0x44 |
| #define MAX98090_0x45_DEV_SHUTDOWN 0x45 |
| |
| /* REVISION ID REGISTER */ |
| #define MAX98090_0xFF_REV_ID 0xFF |
| |
| #define MAX98090_REG_MAX_CACHED 0x45 |
| #define MAX98090_REG_END 0xFF |
| |
| /* |
| * |
| * MAX98090 Registers Bit Fields |
| * |
| */ |
| |
| /* MAX98090_0x06_DAI_IF */ |
| #define MAX98090_DAI_IF_MASK 0x3F |
| #define MAX98090_RJ_M (1 << 5) |
| #define MAX98090_RJ_S (1 << 4) |
| #define MAX98090_LJ_M (1 << 3) |
| #define MAX98090_LJ_S (1 << 2) |
| #define MAX98090_I2S_M (1 << 1) |
| #define MAX98090_I2S_S (1 << 0) |
| |
| /* MAX98090_0x45_DEV_SHUTDOWN */ |
| #define MAX98090_SHDNRUN (1 << 7) |
| |
| /* codec private data */ |
| struct max98090_priv { |
| struct regmap *regmap; |
| }; |
| |
| static const struct reg_default max98090_reg_defaults[] = { |
| /* RESET / STATUS / INTERRUPT REGISTERS */ |
| {MAX98090_0x00_SW_RESET, 0x00}, |
| {MAX98090_0x01_INT_STS, 0x00}, |
| {MAX98090_0x02_JACK_STS, 0x00}, |
| {MAX98090_0x03_INT_MASK, 0x04}, |
| |
| /* QUICK SETUP REGISTERS */ |
| {MAX98090_0x04_SYS_CLK, 0x00}, |
| {MAX98090_0x05_SAMPLE_RATE, 0x00}, |
| {MAX98090_0x06_DAI_IF, 0x00}, |
| {MAX98090_0x07_DAC_PATH, 0x00}, |
| {MAX98090_0x08_MIC_TO_ADC, 0x00}, |
| {MAX98090_0x09_LINE_TO_ADC, 0x00}, |
| {MAX98090_0x0A_ANALOG_MIC_LOOP, 0x00}, |
| {MAX98090_0x0B_ANALOG_LINE_LOOP, 0x00}, |
| |
| /* ANALOG INPUT CONFIGURATION REGISTERS */ |
| {MAX98090_0x0D_INPUT_CONFIG, 0x00}, |
| {MAX98090_0x0E_LINE_IN_LVL, 0x1B}, |
| {MAX98090_0x0F_LINI_IN_CFG, 0x00}, |
| {MAX98090_0x10_MIC1_IN_LVL, 0x11}, |
| {MAX98090_0x11_MIC2_IN_LVL, 0x11}, |
| |
| /* MICROPHONE CONFIGURATION REGISTERS */ |
| {MAX98090_0x12_MIC_BIAS_VOL, 0x00}, |
| {MAX98090_0x13_DIGITAL_MIC_CFG, 0x00}, |
| {MAX98090_0x14_DIGITAL_MIC_MODE, 0x00}, |
| |
| /* ADC PATH AND CONFIGURATION REGISTERS */ |
| {MAX98090_0x15_L_ADC_MIX, 0x00}, |
| {MAX98090_0x16_R_ADC_MIX, 0x00}, |
| {MAX98090_0x17_L_ADC_LVL, 0x03}, |
| {MAX98090_0x18_R_ADC_LVL, 0x03}, |
| {MAX98090_0x19_ADC_BIQUAD_LVL, 0x00}, |
| {MAX98090_0x1A_ADC_SIDETONE, 0x00}, |
| |
| /* CLOCK CONFIGURATION REGISTERS */ |
| {MAX98090_0x1B_SYS_CLK, 0x00}, |
| {MAX98090_0x1C_CLK_MODE, 0x00}, |
| {MAX98090_0x1D_ANY_CLK1, 0x00}, |
| {MAX98090_0x1E_ANY_CLK2, 0x00}, |
| {MAX98090_0x1F_ANY_CLK3, 0x00}, |
| {MAX98090_0x20_ANY_CLK4, 0x00}, |
| {MAX98090_0x21_MASTER_MODE, 0x00}, |
| |
| /* INTERFACE CONTROL REGISTERS */ |
| {MAX98090_0x22_DAI_IF_FMT, 0x00}, |
| {MAX98090_0x23_DAI_TDM_FMT1, 0x00}, |
| {MAX98090_0x24_DAI_TDM_FMT2, 0x00}, |
| {MAX98090_0x25_DAI_IO_CFG, 0x00}, |
| {MAX98090_0x26_FILTER_CFG, 0x80}, |
| {MAX98090_0x27_DAI_PLAYBACK_LVL, 0x00}, |
| {MAX98090_0x28_EQ_PLAYBACK_LVL, 0x00}, |
| |
| /* HEADPHONE CONTROL REGISTERS */ |
| {MAX98090_0x29_L_HP_MIX, 0x00}, |
| {MAX98090_0x2A_R_HP_MIX, 0x00}, |
| {MAX98090_0x2B_HP_CTR, 0x00}, |
| {MAX98090_0x2C_L_HP_VOL, 0x1A}, |
| {MAX98090_0x2D_R_HP_VOL, 0x1A}, |
| |
| /* SPEAKER CONFIGURATION REGISTERS */ |
| {MAX98090_0x2E_L_SPK_MIX, 0x00}, |
| {MAX98090_0x2F_R_SPK_MIX, 0x00}, |
| {MAX98090_0x30_SPK_CTR, 0x00}, |
| {MAX98090_0x31_L_SPK_VOL, 0x2C}, |
| {MAX98090_0x32_R_SPK_VOL, 0x2C}, |
| |
| /* ALC CONFIGURATION REGISTERS */ |
| {MAX98090_0x33_ALC_TIMING, 0x00}, |
| {MAX98090_0x34_ALC_COMPRESSOR, 0x00}, |
| {MAX98090_0x35_ALC_EXPANDER, 0x00}, |
| {MAX98090_0x36_ALC_GAIN, 0x00}, |
| |
| /* RECEIVER AND LINE_OUTPUT REGISTERS */ |
| {MAX98090_0x37_RCV_LOUT_L_MIX, 0x00}, |
| {MAX98090_0x38_RCV_LOUT_L_CNTL, 0x00}, |
| {MAX98090_0x39_RCV_LOUT_L_VOL, 0x15}, |
| {MAX98090_0x3A_LOUT_R_MIX, 0x00}, |
| {MAX98090_0x3B_LOUT_R_CNTL, 0x00}, |
| {MAX98090_0x3C_LOUT_R_VOL, 0x15}, |
| |
| /* JACK DETECT AND ENABLE REGISTERS */ |
| {MAX98090_0x3D_JACK_DETECT, 0x00}, |
| {MAX98090_0x3E_IN_ENABLE, 0x00}, |
| {MAX98090_0x3F_OUT_ENABLE, 0x00}, |
| {MAX98090_0x40_LVL_CTR, 0x00}, |
| {MAX98090_0x41_DSP_FILTER_ENABLE, 0x00}, |
| |
| /* BIAS AND POWER MODE CONFIGURATION REGISTERS */ |
| {MAX98090_0x42_BIAS_CTR, 0x00}, |
| {MAX98090_0x43_DAC_CTR, 0x00}, |
| {MAX98090_0x44_ADC_CTR, 0x06}, |
| {MAX98090_0x45_DEV_SHUTDOWN, 0x00}, |
| }; |
| |
| static const unsigned int max98090_hp_tlv[] = { |
| TLV_DB_RANGE_HEAD(5), |
| 0x0, 0x6, TLV_DB_SCALE_ITEM(-6700, 400, 0), |
| 0x7, 0xE, TLV_DB_SCALE_ITEM(-4000, 300, 0), |
| 0xF, 0x15, TLV_DB_SCALE_ITEM(-1700, 200, 0), |
| 0x16, 0x1B, TLV_DB_SCALE_ITEM(-400, 100, 0), |
| 0x1C, 0x1F, TLV_DB_SCALE_ITEM(150, 50, 0), |
| }; |
| |
| static struct snd_kcontrol_new max98090_snd_controls[] = { |
| SOC_DOUBLE_R_TLV("Headphone Volume", MAX98090_0x2C_L_HP_VOL, |
| MAX98090_0x2D_R_HP_VOL, 0, 31, 0, max98090_hp_tlv), |
| }; |
| |
| /* Left HeadPhone Mixer Switch */ |
| static struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { |
| SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x29_L_HP_MIX, 1, 1, 0), |
| SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x29_L_HP_MIX, 0, 1, 0), |
| }; |
| |
| /* Right HeadPhone Mixer Switch */ |
| static struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { |
| SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x2A_R_HP_MIX, 1, 1, 0), |
| SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x2A_R_HP_MIX, 0, 1, 0), |
| }; |
| |
| static struct snd_soc_dapm_widget max98090_dapm_widgets[] = { |
| /* Output */ |
| SND_SOC_DAPM_OUTPUT("HPL"), |
| SND_SOC_DAPM_OUTPUT("HPR"), |
| |
| /* PGA */ |
| SND_SOC_DAPM_PGA("HPL Out", MAX98090_0x3F_OUT_ENABLE, 7, 0, NULL, 0), |
| SND_SOC_DAPM_PGA("HPR Out", MAX98090_0x3F_OUT_ENABLE, 6, 0, NULL, 0), |
| |
| /* Mixer */ |
| SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, |
| max98090_left_hp_mixer_controls, |
| ARRAY_SIZE(max98090_left_hp_mixer_controls)), |
| |
| SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, |
| max98090_right_hp_mixer_controls, |
| ARRAY_SIZE(max98090_right_hp_mixer_controls)), |
| |
| /* DAC */ |
| SND_SOC_DAPM_DAC("DACL", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 0, 0), |
| SND_SOC_DAPM_DAC("DACR", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 1, 0), |
| }; |
| |
| static struct snd_soc_dapm_route max98090_audio_map[] = { |
| /* Output */ |
| {"HPL", NULL, "HPL Out"}, |
| {"HPR", NULL, "HPR Out"}, |
| |
| /* PGA */ |
| {"HPL Out", NULL, "HPL Mixer"}, |
| {"HPR Out", NULL, "HPR Mixer"}, |
| |
| /* Mixer*/ |
| {"HPL Mixer", "DACR Switch", "DACR"}, |
| {"HPL Mixer", "DACL Switch", "DACL"}, |
| |
| {"HPR Mixer", "DACR Switch", "DACR"}, |
| {"HPR Mixer", "DACL Switch", "DACL"}, |
| }; |
| |
| static bool max98090_volatile(struct device *dev, unsigned int reg) |
| { |
| if ((reg == MAX98090_0x01_INT_STS) || |
| (reg == MAX98090_0x02_JACK_STS) || |
| (reg > MAX98090_REG_MAX_CACHED)) |
| return true; |
| |
| return false; |
| } |
| |
| static int max98090_dai_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; |
| unsigned int val; |
| |
| switch (params_rate(params)) { |
| case 96000: |
| val = 1 << 5; |
| break; |
| case 32000: |
| val = 1 << 4; |
| break; |
| case 48000: |
| val = 1 << 3; |
| break; |
| case 44100: |
| val = 1 << 2; |
| break; |
| case 16000: |
| val = 1 << 1; |
| break; |
| case 8000: |
| val = 1 << 0; |
| break; |
| default: |
| dev_err(codec->dev, "unsupported rate\n"); |
| return -EINVAL; |
| } |
| snd_soc_update_bits(codec, MAX98090_0x05_SAMPLE_RATE, 0x03F, val); |
| |
| return 0; |
| } |
| |
| static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, |
| int clk_id, unsigned int freq, int dir) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| unsigned int val; |
| |
| snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, |
| MAX98090_SHDNRUN, 0); |
| |
| switch (freq) { |
| case 26000000: |
| val = 1 << 7; |
| break; |
| case 19200000: |
| val = 1 << 6; |
| break; |
| case 13000000: |
| val = 1 << 5; |
| break; |
| case 12288000: |
| val = 1 << 4; |
| break; |
| case 12000000: |
| val = 1 << 3; |
| break; |
| case 11289600: |
| val = 1 << 2; |
| break; |
| default: |
| dev_err(codec->dev, "Invalid master clock frequency\n"); |
| return -EINVAL; |
| } |
| snd_soc_update_bits(codec, MAX98090_0x04_SYS_CLK, 0xFD, val); |
| |
| snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, |
| MAX98090_SHDNRUN, MAX98090_SHDNRUN); |
| |
| dev_dbg(dai->dev, "sysclk is %uHz\n", freq); |
| |
| return 0; |
| } |
| |
| static int max98090_dai_set_fmt(struct snd_soc_dai *dai, |
| unsigned int fmt) |
| { |
| struct snd_soc_codec *codec = dai->codec; |
| int is_master; |
| u8 val; |
| |
| /* master/slave mode */ |
| switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| case SND_SOC_DAIFMT_CBM_CFM: |
| is_master = 1; |
| break; |
| case SND_SOC_DAIFMT_CBS_CFS: |
| is_master = 0; |
| break; |
| default: |
| dev_err(codec->dev, "unsupported clock\n"); |
| return -EINVAL; |
| } |
| |
| /* format */ |
| switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| case SND_SOC_DAIFMT_I2S: |
| val = (is_master) ? MAX98090_I2S_M : MAX98090_I2S_S; |
| break; |
| case SND_SOC_DAIFMT_RIGHT_J: |
| val = (is_master) ? MAX98090_RJ_M : MAX98090_RJ_S; |
| break; |
| case SND_SOC_DAIFMT_LEFT_J: |
| val = (is_master) ? MAX98090_LJ_M : MAX98090_LJ_S; |
| break; |
| default: |
| dev_err(codec->dev, "unsupported format\n"); |
| return -EINVAL; |
| } |
| snd_soc_update_bits(codec, MAX98090_0x06_DAI_IF, |
| MAX98090_DAI_IF_MASK, val); |
| |
| return 0; |
| } |
| |
| #define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 |
| #define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) |
| |
| static struct snd_soc_dai_ops max98090_dai_ops = { |
| .set_sysclk = max98090_dai_set_sysclk, |
| .set_fmt = max98090_dai_set_fmt, |
| .hw_params = max98090_dai_hw_params, |
| }; |
| |
| static struct snd_soc_dai_driver max98090_dai = { |
| .name = "max98090-Hifi", |
| .playback = { |
| .stream_name = "Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = MAX98090_RATES, |
| .formats = MAX98090_FORMATS, |
| }, |
| .ops = &max98090_dai_ops, |
| }; |
| |
| static int max98090_probe(struct snd_soc_codec *codec) |
| { |
| struct max98090_priv *priv = snd_soc_codec_get_drvdata(codec); |
| struct device *dev = codec->dev; |
| int ret; |
| |
| codec->control_data = priv->regmap; |
| ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); |
| if (ret < 0) { |
| dev_err(dev, "Failed to set cache I/O: %d\n", ret); |
| return ret; |
| } |
| |
| /* Device active */ |
| snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, |
| MAX98090_SHDNRUN, MAX98090_SHDNRUN); |
| |
| return 0; |
| } |
| |
| static int max98090_remove(struct snd_soc_codec *codec) |
| { |
| return 0; |
| } |
| |
| static struct snd_soc_codec_driver soc_codec_dev_max98090 = { |
| .probe = max98090_probe, |
| .remove = max98090_remove, |
| .controls = max98090_snd_controls, |
| .num_controls = ARRAY_SIZE(max98090_snd_controls), |
| .dapm_widgets = max98090_dapm_widgets, |
| .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), |
| .dapm_routes = max98090_audio_map, |
| .num_dapm_routes = ARRAY_SIZE(max98090_audio_map), |
| }; |
| |
| static const struct regmap_config max98090_regmap = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| .max_register = MAX98090_REG_END, |
| .volatile_reg = max98090_volatile, |
| .cache_type = REGCACHE_RBTREE, |
| .reg_defaults = max98090_reg_defaults, |
| .num_reg_defaults = ARRAY_SIZE(max98090_reg_defaults), |
| }; |
| |
| static int max98090_i2c_probe(struct i2c_client *i2c, |
| const struct i2c_device_id *id) |
| { |
| struct max98090_priv *priv; |
| struct device *dev = &i2c->dev; |
| unsigned int val; |
| int ret; |
| |
| priv = devm_kzalloc(dev, sizeof(struct max98090_priv), |
| GFP_KERNEL); |
| if (!priv) |
| return -ENOMEM; |
| |
| priv->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); |
| if (IS_ERR(priv->regmap)) { |
| ret = PTR_ERR(priv->regmap); |
| dev_err(dev, "Failed to init regmap: %d\n", ret); |
| return ret; |
| } |
| |
| i2c_set_clientdata(i2c, priv); |
| |
| ret = regmap_read(priv->regmap, MAX98090_0xFF_REV_ID, &val); |
| if (ret < 0) { |
| dev_err(dev, "Failed to read device revision: %d\n", ret); |
| return ret; |
| } |
| dev_info(dev, "revision 0x%02x\n", val); |
| |
| ret = snd_soc_register_codec(dev, |
| &soc_codec_dev_max98090, |
| &max98090_dai, 1); |
| |
| return ret; |
| } |
| |
| static int max98090_i2c_remove(struct i2c_client *client) |
| { |
| snd_soc_unregister_codec(&client->dev); |
| return 0; |
| } |
| |
| static const struct i2c_device_id max98090_i2c_id[] = { |
| { "max98090", 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); |
| |
| static struct i2c_driver max98090_i2c_driver = { |
| .driver = { |
| .name = "max98090", |
| .owner = THIS_MODULE, |
| }, |
| .probe = max98090_i2c_probe, |
| .remove = max98090_i2c_remove, |
| .id_table = max98090_i2c_id, |
| }; |
| module_i2c_driver(max98090_i2c_driver); |
| |
| MODULE_DESCRIPTION("ALSA SoC MAX98090 driver"); |
| MODULE_AUTHOR("Peter Hsiang, Kuninori Morimoto"); |
| MODULE_LICENSE("GPL"); |