diff options
Diffstat (limited to 'sound/soc/codecs/av8100_audio.c')
-rw-r--r-- | sound/soc/codecs/av8100_audio.c | 245 |
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"); |