From e1f518308fe6d6320337733b3d862debb22814fb Mon Sep 17 00:00:00 2001 From: Ralf Herz Date: Tue, 4 Sep 2018 12:10:42 +0200 Subject: [PATCH] asoc: codecs: Use sysfs instead of mixer_ctls for EP92 HDMI The format change event signaling for EP92 HDMI input is adapted to provide format information through sysfs nodes. Chip configuration is done through sysfs as well. Change-Id: I8c930ca5647899b4434272009699763c32fe9d04 Signed-off-by: Ralf Herz Signed-off-by: Mangesh Kunchamwar --- asoc/codecs/ep92/ep92.c | 1924 ++++++++++++++++++++++----------------- asoc/codecs/ep92/ep92.h | 18 +- 2 files changed, 1096 insertions(+), 846 deletions(-) diff --git a/asoc/codecs/ep92/ep92.c b/asoc/codecs/ep92/ep92.c index 73110da7ec04..52f5e6d0b98a 100644 --- a/asoc/codecs/ep92/ep92.c +++ b/asoc/codecs/ep92/ep92.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -27,9 +29,10 @@ #include #include "ep92.h" -#define EP92_POLL_INTERVAL_OFF_MSEC 2000 -#define EP92_POLL_INTERVAL_ON_MSEC 100 - +#define EP92_POLL_INTERVAL_OFF_MSEC 200 +#define EP92_POLL_INTERVAL_ON_MSEC 20 +#define EP92_SYSFS_ENTRY_MAX_LEN 64 +#define EP92_HYST_CNT 5 #define EP92_RATES (SNDRV_PCM_RATE_32000 |\ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ @@ -38,71 +41,10 @@ #define EP92_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -#define EP92_UEVENT_CTRL_NUM_KEYS 8 -#define EP92_UEVENT_AUDIO_NUM_KEYS 9 - static const unsigned int ep92_samp_freq_table[8] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000, 768000 }; -static const char hex_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - -struct ep92_uevent_data { - struct kobject kobj; - struct kobj_type ktype; -}; - -static struct kset *ep92_uevent_kset; -static struct ep92_uevent_data *ep92_uevent_ctrl; -static struct ep92_uevent_data *ep92_uevent_audio; - -static void ep92_release_uevent_data(struct kobject *kobj) -{ - struct ep92_uevent_data *data = container_of(kobj, - struct ep92_uevent_data, kobj); - - kfree(data); -} - -static int ep92_init_uevent_data(struct ep92_uevent_data *uevent_data, - char *name) -{ - int ret = -EINVAL; - - if (!uevent_data || !name) - return ret; - - /* Set kset for kobject before initializing the kobject */ - uevent_data->kobj.kset = ep92_uevent_kset; - - /* Initialize kobject and add it to kernel */ - ret = kobject_init_and_add(&uevent_data->kobj, &uevent_data->ktype, - NULL, "%s", name); - if (ret) { - pr_err("%s: error initializing uevent kernel object: %d", - __func__, ret); - kobject_put(&uevent_data->kobj); - return ret; - } - - /* Send kobject add event to the system */ - kobject_uevent(&uevent_data->kobj, KOBJ_ADD); - - return ret; -} - -/** - * ep92_destroy_uevent_data - destroy kernel object. - * - * @uevent_data: uevent data. - */ -static void ep92_destroy_uevent_data(struct ep92_uevent_data *uevent_data) -{ - if (uevent_data) - kobject_put(&uevent_data->kobj); -} - static bool ep92_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { @@ -162,10 +104,25 @@ struct ep92_pdata { struct work_struct read_status_worker; int irq; + int hyst_tx_plug; + int hyst_link_on0; + int hyst_link_on1; + int hyst_link_on2; + int filt_tx_plug; + int filt_link_on0; + int filt_link_on1; + int filt_link_on2; + struct { + u8 tx_info; + u8 video_latency; + } gi; /* General Info block */ + struct { u8 ctl; u8 rx_sel; + u8 ctl2; u8 cec_volume; + u8 link; } gc; /* General Control block */ struct { @@ -178,442 +135,144 @@ struct ep92_pdata { } ai; /* Audio Info block */ u8 old_mode; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *debugfs_dir; + struct dentry *debugfs_file_wo; + struct dentry *debugfs_file_ro; +#endif /* CONFIG_DEBUG_FS */ }; -/* - * EP92 Controls - */ - -/* enumerated controls */ -static const char * const ep92_off_on_text[] = {"Off", "On"}; -static const char * const ep92_aud_path_text[] = {"TV", "Speaker"}; -static const char * const ep92_rx_sel_text[] = {"Port 0", "Port 1", "Port 2", - "Res 3", "Res 4", "Res 5", "None", "Res 7"}; -static const char * const ep92_cec_mute_text[] = {"Normal", "Muted"}; - -static const char * const ep92_state_text[] = {"Inactive", "Active"}; -static const char * const ep92_avmute_text[] = {"Normal", "Muted"}; -static const char * const ep92_layout_text[] = {"Layout 0", "Layout 1"}; -static const char * const ep92_mode_text[] = {"LPCM", "Compr"}; - -SOC_ENUM_SINGLE_DECL(ep92_power_enum, EP92_GENERAL_CONTROL_0, - EP92_GC_POWER_SHIFT, ep92_off_on_text); -SOC_ENUM_SINGLE_DECL(ep92_audio_path_enum, EP92_GENERAL_CONTROL_0, - EP92_GC_AUDIO_PATH_SHIFT, ep92_aud_path_text); -SOC_ENUM_SINGLE_DECL(ep92_rx_sel_enum, EP92_GENERAL_CONTROL_1, - EP92_GC_RX_SEL_SHIFT, ep92_rx_sel_text); -SOC_ENUM_SINGLE_DECL(ep92_arc_en_enum, EP92_GENERAL_CONTROL_0, - EP92_GC_ARC_EN_SHIFT, ep92_off_on_text); -SOC_ENUM_SINGLE_DECL(ep92_cec_mute_enum, EP92_GENERAL_CONTROL_0, - EP92_GC_CEC_MUTE_SHIFT, ep92_cec_mute_text); - -SOC_ENUM_SINGLE_DECL(ep92_state_enum, EP92_AUDIO_INFO_SYSTEM_STATUS_0, - EP92_AI_MCLK_ON_SHIFT, ep92_state_text); -SOC_ENUM_SINGLE_DECL(ep92_avmute_enum, EP92_AUDIO_INFO_SYSTEM_STATUS_0, - EP92_AI_AVMUTE_SHIFT, ep92_avmute_text); -SOC_ENUM_SINGLE_DECL(ep92_layout_enum, EP92_AUDIO_INFO_SYSTEM_STATUS_0, - EP92_AI_LAYOUT_SHIFT, ep92_layout_text); -SOC_ENUM_SINGLE_DECL(ep92_mode_enum, EP92_AUDIO_INFO_AUDIO_STATUS, - EP92_AI_STD_ADO_SHIFT, ep92_mode_text); - -/* get/set functions */ -static int ep92_power_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int debugfs_codec_open_op(struct inode *inode, struct file *file) { - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = - (val >> e->shift_l) & EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_off_on_text[ucontrol->value.enumerated.item[0]]); + file->private_data = inode->i_private; return 0; } -static int ep92_power_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int debugfs_get_parameters(char *buf, u32 *param1, int num_of_par) { - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; + char *token; + int base, cnt; - val = snd_soc_read(codec, e->reg); - val &= ~EP92_GC_POWER_MASK; - val |= (ucontrol->value.enumerated.item[0] & EP92_2CHOICE_MASK) - << e->shift_l; - snd_soc_write(codec, e->reg, val); - ep92->gc.ctl &= ~EP92_GC_POWER_MASK; - ep92->gc.ctl |= val & EP92_GC_POWER_MASK; + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_off_on_text[ucontrol->value.enumerated.item[0] & - EP92_2CHOICE_MASK]); - return 0; -} + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; -static int ep92_audio_path_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_aud_path_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_audio_path_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - val &= ~EP92_GC_AUDIO_PATH_MASK; - val |= (ucontrol->value.enumerated.item[0] & EP92_2CHOICE_MASK) - << e->shift_l; - snd_soc_write(codec, e->reg, val); - ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK; - ep92->gc.ctl |= val & EP92_GC_AUDIO_PATH_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_aud_path_text[ucontrol->value.enumerated.item[0] & - EP92_2CHOICE_MASK]); - return 0; -} - -static int ep92_cec_mute_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_cec_mute_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_cec_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - val &= ~EP92_GC_CEC_MUTE_MASK; - val |= (ucontrol->value.enumerated.item[0] & EP92_2CHOICE_MASK) - << e->shift_l; - snd_soc_write(codec, e->reg, val); - ep92->gc.ctl &= ~EP92_GC_CEC_MUTE_MASK; - ep92->gc.ctl |= val & EP92_GC_CEC_MUTE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_cec_mute_text[ucontrol->value.enumerated.item[0] & - EP92_2CHOICE_MASK]); - return 0; -} - -static int ep92_arc_en_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_off_on_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_arc_en_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - val &= ~EP92_GC_ARC_EN_MASK; - val |= (ucontrol->value.enumerated.item[0] & EP92_2CHOICE_MASK) - << e->shift_l; - snd_soc_write(codec, e->reg, val); - ep92->gc.ctl &= ~EP92_GC_ARC_EN_MASK; - ep92->gc.ctl |= val & EP92_GC_ARC_EN_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_off_on_text[ucontrol->value.enumerated.item[0] & 0x01]); - return 0; -} - -static int ep92_rx_sel_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_GC_RX_SEL_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_rx_sel_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_rx_sel_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - val &= ~EP92_GC_RX_SEL_MASK; - val |= (ucontrol->value.enumerated.item[0] & EP92_GC_RX_SEL_MASK) - << e->shift_l; - snd_soc_write(codec, e->reg, val); - ep92->gc.rx_sel &= ~EP92_GC_RX_SEL_MASK; - ep92->gc.rx_sel |= val & EP92_GC_RX_SEL_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_rx_sel_text[ucontrol->value.enumerated.item[0] & - EP92_GC_RX_SEL_MASK]); - return 0; -} - -static int ep92_cec_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, mc->reg); - ucontrol->value.integer.value[0] = (val >> mc->shift) & - EP92_GC_CEC_VOLUME_MASK; - - pr_debug("%s: volume = %ld\n", __func__, - ucontrol->value.integer.value[0]); - return 0; -} - -static int ep92_cec_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = ucontrol->value.integer.value[0] & EP92_GC_CEC_VOLUME_MASK; - if (val > EP92_GC_CEC_VOLUME_MAX) - val = EP92_GC_CEC_VOLUME_MAX; - snd_soc_write(codec, mc->reg, val); - ep92->gc.cec_volume = val; - - pr_debug("%s: volume = %ld\n", __func__, - ucontrol->value.integer.value[0]); - return 0; -} - -static int ep92_state_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_state_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_avmute_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_avmute_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_layout_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & - EP92_2CHOICE_MASK; - - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_layout_text[ucontrol->value.enumerated.item[0]]); - return 0; -} - -static int ep92_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); - unsigned int val; - - val = snd_soc_read(codec, e->reg); - if (val & EP92_AI_STD_ADO_MASK) { - val = snd_soc_read(codec, EP92_AUDIO_INFO_CHANNEL_STATUS_0); - if (val & EP92_AI_NPCM_MASK) - ucontrol->value.enumerated.item[0] = 1; /* Compr */ - else - ucontrol->value.enumerated.item[0] = 0; /* LPCM */ - } else if (val & EP92_AI_HBR_ADO_MASK) { - ucontrol->value.enumerated.item[0] = 1; /* Compr */ - } else { - ucontrol->value.enumerated.item[0] = ep92->old_mode; + token = strsep(&buf, " "); + } else { + return -EINVAL; + } } - pr_debug("%s: item = %d (%s)\n", __func__, - ucontrol->value.enumerated.item[0], - ep92_mode_text[ucontrol->value.enumerated.item[0]]); return 0; } -static int ep92_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static ssize_t debugfs_codec_write_op(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; + struct ep92_pdata *ep92 = (struct ep92_pdata *) filp->private_data; + struct snd_soc_codec *codec = ep92->codec; + char lbuf[32]; + int rc; + u32 param[2]; - val = snd_soc_read(codec, mc->reg); - val &= EP92_AI_RATE_MASK; - val = ep92_samp_freq_table[val]; - ucontrol->value.integer.value[0] = val; - - pr_debug("%s: rate = %ld\n", __func__, - ucontrol->value.integer.value[0]); - return 0; + if (!filp || !ppos || !ubuf) + return -EINVAL; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + lbuf[cnt] = '\0'; + rc = debugfs_get_parameters(lbuf, param, 2); + if ((param[0] < EP92_ISP_MODE_ENTER_ISP) + || (param[0] > EP92_GENERAL_CONTROL_4)) { + dev_err(codec->dev, "%s: reg address 0x%02X out of range\n", + __func__, param[0]); + return -EINVAL; + } + if ((param[1] < 0) || (param[1] > 255)) { + dev_err(codec->dev, "%s: reg data 0x%02X out of range\n", + __func__, param[1]); + return -EINVAL; + } + if (rc == 0) { + rc = cnt; + dev_info(codec->dev, "%s: reg[0x%02X]=0x%02X\n", + __func__, param[0], param[1]); + snd_soc_write(codec, param[0], param[1]); + } else { + dev_err(codec->dev, "%s: write to register addr=0x%02X failed\n", + __func__, param[0]); + } + return rc; } -static int ep92_ch_count_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static ssize_t debugfs_ep92_reg_show(struct snd_soc_codec *codec, + char __user *ubuf, size_t count, loff_t *ppos) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[20]; - val = snd_soc_read(codec, mc->reg) & EP92_AI_CH_COUNT_MASK; - /* mapping is ch_count = reg_val + 1, with exception: 0 = unknown */ - if (val > 0) - val += 1; + if (!ubuf || !ppos || !codec || *ppos < 0) + return -EINVAL; - ucontrol->value.integer.value[0] = val; + for (i = (int) *ppos / 11; i <= EP92_MAX_REGISTER_ADDR; i++) { + reg_val = snd_soc_read(codec, i); + len = snprintf(tmp_buf, 20, "0x%02X: 0x%02X\n", i, + (reg_val & 0xFF)); + if ((total + len) > count) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + dev_err(codec->dev, "%s: fail to copy reg dump\n", + __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } - pr_debug("%s: ch_count = %ld\n", __func__, - ucontrol->value.integer.value[0]); - return 0; +copy_err: + return total; } -static int ep92_ch_alloc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static ssize_t debugfs_codec_read_op(struct file *filp, + char __user *ubuf, size_t cnt, loff_t *ppos) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - unsigned int val; + struct ep92_pdata *ep92 = (struct ep92_pdata *) filp->private_data; + struct snd_soc_codec *codec = ep92->codec; + ssize_t ret_cnt; - val = snd_soc_read(codec, mc->reg); - ucontrol->value.integer.value[0] = (val >> mc->shift) & - EP92_AI_CH_ALLOC_MASK; - - pr_debug("%s: ch_alloc = 0x%02lx\n", __func__, - ucontrol->value.integer.value[0]); - return 0; + if (!filp || !ppos || !ubuf || *ppos < 0) + return -EINVAL; + ret_cnt = debugfs_ep92_reg_show(codec, ubuf, cnt, ppos); + return ret_cnt; } -static const struct snd_kcontrol_new ep92_snd_controls[] = { - - SOC_ENUM_EXT("HDMI_IN POWER", ep92_power_enum, - ep92_power_get, ep92_power_put), - SOC_ENUM_EXT("HDMI_IN AUDIO_PATH", ep92_audio_path_enum, - ep92_audio_path_get, ep92_audio_path_put), - SOC_ENUM_EXT("HDMI_IN RX_SEL", ep92_rx_sel_enum, - ep92_rx_sel_get, ep92_rx_sel_put), - SOC_ENUM_EXT("HDMI_IN ARC_EN", ep92_arc_en_enum, - ep92_arc_en_get, ep92_arc_en_put), - SOC_ENUM_EXT("HDMI_IN CEC_MUTE", ep92_cec_mute_enum, - ep92_cec_mute_get, ep92_cec_mute_put), - SOC_SINGLE_EXT("HDMI_IN CEC_VOLUME", EP92_GENERAL_CONTROL_3, - EP92_GC_CEC_VOLUME_MIN, EP92_GC_CEC_VOLUME_MAX, - 0, ep92_cec_volume_get, ep92_cec_volume_put), - - SOC_ENUM_EXT("HDMI_IN STATE", ep92_state_enum, ep92_state_get, NULL), - SOC_ENUM_EXT("HDMI_IN AVMUTE", ep92_avmute_enum, ep92_avmute_get, NULL), - SOC_ENUM_EXT("HDMI_IN LAYOUT", ep92_layout_enum, ep92_layout_get, NULL), - SOC_ENUM_EXT("HDMI_IN MODE", ep92_mode_enum, ep92_mode_get, NULL), - SOC_SINGLE_EXT("HDMI_IN RATE", EP92_AUDIO_INFO_AUDIO_STATUS, - EP92_AI_RATE_MIN, EP92_AI_RATE_MAX, 0, ep92_rate_get, NULL), - SOC_SINGLE_EXT("HDMI_IN CH_COUNT", EP92_AUDIO_INFO_ADO_INFO_FRAME_1, - EP92_AI_CH_COUNT_MIN, EP92_AI_CH_COUNT_MAX, - 0, ep92_ch_count_get, NULL), - SOC_SINGLE_EXT("HDMI_IN CH_ALLOC", EP92_AUDIO_INFO_ADO_INFO_FRAME_4, - EP92_AI_CH_ALLOC_MIN, EP92_AI_CH_ALLOC_MAX, 0, - ep92_ch_alloc_get, NULL), +static const struct file_operations debugfs_codec_ops = { + .open = debugfs_codec_open_op, + .write = debugfs_codec_write_op, + .read = debugfs_codec_read_op, }; +#endif /* CONFIG_DEBUG_FS */ + +static int ep92_send_uevent(struct ep92_pdata *ep92, char *event) +{ + char *env[] = { event, NULL }; + + if (!event || !ep92) + return -EINVAL; + + return kobject_uevent_env(&ep92->codec->dev->kobj, KOBJ_CHANGE, env); +} static int ep92_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -669,328 +328,208 @@ static struct snd_soc_dai_driver ep92_dai[] = { }, }; -static const char * const ep92_event_power_text[] = { - "POWER=Off", - "POWER=On", -}; - -static const char * const ep92_event_arc_en_text[] = { - "ARC_EN=Off", - "ARC_EN=On", -}; - -static const char * const ep92_event_audio_path_text[] = { - "AUDIO_PATH=TV", - "AUDIO_PATH=Speaker", -}; - -static const char *const ep92_event_rx_sel_text[] = { - "RX_SEL=Port0", - "RX_SEL=Port1", - "RX_SEL=Port2", - "RX_SEL=Res3", - "RX_SEL=Res4", - "RX_SEL=Res5", - "RX_SEL=None", - "RX_SEL=Res7", -}; - -static const char *const ep92_event_cec_mute_text[] = { - "CEC_MUTE=Normal", - "CEC_MUTE=Muted", -}; - -static int ep92_send_uevent_ctrl(struct ep92_pdata *ep92) -{ - char *env[EP92_UEVENT_CTRL_NUM_KEYS]; - u8 idx = 0; - u8 cec_volume; - char cec_volume_text[] = "CEC_VOLUME=0x00"; - char *ptr; - - env[idx++] = "HDMI_CONTROL=TRUE"; - - if ((ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & - EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_power_text[1]; - else - env[idx++] = (char *)ep92_event_power_text[0]; - - if (ep92->gc.ctl & EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_arc_en_text[1]; - else - env[idx++] = (char *)ep92_event_arc_en_text[0]; - - if ((ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & - EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_audio_path_text[1]; - else - env[idx++] = (char *)ep92_event_audio_path_text[0]; - - switch (ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK) { - case 0: - env[idx++] = (char *)ep92_event_rx_sel_text[0]; - break; - case 1: - env[idx++] = (char *)ep92_event_rx_sel_text[1]; - break; - case 2: - env[idx++] = (char *)ep92_event_rx_sel_text[2]; - break; - case 3: - env[idx++] = (char *)ep92_event_rx_sel_text[3]; - break; - case 4: - env[idx++] = (char *)ep92_event_rx_sel_text[4]; - break; - case 5: - env[idx++] = (char *)ep92_event_rx_sel_text[5]; - break; - case 6: - env[idx++] = (char *)ep92_event_rx_sel_text[6]; - break; - case 7: - env[idx++] = (char *)ep92_event_rx_sel_text[7]; - break; - default: - env[idx++] = (char *)ep92_event_rx_sel_text[0]; - } - - if ((ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & - EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_cec_mute_text[1]; - else - env[idx++] = (char *)ep92_event_cec_mute_text[0]; - - ptr = &cec_volume_text[strlen(cec_volume_text)-2]; - cec_volume = (ep92->ai.ca) & EP92_GC_CEC_VOLUME_MASK; - *ptr++ = hex_to_char[(cec_volume >> 4) & 0x0f]; - *ptr++ = hex_to_char[cec_volume & 0x0f]; - env[idx++] = (char *)cec_volume_text; - - env[idx++] = NULL; - - if (idx != EP92_UEVENT_CTRL_NUM_KEYS) { - pr_err("ep92 wrong number of audio uevent keys (%d).\n", - idx); - return -EINVAL; - } - - return kobject_uevent_env(&ep92_uevent_ctrl->kobj, KOBJ_CHANGE, env); -} - -static const char * const ep92_event_state_text[] = { - "STATE=Inactive", - "STATE=Active", -}; - -static const char *const ep92_event_rate_text[] = { - "RATE=32000", - "RATE=44100", - "RATE=48000", - "RATE=88200", - "RATE=96000", - "RATE=176400", - "RATE=192000", - "RATE=768000", -}; - -static const char *const ep92_event_format_text[] = { - "FORMAT=LPCM", - "FORMAT=Compr", -}; - -static const char *const ep92_event_layout_text[] = { - "LAYOUT=2ch", - "LAYOUT=8ch", -}; - -static const char *const ep92_event_avmute_text[] = { - "AVMUTE=Normal", - "AVMUTE=Muted", -}; - -static const char *const ep92_event_ch_count_text[] = { - "CH_COUNT=One", - "CH_COUNT=Two", - "CH_COUNT=Three", - "CH_COUNT=Four", - "CH_COUNT=Five", - "CH_COUNT=Six", - "CH_COUNT=Seven", - "CH_COUNT=Eight", -}; - -static int ep92_send_uevent_audio(struct ep92_pdata *ep92) -{ - char *env[EP92_UEVENT_AUDIO_NUM_KEYS]; - u8 idx = 0; - u8 ch_alloc; - char ch_alloc_text[] = "CH_ALLOC=0x00"; - char *ptr; - - env[idx++] = "HDMI_FMT_UPDATE=TRUE"; - - if (((ep92->ai.system_status_0 >> EP92_AI_MCLK_ON_SHIFT) & - EP92_2CHOICE_MASK) == EP92_STATUS_AUDIO_ACTIVE) - env[idx++] = (char *)ep92_event_state_text[1]; - else - env[idx++] = (char *)ep92_event_state_text[0]; - - switch (ep92->ai.audio_status & EP92_AI_RATE_MASK) { - case 0: - env[idx++] = (char *)ep92_event_rate_text[0]; - break; - case 1: - env[idx++] = (char *)ep92_event_rate_text[1]; - break; - case 2: - env[idx++] = (char *)ep92_event_rate_text[2]; - break; - case 3: - env[idx++] = (char *)ep92_event_rate_text[3]; - break; - case 4: - env[idx++] = (char *)ep92_event_rate_text[4]; - break; - case 5: - env[idx++] = (char *)ep92_event_rate_text[5]; - break; - case 6: - env[idx++] = (char *)ep92_event_rate_text[6]; - break; - case 7: - env[idx++] = (char *)ep92_event_rate_text[7]; - break; - default: - env[idx++] = (char *)ep92_event_rate_text[2]; - } - - if (ep92->old_mode) - env[idx++] = (char *)ep92_event_format_text[1]; - else - env[idx++] = (char *)ep92_event_format_text[0]; - - if (ep92->ai.system_status_0 & EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_layout_text[1]; - else - env[idx++] = (char *)ep92_event_layout_text[0]; - - if ((ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) & - EP92_2CHOICE_MASK) - env[idx++] = (char *)ep92_event_avmute_text[1]; - else - env[idx++] = (char *)ep92_event_avmute_text[0]; - - /* cc==0 signals n/a and is treated as stereo */ - switch (ep92->ai.cc & EP92_AI_CH_COUNT_MASK) { - case 0: - env[idx++] = (char *)ep92_event_ch_count_text[1]; - break; - case 1: - env[idx++] = (char *)ep92_event_ch_count_text[1]; - break; - case 2: - env[idx++] = (char *)ep92_event_ch_count_text[2]; - break; - case 3: - env[idx++] = (char *)ep92_event_ch_count_text[3]; - break; - case 4: - env[idx++] = (char *)ep92_event_ch_count_text[4]; - break; - case 5: - env[idx++] = (char *)ep92_event_ch_count_text[5]; - break; - case 6: - env[idx++] = (char *)ep92_event_ch_count_text[6]; - break; - case 7: - env[idx++] = (char *)ep92_event_ch_count_text[7]; - break; - default: - env[idx++] = (char *)ep92_event_ch_count_text[1]; - } - - ptr = &ch_alloc_text[strlen(ch_alloc_text)-2]; - ch_alloc = (ep92->ai.ca) & EP92_AI_CH_ALLOC_MASK; - *ptr++ = hex_to_char[(ch_alloc >> 4) & 0x0f]; - *ptr++ = hex_to_char[ch_alloc & 0x0f]; - env[idx++] = (char *)ch_alloc_text; - - env[idx++] = NULL; - - if (idx != EP92_UEVENT_AUDIO_NUM_KEYS) { - pr_err("ep92 wrong number of audio uevent keys (%d).\n", - idx); - return -EINVAL; - } - - return kobject_uevent_env(&ep92_uevent_audio->kobj, KOBJ_CHANGE, env); -} - static void ep92_read_general_control(struct snd_soc_codec *codec, struct ep92_pdata *ep92) { u8 old, change; - bool send_uevent = false; + int val; + + old = ep92->gi.tx_info; + ep92->gi.tx_info = snd_soc_read(codec, EP92_BI_GENERAL_INFO_0); + if (ep92->gi.tx_info == 0xff) { + pr_debug("ep92 EP92_BI_GENERAL_INFO_0 read 0xff\n"); + ep92->gi.tx_info = old; + } + /* implement hysteresis to prevent events on glitches */ + if (ep92->gi.tx_info & EP92_GI_TX_HOT_PLUG_MASK) { + if (ep92->hyst_tx_plug < EP92_HYST_CNT) { + ep92->hyst_tx_plug++; + if ((ep92->hyst_tx_plug == EP92_HYST_CNT) && + (ep92->filt_tx_plug == 0)) { + ep92->filt_tx_plug = 1; + pr_debug("ep92 out_plug changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_OUT_PLUG=CONNECTED"); + } + } + } else { + if (ep92->hyst_tx_plug > 0) { + ep92->hyst_tx_plug--; + if ((ep92->hyst_tx_plug == 0) && + (ep92->filt_tx_plug == 1)) { + ep92->filt_tx_plug = 0; + pr_debug("ep92 out_plug changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_OUT_PLUG=DISCONNECTED"); + } + } + } + + old = ep92->gi.video_latency; + ep92->gi.video_latency = snd_soc_read(codec, EP92_BI_GENERAL_INFO_4); + if (ep92->gi.video_latency == 0xff) { + pr_debug("ep92 EP92_BI_GENERAL_INFO_4 read 0xff\n"); + ep92->gi.video_latency = old; + } + change = ep92->gi.video_latency ^ old; + if (change & EP92_GI_VIDEO_LATENCY_MASK) { + val = ep92->gi.video_latency; + if (val > 0) + val = (val - 1) * 2; + pr_debug("ep92 video latency changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_VIDEO_LATENCY=CHANGED"); + } old = ep92->gc.ctl; ep92->gc.ctl = snd_soc_read(codec, EP92_GENERAL_CONTROL_0); + if (ep92->gc.ctl == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_0 read 0xff\n"); + ep92->gc.ctl = old; + } change = ep92->gc.ctl ^ old; if (change & EP92_GC_POWER_MASK) { - pr_debug("ep92 power changed to %d (%s)\n", - (ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & - EP92_2CHOICE_MASK, - ep92_off_on_text[(ep92->gc.ctl - >> EP92_GC_POWER_SHIFT) & EP92_2CHOICE_MASK]); - send_uevent = true; + val = (ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 power changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_POWER=ON"); + else + ep92_send_uevent(ep92, "EP92EVT_POWER=OFF"); } if (change & EP92_GC_AUDIO_PATH_MASK) { - pr_debug("ep92 audio_path changed to %d (%s)\n", - (ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & - EP92_2CHOICE_MASK, - ep92_aud_path_text[(ep92->gc.ctl - >> EP92_GC_AUDIO_PATH_SHIFT) & EP92_2CHOICE_MASK]); - send_uevent = true; + val = (ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 audio_path changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_AUDIO_PATH=TV"); + else + ep92_send_uevent(ep92, "EP92EVT_AUDIO_PATH=SPEAKER"); } if (change & EP92_GC_CEC_MUTE_MASK) { - pr_debug("ep92 cec_mute changed to %d (%s)\n", - (ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & - EP92_2CHOICE_MASK, - ep92_cec_mute_text[(ep92->gc.ctl - >> EP92_GC_CEC_MUTE_SHIFT) & EP92_2CHOICE_MASK]); - send_uevent = true; + val = (ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 cec_mute changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_CEC_MUTE=NORMAL"); + else + ep92_send_uevent(ep92, "EP92EVT_CEC_MUTE=MUTED"); } if (change & EP92_GC_ARC_EN_MASK) { - pr_debug("ep92 arc_en changed to %d (%s)\n", - ep92->gc.ctl & EP92_2CHOICE_MASK, - ep92_off_on_text[ep92->gc.ctl & EP92_2CHOICE_MASK]); - send_uevent = true; + val = ep92->gc.ctl & EP92_2CHOICE_MASK; + pr_debug("ep92 arc_en changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_ARC_EN=ON"); + else + ep92_send_uevent(ep92, "EP92EVT_ARC_EN=OFF"); } old = ep92->gc.rx_sel; ep92->gc.rx_sel = snd_soc_read(codec, EP92_GENERAL_CONTROL_1); + if (ep92->gc.rx_sel == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_1 read 0xff\n"); + ep92->gc.rx_sel = old; + } change = ep92->gc.rx_sel ^ old; if (change & EP92_GC_RX_SEL_MASK) { - pr_debug("ep92 rx_sel changed to %d (%s)\n", - ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK, - ep92_rx_sel_text[ep92->gc.rx_sel & - EP92_GC_RX_SEL_MASK]); - send_uevent = true; + val = ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK; + pr_debug("ep92 rx_sel changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_SRC_SEL=CHANGED"); } old = ep92->gc.cec_volume; ep92->gc.cec_volume = snd_soc_read(codec, EP92_GENERAL_CONTROL_3); + if (ep92->gc.cec_volume == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_3 read 0xff\n"); + ep92->gc.cec_volume = old; + } change = ep92->gc.cec_volume ^ old; if (change & EP92_GC_CEC_VOLUME_MASK) { - pr_debug("ep92 cec_volume changed to %d\n", - ep92->gc.cec_volume & EP92_GC_CEC_VOLUME_MASK); - send_uevent = true; + val = ep92->gc.cec_volume & EP92_GC_CEC_VOLUME_MASK; + pr_debug("ep92 cec_volume changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_CEC_VOLUME=CHANGED"); } - if (send_uevent) - ep92_send_uevent_ctrl(ep92); + old = ep92->gc.link; + ep92->gc.link = snd_soc_read(codec, EP92_GENERAL_CONTROL_4); + if (ep92->gc.link == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_4 read 0xff\n"); + ep92->gc.link = old; + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON0_MASK) { + if (ep92->hyst_link_on0 < EP92_HYST_CNT) { + ep92->hyst_link_on0++; + if ((ep92->hyst_link_on0 == EP92_HYST_CNT) && + (ep92->filt_link_on0 == 0)) { + ep92->filt_link_on0 = 1; + pr_debug("ep92 link_on0 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON0=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on0 > 0) { + ep92->hyst_link_on0--; + if ((ep92->hyst_link_on0 == 0) && + (ep92->filt_link_on0 == 1)) { + ep92->filt_link_on0 = 0; + pr_debug("ep92 link_on0 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON0=DISCONNECTED"); + } + } + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON1_MASK) { + if (ep92->hyst_link_on1 < EP92_HYST_CNT) { + ep92->hyst_link_on1++; + if ((ep92->hyst_link_on1 == EP92_HYST_CNT) && + (ep92->filt_link_on1 == 0)) { + ep92->filt_link_on1 = 1; + pr_debug("ep92 link_on1 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON1=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on1 > 0) { + ep92->hyst_link_on1--; + if ((ep92->hyst_link_on1 == 0) && + (ep92->filt_link_on1 == 1)) { + ep92->filt_link_on1 = 0; + pr_debug("ep92 link_on1 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON1=DISCONNECTED"); + } + } + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON2_MASK) { + if (ep92->hyst_link_on2 < EP92_HYST_CNT) { + ep92->hyst_link_on2++; + if ((ep92->hyst_link_on2 == EP92_HYST_CNT) && + (ep92->filt_link_on2 == 0)) { + ep92->filt_link_on2 = 1; + pr_debug("ep92 link_on2 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON2=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on2 > 0) { + ep92->hyst_link_on2--; + if ((ep92->hyst_link_on2 == 0) && + (ep92->filt_link_on2 == 1)) { + ep92->filt_link_on2 = 0; + pr_debug("ep92 link_on2 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON2=DISCONNECTED"); + } + } + } } static void ep92_read_audio_info(struct snd_soc_codec *codec, @@ -1003,34 +542,36 @@ static void ep92_read_audio_info(struct snd_soc_codec *codec, old = ep92->ai.system_status_0; ep92->ai.system_status_0 = snd_soc_read(codec, EP92_AUDIO_INFO_SYSTEM_STATUS_0); + if (ep92->ai.system_status_0 == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_SYSTEM_STATUS_0 read 0xff\n"); + ep92->ai.system_status_0 = old; + } change = ep92->ai.system_status_0 ^ old; if (change & EP92_AI_MCLK_ON_MASK) { - pr_debug("ep92 status changed to %d (%s)\n", + pr_debug("ep92 status changed to %d\n", (ep92->ai.system_status_0 >> EP92_AI_MCLK_ON_SHIFT) & - EP92_2CHOICE_MASK, - ep92_state_text[(ep92->ai.system_status_0 - >> EP92_AI_MCLK_ON_SHIFT) & EP92_2CHOICE_MASK]); + EP92_2CHOICE_MASK); send_uevent = true; } if (change & EP92_AI_AVMUTE_MASK) { - pr_debug("ep92 avmute changed to %d (%s)\n", + pr_debug("ep92 avmute changed to %d\n", (ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) & - EP92_2CHOICE_MASK, - ep92_avmute_text[(ep92->ai.system_status_0 - >> EP92_AI_AVMUTE_SHIFT) & EP92_2CHOICE_MASK]); + EP92_2CHOICE_MASK); send_uevent = true; } if (change & EP92_AI_LAYOUT_MASK) { - pr_debug("ep92 layout changed to %d (%s)\n", - (ep92->ai.system_status_0) & EP92_2CHOICE_MASK, - ep92_layout_text[(ep92->ai.system_status_0) & - EP92_2CHOICE_MASK]); + pr_debug("ep92 layout changed to %d\n", + (ep92->ai.system_status_0) & EP92_2CHOICE_MASK); send_uevent = true; } old = ep92->ai.audio_status; ep92->ai.audio_status = snd_soc_read(codec, EP92_AUDIO_INFO_AUDIO_STATUS); + if (ep92->ai.audio_status == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_AUDIO_STATUS read 0xff\n"); + ep92->ai.audio_status = old; + } change = ep92->ai.audio_status ^ old; if (change & EP92_AI_RATE_MASK) { pr_debug("ep92 rate changed to %d\n", @@ -1041,8 +582,13 @@ static void ep92_read_audio_info(struct snd_soc_codec *codec, new_mode = ep92->old_mode; if (ep92->ai.audio_status & EP92_AI_STD_ADO_MASK) { + old = ep92->ai.cs[0]; ep92->ai.cs[0] = snd_soc_read(codec, EP92_AUDIO_INFO_CHANNEL_STATUS_0); + if (ep92->ai.cs[0] == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_CHANNEL_STATUS_0 read 0xff\n"); + ep92->ai.cs[0] = old; + } if (ep92->ai.cs[0] & EP92_AI_NPCM_MASK) new_mode = 1; /* Compr */ else @@ -1051,14 +597,17 @@ static void ep92_read_audio_info(struct snd_soc_codec *codec, new_mode = 1; /* Compr */ if (ep92->old_mode != new_mode) { - pr_debug("ep92 mode changed to %d (%s)\n", new_mode, - ep92_mode_text[new_mode]); + pr_debug("ep92 mode changed to %d\n", new_mode); send_uevent = true; } ep92->old_mode = new_mode; old = ep92->ai.cc; ep92->ai.cc = snd_soc_read(codec, EP92_AUDIO_INFO_ADO_INFO_FRAME_1); + if (ep92->ai.cc == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_ADO_INFO_FRAME_1 read 0xff\n"); + ep92->ai.cc = old; + } change = ep92->ai.cc ^ old; if (change & EP92_AI_CH_COUNT_MASK) { pr_debug("ep92 ch_count changed to %d (%d)\n", @@ -1070,6 +619,10 @@ static void ep92_read_audio_info(struct snd_soc_codec *codec, old = ep92->ai.ca; ep92->ai.ca = snd_soc_read(codec, EP92_AUDIO_INFO_ADO_INFO_FRAME_4); + if (ep92->ai.ca == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_ADO_INFO_FRAME_4 read 0xff\n"); + ep92->ai.ca = old; + } change = ep92->ai.ca ^ old; if (change & EP92_AI_CH_ALLOC_MASK) { pr_debug("ep92 ch_alloc changed to 0x%02x\n", @@ -1078,7 +631,7 @@ static void ep92_read_audio_info(struct snd_soc_codec *codec, } if (send_uevent) - ep92_send_uevent_audio(ep92); + ep92_send_uevent(ep92, "EP92EVT_AUDIO=MEDIA_CONFIG_CHANGE"); } static void ep92_init(struct snd_soc_codec *codec, struct ep92_pdata *ep92) @@ -1117,10 +670,6 @@ static struct snd_soc_codec_driver soc_codec_drv_ep92 = { .probe = ep92_probe, .remove = ep92_remove, .get_regmap = ep92_get_regmap, - .component_driver = { - .controls = ep92_snd_controls, - .num_controls = ARRAY_SIZE(ep92_snd_controls), - }, }; static struct regmap_config ep92_regmap_config = { @@ -1205,11 +754,712 @@ static const struct of_device_id ep92_of_match[] = { }; MODULE_DEVICE_TABLE(of, ep92_of_match); +static ssize_t ep92_sysfs_rda_audio_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->ai.system_status_0 & EP92_AI_MCLK_ON_MASK) >> + EP92_AI_MCLK_ON_SHIFT; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_format(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->old_mode; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92_samp_freq_table[(ep92->ai.audio_status) & + EP92_AI_RATE_MASK]; + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_layout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->ai.system_status_0 & EP92_AI_LAYOUT_MASK) >> + EP92_AI_LAYOUT_SHIFT; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_ch_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->ai.cc & EP92_AI_CH_COUNT_MASK; + /* mapping is ch_count = reg_val + 1, with exception: 0 = unknown */ + if (val > 0) + val += 1; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_ch_alloc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->ai.ca & EP92_AI_CH_ALLOC_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_avmute(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + + val = (ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) & + EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on0; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on1; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on2; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_out_plug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_tx_plug; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_video_latency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->gi.video_latency & EP92_GI_VIDEO_LATENCY_MASK; + if (val > 0) + val = (val - 1) * 2; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_arc_disable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl2 >> EP92_GC_ARC_DIS_SHIFT) & + EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_arc_disable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_2); + reg &= ~EP92_GC_ARC_DIS_MASK; + reg |= ((val << EP92_GC_ARC_DIS_SHIFT) & EP92_GC_ARC_DIS_MASK); + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_2, reg); + ep92->gc.ctl2 &= ~EP92_GC_ARC_DIS_MASK; + ep92->gc.ctl2 |= (val << EP92_GC_ARC_DIS_SHIFT) & EP92_GC_ARC_DIS_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_power(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_POWER_MASK; + reg |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_POWER_MASK; + ep92->gc.ctl |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_audio_path(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_audio_path(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_AUDIO_PATH_MASK; + reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK; + ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) & + EP92_GC_AUDIO_PATH_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_src_sel(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_src_sel(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 7)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_1); + reg &= ~EP92_GC_RX_SEL_MASK; + reg |= (val << EP92_GC_RX_SEL_SHIFT) & EP92_GC_RX_SEL_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_1, reg); + ep92->gc.rx_sel &= ~EP92_GC_RX_SEL_MASK; + ep92->gc.rx_sel |= (val << EP92_GC_RX_SEL_SHIFT) & EP92_GC_RX_SEL_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_arc_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_ARC_EN_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_arc_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_AUDIO_PATH_MASK; + reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK; + ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) & + EP92_GC_AUDIO_PATH_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_cec_mute(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_cec_mute(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_CEC_MUTE_MASK; + reg |= (val << EP92_GC_CEC_MUTE_SHIFT) & EP92_GC_CEC_MUTE_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_CEC_MUTE_MASK; + ep92->gc.ctl |= (val << EP92_GC_CEC_MUTE_SHIFT) & + EP92_GC_CEC_MUTE_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_cec_volume(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.cec_volume >> EP92_GC_CEC_VOLUME_SHIFT) & + EP92_GC_CEC_VOLUME_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_cec_volume(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > EP92_GC_CEC_VOLUME_MAX)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = val & EP92_GC_CEC_VOLUME_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_3, reg); + ep92->gc.cec_volume = val & EP92_GC_CEC_VOLUME_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static DEVICE_ATTR(audio_state, 0444, ep92_sysfs_rda_audio_state, NULL); +static DEVICE_ATTR(audio_format, 0444, ep92_sysfs_rda_audio_format, NULL); +static DEVICE_ATTR(audio_rate, 0444, ep92_sysfs_rda_audio_rate, NULL); +static DEVICE_ATTR(audio_layout, 0444, ep92_sysfs_rda_audio_layout, NULL); +static DEVICE_ATTR(audio_ch_count, 0444, ep92_sysfs_rda_audio_ch_count, NULL); +static DEVICE_ATTR(audio_ch_alloc, 0444, ep92_sysfs_rda_audio_ch_alloc, NULL); +static DEVICE_ATTR(audio_avmute, 0444, ep92_sysfs_rda_avmute, NULL); +static DEVICE_ATTR(link_on0, 0444, ep92_sysfs_rda_link_on0, NULL); +static DEVICE_ATTR(link_on1, 0444, ep92_sysfs_rda_link_on1, NULL); +static DEVICE_ATTR(link_on2, 0444, ep92_sysfs_rda_link_on2, NULL); +static DEVICE_ATTR(out_plug, 0444, ep92_sysfs_rda_out_plug, NULL); +static DEVICE_ATTR(video_latency, 0444, ep92_sysfs_rda_video_latency, NULL); +static DEVICE_ATTR(arc_disable, 0644, ep92_sysfs_rda_arc_disable, + ep92_sysfs_wta_arc_disable); +static DEVICE_ATTR(power_on, 0644, ep92_sysfs_rda_power, ep92_sysfs_wta_power); +static DEVICE_ATTR(audio_path, 0644, ep92_sysfs_rda_audio_path, + ep92_sysfs_wta_audio_path); +static DEVICE_ATTR(src_sel, 0644, ep92_sysfs_rda_src_sel, + ep92_sysfs_wta_src_sel); +static DEVICE_ATTR(arc_enable, 0644, ep92_sysfs_rda_arc_enable, + ep92_sysfs_wta_arc_enable); +static DEVICE_ATTR(cec_mute, 0644, ep92_sysfs_rda_cec_mute, + ep92_sysfs_wta_cec_mute); +static DEVICE_ATTR(cec_volume, 0644, ep92_sysfs_rda_cec_volume, + ep92_sysfs_wta_cec_volume); + +static struct attribute *ep92_fs_attrs[] = { + &dev_attr_audio_state.attr, + &dev_attr_audio_format.attr, + &dev_attr_audio_rate.attr, + &dev_attr_audio_layout.attr, + &dev_attr_audio_ch_count.attr, + &dev_attr_audio_ch_alloc.attr, + &dev_attr_audio_avmute.attr, + &dev_attr_link_on0.attr, + &dev_attr_link_on1.attr, + &dev_attr_link_on2.attr, + &dev_attr_out_plug.attr, + &dev_attr_video_latency.attr, + &dev_attr_arc_disable.attr, + &dev_attr_power_on.attr, + &dev_attr_audio_path.attr, + &dev_attr_src_sel.attr, + &dev_attr_arc_enable.attr, + &dev_attr_cec_mute.attr, + &dev_attr_cec_volume.attr, + NULL, +}; + +static struct attribute_group ep92_fs_attrs_group = { + .attrs = ep92_fs_attrs, +}; + +static int ep92_sysfs_create(struct i2c_client *client, + struct ep92_pdata *ep92) +{ + int rc; + + rc = sysfs_create_group(&client->dev.kobj, &ep92_fs_attrs_group); + + return rc; +} + +static void ep92_sysfs_remove(struct i2c_client *client, + struct ep92_pdata *ep92) +{ + sysfs_remove_group(&client->dev.kobj, &ep92_fs_attrs_group); +} + static int ep92_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ep92_pdata *ep92; int ret; +#if IS_ENABLED(CONFIG_DEBUG_FS) + char debugfs_dir_name[32]; +#endif ep92 = devm_kzalloc(&client->dev, sizeof(struct ep92_pdata), GFP_KERNEL); @@ -1220,8 +1470,8 @@ static int ep92_i2c_probe(struct i2c_client *client, if (IS_ERR(ep92->regmap)) { ret = PTR_ERR(ep92->regmap); dev_err(&client->dev, - "%s %d: Failed to allocate regmap for I2C device: %d\n", - __func__, __LINE__, ret); + "%s: Failed to allocate regmap for I2C device: %d\n", + __func__, ret); return ret; } @@ -1249,6 +1499,39 @@ static int ep92_i2c_probe(struct i2c_client *client, msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC)); } +#if IS_ENABLED(CONFIG_DEBUG_FS) + /* debugfs interface */ + snprintf(debugfs_dir_name, sizeof(debugfs_dir_name), "%s-%s", + client->name, dev_name(&client->dev)); + ep92->debugfs_dir = debugfs_create_dir(debugfs_dir_name, NULL); + if (!ep92->debugfs_dir) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s for debugfs\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + ep92->debugfs_file_wo = debugfs_create_file( + "write_reg_val", S_IFREG | 0444, ep92->debugfs_dir, + (void *) ep92, + &debugfs_codec_ops); + if (!ep92->debugfs_file_wo) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s/write_reg_val\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + ep92->debugfs_file_ro = debugfs_create_file( + "show_reg_dump", S_IFREG | 0444, ep92->debugfs_dir, + (void *) ep92, + &debugfs_codec_ops); + if (!ep92->debugfs_file_ro) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s/show_reg_dump\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } +#endif /* CONFIG_DEBUG_FS */ + /* register codec */ ret = snd_soc_register_codec(&client->dev, &soc_codec_drv_ep92, ep92_dai, ARRAY_SIZE(ep92_dai)); @@ -1259,58 +1542,15 @@ static int ep92_i2c_probe(struct i2c_client *client, goto err_reg; } - /* Create a kset under /sys/kernel/ */ - ep92_uevent_kset = kset_create_and_add("ep92-hdmi", NULL, kernel_kobj); - if (!ep92_uevent_kset) { - pr_err("%s: error creating uevent kernel set", __func__); - ret = -EINVAL; - goto err_kset; - } - - /* uevent to signal control changes */ - ep92_uevent_ctrl = devm_kzalloc(&client->dev, - sizeof(*ep92_uevent_ctrl), GFP_KERNEL); - if (!ep92_uevent_ctrl) { - ret = -ENOMEM; - goto err_ue_ctrl; - } - - ep92_uevent_ctrl->ktype.release = ep92_release_uevent_data; - ret = ep92_init_uevent_data(ep92_uevent_ctrl, "ctrl-uevent"); + ret = ep92_sysfs_create(client, ep92); if (ret) { - dev_err(&client->dev, - "%s: Failed to init ctrl-uevent: %d\n", - __func__, ret); - goto err_ue_init_ctrl; + pr_err("%s: sysfs creation failed ret=%d\n", __func__, ret); + goto err_sysfs; } - /* uevent to signal audio format changes */ - ep92_uevent_audio = devm_kzalloc(&client->dev, - sizeof(*ep92_uevent_audio), GFP_KERNEL); - if (!ep92_uevent_audio) { - ret = -ENOMEM; - goto err_ue_audio; - } - - ep92_uevent_audio->ktype.release = ep92_release_uevent_data; - ret = ep92_init_uevent_data(ep92_uevent_audio, "audio-uevent"); - if (ret) { - dev_err(&client->dev, - "%s: Failed to init ctrl-uevent: %d\n", - __func__, ret); - goto err_ue_init_audio; - } return 0; -err_ue_init_audio: - devm_kfree(&client->dev, ep92_uevent_audio); -err_ue_audio: - ep92_destroy_uevent_data(ep92_uevent_ctrl); -err_ue_init_ctrl: - devm_kfree(&client->dev, ep92_uevent_ctrl); -err_ue_ctrl: - kset_unregister(ep92_uevent_kset); -err_kset: +err_sysfs: snd_soc_unregister_codec(&client->dev); err_reg: if (ep92->irq == 0) @@ -1324,21 +1564,17 @@ static int ep92_i2c_remove(struct i2c_client *client) struct ep92_pdata *ep92; ep92 = i2c_get_clientdata(client); - if ((ep92 != NULL) && (ep92->irq == 0)) - del_timer(&ep92->timer); + if (ep92) { + if (ep92->irq == 0) + del_timer(&ep92->timer); +#if IS_ENABLED(CONFIG_DEBUG_FS) + debugfs_remove_recursive(ep92->debugfs_dir); +#endif + } snd_soc_unregister_codec(&client->dev); - ep92_destroy_uevent_data(ep92_uevent_ctrl); - devm_kfree(&client->dev, ep92_uevent_ctrl); - - ep92_destroy_uevent_data(ep92_uevent_audio); - devm_kfree(&client->dev, ep92_uevent_audio); - - if (ep92_uevent_kset) { - kset_unregister(ep92_uevent_kset); - ep92_uevent_kset = NULL; - } + ep92_sysfs_remove(client, ep92); return 0; } diff --git a/asoc/codecs/ep92/ep92.h b/asoc/codecs/ep92/ep92.h index 237c9eac2a42..ad067ea1dc93 100644 --- a/asoc/codecs/ep92/ep92.h +++ b/asoc/codecs/ep92/ep92.h @@ -139,8 +139,13 @@ static struct reg_default ep92_reg_defaults[] = { * GC = General Control * AI = Audio Info */ -#define EP92_GI_ADO_CHF_MASK 0x01 -#define EP92_GI_CEC_ECF_MASK 0x02 +#define EP92_GI_ADO_CHF_MASK 0x01 +#define EP92_GI_CEC_ECF_MASK 0x02 +#define EP92_GI_TX_HOT_PLUG_SHIFT 7 +#define EP92_GI_TX_HOT_PLUG_MASK 0x80 +#define EP92_GI_VIDEO_LATENCY_SHIFT 0 +#define EP92_GI_VIDEO_LATENCY_MASK 0xff + #define EP92_GC_POWER_SHIFT 7 #define EP92_GC_POWER_MASK 0x80 #define EP92_GC_AUDIO_PATH_SHIFT 5 @@ -149,10 +154,19 @@ static struct reg_default ep92_reg_defaults[] = { #define EP92_GC_CEC_MUTE_MASK 0x02 #define EP92_GC_ARC_EN_SHIFT 0 #define EP92_GC_ARC_EN_MASK 0x01 +#define EP92_GC_ARC_DIS_SHIFT 6 +#define EP92_GC_ARC_DIS_MASK 0x40 #define EP92_GC_RX_SEL_SHIFT 0 #define EP92_GC_RX_SEL_MASK 0x07 #define EP92_GC_CEC_VOLUME_SHIFT 0 #define EP92_GC_CEC_VOLUME_MASK 0xff +#define EP92_GC_LINK_ON0_SHIFT 0 +#define EP92_GC_LINK_ON0_MASK 0x01 +#define EP92_GC_LINK_ON1_SHIFT 1 +#define EP92_GC_LINK_ON1_MASK 0x02 +#define EP92_GC_LINK_ON2_SHIFT 2 +#define EP92_GC_LINK_ON2_MASK 0x04 + #define EP92_AI_MCLK_ON_SHIFT 6 #define EP92_AI_MCLK_ON_MASK 0x40 #define EP92_AI_AVMUTE_SHIFT 5