Commit 7625e052 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

These updates:
* improve the robustness of the driver wrt races
* improve the compliance for sending infoframes and audio
* re-organise the function order in the driver to group like functions
  together.  (This unfortunately causes a conflict with the change in
  drm-misc, but it should be trivial to solve, although it looks more
  scarey than it really is - sfr has already sent two reports about
  this, one earlier today.)
* simplify tda998x_audio_get_eld and DPMS handling
* power down sections of the chip that we never use
* add some initial preparation for supporting the CEC driver

* 'drm-tda998x-devel' of git://git.armlinux.org.uk/~rmk/linux-arm:
  drm/i2c: tda998x: fix spelling mistake
  drm/i2c: tda998x: allow sharing of the CEC device accesses
  drm/i2c: tda998x: allow interrupt to be shared
  drm/i2c: tda998x: power down pre-filter and color conversion
  drm/i2c: tda998x: switch to boolean is_on
  drm/i2c: tda998x: remove complexity from tda998x_audio_get_eld()
  drm/i2c: tda998x: group audio functions together
  drm/i2c: tda998x: separate connector initialisation
  drm/i2c: tda998x: group connector functions and funcs together
  drm/i2c: tda998x: move and rename tda998x_encoder_set_config()
  drm/i2c: tda998x: correct function name in comments
  drm/i2c: tda998x: only enable audio if supported by sink
  drm/i2c: tda998x: only configure infoframes and audio if supported
  drm/i2c: tda998x: avoid race when programming audio
  drm/i2c: tda998x: avoid racy access to mode clock
  drm/i2c: tda998x: avoid race in tda998x_encoder_mode_set()
  drm/i2c: tda998x: move audio mutex initialisation
