blob: b2900d18d0ac8c33233f2ad212e23392635615ed [file] [log] [blame]
/*
* max98506.c -- ALSA SoC MAX98506 driver
*
* Copyright 2013-2015 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/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/max98506.h>
#include "max98506.h"
#include <linux/regulator/consumer.h>
#ifdef CONFIG_SND_SOC_MAXIM_DSM
#include <sound/maxim_dsm_cal.h>
#endif
#define DEBUG_MAX98506
#ifdef DEBUG_MAX98506
#define msg_maxim(format, args...) \
pr_info("[MAX98506_DEBUG] %s: " format "\n", __func__, ## args)
#else
#define msg_maxim(format, args...)
#endif /* DEBUG_MAX98506 */
static int max98506_regmap_write(struct max98506_priv *max98506,
unsigned int reg,
unsigned int val)
{
int ret = 0;
ret = regmap_write(max98506->regmap, reg, val);
if (max98506->sub_regmap)
ret = regmap_write(max98506->sub_regmap, reg, val);
return ret;
}
static int max98506_regmap_update_bits(struct max98506_priv *max98506,
unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret = 0;
ret = regmap_update_bits(max98506->regmap, reg, mask, val);
if (max98506->sub_regmap)
ret = regmap_update_bits(max98506->sub_regmap, reg, mask, val);
return ret;
}
static struct reg_default max98506_reg[] = {
{ 0x02, 0x00 }, /* Live Status0 */
{ 0x03, 0x00 }, /* Live Status1 */
{ 0x04, 0x00 }, /* Live Status2 */
{ 0x05, 0x00 }, /* State0 */
{ 0x06, 0x00 }, /* State1 */
{ 0x07, 0x00 }, /* State2 */
{ 0x08, 0x00 }, /* Flag0 */
{ 0x09, 0x00 }, /* Flag1 */
{ 0x0A, 0x00 }, /* Flag2 */
{ 0x0B, 0x00 }, /* IRQ Enable0 */
{ 0x0C, 0x00 }, /* IRQ Enable1 */
{ 0x0D, 0x00 }, /* IRQ Enable2 */
{ 0x0E, 0x00 }, /* IRQ Clear0 */
{ 0x0F, 0x00 }, /* IRQ Clear1 */
{ 0x10, 0x00 }, /* IRQ Clear2 */
{ 0x1A, 0x06 }, /* DAI Clock Mode 1 */
{ 0x1B, 0xC0 }, /* DAI Clock Mode 2 */
{ 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
{ 0x20, 0x50 }, /* Format */
{ 0x21, 0x00 }, /* TDM Slot Select */
{ 0x22, 0x00 }, /* DOUT Configuration VMON */
{ 0x23, 0x00 }, /* DOUT Configuration IMON */
{ 0x24, 0x00 }, /* DAI Interleaved Configuration */
{ 0x26, 0x00 }, /* DOUT Configuration FLAG */
{ 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
{ 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
{ 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
{ 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
{ 0x2B, 0x02 }, /* DOUT Drive Strength */
{ 0x2C, 0x90 }, /* Filters */
{ 0x2D, 0x00 }, /* Gain */
{ 0x2E, 0x02 }, /* Gain Ramping */
{ 0x2F, 0x00 }, /* Speaker Amplifier */
{ 0x30, 0x0A }, /* Threshold */
{ 0x31, 0x00 }, /* ALC Attack */
{ 0x32, 0x80 }, /* ALC Atten and Release */
{ 0x33, 0x00 }, /* ALC Infinite Hold Release */
{ 0x34, 0x92 }, /* ALC Configuration */
{ 0x35, 0x01 }, /* Boost Converter */
{ 0x36, 0x00 }, /* Block Enable */
{ 0x37, 0x00 }, /* Configuration */
{ 0x38, 0x00 }, /* Global Enable */
{ 0x3A, 0x00 }, /* Boost Limiter */
{ 0xFF, 0x00 }, /* Revision ID */
};
static bool max98506_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX98506_R002_LIVE_STATUS0:
case MAX98506_R003_LIVE_STATUS1:
case MAX98506_R004_LIVE_STATUS2:
case MAX98506_R005_STATE0:
case MAX98506_R006_STATE1:
case MAX98506_R007_STATE2:
case MAX98506_R008_FLAG0:
case MAX98506_R009_FLAG1:
case MAX98506_R00A_FLAG2:
case MAX98506_R0FF_VERSION:
return true;
default:
return false;
}
}
static bool max98506_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x00:
case 0x01:
case MAX98506_R00E_IRQ_CLEAR0:
case MAX98506_R00F_IRQ_CLEAR1:
case MAX98506_R010_IRQ_CLEAR2:
case 0x1C:
case 0x1D:
case 0x1E:
case 0x25:
case MAX98506_R033_ALC_HOLD_RLS:
case 0x39:
case 0x3B ... 0xFE:
return false;
default:
return true;
}
};
#ifdef CONFIG_SND_SOC_MAXIM_DSM
#ifdef USE_DSM_LOG
static int max98506_get_dump_status(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = maxdsm_get_dump_status();
return 0;
}
static int max98506_set_dump_status(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int val;
regmap_read(max98506->regmap,
MAX98506_R038_GLOBAL_ENABLE, &val);
msg_maxim("val: %d", val);
if (val != 0)
maxdsm_update_param();
return 0;
}
static ssize_t max98506_log_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return maxdsm_log_prepare(buf);
}
static DEVICE_ATTR(dsm_log, S_IRUGO, max98506_log_show, NULL);
static ssize_t max98506_log_spk_excu_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%d", values.excursion_max);
}
static DEVICE_ATTR(spk_excu_max, S_IRUGO, max98506_log_spk_excu_max_show, NULL);
static ssize_t max98506_log_spk_excu_maxtime_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%s", values.dsm_timestamp);
}
static DEVICE_ATTR(spk_excu_maxtime, S_IRUGO, max98506_log_spk_excu_maxtime_show, NULL);
static ssize_t max98506_log_spk_excu_overcnt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%d", values.excursion_overcnt);
}
static DEVICE_ATTR(spk_excu_overcnt, S_IRUGO, max98506_log_spk_excu_overcnt_show, NULL);
static ssize_t max98506_log_spk_temp_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%d", values.coil_temp_max);
}
static DEVICE_ATTR(spk_temp_max, S_IRUGO, max98506_log_spk_temp_max_show, NULL);
static ssize_t max98506_log_spk_temp_maxtime_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%s", values.dsm_timestamp);
}
static DEVICE_ATTR(spk_temp_maxtime, S_IRUGO, max98506_log_spk_temp_maxtime_show, NULL);
static ssize_t max98506_log_spk_temp_overcnt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct maxim_dsm_log_max_values values;
maxdsm_log_max_prepare(&values);
return sprintf(buf, "%d", values.coil_temp_overcnt);
}
static DEVICE_ATTR(spk_temp_overcnt, S_IRUGO, max98506_log_spk_temp_overcnt_show, NULL);
#endif /* USE_DSM_LOG */
#ifdef USE_DSM_UPDATE_CAL
static int max98506_get_dsm_param(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = maxdsm_cal_avail();
return 0;
}
static int max98506_set_dsm_param(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
maxdsm_update_caldata(ucontrol->value.integer.value[0]);
return 0;
}
static ssize_t max98506_cal_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return maxdsm_cal_prepare(buf);
}
static DEVICE_ATTR(dsm_cal, S_IRUGO, max98506_cal_show, NULL);
#endif /* USE_DSM_UPDATE_CAL */
#if defined(USE_DSM_LOG) || defined(USE_DSM_UPDATE_CAL)
#define DEFAULT_LOG_CLASS_NAME "dsm"
static const char *class_name_log = DEFAULT_LOG_CLASS_NAME;
static struct attribute *max98506_attributes[] = {
#ifdef USE_DSM_LOG
&dev_attr_dsm_log.attr,
&dev_attr_spk_excu_max.attr,
&dev_attr_spk_excu_maxtime.attr,
&dev_attr_spk_excu_overcnt.attr,
&dev_attr_spk_temp_max.attr,
&dev_attr_spk_temp_maxtime.attr,
&dev_attr_spk_temp_overcnt.attr,
#endif /* USE_DSM_LOG */
#ifdef USE_DSM_UPDATE_CAL
&dev_attr_dsm_cal.attr,
#endif /* USE_DSM_UPDATE_CAL */
NULL
};
static struct attribute_group max98506_attribute_group = {
.attrs = max98506_attributes
};
#endif /* USE_DSM_LOG || USE_DSM_UPDATE_CAL */
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
static const unsigned int max98506_spk_tlv[] = {
TLV_DB_RANGE_HEAD(1),
1, 31, TLV_DB_SCALE_ITEM(-600, 100, 0),
};
static int max98506_spk_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
ucontrol->value.integer.value[0] = pdata->spk_gain;
return 0;
}
static int max98506_spk_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
unsigned int sel = (unsigned int)ucontrol->value.integer.value[0];
max98506_regmap_update_bits(max98506, MAX98506_R02D_GAIN,
MAX98506_SPK_GAIN_MASK, sel << MAX98506_SPK_GAIN_SHIFT);
pdata->spk_gain = sel;
return 0;
}
static int max98506_reg_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol, unsigned int reg,
unsigned int mask, unsigned int shift)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int data;
regmap_read(max98506->regmap, reg, &data);
ucontrol->value.integer.value[0] =
(data & mask) >> shift;
return 0;
}
static int max98506_reg_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol, unsigned int reg,
unsigned int mask, unsigned int shift)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int sel = (unsigned int)ucontrol->value.integer.value[0];
max98506_regmap_update_bits(max98506, reg, mask, sel << shift);
return 0;
}
static int max98506_spk_ramp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_get(kcontrol, ucontrol, MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_RMP_EN_MASK, MAX98506_SPK_RMP_EN_SHIFT);
}
static int max98506_spk_ramp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_put(kcontrol, ucontrol, MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_RMP_EN_MASK, MAX98506_SPK_RMP_EN_SHIFT);
}
static int max98506_spk_zcd_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_get(kcontrol, ucontrol, MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_ZCD_EN_MASK, MAX98506_SPK_ZCD_EN_SHIFT);
}
static int max98506_spk_zcd_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_put(kcontrol, ucontrol, MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_ZCD_EN_MASK, MAX98506_SPK_ZCD_EN_SHIFT);
}
static int max98506_alc_en_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_get(kcontrol, ucontrol, MAX98506_R030_THRESHOLD,
MAX98506_ALC_EN_MASK, MAX98506_ALC_EN_SHIFT);
}
static int max98506_alc_en_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_put(kcontrol, ucontrol, MAX98506_R030_THRESHOLD,
MAX98506_ALC_EN_MASK, MAX98506_ALC_EN_SHIFT);
}
static int max98506_alc_threshold_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_get(kcontrol, ucontrol, MAX98506_R030_THRESHOLD,
MAX98506_ALC_TH_MASK, MAX98506_ALC_TH_SHIFT);
}
static int max98506_alc_threshold_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return max98506_reg_put(kcontrol, ucontrol, MAX98506_R030_THRESHOLD,
MAX98506_ALC_TH_MASK, MAX98506_ALC_TH_SHIFT);
}
static const char * const max98506_boost_voltage_text[] = {"8.5V", "8.25V",
"8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
"6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"};
static const struct soc_enum max98506_boost_voltage_enum =
SOC_ENUM_SINGLE(MAX98506_R037_CONFIGURATION,
MAX98506_BST_VOUT_SHIFT, 16,
max98506_boost_voltage_text);
static const char * const spk_state_text[] = {"Disable", "Enable"};
static const struct soc_enum spk_state_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_state_text), spk_state_text),
};
static const char * const max98506_one_stop_mode_text[] = {
"Mono Left", "Mono Right",
"Receiver Left", "Receiver Right",
"Stereo",
"Stereo II",
};
static const struct soc_enum max98506_one_stop_mode_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98506_one_stop_mode_text),
max98506_one_stop_mode_text);
static const char * const spk_en_text[] = {"Disable", "Enable"};
static const struct soc_enum max98506_spk_en_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_en_text),
spk_en_text),
};
static const char * const spk_clkmon_text[] = {"Disable", "Enable"};
static const struct soc_enum max98506_spk_clkmon_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_clkmon_text),
spk_clkmon_text),
};
static const char * const spk_input_select_text[] = {"PCM", "Analog"};
static const struct soc_enum max98506_spk_input_select_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_input_select_text),
spk_input_select_text),
};
static int __max98506_spk_enable(struct max98506_priv *max98506)
{
struct max98506_pdata *pdata = max98506->pdata;
struct max98506_volume_step_info *vstep = &max98506->vstep;
unsigned int gain_l, gain_r;
unsigned int enable_l, enable_r;
unsigned int zcd_l, zcd_r, spk_mode_l, spk_mode_r;
unsigned int vimon = vstep->adc_status ? MAX98506_ADC_VIMON_EN_MASK : 0;
unsigned int boostv = pdata->boostv;
unsigned int rev_id = pdata->rev_id;
#ifdef CONFIG_SND_SOC_MAXIM_DSM
unsigned int smode_table[MAX98506_OSM_MAX] = {
2, /* MAX98506_OSM_MONO_L */
1, /* MAX98506_OSM_MONO_R */
2, /* MAX98506_OSM_RCV_L */
1, /* MAX98506_OSM_RCV_R */
0, /* MAX98506_OSM_STEREO */
3, /* MAX98506_OSM_STEREO_MODE2 */
};
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
gain_l = gain_r = pdata->spk_gain;
enable_l = enable_r = 0;
zcd_l = zcd_r = spk_mode_l = spk_mode_r = 0;
vimon = pdata->nodsm ? 0 : vimon;
switch (pdata->osm) {
case MAX98506_OSM_STEREO_MODE2:
case MAX98506_OSM_STEREO:
enable_l = enable_r = MAX98506_EN_MASK;
break;
case MAX98506_OSM_RCV_L:
gain_l = ((rev_id & 0xFF) == MAX98506_VERSION2)
? 0x0D : 0x05; /* -2 dB */
zcd_l = 0x01; /* turn on RCV mode */
vimon = 0; /* turn off VIMON */
boostv = 0x0F; /* 6.5V */
spk_mode_l = MAX98506_SPK_MODE_MASK;
case MAX98506_OSM_MONO_L:
enable_l = MAX98506_EN_MASK;
break;
case MAX98506_OSM_RCV_R:
gain_r = (((rev_id >> 8) & 0xFF) == MAX98506_VERSION2)
? 0x0D : 0x05; /* -2 dB */
zcd_r = 0x01; /* turn on RCV mode */
vimon = 0; /* turn off VIMON */
boostv = 0x0F; /* 6.5V */
spk_mode_r = MAX98506_SPK_MODE_MASK;
case MAX98506_OSM_MONO_R:
enable_r = MAX98506_EN_MASK;
break;
default:
msg_maxim("Invalid one_stop_mode");
return -EINVAL;
}
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_set_stereo_mode_configuration(smode_table[pdata->osm]);
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
/*
* If revision IDs are not VERSION2,
* zero-cross detect should be always enabled
*/
if ((rev_id & 0xFF) != MAX98506_VERSION2)
zcd_l = 0x01;
if (((rev_id >> 8) & 0xFF) != MAX98506_VERSION2)
zcd_r = 0x01;
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_SPK_EN_MASK,
0);
regmap_update_bits(max98506->regmap,
MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_ZCD_EN_MASK,
zcd_l);
if (max98506->sub_regmap)
regmap_update_bits(max98506->sub_regmap,
MAX98506_R02E_GAIN_RAMPING,
MAX98506_SPK_ZCD_EN_MASK,
zcd_r);
regmap_update_bits(max98506->regmap,
MAX98506_R02F_SPK_AMP,
MAX98506_SPK_MODE_MASK,
spk_mode_l);
if (max98506->sub_regmap)
regmap_update_bits(max98506->sub_regmap,
MAX98506_R02F_SPK_AMP,
MAX98506_SPK_MODE_MASK,
spk_mode_r);
if (max98506->speaker_dac_enable) {
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_SPK_EN_MASK,
MAX98506_SPK_EN_MASK);
}
regmap_update_bits(max98506->regmap,
MAX98506_R02D_GAIN,
MAX98506_SPK_GAIN_MASK,
gain_l);
if (max98506->sub_regmap)
regmap_update_bits(max98506->sub_regmap,
MAX98506_R02D_GAIN,
MAX98506_SPK_GAIN_MASK,
gain_r);
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_BST_EN_MASK,
MAX98506_BST_EN_MASK);
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
vimon);
vstep->adc_status = !!vimon;
max98506_regmap_update_bits(max98506,
MAX98506_R037_CONFIGURATION,
MAX98506_BST_VOUT_MASK,
boostv << MAX98506_BST_VOUT_SHIFT);
regmap_write(max98506->regmap,
MAX98506_R038_GLOBAL_ENABLE,
enable_l);
if (max98506->sub_regmap)
regmap_write(max98506->sub_regmap,
MAX98506_R038_GLOBAL_ENABLE,
enable_r);
return 0;
}
static void max98506_spk_enable(struct max98506_priv *max98506,
int enable)
{
if (enable)
__max98506_spk_enable(max98506);
else {
max98506_regmap_update_bits(max98506,
MAX98506_R038_GLOBAL_ENABLE,
MAX98506_EN_MASK,
0x00);
usleep_range(10000, 11000);
}
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_set_spk_state(enable);
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
}
static int max98506_spk_out_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
regmap_read(max98506->regmap,
MAX98506_R038_GLOBAL_ENABLE, &val);
ucontrol->value.integer.value[0] = !!(val & MAX98506_EN_MASK);
msg_maxim("The status of speaker is '%s'",
spk_state_text[ucontrol->value.integer.value[0]]);
return 0;
}
static int max98506_spk_out_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int enable = !!ucontrol->value.integer.value[0];
max98506_spk_enable(max98506, enable);
msg_maxim("Speaker was set by '%s'", spk_state_text[enable]);
return 0;
}
static int max98506_adc_en_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int data;
regmap_read(max98506->regmap, MAX98506_R036_BLOCK_ENABLE, &data);
if (data & MAX98506_ADC_VIMON_EN_MASK)
ucontrol->value.integer.value[0] = 1;
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int max98506_adc_en_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
struct max98506_volume_step_info *vstep = &max98506->vstep;
int sel = (int)ucontrol->value.integer.value[0];
if (!pdata->nodsm) {
if (sel)
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
MAX98506_ADC_VIMON_EN_MASK);
else
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
0);
vstep->adc_status = !!sel;
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_update_feature_en_adc(!!sel);
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
}
return 0;
}
static int max98506_adc_thres_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_volume_step_info *vstep = &max98506->vstep;
ucontrol->value.integer.value[0] = vstep->adc_thres;
return 0;
}
static int max98506_adc_thres_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_volume_step_info *vstep = &max98506->vstep;
int ret = 0;
if (ucontrol->value.integer.value[0] >= MAX98506_VSTEP_0 &&
ucontrol->value.integer.value[0] <= MAX98506_VSTEP_15)
vstep->adc_thres = (int)ucontrol->value.integer.value[0];
else
ret = -EINVAL;
return ret;
}
static int max98506_volume_step_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_volume_step_info *vstep = &max98506->vstep;
ucontrol->value.integer.value[0] = vstep->vol_step;
return 0;
}
static int max98506_volume_step_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
struct max98506_volume_step_info *vstep = &max98506->vstep;
int sel = (int)ucontrol->value.integer.value[0];
unsigned int mask = 0;
bool adc_status = vstep->adc_status;
/*
* ADC status will be updated according to the volume.
* Under step 7 : Disable
* Over step 7 : Enable
*/
if (sel >= MAX98506_VSTEP_MAX) {
msg_maxim("Unknown value %d", sel);
return -EINVAL;
}
if (!pdata->nodsm) {
if (sel <= vstep->adc_thres
&& vstep->adc_status) {
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
0);
adc_status = !vstep->adc_status;
} else if (sel > vstep->adc_thres
&& !vstep->adc_status) {
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
MAX98506_ADC_VIMON_EN_MASK);
adc_status = !vstep->adc_status;
}
if (adc_status != vstep->adc_status) {
vstep->adc_status = adc_status;
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_update_feature_en_adc((int)adc_status);
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
}
}
/*
* Boost voltage will be updated according to the volume.
* Step 0 ~ Step 13 : 6.5V
* Step 14 : 8.0V
* Over step 15 : 8.5V
*/
mask |= vstep->boost_step[sel];
max98506->pdata->boostv = mask;
mask <<= MAX98506_BST_VOUT_SHIFT;
max98506_regmap_update_bits(max98506,
MAX98506_R037_CONFIGURATION,
MAX98506_BST_VOUT_MASK,
mask);
/* Set volume step to ... */
vstep->vol_step = sel;
return 0;
}
static int max98506_one_stop_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
ucontrol->value.integer.value[0] = pdata->osm;
return 0;
}
static int max98506_one_stop_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
int osm = (int)ucontrol->value.integer.value[0];
osm = osm < 0 ? 0 : osm;
if (osm < MAX98506_OSM_MAX) {
pdata->osm = osm;
__max98506_spk_enable(max98506);
}
return osm >= MAX98506_OSM_MAX ? -EINVAL : 0;
}
static int max98506_spk_en_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int value = 0;
regmap_read(max98506->regmap,
MAX98506_R036_BLOCK_ENABLE,
&value);
ucontrol->value.integer.value[0] =
(value & MAX98506_SPK_EN_MASK) > 0 ? 1 : 0;
return 0;
}
static int max98506_spk_en_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int enable = !!(int)ucontrol->value.integer.value[0];
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_SPK_EN_MASK,
enable ? MAX98506_SPK_EN_MASK : 0);
max98506->speaker_dac_enable = enable;
return 0;
}
static int max98506_spk_clkmon_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int value = 0;
regmap_read(max98506->regmap,
MAX98506_R036_BLOCK_ENABLE,
&value);
ucontrol->value.integer.value[0] =
(value & MAX98506_CLKMON_EN_MASK) > 0 ? 1 : 0;
return 0;
}
static int max98506_spk_clkmon_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int enable = !!(int)ucontrol->value.integer.value[0];
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_CLKMON_EN_MASK,
enable ? MAX98506_CLKMON_EN_MASK : 0);
return 0;
}
static int max98506_spk_input_select_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int value = 0;
regmap_read(max98506->regmap,
MAX98506_R02F_SPK_AMP,
&value);
ucontrol->value.integer.value[0] =
(value & MAX98506_SPK_INSEL_MASK) > 0 ? 1 : 0;
return 0;
}
static int max98506_spk_input_select_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
int input_sel = (int)ucontrol->value.integer.value[0];
max98506_regmap_update_bits(max98506,
MAX98506_R02F_SPK_AMP,
MAX98506_SPK_INSEL_MASK,
input_sel ? MAX98506_SPK_INSEL_MASK : 0);
return 0;
}
static const struct snd_kcontrol_new max98506_snd_controls[] = {
SOC_SINGLE_EXT_TLV("Speaker Gain", MAX98506_R02D_GAIN,
MAX98506_SPK_GAIN_SHIFT,
(1 << MAX98506_SPK_GAIN_WIDTH) - 1,
0,
max98506_spk_gain_get,
max98506_spk_gain_put,
max98506_spk_tlv),
SOC_SINGLE_EXT("Speaker Ramp", 0, 0, 1, 0,
max98506_spk_ramp_get, max98506_spk_ramp_put),
SOC_SINGLE_EXT("Speaker ZCD", 0, 0, 1, 0,
max98506_spk_zcd_get, max98506_spk_zcd_put),
SOC_SINGLE_EXT("ALC Enable", 0, 0, 1, 0,
max98506_alc_en_get, max98506_alc_en_put),
SOC_SINGLE_EXT("ALC Threshold", 0, 0, (1<<MAX98506_ALC_TH_WIDTH)-1, 0,
max98506_alc_threshold_get, max98506_alc_threshold_put),
SOC_ENUM("Boost Output Voltage", max98506_boost_voltage_enum),
SOC_ENUM_EXT("SPK out", spk_state_enum[0],
max98506_spk_out_get, max98506_spk_out_put),
SOC_SINGLE_EXT("ADC Enable", 0, 0, 1, 0,
max98506_adc_en_get, max98506_adc_en_put),
SOC_SINGLE_EXT("ADC Threshold", SND_SOC_NOPM, 0, 15, 0,
max98506_adc_thres_get, max98506_adc_thres_put),
SOC_SINGLE_EXT("Volume Step", SND_SOC_NOPM, 0, 15, 0,
max98506_volume_step_get, max98506_volume_step_put),
SOC_ENUM_EXT("One Stop Mode", max98506_one_stop_mode_enum,
max98506_one_stop_mode_get, max98506_one_stop_mode_put),
SOC_ENUM_EXT("SPK Enable Switch", max98506_spk_en_enum,
max98506_spk_en_get,
max98506_spk_en_put),
SOC_ENUM_EXT("SPK CLK Monitor Enable", max98506_spk_clkmon_enum,
max98506_spk_clkmon_get,
max98506_spk_clkmon_put),
SOC_ENUM_EXT("Input Select", max98506_spk_input_select_enum,
max98506_spk_input_select_get,
max98506_spk_input_select_put),
#ifdef USE_DSM_LOG
SOC_SINGLE_EXT("DSM LOG", SND_SOC_NOPM, 0, 3, 0,
max98506_get_dump_status, max98506_set_dump_status),
#endif /* USE_DSM_LOG */
#ifdef USE_DSM_UPDATE_CAL
SOC_SINGLE_EXT("DSM SetParam", SND_SOC_NOPM, 0, 1, 0,
max98506_get_dsm_param, max98506_set_dsm_param),
#endif /* USE_DSM_UPDATE_CAL */
};
/* codec sample rate and n/m dividers parameter table */
static const struct {
u32 rate;
u8 sr;
} rate_table[] = {
{ 8000, 0 },
{ 11025, 1 },
{ 12000, 2 },
{ 16000, 3 },
{ 22050, 4 },
{ 24000, 5 },
{ 32000, 6 },
{ 44100, 7 },
{ 48000, 8 },
};
static inline int max98506_rate_value(int rate, int clock, u8 *value)
{
int ret = -ENODATA;
int i;
for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
if (rate_table[i].rate >= rate) {
*value = rate_table[i].sr;
ret = 0;
break;
}
}
msg_maxim("sample rate is %d, returning %d",
rate_table[i < ARRAY_SIZE(rate_table) ? i : ARRAY_SIZE(rate_table)].rate, *value);
return ret;
}
static int max98506_set_tdm_slot(struct snd_soc_dai *codec_dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
msg_maxim("tx_mask 0x%X, rx_mask 0x%X, slots %d, slot width %d",
tx_mask, rx_mask, slots, slot_width);
return 0;
}
static void max98506_set_slave(struct max98506_priv *max98506)
{
struct max98506_pdata *pdata = max98506->pdata;
msg_maxim("enter");
/*
* 1. use BCLK instead of MCLK
*/
max98506_regmap_update_bits(max98506,
MAX98506_R01A_DAI_CLK_MODE1,
MAX98506_DAI_CLK_SOURCE_MASK,
MAX98506_DAI_CLK_SOURCE_MASK);
/*
* 2. set DAI to slave mode
*/
max98506_regmap_update_bits(max98506,
MAX98506_R01B_DAI_CLK_MODE2,
MAX98506_DAI_MAS_MASK,
0);
/*
* 3. set BLCKs to LRCLKs to 64
*/
if (max98506->sub_regmap && !pdata->interleave)
max98506_regmap_update_bits(max98506,
MAX98506_R01B_DAI_CLK_MODE2,
MAX98506_DAI_BSEL_MASK,
MAX98506_DAI_BSEL_64);
else
max98506_regmap_update_bits(max98506,
MAX98506_R01B_DAI_CLK_MODE2,
MAX98506_DAI_BSEL_MASK,
MAX98506_DAI_BSEL_32);
/*
* 4. set VMON and IMON slots
*/
max98506_regmap_update_bits(max98506,
MAX98506_R022_DOUT_CFG_VMON,
MAX98506_DAI_VMON_EN_MASK,
MAX98506_DAI_VMON_EN_MASK);
max98506_regmap_update_bits(max98506,
MAX98506_R023_DOUT_CFG_IMON,
MAX98506_DAI_IMON_EN_MASK,
MAX98506_DAI_IMON_EN_MASK);
if (!pdata->vmon_slot) {
max98506_regmap_update_bits(max98506,
MAX98506_R022_DOUT_CFG_VMON,
MAX98506_DAI_VMON_SLOT_MASK,
MAX98506_DAI_VMON_SLOT_02_03);
max98506_regmap_update_bits(max98506,
MAX98506_R023_DOUT_CFG_IMON,
MAX98506_DAI_IMON_SLOT_MASK,
MAX98506_DAI_IMON_SLOT_00_01);
} else {
max98506_regmap_update_bits(max98506,
MAX98506_R022_DOUT_CFG_VMON,
MAX98506_DAI_VMON_SLOT_MASK,
MAX98506_DAI_VMON_SLOT_00_01);
max98506_regmap_update_bits(max98506,
MAX98506_R023_DOUT_CFG_IMON,
MAX98506_DAI_IMON_SLOT_MASK,
MAX98506_DAI_IMON_SLOT_02_03);
}
}
static void max98506_set_master(struct max98506_priv *max98506)
{
msg_maxim("enter");
/*
* 1. use MCLK for Left channel, right channel always BCLK
*/
max98506_regmap_update_bits(max98506, MAX98506_R01A_DAI_CLK_MODE1,
MAX98506_DAI_CLK_SOURCE_MASK, 0);
/*
* 2. set left channel DAI to master mode, right channel always slave
*/
max98506_regmap_update_bits(max98506, MAX98506_R01B_DAI_CLK_MODE2,
MAX98506_DAI_MAS_MASK, MAX98506_DAI_MAS_MASK);
}
static int max98506_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int invert = 0;
msg_maxim("fmt 0x%08X", fmt);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
max98506_set_slave(max98506);
break;
case SND_SOC_DAIFMT_CBM_CFM:
max98506_set_master(max98506);
break;
case SND_SOC_DAIFMT_CBS_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
default:
dev_err(codec->dev, "DAI clock mode unsupported");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
msg_maxim("set SND_SOC_DAIFMT_I2S");
break;
case SND_SOC_DAIFMT_LEFT_J:
msg_maxim("set SND_SOC_DAIFMT_LEFT_J");
break;
case SND_SOC_DAIFMT_DSP_A:
msg_maxim("set SND_SOC_DAIFMT_DSP_A");
default:
dev_warn(codec->dev, "DAI format unsupported, fmt:0x%x", fmt);
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
invert = MAX98506_DAI_WCI_MASK;
break;
case SND_SOC_DAIFMT_IB_NF:
invert = MAX98506_DAI_BCI_MASK;
break;
case SND_SOC_DAIFMT_IB_IF:
invert = MAX98506_DAI_BCI_MASK | MAX98506_DAI_WCI_MASK;
break;
default:
dev_err(codec->dev, "DAI invert mode unsupported");
return -EINVAL;
}
max98506_regmap_update_bits(max98506, MAX98506_R020_FORMAT,
MAX98506_DAI_BCI_MASK | MAX98506_DAI_WCI_MASK, invert);
return 0;
}
static int max98506_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
/* todo */
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_OFF:
break;
}
return 0;
}
static int max98506_set_clock(struct max98506_priv *max98506, unsigned int rate)
{
struct snd_soc_codec *codec = max98506->codec;
struct max98506_pdata *pdata = max98506->pdata;
unsigned int clock;
unsigned int mdll;
u8 dai_sr = 0;
switch (pdata->sysclk) {
case 6000000:
clock = 0;
mdll = MAX98506_MDLL_MULT_MCLKx16;
break;
case 11289600:
clock = 1;
mdll = MAX98506_MDLL_MULT_MCLKx8;
break;
case 12000000:
clock = 0;
mdll = MAX98506_MDLL_MULT_MCLKx8;
break;
case 12288000:
clock = 2;
mdll = MAX98506_MDLL_MULT_MCLKx8;
break;
default:
dev_info(codec->dev, "unsupported sysclk %d\n",
pdata->sysclk);
return -EINVAL;
}
if (max98506_rate_value(rate, clock, &dai_sr))
return -EINVAL;
/*
* 1. set DAI_SR to correct LRCLK frequency
*/
max98506_regmap_update_bits(max98506, MAX98506_R01B_DAI_CLK_MODE2,
MAX98506_DAI_SR_MASK, dai_sr << MAX98506_DAI_SR_SHIFT);
/*
* 2. set MDLL
*/
max98506_regmap_update_bits(max98506,
MAX98506_R01A_DAI_CLK_MODE1,
MAX98506_MDLL_MULT_MASK,
mdll << MAX98506_MDLL_MULT_SHIFT);
/*
* 3. set in accordance to sampling frequency
*/
switch (rate) {
case 44100:
case 22050:
case 11025:
dai_sr = 1;
break;
default:
dai_sr = 0;
break;
}
max98506_regmap_update_bits(max98506,
MAX98506_R035_BOOST_CONVERTER,
MAX98506_BST_SYNC_MASK,
dai_sr << MAX98506_BST_SYNC_SHIFT);
return 0;
}
static int max98506_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;
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
unsigned int rate;
msg_maxim("enter");
rate = params_rate(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
msg_maxim("set SNDRV_PCM_FORMAT_S16_LE");
max98506_regmap_update_bits(max98506,
MAX98506_R020_FORMAT,
MAX98506_DAI_CHANSZ_MASK,
MAX98506_DAI_CHANSZ_16);
break;
case SNDRV_PCM_FORMAT_S24_LE:
msg_maxim("set SNDRV_PCM_FORMAT_S24_LE");
max98506_regmap_update_bits(max98506,
MAX98506_R020_FORMAT,
MAX98506_DAI_CHANSZ_MASK,
MAX98506_DAI_CHANSZ_24);
break;
case SNDRV_PCM_FORMAT_S32_LE:
msg_maxim("set SNDRV_PCM_FORMAT_S32_LE");
max98506_regmap_update_bits(max98506,
MAX98506_R020_FORMAT,
MAX98506_DAI_CHANSZ_MASK,
MAX98506_DAI_CHANSZ_32);
break;
default:
msg_maxim("format unsupported %d", params_format(params));
return -EINVAL;
}
return max98506_set_clock(max98506, rate);
}
static int max98506_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
msg_maxim("clk_id %d, freq %d, dir %d", clk_id, freq, dir);
pdata->sysclk = freq;
return 0;
}
static int max98506_dai_mute_stream(struct snd_soc_dai *dai,
int mute, int stream)
{
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(dai->codec);
#ifdef CONFIG_SND_SOC_MAXIM_DSM
struct max98506_pdata *pdata = max98506->pdata;
int rdc = 0, temp = 0;
int ret = 0;
#ifdef USE_DSM_LOG
if ((stream == SNDRV_PCM_STREAM_PLAYBACK) && mute)
maxdsm_update_param(); /* get logging parameters */
#endif
#endif
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
msg_maxim("max98506_spk_enable mute=%d\n", mute);
max98506_spk_enable(max98506, mute != 0 ? 0 : 1);
}
#ifdef CONFIG_SND_SOC_MAXIM_DSM
if ((!pdata->nodsm)
&& (stream == SNDRV_PCM_STREAM_CAPTURE) && (mute == 0)) {
msg_maxim("set maxdsm calibration\n");
ret = maxdsm_cal_get_rdc(&rdc);
if (ret || rdc <= 0) {
pr_info("%s: rdc(0x%08x)\n",
__func__, rdc);
goto exit;
}
ret = maxdsm_cal_get_temp(&temp);
if (ret || temp <= 0) {
pr_info("%s: temp(%d)\n",
__func__, temp);
goto exit;
}
ret = maxdsm_set_rdc_temp(rdc, (int)(temp / 10));
if (ret < 0) {
pr_err("%s: Failed to set calibration ret = (%d)\n",
__func__, ret);
}
}
exit:
#ifdef USE_REG_DUMP
reg_dump(max98506);
#endif /* USE_REG_DUMP */
#endif
return 0;
}
#define MAX98506_RATES SNDRV_PCM_RATE_8000_48000
#define MAX98506_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops max98506_dai_ops = {
.set_sysclk = max98506_dai_set_sysclk,
.set_fmt = max98506_dai_set_fmt,
.set_tdm_slot = max98506_set_tdm_slot,
.hw_params = max98506_dai_hw_params,
.mute_stream = max98506_dai_mute_stream,
};
static struct snd_soc_dai_driver max98506_dai[] = {
{
.name = "max98506-aif1",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MAX98506_RATES,
.formats = MAX98506_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = MAX98506_RATES,
.formats = MAX98506_FORMATS,
},
.ops = &max98506_dai_ops,
}
};
static void max98506_handle_pdata(struct snd_soc_codec *codec)
{
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
struct reg_default *reg_chg;
int loop;
int len = 0;
if (!pdata) {
dev_dbg(codec->dev, "No platform data\n");
return;
}
len = pdata->reg_arr_len / sizeof(uint32_t);
if (pdata->reg_arr != NULL) {
for (loop = 0; loop < len; loop += 2) {
reg_chg = (struct reg_default *)&pdata->reg_arr[loop];
msg_maxim("[0x%02x, 0x%02x]",
be32_to_cpu(reg_chg->reg),
be32_to_cpu(reg_chg->def));
max98506_regmap_write(max98506,
be32_to_cpu(reg_chg->reg),
be32_to_cpu(reg_chg->def));
}
}
}
#ifdef CONFIG_PM
static int max98506_suspend(struct snd_soc_codec *codec)
{
msg_maxim("enter");
return 0;
}
static int max98506_resume(struct snd_soc_codec *codec)
{
msg_maxim("enter");
return 0;
}
#else
#define max98506_suspend NULL
#define max98506_resume NULL
#endif /* CONFIG_PM */
#ifdef USE_MAX98506_IRQ
static irqreturn_t max98506_interrupt(int irq, void *data)
{
struct max98506_priv *max98506 = (struct max98506_priv *) data;
unsigned int mask0;
unsigned int mask1;
unsigned int mask2;
unsigned int flag0;
unsigned int flag1;
unsigned int flag2;
regmap_read(max98506->regmap, MAX98506_R00B_IRQ_ENABLE0, &mask0);
regmap_read(max98506->regmap, MAX98506_R008_FLAG0, &flag0);
regmap_read(max98506->regmap, MAX98506_R00C_IRQ_ENABLE1, &mask1);
regmap_read(max98506->regmap, MAX98506_R009_FLAG1, &flag1);
regmap_read(max98506->regmap, MAX98506_R00D_IRQ_ENABLE2, &mask2);
regmap_read(max98506->regmap, MAX98506_R00A_FLAG2, &flag2);
flag0 &= mask0;
flag1 &= mask1;
flag2 &= mask2;
if (!flag0 && !flag1 && !flag2)
return IRQ_NONE;
/* Send work to be scheduled */
if (flag0 & MAX98506_THERMWARN_END_STATE_MASK)
msg_maxim("MAX98506_THERMWARN_STATE_MASK active!");
if (flag0 & MAX98506_THERMWARN_BGN_STATE_MASK)
msg_maxim("MAX98506_THERMWARN_BGN_STATE_MASK active!");
if (flag0 & MAX98506_THERMSHDN_END_STATE_MASK)
msg_maxim("MAX98506_THERMSHDN_END_STATE_MASK active!");
if (flag0 & MAX98506_THERMSHDN_BGN_STATE_MASK)
msg_maxim("MAX98506_THERMSHDN_BGN_STATE_MASK active!");
if (flag1 & MAX98506_SPRCURNT_STATE_MASK)
msg_maxim("MAX98506_SPRCURNT_STATE_MASK active!");
if (flag1 & MAX98506_WATCHFAIL_STATE_MASK)
msg_maxim("MAX98506_WATCHFAIL_STATE_MASK active!");
if (flag1 & MAX98506_ALCINFH_STATE_MASK)
msg_maxim("MAX98506_ALCINFH_STATE_MASK active!");
if (flag1 & MAX98506_ALCACT_STATE_MASK)
msg_maxim("MAX98506_ALCACT_STATE_MASK active!");
if (flag1 & MAX98506_ALCMUT_STATE_MASK)
msg_maxim("MAX98506_ALCMUT_STATE_MASK active!");
if (flag1 & MAX98506_ALCP_STATE_MASK)
msg_maxim("MAX98506_ALCP_STATE_MASK active!");
if (flag2 & MAX98506_SLOTOVRN_STATE_MASK)
msg_maxim("MAX98506_SLOTOVRN_STATE_MASK active!");
if (flag2 & MAX98506_INVALSLOT_STATE_MASK)
msg_maxim("MAX98506_INVALSLOT_STATE_MASK active!");
if (flag2 & MAX98506_SLOTCNFLT_STATE_MASK)
msg_maxim("MAX98506_SLOTCNFLT_STATE_MASK active!");
if (flag2 & MAX98506_VBSTOVFL_STATE_MASK)
msg_maxim("MAX98506_VBSTOVFL_STATE_MASK active!");
if (flag2 & MAX98506_VBATOVFL_STATE_MASK)
msg_maxim("MAX98506_VBATOVFL_STATE_MASK active!");
if (flag2 & MAX98506_IMONOVFL_STATE_MASK)
msg_maxim("MAX98506_IMONOVFL_STATE_MASK active!");
if (flag2 & MAX98506_VMONOVFL_STATE_MASK)
msg_maxim("MAX98506_VMONOVFL_STATE_MASK active!");
regmap_write(max98506->regmap, MAX98506_R00E_IRQ_CLEAR0,
flag0&0xff);
regmap_write(max98506->regmap, MAX98506_R00F_IRQ_CLEAR1,
flag1&0xff);
regmap_write(max98506->regmap, MAX98506_R010_IRQ_CLEAR2,
flag2&0xff);
return IRQ_HANDLED;
}
#endif /* USE_MAX98506_IRQ */
static uint32_t max98506_check_revID(struct regmap *regmap)
{
int loop;
uint32_t ret = 0;
uint32_t reg = 0;
uint32_t version_table[] = {
MAX98506_VERSION0,
MAX98506_VERSION1,
MAX98506_VERSION2,
};
regmap_read(regmap, MAX98506_R0FF_VERSION, &reg);
for (loop = 0; loop < ARRAY_SIZE(version_table); loop++) {
if (reg == version_table[loop])
ret = reg;
}
return ret;
}
static uint32_t max98506_check_version(struct max98506_priv *max98506)
{
uint32_t ret = 0;
uint32_t rev_id_l = 0;
uint32_t rev_id_r = 0;
rev_id_l = max98506_check_revID(max98506->regmap);
msg_maxim("rev_id_l %#x", rev_id_l);
/* spk 0x00FF, rcv 0xFF00 */
if (max98506->sub_regmap) {
rev_id_r = max98506_check_revID(max98506->sub_regmap);
msg_maxim("rev_id_r %#x", rev_id_r);
ret = ((0xFF & rev_id_r) << 8) | (0xFF & rev_id_l);
} else
ret = 0xFF & rev_id_l;
return ret;
}
static int max98506_probe(struct snd_soc_codec *codec)
{
struct max98506_priv *max98506 = snd_soc_codec_get_drvdata(codec);
struct max98506_pdata *pdata = max98506->pdata;
struct max98506_volume_step_info *vstep = &max98506->vstep;
unsigned int vimon = pdata->nodsm ? 0 : MAX98506_ADC_VIMON_EN_MASK;
unsigned int rev_id = 0;
int ret = 0;
dev_info(codec->dev, "build number %s\n", MAX98506_REVISION);
max98506->codec = codec;
codec->control_data = max98506->regmap;
rev_id = max98506_check_version(max98506);
if (!rev_id) {
dev_err(codec->dev,
"device initialization error (0x%02X)\n",
rev_id);
goto err_version;
}
msg_maxim("device version %#x", rev_id);
pdata->rev_id = rev_id;
max98506_regmap_write(max98506, MAX98506_R038_GLOBAL_ENABLE, 0x00);
max98506_regmap_write(max98506, MAX98506_R020_FORMAT,
MAX98506_DAI_DLY_MASK);
max98506_regmap_write(max98506, MAX98506_R021_TDM_SLOT_SELECT, 0xC8);
max98506_regmap_write(max98506, MAX98506_R027_DOUT_HIZ_CFG1, 0xFF);
max98506_regmap_write(max98506, MAX98506_R028_DOUT_HIZ_CFG2, 0xFF);
max98506_regmap_write(max98506, MAX98506_R029_DOUT_HIZ_CFG3, 0xFF);
max98506_regmap_write(max98506, MAX98506_R02C_FILTERS, 0xD9);
if (max98506->sub_regmap) {
if (pdata->interleave) {
regmap_update_bits(max98506->regmap,
MAX98506_R024_DAI_INT_CFG,
MAX98506_DAI_VBAT_SLOT_MASK,
0x0);
regmap_update_bits(max98506->sub_regmap,
MAX98506_R024_DAI_INT_CFG,
MAX98506_DAI_VBAT_SLOT_MASK,
0x2);
regmap_write(max98506->regmap,
MAX98506_R02A_DOUT_HIZ_CFG4, 0xFC);
regmap_write(max98506->sub_regmap,
MAX98506_R02A_DOUT_HIZ_CFG4, 0xF3);
} else {
regmap_write(max98506->regmap,
MAX98506_R02A_DOUT_HIZ_CFG4, 0xF0);
regmap_write(max98506->sub_regmap,
MAX98506_R02A_DOUT_HIZ_CFG4, 0x0F);
}
regmap_update_bits(max98506->regmap,
MAX98506_R02D_GAIN, MAX98506_DAC_IN_SEL_MASK,
MAX98506_DAC_IN_SEL_LEFT_DAI); /* LEFT */
regmap_update_bits(max98506->sub_regmap,
MAX98506_R02D_GAIN, MAX98506_DAC_IN_SEL_MASK,
MAX98506_DAC_IN_SEL_RIGHT_DAI); /* RIGHT */
max98506_regmap_write(max98506, MAX98506_R02F_SPK_AMP, 0x00);
} else {
max98506_regmap_write(max98506,
MAX98506_R02A_DOUT_HIZ_CFG4, 0xF0);
max98506_regmap_update_bits(max98506,
MAX98506_R02D_GAIN, MAX98506_DAC_IN_SEL_MASK,
MAX98506_DAC_IN_SEL_DIV2_SUMMED_DAI);
max98506_regmap_write(max98506, MAX98506_R02F_SPK_AMP, 0x02);
}
max98506_regmap_write(max98506, MAX98506_R034_ALC_CONFIGURATION, 0x12);
/* Enable ADC */
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_ADC_VIMON_EN_MASK,
vimon);
vstep->adc_status = !!vimon;
/* Enable Speaker */
max98506_regmap_update_bits(max98506,
MAX98506_R036_BLOCK_ENABLE,
MAX98506_SPK_EN_MASK,
MAX98506_SPK_EN_MASK);
/* Set boost output to maximum */
max98506_regmap_write(max98506, MAX98506_R037_CONFIGURATION, 0x00);
pdata->boostv = 0x00; /* 8.5V(default) */
/* Disable ALC muting */
max98506_regmap_write(max98506, MAX98506_R03A_BOOST_LIMITER, 0xF8);
max98506_set_slave(max98506);
max98506_handle_pdata(codec);
if (pdata->interleave)
max98506_regmap_update_bits(max98506,
MAX98506_R020_FORMAT,
MAX98506_DAI_INTERLEAVE_MASK,
MAX98506_DAI_INTERLEAVE_MASK);
#if defined(USE_DSM_LOG) || defined(USE_DSM_UPDATE_CAL)
if (!g_class)
g_class = class_create(THIS_MODULE, class_name_log);
max98506->dev_log_class = g_class;
if (max98506->dev_log_class) {
max98506->dev_log =
device_create(max98506->dev_log_class,
NULL, 1, NULL, "max98506");
if (IS_ERR(max98506->dev_log)) {
ret = sysfs_create_group(&codec->dev->kobj,
&max98506_attribute_group);
if (ret)
msg_maxim(
"failed to create sysfs group [%d]", ret);
} else {
ret = sysfs_create_group(&max98506->dev_log->kobj,
&max98506_attribute_group);
if (ret)
msg_maxim(
"failed to create sysfs group [%d]", ret);
}
}
msg_maxim("g_class=%p %p", g_class, max98506->dev_log_class);
#endif /* USE_DSM_LOG */
err_version:
msg_maxim("exit %d", ret);
return ret;
}
static int max98506_remove(struct snd_soc_codec *codec)
{
msg_maxim("enter");
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_max98506 = {
.probe = max98506_probe,
.remove = max98506_remove,
.set_bias_level = max98506_set_bias_level,
.suspend = max98506_suspend,
.resume = max98506_resume,
.controls = max98506_snd_controls,
.num_controls = ARRAY_SIZE(max98506_snd_controls),
};
static const struct regmap_config max98506_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX98506_R0FF_VERSION,
.reg_defaults = max98506_reg,
.num_reg_defaults = ARRAY_SIZE(max98506_reg),
.volatile_reg = max98506_volatile_register,
.readable_reg = max98506_readable_register,
.cache_type = REGCACHE_RBTREE,
};
static struct i2c_board_info max98506_i2c_sub_board[] = {
{
I2C_BOARD_INFO("max98506_sub", 0x34),
}
};
static struct i2c_driver max98506_i2c_sub_driver = {
.driver = {
.name = "max98506_sub",
.owner = THIS_MODULE,
},
};
struct i2c_client *max98506_add_sub_device(int bus_id, int slave_addr)
{
struct i2c_client *i2c = NULL;
struct i2c_adapter *adapter;
msg_maxim("bus_id:%d, slave_addr:0x%x", bus_id, slave_addr);
max98506_i2c_sub_board[0].addr = slave_addr;
adapter = i2c_get_adapter(bus_id);
if (adapter) {
i2c = i2c_new_device(adapter, max98506_i2c_sub_board);
if (i2c)
i2c->dev.driver = &max98506_i2c_sub_driver.driver;
}
return i2c;
}
static int max98506_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max98506_priv *max98506;
struct max98506_pdata *pdata;
struct max98506_volume_step_info *vstep;
int ret;
int pinfo_status = 0;
msg_maxim("enter, device '%s'", id->name);
max98506 = devm_kzalloc(&i2c->dev,
sizeof(struct max98506_priv), GFP_KERNEL);
if (!max98506) {
ret = -ENOMEM;
goto err_allocate_priv;
}
max98506->pdata = devm_kzalloc(&i2c->dev,
sizeof(struct max98506_pdata), GFP_KERNEL);
if (!max98506->pdata) {
ret = -ENOMEM;
goto err_allocate_pdata;
}
i2c_set_clientdata(i2c, max98506);
pdata = max98506->pdata;
vstep = &max98506->vstep;
if (i2c->dev.of_node) {
/* Read system clock */
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,sysclk", &pdata->sysclk);
if (ret) {
dev_err(&i2c->dev, "There is no sysclk property.\n");
pdata->sysclk = 12288000;
}
/* Read speaker volume */
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,spk-gain", &pdata->spk_gain);
if (ret) {
dev_err(&i2c->dev, "There is no spk_gain property.\n");
pdata->spk_gain = 0x14;
}
/* Read VMON slot info.*/
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,vmon_slot", &pdata->vmon_slot);
if (ret) {
dev_err(&i2c->dev, "There is no vmon_slot property.\n");
pdata->vmon_slot = 0;
}
#ifdef USE_MAX98506_IRQ
pdata->irq = of_get_named_gpio_flags(
i2c->dev.of_node, "maxim,irq-gpio", 0, NULL);
#endif /* USE_MAX98506_IRQ */
/* Read information related to DSM */
ret = of_property_read_u32_array(i2c->dev.of_node,
"maxim,platform_info", (u32 *) &pdata->pinfo,
sizeof(pdata->pinfo)/sizeof(uint32_t));
if (ret)
dev_err(&i2c->dev, "There is no platform info. property.\n");
else
pinfo_status = 1;
ret = of_property_read_u32_array(i2c->dev.of_node,
"maxim,boost_step",
(uint32_t *) &vstep->boost_step,
sizeof(vstep->boost_step)/sizeof(uint32_t));
if (ret) {
dev_err(&i2c->dev, "There is no boost_step property.\n");
for (ret = 0; ret < MAX98506_VSTEP_14; ret++)
vstep->boost_step[ret] = 0x0F;
vstep->boost_step[MAX98506_VSTEP_14] = 0x02;
vstep->boost_step[MAX98506_VSTEP_15] = 0x00;
}
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,adc_threshold", &vstep->adc_thres);
if (ret) {
dev_err(&i2c->dev, "There is no adc_threshold property.\n");
vstep->adc_thres = MAX98506_VSTEP_7;
}
pdata->reg_arr = of_get_property(i2c->dev.of_node,
"maxim,registers-of-amp", &pdata->reg_arr_len);
if (pdata->reg_arr == NULL)
dev_err(&i2c->dev, "There is no registers-diff property.\n");
#ifdef USE_DSM_LOG
ret = of_property_read_string(i2c->dev.of_node,
"maxim,log_class", &class_name_log);
if (ret) {
dev_err(&i2c->dev, "There is no log_class property.\n");
class_name_log = DEFAULT_LOG_CLASS_NAME;
}
#endif /* USE_DSM_LOG */
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,sub_reg", &pdata->sub_reg);
if (ret) {
dev_err(&i2c->dev, "Sub-device register was not found.\n");
pdata->sub_reg = 0;
}
ret = of_property_read_u32(i2c->dev.of_node,
"maxim,interleave", &pdata->interleave);
if (ret) {
dev_err(&i2c->dev, "There is no interleave information.\n");
pdata->interleave = 0;
}
pdata->nodsm = of_property_read_bool(
i2c->dev.of_node, "maxim,nodsm");
msg_maxim("use DSM(%d)", pdata->nodsm);
} else {
pdata->sysclk = 12288000;
pdata->spk_gain = 0x14;
pdata->vmon_slot = 0;
pdata->nodsm = 0;
vstep->adc_thres = MAX98506_VSTEP_7;
}
max98506->speaker_dac_enable = 1;
#ifdef USE_MAX98506_IRQ
if (pdata != NULL && gpio_is_valid(pdata->irq)) {
ret = gpio_request(pdata->irq, "max98506_irq_gpio");
if (ret) {
dev_err(&i2c->dev, "unable to request gpio [%d]",
pdata->irq);
goto err_irq_gpio_req;
}
ret = gpio_direction_input(pdata->irq);
if (ret) {
dev_err(&i2c->dev,
"unable to set direction for gpio [%d]",
pdata->irq);
goto err_irq_gpio_req;
}
i2c->irq = gpio_to_irq(pdata->irq);
ret = request_threaded_irq(i2c->irq, NULL, max98506_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max98506_interrupt", max98506);
if (ret)
dev_err(&i2c->dev, "Failed to register interrupt");
} else {
dev_err(&i2c->dev, "irq gpio not provided\n");
}
dev_dbg(&i2c->dev, "requested irq for max98506");
goto go_ahead_next_step;
err_irq_gpio_req:
if (gpio_is_valid(pdata->irq))
gpio_free(pdata->irq);
go_ahead_next_step:
#endif /* USE_MAX98506_IRQ */
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_init();
if (pinfo_status)
maxdsm_update_info(pdata->pinfo);
maxdsm_update_sub_reg(pdata->sub_reg);
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98506,
max98506_dai, ARRAY_SIZE(max98506_dai));
if (ret) {
dev_err(&i2c->dev, "Failed to register codec");
goto err_register_codec;
}
max98506->regmap = regmap_init_i2c(i2c, &max98506_regmap);
if (IS_ERR(max98506->regmap)) {
ret = PTR_ERR(max98506->regmap);
dev_err(&i2c->dev, "Failed to initialize regmap: %d", ret);
goto err_regmap;
}
if (pdata->sub_reg != 0) {
max98506->sub_i2c =
max98506_add_sub_device(i2c->adapter->nr,
pdata->sub_reg);
if (IS_ERR(max98506->sub_i2c)) {
dev_err(&max98506->sub_i2c->dev,
"Second MAX98506 was not found\n");
ret = -ENODEV;
goto err_regmap;
} else {
max98506->sub_regmap = regmap_init_i2c(
max98506->sub_i2c, &max98506_regmap);
if (IS_ERR(max98506->sub_regmap)) {
ret = PTR_ERR(max98506->sub_regmap);
dev_err(&max98506->sub_i2c->dev,
"Failed to allocate sub_regmap: %d\n",
ret);
goto err_regmap;
}
}
}
msg_maxim("exit, device '%s'", id->name);
return 0;
err_regmap:
snd_soc_unregister_codec(&i2c->dev);
if (max98506->regmap)
regmap_exit(max98506->regmap);
if (max98506->sub_regmap)
regmap_exit(max98506->sub_regmap);
err_register_codec:
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_deinit();
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
devm_kfree(&i2c->dev, max98506->pdata);
err_allocate_pdata:
devm_kfree(&i2c->dev, max98506);
err_allocate_priv:
msg_maxim("exit with errors. ret=%d", ret);
return ret;
}
static int max98506_i2c_remove(struct i2c_client *client)
{
struct max98506_priv *max98506 = i2c_get_clientdata(client);
struct max98506_pdata *pdata = max98506->pdata;
snd_soc_unregister_codec(&client->dev);
if (max98506->regmap)
regmap_exit(max98506->regmap);
if (max98506->sub_regmap)
regmap_exit(max98506->sub_regmap);
devm_kfree(&client->dev, pdata);
devm_kfree(&client->dev, max98506);
#ifdef CONFIG_SND_SOC_MAXIM_DSM
maxdsm_deinit();
#endif /* CONFIG_SND_SOC_MAXIM_DSM */
msg_maxim("exit");
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id max98506_dt_ids[] = {
{ .compatible = "maxim,max98506" },
{ }
};
#else
#define max98506_dt_ids NULL
#endif /* CONFIG_OF */
static const struct i2c_device_id max98506_i2c_id[] = {
{ "max98506", MAX98506 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98506_i2c_id);
static struct i2c_driver max98506_i2c_driver = {
.driver = {
.name = "max98506",
.owner = THIS_MODULE,
.of_match_table = max98506_dt_ids,
.suppress_bind_attrs = true,
},
.probe = max98506_i2c_probe,
.remove = max98506_i2c_remove,
.id_table = max98506_i2c_id,
};
module_i2c_driver(max98506_i2c_driver);
MODULE_DESCRIPTION("ALSA SoC MAX98506 driver");
MODULE_AUTHOR("Ralph Birt <rdbirt@gmail.com>");
MODULE_AUTHOR("KyoungHun Jeon <hun.jeon@maximintegrated.com>");
MODULE_LICENSE("GPL");