Loading Documentation/arm/OMAP/DSS +45 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,51 @@ flexible way to enable non-common multi-display configuration. In addition to modelling the hardware overlays, omapdss supports virtual overlays and overlay managers. These can be used when updating a display with CPU or system DMA. omapdss driver support for audio -------------------------------- There exist several display technologies and standards that support audio as well. Hence, it is relevant to update the DSS device driver to provide an audio interface that may be used by an audio driver or any other driver interested in the functionality. The audio_enable function is intended to prepare the relevant IP for playback (e.g., enabling an audio FIFO, taking in/out of reset some IP, enabling companion chips, etc). It is intended to be called before audio_start. The audio_disable function performs the reverse operation and is intended to be called after audio_stop. While a given DSS device driver may support audio, it is possible that for certain configurations audio is not supported (e.g., an HDMI display using a VESA video timing). The audio_supported function is intended to query whether the current configuration of the display supports audio. The audio_config function is intended to configure all the relevant audio parameters of the display. In order to make the function independent of any specific DSS device driver, a struct omap_dss_audio is defined. Its purpose is to contain all the required parameters for audio configuration. At the moment, such structure contains pointers to IEC-60958 channel status word and CEA-861 audio infoframe structures. This should be enough to support HDMI and DisplayPort, as both are based on CEA-861 and IEC-60958. The audio_enable/disable, audio_config and audio_supported functions could be implemented as functions that may sleep. Hence, they should not be called while holding a spinlock or a readlock. The audio_start/audio_stop function is intended to effectively start/stop audio playback after the configuration has taken place. These functions are designed to be used in an atomic context. Hence, audio_start should return quickly and be called only after all the needed resources for audio playback (audio FIFOs, DMA channels, companion chips, etc) have been enabled to begin data transfers. audio_stop is designed to only stop the audio transfers. The resources used for playback are released using audio_disable. The enum omap_dss_audio_state may be used to help the implementations of the interface to keep track of the audio state. The initial state is _DISABLED; then, the state transitions to _CONFIGURED, and then, when it is ready to play audio, to _ENABLED. The state _PLAYING is used when the audio is being rendered. Panel and controller drivers ---------------------------- Loading drivers/video/omap2/dss/Kconfig +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/video/omap2/dss/dss.h +8 −0 Original line number Diff line number Diff line Loading @@ -464,6 +464,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 */ int rfbi_init_platform_driver(void) __init; Loading drivers/video/omap2/dss/dss_features.c +6 −2 Original line number Diff line number Diff line Loading @@ -568,13 +568,17 @@ 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, .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, .audio_stop = ti_hdmi_4xxx_audio_stop, .audio_config = ti_hdmi_4xxx_audio_config, #endif }; Loading drivers/video/omap2/dss/hdmi.c +135 −224 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -324,7 +318,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); Loading Loading @@ -358,7 +352,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) /* tv size */ dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); 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) Loading @@ -367,7 +363,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: Loading @@ -379,7 +376,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(); Loading Loading @@ -536,241 +533,171 @@ 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) static int hdmi_get_clocks(struct platform_device *pdev) { 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; struct clk *clk; clk = clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); return PTR_ERR(clk); } hdmi.sys_clk = clk; return 0; } if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { dev_err(&pdev->dev, "Cannot enable/disable audio\n"); return -ENODEV; static void hdmi_put_clocks(void) { if (hdmi.sys_clk) clk_put(hdmi.sys_clk); } 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, true); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 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) return -EINVAL; /* 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: 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 SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ip_data->ops->audio_enable(ip_data, false); case 44100: case 88200: case 176400: if (deep_color == 125) if (pclk == 27027) deep_color_correct = true; 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; return -EINVAL; } if (deep_color_correct) { switch (sample_freq) { case 32000: *n = 8192; break; case 44100: *n = 12544; 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; 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; } switch (params_rate(params)) { } else { switch (sample_freq) { case 32000: sample_freq = HDMI_AUDIO_FS_32000; *n = 4096; break; case 44100: sample_freq = HDMI_AUDIO_FS_44100; *n = 6272; break; case 48000: sample_freq = HDMI_AUDIO_FS_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); 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) int hdmi_audio_enable(void) { if (!hdmi.ip_data.cfg.cm.mode) { pr_err("Current video settings do not support audio.\n"); return -EIO; } return 0; DSSDBG("audio_enable\n"); return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); } static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) void hdmi_audio_disable(void) { struct hdmi_ip_data *priv = &hdmi.ip_data; DSSDBG("audio_disable\n"); snd_soc_codec_set_drvdata(codec, priv); return 0; hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); } 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, }; int hdmi_audio_start(void) { DSSDBG("audio_start\n"); 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 return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); } static int hdmi_get_clocks(struct platform_device *pdev) void hdmi_audio_stop(void) { struct clk *clk; DSSDBG("audio_stop\n"); clk = clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); return PTR_ERR(clk); hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); } hdmi.sys_clk = clk; return 0; bool hdmi_mode_has_audio(void) { if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) return true; else return false; } static void hdmi_put_clocks(void) int hdmi_audio_config(struct omap_dss_audio *audio) { if (hdmi.sys_clk) clk_put(hdmi.sys_clk); return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); } #endif static void __init hdmi_probe_pdata(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; Loading Loading @@ -838,17 +765,6 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_probe_pdata(pdev); #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; } Loading @@ -858,11 +774,6 @@ static int __exit 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(); Loading Loading
Documentation/arm/OMAP/DSS +45 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,51 @@ flexible way to enable non-common multi-display configuration. In addition to modelling the hardware overlays, omapdss supports virtual overlays and overlay managers. These can be used when updating a display with CPU or system DMA. omapdss driver support for audio -------------------------------- There exist several display technologies and standards that support audio as well. Hence, it is relevant to update the DSS device driver to provide an audio interface that may be used by an audio driver or any other driver interested in the functionality. The audio_enable function is intended to prepare the relevant IP for playback (e.g., enabling an audio FIFO, taking in/out of reset some IP, enabling companion chips, etc). It is intended to be called before audio_start. The audio_disable function performs the reverse operation and is intended to be called after audio_stop. While a given DSS device driver may support audio, it is possible that for certain configurations audio is not supported (e.g., an HDMI display using a VESA video timing). The audio_supported function is intended to query whether the current configuration of the display supports audio. The audio_config function is intended to configure all the relevant audio parameters of the display. In order to make the function independent of any specific DSS device driver, a struct omap_dss_audio is defined. Its purpose is to contain all the required parameters for audio configuration. At the moment, such structure contains pointers to IEC-60958 channel status word and CEA-861 audio infoframe structures. This should be enough to support HDMI and DisplayPort, as both are based on CEA-861 and IEC-60958. The audio_enable/disable, audio_config and audio_supported functions could be implemented as functions that may sleep. Hence, they should not be called while holding a spinlock or a readlock. The audio_start/audio_stop function is intended to effectively start/stop audio playback after the configuration has taken place. These functions are designed to be used in an atomic context. Hence, audio_start should return quickly and be called only after all the needed resources for audio playback (audio FIFOs, DMA channels, companion chips, etc) have been enabled to begin data transfers. audio_stop is designed to only stop the audio transfers. The resources used for playback are released using audio_disable. The enum omap_dss_audio_state may be used to help the implementations of the interface to keep track of the audio state. The initial state is _DISABLED; then, the state transitions to _CONFIGURED, and then, when it is ready to play audio, to _ENABLED. The state _PLAYING is used when the audio is being rendered. Panel and controller drivers ---------------------------- Loading
drivers/video/omap2/dss/Kconfig +4 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/video/omap2/dss/dss.h +8 −0 Original line number Diff line number Diff line Loading @@ -464,6 +464,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 */ int rfbi_init_platform_driver(void) __init; Loading
drivers/video/omap2/dss/dss_features.c +6 −2 Original line number Diff line number Diff line Loading @@ -568,13 +568,17 @@ 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, .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, .audio_stop = ti_hdmi_4xxx_audio_stop, .audio_config = ti_hdmi_4xxx_audio_config, #endif }; Loading
drivers/video/omap2/dss/hdmi.c +135 −224 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -324,7 +318,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); Loading Loading @@ -358,7 +352,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) /* tv size */ dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); 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) Loading @@ -367,7 +363,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: Loading @@ -379,7 +376,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(); Loading Loading @@ -536,241 +533,171 @@ 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) static int hdmi_get_clocks(struct platform_device *pdev) { 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; struct clk *clk; clk = clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); return PTR_ERR(clk); } hdmi.sys_clk = clk; return 0; } if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { dev_err(&pdev->dev, "Cannot enable/disable audio\n"); return -ENODEV; static void hdmi_put_clocks(void) { if (hdmi.sys_clk) clk_put(hdmi.sys_clk); } 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, true); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 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) return -EINVAL; /* 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: 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 SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ip_data->ops->audio_enable(ip_data, false); case 44100: case 88200: case 176400: if (deep_color == 125) if (pclk == 27027) deep_color_correct = true; 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; return -EINVAL; } if (deep_color_correct) { switch (sample_freq) { case 32000: *n = 8192; break; case 44100: *n = 12544; 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; 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; } switch (params_rate(params)) { } else { switch (sample_freq) { case 32000: sample_freq = HDMI_AUDIO_FS_32000; *n = 4096; break; case 44100: sample_freq = HDMI_AUDIO_FS_44100; *n = 6272; break; case 48000: sample_freq = HDMI_AUDIO_FS_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); 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) int hdmi_audio_enable(void) { if (!hdmi.ip_data.cfg.cm.mode) { pr_err("Current video settings do not support audio.\n"); return -EIO; } return 0; DSSDBG("audio_enable\n"); return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); } static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) void hdmi_audio_disable(void) { struct hdmi_ip_data *priv = &hdmi.ip_data; DSSDBG("audio_disable\n"); snd_soc_codec_set_drvdata(codec, priv); return 0; hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); } 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, }; int hdmi_audio_start(void) { DSSDBG("audio_start\n"); 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 return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); } static int hdmi_get_clocks(struct platform_device *pdev) void hdmi_audio_stop(void) { struct clk *clk; DSSDBG("audio_stop\n"); clk = clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); return PTR_ERR(clk); hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); } hdmi.sys_clk = clk; return 0; bool hdmi_mode_has_audio(void) { if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) return true; else return false; } static void hdmi_put_clocks(void) int hdmi_audio_config(struct omap_dss_audio *audio) { if (hdmi.sys_clk) clk_put(hdmi.sys_clk); return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); } #endif static void __init hdmi_probe_pdata(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; Loading Loading @@ -838,17 +765,6 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_probe_pdata(pdev); #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; } Loading @@ -858,11 +774,6 @@ static int __exit 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(); Loading