msm: mdss: hdmi: Add support for new HDMI-Audio interface

Using this new interface, HDMI-Audio codec driver can retrieve
sink's audio capabilities. Based on this supported capabilities
and current playback clip, it will pass appropriate audio setup
information to HDMI driver for configuration.

Change-Id: Ia52f72d955778859c381a5e6c94aba57e40d13b2
Signed-off-by: Ujwal Patel <ujwalp@codeaurora.org>
This commit is contained in:
Ujwal Patel 2012-11-15 12:46:10 -08:00 committed by David Keitel
parent 4c70249244
commit de43093857
5 changed files with 219 additions and 52 deletions

View file

@ -21,6 +21,14 @@ Required properties:
- <compatible-name>-<gpio-name>: specifies gpios assigned for the device.
[Optional child nodes]: These nodes are for devices which are
dependent on HDMI Tx controller. If HDMI Tx controller is disabled then
these devices will be disabled as well. Ex. HDMI Audio Codec device.
- qcom,msm-hdmi-audio-rx: Node for HDMI audio codec.
Required properties:
- compatible : "msm-hdmi-audio-codec-rx";
Example:
qcom,hdmi_tx@fd922100 {
cell-index = <0>;
@ -43,4 +51,8 @@ Example:
qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>;
qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>;
qcom,hdmi-tx-hpd = <&msmgpio 34 0>;
qcom,msm-hdmi-audio-rx {
compatible = "qcom,msm-hdmi-audio-codec-rx";
};
};

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __MSM_HDMI_AUDIO_CODEC_H__
#define __MSM_HDMI_AUDIO_CODEC_H__
#include <linux/device.h>
#include <linux/platform_device.h>
struct msm_hdmi_audio_edid_blk {
u8 *audio_data_blk;
unsigned int audio_data_blk_size; /* in bytes */
u8 *spk_alloc_data_blk;
unsigned int spk_alloc_data_blk_size; /* in bytes */
};
struct msm_hdmi_audio_codec_ops {
int (*audio_info_setup)(struct platform_device *pdev,
u32 num_of_channels, u32 channel_allocation, u32 level_shift,
bool down_mix);
int (*get_audio_edid_blk) (struct platform_device *pdev,
struct msm_hdmi_audio_edid_blk *blk);
};
int msm_hdmi_register_audio_codec(struct platform_device *pdev,
struct msm_hdmi_audio_codec_ops *ops);
#endif /* __MSM_HDMI_AUDIO_CODEC_H__ */

View file

