From patchwork Sun May 19 16:49:22 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Hesselbarth X-Patchwork-Id: 2592641 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork1.kernel.org (Postfix) with ESMTP id DE0443FD4E for ; Mon, 20 May 2013 12:08:21 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EC2D5E5DCF for ; Mon, 20 May 2013 05:08:21 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ee0-f51.google.com (mail-ee0-f51.google.com [74.125.83.51]) by gabe.freedesktop.org (Postfix) with ESMTP id 6DA97E5DF3 for ; Sun, 19 May 2013 09:49:34 -0700 (PDT) Received: by mail-ee0-f51.google.com with SMTP id e51so3468210eek.10 for ; Sun, 19 May 2013 09:49:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer; bh=ZhPzzN/swFwWuQIo2hSrr0+CrDC4TDisi5hh6LDcBFc=; b=XzdtLzpmQEcjjxBcFwt3qJmlgQVOhRR/mzDZtS5IHzkGBu7vEWJIpo4COoHxlkXgk4 OGeQkBM5xyfGDFPu4AYbXWE1DDb5FcDkwhyxm7KUi/+rYHNeIMZEZiMgM853NWtauRoO ZcL+xLnjGaW0B34zh2Putt88mlMyd683LLVnuGviz0ANAxjBwXQkGlsddaWlQlYtvEF0 VI0QZWVUhY8LzSLlfZy3xuVEwouavOyk1yJEEIVA2EL/LiYFgyPPmO09X7ms5cQk5pyH LHZlX79zbuzpaaxcJuAr72OAAGibFspGEESg46Tb/IsdNQKIcQA3meiwaZLk/iOlQ4VV Fq3w== X-Received: by 10.14.99.8 with SMTP id w8mr55928324eef.30.1368982173716; Sun, 19 May 2013 09:49:33 -0700 (PDT) Received: from topkick.lan (dslc-082-083-251-181.pools.arcor-ip.net. [82.83.251.181]) by mx.google.com with ESMTPSA id y2sm8304197eeu.2.2013.05.19.09.49.31 for (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 19 May 2013 09:49:32 -0700 (PDT) Received: from edge.lan (magicgate.lan [192.168.1.1]) by topkick.lan (Postfix) with ESMTPSA id 8BD175F93C; Sun, 19 May 2013 18:48:47 +0200 (CEST) From: Sebastian Hesselbarth To: Sebastian Hesselbarth Subject: [RFC] drm: i2c: add irq handler for tda998x slave encoder Date: Sun, 19 May 2013 18:49:22 +0200 Message-Id: <1368982162-10126-1-git-send-email-sebastian.hesselbarth@gmail.com> X-Mailer: git-send-email 1.7.10.4 X-Mailman-Approved-At: Mon, 20 May 2013 05:07:00 -0700 Cc: Russell King , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org This adds an irq handler for HPD to the tda998x slave encoder driver to trigger HPD change instead of polling. The gpio connected to int pin of tda998x is passed through platform_data of the i2c client. As HPD will ultimately cause EDID read and that will raise an EDID_READ_DONE interrupt, the irq handling is done threaded with a workqueue to notify drm backend of HPD events. Signed-off-by: Sebastian Hesselbarth --- Cc: David Airlie Cc: Russell King Cc: Rob Clark Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org This patch currently is based on top of Russell King's Dove DRM driver. I only have DT (plus the patches to get the above running) I post it as an RFC first. To rebase it on top of v3.10-rc1, I would just have to create include/drm/i2c/tda998x.h without rmk's initial include. @Russell: TDA19988 int line on Cubox is on mpp27. Sebastian --- drivers/gpu/drm/i2c/tda998x_drv.c | 134 +++++++++++++++++++++++++++++++++---- include/drm/i2c/tda998x.h | 4 ++ 2 files changed, 125 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 8ffb844..d71b9d8 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -16,8 +16,10 @@ */ - +#include #include +#include +#include #include #include @@ -29,6 +31,7 @@ struct tda998x_priv { struct i2c_client *cec; + struct drm_encoder *encoder; uint16_t rev; uint8_t current_page; int dpms; @@ -37,6 +40,10 @@ struct tda998x_priv { u8 vip_cntrl_1; u8 vip_cntrl_2; struct tda998x_encoder_params params; + wait_queue_head_t wq_edid; + bool wq_edid_done; + struct work_struct work; + int irq; }; #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) @@ -285,14 +292,19 @@ struct tda998x_priv { /* CEC registers: (not paged) */ +#define REG_CEC_INTSTATUS 0xee /* read */ +# define CEC_INTSTATUS_HDMI (1 << 1) +# define CEC_INTSTATUS_CEC (1 << 0) #define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */ # define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7) # define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6) # define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1) # define CEC_FRO_IM_CLK_CTRL_FRO_DIV (1 << 0) +#define REG_CEC_RXSHPDINTENA 0xfc /* read/write */ +#define REG_CEC_RXSHPDINT 0xfd /* read */ #define REG_CEC_RXSHPDLEV 0xfe /* read */ -# define CEC_RXSHPDLEV_RXSENS (1 << 0) -# define CEC_RXSHPDLEV_HPD (1 << 1) +# define CEC_RXSHPD_RXSENS (1 << 0) +# define CEC_RXSHPD_HPD (1 << 1) #define REG_CEC_ENAMODS 0xff /* read/write */ # define CEC_ENAMODS_DIS_FRO (1 << 6) @@ -666,6 +678,54 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) tda998x_configure_audio(encoder, p); } +static irqreturn_t tda998x_irq_thread(int irq, void *data) +{ + struct drm_encoder *encoder = data; + struct tda998x_priv *priv; + uint8_t sta, cec, hdmi, lev; + + if (!encoder) + return IRQ_HANDLED; + + priv = to_tda998x_priv(encoder); + if (!priv) + return IRQ_HANDLED; + + sta = cec_read(encoder, REG_CEC_INTSTATUS); + cec = cec_read(encoder, REG_CEC_RXSHPDINT); + lev = cec_read(encoder, REG_CEC_RXSHPDLEV); + hdmi = reg_read(encoder, REG_INT_FLAGS_2); + + /* clear all interrupts */ + cec_write(encoder, REG_CEC_RXSHPDINTENA, 0x00); + + if (sta & CEC_INTSTATUS_HDMI) { + /* schedule EDID read on active HPD */ + if ((cec & CEC_RXSHPD_HPD) && (lev & CEC_RXSHPD_HPD)) + schedule_work(&priv->work); + + /* wake up thread waiting on EDID read done */ + if (hdmi & INT_FLAGS_2_EDID_BLK_RD) { + priv->wq_edid_done = true; + wake_up(&priv->wq_edid); + } + }; + + /* re-enable HPD interrupts */ + cec_write(encoder, REG_CEC_RXSHPDINTENA, CEC_RXSHPD_HPD); + + return IRQ_HANDLED; +} + +static void tda998x_hpd_worker(struct work_struct *work) +{ + struct tda998x_priv *priv = + container_of(work, struct tda998x_priv, work); + + if (priv->encoder && priv->encoder->dev) + drm_helper_hpd_irq_event(priv->encoder->dev); +} + static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) { @@ -878,19 +938,17 @@ tda998x_encoder_detect(struct drm_encoder *encoder, struct drm_connector *connector) { uint8_t val = cec_read(encoder, REG_CEC_RXSHPDLEV); - return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : + return (val & CEC_RXSHPD_HPD) ? connector_status_connected : connector_status_disconnected; } static int read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) { + struct tda998x_priv *priv = to_tda998x_priv(encoder); uint8_t offset, segptr; int ret, i; - /* enable EDID read irq: */ - reg_set(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - offset = (blk & 1) ? 128 : 0; segptr = blk / 2; @@ -900,17 +958,24 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) reg_write(encoder, REG_DDC_SEGM, segptr); /* enable reading EDID: */ + priv->wq_edid_done = false; + reg_set(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); reg_write(encoder, REG_EDID_CTRL, 0x1); /* flag must be cleared by sw: */ reg_write(encoder, REG_EDID_CTRL, 0x0); /* wait for block read to complete: */ - for (i = 100; i > 0; i--) { - uint8_t val = reg_read(encoder, REG_INT_FLAGS_2); - if (val & INT_FLAGS_2_EDID_BLK_RD) - break; - msleep(1); + if (priv->irq < 0) { + for (i = 100; i > 0; i--) { + uint8_t val = reg_read(encoder, REG_INT_FLAGS_2); + if (val & INT_FLAGS_2_EDID_BLK_RD) + break; + msleep(1); + } + } else { + i = wait_event_timeout(priv->wq_edid, priv->wq_edid_done, + msecs_to_jiffies(100)); } if (i == 0) @@ -1014,7 +1079,15 @@ static int tda998x_encoder_create_resources(struct drm_encoder *encoder, struct drm_connector *connector) { - DBG(""); + struct tda998x_priv *priv = to_tda998x_priv(encoder); + + /* announce polling if no irq is available */ + if (priv->irq < 0) + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + else + connector->polled = DRM_CONNECTOR_POLL_HPD; + return 0; } @@ -1105,7 +1178,9 @@ tda998x_encoder_init(struct i2c_client *client, struct drm_encoder_slave *encoder_slave) { struct drm_encoder *encoder = &encoder_slave->base; + struct tda998x_platform_data *pdata = client->dev.platform_data; struct tda998x_priv *priv; + int ret; /* debug */ device_create_file(&client->dev, &dev_attr_i2c_read); device_create_file(&client->dev, &dev_attr_i2c_write); @@ -1122,7 +1197,9 @@ tda998x_encoder_init(struct i2c_client *client, priv->current_page = 0; priv->cec = i2c_new_dummy(client->adapter, 0x34); + priv->irq = -EINVAL; priv->dpms = DRM_MODE_DPMS_OFF; + priv->encoder = encoder; encoder_slave->slave_priv = priv; encoder_slave->slave_funcs = &tda998x_encoder_funcs; @@ -1163,6 +1240,37 @@ tda998x_encoder_init(struct i2c_client *client, cec_write(encoder, REG_CEC_FRO_IM_CLK_CTRL, CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL); + /* allow no platform_data */ + if (!pdata) + goto no_pdata; + + if (gpio_is_valid(pdata->int_gpio)) { + ret = devm_gpio_request_one(&client->dev, pdata->int_gpio, + GPIOF_DIR_IN, "HDMI HPD"); + if (ret) + goto fail; + + priv->irq = gpio_to_irq(pdata->int_gpio); + /* init work and waitqueue */ + INIT_WORK(&priv->work, tda998x_hpd_worker); + init_waitqueue_head(&priv->wq_edid); + /* clear pending interrupts */ + reg_read(encoder, REG_INT_FLAGS_0); + reg_read(encoder, REG_INT_FLAGS_1); + reg_read(encoder, REG_INT_FLAGS_2); + + ret = devm_request_threaded_irq(&client->dev, priv->irq, NULL, + tda998x_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tda998x-int", encoder); + if (ret) + goto fail; + + /* enable HPD irq */ + cec_write(encoder, REG_CEC_RXSHPDINTENA, CEC_RXSHPD_HPD); + } + +no_pdata: return 0; fail: diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h index 41f799f..1838703 100644 --- a/include/drm/i2c/tda998x.h +++ b/include/drm/i2c/tda998x.h @@ -20,4 +20,8 @@ struct tda998x_encoder_params { int swap_f, mirr_f; }; +struct tda998x_platform_data { + int int_gpio; +}; + #endif