mirror of https://gitee.com/openkylin/linux.git
Merge branch 'drm-tda998x-devel' of git://git.armlinux.org.uk/~rmk/linux-arm into drm-next
This adds the ASoC codec interfaces for TDA998x HDMI audio from Jyri Sarha. * 'drm-tda998x-devel' of git://git.armlinux.org.uk/~rmk/linux-arm: ARM: dts: am335x-boneblack: Add HDMI audio support drm/i2c: tda998x: Register ASoC hdmi-codec and add audio DT binding drm/i2c: tda998x: Improve tda998x_configure_audio() audio related pdata
This commit is contained in:
commit
8506912b96
|
@ -21,8 +21,19 @@ Optional properties:
|
|||
- video-ports: 24 bits value which defines how the video controller
|
||||
output is wired to the TDA998x input - default: <0x230145>
|
||||
|
||||
- audio-ports: array of 8-bit values, 2 values per one DAI[1].
|
||||
The first value defines the DAI type: TDA998x_SPDIF or TDA998x_I2S[2].
|
||||
The second value defines the tda998x AP_ENA reg content when the DAI
|
||||
in question is used. The implementation allows one or two DAIs. If two
|
||||
DAIs are defined, they must be of different type.
|
||||
|
||||
[1] Documentation/sound/alsa/soc/DAI.txt
|
||||
[2] include/dt-bindings/display/tda998x.h
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/display/tda998x.h>
|
||||
|
||||
tda998x: hdmi-encoder {
|
||||
compatible = "nxp,tda998x";
|
||||
reg = <0x70>;
|
||||
|
@ -30,4 +41,11 @@ Example:
|
|||
interrupts = <27 2>; /* falling edge */
|
||||
pinctrl-0 = <&pmx_camera>;
|
||||
pinctrl-names = "default";
|
||||
video-ports = <0x230145>;
|
||||
|
||||
#sound-dai-cells = <2>;
|
||||
/* DAI-format AP_ENA reg value */
|
||||
audio-ports = < TDA998x_SPDIF 0x04
|
||||
TDA998x_I2S 0x03>;
|
||||
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "am33xx.dtsi"
|
||||
#include "am335x-bone-common.dtsi"
|
||||
#include <dt-bindings/display/tda998x.h>
|
||||
|
||||
/ {
|
||||
model = "TI AM335x BeagleBone Black";
|
||||
|
@ -75,6 +76,16 @@ nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins {
|
|||
AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */
|
||||
>;
|
||||
};
|
||||
|
||||
mcasp0_pins: mcasp0_pins {
|
||||
pinctrl-single,pins = <
|
||||
AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */
|
||||
AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/
|
||||
AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */
|
||||
AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */
|
||||
AM33XX_IOPAD(0x86c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
&lcdc {
|
||||
|
@ -87,16 +98,22 @@ lcdc_0: endpoint@0 {
|
|||
};
|
||||
|
||||
&i2c0 {
|
||||
tda19988 {
|
||||
tda19988: tda19988 {
|
||||
compatible = "nxp,tda998x";
|
||||
reg = <0x70>;
|
||||
|
||||
pinctrl-names = "default", "off";
|
||||
pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
|
||||
pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
|
||||
|
||||
port {
|
||||
hdmi_0: endpoint@0 {
|
||||
remote-endpoint = <&lcdc_0>;
|
||||
#sound-dai-cells = <0>;
|
||||
audio-ports = < TDA998x_I2S 0x03>;
|
||||
|
||||
ports {
|
||||
port@0 {
|
||||
hdmi_0: endpoint@0 {
|
||||
remote-endpoint = <&lcdc_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -105,3 +122,49 @@ hdmi_0: endpoint@0 {
|
|||
&rtc {
|
||||
system-power-controller;
|
||||
};
|
||||
|
||||
&mcasp0 {
|
||||
#sound-dai-cells = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mcasp0_pins>;
|
||||
status = "okay";
|
||||
op-mode = <0>; /* MCASP_IIS_MODE */
|
||||
tdm-slots = <2>;
|
||||
serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
|
||||
0 0 1 0
|
||||
>;
|
||||
tx-num-evt = <32>;
|
||||
rx-num-evt = <32>;
|
||||
};
|
||||
|
||||
/ {
|
||||
clk_mcasp0_fixed: clk_mcasp0_fixed {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <24576000>;
|
||||
};
|
||||
|
||||
clk_mcasp0: clk_mcasp0 {
|
||||
#clock-cells = <0>;
|
||||
compatible = "gpio-gate-clock";
|
||||
clocks = <&clk_mcasp0_fixed>;
|
||||
enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */
|
||||
};
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "TI BeagleBone Black";
|
||||
simple-audio-card,format = "i2s";
|
||||
simple-audio-card,bitclock-master = <&dailink0_master>;
|
||||
simple-audio-card,frame-master = <&dailink0_master>;
|
||||
|
||||
dailink0_master: simple-audio-card,cpu {
|
||||
sound-dai = <&mcasp0>;
|
||||
clocks = <&clk_mcasp0>;
|
||||
};
|
||||
|
||||
simple-audio-card,codec {
|
||||
sound-dai = <&tda19988>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ config DRM_I2C_SIL164
|
|||
config DRM_I2C_NXP_TDA998X
|
||||
tristate "NXP Semiconductors TDA998X HDMI encoder"
|
||||
default m if DRM_TILCDC
|
||||
select SND_SOC_HDMI_CODEC if SND_SOC
|
||||
help
|
||||
Support for NXP Semiconductors TDA998X HDMI encoders.
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
@ -30,6 +31,11 @@
|
|||
|
||||
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
|
||||
|
||||
struct tda998x_audio_port {
|
||||
u8 format; /* AFMT_xxx */
|
||||
u8 config; /* AP value */
|
||||
};
|
||||
|
||||
struct tda998x_priv {
|
||||
struct i2c_client *cec;
|
||||
struct i2c_client *hdmi;
|
||||
|
@ -41,7 +47,10 @@ struct tda998x_priv {
|
|||
u8 vip_cntrl_0;
|
||||
u8 vip_cntrl_1;
|
||||
u8 vip_cntrl_2;
|
||||
struct tda998x_encoder_params params;
|
||||
struct tda998x_audio_params audio_params;
|
||||
|
||||
struct platform_device *audio_pdev;
|
||||
struct mutex audio_mutex;
|
||||
|
||||
wait_queue_head_t wq_edid;
|
||||
volatile int wq_edid_wait;
|
||||
|
@ -53,6 +62,8 @@ struct tda998x_priv {
|
|||
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
|
||||
struct tda998x_audio_port audio_port[2];
|
||||
};
|
||||
|
||||
#define conn_to_tda998x_priv(x) \
|
||||
|
@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
|
|||
reg_set(priv, REG_DIP_IF_FLAGS, bit);
|
||||
}
|
||||
|
||||
static void
|
||||
tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p)
|
||||
static int tda998x_write_aif(struct tda998x_priv *priv,
|
||||
struct hdmi_audio_infoframe *cea)
|
||||
{
|
||||
union hdmi_infoframe frame;
|
||||
|
||||
hdmi_audio_infoframe_init(&frame.audio);
|
||||
|
||||
frame.audio.channels = p->audio_frame[1] & 0x07;
|
||||
frame.audio.channel_allocation = p->audio_frame[4];
|
||||
frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3;
|
||||
frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7;
|
||||
|
||||
/*
|
||||
* L-PCM and IEC61937 compressed audio shall always set sample
|
||||
* frequency to "refer to stream". For others, see the HDMI
|
||||
* specification.
|
||||
*/
|
||||
frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2;
|
||||
frame.audio = *cea;
|
||||
|
||||
tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -710,20 +711,21 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
tda998x_configure_audio(struct tda998x_priv *priv,
|
||||
struct drm_display_mode *mode, struct tda998x_encoder_params *p)
|
||||
struct tda998x_audio_params *params,
|
||||
unsigned mode_clock)
|
||||
{
|
||||
u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
|
||||
u32 n;
|
||||
|
||||
/* Enable audio ports */
|
||||
reg_write(priv, REG_ENA_AP, p->audio_cfg);
|
||||
reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
|
||||
reg_write(priv, REG_ENA_AP, params->config);
|
||||
|
||||
/* Set audio input source */
|
||||
switch (p->audio_format) {
|
||||
switch (params->format) {
|
||||
case AFMT_SPDIF:
|
||||
reg_write(priv, REG_ENA_ACLK, 0);
|
||||
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
|
||||
clksel_aip = AIP_CLKSEL_AIP_SPDIF;
|
||||
clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
|
||||
|
@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv,
|
|||
break;
|
||||
|
||||
case AFMT_I2S:
|
||||
reg_write(priv, REG_ENA_ACLK, 1);
|
||||
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
|
||||
clksel_aip = AIP_CLKSEL_AIP_I2S;
|
||||
clksel_fs = AIP_CLKSEL_FS_ACLK;
|
||||
cts_n = CTS_N_M(3) | CTS_N_K(3);
|
||||
switch (params->sample_width) {
|
||||
case 16:
|
||||
cts_n = CTS_N_M(3) | CTS_N_K(1);
|
||||
break;
|
||||
case 18:
|
||||
case 20:
|
||||
case 24:
|
||||
cts_n = CTS_N_M(3) | CTS_N_K(2);
|
||||
break;
|
||||
default:
|
||||
case 32:
|
||||
cts_n = CTS_N_M(3) | CTS_N_K(3);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
dev_err(&priv->hdmi->dev, "Unsupported I2S format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
|
||||
|
@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv,
|
|||
* assume 100MHz requires larger divider.
|
||||
*/
|
||||
adiv = AUDIO_DIV_SERCLK_8;
|
||||
if (mode->clock > 100000)
|
||||
if (mode_clock > 100000)
|
||||
adiv++; /* AUDIO_DIV_SERCLK_16 */
|
||||
|
||||
/* S/PDIF asks for a larger divider */
|
||||
if (p->audio_format == AFMT_SPDIF)
|
||||
if (params->format == AFMT_SPDIF)
|
||||
adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */
|
||||
|
||||
reg_write(priv, REG_AUDIO_DIV, adiv);
|
||||
|
@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
|
|||
* This is the approximate value of N, which happens to be
|
||||
* the recommended values for non-coherent clocks.
|
||||
*/
|
||||
n = 128 * p->audio_sample_rate / 1000;
|
||||
n = 128 * params->sample_rate / 1000;
|
||||
|
||||
/* Write the CTS and N values */
|
||||
buf[0] = 0x44;
|
||||
|
@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv,
|
|||
reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
|
||||
reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
|
||||
|
||||
/* Write the channel status */
|
||||
buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = IEC958_AES3_CON_FS_NOTID;
|
||||
buf[3] = IEC958_AES4_CON_ORIGFS_NOTID |
|
||||
IEC958_AES4_CON_MAX_WORDLEN_24;
|
||||
/* Write the channel status
|
||||
* The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because
|
||||
* there is a separate register for each I2S wire.
|
||||
*/
|
||||
buf[0] = params->status[0];
|
||||
buf[1] = params->status[1];
|
||||
buf[2] = params->status[3];
|
||||
buf[3] = params->status[4];
|
||||
reg_write_range(priv, REG_CH_STAT_B(0), buf, 4);
|
||||
|
||||
tda998x_audio_mute(priv, true);
|
||||
msleep(20);
|
||||
tda998x_audio_mute(priv, false);
|
||||
|
||||
/* Write the audio information packet */
|
||||
tda998x_write_aif(priv, p);
|
||||
return tda998x_write_aif(priv, ¶ms->cea);
|
||||
}
|
||||
|
||||
/* DRM encoder functions */
|
||||
|
@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
|
|||
VIP_CNTRL_2_SWAP_F(p->swap_f) |
|
||||
(p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
|
||||
|
||||
priv->params = *p;
|
||||
priv->audio_params = p->audio_params;
|
||||
}
|
||||
|
||||
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
|
@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
|
|||
|
||||
tda998x_write_avi(priv, adjusted_mode);
|
||||
|
||||
if (priv->params.audio_cfg)
|
||||
tda998x_configure_audio(priv, adjusted_mode,
|
||||
&priv->params);
|
||||
if (priv->audio_params.format != AFMT_UNUSED) {
|
||||
mutex_lock(&priv->audio_mutex);
|
||||
tda998x_configure_audio(priv,
|
||||
&priv->audio_params,
|
||||
adjusted_mode->clock);
|
||||
mutex_unlock(&priv->audio_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1159,6 +1180,8 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
|
|||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
n = drm_add_edid_modes(connector, edid);
|
||||
priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
|
||||
drm_edid_to_eld(connector, edid);
|
||||
|
||||
kfree(edid);
|
||||
|
||||
return n;
|
||||
|
@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv)
|
|||
cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
|
||||
reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
|
||||
|
||||
if (priv->audio_pdev)
|
||||
platform_device_unregister(priv->audio_pdev);
|
||||
|
||||
if (priv->hdmi->irq)
|
||||
free_irq(priv->hdmi->irq, priv);
|
||||
|
||||
|
@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv)
|
|||
i2c_unregister_device(priv->cec);
|
||||
}
|
||||
|
||||
static int tda998x_audio_hw_params(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *daifmt,
|
||||
struct hdmi_codec_params *params)
|
||||
{
|
||||
struct tda998x_priv *priv = dev_get_drvdata(dev);
|
||||
int i, ret;
|
||||
struct tda998x_audio_params audio = {
|
||||
.sample_width = params->sample_width,
|
||||
.sample_rate = params->sample_rate,
|
||||
.cea = params->cea,
|
||||
};
|
||||
|
||||
if (!priv->encoder.crtc)
|
||||
return -ENODEV;
|
||||
|
||||
memcpy(audio.status, params->iec.status,
|
||||
min(sizeof(audio.status), sizeof(params->iec.status)));
|
||||
|
||||
switch (daifmt->fmt) {
|
||||
case HDMI_I2S:
|
||||
if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
|
||||
daifmt->bit_clk_master || daifmt->frame_clk_master) {
|
||||
dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
|
||||
daifmt->bit_clk_inv, daifmt->frame_clk_inv,
|
||||
daifmt->bit_clk_master,
|
||||
daifmt->frame_clk_master);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
|
||||
if (priv->audio_port[i].format == AFMT_I2S)
|
||||
audio.config = priv->audio_port[i].config;
|
||||
audio.format = AFMT_I2S;
|
||||
break;
|
||||
case HDMI_SPDIF:
|
||||
for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
|
||||
if (priv->audio_port[i].format == AFMT_SPDIF)
|
||||
audio.config = priv->audio_port[i].config;
|
||||
audio.format = AFMT_SPDIF;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (audio.config == 0) {
|
||||
dev_err(dev, "%s: No audio configutation found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->audio_mutex);
|
||||
ret = tda998x_configure_audio(priv,
|
||||
&audio,
|
||||
priv->encoder.crtc->hwmode.clock);
|
||||
|
||||
if (ret == 0)
|
||||
priv->audio_params = audio;
|
||||
mutex_unlock(&priv->audio_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tda998x_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
struct tda998x_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&priv->audio_mutex);
|
||||
|
||||
reg_write(priv, REG_ENA_AP, 0);
|
||||
|
||||
priv->audio_params.format = AFMT_UNUSED;
|
||||
|
||||
mutex_unlock(&priv->audio_mutex);
|
||||
}
|
||||
|
||||
int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
|
||||
{
|
||||
struct tda998x_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&priv->audio_mutex);
|
||||
|
||||
tda998x_audio_mute(priv, enable);
|
||||
|
||||
mutex_unlock(&priv->audio_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda998x_audio_get_eld(struct device *dev, void *data,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
struct tda998x_priv *priv = dev_get_drvdata(dev);
|
||||
struct drm_mode_config *config = &priv->encoder.dev->mode_config;
|
||||
struct drm_connector *connector;
|
||||
int ret = -ENODEV;
|
||||
|
||||
mutex_lock(&config->mutex);
|
||||
list_for_each_entry(connector, &config->connector_list, head) {
|
||||
if (&priv->encoder == connector->encoder) {
|
||||
memcpy(buf, connector->eld,
|
||||
min(sizeof(connector->eld), len));
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&config->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops audio_codec_ops = {
|
||||
.hw_params = tda998x_audio_hw_params,
|
||||
.audio_shutdown = tda998x_audio_shutdown,
|
||||
.digital_mute = tda998x_audio_digital_mute,
|
||||
.get_eld = tda998x_audio_get_eld,
|
||||
};
|
||||
|
||||
static int tda998x_audio_codec_init(struct tda998x_priv *priv,
|
||||
struct device *dev)
|
||||
{
|
||||
struct hdmi_codec_pdata codec_data = {
|
||||
.ops = &audio_codec_ops,
|
||||
.max_i2s_channels = 2,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
|
||||
if (priv->audio_port[i].format == AFMT_I2S &&
|
||||
priv->audio_port[i].config != 0)
|
||||
codec_data.i2s = 1;
|
||||
if (priv->audio_port[i].format == AFMT_SPDIF &&
|
||||
priv->audio_port[i].config != 0)
|
||||
codec_data.spdif = 1;
|
||||
}
|
||||
|
||||
priv->audio_pdev = platform_device_register_data(
|
||||
dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
|
||||
&codec_data, sizeof(codec_data));
|
||||
|
||||
return PTR_ERR_OR_ZERO(priv->audio_pdev);
|
||||
}
|
||||
|
||||
/* I2C driver functions */
|
||||
|
||||
static int tda998x_get_audio_ports(struct tda998x_priv *priv,
|
||||
struct device_node *np)
|
||||
{
|
||||
const u32 *port_data;
|
||||
u32 size;
|
||||
int i;
|
||||
|
||||
port_data = of_get_property(np, "audio-ports", &size);
|
||||
if (!port_data)
|
||||
return 0;
|
||||
|
||||
size /= sizeof(u32);
|
||||
if (size > 2 * ARRAY_SIZE(priv->audio_port) || size % 2 != 0) {
|
||||
dev_err(&priv->hdmi->dev,
|
||||
"Bad number of elements in audio-ports dt-property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size /= 2;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
u8 afmt = be32_to_cpup(&port_data[2*i]);
|
||||
u8 ena_ap = be32_to_cpup(&port_data[2*i+1]);
|
||||
|
||||
if (afmt != AFMT_SPDIF && afmt != AFMT_I2S) {
|
||||
dev_err(&priv->hdmi->dev,
|
||||
"Bad audio format %u\n", afmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->audio_port[i].format = afmt;
|
||||
priv->audio_port[i].config = ena_ap;
|
||||
}
|
||||
|
||||
if (priv->audio_port[0].format == priv->audio_port[1].format) {
|
||||
dev_err(&priv->hdmi->dev,
|
||||
"There can only be on I2S port and one SPDIF port\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
|||
if (!np)
|
||||
return 0; /* non-DT */
|
||||
|
||||
/* get the optional video properties */
|
||||
/* get the device tree parameters */
|
||||
ret = of_property_read_u32(np, "video-ports", &video);
|
||||
if (ret == 0) {
|
||||
priv->vip_cntrl_0 = video >> 16;
|
||||
|
@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
|||
priv->vip_cntrl_2 = video;
|
||||
}
|
||||
|
||||
return 0;
|
||||
mutex_init(&priv->audio_mutex); /* Protect access from audio thread */
|
||||
|
||||
ret = tda998x_get_audio_ports(priv, np);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (priv->audio_port[0].format != AFMT_UNUSED)
|
||||
tda998x_audio_codec_init(priv, &client->dev);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
/* if encoder_init fails, the encoder slave is never registered,
|
||||
* so cleanup here:
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
#ifndef __DRM_I2C_TDA998X_H__
|
||||
#define __DRM_I2C_TDA998X_H__
|
||||
|
||||
#include <linux/hdmi.h>
|
||||
#include <dt-bindings/display/tda998x.h>
|
||||
|
||||
enum {
|
||||
AFMT_UNUSED = 0,
|
||||
AFMT_SPDIF = TDA998x_SPDIF,
|
||||
AFMT_I2S = TDA998x_I2S,
|
||||
};
|
||||
|
||||
struct tda998x_audio_params {
|
||||
u8 config;
|
||||
u8 format;
|
||||
unsigned sample_width;
|
||||
unsigned sample_rate;
|
||||
struct hdmi_audio_infoframe cea;
|
||||
u8 status[5];
|
||||
};
|
||||
|
||||
struct tda998x_encoder_params {
|
||||
u8 swap_b:3;
|
||||
u8 mirr_b:1;
|
||||
|
@ -15,16 +33,7 @@ struct tda998x_encoder_params {
|
|||
u8 swap_e:3;
|
||||
u8 mirr_e:1;
|
||||
|
||||
u8 audio_cfg;
|
||||
u8 audio_clk_cfg;
|
||||
u8 audio_frame[6];
|
||||
|
||||
enum {
|
||||
AFMT_SPDIF,
|
||||
AFMT_I2S
|
||||
} audio_format;
|
||||
|
||||
unsigned audio_sample_rate;
|
||||
struct tda998x_audio_params audio_params;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _DT_BINDINGS_TDA998X_H
|
||||
#define _DT_BINDINGS_TDA998X_H
|
||||
|
||||
#define TDA998x_SPDIF 1
|
||||
#define TDA998x_I2S 2
|
||||
|
||||
#endif /*_DT_BINDINGS_TDA998X_H */
|
Loading…
Reference in New Issue