@@ -39,6 +39,14 @@
#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
+#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
+ xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = omap_mcbsp_st_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long) &(struct soc_mixer_control) \
+ {.min = xmin, .max = xmax} }
+
struct omap_mcbsp_data {
unsigned int bus_id;
struct omap_mcbsp_reg_cfg regs;
@@ -637,6 +645,174 @@ struct snd_soc_dai omap_mcbsp_dai[] = {
EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
+int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
+ int min = mc->min;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = min;
+ uinfo->value.integer.max = max;
+ return 0;
+}
+
+static int omap_mcbsp_set_st_channel_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ int id, int channel)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
+ int min = mc->min;
+ int val = ucontrol->value.integer.value[0];
+
+ if (val < min || val > max)
+ return -EINVAL;
+
+ return omap_st_set_chgain((id)-1, val, 0, channel);
+}
+
+static int omap_mcbsp_get_st_channel_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ int id, int channel)
+{
+ s16 ch0gain, ch1gain;
+
+ if (omap_st_get_chgain((id)-1, &ch0gain, &ch1gain))
+ return -EAGAIN;
+
+ if (channel == OMAP_MCBSP_ST_CHANNEL_0)
+ ucontrol->value.integer.value[0] = ch0gain;
+ else if (channel == OMAP_MCBSP_ST_CHANNEL_1)
+ ucontrol->value.integer.value[0] = ch1gain;
+
+ return 0;
+}
+
+#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \
+static int \
+omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+ struct snd_ctl_elem_value *uc) \
+{ \
+ return omap_mcbsp_set_st_channel_vol(kc, uc, id, \
+ OMAP_MCBSP_ST_CHANNEL_##channel); \
+}
+
+#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \
+static int \
+omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+ struct snd_ctl_elem_value *uc) \
+{ \
+ return omap_mcbsp_get_st_channel_vol(kc, uc, id, \
+ OMAP_MCBSP_ST_CHANNEL_##channel); \
+}
+
+OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0)
+OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1)
+OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0)
+OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1)
+OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0)
+OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1)
+OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0)
+OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1)
+
+static int omap_mcbsp2_st_set_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = ucontrol->value.integer.value[0];
+
+ if (value == omap_st_is_enabled(1))
+ return 0;
+
+ if (value)
+ omap_st_enable(1);
+ else
+ omap_st_disable(1);
+
+ return 1;
+}
+
+static int omap_mcbsp2_st_get_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = omap_st_is_enabled(1);
+ return 0;
+}
+
+static int omap_mcbsp3_st_set_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = ucontrol->value.integer.value[0];
+
+ if (value == omap_st_is_enabled(2))
+ return 0;
+
+ if (value)
+ omap_st_enable(2);
+ else
+ omap_st_disable(2);
+
+ return 1;
+}
+
+static int omap_mcbsp3_st_get_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ ucontrol->value.integer.value[0] = omap_st_is_enabled(2);
+ return 0;
+}
+
+static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = {
+ SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 0, 0, 1, 0,
+ omap_mcbsp2_st_get_mode, omap_mcbsp2_st_set_mode),
+ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume",
+ -32768, 32767,
+ omap_mcbsp2_get_st_ch0_volume,
+ omap_mcbsp2_set_st_ch0_volume),
+ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume",
+ -32768, 32767,
+ omap_mcbsp2_get_st_ch1_volume,
+ omap_mcbsp2_set_st_ch1_volume),
+};
+
+static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = {
+ SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 0, 0, 1, 0,
+ omap_mcbsp3_st_get_mode, omap_mcbsp3_st_set_mode),
+ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume",
+ -32768, 32767,
+ omap_mcbsp3_get_st_ch0_volume,
+ omap_mcbsp3_set_st_ch0_volume),
+ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume",
+ -32768, 32767,
+ omap_mcbsp3_get_st_ch1_volume,
+ omap_mcbsp3_set_st_ch1_volume),
+};
+
+int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id)
+{
+ if (!cpu_is_omap34xx())
+ return -ENODEV;
+
+ switch (mcbsp_id) {
+ case 2: /* McBSP 2 */
+ return snd_soc_add_controls(codec, omap_mcbsp2_st_controls,
+ ARRAY_SIZE(omap_mcbsp2_st_controls));
+ case 3: /* McBSP 3 */
+ return snd_soc_add_controls(codec, omap_mcbsp3_st_controls,
+ ARRAY_SIZE(omap_mcbsp3_st_controls));
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
+
static int __init snd_omap_mcbsp_init(void)
{
return snd_soc_register_dais(omap_mcbsp_dai,
@@ -57,4 +57,6 @@ enum omap_mcbsp_div {
extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
+int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id);
+
#endif