@ -19,6 +19,14 @@
#define HDMI_VSDB_3D_EVF_DATA_OFFSET(vsd) \
(!((vsd)[8] & BIT(7)) ? 9 : (!((vsd)[8] & BIT(6)) ? 11 : 13))
/*
* As per the CEA-861E spec, there can be a total of 10 short audio
* descriptors with each SAD being 3 bytes long.
* Thus, the maximum length of the audio data block would be 30 bytes
*/
#define MAX_AUDIO_DATA_BLOCK_SIZE 30
#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
struct hdmi_edid_sink_data {
u32 disp_mode_list[HDMI_VFRMT_MAX];
u32 disp_3d_mode_list[HDMI_VFRMT_MAX];
@ -35,11 +43,13 @@ struct hdmi_edid_ctrl {
u16 physical_address;
u32 video_resolution; /* selected by user */
u32 sink_mode; /* HDMI or DVI */
u8 speaker_allocation_block;
u8 audio_data_block_cnt;
u16 audio_latency;
u16 video_latency;
u32 present_3d;
u8 audio_data_block[MAX_AUDIO_DATA_BLOCK_SIZE];
int adb_size;
u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
int sadb_size;
struct hdmi_edid_sink_data sink_data;
struct hdmi_edid_init_data init_data;
@ -551,26 +561,28 @@ static void hdmi_edid_extract_3d_present(struct hdmi_edid_ctrl *edid_ctrl,
static void hdmi_edid_extract_audio_data_blocks(
struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf)
{
u8 len;
const u8 *sad = NULL;
u8 len, cnt = 0;
const u8 *adb = NULL;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, &len);
if (sad == NULL)
adb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1, &len);
if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE))
return;
edid_ctrl->audio_data_block_cnt = 0;
while (len >= 3 && edid_ctrl->audio_data_block_cnt < 16) {
DEV_DBG("%s: ch=%d fmt=%d sampling=0x%02x bitdepth=0x%02x\n",
__func__, (sad[1]&0x7)+1, sad[1]>>3, sad[2], sad[3]);
memcpy(edid_ctrl->audio_data_block, adb + 1, len);
edid_ctrl->adb_size = len;
++edid_ctrl->audio_data_block_cnt;
while (len >= 3 && cnt < 16) {
DEV_DBG("%s: ch=%d fmt=%d sampling=0x%02x bitdepth=0x%02x\n",
__func__, (adb[1]&0x7)+1, adb[1]>>3, adb[2], adb[3]);
cnt++;
len -= 3;
sad += 3;
adb += 3;
}
} /* hdmi_edid_extract_audio_data_blocks */
@ -578,27 +590,29 @@ static void hdmi_edid_extract_speaker_allocation_data(
struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf)
{
u8 len;
const u8 *sad = NULL;
const u8 *sadb = NULL;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, &len);
if (sad == NULL)
sadb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4, &len);
if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE))
return;
edid_ctrl->speaker_allocation_block = sad[1];
memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
edid_ctrl->sadb_size = len;
DEV_DBG("%s: EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
__func__, sad[1],
(sad[1] & BIT(0)) ? "FL/FR," : "",
(sad[1] & BIT(1)) ? "LFE," : "",
(sad[1] & BIT(2)) ? "FC," : "",
(sad[1] & BIT(3)) ? "RL/RR," : "",
(sad[1] & BIT(4)) ? "RC," : "",
(sad[1] & BIT(5)) ? "FLC/FRC," : "",
(sad[1] & BIT(6)) ? "RLC/RRC," : "");
__func__, sadb[1],
(sadb[1] & BIT(0)) ? "FL/FR," : "",
(sadb[1] & BIT(1)) ? "LFE," : "",
(sadb[1] & BIT(2)) ? "FC," : "",
(sadb[1] & BIT(3)) ? "RL/RR," : "",
(sadb[1] & BIT(4)) ? "RC," : "",
(sadb[1] & BIT(5)) ? "FLC/FRC," : "",
(sadb[1] & BIT(6)) ? "RLC/RRC," : "");
} /* hdmi_edid_extract_speaker_allocation_data */
static void hdmi_edid_extract_latency_fields(struct hdmi_edid_ctrl *edid_ctrl,
@ -1225,6 +1239,12 @@ int hdmi_edid_read(void *input)
edid_ctrl->present_3d = 0;
memset(&edid_ctrl->sink_data, 0, sizeof(edid_ctrl->sink_data));
memset(edid_buf, 0, sizeof(edid_buf));
memset(edid_ctrl->audio_data_block, 0,
sizeof(edid_ctrl->audio_data_block));
memset(edid_ctrl->spkr_alloc_data_block, 0,
sizeof(edid_ctrl->spkr_alloc_data_block));
edid_ctrl->adb_size = 0;
edid_ctrl->sadb_size = 0;
status = hdmi_edid_read_block(edid_ctrl, 0, edid_buf);
if (status || !hdmi_edid_check_header(edid_buf)) {
@ -1401,6 +1421,24 @@ u32 hdmi_edid_get_sink_mode(void *input)
return edid_ctrl->sink_mode;
} /* hdmi_edid_get_sink_mode */
int hdmi_edid_get_audio_blk(void *input, struct msm_hdmi_audio_edid_blk *blk)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
if (!edid_ctrl || !blk) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
blk->audio_data_blk = edid_ctrl->audio_data_block;
blk->audio_data_blk_size = edid_ctrl->adb_size;
blk->spk_alloc_data_blk = edid_ctrl->spkr_alloc_data_block;
blk->spk_alloc_data_blk_size = edid_ctrl->sadb_size;
return 0;
} /* hdmi_edid_get_audio_blk */
void hdmi_edid_set_video_resolution(void *input, u32 resolution)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;

View file