parents 4d5304d8 9b2502b6
...@@ -41,12 +41,15 @@ struct tda998x_priv { ...@@ -41,12 +41,15 @@ struct tda998x_priv {
struct i2c_client *hdmi; struct i2c_client *hdmi;
struct mutex mutex; struct mutex mutex;
u16 rev; u16 rev;
u8 cec_addr;
u8 current_page; u8 current_page;
int dpms; bool is_on;
bool is_hdmi_sink; bool supports_infoframes;
bool sink_has_audio;
u8 vip_cntrl_0; u8 vip_cntrl_0;
u8 vip_cntrl_1; u8 vip_cntrl_1;
u8 vip_cntrl_2; u8 vip_cntrl_2;
unsigned long tmds_clock;
struct tda998x_audio_params audio_params; struct tda998x_audio_params audio_params;
struct platform_device *audio_pdev; struct platform_device *audio_pdev;
...@@ -105,6 +108,8 @@ struct tda998x_priv { ...@@ -105,6 +108,8 @@ struct tda998x_priv {
# define I2C_MASTER_DIS_FILT (1 << 1) # define I2C_MASTER_DIS_FILT (1 << 1)
# define I2C_MASTER_APP_STRT_LAT (1 << 2) # define I2C_MASTER_APP_STRT_LAT (1 << 2)
#define REG_FEAT_POWERDOWN REG(0x00, 0x0e) /* read/write */ #define REG_FEAT_POWERDOWN REG(0x00, 0x0e) /* read/write */
# define FEAT_POWERDOWN_PREFILT BIT(0)
# define FEAT_POWERDOWN_CSC BIT(1)
# define FEAT_POWERDOWN_SPDIF (1 << 3) # define FEAT_POWERDOWN_SPDIF (1 << 3)
#define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ #define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */
#define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ #define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */
...@@ -370,35 +375,46 @@ struct tda998x_priv { ...@@ -370,35 +375,46 @@ struct tda998x_priv {
static void static void
cec_write(struct tda998x_priv *priv, u16 addr, u8 val) cec_write(struct tda998x_priv *priv, u16 addr, u8 val)
{ {
struct i2c_client *client = priv->cec;
u8 buf[] = {addr, val}; u8 buf[] = {addr, val};
struct i2c_msg msg = {
.addr = priv->cec_addr,
.len = 2,
.buf = buf,
};
int ret; int ret;
ret = i2c_master_send(client, buf, sizeof(buf)); ret = i2c_transfer(priv->hdmi->adapter, &msg, 1);
if (ret < 0) if (ret < 0)
dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr); dev_err(&priv->hdmi->dev, "Error %d writing to cec:0x%x\n",
ret, addr);
} }
static u8 static u8
cec_read(struct tda998x_priv *priv, u8 addr) cec_read(struct tda998x_priv *priv, u8 addr)
{ {
struct i2c_client *client = priv->cec;
u8 val; u8 val;
struct i2c_msg msg[2] = {
{
.addr = priv->cec_addr,
.len = 1,
.buf = &addr,
}, {
.addr = priv->cec_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = &val,
},
};
int ret; int ret;
ret = i2c_master_send(client, &addr, sizeof(addr)); ret = i2c_transfer(priv->hdmi->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) if (ret < 0) {
goto fail; dev_err(&priv->hdmi->dev, "Error %d reading from cec:0x%x\n",
ret, addr);
ret = i2c_master_recv(client, &val, sizeof(val)); val = 0;
if (ret < 0) }
goto fail;
return val; return val;
fail:
dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
return 0;
} }
static int static int
...@@ -579,9 +595,9 @@ tda998x_reset(struct tda998x_priv *priv) ...@@ -579,9 +595,9 @@ tda998x_reset(struct tda998x_priv *priv)
* HPD assertion: it needs a delay of 100ms to avoid timing out while * HPD assertion: it needs a delay of 100ms to avoid timing out while
* trying to read EDID data. * trying to read EDID data.
* *
* However, tda998x_encoder_get_modes() may be called at any moment * However, tda998x_connector_get_modes() may be called at any moment
* after tda998x_connector_detect() indicates that we are connected, so * after tda998x_connector_detect() indicates that we are connected, so
* we need to delay probing modes in tda998x_encoder_get_modes() after * we need to delay probing modes in tda998x_connector_get_modes() after
* we have seen a HPD inactive->active transition. This code implements * we have seen a HPD inactive->active transition. This code implements
* that delay. * that delay.
*/ */
...@@ -630,28 +646,30 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) ...@@ -630,28 +646,30 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
bool handled = false; bool handled = false;
sta = cec_read(priv, REG_CEC_INTSTATUS); sta = cec_read(priv, REG_CEC_INTSTATUS);
cec = cec_read(priv, REG_CEC_RXSHPDINT); if (sta & CEC_INTSTATUS_HDMI) {
lvl = cec_read(priv, REG_CEC_RXSHPDLEV); cec = cec_read(priv, REG_CEC_RXSHPDINT);
flag0 = reg_read(priv, REG_INT_FLAGS_0); lvl = cec_read(priv, REG_CEC_RXSHPDLEV);
flag1 = reg_read(priv, REG_INT_FLAGS_1); flag0 = reg_read(priv, REG_INT_FLAGS_0);
flag2 = reg_read(priv, REG_INT_FLAGS_2); flag1 = reg_read(priv, REG_INT_FLAGS_1);
DRM_DEBUG_DRIVER( flag2 = reg_read(priv, REG_INT_FLAGS_2);
"tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n", DRM_DEBUG_DRIVER(
sta, cec, lvl, flag0, flag1, flag2); "tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n",
sta, cec, lvl, flag0, flag1, flag2);
if (cec & CEC_RXSHPDINT_HPD) {
if (lvl & CEC_RXSHPDLEV_HPD) if (cec & CEC_RXSHPDINT_HPD) {
tda998x_edid_delay_start(priv); if (lvl & CEC_RXSHPDLEV_HPD)
else tda998x_edid_delay_start(priv);
schedule_work(&priv->detect_work); else
schedule_work(&priv->detect_work);
handled = true;
} handled = true;
}
if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
priv->wq_edid_wait = 0; priv->wq_edid_wait = 0;
wake_up(&priv->wq_edid); wake_up(&priv->wq_edid);
handled = true; handled = true;
}
} }
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
...@@ -700,6 +718,8 @@ tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode) ...@@ -700,6 +718,8 @@ tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode)
tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, &frame); tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, &frame);
} }
/* Audio support */
static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
{ {
if (on) { if (on) {
...@@ -713,8 +733,7 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) ...@@ -713,8 +733,7 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
static int static int
tda998x_configure_audio(struct tda998x_priv *priv, tda998x_configure_audio(struct tda998x_priv *priv,
struct tda998x_audio_params *params, 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;
...@@ -771,7 +790,7 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -771,7 +790,7 @@ 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 (priv->tmds_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 */
...@@ -819,269 +838,164 @@ tda998x_configure_audio(struct tda998x_priv *priv, ...@@ -819,269 +838,164 @@ tda998x_configure_audio(struct tda998x_priv *priv,
return tda998x_write_aif(priv, &params->cea); return tda998x_write_aif(priv, &params->cea);
} }
/* DRM encoder functions */ static int tda998x_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
static void tda998x_encoder_set_config(struct tda998x_priv *priv, struct hdmi_codec_params *params)
const struct tda998x_encoder_params *p)
{
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
(p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
VIP_CNTRL_0_SWAP_B(p->swap_b) |
(p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
(p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
VIP_CNTRL_1_SWAP_D(p->swap_d) |
(p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
(p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
VIP_CNTRL_2_SWAP_F(p->swap_f) |
(p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
priv->audio_params = p->audio_params;
}
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{ {
struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); struct tda998x_priv *priv = dev_get_drvdata(dev);
int i, ret;
/* we only care about on or off: */ struct tda998x_audio_params audio = {
if (mode != DRM_MODE_DPMS_ON) .sample_width = params->sample_width,
mode = DRM_MODE_DPMS_OFF; .sample_rate = params->sample_rate,
.cea = params->cea,
};
if (mode == priv->dpms) memcpy(audio.status, params->iec.status,
return; min(sizeof(audio.status), sizeof(params->iec.status)));
switch (mode) { switch (daifmt->fmt) {
case DRM_MODE_DPMS_ON: case HDMI_I2S:
/* enable video ports, audio will be enabled later */ if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
reg_write(priv, REG_ENA_VP_0, 0xff); daifmt->bit_clk_master || daifmt->frame_clk_master) {
reg_write(priv, REG_ENA_VP_1, 0xff); dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
reg_write(priv, REG_ENA_VP_2, 0xff); daifmt->bit_clk_inv, daifmt->frame_clk_inv,
/* set muxing after enabling ports: */ daifmt->bit_clk_master,
reg_write(priv, REG_VIP_CNTRL_0, priv->vip_cntrl_0); daifmt->frame_clk_master);
reg_write(priv, REG_VIP_CNTRL_1, priv->vip_cntrl_1); return -EINVAL;
reg_write(priv, REG_VIP_CNTRL_2, priv->vip_cntrl_2); }
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; break;
case DRM_MODE_DPMS_OFF: case HDMI_SPDIF:
/* disable video ports */ for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
reg_write(priv, REG_ENA_VP_0, 0x00); if (priv->audio_port[i].format == AFMT_SPDIF)
reg_write(priv, REG_ENA_VP_1, 0x00); audio.config = priv->audio_port[i].config;
reg_write(priv, REG_ENA_VP_2, 0x00); audio.format = AFMT_SPDIF;
break; break;
default:
dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
return -EINVAL;
} }
priv->dpms = mode; if (audio.config == 0) {
} dev_err(dev, "%s: No audio configuration found\n", __func__);
return -EINVAL;
}
static int tda998x_connector_mode_valid(struct drm_connector *connector, mutex_lock(&priv->audio_mutex);
struct drm_display_mode *mode) if (priv->supports_infoframes && priv->sink_has_audio)
{ ret = tda998x_configure_audio(priv, &audio);
/* TDA19988 dotclock can go up to 165MHz */ else
struct tda998x_priv *priv = conn_to_tda998x_priv(connector); ret = 0;
if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000)) if (ret == 0)
return MODE_CLOCK_HIGH; priv->audio_params = audio;
if (mode->htotal >= BIT(13)) mutex_unlock(&priv->audio_mutex);
return MODE_BAD_HVALUE;
if (mode->vtotal >= BIT(11)) return ret;
return MODE_BAD_VVALUE;
return MODE_OK;
} }
static void static void tda998x_audio_shutdown(struct device *dev, void *data)
tda998x_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{ {
struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); struct tda998x_priv *priv = dev_get_drvdata(dev);
u16 ref_pix, ref_line, n_pix, n_line;
u16 hs_pix_s, hs_pix_e;
u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
u16 vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e;
u16 vwin1_line_s, vwin1_line_e;
u16 vwin2_line_s, vwin2_line_e;
u16 de_pix_s, de_pix_e;
u8 reg, div, rep;
/*
* Internally TDA998x is using ITU-R BT.656 style sync but
* we get VESA style sync. TDA998x is using a reference pixel
* relative to ITU to sync to the input frame and for output
* sync generation. Currently, we are using reference detection
* from HS/VS, i.e. REFPIX/REFLINE denote frame start sync point
* which is position of rising VS with coincident rising HS.
*
* Now there is some issues to take care of:
* - HDMI data islands require sync-before-active
* - TDA998x register values must be > 0 to be enabled
* - REFLINE needs an additional offset of +1
* - REFPIX needs an addtional offset of +1 for UYUV and +3 for RGB
*
* So we add +1 to all horizontal and vertical register values,
* plus an additional +3 for REFPIX as we are using RGB input only.
*/
n_pix = mode->htotal;
n_line = mode->vtotal;
hs_pix_e = mode->hsync_end - mode->hdisplay; mutex_lock(&priv->audio_mutex);
hs_pix_s = mode->hsync_start - mode->hdisplay;
de_pix_e = mode->htotal;
de_pix_s = mode->htotal - mode->hdisplay;
ref_pix = 3 + hs_pix_s;
/* reg_write(priv, REG_ENA_AP, 0);
* Attached LCD controllers may generate broken sync. Allow
* those to adjust the position of the rising VS edge by adding
* HSKEW to ref_pix.
*/
if (adjusted_mode->flags & DRM_MODE_FLAG_HSKEW)
ref_pix += adjusted_mode->hskew;
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) == 0) { priv->audio_params.format = AFMT_UNUSED;
ref_line = 1 + mode->vsync_start - mode->vdisplay;
vwin1_line_s = mode->vtotal - mode->vdisplay - 1;
vwin1_line_e = vwin1_line_s + mode->vdisplay;
vs1_pix_s = vs1_pix_e = hs_pix_s;
vs1_line_s = mode->vsync_start - mode->vdisplay;
vs1_line_e = vs1_line_s +
mode->vsync_end - mode->vsync_start;
vwin2_line_s = vwin2_line_e = 0;
vs2_pix_s = vs2_pix_e = 0;
vs2_line_s = vs2_line_e = 0;
} else {
ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2;
vwin1_line_s = (mode->vtotal - mode->vdisplay)/2;
vwin1_line_e = vwin1_line_s + mode->vdisplay/2;
vs1_pix_s = vs1_pix_e = hs_pix_s;
vs1_line_s = (mode->vsync_start - mode->vdisplay)/2;
vs1_line_e = vs1_line_s +
(mode->vsync_end - mode->vsync_start)/2;
vwin2_line_s = vwin1_line_s + mode->vtotal/2;
vwin2_line_e = vwin2_line_s + mode->vdisplay/2;
vs2_pix_s = vs2_pix_e = hs_pix_s + mode->htotal/2;
vs2_line_s = vs1_line_s + mode->vtotal/2 ;
vs2_line_e = vs2_line_s +
(mode->vsync_end - mode->vsync_start)/2;
}
div = 148500 / mode->clock; mutex_unlock(&priv->audio_mutex);
if (div != 0) { }
div--;
if (div > 3)
div = 3;
}
/* mute the audio FIFO: */ int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); {
struct tda998x_priv *priv = dev_get_drvdata(dev);
/* set HDMI HDCP mode off: */ mutex_lock(&priv->audio_mutex);
reg_write(priv, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
reg_clear(priv, REG_TX33, TX33_HDMI);
reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0));
/* no pre-filter or interpolator: */ tda998x_audio_mute(priv, enable);
reg_write(priv, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) |
HVF_CNTRL_0_INTPOL(0));
reg_write(priv, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
reg_write(priv, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) |
VIP_CNTRL_4_BLC(0));
reg_clear(priv, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ); mutex_unlock(&priv->audio_mutex);
reg_clear(priv, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR | return 0;
PLL_SERIAL_3_SRL_DE); }
reg_write(priv, REG_SERIALIZER, 0);
reg_write(priv, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0));
/* TODO enable pixel repeat for pixel rates less than 25Msamp/s */ static int tda998x_audio_get_eld(struct device *dev, void *data,
rep = 0; uint8_t *buf, size_t len)
reg_write(priv, REG_RPT_CNTRL, 0); {
reg_write(priv, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | struct tda998x_priv *priv = dev_get_drvdata(dev);
SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
reg_write(priv, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) | mutex_lock(&priv->audio_mutex);
PLL_SERIAL_2_SRL_PR(rep)); memcpy(buf, priv->connector.eld,
min(sizeof(priv->connector.eld), len));
mutex_unlock(&priv->audio_mutex);
/* set color matrix bypass flag: */ return 0;
reg_write(priv, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP | }
MAT_CONTRL_MAT_SC(1));
/* set BIAS tmds value: */ static const struct hdmi_codec_ops audio_codec_ops = {
reg_write(priv, REG_ANA_GENERAL, 0x09); .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,
* Sync on rising HSYNC/VSYNC struct device *dev)
*/ {
reg = VIP_CNTRL_3_SYNC_HS; 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++) {
* TDA19988 requires high-active sync at input stage, if (priv->audio_port[i].format == AFMT_I2S &&
* so invert low-active sync provided by master encoder here priv->audio_port[i].config != 0)
*/ codec_data.i2s = 1;
if (mode->flags & DRM_MODE_FLAG_NHSYNC) if (priv->audio_port[i].format == AFMT_SPDIF &&
reg |= VIP_CNTRL_3_H_TGL; priv->audio_port[i].config != 0)
if (mode->flags & DRM_MODE_FLAG_NVSYNC) codec_data.spdif = 1;
reg |= VIP_CNTRL_3_V_TGL; }
reg_write(priv, REG_VIP_CNTRL_3, reg);
reg_write(priv, REG_VIDFORMAT, 0x00); priv->audio_pdev = platform_device_register_data(
reg_write16(priv, REG_REFPIX_MSB, ref_pix); dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
reg_write16(priv, REG_REFLINE_MSB, ref_line); &codec_data, sizeof(codec_data));
reg_write16(priv, REG_NPIX_MSB, n_pix);
reg_write16(priv, REG_NLINE_MSB, n_line);
reg_write16(priv, REG_VS_LINE_STRT_1_MSB, vs1_line_s);
reg_write16(priv, REG_VS_PIX_STRT_1_MSB, vs1_pix_s);
reg_write16(priv, REG_VS_LINE_END_1_MSB, vs1_line_e);
reg_write16(priv, REG_VS_PIX_END_1_MSB, vs1_pix_e);
reg_write16(priv, REG_VS_LINE_STRT_2_MSB, vs2_line_s);
reg_write16(priv, REG_VS_PIX_STRT_2_MSB, vs2_pix_s);
reg_write16(priv, REG_VS_LINE_END_2_MSB, vs2_line_e);
reg_write16(priv, REG_VS_PIX_END_2_MSB, vs2_pix_e);
reg_write16(priv, REG_HS_PIX_START_MSB, hs_pix_s);
reg_write16(priv, REG_HS_PIX_STOP_MSB, hs_pix_e);
reg_write16(priv, REG_VWIN_START_1_MSB, vwin1_line_s);
reg_write16(priv, REG_VWIN_END_1_MSB, vwin1_line_e);
reg_write16(priv, REG_VWIN_START_2_MSB, vwin2_line_s);
reg_write16(priv, REG_VWIN_END_2_MSB, vwin2_line_e);
reg_write16(priv, REG_DE_START_MSB, de_pix_s);
reg_write16(priv, REG_DE_STOP_MSB, de_pix_e);
if (priv->rev == TDA19988) { return PTR_ERR_OR_ZERO(priv->audio_pdev);
/* let incoming pixels fill the active space (if any) */ }
reg_write(priv, REG_ENABLE_SPACE, 0x00);
}
/* /* DRM connector functions */
* Always generate sync polarity relative to input sync and
* revert input stage toggled sync at output stage
*/
reg = TBG_CNTRL_1_DWIN_DIS | TBG_CNTRL_1_TGL_EN;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
reg |= TBG_CNTRL_1_H_TGL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
reg |= TBG_CNTRL_1_V_TGL;
reg_write(priv, REG_TBG_CNTRL_1, reg);
/* must be last register set: */ static int tda998x_connector_dpms(struct drm_connector *connector, int mode)
reg_write(priv, REG_TBG_CNTRL_0, 0); {
if (drm_core_check_feature(connector->dev, DRIVER_ATOMIC))
return drm_atomic_helper_connector_dpms(connector, mode);
else
return drm_helper_connector_dpms(connector, mode);
}
/* Only setup the info frames if the sink is HDMI */ static int tda998x_connector_fill_modes(struct drm_connector *connector,
if (priv->is_hdmi_sink) { uint32_t maxX, uint32_t maxY)
/* We need to turn HDMI HDCP stuff on to get audio through */ {
reg &= ~TBG_CNTRL_1_DWIN_DIS; struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
reg_write(priv, REG_TBG_CNTRL_1, reg); int ret;
reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
reg_set(priv, REG_TX33, TX33_HDMI);
tda998x_write_avi(priv, adjusted_mode); mutex_lock(&priv->audio_mutex);
ret = drm_helper_probe_single_connector_modes(connector, maxX, maxY);
if (priv->audio_params.format != AFMT_UNUSED) { if (connector->edid_blob_ptr) {
mutex_lock(&priv->audio_mutex); struct edid *edid = (void *)connector->edid_blob_ptr->data;
tda998x_configure_audio(priv,
&priv->audio_params, priv->sink_has_audio = drm_detect_monitor_audio(edid);
adjusted_mode->clock); } else {
mutex_unlock(&priv->audio_mutex); priv->sink_has_audio = false;
}
} }
mutex_unlock(&priv->audio_mutex);
return ret;
} }
static enum drm_connector_status static enum drm_connector_status
...@@ -1094,6 +1008,21 @@ tda998x_connector_detect(struct drm_connector *connector, bool force) ...@@ -1094,6 +1008,21 @@ tda998x_connector_detect(struct drm_connector *connector, bool force)
connector_status_disconnected; connector_status_disconnected;
} }
static void tda998x_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs tda998x_connector_funcs = {
.dpms = tda998x_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.fill_modes = tda998x_connector_fill_modes,
.detect = tda998x_connector_detect,
.destroy = tda998x_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
{ {
struct tda998x_priv *priv = data; struct tda998x_priv *priv = data;
...@@ -1179,7 +1108,6 @@ static int tda998x_connector_get_modes(struct drm_connector *connector) ...@@ -1179,7 +1108,6 @@ 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);
drm_edid_to_eld(connector, edid); drm_edid_to_eld(connector, edid);
kfree(edid); kfree(edid);
...@@ -1187,172 +1115,323 @@ static int tda998x_connector_get_modes(struct drm_connector *connector) ...@@ -1187,172 +1115,323 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
return n; return n;
} }
static void tda998x_encoder_set_polling(struct tda998x_priv *priv, static int tda998x_connector_mode_valid(struct drm_connector *connector,
struct drm_connector *connector) struct drm_display_mode *mode)
{
/* TDA19988 dotclock can go up to 165MHz */
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000))
return MODE_CLOCK_HIGH;
if (mode->htotal >= BIT(13))
return MODE_BAD_HVALUE;
if (mode->vtotal >= BIT(11))
return MODE_BAD_VVALUE;
return MODE_OK;
}
static struct drm_encoder *
tda998x_connector_best_encoder(struct drm_connector *connector)
{
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
return &priv->encoder;
}
static
const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
.get_modes = tda998x_connector_get_modes,
.mode_valid = tda998x_connector_mode_valid,
.best_encoder = tda998x_connector_best_encoder,
};
static int tda998x_connector_init(struct tda998x_priv *priv,
struct drm_device *drm)
{ {
struct drm_connector *connector = &priv->connector;
int ret;
connector->interlace_allowed = 1;
if (priv->hdmi->irq) if (priv->hdmi->irq)
connector->polled = DRM_CONNECTOR_POLL_HPD; connector->polled = DRM_CONNECTOR_POLL_HPD;
else else
connector->polled = DRM_CONNECTOR_POLL_CONNECT | connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT; DRM_CONNECTOR_POLL_DISCONNECT;
drm_connector_helper_add(connector, &tda998x_connector_helper_funcs);
ret = drm_connector_init(drm, connector, &tda998x_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
if (ret)
return ret;
drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
return 0;
} }
static void tda998x_destroy(struct tda998x_priv *priv) /* DRM encoder functions */
static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{ {
/* disable all IRQs and free the IRQ handler */ struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
cec_write(priv, REG_CEC_RXSHPDINTENA, 0); bool on;
reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
if (priv->audio_pdev) /* we only care about on or off: */
platform_device_unregister(priv->audio_pdev); on = mode == DRM_MODE_DPMS_ON;
if (priv->hdmi->irq) if (on == priv->is_on)
free_irq(priv->hdmi->irq, priv); return;
del_timer_sync(&priv->edid_delay_timer); if (on) {
cancel_work_sync(&priv->detect_work); /* enable video ports, audio will be enabled later */
reg_write(priv, REG_ENA_VP_0, 0xff);
reg_write(priv, REG_ENA_VP_1, 0xff);
reg_write(priv, REG_ENA_VP_2, 0xff);
/* set muxing after enabling ports: */
reg_write(priv, REG_VIP_CNTRL_0, priv->vip_cntrl_0);
reg_write(priv, REG_VIP_CNTRL_1, priv->vip_cntrl_1);
reg_write(priv, REG_VIP_CNTRL_2, priv->vip_cntrl_2);
i2c_unregister_device(priv->cec); priv->is_on = true;
} else {
/* disable video ports */
reg_write(priv, REG_ENA_VP_0, 0x00);
reg_write(priv, REG_ENA_VP_1, 0x00);
reg_write(priv, REG_ENA_VP_2, 0x00);
priv->is_on = false;
}
} }
static int tda998x_audio_hw_params(struct device *dev, void *data, static void
struct hdmi_codec_daifmt *daifmt, tda998x_encoder_mode_set(struct drm_encoder *encoder,
struct hdmi_codec_params *params) struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{ {
struct tda998x_priv *priv = dev_get_drvdata(dev); struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
int i, ret; u16 ref_pix, ref_line, n_pix, n_line;
struct tda998x_audio_params audio = { u16 hs_pix_s, hs_pix_e;
.sample_width = params->sample_width, u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
.sample_rate = params->sample_rate, u16 vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e;
.cea = params->cea, u16 vwin1_line_s, vwin1_line_e;
}; u16 vwin2_line_s, vwin2_line_e;
u16 de_pix_s, de_pix_e;
u8 reg, div, rep;
if (!priv->encoder.crtc) /*
return -ENODEV; * Internally TDA998x is using ITU-R BT.656 style sync but
* we get VESA style sync. TDA998x is using a reference pixel
* relative to ITU to sync to the input frame and for output
* sync generation. Currently, we are using reference detection
* from HS/VS, i.e. REFPIX/REFLINE denote frame start sync point
* which is position of rising VS with coincident rising HS.
*
* Now there is some issues to take care of:
* - HDMI data islands require sync-before-active
* - TDA998x register values must be > 0 to be enabled
* - REFLINE needs an additional offset of +1
* - REFPIX needs an addtional offset of +1 for UYUV and +3 for RGB
*
* So we add +1 to all horizontal and vertical register values,
* plus an additional +3 for REFPIX as we are using RGB input only.
*/
n_pix = mode->htotal;
n_line = mode->vtotal;
memcpy(audio.status, params->iec.status, hs_pix_e = mode->hsync_end - mode->hdisplay;
min(sizeof(audio.status), sizeof(params->iec.status))); hs_pix_s = mode->hsync_start - mode->hdisplay;
de_pix_e = mode->htotal;
de_pix_s = mode->htotal - mode->hdisplay;
ref_pix = 3 + hs_pix_s;
switch (daifmt->fmt) { /*
case HDMI_I2S: * Attached LCD controllers may generate broken sync. Allow
if (daifmt->bit_clk_inv || daifmt->frame_clk_inv || * those to adjust the position of the rising VS edge by adding
daifmt->bit_clk_master || daifmt->frame_clk_master) { * HSKEW to ref_pix.
dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, */
daifmt->bit_clk_inv, daifmt->frame_clk_inv, if (adjusted_mode->flags & DRM_MODE_FLAG_HSKEW)
daifmt->bit_clk_master, ref_pix += adjusted_mode->hskew;
daifmt->frame_clk_master);
return -EINVAL; if ((mode->flags & DRM_MODE_FLAG_INTERLACE) == 0) {
} ref_line = 1 + mode->vsync_start - mode->vdisplay;
for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) vwin1_line_s = mode->vtotal - mode->vdisplay - 1;
if (priv->audio_port[i].format == AFMT_I2S) vwin1_line_e = vwin1_line_s + mode->vdisplay;
audio.config = priv->audio_port[i].config; vs1_pix_s = vs1_pix_e = hs_pix_s;
audio.format = AFMT_I2S; vs1_line_s = mode->vsync_start - mode->vdisplay;
break; vs1_line_e = vs1_line_s +
case HDMI_SPDIF: mode->vsync_end - mode->vsync_start;
for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) vwin2_line_s = vwin2_line_e = 0;
if (priv->audio_port[i].format == AFMT_SPDIF) vs2_pix_s = vs2_pix_e = 0;
audio.config = priv->audio_port[i].config; vs2_line_s = vs2_line_e = 0;
audio.format = AFMT_SPDIF; } else {
break; ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2;
default: vwin1_line_s = (mode->vtotal - mode->vdisplay)/2;
dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt); vwin1_line_e = vwin1_line_s + mode->vdisplay/2;
return -EINVAL; vs1_pix_s = vs1_pix_e = hs_pix_s;
vs1_line_s = (mode->vsync_start - mode->vdisplay)/2;
vs1_line_e = vs1_line_s +
(mode->vsync_end - mode->vsync_start)/2;
vwin2_line_s = vwin1_line_s + mode->vtotal/2;
vwin2_line_e = vwin2_line_s + mode->vdisplay/2;
vs2_pix_s = vs2_pix_e = hs_pix_s + mode->htotal/2;
vs2_line_s = vs1_line_s + mode->vtotal/2 ;
vs2_line_e = vs2_line_s +
(mode->vsync_end - mode->vsync_start)/2;
} }
if (audio.config == 0) { div = 148500 / mode->clock;
dev_err(dev, "%s: No audio configutation found\n", __func__); if (div != 0) {
return -EINVAL; div--;
if (div > 3)
div = 3;
} }
mutex_lock(&priv->audio_mutex); mutex_lock(&priv->audio_mutex);
ret = tda998x_configure_audio(priv,
&audio,
priv->encoder.crtc->hwmode.clock);
if (ret == 0) /* mute the audio FIFO: */
priv->audio_params = audio; reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO);
mutex_unlock(&priv->audio_mutex);
return ret; /* set HDMI HDCP mode off: */
} reg_write(priv, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS);
reg_clear(priv, REG_TX33, TX33_HDMI);
reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0));
static void tda998x_audio_shutdown(struct device *dev, void *data) /* no pre-filter or interpolator: */
{ reg_write(priv, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) |
struct tda998x_priv *priv = dev_get_drvdata(dev); HVF_CNTRL_0_INTPOL(0));
reg_set(priv, REG_FEAT_POWERDOWN, FEAT_POWERDOWN_PREFILT);
reg_write(priv, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0));
reg_write(priv, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) |
VIP_CNTRL_4_BLC(0));
mutex_lock(&priv->audio_mutex); reg_clear(priv, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ);
reg_clear(priv, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR |
PLL_SERIAL_3_SRL_DE);
reg_write(priv, REG_SERIALIZER, 0);
reg_write(priv, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0));
reg_write(priv, REG_ENA_AP, 0); /* TODO enable pixel repeat for pixel rates less than 25Msamp/s */
rep = 0;
reg_write(priv, REG_RPT_CNTRL, 0);
reg_write(priv, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) |
SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK);
priv->audio_params.format = AFMT_UNUSED; reg_write(priv, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) |
PLL_SERIAL_2_SRL_PR(rep));
mutex_unlock(&priv->audio_mutex); /* set color matrix bypass flag: */
} reg_write(priv, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP |
MAT_CONTRL_MAT_SC(1));
reg_set(priv, REG_FEAT_POWERDOWN, FEAT_POWERDOWN_CSC);
static int /* set BIAS tmds value: */
tda998x_audio_digital_mute(struct device *dev, void *data, bool enable) reg_write(priv, REG_ANA_GENERAL, 0x09);
{
struct tda998x_priv *priv = dev_get_drvdata(dev);
mutex_lock(&priv->audio_mutex); /*
* Sync on rising HSYNC/VSYNC
*/
reg = VIP_CNTRL_3_SYNC_HS;
tda998x_audio_mute(priv, enable); /*
* TDA19988 requires high-active sync at input stage,
* so invert low-active sync provided by master encoder here
*/
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
reg |= VIP_CNTRL_3_H_TGL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
reg |= VIP_CNTRL_3_V_TGL;
reg_write(priv, REG_VIP_CNTRL_3, reg);
mutex_unlock(&priv->audio_mutex); reg_write(priv, REG_VIDFORMAT, 0x00);
return 0; reg_write16(priv, REG_REFPIX_MSB, ref_pix);
} reg_write16(priv, REG_REFLINE_MSB, ref_line);
reg_write16(priv, REG_NPIX_MSB, n_pix);
reg_write16(priv, REG_NLINE_MSB, n_line);
reg_write16(priv, REG_VS_LINE_STRT_1_MSB, vs1_line_s);
reg_write16(priv, REG_VS_PIX_STRT_1_MSB, vs1_pix_s);
reg_write16(priv, REG_VS_LINE_END_1_MSB, vs1_line_e);
reg_write16(priv, REG_VS_PIX_END_1_MSB, vs1_pix_e);
reg_write16(priv, REG_VS_LINE_STRT_2_MSB, vs2_line_s);
reg_write16(priv, REG_VS_PIX_STRT_2_MSB, vs2_pix_s);
reg_write16(priv, REG_VS_LINE_END_2_MSB, vs2_line_e);
reg_write16(priv, REG_VS_PIX_END_2_MSB, vs2_pix_e);
reg_write16(priv, REG_HS_PIX_START_MSB, hs_pix_s);
reg_write16(priv, REG_HS_PIX_STOP_MSB, hs_pix_e);
reg_write16(priv, REG_VWIN_START_1_MSB, vwin1_line_s);
reg_write16(priv, REG_VWIN_END_1_MSB, vwin1_line_e);
reg_write16(priv, REG_VWIN_START_2_MSB, vwin2_line_s);
reg_write16(priv, REG_VWIN_END_2_MSB, vwin2_line_e);
reg_write16(priv, REG_DE_START_MSB, de_pix_s);
reg_write16(priv, REG_DE_STOP_MSB, de_pix_e);
static int tda998x_audio_get_eld(struct device *dev, void *data, if (priv->rev == TDA19988) {
uint8_t *buf, size_t len) /* let incoming pixels fill the active space (if any) */
{ reg_write(priv, REG_ENABLE_SPACE, 0x00);
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; /*
} * Always generate sync polarity relative to input sync and
* revert input stage toggled sync at output stage
*/
reg = TBG_CNTRL_1_DWIN_DIS | TBG_CNTRL_1_TGL_EN;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
reg |= TBG_CNTRL_1_H_TGL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
reg |= TBG_CNTRL_1_V_TGL;
reg_write(priv, REG_TBG_CNTRL_1, reg);
static const struct hdmi_codec_ops audio_codec_ops = { /* must be last register set: */
.hw_params = tda998x_audio_hw_params, reg_write(priv, REG_TBG_CNTRL_0, 0);
.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, priv->tmds_clock = adjusted_mode->clock;
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++) { /* CEA-861B section 6 says that:
if (priv->audio_port[i].format == AFMT_I2S && * CEA version 1 (CEA-861) has no support for infoframes.
priv->audio_port[i].config != 0) * CEA version 2 (CEA-861A) supports version 1 AVI infoframes,
codec_data.i2s = 1; * and optional basic audio.
if (priv->audio_port[i].format == AFMT_SPDIF && * CEA version 3 (CEA-861B) supports version 1 and 2 AVI infoframes,
priv->audio_port[i].config != 0) * and optional digital audio, with audio infoframes.
codec_data.spdif = 1; *
* Since we only support generation of version 2 AVI infoframes,
* ignore CEA version 2 and below (iow, behave as if we're a
* CEA-861 source.)
*/
priv->supports_infoframes = priv->connector.display_info.cea_rev >= 3;
if (priv->supports_infoframes) {
/* We need to turn HDMI HDCP stuff on to get audio through */
reg &= ~TBG_CNTRL_1_DWIN_DIS;
reg_write(priv, REG_TBG_CNTRL_1, reg);
reg_write(priv, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1));
reg_set(priv, REG_TX33, TX33_HDMI);
tda998x_write_avi(priv, adjusted_mode);
if (priv->audio_params.format != AFMT_UNUSED &&
priv->sink_has_audio)
tda998x_configure_audio(priv, &priv->audio_params);
} }
priv->audio_pdev = platform_device_register_data( mutex_unlock(&priv->audio_mutex);
dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, }
&codec_data, sizeof(codec_data));
return PTR_ERR_OR_ZERO(priv->audio_pdev); static void tda998x_destroy(struct tda998x_priv *priv)
{
/* disable all IRQs and free the IRQ handler */
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);
del_timer_sync(&priv->edid_delay_timer);
cancel_work_sync(&priv->detect_work);
i2c_unregister_device(priv->cec);
} }
/* I2C driver functions */ /* I2C driver functions */
...@@ -1404,22 +1483,21 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1404,22 +1483,21 @@ 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;
u32 video; u32 video;
int rev_lo, rev_hi, ret; int rev_lo, rev_hi, ret;
unsigned short cec_addr;
mutex_init(&priv->audio_mutex); /* Protect access from audio thread */
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3); priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1); priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5); priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
/* CEC I2C address bound to TDA998x I2C addr by configuration pins */
priv->cec_addr = 0x34 + (client->addr & 0x03);
priv->current_page = 0xff; priv->current_page = 0xff;
priv->hdmi = client; priv->hdmi = client;
/* CEC I2C address bound to TDA998x I2C addr by configuration pins */ priv->cec = i2c_new_dummy(client->adapter, priv->cec_addr);
cec_addr = 0x34 + (client->addr & 0x03);
priv->cec = i2c_new_dummy(client->adapter, cec_addr);
if (!priv->cec) if (!priv->cec)
return -ENODEV; return -ENODEV;
priv->dpms = DRM_MODE_DPMS_OFF;
mutex_init(&priv->mutex); /* protect the page access */ mutex_init(&priv->mutex); /* protect the page access */
init_waitqueue_head(&priv->edid_delay_waitq); init_waitqueue_head(&priv->edid_delay_waitq);
setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done, setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done,
...@@ -1479,7 +1557,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1479,7 +1557,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
/* initialize the optional IRQ */ /* initialize the optional IRQ */
if (client->irq) { if (client->irq) {
int irqf_trigger; unsigned long irq_flags;
/* init read EDID waitqueue and HDP work */ /* init read EDID waitqueue and HDP work */
init_waitqueue_head(&priv->wq_edid); init_waitqueue_head(&priv->wq_edid);
...@@ -1489,11 +1567,11 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1489,11 +1567,11 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
reg_read(priv, REG_INT_FLAGS_1); reg_read(priv, REG_INT_FLAGS_1);
reg_read(priv, REG_INT_FLAGS_2); reg_read(priv, REG_INT_FLAGS_2);
irqf_trigger = irq_flags =
irqd_get_trigger_type(irq_get_irq_data(client->irq)); irqd_get_trigger_type(irq_get_irq_data(client->irq));
irq_flags |= IRQF_SHARED | IRQF_ONESHOT;
ret = request_threaded_irq(client->irq, NULL, ret = request_threaded_irq(client->irq, NULL,
tda998x_irq_thread, tda998x_irq_thread, irq_flags,
irqf_trigger | IRQF_ONESHOT,
"tda998x", priv); "tda998x", priv);
if (ret) { if (ret) {
dev_err(&client->dev, dev_err(&client->dev,
...@@ -1520,8 +1598,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) ...@@ -1520,8 +1598,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
priv->vip_cntrl_2 = video; priv->vip_cntrl_2 = video;
} }
mutex_init(&priv->audio_mutex); /* Protect access from audio thread */
ret = tda998x_get_audio_ports(priv, np); ret = tda998x_get_audio_ports(priv, np);
if (ret) if (ret)
goto fail; goto fail;
...@@ -1568,44 +1644,25 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = { ...@@ -1568,44 +1644,25 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = {
.destroy = tda998x_encoder_destroy, .destroy = tda998x_encoder_destroy,
}; };
static struct drm_encoder * static void tda998x_set_config(struct tda998x_priv *priv,
tda998x_connector_best_encoder(struct drm_connector *connector) const struct tda998x_encoder_params *p)
{ {
struct tda998x_priv *priv = conn_to_tda998x_priv(connector); priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
(p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
return &priv->encoder; VIP_CNTRL_0_SWAP_B(p->swap_b) |
} (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) |
static (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) |
const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = { VIP_CNTRL_1_SWAP_D(p->swap_d) |
.get_modes = tda998x_connector_get_modes, (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0);
.mode_valid = tda998x_connector_mode_valid, priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) |
.best_encoder = tda998x_connector_best_encoder, (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) |
}; VIP_CNTRL_2_SWAP_F(p->swap_f) |
(p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
static void tda998x_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
}
static int tda998x_connector_dpms(struct drm_connector *connector, int mode) priv->audio_params = p->audio_params;
{
if (drm_core_check_feature(connector->dev, DRIVER_ATOMIC))
return drm_atomic_helper_connector_dpms(connector, mode);
else
return drm_helper_connector_dpms(connector, mode);
} }
static const struct drm_connector_funcs tda998x_connector_funcs = {
.dpms = tda998x_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = tda998x_connector_detect,
.destroy = tda998x_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int tda998x_bind(struct device *dev, struct device *master, void *data) static int tda998x_bind(struct device *dev, struct device *master, void *data)
{ {
struct tda998x_encoder_params *params = dev->platform_data; struct tda998x_encoder_params *params = dev->platform_data;
...@@ -1630,7 +1687,6 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) ...@@ -1630,7 +1687,6 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
crtcs = 1 << 0; crtcs = 1 << 0;
} }
priv->connector.interlace_allowed = 1;
priv->encoder.possible_crtcs = crtcs; priv->encoder.possible_crtcs = crtcs;
ret = tda998x_create(client, priv); ret = tda998x_create(client, priv);
...@@ -1638,9 +1694,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) ...@@ -1638,9 +1694,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
return ret; return ret;
if (!dev->of_node && params) if (!dev->of_node && params)
tda998x_encoder_set_config(priv, params); tda998x_set_config(priv, params);
tda998x_encoder_set_polling(priv, &priv->connector);
drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs); drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs, ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
...@@ -1648,16 +1702,10 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) ...@@ -1648,16 +1702,10 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
if (ret) if (ret)
goto err_encoder; goto err_encoder;
drm_connector_helper_add(&priv->connector, ret = tda998x_connector_init(priv, drm);
&tda998x_connector_helper_funcs);
ret = drm_connector_init(drm, &priv->connector,
&tda998x_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
if (ret) if (ret)
goto err_connector; goto err_connector;
drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
return 0; return 0;
err_connector: err_connector:
...@@ -1685,6 +1733,10 @@ static const struct component_ops tda998x_ops = { ...@@ -1685,6 +1733,10 @@ static const struct component_ops tda998x_ops = {
static int static int
tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ {
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_warn(&client->dev, "adapter does not support I2C\n");
return -EIO;
}
return component_add(&client->dev, &tda998x_ops); return component_add(&client->dev, &tda998x_ops);
} }
......
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