summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/av8100_audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/av8100_audio.c')
-rw-r--r--sound/soc/codecs/av8100_audio.c245
1 files changed, 188 insertions, 57 deletions
diff --git a/sound/soc/codecs/av8100_audio.c b/sound/soc/codecs/av8100_audio.c
index 7bcb985b9eb..d85a8ca24e5 100644
--- a/sound/soc/codecs/av8100_audio.c
+++ b/sound/soc/codecs/av8100_audio.c
@@ -28,21 +28,21 @@
#include "av8100_audio.h"
-#define AV8100_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
-#define AV8100_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
+/* codec private data */
+struct av8100_codec_private_data {
+ struct hdmi_audio_settings as;
+};
-static int setupAV8100_stereo(void)
+static int av8100_codec_powerup(void)
{
- union av8100_configuration config;
struct av8100_status status;
int ret;
pr_debug("%s: Enter.\n", __func__);
- /* Startup AV8100 if it is not already started */
status = av8100_status_get();
if (status.av8100_state < AV8100_OPMODE_STANDBY) {
- pr_info("%s: Powering up AV8100.", __func__);
+ pr_debug("%s: Powering up AV8100.", __func__);
ret = av8100_powerup();
if (ret != 0) {
pr_err("%s: Power up AV8100 failed "
@@ -63,11 +63,21 @@ static int setupAV8100_stereo(void)
}
}
- /* Set the HDMI format of AV8100 */
- pr_info("%s: Setting hdmi_format.", __func__);
+ return 0;
+}
+
+static int av8100_codec_setup_hdmi_format(void)
+{
+ union av8100_configuration config;
+ int ret;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ pr_debug("%s: hdmi_mode = AV8100_HDMI_ON.", __func__);
+ pr_debug("%s: hdmi_format = AV8100_HDMI.", __func__);
config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
config.hdmi_format.hdmi_format = AV8100_HDMI;
- ret = av8100_conf_prep(AV8100_COMMAND_AUDIO_INPUT_FORMAT, &config);
+ ret = av8100_conf_prep(AV8100_COMMAND_HDMI, &config);
if (ret != 0) {
pr_err("%s: Setting hdmi_format failed "
"(av8100_conf_prep returned %d)!\n",
@@ -75,7 +85,7 @@ static int setupAV8100_stereo(void)
ret);
return -EINVAL;
}
- ret = av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
+ ret = av8100_conf_w(AV8100_COMMAND_HDMI,
NULL,
NULL,
I2C_INTERFACE);
@@ -87,26 +97,78 @@ static int setupAV8100_stereo(void)
return -EINVAL;
}
- /* Set the audio input format of AV8100 */
- pr_info("%s: Setting audio_input_format.", __func__);
- config.audio_input_format.audio_input_if_format = AV8100_AUDIO_I2SDELAYED_MODE;
- config.audio_input_format.i2s_input_nb = 1;
- config.audio_input_format.sample_audio_freq = AV8100_AUDIO_FREQ_48KHZ;
- config.audio_input_format.audio_word_lg = AV8100_AUDIO_16BITS;
- config.audio_input_format.audio_format = AV8100_AUDIO_LPCM_MODE;
- config.audio_input_format.audio_if_mode = AV8100_AUDIO_MASTER;
- config.audio_input_format.audio_mute = AV8100_AUDIO_MUTE_DISABLE;
- ret = av8100_conf_prep(AV8100_COMMAND_AUDIO_INPUT_FORMAT, &config);
+ return 0;
+}
+
+static int av8100_codec_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s: Enter.\n", __func__);
+
+ return 0;
+}
+
+static int av8100_codec_send_audio_infoframe(struct hdmi_audio_settings *as)
+{
+ union av8100_configuration config;
+ struct av8100_infoframes_format_cmd info_fr;
+ int ret;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ pr_debug("%s: HDMI-settings:\n", __func__);
+ pr_debug("%s: audio_coding_type = %d\n", __func__, as->audio_coding_type);
+ pr_debug("%s: audio_channel_count = %d\n", __func__, as->audio_channel_count);
+ pr_debug("%s: sampling_frequency = %d\n", __func__, as->sampling_frequency);
+ pr_debug("%s: sample_size = %d\n", __func__, as->sample_size);
+ pr_debug("%s: channel_allocation = %d\n", __func__, as->channel_allocation);
+ pr_debug("%s: level_shift_value = %d\n", __func__, as->level_shift_value);
+ pr_debug("%s: downmix_inhibit = %d\n", __func__, as->downmix_inhibit);
+
+ /* Prepare the infoframe from the hdmi_audio_settings struct */
+ pr_info("%s: Preparing audio info-frame.", __func__);
+ info_fr.type = 0x84;
+ info_fr.version = 0x01;
+ info_fr.length = 0x0a;
+ info_fr.data[0] = (as->audio_coding_type << 4) | as->audio_channel_count;
+ info_fr.data[1] = (as->sampling_frequency << 2) | as->sample_size;
+ info_fr.data[2] = 0;
+ info_fr.data[3] = as->channel_allocation;
+ info_fr.data[4] = ((int)as->downmix_inhibit << 7) |
+ (as->level_shift_value << 3);
+ info_fr.data[5] = 0;
+ info_fr.data[6] = 0;
+ info_fr.data[7] = 0;
+ info_fr.data[8] = 0;
+ info_fr.data[9] = 0;
+ info_fr.crc = info_fr.version +
+ info_fr.length +
+ info_fr.data[0] +
+ info_fr.data[1] +
+ info_fr.data[3] +
+ info_fr.data[4];
+ config.infoframes_format.type = info_fr.type;
+ config.infoframes_format.version = info_fr.version;
+ config.infoframes_format.crc = info_fr.crc;
+ config.infoframes_format.length = info_fr.length;
+ memcpy(&config.infoframes_format.data, info_fr.data, info_fr.length);
+
+ /* Send audio info-frame */
+ pr_info("%s: Sending audio info-frame.", __func__);
+ ret = av8100_conf_prep(AV8100_COMMAND_INFOFRAMES, &config);
if (ret != 0) {
- pr_err("%s: Setting audio_input_format failed "
+ pr_err("%s: Sending audio info-frame failed "
"(av8100_conf_prep returned %d)!\n",
__func__,
ret);
return -EINVAL;
}
- if (av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
- NULL, NULL, I2C_INTERFACE) != 0) {
- pr_err("%s: Setting audio_input_format failed "
+ ret = av8100_conf_w(AV8100_COMMAND_INFOFRAMES,
+ NULL,
+ NULL,
+ I2C_INTERFACE);
+ if (ret != 0) {
+ pr_err("%s: Sending audio info-frame failed "
"(av8100_conf_w returned %d)!\n",
__func__,
ret);
@@ -116,11 +178,23 @@ static int setupAV8100_stereo(void)
return 0;
}
-static int av8100_codec_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+int av8100_codec_change_hdmi_audio_settings(struct snd_pcm_substream *substream,
+ struct hdmi_audio_settings *as)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
+ struct av8100_codec_private_data *av8100_codec_priv = codec->private_data;
+
pr_debug("%s: Enter.\n", __func__);
+ av8100_codec_priv->as.audio_coding_type = as->audio_coding_type;
+ av8100_codec_priv->as.audio_channel_count = as->audio_channel_count;
+ av8100_codec_priv->as.sampling_frequency = as->sampling_frequency;
+ av8100_codec_priv->as.sample_size = as->sample_size;
+ av8100_codec_priv->as.channel_allocation = as->channel_allocation;
+ av8100_codec_priv->as.level_shift_value = as->level_shift_value;
+ av8100_codec_priv->as.downmix_inhibit = as->downmix_inhibit;
+
return 0;
}
@@ -128,30 +202,35 @@ static int av8100_codec_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{
- int ret;
- int channels;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
+ struct av8100_codec_private_data *av8100_codec_priv = codec->private_data;
pr_debug("%s: Enter.\n", __func__);
- channels = params_channels(hw_params);
- switch (channels) {
- case 1:
- goto error_channels;
- case 2:
- ret = setupAV8100_stereo();
- break;
- case 6:
- goto error_channels;
- default:
- goto error_channels;
- }
+ av8100_codec_send_audio_infoframe(&av8100_codec_priv->as);
- return ret;
+ return 0;
+}
+
+static int av8100_codec_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ pr_debug("%s: Enter.\n", __func__);
-error_channels:
- pr_err("%s: Unsupported number of channels (%d)!\n", __func__, channels);
+ /* Startup AV8100 if it is not already started */
+ ret = av8100_codec_powerup();
+ if (ret != 0) {
+ pr_err("%s: Startup of AV8100 failed "
+ "(av8100_codec_powerupAV8100 returned %d)!\n",
+ __func__,
+ ret);
+ return -EINVAL;
+ }
- return -1;
+ return 0;
}
static void av8100_codec_pcm_shutdown(struct snd_pcm_substream *substream,
@@ -172,8 +251,53 @@ static int av8100_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int av8100_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
+ union av8100_configuration config;
+ int ret;
+
pr_debug("%s: Enter.\n", __func__);
+ /* Set the HDMI format of AV8100 */
+ ret = av8100_codec_setup_hdmi_format();
+ if (ret != 0)
+ return ret;
+
+ /* Set the audio input format of AV8100 */
+ config.audio_input_format.audio_input_if_format =
+ ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_B) ?
+ AV8100_AUDIO_TDM_MODE : AV8100_AUDIO_I2SDELAYED_MODE;
+ config.audio_input_format.audio_if_mode =
+ ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM) ?
+ AV8100_AUDIO_MASTER : AV8100_AUDIO_SLAVE;
+ pr_info("%s: Setting audio_input_format "
+ "(if_format = %d, if_mode = %d).",
+ __func__,
+ config.audio_input_format.audio_input_if_format,
+ config.audio_input_format.audio_if_mode);
+ config.audio_input_format.i2s_input_nb = 1;
+ config.audio_input_format.sample_audio_freq = AV8100_AUDIO_FREQ_48KHZ;
+ config.audio_input_format.audio_word_lg = AV8100_AUDIO_16BITS;
+ config.audio_input_format.audio_format = AV8100_AUDIO_LPCM_MODE;
+ config.audio_input_format.audio_mute = AV8100_AUDIO_MUTE_DISABLE;
+ ret = av8100_conf_prep(AV8100_COMMAND_AUDIO_INPUT_FORMAT, &config);
+ if (ret != 0) {
+ pr_err("%s: Setting audio_input_format failed "
+ "(av8100_conf_prep returned %d)!\n",
+ __func__,
+ ret);
+ return -EINVAL;
+ }
+ ret = av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
+ NULL,
+ NULL,
+ I2C_INTERFACE);
+ if (ret != 0) {
+ pr_err("%s: Setting audio_input_format failed "
+ "(av8100_conf_w returned %d)!\n",
+ __func__,
+ ret);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -182,15 +306,8 @@ struct snd_soc_dai av8100_codec_dai[] = {
.name = "av8100_0",
.playback = {
.stream_name = "av8100_0",
- .channels_min = 2,
- .channels_max = 6,
- .rates = AV8100_SUPPORTED_RATE,
- .formats = AV8100_SUPPORTED_FMT,
- },
- .capture = {
- .stream_name = "av8100_0",
- .channels_min = 2,
- .channels_max = 6,
+ .channels_min = 1,
+ .channels_max = 8,
.rates = AV8100_SUPPORTED_RATE,
.formats = AV8100_SUPPORTED_FMT,
},
@@ -198,6 +315,7 @@ struct snd_soc_dai av8100_codec_dai[] = {
{
.prepare = av8100_codec_pcm_prepare,
.hw_params = av8100_codec_pcm_hw_params,
+ .startup = av8100_codec_pcm_startup,
.shutdown = av8100_codec_pcm_shutdown,
.set_sysclk = av8100_codec_set_dai_sysclk,
.set_fmt = av8100_codec_set_dai_fmt,
@@ -228,6 +346,7 @@ static int av8100_codec_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
+ struct av8100_codec_private_data *av8100_codec_priv;
int ret;
pr_info("%s: Enter (pdev = %p).\n", __func__, pdev);
@@ -244,8 +363,22 @@ static int av8100_codec_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- mutex_init(&codec->mutex);
+ pr_info("%s: Init codec private data..\n", __func__);
+ av8100_codec_priv = kzalloc(sizeof(struct av8100_codec_private_data), GFP_KERNEL);
+ if (av8100_codec_priv == NULL)
+ return -ENOMEM;
+
+ /* Setup hdmi_audio_settings default values */
+ av8100_codec_priv->as.audio_coding_type = AV8100_CODEC_CT_IEC60958_PCM;
+ av8100_codec_priv->as.audio_channel_count = AV8100_CODEC_CC_2CH;
+ av8100_codec_priv->as.sampling_frequency = AV8100_CODEC_SF_48KHZ;
+ av8100_codec_priv->as.sample_size = AV8100_CODEC_SS_16BIT;
+ av8100_codec_priv->as.channel_allocation = AV8100_CODEC_CA_FL_FR;
+ av8100_codec_priv->as.level_shift_value = AV8100_CODEC_LSV_0DB;
+ av8100_codec_priv->as.downmix_inhibit = false;
+ codec->private_data = &av8100_codec_priv;
+ mutex_init(&codec->mutex);
socdev->card->codec = codec;
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -329,6 +462,4 @@ static void av8100_codec_exit(void)
module_init(av8100_codec_init);
module_exit(av8100_codec_exit);
-MODULE_DESCRIPTION("AV8100 ASoC codec driver");
-MODULE_AUTHOR("www.stericsson.com");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");