From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- kernel/sound/soc/codecs/hdmi-codec.c | 591 +++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 378 insertions(+), 213 deletions(-) diff --git a/kernel/sound/soc/codecs/hdmi-codec.c b/kernel/sound/soc/codecs/hdmi-codec.c index 0cb4976..4d3725b 100644 --- a/kernel/sound/soc/codecs/hdmi-codec.c +++ b/kernel/sound/soc/codecs/hdmi-codec.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ALSA SoC codec for HDMI encoder drivers - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * 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 <linux/module.h> #include <linux/string.h> @@ -30,7 +22,6 @@ struct hdmi_codec_channel_map_table { unsigned char map; /* ALSA API channel map position */ - unsigned long spk_mask; /* speaker position bit mask */ }; /* @@ -279,16 +270,16 @@ struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; - struct snd_soc_dai_driver *daidrv; - struct hdmi_codec_daifmt daifmt[2]; - struct mutex current_stream_lock; - struct snd_pcm_substream *current_stream; uint8_t eld[MAX_ELD_BYTES]; struct snd_pcm_chmap *chmap_info; unsigned int chmap_idx; - unsigned int mode; + struct mutex lock; + bool busy; + bool eld_bypass; struct snd_soc_jack *jack; unsigned int jack_status; + u8 iec_status[24]; + struct snd_pcm_substream *substream; }; static const struct snd_soc_dapm_widget hdmi_widgets[] = { @@ -305,7 +296,7 @@ struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = FIELD_SIZEOF(struct hdmi_codec_priv, eld); + uinfo->count = sizeof_field(struct hdmi_codec_priv, eld); return 0; } @@ -318,36 +309,6 @@ memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); - return 0; -} - -static int hdmi_audio_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = HBR; - return 0; -} - -static int hdmi_audio_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - - ucontrol->value.integer.value[0] = hcp->mode; - return 0; -} - -static int hdmi_audio_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - - hcp->mode = ucontrol->value.integer.value[0]; return 0; } @@ -391,6 +352,9 @@ unsigned long spk_mask; const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; + if (hcp->eld_bypass) + return 0; + spk_alloc = drm_eld_get_spk_alloc(hcp->eld); spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); @@ -427,22 +391,70 @@ return 0; } -static int hdmi_codec_new_stream(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - int ret = 0; + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} - mutex_lock(&hcp->current_stream_lock); - if (!hcp->current_stream) { - hcp->current_stream = substream; - } else if (hcp->current_stream != substream) { - dev_err(dai->dev, "Only one simultaneous stream supported!\n"); - ret = -EINVAL; - } - mutex_unlock(&hcp->current_stream_lock); +static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - return ret; + memcpy(ucontrol->value.iec958.status, hcp->iec_status, + sizeof(hcp->iec_status)); + + return 0; +} + +static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + memcpy(hcp->iec_status, ucontrol->value.iec958.status, + sizeof(hcp->iec_status)); + + return 0; +} + +static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, + sizeof_field(struct hdmi_codec_priv, iec_status)); + + return 0; +} + +static int hdmi_codec_eld_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = hcp->eld_bypass; + + return 0; +} + +static int hdmi_codec_eld_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + if (hcp->eld_bypass == ucontrol->value.integer.value[0]) + return 0; + + hcp->eld_bypass = ucontrol->value.integer.value[0]; + + return 1; } static int hdmi_codec_startup(struct snd_pcm_substream *substream, @@ -452,40 +464,39 @@ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int ret = 0; - dev_dbg(dai->dev, "%s()\n", __func__); - - ret = hdmi_codec_new_stream(substream, dai); - if (ret) - return ret; + mutex_lock(&hcp->lock); + if (hcp->busy) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); + mutex_unlock(&hcp->lock); + return -EINVAL; + } if (hcp->hcd.ops->audio_startup) { ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); - if (ret) { - mutex_lock(&hcp->current_stream_lock); - hcp->current_stream = NULL; - mutex_unlock(&hcp->current_stream_lock); - return ret; - } + if (ret) + goto err; } - if (tx && hcp->hcd.ops->get_eld) { + if (tx && !hcp->eld_bypass && hcp->hcd.ops->get_eld) { ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); + if (ret) + goto err; - if (!ret) { - ret = snd_pcm_hw_constraint_eld(substream->runtime, - hcp->eld); - if (ret) { - mutex_lock(&hcp->current_stream_lock); - hcp->current_stream = NULL; - mutex_unlock(&hcp->current_stream_lock); - return ret; - } - } + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); + if (ret) + goto err; + /* Select chmap supported */ hdmi_codec_eld_chmap(hcp); } - return 0; + + hcp->busy = true; + hcp->substream = substream; + +err: + mutex_unlock(&hcp->lock); + return ret; } static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, @@ -493,16 +504,49 @@ { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - dev_dbg(dai->dev, "%s()\n", __func__); - - WARN_ON(hcp->current_stream != substream); - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - mutex_lock(&hcp->current_stream_lock); - hcp->current_stream = NULL; - mutex_unlock(&hcp->current_stream_lock); + mutex_lock(&hcp->lock); + hcp->substream = NULL; + hcp->busy = false; + mutex_unlock(&hcp->lock); +} + +static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, + unsigned int sample_width, + unsigned int sample_rate, + unsigned int channels, + struct hdmi_codec_params *hp) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int idx; + + /* Select a channel allocation that matches with ELD and pcm channels */ + idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); + if (idx < 0) { + dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", + idx); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + return idx; + } + + memset(hp, 0, sizeof(*hp)); + + hdmi_audio_infoframe_init(&hp->cea); + hp->cea.channels = channels; + hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; + + hp->sample_width = sample_width; + hp->sample_rate = sample_rate; + hp->channels = channels; + + hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; + + return 0; } static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, @@ -510,6 +554,7 @@ struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_daifmt *cf = dai->playback_dma_data; struct hdmi_codec_params hp = { .iec = { .status = { 0 }, @@ -518,74 +563,91 @@ .dig_subframe = { 0 }, } }; - int ret, idx; + int ret; + + if (!hcp->hcd.ops->hw_params) + return 0; dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, params_width(params), params_rate(params), params_channels(params)); - if (params_width(params) > 24) - params->msbits = 24; + ret = hdmi_codec_fill_codec_params(dai, + params_width(params), + params_rate(params), + params_channels(params), + &hp); + if (ret < 0) + return ret; - ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status, - sizeof(hp.iec.status)); + memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); + ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status, + sizeof(hp.iec.status)); if (ret < 0) { dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", ret); return ret; } - ret = hdmi_codec_new_stream(substream, dai); - if (ret) - return ret; - - hdmi_audio_infoframe_init(&hp.cea); - hp.cea.channels = params_channels(params); - hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; - hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; - hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; - - /* Select a channel allocation that matches with ELD and pcm channels */ - idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels); - if (idx < 0) { - dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", - idx); - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - return idx; - } - hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; - hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; - - hp.sample_width = params_width(params); - hp.sample_rate = params_rate(params); - hp.channels = params_channels(params); - hp.mode = hcp->mode; - + cf->bit_fmt = params_format(params); return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data, - &hcp->daifmt[dai->id], &hp); + cf, &hp); } -static int hdmi_codec_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) +static int hdmi_codec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct hdmi_codec_daifmt cf = { 0 }; + struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channels = runtime->channels; + unsigned int width = snd_pcm_format_width(runtime->format); + unsigned int rate = runtime->rate; + struct hdmi_codec_params hp; + int ret; - dev_dbg(dai->dev, "%s()\n", __func__); - - if (dai->id == DAI_ID_SPDIF) + if (!hcp->hcd.ops->prepare) return 0; + + dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, + width, rate, channels); + + ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp); + if (ret < 0) + return ret; + + memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); + ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status, + sizeof(hp.iec.status)); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + + cf->bit_fmt = runtime->format; + return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data, + cf, &hp); +} + +static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + + /* Reset daifmt */ + memset(cf, 0, sizeof(*cf)); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - cf.bit_clk_master = 1; - cf.frame_clk_master = 1; + cf->bit_clk_master = 1; + cf->frame_clk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFM: - cf.frame_clk_master = 1; + cf->frame_clk_master = 1; break; case SND_SOC_DAIFMT_CBM_CFS: - cf.bit_clk_master = 1; + cf->bit_clk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFS: break; @@ -597,67 +659,79 @@ case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_NB_IF: - cf.frame_clk_inv = 1; + cf->frame_clk_inv = 1; break; case SND_SOC_DAIFMT_IB_NF: - cf.bit_clk_inv = 1; + cf->bit_clk_inv = 1; break; case SND_SOC_DAIFMT_IB_IF: - cf.frame_clk_inv = 1; - cf.bit_clk_inv = 1; + cf->frame_clk_inv = 1; + cf->bit_clk_inv = 1; break; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - cf.fmt = HDMI_I2S; + cf->fmt = HDMI_I2S; break; case SND_SOC_DAIFMT_DSP_A: - cf.fmt = HDMI_DSP_A; + cf->fmt = HDMI_DSP_A; break; case SND_SOC_DAIFMT_DSP_B: - cf.fmt = HDMI_DSP_B; + cf->fmt = HDMI_DSP_B; break; case SND_SOC_DAIFMT_RIGHT_J: - cf.fmt = HDMI_RIGHT_J; + cf->fmt = HDMI_RIGHT_J; break; case SND_SOC_DAIFMT_LEFT_J: - cf.fmt = HDMI_LEFT_J; + cf->fmt = HDMI_LEFT_J; break; case SND_SOC_DAIFMT_AC97: - cf.fmt = HDMI_AC97; + cf->fmt = HDMI_AC97; break; default: dev_err(dai->dev, "Invalid DAI interface format\n"); return -EINVAL; } - hcp->daifmt[dai->id] = cf; - return 0; } -static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute) +static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - dev_dbg(dai->dev, "%s()\n", __func__); + /* + * ignore if direction was CAPTURE + * and it had .no_capture_mute flag + * see + * snd_soc_dai_digital_mute() + */ + if (hcp->hcd.ops->mute_stream && + (direction == SNDRV_PCM_STREAM_PLAYBACK || + !hcp->hcd.ops->no_capture_mute)) + return hcp->hcd.ops->mute_stream(dai->dev->parent, + hcp->hcd.data, + mute, direction); - if (hcp->hcd.ops->digital_mute) - return hcp->hcd.ops->digital_mute(dai->dev->parent, - hcp->hcd.data, mute); - - return 0; + return -ENOTSUPP; } -static const struct snd_soc_dai_ops hdmi_dai_ops = { +static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { .startup = hdmi_codec_startup, .shutdown = hdmi_codec_shutdown, .hw_params = hdmi_codec_hw_params, - .set_fmt = hdmi_codec_set_fmt, - .digital_mute = hdmi_codec_digital_mute, + .prepare = hdmi_codec_prepare, + .set_fmt = hdmi_codec_i2s_set_fmt, + .mute_stream = hdmi_codec_mute, }; +static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .mute_stream = hdmi_codec_mute, +}; #define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ @@ -681,36 +755,43 @@ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static struct snd_kcontrol_new hdmi_codec_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = hdmi_codec_iec958_info, + .get = hdmi_codec_iec958_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = hdmi_codec_iec958_info, + .get = hdmi_codec_iec958_default_get, + .put = hdmi_codec_iec958_default_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, + }, + SOC_SINGLE_BOOL_EXT("ELD Bypass Switch", 0, + hdmi_codec_eld_bypass_get, hdmi_codec_eld_bypass_put), +}; static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_dai_driver *drv = dai->driver; struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct snd_kcontrol *kctl; - struct snd_kcontrol_new hdmi_eld_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdmi_eld_ctl_info, - .get = hdmi_eld_ctl_get, - .device = rtd->pcm->device, - }; - struct snd_kcontrol_new hdmi_mode_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_WRITE | - SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "AUDIO MODE", - .info = hdmi_audio_mode_info, - .get = hdmi_audio_mode_get, - .put = hdmi_audio_mode_put, - }; + unsigned int i; int ret; - - dev_dbg(dai->dev, "%s()\n", __func__); ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, drv->playback.channels_max, 0, @@ -726,26 +807,27 @@ hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - /* add ELD ctl with the device number corresponding to the PCM stream */ - kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component); - if (!kctl) - return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) { + struct snd_kcontrol *kctl; - ret = snd_ctl_add(rtd->card->snd_card, kctl); - if (ret < 0) - return ret; + /* add ELD ctl with the device number corresponding to the PCM stream */ + kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component); + if (!kctl) + return -ENOMEM; - /* add MODE ctl with the device number corresponding to the PCM stream */ - kctl = snd_ctl_new1(&hdmi_mode_ctl, dai->component); - if (!kctl) - return -ENOMEM; + kctl->id.device = rtd->pcm->device; + ret = snd_ctl_add(rtd->card->snd_card, kctl); + if (ret < 0) + return ret; + } - return snd_ctl_add(rtd->card->snd_card, kctl); + return 0; } static int hdmi_dai_probe(struct snd_soc_dai *dai) { struct snd_soc_dapm_context *dapm; + struct hdmi_codec_daifmt *daifmt; struct snd_soc_dapm_route route[] = { { .sink = "TX", @@ -756,9 +838,19 @@ .source = "RX", }, }; + int ret; dapm = snd_soc_component_get_dapm(dai->component); - return snd_soc_dapm_add_routes(dapm, route, 2); + ret = snd_soc_dapm_add_routes(dapm, route, 2); + if (ret) + return ret; + + daifmt = kzalloc(sizeof(*daifmt), GFP_KERNEL); + if (!daifmt) + return -ENOMEM; + + dai->playback_dma_data = daifmt; + return 0; } static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, @@ -774,10 +866,49 @@ { struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); - if (plugged) + if (plugged) { + if (!hcp->eld_bypass && hcp->hcd.ops->get_eld) { + hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + } hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); - else + } else { hdmi_codec_jack_report(hcp, 0); + memset(hcp->eld, 0, sizeof(hcp->eld)); + } + + mutex_lock(&hcp->lock); + if (hcp->substream) { + /* + * Workaround for HDMIIN and HDMIOUT plug-{in,out} when streaming. + * + * Actually, we should do stop stream both for HDMI_{OUT,IN} on + * plug-{out,in} event. but for better experience and depop stream, + * we optimize as follows: + * + * a) Do stop stream for HDMIIN on plug-out when streaming. + * because HDMIIN work as SLAVE mode, CLK lost after HDMI cable + * plugged out which will make stream stuck until ALSA timeout(10s). + * so, for better experience, we should stop stream at the moment. + * + * b) Do stop stream for HDMIOUT on plug-in when streaming. + * because HDMIOUT work as MASTER mode, there is no clk-issue like + * HDMIIN, but, on HDR situation, HDMI will be reconfigured which + * make HDMI audio configure lost, especially for NLPCM/HBR bitstream + * which require IEC937 packet alignment, so, for this situation, + * we stop stream to notify user to re-open and configure sound card + * and then go on streaming. + */ + int stream = hcp->substream->stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK && plugged) + snd_pcm_stop(hcp->substream, SNDRV_PCM_STATE_SETUP); + else if (stream == SNDRV_PCM_STREAM_CAPTURE && !plugged) + snd_pcm_stop(hcp->substream, SNDRV_PCM_STATE_DISCONNECTED); + + dev_dbg(dev, "stream[%d]: %s\n", stream, plugged ? "plug in" : "plug out"); + } + mutex_unlock(&hcp->lock); } static int hdmi_codec_set_jack(struct snd_soc_component *component, @@ -785,7 +916,7 @@ void *data) { struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - int ret = -EOPNOTSUPP; + int ret = -ENOTSUPP; if (hcp->hcd.ops->hook_plugged_cb) { hcp->jack = jack; @@ -799,10 +930,32 @@ return ret; } +static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) +{ + struct hdmi_codec_daifmt *cf; + int ret; + + ret = hdmi_dai_probe(dai); + if (ret) + return ret; + + cf = dai->playback_dma_data; + cf->fmt = HDMI_SPDIF; + + return 0; +} + +static int hdmi_codec_dai_remove(struct snd_soc_dai *dai) +{ + kfree(dai->playback_dma_data); + return 0; +} + static const struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, .probe = hdmi_dai_probe, + .remove = hdmi_codec_dai_remove, .playback = { .stream_name = "I2S Playback", .channels_min = 2, @@ -819,14 +972,15 @@ .formats = I2S_FORMATS, .sig_bits = 24, }, - .ops = &hdmi_dai_ops, + .ops = &hdmi_codec_i2s_dai_ops, .pcm_new = hdmi_codec_pcm_new, }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { .name = "spdif-hifi", .id = DAI_ID_SPDIF, - .probe = hdmi_dai_probe, + .probe = hdmi_dai_spdif_probe, + .remove = hdmi_codec_dai_remove, .playback = { .stream_name = "SPDIF Playback", .channels_min = 2, @@ -841,7 +995,7 @@ .rates = HDMI_RATES, .formats = SPDIF_FORMATS, }, - .ops = &hdmi_dai_ops, + .ops = &hdmi_codec_spdif_dai_ops, .pcm_new = hdmi_codec_pcm_new, }; @@ -857,7 +1011,17 @@ return ret; } +static void hdmi_remove(struct snd_soc_component *component) +{ + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + if (hcp->hcd.ops->hook_plugged_cb) + hcp->hcd.ops->hook_plugged_cb(component->dev->parent, + hcp->hcd.data, NULL, NULL); +} + static const struct snd_soc_component_driver hdmi_driver = { + .remove = hdmi_remove, .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), .of_xlate_dai_id = hdmi_of_xlate_dai_id, @@ -871,20 +1035,20 @@ static int hdmi_codec_probe(struct platform_device *pdev) { struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; + struct snd_soc_dai_driver *daidrv; struct device *dev = &pdev->dev; struct hdmi_codec_priv *hcp; int dai_count, i = 0; int ret; - dev_dbg(dev, "%s()\n", __func__); - if (!hcd) { - dev_err(dev, "%s: No plalform data\n", __func__); + dev_err(dev, "%s: No platform data\n", __func__); return -EINVAL; } dai_count = hcd->i2s + hcd->spdif; - if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || + if (dai_count < 1 || !hcd->ops || + (!hcd->ops->hw_params && !hcd->ops->prepare) || !hcd->ops->audio_shutdown) { dev_err(dev, "%s: Invalid parameters\n", __func__); return -EINVAL; @@ -895,29 +1059,30 @@ return -ENOMEM; hcp->hcd = *hcd; - mutex_init(&hcp->current_stream_lock); + mutex_init(&hcp->lock); - hcp->daidrv = devm_kcalloc(dev, dai_count, sizeof(*hcp->daidrv), - GFP_KERNEL); - if (!hcp->daidrv) + ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status, + sizeof(hcp->iec_status)); + if (ret < 0) + return ret; + + daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL); + if (!daidrv) return -ENOMEM; if (hcd->i2s) { - hcp->daidrv[i] = hdmi_i2s_dai; - hcp->daidrv[i].playback.channels_max = - hcd->max_i2s_channels; + daidrv[i] = hdmi_i2s_dai; + daidrv[i].playback.channels_max = hcd->max_i2s_channels; i++; } - if (hcd->spdif) { - hcp->daidrv[i] = hdmi_spdif_dai; - hcp->daifmt[DAI_ID_SPDIF].fmt = HDMI_SPDIF; - } + if (hcd->spdif) + daidrv[i] = hdmi_spdif_dai; dev_set_drvdata(dev, hcp); - ret = devm_snd_soc_register_component(dev, &hdmi_driver, hcp->daidrv, - dai_count); + ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv, + dai_count); if (ret) { dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n", __func__, ret); -- Gitblit v1.6.2