blob: 3a87d28efd3630abd3b723245420092f27bfc758 [file] [log] [blame]
/*
* mt6392-codec.c -- MT6392 ALSA SoC codec driver
*
* Copyright (c) 2016 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include "mt6392-codec.h"
#define MT6392_CODEC_NAME "mt6392-codec"
enum mt6392_speaker_mode {
MT6392_CLASS_D = 0,
MT6392_CLASS_AB,
};
struct mt6392_codec_priv {
struct snd_soc_codec *codec;
struct regmap *regmap;
uint32_t speaker_mode;
uint32_t spk_amp_gain;
uint16_t spk_trim_offset;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
};
/*
* Class D: has HW Trim mode and SW Trim mode
* Class AB: use the trim offset derived from Class D HW Trim
*
* The option used to choose the trim mode of Class D
*/
#define USE_HW_TRIM_CLASS_D
/* Int Spk Amp Playback Volume
* {mute, 0, 4, 5, 6, 7, 8, ..., 17} dB
*/
static const unsigned int int_spk_amp_gain_tlv[] = {
TLV_DB_RANGE_HEAD(3),
0, 0, TLV_DB_SCALE_ITEM(0, 0, 1),
1, 1, TLV_DB_SCALE_ITEM(0, 0, 0),
2, 15, TLV_DB_SCALE_ITEM(400, 100, 0),
};
/* Audio_Speaker_PGA_gain
* {mute, 0, 4, 5, 6, 7, 8, ..., 17} dB
*/
static const char *const int_spk_amp_gain_text[] = {
"MUTE", "+0dB", "+4dB", "+5dB",
"+6dB", "+7dB", "+8dB", "+9dB",
"+10dB", "+11dB", "+12dB", "+13dB",
"+14dB", "+15dB", "+16dB", "+17dB",
};
static const struct soc_enum int_spk_amp_gain_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(int_spk_amp_gain_text),
int_spk_amp_gain_text);
static int int_spk_amp_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
uint32_t value = 0;
value = codec_data->spk_amp_gain;
ucontrol->value.integer.value[0] = value;
return 0;
}
static int int_spk_amp_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
uint32_t value = ucontrol->value.integer.value[0];
if (value >= e->items)
return -EINVAL;
snd_soc_update_bits(codec_data->codec, SPK_CON9,
GENMASK(11, 8), value << 8);
codec_data->spk_amp_gain = value;
dev_dbg(codec_data->codec->dev, "%s value = %u\n",
__func__, value);
return 0;
}
static int int_spk_amp_gain_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = 0;
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
ret = snd_soc_put_volsw(kcontrol, ucontrol);
if (ret < 0)
return ret;
codec_data->spk_amp_gain = ucontrol->value.integer.value[0];
return 0;
}
/* Internal speaker mode (AB/D) */
static const char * const int_spk_amp_mode_texts[] = {
"Class D",
"Class AB",
};
static SOC_ENUM_SINGLE_EXT_DECL(mt6392_speaker_mode_enum,
int_spk_amp_mode_texts);
static int mt6392_spk_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = codec_data->speaker_mode;
return 0;
}
static int mt6392_spk_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
int ret = 0;
uint32_t mode = ucontrol->value.integer.value[0];
switch (mode) {
case MT6392_CLASS_D:
case MT6392_CLASS_AB:
codec_data->speaker_mode = ucontrol->value.integer.value[0];
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/* Check OC Flag */
static const char *const mt6392_speaker_oc_flag_texts[] = {
"NoOverCurrent",
"OverCurrent"
};
static SOC_ENUM_SINGLE_EXT_DECL(mt6392_speaker_oc_flag_enum,
mt6392_speaker_oc_flag_texts);
static int mt6392_speaker_oc_flag_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
uint32_t reg_value = snd_soc_read(codec_data->codec, SPK_CON6);
if (codec_data->speaker_mode == MT6392_CLASS_AB)
ucontrol->value.integer.value[0] =
(reg_value & BIT(15)) ? 1 : 0;
else
ucontrol->value.integer.value[0] =
(reg_value & BIT(14)) ? 1 : 0;
return 0;
}
static void mt6392_codec_get_spk_trim_offset(struct snd_soc_codec *codec)
{
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
/* turn on spk (class D) and hw trim */
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x000E);
snd_soc_update_bits(codec, SPK_CON7, 0xFFFF, 0x48F4);
snd_soc_update_bits(codec, SPK_CON11, 0xFFFF, 0x0055);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2018);
snd_soc_update_bits(codec, SPK_CON2, 0xFFFF, 0x0414);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3409);
usleep_range(20000, 21000);
/* save trim offset */
codec_data->spk_trim_offset =
snd_soc_read(codec, SPK_CON1) & GENMASK(4, 0);
/* turn off trim */
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3401);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2000);
usleep_range(2000, 3000);
/* turn off spk */
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0000);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3400);
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x0000);
}
static void mt6392_int_spk_on_with_trim(struct snd_soc_codec *codec)
{
#if defined(USE_HW_TRIM_CLASS_D)
/* turn on spk (class D) and hw trim */
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x000E);
snd_soc_update_bits(codec, SPK_CON7, 0xFFFF, 0x48F4);
snd_soc_update_bits(codec, SPK_CON11, 0xFFFF, 0x0055);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2018);
snd_soc_update_bits(codec, SPK_CON2, 0xFFFF, 0x0414);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3409);
usleep_range(20000, 21000);
/* turn off trim */
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3401);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2000);
#else
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
/* turn on spk (class D) */
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x000E);
snd_soc_update_bits(codec, SPK_CON7, 0xFFFF, 0x48F4);
snd_soc_update_bits(codec, SPK_CON2, 0xFFFF, 0x0414);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3001);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2000);
/* enable sw trim */
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0009);
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0001);
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0283);
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0281);
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x2A81);
snd_soc_update_bits(codec, SPK_CON1, 0xFFFF, 0x6000);
/* class D and class AB use the same trim offset value */
snd_soc_update_bits(codec, SPK_CON1,
GENMASK(12, 8), (codec_data->spk_trim_offset << 8));
/* trim stop */
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0xAA81);
#endif
usleep_range(2000, 3000);
}
static int mt6392_int_spk_turn_on(struct snd_soc_codec *codec)
{
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
switch (codec_data->speaker_mode) {
case MT6392_CLASS_D:
mt6392_int_spk_on_with_trim(codec);
break;
case MT6392_CLASS_AB:
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x000E);
snd_soc_update_bits(codec, SPK_CON7, 0xFFFF, 0x48F4);
snd_soc_update_bits(codec, SPK_CON2, 0xFFFF, 0x0414);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3005);
snd_soc_update_bits(codec, SPK_CON9, 0xF0FF, 0x2000);
snd_soc_update_bits(codec, SPK_CON1, 0xFFFF, 0x6000);
/* class D and class AB use the same trim offset value */
snd_soc_update_bits(codec, SPK_CON1,
GENMASK(12, 8), (codec_data->spk_trim_offset << 8));
usleep_range(2000, 3000);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mt6392_int_spk_turn_off(struct snd_soc_codec *codec)
{
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
int ret = 0;
dev_dbg(codec->dev, "%s\n", __func__);
switch (codec_data->speaker_mode) {
case MT6392_CLASS_D:
snd_soc_update_bits(codec, SPK_CON12, 0xFFFF, 0x0000);
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3400);
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x0000);
break;
case MT6392_CLASS_AB:
snd_soc_update_bits(codec, SPK_CON0, 0xFFFF, 0x3404);
snd_soc_update_bits(codec, TOP_CKPDN1_CLR, 0x000E, 0x0000);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mt6392_int_spk_amp_wevent(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s, event %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
mt6392_int_spk_turn_on(codec);
break;
case SND_SOC_DAPM_PRE_PMD:
mt6392_int_spk_turn_off(codec);
break;
default:
break;
}
return 0;
}
/* Int Spk Amp Switch */
static int mt6392_codec_int_spk_amp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
struct snd_soc_codec *codec = codec_data->codec;
long int_spk_amp_en = snd_soc_read(codec, SPK_CON0) & BIT(0);
ucontrol->value.integer.value[0] = int_spk_amp_en;
return 0;
}
static int mt6392_codec_int_spk_amp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mt6392_codec_priv *codec_data =
snd_soc_component_get_drvdata(component);
struct snd_soc_codec *codec = codec_data->codec;
long int_spk_amp_en = ucontrol->value.integer.value[0];
if (int_spk_amp_en)
mt6392_int_spk_turn_on(codec);
else
mt6392_int_spk_turn_off(codec);
return 0;
}
static const struct snd_kcontrol_new mt6392_codec_controls[] = {
/* Internal speaker PGA gain control */
SOC_SINGLE_EXT_TLV("Int Spk Amp Playback Volume",
SPK_CON9, 8, 15, 0,
snd_soc_get_volsw,
int_spk_amp_gain_put_volsw,
int_spk_amp_gain_tlv),
/* Audio_Speaker_PGA_gain */
SOC_ENUM_EXT("Audio_Speaker_PGA_gain",
int_spk_amp_gain_enum,
int_spk_amp_gain_get,
int_spk_amp_gain_put),
/* Internal speaker mode (AB/D) */
SOC_ENUM_EXT("Int Spk Amp Mode", mt6392_speaker_mode_enum,
mt6392_spk_mode_get, mt6392_spk_mode_put),
/* Check OC Flag */
SOC_ENUM_EXT("Speaker_OC_Flag", mt6392_speaker_oc_flag_enum,
mt6392_speaker_oc_flag_get, NULL),
/* Int Spk Amp Switch */
SOC_SINGLE_BOOL_EXT("Int Spk Amp Switch",
0,
mt6392_codec_int_spk_amp_get,
mt6392_codec_int_spk_amp_put),
};
static const struct snd_soc_dapm_widget mt6392_codec_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("MT6392 AIF RX", "MT6392 Playback", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SPK("Int Spk Amp", mt6392_int_spk_amp_wevent),
};
static const struct snd_soc_dapm_route mt6392_codec_dapm_routes[] = {
{"Int Spk Amp", NULL, "MT6392 AIF RX"},
};
static int module_reg_read(void *context, unsigned int reg, unsigned int *val,
unsigned int offset)
{
struct mt6392_codec_priv *codec_data =
(struct mt6392_codec_priv *) context;
int ret = 0;
if (!(codec_data && codec_data->regmap))
return -1;
ret = regmap_read(codec_data->regmap,
(reg & (~offset)), val);
return ret;
}
static int module_reg_write(void *context, unsigned int reg, unsigned int val,
unsigned int offset)
{
struct mt6392_codec_priv *codec_data =
(struct mt6392_codec_priv *) context;
int ret = 0;
if (!(codec_data && codec_data->regmap))
return -1;
ret = regmap_write(codec_data->regmap,
(reg & (~offset)), val);
return ret;
}
static bool reg_is_in_pmic(unsigned int reg)
{
if (reg & PMIC_OFFSET)
return true;
else
return false;
}
/* regmap functions */
static int codec_reg_read(void *context,
unsigned int reg, unsigned int *val)
{
unsigned int offset;
if (reg_is_in_pmic(reg))
offset = PMIC_OFFSET;
else
return -1;
return module_reg_read(context, reg, val, offset);
}
static int codec_reg_write(void *context,
unsigned int reg, unsigned int val)
{
unsigned int offset;
if (reg_is_in_pmic(reg))
offset = PMIC_OFFSET;
else
return -1;
return module_reg_write(context, reg, val, offset);
}
static void codec_regmap_lock(void *lock_arg)
{
}
static void codec_regmap_unlock(void *lock_arg)
{
}
static struct regmap_config mt6392_codec_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
.reg_read = codec_reg_read,
.reg_write = codec_reg_write,
.lock = codec_regmap_lock,
.unlock = codec_regmap_unlock,
.cache_type = REGCACHE_NONE,
};
#ifdef CONFIG_DEBUG_FS
struct mt6392_codec_reg_attr {
uint32_t offset;
char *name;
};
#define DUMP_REG_ENTRY(reg) {reg, #reg}
static const struct mt6392_codec_reg_attr mt6392_codec_dump_reg_list[] = {
DUMP_REG_ENTRY(SPK_CON0),
DUMP_REG_ENTRY(SPK_CON1),
DUMP_REG_ENTRY(SPK_CON2),
DUMP_REG_ENTRY(SPK_CON3),
DUMP_REG_ENTRY(SPK_CON4),
DUMP_REG_ENTRY(SPK_CON5),
DUMP_REG_ENTRY(SPK_CON6),
DUMP_REG_ENTRY(SPK_CON7),
DUMP_REG_ENTRY(SPK_CON8),
DUMP_REG_ENTRY(SPK_CON9),
DUMP_REG_ENTRY(SPK_CON10),
DUMP_REG_ENTRY(SPK_CON11),
DUMP_REG_ENTRY(SPK_CON12),
};
static ssize_t mt6392_codec_debug_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *pos)
{
struct mt6392_codec_priv *codec_data = file->private_data;
ssize_t ret, i;
char *buf;
int n = 0;
if (*pos < 0 || !count)
return -EINVAL;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(mt6392_codec_dump_reg_list); i++) {
n += scnprintf(buf + n, count - n, "%s = 0x%x\n",
mt6392_codec_dump_reg_list[i].name,
snd_soc_read(codec_data->codec,
mt6392_codec_dump_reg_list[i].offset));
}
ret = simple_read_from_buffer(user_buf, count, pos, buf, n);
kfree(buf);
return ret;
}
static const struct file_operations mt6392_codec_debug_ops = {
.open = simple_open,
.read = mt6392_codec_debug_read,
.llseek = default_llseek,
};
#endif
static void mt6392_codec_init_regs(struct mt6392_codec_priv *codec_data)
{
struct snd_soc_codec *codec = codec_data->codec;
dev_dbg(codec->dev, "%s\n", __func__);
/* default PGA gain: 12dB */
codec_data->spk_amp_gain = 0xA;
snd_soc_update_bits(codec, SPK_CON9,
GENMASK(11, 8), (codec_data->spk_amp_gain) << 8);
}
static struct regmap *mt6392_codec_get_regmap_from_dt(const char *phandle_name,
struct mt6392_codec_priv *codec_data)
{
struct device_node *self_node = NULL, *node = NULL;
struct platform_device *platdev = NULL;
struct device *dev = codec_data->codec->dev;
struct regmap *regmap = NULL;
self_node = of_find_compatible_node(NULL, NULL,
"mediatek," MT6392_CODEC_NAME);
if (!self_node) {
dev_dbg(dev, "%s failed to find %s node\n",
__func__, MT6392_CODEC_NAME);
return NULL;
}
dev_dbg(dev, "%s found %s node\n", __func__, MT6392_CODEC_NAME);
node = of_parse_phandle(self_node, phandle_name, 0);
if (!node) {
dev_dbg(dev, "%s failed to find %s node\n",
__func__, phandle_name);
return NULL;
}
dev_dbg(dev, "%s found %s\n", __func__, phandle_name);
platdev = of_find_device_by_node(node);
if (!platdev) {
dev_dbg(dev, "%s failed to get platform device of %s\n",
__func__, phandle_name);
return NULL;
}
dev_dbg(dev, "%s found platform device of %s\n",
__func__, phandle_name);
regmap = dev_get_regmap(&platdev->dev, NULL);
if (regmap) {
dev_dbg(dev, "%s found regmap of %s\n", __func__, phandle_name);
return regmap;
}
return NULL;
}
static int mt6392_codec_parse_dt(struct snd_soc_codec *codec)
{
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
struct device *dev = codec->dev;
int ret = 0;
codec_data->regmap = mt6392_codec_get_regmap_from_dt(
"mediatek,pwrap-regmap",
codec_data);
if (!codec_data->regmap) {
dev_dbg(dev, "%s failed to get %s\n",
__func__, "mediatek,pwrap-regmap");
devm_kfree(dev, codec_data);
ret = -EPROBE_DEFER;
return ret;
}
ret = of_property_read_u32(dev->of_node, "mediatek,speaker-mode",
&codec_data->speaker_mode);
if (ret) {
dev_dbg(dev, "%s fail to read speaker-mode in node %s\n",
__func__, dev->of_node->full_name);
codec_data->speaker_mode = MT6392_CLASS_D;
} else if (codec_data->speaker_mode != MT6392_CLASS_D &&
codec_data->speaker_mode != MT6392_CLASS_AB) {
codec_data->speaker_mode = MT6392_CLASS_D;
}
return ret;
}
#define MT6392_CODEC_DL_RATES SNDRV_PCM_RATE_8000_48000
static struct snd_soc_dai_driver mt6392_codec_dai = {
.name = "mt6392-codec-dai",
.playback = {
.stream_name = "MT6392 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MT6392_CODEC_DL_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static int mt6392_codec_probe(struct snd_soc_codec *codec)
{
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
int ret = 0;
codec_data->codec = codec;
mt6392_codec_parse_dt(codec);
mt6392_codec_init_regs(codec_data);
mt6392_codec_get_spk_trim_offset(codec);
#ifdef CONFIG_DEBUG_FS
codec_data->debugfs = debugfs_create_file("mt6392_codec_regs",
S_IFREG | 0444,
NULL, codec_data, &mt6392_codec_debug_ops);
#endif
return ret;
}
static int mt6392_codec_remove(struct snd_soc_codec *codec)
{
#ifdef CONFIG_DEBUG_FS
struct mt6392_codec_priv *codec_data =
snd_soc_codec_get_drvdata(codec);
debugfs_remove(codec_data->debugfs);
#endif
dev_dbg(codec->dev, "%s\n", __func__);
return 0;
}
static struct snd_soc_codec_driver mt6392_codec_driver = {
.probe = mt6392_codec_probe,
.remove = mt6392_codec_remove,
.component_driver = {
.controls = mt6392_codec_controls,
.num_controls = ARRAY_SIZE(mt6392_codec_controls),
.dapm_widgets = mt6392_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt6392_codec_dapm_widgets),
.dapm_routes = mt6392_codec_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(mt6392_codec_dapm_routes),
},
};
static int mt6392_codec_dev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mt6392_codec_priv *codec_data = NULL;
dev_dbg(dev, "%s dev name %s\n", __func__, dev_name(dev));
if (dev->of_node) {
dev_set_name(dev, "%s", MT6392_CODEC_NAME);
dev_dbg(dev, "%s set dev name %s\n", __func__, dev_name(dev));
}
codec_data = devm_kzalloc(dev,
sizeof(struct mt6392_codec_priv), GFP_KERNEL);
pdev->name = pdev->dev.kobj.name;
if (!codec_data)
return -ENOMEM;
dev_set_drvdata(dev, codec_data);
/* get regmap of codec */
codec_data->regmap = devm_regmap_init(dev, NULL, codec_data,
&mt6392_codec_regmap_config);
if (IS_ERR(codec_data->regmap)) {
dev_dbg(dev, "%s failed to get regmap of codec\n", __func__);
devm_kfree(dev, codec_data);
codec_data->regmap = NULL;
return -EINVAL;
}
return snd_soc_register_codec(dev,
&mt6392_codec_driver, &mt6392_codec_dai, 1);
}
static int mt6392_codec_dev_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static const struct of_device_id mt6392_codec_dt_match[] = {
{.compatible = "mediatek," MT6392_CODEC_NAME,},
{}
};
MODULE_DEVICE_TABLE(of, mt6392_codec_dt_match);
static struct platform_driver mt6392_codec_device_driver = {
.driver = {
.name = MT6392_CODEC_NAME,
.owner = THIS_MODULE,
.of_match_table = mt6392_codec_dt_match,
},
.probe = mt6392_codec_dev_probe,
.remove = mt6392_codec_dev_remove,
};
module_platform_driver(mt6392_codec_device_driver);
/* Module information */
MODULE_DESCRIPTION("ASoC MT6392 driver");
MODULE_LICENSE("GPL v2");