diff options
author | Dave Airlie <airlied@redhat.com> | 2016-11-25 09:55:33 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-11-25 09:55:33 +1000 |
commit | 7625e05286cf3f37c8a5e633379a4d014ddbe555 (patch) | |
tree | 40a4be339cec8dccebfea8cd8b4c557dee15d9c2 /drivers/gpu | |
parent | 4d5304d87034fe3f003cffa7ef2350d066ef7f3e (diff) | |
parent | 9b2502b6ebc632ff49743b3639ea12d4f08808a5 (diff) | |
download | linux-7625e05286cf3f37c8a5e633379a4d014ddbe555.tar.bz2 |
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
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i2c/tda998x_drv.c | 856 |
1 files changed, 454 insertions, 402 deletions
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 027521f30b6e..86f47e190309 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -41,12 +41,15 @@ struct tda998x_priv { struct i2c_client *hdmi; struct mutex mutex; u16 rev; + u8 cec_addr; u8 current_page; - int dpms; - bool is_hdmi_sink; + bool is_on; + bool supports_infoframes; + bool sink_has_audio; u8 vip_cntrl_0; u8 vip_cntrl_1; u8 vip_cntrl_2; + unsigned long tmds_clock; struct tda998x_audio_params audio_params; struct platform_device *audio_pdev; @@ -105,6 +108,8 @@ struct tda998x_priv { # define I2C_MASTER_DIS_FILT (1 << 1) # define I2C_MASTER_APP_STRT_LAT (1 << 2) #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 REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ #define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ @@ -370,35 +375,46 @@ struct tda998x_priv { static void cec_write(struct tda998x_priv *priv, u16 addr, u8 val) { - struct i2c_client *client = priv->cec; u8 buf[] = {addr, val}; + struct i2c_msg msg = { + .addr = priv->cec_addr, + .len = 2, + .buf = buf, + }; int ret; - ret = i2c_master_send(client, buf, sizeof(buf)); + ret = i2c_transfer(priv->hdmi->adapter, &msg, 1); 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 cec_read(struct tda998x_priv *priv, u8 addr) { - struct i2c_client *client = priv->cec; 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; - ret = i2c_master_send(client, &addr, sizeof(addr)); - if (ret < 0) - goto fail; - - ret = i2c_master_recv(client, &val, sizeof(val)); - if (ret < 0) - goto fail; + ret = i2c_transfer(priv->hdmi->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + dev_err(&priv->hdmi->dev, "Error %d reading from cec:0x%x\n", + ret, addr); + val = 0; + } return val; - -fail: - dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr); - return 0; } static int @@ -579,9 +595,9 @@ tda998x_reset(struct tda998x_priv *priv) * HPD assertion: it needs a delay of 100ms to avoid timing out while * 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 - * 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 * that delay. */ @@ -630,28 +646,30 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) bool handled = false; sta = cec_read(priv, REG_CEC_INTSTATUS); - cec = cec_read(priv, REG_CEC_RXSHPDINT); - lvl = cec_read(priv, REG_CEC_RXSHPDLEV); - flag0 = reg_read(priv, REG_INT_FLAGS_0); - flag1 = reg_read(priv, REG_INT_FLAGS_1); - flag2 = reg_read(priv, REG_INT_FLAGS_2); - DRM_DEBUG_DRIVER( - "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) - tda998x_edid_delay_start(priv); - else - schedule_work(&priv->detect_work); - - handled = true; - } + if (sta & CEC_INTSTATUS_HDMI) { + cec = cec_read(priv, REG_CEC_RXSHPDINT); + lvl = cec_read(priv, REG_CEC_RXSHPDLEV); + flag0 = reg_read(priv, REG_INT_FLAGS_0); + flag1 = reg_read(priv, REG_INT_FLAGS_1); + flag2 = reg_read(priv, REG_INT_FLAGS_2); + DRM_DEBUG_DRIVER( + "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) + tda998x_edid_delay_start(priv); + else + schedule_work(&priv->detect_work); + + handled = true; + } - if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { - priv->wq_edid_wait = 0; - wake_up(&priv->wq_edid); - handled = true; + if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { + priv->wq_edid_wait = 0; + wake_up(&priv->wq_edid); + handled = true; + } } return IRQ_RETVAL(handled); @@ -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); } +/* Audio support */ + static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) { if (on) { @@ -713,8 +733,7 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) static int tda998x_configure_audio(struct tda998x_priv *priv, - struct tda998x_audio_params *params, - unsigned mode_clock) + struct tda998x_audio_params *params) { u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv; u32 n; @@ -771,7 +790,7 @@ tda998x_configure_audio(struct tda998x_priv *priv, * assume 100MHz requires larger divider. */ adiv = AUDIO_DIV_SERCLK_8; - if (mode_clock > 100000) + if (priv->tmds_clock > 100000) adiv++; /* AUDIO_DIV_SERCLK_16 */ /* S/PDIF asks for a larger divider */ @@ -819,58 +838,281 @@ tda998x_configure_audio(struct tda998x_priv *priv, return tda998x_write_aif(priv, ¶ms->cea); } -/* DRM encoder functions */ +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, + }; + + memcpy(audio.status, params->iec.status, + min(sizeof(audio.status), sizeof(params->iec.status))); -static void tda998x_encoder_set_config(struct tda998x_priv *priv, - const struct tda998x_encoder_params *p) + 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 configuration found\n", __func__); + return -EINVAL; + } + + mutex_lock(&priv->audio_mutex); + if (priv->supports_infoframes && priv->sink_has_audio) + ret = tda998x_configure_audio(priv, &audio); + else + ret = 0; + + if (ret == 0) + priv->audio_params = audio; + mutex_unlock(&priv->audio_mutex); + + return ret; +} + +static void tda998x_audio_shutdown(struct device *dev, void *data) { - 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); + struct tda998x_priv *priv = dev_get_drvdata(dev); - priv->audio_params = p->audio_params; + mutex_lock(&priv->audio_mutex); + + reg_write(priv, REG_ENA_AP, 0); + + priv->audio_params.format = AFMT_UNUSED; + + mutex_unlock(&priv->audio_mutex); } -static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) +int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable) { - struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); + struct tda998x_priv *priv = dev_get_drvdata(dev); - /* we only care about on or off: */ - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; + mutex_lock(&priv->audio_mutex); - if (mode == priv->dpms) - return; + tda998x_audio_mute(priv, enable); - switch (mode) { - case DRM_MODE_DPMS_ON: - /* 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); - break; - case DRM_MODE_DPMS_OFF: - /* 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); - break; + 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); + + mutex_lock(&priv->audio_mutex); + memcpy(buf, priv->connector.eld, + min(sizeof(priv->connector.eld), len)); + mutex_unlock(&priv->audio_mutex); + + return 0; +} + +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->dpms = mode; + 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); +} + +/* DRM connector functions */ + +static int tda998x_connector_dpms(struct drm_connector *connector, int mode) +{ + 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 int tda998x_connector_fill_modes(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY) +{ + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); + int ret; + + mutex_lock(&priv->audio_mutex); + ret = drm_helper_probe_single_connector_modes(connector, maxX, maxY); + + if (connector->edid_blob_ptr) { + struct edid *edid = (void *)connector->edid_blob_ptr->data; + + priv->sink_has_audio = drm_detect_monitor_audio(edid); + } else { + priv->sink_has_audio = false; + } + mutex_unlock(&priv->audio_mutex); + + return ret; +} + +static enum drm_connector_status +tda998x_connector_detect(struct drm_connector *connector, bool force) +{ + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); + u8 val = cec_read(priv, REG_CEC_RXSHPDLEV); + + return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : + 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) +{ + struct tda998x_priv *priv = data; + u8 offset, segptr; + int ret, i; + + offset = (blk & 1) ? 128 : 0; + segptr = blk / 2; + + reg_write(priv, REG_DDC_ADDR, 0xa0); + reg_write(priv, REG_DDC_OFFS, offset); + reg_write(priv, REG_DDC_SEGM_ADDR, 0x60); + reg_write(priv, REG_DDC_SEGM, segptr); + + /* enable reading EDID: */ + priv->wq_edid_wait = 1; + reg_write(priv, REG_EDID_CTRL, 0x1); + + /* flag must be cleared by sw: */ + reg_write(priv, REG_EDID_CTRL, 0x0); + + /* wait for block read to complete: */ + if (priv->hdmi->irq) { + i = wait_event_timeout(priv->wq_edid, + !priv->wq_edid_wait, + msecs_to_jiffies(100)); + if (i < 0) { + dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i); + return i; + } + } else { + for (i = 100; i > 0; i--) { + msleep(1); + ret = reg_read(priv, REG_INT_FLAGS_2); + if (ret < 0) + return ret; + if (ret & INT_FLAGS_2_EDID_BLK_RD) + break; + } + } + + if (i == 0) { + dev_err(&priv->hdmi->dev, "read edid timeout\n"); + return -ETIMEDOUT; + } + + ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length); + if (ret != length) { + dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n", + blk, ret); + return ret; + } + + return 0; +} + +static int tda998x_connector_get_modes(struct drm_connector *connector) +{ + struct tda998x_priv *priv = conn_to_tda998x_priv(connector); + struct edid *edid; + int n; + + /* + * If we get killed while waiting for the HPD timeout, return + * no modes found: we are not in a restartable path, so we + * can't handle signals gracefully. + */ + if (tda998x_edid_delay_wait(priv)) + return 0; + + if (priv->rev == TDA19988) + reg_clear(priv, REG_TX4, TX4_PD_RAM); + + edid = drm_do_get_edid(connector, read_edid_block, priv); + + if (priv->rev == TDA19988) + reg_set(priv, REG_TX4, TX4_PD_RAM); + + if (!edid) { + dev_warn(&priv->hdmi->dev, "failed to read EDID\n"); + return 0; + } + + drm_mode_connector_update_edid_property(connector, edid); + n = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); + + kfree(edid); + + return n; } static int tda998x_connector_mode_valid(struct drm_connector *connector, @@ -888,6 +1130,80 @@ static int tda998x_connector_mode_valid(struct drm_connector *connector, 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) + connector->polled = DRM_CONNECTOR_POLL_HPD; + else + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + 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; +} + +/* DRM encoder functions */ + +static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); + bool on; + + /* we only care about on or off: */ + on = mode == DRM_MODE_DPMS_ON; + + if (on == priv->is_on) + return; + + if (on) { + /* 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); + + 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 void tda998x_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, @@ -971,6 +1287,8 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, div = 3; } + mutex_lock(&priv->audio_mutex); + /* mute the audio FIFO: */ reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); @@ -982,6 +1300,7 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, /* no pre-filter or interpolator: */ reg_write(priv, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) | 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)); @@ -1004,6 +1323,7 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, /* 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); /* set BIAS tmds value: */ reg_write(priv, REG_ANA_GENERAL, 0x09); @@ -1064,8 +1384,22 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, /* must be last register set: */ reg_write(priv, REG_TBG_CNTRL_0, 0); - /* Only setup the info frames if the sink is HDMI */ - if (priv->is_hdmi_sink) { + priv->tmds_clock = adjusted_mode->clock; + + /* CEA-861B section 6 says that: + * CEA version 1 (CEA-861) has no support for infoframes. + * CEA version 2 (CEA-861A) supports version 1 AVI infoframes, + * and optional basic audio. + * CEA version 3 (CEA-861B) supports version 1 and 2 AVI infoframes, + * and optional digital audio, with audio infoframes. + * + * 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); @@ -1074,127 +1408,12 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, tda998x_write_avi(priv, adjusted_mode); - 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); - } + if (priv->audio_params.format != AFMT_UNUSED && + priv->sink_has_audio) + tda998x_configure_audio(priv, &priv->audio_params); } -} -static enum drm_connector_status -tda998x_connector_detect(struct drm_connector *connector, bool force) -{ - struct tda998x_priv *priv = conn_to_tda998x_priv(connector); - u8 val = cec_read(priv, REG_CEC_RXSHPDLEV); - - return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : - connector_status_disconnected; -} - -static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) -{ - struct tda998x_priv *priv = data; - u8 offset, segptr; - int ret, i; - - offset = (blk & 1) ? 128 : 0; - segptr = blk / 2; - - reg_write(priv, REG_DDC_ADDR, 0xa0); - reg_write(priv, REG_DDC_OFFS, offset); - reg_write(priv, REG_DDC_SEGM_ADDR, 0x60); - reg_write(priv, REG_DDC_SEGM, segptr); - - /* enable reading EDID: */ - priv->wq_edid_wait = 1; - reg_write(priv, REG_EDID_CTRL, 0x1); - - /* flag must be cleared by sw: */ - reg_write(priv, REG_EDID_CTRL, 0x0); - - /* wait for block read to complete: */ - if (priv->hdmi->irq) { - i = wait_event_timeout(priv->wq_edid, - !priv->wq_edid_wait, - msecs_to_jiffies(100)); - if (i < 0) { - dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i); - return i; - } - } else { - for (i = 100; i > 0; i--) { - msleep(1); - ret = reg_read(priv, REG_INT_FLAGS_2); - if (ret < 0) - return ret; - if (ret & INT_FLAGS_2_EDID_BLK_RD) - break; - } - } - - if (i == 0) { - dev_err(&priv->hdmi->dev, "read edid timeout\n"); - return -ETIMEDOUT; - } - - ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length); - if (ret != length) { - dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n", - blk, ret); - return ret; - } - - return 0; -} - -static int tda998x_connector_get_modes(struct drm_connector *connector) -{ - struct tda998x_priv *priv = conn_to_tda998x_priv(connector); - struct edid *edid; - int n; - - /* - * If we get killed while waiting for the HPD timeout, return - * no modes found: we are not in a restartable path, so we - * can't handle signals gracefully. - */ - if (tda998x_edid_delay_wait(priv)) - return 0; - - if (priv->rev == TDA19988) - reg_clear(priv, REG_TX4, TX4_PD_RAM); - - edid = drm_do_get_edid(connector, read_edid_block, priv); - - if (priv->rev == TDA19988) - reg_set(priv, REG_TX4, TX4_PD_RAM); - - if (!edid) { - dev_warn(&priv->hdmi->dev, "failed to read EDID\n"); - return 0; - } - - 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; -} - -static void tda998x_encoder_set_polling(struct tda998x_priv *priv, - struct drm_connector *connector) -{ - if (priv->hdmi->irq) - connector->polled = DRM_CONNECTOR_POLL_HPD; - else - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; + mutex_unlock(&priv->audio_mutex); } static void tda998x_destroy(struct tda998x_priv *priv) @@ -1215,146 +1434,6 @@ 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); -} - -static 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, @@ -1404,22 +1483,21 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) struct device_node *np = client->dev.of_node; u32 video; 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_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); + /* CEC I2C address bound to TDA998x I2C addr by configuration pins */ + priv->cec_addr = 0x34 + (client->addr & 0x03); priv->current_page = 0xff; priv->hdmi = client; - /* CEC I2C address bound to TDA998x I2C addr by configuration pins */ - cec_addr = 0x34 + (client->addr & 0x03); - priv->cec = i2c_new_dummy(client->adapter, cec_addr); + priv->cec = i2c_new_dummy(client->adapter, priv->cec_addr); if (!priv->cec) return -ENODEV; - priv->dpms = DRM_MODE_DPMS_OFF; - mutex_init(&priv->mutex); /* protect the page access */ init_waitqueue_head(&priv->edid_delay_waitq); 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) /* initialize the optional IRQ */ if (client->irq) { - int irqf_trigger; + unsigned long irq_flags; /* init read EDID waitqueue and HDP work */ init_waitqueue_head(&priv->wq_edid); @@ -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_2); - irqf_trigger = + irq_flags = irqd_get_trigger_type(irq_get_irq_data(client->irq)); + irq_flags |= IRQF_SHARED | IRQF_ONESHOT; ret = request_threaded_irq(client->irq, NULL, - tda998x_irq_thread, - irqf_trigger | IRQF_ONESHOT, + tda998x_irq_thread, irq_flags, "tda998x", priv); if (ret) { dev_err(&client->dev, @@ -1520,8 +1598,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) priv->vip_cntrl_2 = video; } - mutex_init(&priv->audio_mutex); /* Protect access from audio thread */ - ret = tda998x_get_audio_ports(priv, np); if (ret) goto fail; @@ -1568,44 +1644,25 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = { .destroy = tda998x_encoder_destroy, }; -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 void tda998x_connector_destroy(struct drm_connector *connector) +static void tda998x_set_config(struct tda998x_priv *priv, + const struct tda998x_encoder_params *p) { - drm_connector_cleanup(connector); -} + 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); -static int tda998x_connector_dpms(struct drm_connector *connector, int mode) -{ - 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); + priv->audio_params = p->audio_params; } -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) { struct tda998x_encoder_params *params = dev->platform_data; @@ -1630,7 +1687,6 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) crtcs = 1 << 0; } - priv->connector.interlace_allowed = 1; priv->encoder.possible_crtcs = crtcs; ret = tda998x_create(client, priv); @@ -1638,9 +1694,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) return ret; if (!dev->of_node && params) - tda998x_encoder_set_config(priv, params); - - tda998x_encoder_set_polling(priv, &priv->connector); + tda998x_set_config(priv, params); drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_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) if (ret) goto err_encoder; - drm_connector_helper_add(&priv->connector, - &tda998x_connector_helper_funcs); - ret = drm_connector_init(drm, &priv->connector, - &tda998x_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + ret = tda998x_connector_init(priv, drm); if (ret) goto err_connector; - drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder); - return 0; err_connector: @@ -1685,6 +1733,10 @@ static const struct component_ops tda998x_ops = { static int 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); } |