Add support for audio on I2S based DesignWare HDMI controllers.
Relnotes: yes
This commit is contained in:
parent
7fd348080f
commit
6443acaa6c
@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/videomode/videomode.h>
|
||||
#include <dev/videomode/edidvar.h>
|
||||
|
||||
#include <arm/freescale/imx/imx_ccmvar.h>
|
||||
#include <arm/freescale/imx/imx_iomuxvar.h>
|
||||
|
@ -53,8 +53,23 @@ __FBSDID("$FreeBSD$");
|
||||
#include "hdmi_if.h"
|
||||
|
||||
#define I2C_DDC_ADDR (0x50 << 1)
|
||||
#define I2C_DDC_SEGADDR (0x30 << 1)
|
||||
#define EDID_LENGTH 0x80
|
||||
|
||||
#define EXT_TAG 0x00
|
||||
#define CEA_TAG_ID 0x02
|
||||
#define CEA_DTD 0x03
|
||||
#define DTD_BASIC_AUDIO (1 << 6)
|
||||
#define CEA_REV 0x02
|
||||
#define CEA_DATA_OFF 0x03
|
||||
#define CEA_DATA_START 4
|
||||
#define BLOCK_TAG(x) (((x) >> 5) & 0x7)
|
||||
#define BLOCK_TAG_VSDB 3
|
||||
#define BLOCK_LEN(x) ((x) & 0x1f)
|
||||
#define HDMI_VSDB_MINLEN 5
|
||||
#define HDMI_OUI "\x03\x0c\x00"
|
||||
#define HDMI_OUI_LEN 3
|
||||
|
||||
static void
|
||||
dwc_hdmi_phy_wait_i2c_done(struct dwc_hdmi_softc *sc, int msec)
|
||||
{
|
||||
@ -122,7 +137,7 @@ dwc_hdmi_av_composer(struct dwc_hdmi_softc *sc)
|
||||
HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
|
||||
|
||||
/* TODO: implement HDMI part */
|
||||
is_dvi = 1;
|
||||
is_dvi = sc->sc_has_audio == 0;
|
||||
inv_val |= (is_dvi ?
|
||||
HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
|
||||
HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
|
||||
@ -418,6 +433,70 @@ dwc_hdmi_enable_video_path(struct dwc_hdmi_softc *sc)
|
||||
WR1(sc, HDMI_MC_CLKDIS, clkdis);
|
||||
}
|
||||
|
||||
static void
|
||||
dwc_hdmi_configure_audio(struct dwc_hdmi_softc *sc)
|
||||
{
|
||||
unsigned int n;
|
||||
uint8_t val;
|
||||
|
||||
if (sc->sc_has_audio == 0)
|
||||
return;
|
||||
|
||||
/* The following values are for 48 kHz */
|
||||
switch (sc->sc_mode.dot_clock) {
|
||||
case 25170:
|
||||
n = 6864;
|
||||
break;
|
||||
case 27020:
|
||||
n = 6144;
|
||||
break;
|
||||
case 74170:
|
||||
n = 11648;
|
||||
break;
|
||||
case 148350:
|
||||
n = 5824;
|
||||
break;
|
||||
default:
|
||||
n = 6144;
|
||||
break;
|
||||
}
|
||||
|
||||
WR1(sc, HDMI_AUD_N1, (n >> 0) & 0xff);
|
||||
WR1(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
|
||||
WR1(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
|
||||
|
||||
val = RD1(sc, HDMI_AUD_CTS3);
|
||||
val &= ~(HDMI_AUD_CTS3_N_SHIFT_MASK | HDMI_AUD_CTS3_CTS_MANUAL);
|
||||
WR1(sc, HDMI_AUD_CTS3, val);
|
||||
|
||||
val = RD1(sc, HDMI_AUD_CONF0);
|
||||
val &= ~HDMI_AUD_CONF0_INTERFACE_MASK;
|
||||
val |= HDMI_AUD_CONF0_INTERFACE_IIS;
|
||||
val &= ~HDMI_AUD_CONF0_I2SINEN_MASK;
|
||||
val |= HDMI_AUD_CONF0_I2SINEN_CH2;
|
||||
WR1(sc, HDMI_AUD_CONF0, val);
|
||||
|
||||
val = RD1(sc, HDMI_AUD_CONF1);
|
||||
val &= ~HDMI_AUD_CONF1_DATAMODE_MASK;
|
||||
val |= HDMI_AUD_CONF1_DATAMODE_IIS;
|
||||
val &= ~HDMI_AUD_CONF1_DATWIDTH_MASK;
|
||||
val |= HDMI_AUD_CONF1_DATWIDTH_16BIT;
|
||||
WR1(sc, HDMI_AUD_CONF1, val);
|
||||
|
||||
WR1(sc, HDMI_AUD_INPUTCLKFS, HDMI_AUD_INPUTCLKFS_64);
|
||||
|
||||
WR1(sc, HDMI_FC_AUDICONF0, 1 << 4); /* CC=1 */
|
||||
WR1(sc, HDMI_FC_AUDICONF1, 0);
|
||||
WR1(sc, HDMI_FC_AUDICONF2, 0); /* CA=0 */
|
||||
WR1(sc, HDMI_FC_AUDICONF3, 0);
|
||||
WR1(sc, HDMI_FC_AUDSV, 0xee); /* channels valid */
|
||||
|
||||
/* Enable audio clock */
|
||||
val = RD1(sc, HDMI_MC_CLKDIS);
|
||||
val &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
|
||||
WR1(sc, HDMI_MC_CLKDIS, val);
|
||||
}
|
||||
|
||||
static void
|
||||
dwc_hdmi_video_packetize(struct dwc_hdmi_softc *sc)
|
||||
{
|
||||
@ -552,11 +631,15 @@ static int
|
||||
dwc_hdmi_set_mode(struct dwc_hdmi_softc *sc)
|
||||
{
|
||||
|
||||
/* XXX */
|
||||
sc->sc_has_audio = 1;
|
||||
|
||||
dwc_hdmi_disable_overflow_interrupts(sc);
|
||||
dwc_hdmi_av_composer(sc);
|
||||
dwc_hdmi_phy_init(sc);
|
||||
dwc_hdmi_enable_video_path(sc);
|
||||
/* TODO: AVI infoframes */
|
||||
dwc_hdmi_configure_audio(sc);
|
||||
/* TODO: dwc_hdmi_config_avi(sc); */
|
||||
dwc_hdmi_video_packetize(sc);
|
||||
/* TODO: dwc_hdmi_video_csc(sc); */
|
||||
dwc_hdmi_video_sample(sc);
|
||||
@ -567,14 +650,17 @@ dwc_hdmi_set_mode(struct dwc_hdmi_softc *sc)
|
||||
}
|
||||
|
||||
static int
|
||||
hdmi_edid_read(struct dwc_hdmi_softc *sc, uint8_t **edid, uint32_t *edid_len)
|
||||
hdmi_edid_read(struct dwc_hdmi_softc *sc, int block, uint8_t **edid,
|
||||
uint32_t *edid_len)
|
||||
{
|
||||
device_t i2c_dev;
|
||||
int result;
|
||||
uint8_t addr = 0;
|
||||
uint8_t addr = block & 1 ? EDID_LENGTH : 0;
|
||||
uint8_t segment = block >> 1;
|
||||
struct iic_msg msg[] = {
|
||||
{ 0, IIC_M_WR, 1, &addr },
|
||||
{ 0, IIC_M_RD, EDID_LENGTH, NULL}
|
||||
{ I2C_DDC_SEGADDR, IIC_M_WR, 1, &segment },
|
||||
{ I2C_DDC_ADDR, IIC_M_WR, 1, &addr },
|
||||
{ I2C_DDC_ADDR, IIC_M_RD, EDID_LENGTH, sc->sc_edid }
|
||||
};
|
||||
|
||||
*edid = NULL;
|
||||
@ -588,12 +674,10 @@ hdmi_edid_read(struct dwc_hdmi_softc *sc, uint8_t **edid, uint32_t *edid_len)
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_printf(sc->sc_dev, "reading EDID from %s, addr %02x\n",
|
||||
device_get_nameunit(i2c_dev), I2C_DDC_ADDR/2);
|
||||
|
||||
msg[0].slave = I2C_DDC_ADDR;
|
||||
msg[1].slave = I2C_DDC_ADDR;
|
||||
msg[1].buf = sc->sc_edid;
|
||||
if (bootverbose)
|
||||
device_printf(sc->sc_dev,
|
||||
"reading EDID from %s, block %d, addr %02x\n",
|
||||
device_get_nameunit(i2c_dev), block, I2C_DDC_ADDR/2);
|
||||
|
||||
result = iicbus_request_bus(i2c_dev, sc->sc_dev, IIC_INTRWAIT);
|
||||
|
||||
@ -602,7 +686,7 @@ hdmi_edid_read(struct dwc_hdmi_softc *sc, uint8_t **edid, uint32_t *edid_len)
|
||||
return (result);
|
||||
}
|
||||
|
||||
result = iicbus_transfer(i2c_dev, msg, 2);
|
||||
result = iicbus_transfer(i2c_dev, msg, 3);
|
||||
iicbus_release_bus(i2c_dev, sc->sc_dev);
|
||||
|
||||
if (result) {
|
||||
@ -670,11 +754,84 @@ dwc_hdmi_init(device_t dev)
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
dwc_hdmi_detect_hdmi_vsdb(uint8_t *edid)
|
||||
{
|
||||
int off, p, btag, blen;
|
||||
|
||||
if (edid[EXT_TAG] != CEA_TAG_ID)
|
||||
return (0);
|
||||
|
||||
off = edid[CEA_DATA_OFF];
|
||||
|
||||
/* CEA data block collection starts at byte 4 */
|
||||
if (off <= CEA_DATA_START)
|
||||
return (0);
|
||||
|
||||
/* Parse the CEA data blocks */
|
||||
for (p = CEA_DATA_START; p < off;) {
|
||||
btag = BLOCK_TAG(edid[p]);
|
||||
blen = BLOCK_LEN(edid[p]);
|
||||
|
||||
/* Make sure the length is sane */
|
||||
if (p + blen + 1 > off)
|
||||
break;
|
||||
|
||||
/* Look for a VSDB with the HDMI 24-bit IEEE registration ID */
|
||||
if (btag == BLOCK_TAG_VSDB && blen >= HDMI_VSDB_MINLEN &&
|
||||
memcmp(&edid[p + 1], HDMI_OUI, HDMI_OUI_LEN) == 0)
|
||||
return (1);
|
||||
|
||||
/* Next data block */
|
||||
p += (1 + blen);
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
dwc_hdmi_detect_hdmi(struct dwc_hdmi_softc *sc)
|
||||
{
|
||||
uint8_t *edid;
|
||||
uint32_t edid_len;
|
||||
int block;
|
||||
|
||||
sc->sc_has_audio = 0;
|
||||
|
||||
/* Scan through extension blocks, looking for a CEA-861 block */
|
||||
for (block = 1; block <= sc->sc_edid_info.edid_ext_block_count;
|
||||
block++) {
|
||||
if (hdmi_edid_read(sc, block, &edid, &edid_len) != 0)
|
||||
return;
|
||||
if (dwc_hdmi_detect_hdmi_vsdb(edid) != 0) {
|
||||
if (bootverbose)
|
||||
device_printf(sc->sc_dev,
|
||||
"enabling audio support\n");
|
||||
sc->sc_has_audio =
|
||||
(edid[CEA_DTD] & DTD_BASIC_AUDIO) != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dwc_hdmi_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len)
|
||||
{
|
||||
struct dwc_hdmi_softc *sc;
|
||||
int error;
|
||||
|
||||
return (hdmi_edid_read(device_get_softc(dev), edid, edid_len));
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
memset(&sc->sc_edid_info, 0, sizeof(sc->sc_edid_info));
|
||||
|
||||
error = hdmi_edid_read(sc, 0, edid, edid_len);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
edid_parse(*edid, &sc->sc_edid_info);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
@ -685,6 +842,8 @@ dwc_hdmi_set_videomode(device_t dev, const struct videomode *mode)
|
||||
sc = device_get_softc(dev);
|
||||
memcpy(&sc->sc_mode, mode, sizeof(*mode));
|
||||
|
||||
dwc_hdmi_detect_hdmi(sc);
|
||||
|
||||
dwc_hdmi_set_mode(sc);
|
||||
|
||||
return (0);
|
||||
|
@ -40,6 +40,9 @@ struct dwc_hdmi_softc {
|
||||
uint8_t sc_edid_len;
|
||||
struct intr_config_hook sc_mode_hook;
|
||||
struct videomode sc_mode;
|
||||
|
||||
struct edid_info sc_edid_info;
|
||||
int sc_has_audio;
|
||||
};
|
||||
|
||||
static inline uint8_t
|
||||
|
@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/extres/clk/clk.h>
|
||||
|
||||
#include <dev/videomode/videomode.h>
|
||||
#include <dev/videomode/edidvar.h>
|
||||
|
||||
#include <dev/hdmi/dwc_hdmi.h>
|
||||
|
||||
|
@ -250,6 +250,7 @@
|
||||
#define HDMI_FC_SPDDEVICEINF 0x1062
|
||||
#define HDMI_FC_AUDSCONF 0x1063
|
||||
#define HDMI_FC_AUDSSTAT 0x1064
|
||||
#define HDMI_FC_AUDSV 0x1065
|
||||
#define HDMI_FC_DATACH0FILL 0x1070
|
||||
#define HDMI_FC_DATACH1FILL 0x1071
|
||||
#define HDMI_FC_DATACH2FILL 0x1072
|
||||
@ -472,7 +473,24 @@
|
||||
|
||||
/* Audio Sampler Registers */
|
||||
#define HDMI_AUD_CONF0 0x3100
|
||||
#define HDMI_AUD_CONF0_INTERFACE_MASK 0x20
|
||||
#define HDMI_AUD_CONF0_INTERFACE_IIS 0x20
|
||||
#define HDMI_AUD_CONF0_INTERFACE_SPDIF 0x00
|
||||
#define HDMI_AUD_CONF0_I2SINEN_MASK 0x0f
|
||||
#define HDMI_AUD_CONF0_I2SINEN_CH2 0x01
|
||||
#define HDMI_AUD_CONF0_I2SINEN_CH4 0x03
|
||||
#define HDMI_AUD_CONF0_I2SINEN_CH6 0x07
|
||||
#define HDMI_AUD_CONF0_I2SINEN_CH8 0x0f
|
||||
#define HDMI_AUD_CONF1 0x3101
|
||||
#define HDMI_AUD_CONF1_DATAMODE_MASK 0xe0
|
||||
#define HDMI_AUD_CONF1_DATAMODE_IIS 0x00
|
||||
#define HDMI_AUD_CONF1_DATAMODE_RIGHT_J 0x20
|
||||
#define HDMI_AUD_CONF1_DATAMODE_LEFT_J 0x40
|
||||
#define HDMI_AUD_CONF1_DATAMODE_BURST_1 0x60
|
||||
#define HDMI_AUD_CONF1_DATAMDOE_BURST_2 0x80
|
||||
#define HDMI_AUD_CONF1_DATWIDTH_MASK 0x1f
|
||||
#define HDMI_AUD_CONF1_DATWIDTH_16BIT 16
|
||||
#define HDMI_AUD_CONF1_DATWIDTH_24BIT 24
|
||||
#define HDMI_AUD_INT 0x3102
|
||||
#define HDMI_AUD_CONF2 0x3103
|
||||
#define HDMI_AUD_N1 0x3200
|
||||
@ -481,7 +499,14 @@
|
||||
#define HDMI_AUD_CTS1 0x3203
|
||||
#define HDMI_AUD_CTS2 0x3204
|
||||
#define HDMI_AUD_CTS3 0x3205
|
||||
#define HDMI_AUD_CTS3_N_SHIFT_MASK 0xe0
|
||||
#define HDMI_AUD_CTS3_CTS_MANUAL 0x10
|
||||
#define HDMI_AUD_INPUTCLKFS 0x3206
|
||||
#define HDMI_AUD_INPUTCLKFS_128 0
|
||||
#define HDMI_AUD_INPUTCLKFS_256 1
|
||||
#define HDMI_AUD_INPUTCLKFS_512 2
|
||||
#define HDMI_AUD_INPUTCLKFS_1024 3
|
||||
#define HDMI_AUD_INPUTCLKFS_64 4
|
||||
#define HDMI_AUD_SPDIFINT 0x3302
|
||||
#define HDMI_AUD_CONF0_HBR 0x3400
|
||||
#define HDMI_AUD_HBR_STATUS 0x3401
|
||||
|
Loading…
Reference in New Issue
Block a user