@ -13,6 +13,7 @@
#ifndef __HDMI_EDID_H__
#define __HDMI_EDID_H__
#include <mach/msm_hdmi_audio_codec.h>
#include "mdss_hdmi_util.h"
struct hdmi_edid_init_data {
@ -26,6 +27,8 @@ struct hdmi_edid_init_data {
int hdmi_edid_read(void *edid_ctrl);
u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution);
u32 hdmi_edid_get_sink_mode(void *edid_ctrl);
int hdmi_edid_get_audio_blk(void *edid_ctrl,
struct msm_hdmi_audio_edid_blk *blk);
void hdmi_edid_set_video_resolution(void *edid_ctrl, u32 resolution);
void hdmi_edid_deinit(void *edid_ctrl);
void *hdmi_edid_init(struct hdmi_edid_init_data *init_data);

View file

@ -18,8 +18,9 @@
#include <linux/iopoll.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/types.h>
#include <mach/msm_hdmi_audio.h>
#include <mach/msm_hdmi_audio_codec.h>
#define REG_DUMP 0
@ -49,6 +50,23 @@
/* Enable HDCP by default */
static bool hdcp_feature_on = true;
/* Supported HDMI Audio channels */
#define MSM_HDMI_AUDIO_CHANNEL_2 2
#define MSM_HDMI_AUDIO_CHANNEL_4 4
#define MSM_HDMI_AUDIO_CHANNEL_6 6
#define MSM_HDMI_AUDIO_CHANNEL_8 8
enum msm_hdmi_supported_audio_sample_rates {
AUDIO_SAMPLE_RATE_32KHZ,
AUDIO_SAMPLE_RATE_44_1KHZ,
AUDIO_SAMPLE_RATE_48KHZ,
AUDIO_SAMPLE_RATE_88_2KHZ,
AUDIO_SAMPLE_RATE_96KHZ,
AUDIO_SAMPLE_RATE_176_4KHZ,
AUDIO_SAMPLE_RATE_192KHZ,
AUDIO_SAMPLE_RATE_MAX
};
/* parameters for clock regeneration */
struct hdmi_tx_audio_acr {
u32 n;
@ -57,7 +75,7 @@ struct hdmi_tx_audio_acr {
struct hdmi_tx_audio_acr_arry {
u32 pclk;
struct hdmi_tx_audio_acr lut[HDMI_SAMPLE_RATE_MAX];
struct hdmi_tx_audio_acr lut[AUDIO_SAMPLE_RATE_MAX];
};
static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on);
@ -1508,13 +1526,13 @@ static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
if (
(HDMI_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
(AUDIO_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
multiplier = 4;
n >>= 2; /* divide N by 4 and use multiplier */
} else if (
(HDMI_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate)) {
(AUDIO_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate)) {
multiplier = 2;
n >>= 1; /* divide N by 2 and use multiplier */
} else {
@ -1528,9 +1546,9 @@ static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
/* N_MULTIPLE(multiplier) */
acr_pck_ctrl_reg |= (multiplier & 7) << 16;
if ((HDMI_SAMPLE_RATE_48KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate)) {
if ((AUDIO_SAMPLE_RATE_48KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate)) {
/* SELECT(3) */
acr_pck_ctrl_reg |= 3 << 4;
/* CTS_48 */
@ -1541,9 +1559,9 @@ static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
/* N */
DSS_REG_W(io, HDMI_ACR_48_1, n);
} else if (
(HDMI_SAMPLE_RATE_44_1KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate) ||
(HDMI_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
(AUDIO_SAMPLE_RATE_44_1KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate) ||
(AUDIO_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
/* SELECT(2) */
acr_pck_ctrl_reg |= 2 << 4;
/* CTS_44 */
@ -1581,11 +1599,10 @@ static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
return 0;
} /* hdmi_tx_audio_acr_setup */
static int hdmi_tx_audio_info_setup(void *priv_d, bool enabled,
u32 num_of_channels, u32 channel_allocation, u32 level_shift,
bool down_mix)
static int hdmi_tx_audio_iframe_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
bool enabled, u32 num_of_channels, u32 channel_allocation,
u32 level_shift, bool down_mix)
{
struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)priv_d;
struct dss_io_data *io = NULL;
u32 channel_count = 1; /* Def to 2 channels -> Table 17 in CEA-D */
@ -1630,22 +1647,15 @@ static int hdmi_tx_audio_info_setup(void *priv_d, bool enabled,
if (enabled) {
switch (num_of_channels) {
case MSM_HDMI_AUDIO_CHANNEL_2:
channel_allocation = 0; /* Default to FR, FL */
break;
case MSM_HDMI_AUDIO_CHANNEL_4:
channel_count = 3;
/* FC, LFE, FR, FL */
channel_allocation = 0x3;
break;
case MSM_HDMI_AUDIO_CHANNEL_6:
channel_count = 5;
/* RR, RL, FC, LFE, FR, FL */
channel_allocation = 0xB;
break;
case MSM_HDMI_AUDIO_CHANNEL_8:
channel_count = 7;
/* FRC, FLC, RR, RL, FC, LFE, FR, FL */
channel_allocation = 0x1f;
break;
default:
DEV_ERR("%s: Unsupported num_of_channels = %u\n",
@ -1712,9 +1722,67 @@ static int hdmi_tx_audio_info_setup(void *priv_d, bool enabled,
dss_reg_dump(io->base, io->len,
enabled ? "HDMI-AUDIO-ON: " : "HDMI-AUDIO-OFF: ", REG_DUMP);
return 0;
} /* hdmi_tx_audio_iframe_setup */
static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
u32 num_of_channels, u32 channel_allocation, u32 level_shift,
bool down_mix)
{
int rc = 0;
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -ENODEV;
}
if (hdmi_ctrl->panel_power_on) {
rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true,
num_of_channels, channel_allocation, level_shift,
down_mix);
if (rc)
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.rc=%d\n",
__func__, rc);
} else {
DEV_ERR("%s: Error. panel is not on.\n", __func__);
rc = -EPERM;
}
return 0;
} /* hdmi_tx_audio_info_setup */
static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev,
struct msm_hdmi_audio_edid_blk *blk)
{
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -ENODEV;
}
return hdmi_edid_get_audio_blk(
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID], blk);
} /* hdmi_tx_get_audio_edid_blk */
int msm_hdmi_register_audio_codec(struct platform_device *pdev,
struct msm_hdmi_audio_codec_ops *ops)
{
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -ENODEV;
}
ops->audio_info_setup = hdmi_tx_audio_info_setup;
ops->get_audio_edid_blk = hdmi_tx_get_audio_edid_blk;
return 0;
} /* hdmi_tx_audio_register */
EXPORT_SYMBOL(msm_hdmi_register_audio_codec);
static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc = 0;
@ -1739,9 +1807,9 @@ static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
return rc;
}
rc = hdmi_tx_audio_info_setup(hdmi_ctrl, true, channels, 0, 0, false);
rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true, channels, 0, 0, false);
if (rc) {
DEV_ERR("%s: hdmi_tx_audio_info_setup failed. rc=%d\n",
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed. rc=%d\n",
__func__, rc);
return rc;
}
@ -1788,8 +1856,8 @@ static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
if (i == timeout_sec)
DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__);
if (hdmi_tx_audio_info_setup(hdmi_ctrl, false, 0, 0, 0, false))
DEV_ERR("%s: hdmi_tx_audio_info_setup failed.\n", __func__);
if (hdmi_tx_audio_iframe_setup(hdmi_ctrl, false, 0, 0, 0, false))
DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.\n", __func__);
if (hdmi_tx_audio_acr_setup(hdmi_ctrl, false, 0))
DEV_ERR("%s: hdmi_tx_audio_acr_setup failed.\n", __func__);
@ -2243,7 +2311,7 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl)
INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work);
hdmi_ctrl->audio_sample_rate = HDMI_SAMPLE_RATE_48KHZ;
hdmi_ctrl->audio_sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
hdmi_ctrl->sdev.name = "hdmi";
if (switch_dev_register(&hdmi_ctrl->sdev) < 0) {
@ -3026,6 +3094,15 @@ static int hdmi_tx_probe(struct platform_device *pdev)
goto failed_init_features;
}
rc = of_platform_populate(of_node, NULL, NULL, &pdev->dev);
if (rc) {
DEV_ERR("%s: failed to add child devices, rc=%d\n",
__func__, rc);
goto failed_init_features;
} else {
DEV_DBG("%s: added child devices.\n", __func__);
}
return rc;
failed_init_features: