Commit 8506912b authored by Dave Airlie's avatar Dave Airlie

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
parents b4eac546 df0bd1e8
...@@ -21,8 +21,19 @@ Optional properties: ...@@ -21,8 +21,19 @@ Optional properties:
- video-ports: 24 bits value which defines how the video controller - video-ports: 24 bits value which defines how the video controller
output is wired to the TDA998x input - default: <0x230145> 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: Example:
#include <dt-bindings/display/tda998x.h>
tda998x: hdmi-encoder { tda998x: hdmi-encoder {
compatible = "nxp,tda998x"; compatible = "nxp,tda998x";
reg = <0x70>; reg = <0x70>;
...@@ -30,4 +41,11 @@ Example: ...@@ -30,4 +41,11 @@ Example:
interrupts = <27 2>; /* falling edge */ interrupts = <27 2>; /* falling edge */
pinctrl-0 = <&pmx_camera>; pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default"; 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 @@ ...@@ -9,6 +9,7 @@
#include "am33xx.dtsi" #include "am33xx.dtsi"
#include "am335x-bone-common.dtsi" #include "am335x-bone-common.dtsi"
#include <dt-bindings/display/tda998x.h>
/ { / {
model = "TI AM335x BeagleBone Black"; model = "TI AM335x BeagleBone Black";
...@@ -75,6 +76,16 @@ nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { ...@@ -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 */ 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 { &lcdc {
...@@ -87,16 +98,22 @@ lcdc_0: endpoint@0 { ...@@ -87,16 +98,22 @@ lcdc_0: endpoint@0 {
}; };
&i2c0 { &i2c0 {
tda19988 { tda19988: tda19988 {
compatible = "nxp,tda998x"; compatible = "nxp,tda998x";
reg = <0x70>; reg = <0x70>;
pinctrl-names = "default", "off"; pinctrl-names = "default", "off";
pinctrl-0 = <&nxp_hdmi_bonelt_pins>; pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
port { #sound-dai-cells = <0>;
hdmi_0: endpoint@0 { audio-ports = < TDA998x_I2S 0x03>;
remote-endpoint = <&lcdc_0>;
ports {
port@0 {
hdmi_0: endpoint@0 {
remote-endpoint = <&lcdc_0>;
};
}; };
}; };
}; };
...@@ -105,3 +122,49 @@ hdmi_0: endpoint@0 { ...@@ -105,3 +122,49 @@ hdmi_0: endpoint@0 {
&rtc { &rtc {
system-power-controller; 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 ...@@ -22,6 +22,7 @@ config DRM_I2C_SIL164
config DRM_I2C_NXP_TDA998X config DRM_I2C_NXP_TDA998X
tristate "NXP Semiconductors TDA998X HDMI encoder" tristate "NXP Semiconductors TDA998X HDMI encoder"
default m if DRM_TILCDC default m if DRM_TILCDC
select SND_SOC_HDMI_CODEC if SND_SOC
help help
Support for NXP Semiconductors TDA998X HDMI encoders. Support for NXP Semiconductors TDA998X HDMI encoders.
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <sound/asoundef.h> #include <sound/asoundef.h>
#include <sound/hdmi-codec.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
...@@ -30,6 +31,11 @@ ...@@ -30,6 +31,11 @@
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #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 tda998x_priv {
struct i2c_client *cec; struct i2c_client *cec;
struct i2c_client *hdmi; struct i2c_client *hdmi;
...@@ -41,7 +47,10 @@ struct tda998x_priv { ...@@ -41,7 +47,10 @@ struct tda998x_priv {
u8 vip_cntrl_0; u8 vip_cntrl_0;
u8 vip_cntrl_1; u8 vip_cntrl_1;
u8 vip_cntrl_2; 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; wait_queue_head_t wq_edid;
volatile int wq_edid_wait; volatile int wq_edid_wait;
...@@ -53,6 +62,8 @@ struct tda998x_priv { ...@@ -53,6 +62,8 @@ struct tda998x_priv {
struct drm_encoder encoder; struct drm_encoder encoder;
struct drm_connector connector; struct drm_connector connector;
struct tda998x_audio_port audio_port[2];
}; };
#define conn_to_tda998x_priv(x) \ #define conn_to_tda998x_priv(x) \
...@@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr, ...@@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
reg_set(priv, REG_DIP_IF_FLAGS, bit); reg_set(priv, REG_DIP_IF_FLAGS, bit);
} }
static void static int tda998x_write_aif(struct tda998x_priv *priv,
tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p) struct hdmi_audio_infoframe *cea)
{ {
union hdmi_infoframe frame; union hdmi_infoframe frame;
hdmi_audio_infoframe_init(&frame.audio); frame.audio = *cea;
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;
tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame); tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame);
return 0;
} }
static void static void
...@@ -710,20 +711,21 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) ...@@ -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, 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; u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
u32 n; u32 n;
/* Enable audio ports */ /* Enable audio ports */
reg_write(priv, REG_ENA_AP, p->audio_cfg); reg_write(priv, REG_ENA_AP, params->config);
reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
/* Set audio input source */ /* Set audio input source */
switch (p->audio_format) { switch (params->format) {
case AFMT_SPDIF: case AFMT_SPDIF:
reg_write(priv, REG_ENA_ACLK, 0);
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF); reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
clksel_aip = AIP_CLKSEL_AIP_SPDIF; clksel_aip = AIP_CLKSEL_AIP_SPDIF;
clksel_fs = AIP_CLKSEL_FS_FS64SPDIF; clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
...@@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv,
break; break;
case AFMT_I2S: case AFMT_I2S:
reg_write(priv, REG_ENA_ACLK, 1);
reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S); reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
clksel_aip = AIP_CLKSEL_AIP_I2S; clksel_aip = AIP_CLKSEL_AIP_I2S;
clksel_fs = AIP_CLKSEL_FS_ACLK; 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; break;
default: default:
BUG(); dev_err(&priv->hdmi->dev, "Unsupported I2S format\n");
return; return -EINVAL;
} }
reg_write(priv, REG_AIP_CLKSEL, clksel_aip); reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
...@@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv,
* assume 100MHz requires larger divider. * assume 100MHz requires larger divider.
*/ */
adiv = AUDIO_DIV_SERCLK_8; adiv = AUDIO_DIV_SERCLK_8;
if (mode->clock > 100000) if (mode_clock > 100000)
adiv++; /* AUDIO_DIV_SERCLK_16 */ adiv++; /* AUDIO_DIV_SERCLK_16 */
/* S/PDIF asks for a larger divider */ /* 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 */ adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */
reg_write(priv, REG_AUDIO_DIV, adiv); reg_write(priv, REG_AUDIO_DIV, adiv);
...@@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
* This is the approximate value of N, which happens to be * This is the approximate value of N, which happens to be
* the recommended values for non-coherent clocks. * 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 */ /* Write the CTS and N values */
buf[0] = 0x44; buf[0] = 0x44;
...@@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv,
reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
reg_clear(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 */ /* Write the channel status
buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT; * The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because
buf[1] = 0x00; * there is a separate register for each I2S wire.
buf[2] = IEC958_AES3_CON_FS_NOTID; */
buf[3] = IEC958_AES4_CON_ORIGFS_NOTID | buf[0] = params->status[0];
IEC958_AES4_CON_MAX_WORDLEN_24; 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); reg_write_range(priv, REG_CH_STAT_B(0), buf, 4);
tda998x_audio_mute(priv, true); tda998x_audio_mute(priv, true);
msleep(20); msleep(20);
tda998x_audio_mute(priv, false); tda998x_audio_mute(priv, false);
/* Write the audio information packet */ return tda998x_write_aif(priv, &params->cea);
tda998x_write_aif(priv, p);
} }
/* DRM encoder functions */ /* DRM encoder functions */
...@@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv, ...@@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
VIP_CNTRL_2_SWAP_F(p->swap_f) | VIP_CNTRL_2_SWAP_F(p->swap_f) |
(p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); (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) static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
...@@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, ...@@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
tda998x_write_avi(priv, adjusted_mode); tda998x_write_avi(priv, adjusted_mode);
if (priv->params.audio_cfg) if (priv->audio_params.format != AFMT_UNUSED) {
tda998x_configure_audio(priv, adjusted_mode, mutex_lock(&priv->audio_mutex);
&priv->params); 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) ...@@ -1159,6 +1180,8 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector, edid); drm_mode_connector_update_edid_property(connector, edid);
n = drm_add_edid_modes(connector, edid); n = drm_add_edid_modes(connector, edid);
priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
drm_edid_to_eld(connector, edid);
kfree(edid); kfree(edid);
return n; return n;
...@@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv) ...@@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv)
cec_write(priv, REG_CEC_RXSHPDINTENA, 0); cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); 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) if (priv->hdmi->irq)
free_irq(priv->hdmi->irq, priv); free_irq(priv->hdmi->irq, priv);
...@@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv) ...@@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv)
i2c_unregister_device(priv->cec); 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 */ /* 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) static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
{ {
struct device_node *np = client->dev.of_node; struct device_node *np = client->dev.of_node;
...@@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
if (!np) if (!np)
return 0; /* non-DT */ return 0; /* non-DT */
/* get the optional video properties */ /* get the device tree parameters */
ret = of_property_read_u32(np, "video-ports", &video); ret = of_property_read_u32(np, "video-ports", &video);
if (ret == 0) { if (ret == 0) {
priv->vip_cntrl_0 = video >> 16; priv->vip_cntrl_0 = video >> 16;
...@@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
priv->vip_cntrl_2 = video; 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: fail:
/* if encoder_init fails, the encoder slave is never registered, /* if encoder_init fails, the encoder slave is never registered,
* so cleanup here: * so cleanup here:
......
#ifndef __DRM_I2C_TDA998X_H__ #ifndef __DRM_I2C_TDA998X_H__
#define __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 { struct tda998x_encoder_params {
u8 swap_b:3; u8 swap_b:3;
u8 mirr_b:1; u8 mirr_b:1;
...@@ -15,16 +33,7 @@ struct tda998x_encoder_params { ...@@ -15,16 +33,7 @@ struct tda998x_encoder_params {
u8 swap_e:3; u8 swap_e:3;
u8 mirr_e:1; u8 mirr_e:1;
u8 audio_cfg; struct tda998x_audio_params audio_params;
u8 audio_clk_cfg;
u8 audio_frame[6];
enum {
AFMT_SPDIF,
AFMT_I2S
} audio_format;
unsigned audio_sample_rate;
}; };
#endif #endif
#ifndef _DT_BINDINGS_TDA998X_H
#define _DT_BINDINGS_TDA998X_H
#define TDA998x_SPDIF 1
#define TDA998x_I2S 2
#endif /*_DT_BINDINGS_TDA998X_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment