[alsa-devel] [PATCH v2 00/14] OMAPDSS: HDMI: Prepare for DSS dev driver audio support and OMAP5
Hi,
This set of patches is intended to prepare the HDMI driver to implement the DSS device driver interface for audio proposed here: http://www.mail-archive.com/linux-omap@vger.kernel.org/msg67804.html
In preparation for that, it removes the current ASoC HDMI codec driver and decouples HDMI audio build configuration from ASoC. Instead, a config option may be selected by the parties interested in the HDMI audio functionality. While the ASoC HDMI codec is removed, HDMI audio functionality restored in the last patch of the series in the form of DSS audio support.
Also, this set prepares the HDMI driver for the introduction of the OMAP5 HDMI audio functionality by further abstracting the portions of code that are generic to all HDMI implementations (e.g, N/CTS params calculation). Also, an IP-dependent audio configuration function is introduced as an HDMI IP operation; this function takes IP-independent parameters and applies them as required by the IP.
For the specific case of OMAP4, the configuration of the IEC-60958 channel status word is expanded to provide more flexibility. Also, some duplicated IEC-60958 definitions are removed to, instead, reuse the definitions provided by ALSA. Please note that this patches depend on the following patch adding CEA-861 definitions: http://www.spinics.net/lists/linux-omap/msg68246.html. Without such patch, the patches in this series will not compile.
The changes for OMAP4 configuration expand the current support to cover more audio sample rates: 32, 44.1, 48, 88.2, 176.4 and 192 kHz. Audio sample world length of 16 through 24 bits as well as up to 8 audio channels.
These changes are based on the 3.4-rc5 kernel.
Validation was performed using Onkyo TX-SR508 and Yamaha RX-V367 AV receivers.
These are the changes with respect to v1: *The DSS dev driver audio operations are, at the moment, protected using a hardirq-safe spinlock. Except for the audio_start/stop functions, it was concluded that a mutex could be used in the future is needed. *In audio configuration, instead of parsing the IEC-60958 channel status word, and setting bits individually, the word is passed directly to the IP. This saves code, makes it more readable and fully configurable by the user. *Split HDMI video_enable, audio_enable and audio_start into two separate functions to enable/disable and start/stop. Also, update their return values. *Audio channels are remapped to match the order preferred by ALSA's speaker-test. *Remove OMAP4 HDMI IP configuration regarding High Bitrate audio and I2S word select polarity, as this is not supported according to updated TI's documentation. *Rename the lock of the HDMI panel. *Remove unneeded header files. *Rephrase the description of several patches.
BR,
Ricardo
Axel Castaneda Gonzalez (1): OMAPDSS: HDMI: Decouple wrapper enable/disable and audio start/stop
Ricardo Neri (13): OMAPDSS: HDMI: Split audio_enable into audio_enable/disable OMAPDSS: HDMI: Split video_enable into video_enable/disable OMAPDSS: HDMI: Remove ASoC codec OMAPDSS: HDMI: OMAP4: Remove CEA-861 audio infoframe and IEC-60958 enums OMAPDSS: HDMI: OMAP4: Remove invalid I2S settings OMAPDSS: HDMI: Decouple HDMI audio from ASoC OMAPDSS: HDMI: OMAP4: Expand configuration for IEC-60958 audio OMAPDSS: HDMI: Relocate N/CTS calculation OMAPDSS: HDMI: Add support for more audio sample rates in N/CTS calculation OMAPDSS: HDMI: Add an audio configuration function OMAPDSS: HDMI: OMAP4: Remap speaker order to match ALSA order OMAPDSS: HDMI: Panel: Simplify the name of the HDMI mutex OMAPDSS: HDMI: Implement DSS driver interface for audio
drivers/video/omap2/dss/Kconfig | 4 + drivers/video/omap2/dss/dss.h | 8 + drivers/video/omap2/dss/dss_features.c | 8 +- drivers/video/omap2/dss/hdmi.c | 355 +++++++++++------------------ drivers/video/omap2/dss/hdmi_panel.c | 141 ++++++++++-- drivers/video/omap2/dss/ti_hdmi.h | 32 ++- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 316 +++++++++++++++++++------ drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 104 +--------- 8 files changed, 537 insertions(+), 431 deletions(-)
To improve readability, split the audio_enable HDMI IP operation into two separate functions for enabling and disabling audio. The audio_enable function is also modified to return an error value.
While there, update these operations for the OMAP4 IP accordingly.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/dss_features.c | 1 + drivers/video/omap2/dss/hdmi.c | 4 ++-- drivers/video/omap2/dss/ti_hdmi.h | 7 +++++-- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 19 +++++++++++++++---- 4 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index ce14aa6..52fbc72 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -569,6 +569,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, + .audio_disable = ti_hdmi_4xxx_wp_audio_disable, #endif
}; diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c4b4f69..9a8b32c 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -576,12 +576,12 @@ static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ip_data->ops->audio_enable(ip_data, true); + ip_data->ops->audio_enable(ip_data); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ip_data->ops->audio_enable(ip_data, false); + ip_data->ops->audio_disable(ip_data); break; default: err = -EINVAL; diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 1f58b84..88fdc1c 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -108,7 +108,9 @@ struct ti_hdmi_ip_ops {
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - void (*audio_enable)(struct hdmi_ip_data *ip_data, bool start); + int (*audio_enable)(struct hdmi_ip_data *ip_data); + + void (*audio_disable)(struct hdmi_ip_data *ip_data); #endif
}; @@ -183,6 +185,7 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable); +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index bfe6fe6..b25b519 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1249,13 +1249,24 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, return 0; }
-void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable) +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_av_base(ip_data), - HDMI_CORE_AV_AUD_MODE, enable, 0, 0); + HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 31, 31); + HDMI_WP_AUDIO_CTRL, true, 31, 31); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 30, 30); + HDMI_WP_AUDIO_CTRL, true, 30, 30); + return 0; +} + +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, false, 0, 0); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 30, 30); } #endif
To improve readability, split the video_enable HDMI IP operation into two separate functions for enabling and disabling video. The video_enable function is also modified to return an error value.
While there, update these operations for the OMAP4 IP accordingly.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/dss_features.c | 1 + drivers/video/omap2/dss/hdmi.c | 11 +++++++---- drivers/video/omap2/dss/ti_hdmi.h | 7 +++++-- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 10 ++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 52fbc72..c677095 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -562,6 +562,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .pll_enable = ti_hdmi_4xxx_pll_enable, .pll_disable = ti_hdmi_4xxx_pll_disable, .video_enable = ti_hdmi_4xxx_wp_video_start, + .video_disable = ti_hdmi_4xxx_wp_video_stop, .dump_wrapper = ti_hdmi_4xxx_wp_dump, .dump_core = ti_hdmi_4xxx_core_dump, .dump_pll = ti_hdmi_4xxx_pll_dump, diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 9a8b32c..159aa66 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -344,7 +344,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
- hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
/* config the PLL and PHY hdmi_set_pll_pwrfirst */ r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); @@ -379,7 +379,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dispc_set_digit_size(dssdev->panel.timings.x_res, dssdev->panel.timings.y_res);
- hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1); + r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data); + if (r) + goto err_vid_enable;
r = dss_mgr_enable(dssdev->manager); if (r) @@ -388,7 +390,8 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) return 0;
err_mgr_enable: - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); +err_vid_enable: hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); err: @@ -400,7 +403,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev) { dss_mgr_disable(dssdev->manager);
- hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); hdmi_runtime_put(); diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 88fdc1c..4c84a9c 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -96,7 +96,9 @@ struct ti_hdmi_ip_ops {
void (*pll_disable)(struct hdmi_ip_data *ip_data);
- void (*video_enable)(struct hdmi_ip_data *ip_data, bool start); + int (*video_enable)(struct hdmi_ip_data *ip_data); + + void (*video_disable)(struct hdmi_ip_data *ip_data);
void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s);
@@ -175,7 +177,8 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start); +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index b25b519..aa18163 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -699,9 +699,15 @@ static void hdmi_wp_init(struct omap_video_timings *timings,
}
-void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data) { - REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31); }
static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
Remove the ASoC OMAP HDMI audio codec. The goal of removing the codec is to, in subsequent patches, give way to the implementation of the HDMI audio support using the DSS device driver audio interface. This approach will expose the HDMI audio functionality to any interested entity.
In a separate patch, ASoC will use this new approach to expose HDMI audio to ALSA.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/hdmi.c | 236 ----------------------------- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 5 - 2 files changed, 0 insertions(+), 241 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 159aa66..11d6ca8 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -33,12 +33,6 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <video/omapdss.h> -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#include "ti_hdmi_4xxx_ip.h" -#endif
#include "ti_hdmi.h" #include "dss.h" @@ -558,220 +552,6 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); }
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - -static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct platform_device *pdev = to_platform_device(codec->dev); - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - int err = 0; - - if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { - dev_err(&pdev->dev, "Cannot enable/disable audio\n"); - return -ENODEV; - } - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ip_data->ops->audio_enable(ip_data); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ip_data->ops->audio_disable(ip_data); - break; - default: - err = -EINVAL; - } - return err; -} - -static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - struct hdmi_audio_format audio_format; - struct hdmi_audio_dma audio_dma; - struct hdmi_core_audio_config core_cfg; - struct hdmi_core_infoframe_audio aud_if_cfg; - int err, n, cts; - enum hdmi_core_audio_sample_freq sample_freq; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_20BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_16; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_dma.transfer_size = 0x10; - break; - case SNDRV_PCM_FORMAT_S24_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_24BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_24; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - audio_dma.transfer_size = 0x20; - break; - default: - return -EINVAL; - } - - switch (params_rate(params)) { - case 32000: - sample_freq = HDMI_AUDIO_FS_32000; - break; - case 44100: - sample_freq = HDMI_AUDIO_FS_44100; - break; - case 48000: - sample_freq = HDMI_AUDIO_FS_48000; - break; - default: - return -EINVAL; - } - - err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts); - if (err < 0) - return err; - - /* Audio wrapper config */ - audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; - audio_format.active_chnnls_msk = 0x03; - audio_format.type = HDMI_AUDIO_TYPE_LPCM; - audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; - /* Disable start/stop signals of IEC 60958 blocks */ - audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; - - audio_dma.block_size = 0xC0; - audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; - audio_dma.fifo_threshold = 0x20; /* in number of samples */ - - hdmi_wp_audio_config_dma(ip_data, &audio_dma); - hdmi_wp_audio_config_format(ip_data, &audio_format); - - /* - * I2S config - */ - core_cfg.i2s_cfg.en_high_bitrate_aud = false; - /* Only used with high bitrate audio */ - core_cfg.i2s_cfg.cbit_order = false; - /* Serial data and word select should change on sck rising edge */ - core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; - core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; - /* Set I2S word select polarity */ - core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; - core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; - /* Set serial data to word select shift. See Phillips spec. */ - core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; - /* Enable one of the four available serial data channels */ - core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; - - /* Core audio config */ - core_cfg.freq_sample = sample_freq; - core_cfg.n = n; - core_cfg.cts = cts; - if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { - core_cfg.aud_par_busclk = 0; - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); - } else { - core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; - core_cfg.use_mclk = true; - } - - if (core_cfg.use_mclk) - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; - core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; - core_cfg.en_spdif = false; - /* Use sample frequency from channel status word */ - core_cfg.fs_override = true; - /* Enable ACR packets */ - core_cfg.en_acr_pkt = true; - /* Disable direct streaming digital audio */ - core_cfg.en_dsd_audio = false; - /* Use parallel audio interface */ - core_cfg.en_parallel_aud_input = true; - - hdmi_core_audio_config(ip_data, &core_cfg); - - /* - * Configure packet - * info frame audio see doc CEA861-D page 74 - */ - aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; - aud_if_cfg.db1_channel_count = 2; - aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; - aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; - aud_if_cfg.db4_channel_alloc = 0x00; - aud_if_cfg.db5_downmix_inh = false; - aud_if_cfg.db5_lsv = 0; - - hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg); - return 0; -} - -static int hdmi_audio_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - if (!hdmi.ip_data.cfg.cm.mode) { - pr_err("Current video settings do not support audio.\n"); - return -EIO; - } - return 0; -} - -static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) -{ - struct hdmi_ip_data *priv = &hdmi.ip_data; - - snd_soc_codec_set_drvdata(codec, priv); - return 0; -} - -static struct snd_soc_codec_driver hdmi_audio_codec_drv = { - .probe = hdmi_audio_codec_probe, -}; - -static struct snd_soc_dai_ops hdmi_audio_codec_ops = { - .hw_params = hdmi_audio_hw_params, - .trigger = hdmi_audio_trigger, - .startup = hdmi_audio_startup, -}; - -static struct snd_soc_dai_driver hdmi_codec_dai_drv = { - .name = "hdmi-audio-codec", - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &hdmi_audio_codec_ops, -}; -#endif - static int hdmi_get_clocks(struct platform_device *pdev) { struct clk *clk; @@ -833,17 +613,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi_panel_init();
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - - /* Register ASoC codec DAI */ - r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, - &hdmi_codec_dai_drv, 1); - if (r) { - DSSERR("can't register ASoC HDMI audio codec\n"); - return r; - } -#endif return 0; }
@@ -851,11 +620,6 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev) { hdmi_panel_exit();
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - snd_soc_unregister_codec(&pdev->dev); -#endif - pm_runtime_disable(&pdev->dev);
hdmi_put_clocks(); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a14d1a0..a5f28af 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -24,11 +24,6 @@ #include <linux/string.h> #include <video/omapdss.h> #include "ti_hdmi.h" -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#endif
/* HDMI Wrapper */
Instead of having its own definitions for CEA-861 and IEC-60958, the HDMI driver should use those provided by ALSA. This patch removes the definitions that are already provided by ALSA.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 34 ++++++------ drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 81 +---------------------------- 2 files changed, 20 insertions(+), 95 deletions(-)
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index aa18163..ac73309 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -29,6 +29,10 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#include <sound/asound.h> +#endif
#include "ti_hdmi_4xxx_ip.h" #include "dss.h" @@ -1147,9 +1151,8 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, }
void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud) + struct snd_cea_861_aud_if *info_aud) { - u8 val; u8 sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data);
@@ -1163,24 +1166,23 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); sum += 0x84 + 0x001 + 0x00a;
- val = (info_aud->db1_coding_type << 4) - | (info_aud->db1_channel_count - 1); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), + info_aud->db1_ct_cc); + sum += info_aud->db1_ct_cc;
- val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), + info_aud->db2_sf_ss); + sum += info_aud->db2_sf_ss;
- hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3); + sum += info_aud->db3;
- val = info_aud->db4_channel_alloc; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca); + sum += info_aud->db4_ca;
- val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), + info_aud->db5_dminh_lsv); + sum += info_aud->db5_dminh_lsv;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a5f28af..26b91ff 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -279,35 +279,6 @@ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB5PR_8 = 7, HDMI_INFOFRAME_AVI_DB5PR_9 = 8, HDMI_INFOFRAME_AVI_DB5PR_10 = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, - HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, - HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, - HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, - HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, - HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, - HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, - HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, - HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, - HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, - HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, - HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, - HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, - HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, - HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, - HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, - HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, - HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, - HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 };
enum hdmi_packing_mode { @@ -317,17 +288,6 @@ enum hdmi_packing_mode { HDMI_PACK_ALREADYPACKED = 7 };
-enum hdmi_core_audio_sample_freq { - HDMI_AUDIO_FS_32000 = 0x3, - HDMI_AUDIO_FS_44100 = 0x0, - HDMI_AUDIO_FS_48000 = 0x2, - HDMI_AUDIO_FS_88200 = 0x8, - HDMI_AUDIO_FS_96000 = 0xA, - HDMI_AUDIO_FS_176400 = 0xC, - HDMI_AUDIO_FS_192000 = 0xE, - HDMI_AUDIO_FS_NOT_INDICATED = 0x1 -}; - enum hdmi_core_audio_layout { HDMI_AUDIO_LAYOUT_2CH = 0, HDMI_AUDIO_LAYOUT_8CH = 1 @@ -386,33 +346,10 @@ enum hdmi_audio_i2s_config { HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, - HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, - HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, - HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, - HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, - HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, - HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, - HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, - HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, - HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, - HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, - HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, - HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, - HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, - HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, HDMI_AUDIO_I2S_SD0_EN = 1, @@ -441,20 +378,6 @@ struct hdmi_core_video_config { enum hdmi_core_tclkselclkmult tclk_sel_clkmult; };
-/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ -struct hdmi_core_infoframe_audio { - u8 db1_coding_type; - u8 db1_channel_count; - u8 db2_sample_freq; - u8 db2_sample_size; - u8 db4_channel_alloc; - bool db5_downmix_inh; - u8 db5_lsv; /* Level shift values for downmix */ -}; - struct hdmi_core_packet_enable_repeat { u32 audio_pkt; u32 audio_pkt_repeat; @@ -507,7 +430,7 @@ struct hdmi_core_audio_i2s_config {
struct hdmi_core_audio_config { struct hdmi_core_audio_i2s_config i2s_cfg; - enum hdmi_core_audio_sample_freq freq_sample; + u32 freq_sample; bool fs_override; u32 n; u32 cts; @@ -527,7 +450,7 @@ struct hdmi_core_audio_config { int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, u32 sample_freq, u32 *n, u32 *cts); void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud); + struct snd_cea_861_aud_if *info_aud); void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg); void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
According to the most up-to-date documentation from Texas Instruments, the configuration of High Bitrate Audio is not possible. Also, it is not possible to set polarity of the I2S Word Select signal. This patch removes the invalid settings.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 3 --- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 5 ----- 2 files changed, 0 insertions(+), 8 deletions(-)
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index ac73309..985caf9 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1121,11 +1121,8 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, cfg->freq_sample, 3, 0);
r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); - r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); - r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); - r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 26b91ff..40b724c 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -342,8 +342,6 @@ enum hdmi_audio_blk_strt_end_sig { };
enum hdmi_audio_i2s_config { - HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, - HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, @@ -418,11 +416,8 @@ struct hdmi_core_audio_i2s_config { u8 word_length; u8 in_length_bits; u8 justification; - u8 en_high_bitrate_aud; u8 sck_edge_mode; - u8 cbit_order; u8 vbit; - u8 ws_polarity; u8 direction; u8 shift; u8 active_sds;
From: Axel Castaneda Gonzalez x0055901@ti.com
Decouple the enable/disable operation of the HDMI audio wrapper from audio start/stop. Otherwise, an audio FIFO underflow may occur. The audio wrapper enablement must be done after configuration and before audio playback is started.
Signed-off-by: Axel Castaneda Gonzalez x0055901@ti.com Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/dss_features.c | 2 ++ drivers/video/omap2/dss/ti_hdmi.h | 6 ++++++ drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index c677095..8005128 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -571,6 +571,8 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, .audio_disable = ti_hdmi_4xxx_wp_audio_disable, + .audio_start = ti_hdmi_4xxx_audio_start, + .audio_stop = ti_hdmi_4xxx_audio_stop, #endif
}; diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 4c84a9c..8afdd0b 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -113,6 +113,10 @@ struct ti_hdmi_ip_ops { int (*audio_enable)(struct hdmi_ip_data *ip_data);
void (*audio_disable)(struct hdmi_ip_data *ip_data); + + int (*audio_start)(struct hdmi_ip_data *ip_data); + + void (*audio_stop)(struct hdmi_ip_data *ip_data); #endif
}; @@ -190,5 +194,7 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 985caf9..12c21d3 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1256,22 +1256,31 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) { - REG_FLD_MOD(hdmi_av_base(ip_data), - HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 31, 31); +} + +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, true, 30, 30); return 0; }
-void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_av_base(ip_data), HDMI_CORE_AV_AUD_MODE, false, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, false, 31, 31); - REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, false, 30, 30); } #endif
Instead of having OMAPDSS HDMI audio functionality depending on the ASoC HDMI audio driver, use a new config option so that potential users, including ASoC, may select if needed.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/Kconfig | 4 ++++ drivers/video/omap2/dss/dss_features.c | 3 +-- drivers/video/omap2/dss/ti_hdmi.h | 6 ++---- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 6 ++---- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 3 +-- 5 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 7be7c06..b492001 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -68,6 +68,10 @@ config OMAP4_DSS_HDMI HDMI Interface. This adds the High Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI specification.
+config OMAP4_DSS_HDMI_AUDIO + bool + depends on OMAP4_DSS_HDMI + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 8005128..508cc4a 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -567,8 +567,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .dump_core = ti_hdmi_4xxx_core_dump, .dump_pll = ti_hdmi_4xxx_pll_dump, .dump_phy = ti_hdmi_4xxx_phy_dump, -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, .audio_disable = ti_hdmi_4xxx_wp_audio_disable, .audio_start = ti_hdmi_4xxx_audio_start, diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 8afdd0b..6aedb89 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -108,8 +108,7 @@ struct ti_hdmi_ip_ops {
void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int (*audio_enable)(struct hdmi_ip_data *ip_data);
void (*audio_disable)(struct hdmi_ip_data *ip_data); @@ -190,8 +189,7 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 12c21d3..cbda730 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -29,8 +29,7 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <linux/gpio.h> -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) #include <sound/asound.h> #endif
@@ -1026,8 +1025,7 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); }
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, struct hdmi_audio_format *aud_fmt) { diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 40b724c..5d6e37a 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -440,8 +440,7 @@ struct hdmi_core_audio_config { bool en_spdif; };
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, u32 sample_freq, u32 *n, u32 *cts); void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data,
Utilize a snd_aes_iec958 struct to write the parameters of the IEC-60958 channel status word into the HDMI IP registers. Hence, the user of the driver has full control of what parameters are written in the word.
Also, some of the parameters of the I2S structure have been removed as they are actually IEC-60958 parameters.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 29 +++++++++++++++++++---------- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 4 +--- 2 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index cbda730..9c3dfd4 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1114,10 +1114,25 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1);
- /* I2S parameters */ - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4, - cfg->freq_sample, 3, 0); - + /* + * Set IEC-60958-3 channel status word. It is passed to the IP + * just as it is received. The user of the driver is responsible + * for its contents. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0, + cfg->iec60958_cfg->status[0]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1, + cfg->iec60958_cfg->status[1]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2, + cfg->iec60958_cfg->status[2]); + /* yes, this is correct: status[3] goes to CHST4 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4, + cfg->iec60958_cfg->status[3]); + /* yes, this is correct: status[4] goes to CHST5 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, + cfg->iec60958_cfg->status[4]); + + /* set I2S parameters */ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); @@ -1126,12 +1141,6 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r);
- r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5); - r = FLD_MOD(r, cfg->freq_sample, 7, 4); - r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); - r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r); - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0);
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 5d6e37a..4fd4f35 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -412,8 +412,6 @@ struct hdmi_audio_dma { };
struct hdmi_core_audio_i2s_config { - u8 word_max_length; - u8 word_length; u8 in_length_bits; u8 justification; u8 sck_edge_mode; @@ -425,7 +423,7 @@ struct hdmi_core_audio_i2s_config {
struct hdmi_core_audio_config { struct hdmi_core_audio_i2s_config i2s_cfg; - u32 freq_sample; + struct snd_aes_iec958 *iec60958_cfg; bool fs_override; u32 n; u32 cts;
The N and CTS parameters are relevant to all HDMI implementations and not specific to a given IP. Hence, the calculation is relocated into the generic HDMI driver.
Also, deep color is not queried but it is still considered in the calculation of N. This is to be changed when deep color functionality is implemented in the driver.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/hdmi.c | 42 +++++++++++++++++++++ drivers/video/omap2/dss/ti_hdmi.h | 1 + drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 57 ----------------------------- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 2 - 4 files changed, 43 insertions(+), 59 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 11d6ca8..2a810b7 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -573,6 +573,48 @@ static void hdmi_put_clocks(void) clk_put(hdmi.sys_clk); }
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 deep_color; + u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock; + + if (n == NULL || cts == NULL) + return -EINVAL; + + /* TODO: When implemented, query deep color mode here. */ + deep_color = 100; + + switch (sample_freq) { + case 32000: + if ((deep_color == 125) && ((pclk == 54054) || + (pclk == 74250))) + *n = 8192; + else + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + if ((deep_color == 125) && ((pclk == 54054) || + (pclk == 74250))) + *n = 8192; + else + *n = 6144; + break; + default: + *n = 0; + return -EINVAL; + } + + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + + return 0; +} +#endif + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 6aedb89..852a803 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -190,6 +190,7 @@ void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts); int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 9c3dfd4..5612534 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1204,63 +1204,6 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, */ }
-int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts) -{ - u32 r; - u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.pixel_clock; - - if (n == NULL || cts == NULL) - return -EINVAL; - /* - * Obtain current deep color configuration. This needed - * to calculate the TMDS clock based on the pixel clock. - */ - r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0); - switch (r) { - case 1: /* No deep color selected */ - deep_color = 100; - break; - case 2: /* 10-bit deep color selected */ - deep_color = 125; - break; - case 3: /* 12-bit deep color selected */ - deep_color = 150; - break; - default: - return -EINVAL; - } - - switch (sample_freq) { - case 32000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 4096; - break; - case 44100: - *n = 6272; - break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 6144; - break; - default: - *n = 0; - return -EINVAL; - } - - /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ - *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); - - return 0; -} - int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_wp_base(ip_data), diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 4fd4f35..7199c19 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -439,8 +439,6 @@ struct hdmi_core_audio_config { };
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts); void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, struct snd_cea_861_aud_if *info_aud); void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
Add support for more sample rates when calculating N and CTS. This covers all the audio sample rates that an HDMI source is allowed to transmit according to the HDMI 1.4a specification.
Also, reorganize the logic for the calculation when using deep color.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/hdmi.c | 88 +++++++++++++++++++++++++++++++++------ 1 files changed, 74 insertions(+), 14 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 2a810b7..aee0acf 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -577,6 +577,7 @@ static void hdmi_put_clocks(void) int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) { u32 deep_color; + bool deep_color_correct = false; u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock;
if (n == NULL || cts == NULL) @@ -585,29 +586,88 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) /* TODO: When implemented, query deep color mode here. */ deep_color = 100;
+ /* + * When using deep color, the default N value (as in the HDMI + * specification) yields to an non-integer CTS. Hence, we + * modify it while keeping the restrictions described in + * section 7.2.1 of the HDMI 1.4a specification. + */ switch (sample_freq) { case 32000: - if ((deep_color == 125) && ((pclk == 54054) || - (pclk == 74250))) - *n = 8192; - else - *n = 4096; + case 48000: + case 96000: + case 192000: + if (deep_color == 125) + if (pclk == 27027 || pclk == 74250) + deep_color_correct = true; + if (deep_color == 150) + if (pclk == 27027) + deep_color_correct = true; break; case 44100: - *n = 6272; - break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) || - (pclk == 74250))) - *n = 8192; - else - *n = 6144; + case 88200: + case 176400: + if (deep_color == 125) + if (pclk == 27027) + deep_color_correct = true; break; default: - *n = 0; return -EINVAL; }
+ if (deep_color_correct) { + switch (sample_freq) { + case 32000: + *n = 8192; + break; + case 44100: + *n = 12544; + break; + case 48000: + *n = 8192; + break; + case 88200: + *n = 25088; + break; + case 96000: + *n = 16384; + break; + case 176400: + *n = 50176; + break; + case 192000: + *n = 32768; + break; + default: + return -EINVAL; + } + } else { + switch (sample_freq) { + case 32000: + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + *n = 6144; + break; + case 88200: + *n = 12544; + break; + case 96000: + *n = 12288; + break; + case 176400: + *n = 25088; + break; + case 192000: + *n = 24576; + break; + default: + return -EINVAL; + } + } /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
The generic HDMI driver does not need to know about the specific settings of a given IP. Hence, it just passes the audio configuration and the IP library parses such configuration and sets the IP accordingly. This patch introduces an IP-specific audio configuration function.
Also, this patch implements the audio config function for OMAP4. The DMA, format and core config functions are no longer exposed to the generic HDMI driver as they are IP-specific.
The audio configuration function caters for 16-bit through 24-bit audio samples with sample rates from 32kHz and up to 192kHz as well as up to 8 audio channels.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/dss_features.c | 1 + drivers/video/omap2/dss/ti_hdmi.h | 5 + drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 189 ++++++++++++++++++++++++++++- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 10 -- 4 files changed, 191 insertions(+), 14 deletions(-)
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 508cc4a..bff51a0 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -572,6 +572,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .audio_disable = ti_hdmi_4xxx_wp_audio_disable, .audio_start = ti_hdmi_4xxx_audio_start, .audio_stop = ti_hdmi_4xxx_audio_stop, + .audio_config = ti_hdmi_4xxx_audio_config, #endif
}; diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 852a803..e734cb4 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -116,6 +116,9 @@ struct ti_hdmi_ip_ops { int (*audio_start)(struct hdmi_ip_data *ip_data);
void (*audio_stop)(struct hdmi_ip_data *ip_data); + + int (*audio_config)(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif
}; @@ -195,5 +198,7 @@ int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 5612534..74d9420 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -31,10 +31,12 @@ #include <linux/gpio.h> #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) #include <sound/asound.h> +#include <sound/asoundef.h> #endif
#include "ti_hdmi_4xxx_ip.h" #include "dss.h" +#include "dss_features.h"
static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) @@ -1026,7 +1028,7 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) }
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data, struct hdmi_audio_format *aud_fmt) { u32 r; @@ -1045,7 +1047,7 @@ void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r); }
-void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data, struct hdmi_audio_dma *aud_dma) { u32 r; @@ -1063,7 +1065,7 @@ void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r); }
-void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg) { u32 r; @@ -1154,7 +1156,7 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); }
-void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data, struct snd_cea_861_aud_if *info_aud) { u8 sum = 0, checksum = 0; @@ -1204,6 +1206,185 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, */ }
+int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio) +{ + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core; + int err, n, cts, channel_count; + unsigned int fs_nr; + bool word_length_16b = false; + + if (!audio || !audio->iec || !audio->cea || !ip_data) + return -EINVAL; + + core.iec60958_cfg = audio->iec; + /* + * In the IEC-60958 status word, check if the audio sample word length + * is 16-bit as several optimizations can be performed in such case. + */ + if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)) + if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16) + word_length_16b = true; + + /* I2S configuration. See Phillips' specification */ + if (word_length_16b) + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + else + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + /* + * The I2S input word length is twice the lenght given in the IEC-60958 + * status word. If the word size is greater than + * 20 bits, increment by one. + */ + core.i2s_cfg.in_length_bits = audio->iec->status[4] + & IEC958_AES4_CON_WORDLEN; + if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) + core.i2s_cfg.in_length_bits++; + core.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + core.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + core.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + + /* convert sample frequency to a number */ + switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { + case IEC958_AES3_CON_FS_32000: + fs_nr = 32000; + break; + case IEC958_AES3_CON_FS_44100: + fs_nr = 44100; + break; + case IEC958_AES3_CON_FS_48000: + fs_nr = 48000; + break; + case IEC958_AES3_CON_FS_88200: + fs_nr = 88200; + break; + case IEC958_AES3_CON_FS_96000: + fs_nr = 96000; + break; + case IEC958_AES3_CON_FS_176400: + fs_nr = 176400; + break; + case IEC958_AES3_CON_FS_192000: + fs_nr = 192000; + break; + default: + return -EINVAL; + } + + err = hdmi_compute_acr(fs_nr, &n, &cts); + + /* Audio clock regeneration settings */ + core.n = n; + core.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core.aud_par_busclk = 0; + core.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); + } else { + core.aud_par_busclk = (((128 * 31) - 1) << 8); + core.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core.use_mclk = true; + } + + if (core.use_mclk) + core.mclk_mode = HDMI_AUDIO_MCLK_128FS; + + /* Audio channels settings */ + channel_count = (audio->cea->db1_ct_cc & + CEA861_AUDIO_INFOFRAME_DB1CC) + 1; + + switch (channel_count) { + case 2: + audio_format.active_chnnls_msk = 0x03; + break; + case 3: + audio_format.active_chnnls_msk = 0x07; + break; + case 4: + audio_format.active_chnnls_msk = 0x0f; + break; + case 5: + audio_format.active_chnnls_msk = 0x1f; + break; + case 6: + audio_format.active_chnnls_msk = 0x3f; + break; + case 7: + audio_format.active_chnnls_msk = 0x7f; + break; + case 8: + audio_format.active_chnnls_msk = 0xff; + break; + default: + return -EINVAL; + } + + /* + * the HDMI IP needs to enable four stereo channels when transmitting + * more than 2 audio channels + */ + if (channel_count == 2) { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + core.layout = HDMI_AUDIO_LAYOUT_2CH; + } else { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN | + HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN | + HDMI_AUDIO_I2S_SD3_EN; + core.layout = HDMI_AUDIO_LAYOUT_8CH; + } + + core.en_spdif = false; + /* use sample frequency from channel status word */ + core.fs_override = true; + /* enable ACR packets */ + core.en_acr_pkt = true; + /* disable direct streaming digital audio */ + core.en_dsd_audio = false; + /* use parallel audio interface */ + core.en_parallel_aud_input = true; + + /* DMA settings */ + if (word_length_16b) + audio_dma.transfer_size = 0x10; + else + audio_dma.transfer_size = 0x20; + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + /* audio FIFO format settings */ + if (word_length_16b) { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + } else { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + } + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; + + /* configure DMA and audio FIFO format*/ + ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma); + ti_hdmi_4xxx_wp_audio_config_format(ip_data, &audio_format); + + /* configure the core*/ + ti_hdmi_4xxx_core_audio_config(ip_data, &core); + + /* configure CEA 861 audio infoframe*/ + ti_hdmi_4xxx_core_audio_infoframe_cfg(ip_data, audio->cea); + + return 0; +} + int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_wp_base(ip_data), diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 7199c19..609d086 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -438,14 +438,4 @@ struct hdmi_core_audio_config { bool en_spdif; };
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct snd_cea_861_aud_if *info_aud); -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_audio_config *cfg); -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, - struct hdmi_audio_dma *aud_dma); -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, - struct hdmi_audio_format *aud_fmt); -#endif #endif
As of today, the only know user of the DSS HDMI audio support is ASoC. Hence, it makes sense to remap the speaker order to match the ALSA speaker order. In the future, a dynamic mapping mechanism may be implemented.
Remapping is needed as the HDMI speaker order is FL/FR/LFE/C/RL/RR/ RLC-FLC/RRC-FLC while the ALSA order is FL/FR/RL/RR/C/LFE/SL/SR. Refer to CEA-861 Section 6.6.2 for further details.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 74d9420..4dcdbbb 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -1154,6 +1154,14 @@ static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); r = FLD_MOD(r, cfg->en_spdif, 1, 1); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); + + /* Audio channel mappings */ + /* TODO: Make channel mapping dynamic. For now, map channels + * in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as + * HDMI speaker order is different. See CEA-861 Section 6.6.2. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78); + REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5); }
static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data,
As the hdmi_lock mutex is inside the hdmi struct, rename to simply "lock". This is only a change in the name. There are not changes in functionality.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/hdmi_panel.c | 41 +++++++++++++++++---------------- 1 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 533d5dc..5e215d6 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -30,7 +30,8 @@ #include "dss.h"
static struct { - struct mutex hdmi_lock; + /* This protects the panel ops, mainly when accessing the HDMI IP. */ + struct mutex lock; } hdmi;
@@ -59,7 +60,7 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) int r = 0; DSSDBG("ENTER hdmi_panel_enable\n");
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; @@ -75,28 +76,28 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock);
return r; }
static void hdmi_panel_disable(struct omap_dss_device *dssdev) { - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) omapdss_hdmi_display_disable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
- mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); }
static int hdmi_panel_suspend(struct omap_dss_device *dssdev) { int r = 0;
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = -EINVAL; @@ -108,7 +109,7 @@ static int hdmi_panel_suspend(struct omap_dss_device *dssdev) omapdss_hdmi_display_disable(dssdev);
err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock);
return r; } @@ -117,7 +118,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) { int r = 0;
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { r = -EINVAL; @@ -133,7 +134,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock);
return r; } @@ -141,11 +142,11 @@ err: static void hdmi_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
*timings = dssdev->panel.timings;
- mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); }
static void hdmi_set_timings(struct omap_dss_device *dssdev, @@ -153,12 +154,12 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, { DSSDBG("hdmi_set_timings\n");
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
dssdev->panel.timings = *timings; omapdss_hdmi_display_set_timing(dssdev);
- mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); }
static int hdmi_check_timings(struct omap_dss_device *dssdev, @@ -168,11 +169,11 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev,
DSSDBG("hdmi_check_timings\n");
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
r = omapdss_hdmi_display_check_timing(dssdev, timings);
- mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; }
@@ -180,7 +181,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) { int r;
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -194,7 +195,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock);
return r; } @@ -203,7 +204,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) { int r;
- mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -217,7 +218,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock);
return r; } @@ -242,7 +243,7 @@ static struct omap_dss_driver hdmi_driver = {
int hdmi_panel_init(void) { - mutex_init(&hdmi.hdmi_lock); + mutex_init(&hdmi.lock);
omap_dss_register_driver(&hdmi_driver);
Implement the DSS device driver audio support interface in the HDMI panel driver and generic driver. The implementation relies on the IP-specific functions that are defined at DSS probe time.
At the moment, a hardirq-safe spinlock is used to protect the audio functions. This is because such functions might be called while holding a lock (this especially true for audio_start/stop). For the rest of the audio functions, a mutex could be used in the future as the enablement of resources might take too much time.
Signed-off-by: Ricardo Neri ricardo.neri@ti.com --- drivers/video/omap2/dss/dss.h | 8 +++ drivers/video/omap2/dss/hdmi.c | 42 ++++++++++++++ drivers/video/omap2/dss/hdmi_panel.c | 100 ++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 0 deletions(-)
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index d4b3dff..ec4aa14 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -514,6 +514,14 @@ int omapdss_hdmi_read_edid(u8 *buf, int len); bool omapdss_hdmi_detect(void); int hdmi_panel_init(void); void hdmi_panel_exit(void); +#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO +int hdmi_audio_enable(void); +void hdmi_audio_disable(void); +int hdmi_audio_start(void); +void hdmi_audio_stop(void); +bool hdmi_mode_has_audio(void); +int hdmi_audio_config(struct omap_dss_audio *audio); +#endif
/* RFBI */ #ifdef CONFIG_OMAP2_DSS_RFBI diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index aee0acf..388301e 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -673,6 +673,48 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
return 0; } + +int hdmi_audio_enable(void) +{ + DSSDBG("audio_enable\n"); + + return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); +} + +void hdmi_audio_disable(void) +{ + DSSDBG("audio_disable\n"); + + hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); +} + +int hdmi_audio_start(void) +{ + DSSDBG("audio_start\n"); + + return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); +} + +void hdmi_audio_stop(void) +{ + DSSDBG("audio_stop\n"); + + hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); +} + +bool hdmi_mode_has_audio(void) +{ + if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) + return true; + else + return false; +} + +int hdmi_audio_config(struct omap_dss_audio *audio) +{ + return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); +} + #endif
/* HDMI HW IP initialisation */ diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 5e215d6..8c17daa 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -32,6 +32,10 @@ static struct { /* This protects the panel ops, mainly when accessing the HDMI IP. */ struct mutex lock; +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + /* This protects the audio ops, specifically. */ + spinlock_t audio_lock; +#endif } hdmi;
@@ -223,6 +227,90 @@ err: return r; }
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + r = hdmi_audio_enable(); + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_disable(); + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + r = hdmi_audio_start(); + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_stop(); + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + unsigned long flags; + bool r = false; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto err; + + if (!hdmi_mode_has_audio()) + goto err; + + r = true; +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + r = hdmi_audio_config(audio); + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +#endif + static struct omap_dss_driver hdmi_driver = { .probe = hdmi_panel_probe, .remove = hdmi_panel_remove, @@ -235,6 +323,14 @@ static struct omap_dss_driver hdmi_driver = { .check_timings = hdmi_check_timings, .read_edid = hdmi_read_edid, .detect = hdmi_detect, +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + .audio_enable = hdmi_panel_audio_enable, + .audio_disable = hdmi_panel_audio_disable, + .audio_start = hdmi_panel_audio_start, + .audio_stop = hdmi_panel_audio_stop, + .audio_supported = hdmi_panel_audio_supported, + .audio_config = hdmi_panel_audio_config, +#endif .driver = { .name = "hdmi_panel", .owner = THIS_MODULE, @@ -245,6 +341,10 @@ int hdmi_panel_init(void) { mutex_init(&hdmi.lock);
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + spin_lock_init(&hdmi.audio_lock); +#endif + omap_dss_register_driver(&hdmi_driver);
return 0;
Hi,
On Thu, 2012-05-03 at 20:44 -0500, Ricardo Neri wrote:
Implement the DSS device driver audio support interface in the HDMI panel driver and generic driver. The implementation relies on the IP-specific functions that are defined at DSS probe time.
At the moment, a hardirq-safe spinlock is used to protect the audio functions. This is because such functions might be called while holding a lock (this especially true for audio_start/stop). For the rest of the audio functions, a mutex could be used in the future as the enablement of resources might take too much time.
The series looks good, except locking. Granted, the locking in omapdss is a bit bad generally also, but here I think it's a bit more broken.
For example, hdmi_panel.c:hdmi_panel_audio_supported() takes the audio lock, and then uses variables like dssdev->state, and the hdmi video mode. However, the video functions do not use audio lock, so effectively the lock doesn't protect at all.
I'm not sure how to fix it, though. I think this shows the shortcomings of the current locking strategy (or lack of =). What if the audio functions that can sleep would take the hdmi panel's mutex, and also the audio spinlock? That would at least fix some of the cases.
Tomi
Hi Tomi,
Thanks for taking the time to comment.
On 05/07/2012 06:43 AM, Tomi Valkeinen wrote:
Hi,
On Thu, 2012-05-03 at 20:44 -0500, Ricardo Neri wrote:
Implement the DSS device driver audio support interface in the HDMI panel driver and generic driver. The implementation relies on the IP-specific functions that are defined at DSS probe time.
At the moment, a hardirq-safe spinlock is used to protect the audio functions. This is because such functions might be called while holding a lock (this especially true for audio_start/stop). For the rest of the audio functions, a mutex could be used in the future as the enablement of resources might take too much time.
The series looks good, except locking. Granted, the locking in omapdss is a bit bad generally also, but here I think it's a bit more broken.
For example, hdmi_panel.c:hdmi_panel_audio_supported() takes the audio lock, and then uses variables like dssdev->state, and the hdmi video mode. However, the video functions do not use audio lock, so effectively the lock doesn't protect at all.
Yes, it does not protect.
I'm not sure how to fix it, though. I think this shows the shortcomings of the current locking strategy (or lack of =). What if the audio functions that can sleep would take the hdmi panel's mutex, and also the audio spinlock? That would at least fix some of the cases.
But if the function can sleep, protecting it with the HDMI panel's mutex should be enough, shouldn't it? Wouldn't it be pointless to also hold the spinlock?
Ideally, I think, only one lock, the HDMI panel's mutex, should be enough to protect the HDMI panel's functions, including the audio functions. Reusing the HDMI panel's mutex for the audio functions would prevent the situation you describe regarding hdmi_panel.c:hdmi_panel_audio_supported()... and the spinlock would not be required.
The only functions that cause problems with this approach are audio_start/stop as ALSA calls them while holding a spinlock. The spinlock could be used for these as they dont read or write the panel's variables.
However, holding a spinlock only when start/stopping audio would fail, for instance, if someone starts/stops audio while enabling or configuring audio; but that would be an issue in the design of the component using DSS HDMI audio, wouldn't be? To prevent that, an audio_configured state variable in the HDMI panel could be used to monitor that audio is started only after it is enabled and configured. In that case, both the spinlock and the mutex would be required, as you propose, because audio_configured would be read inside audio_start/stop.
BR,
Ricardo
Tomi
On Tue, 2012-05-08 at 18:55 -0500, Ricardo Neri wrote:
Hi Tomi,
Thanks for taking the time to comment.
On 05/07/2012 06:43 AM, Tomi Valkeinen wrote:
Hi,
On Thu, 2012-05-03 at 20:44 -0500, Ricardo Neri wrote:
Implement the DSS device driver audio support interface in the HDMI panel driver and generic driver. The implementation relies on the IP-specific functions that are defined at DSS probe time.
At the moment, a hardirq-safe spinlock is used to protect the audio functions. This is because such functions might be called while holding a lock (this especially true for audio_start/stop). For the rest of the audio functions, a mutex could be used in the future as the enablement of resources might take too much time.
The series looks good, except locking. Granted, the locking in omapdss is a bit bad generally also, but here I think it's a bit more broken.
For example, hdmi_panel.c:hdmi_panel_audio_supported() takes the audio lock, and then uses variables like dssdev->state, and the hdmi video mode. However, the video functions do not use audio lock, so effectively the lock doesn't protect at all.
Yes, it does not protect.
I'm not sure how to fix it, though. I think this shows the shortcomings of the current locking strategy (or lack of =). What if the audio
Btw, I meant shortcomings in the general DSS locking strategy, not the locking in this particular patch.
functions that can sleep would take the hdmi panel's mutex, and also the audio spinlock? That would at least fix some of the cases.
But if the function can sleep, protecting it with the HDMI panel's mutex should be enough, shouldn't it? Wouldn't it be pointless to also hold the spinlock?
If the start/stop functions use the spinlock, but not the mutex, then the sleeping functions should also use the spinlock to prevent touching the same data at the same time.
Ideally, I think, only one lock, the HDMI panel's mutex, should be enough to protect the HDMI panel's functions, including the audio functions. Reusing the HDMI panel's mutex for the audio functions would prevent the situation you describe regarding hdmi_panel.c:hdmi_panel_audio_supported()... and the spinlock would not be required.
The only functions that cause problems with this approach are audio_start/stop as ALSA calls them while holding a spinlock. The spinlock could be used for these as they dont read or write the panel's variables.
Locks always protect a particular piece of data. What is the data in this case, if not panel's variables? DSS registers?
However, holding a spinlock only when start/stopping audio would fail, for instance, if someone starts/stops audio while enabling or configuring audio; but that would be an issue in the design of the component using DSS HDMI audio, wouldn't be? To prevent that, an
I guess it's up to us to decide what is the supposed use-patter of the functions =). For dss functions in general it's pretty vague and non-defined. But we could start here with audio functions and define that the audio functions may not be called from multiple threads at the same time. That would remove any issues with concurrent calls to audio functions, presuming the audio side actually conforms to this =).
But the video and audio paths are probably always separate, and for those we need protection. As you said, using the mutex for the may-sleep audio functions solves the issue for those, leaving start/stop as the only problem case.
However, even if we could protect start/stop with locks, we still have a problem (which is general problem related to dss locking): we don't have any protection between the function calls. So basically this could happen:
[video thread] setup video & enable [audio thread] check that all is ok, and configure audio [video thread] change video config or disable video [audio thread] start_audio -> fails, because video config no longer valid for audio
But I guess we have to accept that the locking is not perfect, and try to solve it properly later, as it's a bigger, dss-wide change.
Tomi
On 05/09/2012 03:28 AM, Tomi Valkeinen wrote:
On Tue, 2012-05-08 at 18:55 -0500, Ricardo Neri wrote:
Hi Tomi,
Thanks for taking the time to comment.
On 05/07/2012 06:43 AM, Tomi Valkeinen wrote:
Hi,
On Thu, 2012-05-03 at 20:44 -0500, Ricardo Neri wrote:
Implement the DSS device driver audio support interface in the HDMI panel driver and generic driver. The implementation relies on the IP-specific functions that are defined at DSS probe time.
At the moment, a hardirq-safe spinlock is used to protect the audio functions. This is because such functions might be called while holding a lock (this especially true for audio_start/stop). For the rest of the audio functions, a mutex could be used in the future as the enablement of resources might take too much time.
The series looks good, except locking. Granted, the locking in omapdss is a bit bad generally also, but here I think it's a bit more broken.
For example, hdmi_panel.c:hdmi_panel_audio_supported() takes the audio lock, and then uses variables like dssdev->state, and the hdmi video mode. However, the video functions do not use audio lock, so effectively the lock doesn't protect at all.
Yes, it does not protect.
I'm not sure how to fix it, though. I think this shows the shortcomings of the current locking strategy (or lack of =). What if the audio
Btw, I meant shortcomings in the general DSS locking strategy, not the locking in this particular patch.
functions that can sleep would take the hdmi panel's mutex, and also the audio spinlock? That would at least fix some of the cases.
But if the function can sleep, protecting it with the HDMI panel's mutex should be enough, shouldn't it? Wouldn't it be pointless to also hold the spinlock?
If the start/stop functions use the spinlock, but not the mutex, then the sleeping functions should also use the spinlock to prevent touching the same data at the same time.
Ideally, I think, only one lock, the HDMI panel's mutex, should be enough to protect the HDMI panel's functions, including the audio functions. Reusing the HDMI panel's mutex for the audio functions would prevent the situation you describe regarding hdmi_panel.c:hdmi_panel_audio_supported()... and the spinlock would not be required.
The only functions that cause problems with this approach are audio_start/stop as ALSA calls them while holding a spinlock. The spinlock could be used for these as they dont read or write the panel's variables.
Locks always protect a particular piece of data. What is the data in this case, if not panel's variables? DSS registers?
Yes, in this case, it protects access to HDMI IP registers.
I just implemented an improved locking strategy. I included a new variable to monitor the audio state, this will be protected by the audio lock.
This is the new implementation: http://www.mail-archive.com/linux-omap@vger.kernel.org/msg68409.html http://www.mail-archive.com/linux-omap@vger.kernel.org/msg68411.html
However, holding a spinlock only when start/stopping audio would fail, for instance, if someone starts/stops audio while enabling or configuring audio; but that would be an issue in the design of the component using DSS HDMI audio, wouldn't be? To prevent that, an
I guess it's up to us to decide what is the supposed use-patter of the functions =). For dss functions in general it's pretty vague and non-defined. But we could start here with audio functions and define that the audio functions may not be called from multiple threads at the same time. That would remove any issues with concurrent calls to audio functions, presuming the audio side actually conforms to this =).
Under the new strategy, in addition to not allowing the audio functions to be called from multiple threads, audio functions will fail if the sequence _CONFIGURED -> _ENABLED -> PLAYING -> DISABLED is not followed. This is aligned with the behavior that ALSA follows for the audio codecs. Also, it checks the state of the panel to allow the audio transitions.
But the video and audio paths are probably always separate, and for those we need protection. As you said, using the mutex for the may-sleep audio functions solves the issue for those, leaving start/stop as the only problem case.
Audio only needs to know if the display is active. Under the improved strategy, audio_start indirectly checks the state of the panel because the audio needs to be in AUDIO_ENABLED state to start and this state is reached only if the panel is active. The mutex is held to check the state of the panel and the audio lock is held to change the audio state. Also, the audio transitions to AUDIO_DISABLED if the panel is disabled. Therefore, I think, this should ensure that the panel is active when starting audio.
However, even if we could protect start/stop with locks, we still have a problem (which is general problem related to dss locking): we don't have any protection between the function calls. So basically this could happen:
[video thread] setup video& enable [audio thread] check that all is ok, and configure audio [video thread] change video config or disable video [audio thread] start_audio -> fails, because video config no longer valid for audio
Under the improved locking strategy, at least it does not fail silently. The video thread will disable audio (while holding the audio lock) and, when the audio thread gains the lock, audio_start will fail because it is in an invalid state. The error is reported to the audio user where it can be handled properly(e.g., it can reconfigure and try again).
But I guess we have to accept that the locking is not perfect, and try to solve it properly later, as it's a bigger, dss-wide change.
Another piece that is missing is the notify mechanism to let know the audio user that the panel was disabled or the video configuration changed.
Tomi
On Wed, 2012-05-09 at 23:12 -0500, Ricardo Neri wrote:
Under the new strategy, in addition to not allowing the audio functions to be called from multiple threads, audio functions will fail if the sequence _CONFIGURED -> _ENABLED -> PLAYING -> DISABLED is not followed. This is aligned with the behavior that ALSA follows for the audio codecs. Also, it checks the state of the panel to allow the audio transitions.
But the video and audio paths are probably always separate, and for those we need protection. As you said, using the mutex for the may-sleep audio functions solves the issue for those, leaving start/stop as the only problem case.
Audio only needs to know if the display is active. Under the improved
Audio also needs to know if the video mode is suitable for audio, right? So not only disabling the video has to stop audio, but also if the video mode changes to a non-supported one.
strategy, audio_start indirectly checks the state of the panel because the audio needs to be in AUDIO_ENABLED state to start and this state is reached only if the panel is active. The mutex is held to check the state of the panel and the audio lock is held to change the audio state. Also, the audio transitions to AUDIO_DISABLED if the panel is disabled.
Hmm, I can't see the code that does that. As far as I see, no video enable/disable/reconfig affects audio in any way. Am I missing a patch? Could you setup a public git branch so it's easier for me to get the whole series, instead of sending individual patches.
Tomi
On 05/10/2012 02:54 AM, Tomi Valkeinen wrote:
On Wed, 2012-05-09 at 23:12 -0500, Ricardo Neri wrote:
Under the new strategy, in addition to not allowing the audio functions to be called from multiple threads, audio functions will fail if the sequence _CONFIGURED -> _ENABLED -> PLAYING -> DISABLED is not followed. This is aligned with the behavior that ALSA follows for the audio codecs. Also, it checks the state of the panel to allow the audio transitions.
But the video and audio paths are probably always separate, and for those we need protection. As you said, using the mutex for the may-sleep audio functions solves the issue for those, leaving start/stop as the only problem case.
Audio only needs to know if the display is active. Under the improved
Audio also needs to know if the video mode is suitable for audio, right? So not only disabling the video has to stop audio, but also if the video mode changes to a non-supported one.
Yes. I overlooked the mode changes. Audio has to be stopped at any mode change or timings change because the audio regeneration clock params need to be recalculated. I think resuming audio, if possible, should be done by the user, which should be notified of the mode change.
strategy, audio_start indirectly checks the state of the panel because the audio needs to be in AUDIO_ENABLED state to start and this state is reached only if the panel is active. The mutex is held to check the state of the panel and the audio lock is held to change the audio state. Also, the audio transitions to AUDIO_DISABLED if the panel is disabled.
Hmm, I can't see the code that does that. As far as I see, no video enable/disable/reconfig affects audio in any way. Am I missing a patch? Could you setup a public git branch so it's easier for me to get the whole series, instead of sending individual patches.
Sorry, some hunks where missing in the patch that I submitted yesterday.
I just pushed a branch containing the whole most up-to-date series here:
git://gitorious.org/omap-audio/linux-audio.git ricardon/topic/dss_audio-for-tomi
This includes the implementation of the DSS audio interface for HDMI covering the improved locking strategy [1][2], plus the missing hunks in my yesterday's patch, plus handling of mode changes you pointed out.
Please let me know if you want me to resubmit the whole patch series so that you can comment if you need to.
BR,
Ricardo
[1] http://www.spinics.net/lists/linux-omap/msg70059.html [2] http://www.spinics.net/lists/linux-omap/msg70057.html
Tomi
On Thu, 2012-05-10 at 20:56 -0500, Ricardo Neri wrote:
Sorry, some hunks where missing in the patch that I submitted yesterday.
I just pushed a branch containing the whole most up-to-date series here:
git://gitorious.org/omap-audio/linux-audio.git ricardon/topic/dss_audio-for-tomi
This includes the implementation of the DSS audio interface for HDMI covering the improved locking strategy [1][2], plus the missing hunks in my yesterday's patch, plus handling of mode changes you pointed out.
Please let me know if you want me to resubmit the whole patch series so that you can comment if you need to.
Thanks, I think this is fine. I had to rebase your branch as you had some extra patches there also. There were also a few conflicts with latest dss stuff, but nothing serious.
I pushed the branch with latest dss and your branch to:
git://gitorious.org/linux-omap-dss2/linux.git test
With a quick test video works ok, but I didn't test audio.
Tomi
On 05/11/2012 07:51 AM, Tomi Valkeinen wrote:
On Thu, 2012-05-10 at 20:56 -0500, Ricardo Neri wrote:
Sorry, some hunks where missing in the patch that I submitted yesterday.
I just pushed a branch containing the whole most up-to-date series here:
git://gitorious.org/omap-audio/linux-audio.git ricardon/topic/dss_audio-for-tomi
This includes the implementation of the DSS audio interface for HDMI covering the improved locking strategy [1][2], plus the missing hunks in my yesterday's patch, plus handling of mode changes you pointed out.
Please let me know if you want me to resubmit the whole patch series so that you can comment if you need to.
Thanks, I think this is fine. I had to rebase your branch as you had some extra patches there also. There were also a few conflicts with latest dss stuff, but nothing serious.
I pushed the branch with latest dss and your branch to:
git://gitorious.org/linux-omap-dss2/linux.git test
With a quick test video works ok, but I didn't test audio.
Thanks for taking the patches! In your branch, the audio part would not build with audio enabled as your branch does not have the ALSA CEA-861 definitions patch [1]. The platform devices for the ASoC HDMI drivers[2] are also missing. I put these patches on top of your test branch and audio works well. [1] and [2] will come from Takashi's and Tony's branches.
BR,
Ricardo
[1]http://www.mail-archive.com/linux-omap@vger.kernel.org/msg66600.html [2]http://www.spinics.net/lists/linux-omap/msg68834.html
Tomi
participants (2)
-
Ricardo Neri
-
Tomi Valkeinen