From patchwork Thu Jan 9 11:08:03 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Francois Moine X-Patchwork-Id: 3459691 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A7DB89F384 for ; Thu, 9 Jan 2014 12:28:33 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EE07E2012E for ; Thu, 9 Jan 2014 12:28:28 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DDCD720108 for ; Thu, 9 Jan 2014 12:28:27 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1W1DZq-000262-Lc; Thu, 09 Jan 2014 11:15:07 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W1DZD-0001Nx-Fe; Thu, 09 Jan 2014 11:14:27 +0000 Received: from smtp1-g21.free.fr ([2a01:e0c:1:1599::10]) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W1DYb-0001G5-Vg for linux-arm-kernel@lists.infradead.org; Thu, 09 Jan 2014 11:13:51 +0000 Received: from armhf (unknown [IPv6:2a01:e35:2f5c:9de0:212:bfff:fe1e:9ce4]) by smtp1-g21.free.fr (Postfix) with ESMTP id EF534940138; Thu, 9 Jan 2014 12:13:18 +0100 (CET) Date: Thu, 9 Jan 2014 12:08:03 +0100 From: Jean-Francois Moine To: dri-devel@lists.freedesktop.org Subject: [PATCH v2 28/28] drm/i2c: tda998x: add a CODEC interface Message-ID: <20140109120803.6c26b401@armhf> X-Mailer: Claws Mail 3.9.3 (GTK+ 2.24.22; arm-unknown-linux-gnueabihf) MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140109_061350_645554_DAEF533D X-CRM114-Status: GOOD ( 20.26 ) X-Spam-Score: -1.9 (-) Cc: Rob Clark , alsa-devel@alsa-project.org, Dave Airlie , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The TDA998x chips may get audio input from I2S or S/PDIF and this input is defined by the audio subsystem. In the other way, the audio subsystem may need to know if audio output may be done through the HDMI connector. This patch adds a CODEC interface for exchanges between the HDMI transmitter and the audio subsystem. Signed-off-by: Jean-Francois Moine --- drivers/gpu/drm/i2c/tda998x_drv.c | 70 ++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 67d7450..5a186e4 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -42,7 +42,8 @@ struct tda998x_priv { u8 vip_cntrl_1; u8 vip_cntrl_2; - u8 audio_type; /* audio type */ + u8 audio_type; /* audio type and 'active' flag */ +#define AUDIO_ACTIVE 0x80 u8 audio_frame[6]; u32 audio_port; @@ -50,6 +51,9 @@ struct tda998x_priv { wait_queue_head_t wq_edid; volatile int wq_edid_wait; struct drm_encoder *encoder; + + struct snd_soc_codec *codec; + void (*event)(struct snd_soc_codec *codec, int activate); }; #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) @@ -731,14 +735,14 @@ tda998x_configure_audio(struct tda998x_priv *priv, /* Set audio input source */ switch (priv->audio_type) { - case AFMT_SPDIF: + case AFMT_SPDIF | AUDIO_ACTIVE: reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF); clksel_aip = AIP_CLKSEL_AIP(SEL_AIP_SPDIF); clksel_fs = AIP_CLKSEL_FS(CTSREF_FS64SPDIF); cts_n = CTS_N_M(3) | CTS_N_K(3); break; - case AFMT_I2S: + case AFMT_I2S | AUDIO_ACTIVE: reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S); clksel_aip = AIP_CLKSEL_AIP(SEL_AIP_I2S); clksel_fs = AIP_CLKSEL_FS(CTSREF_ACLK); @@ -813,6 +817,58 @@ tda998x_configure_audio(struct tda998x_priv *priv, tda998x_write_aif(priv); } +/* tda998x codec interface */ +void tda998x_audio_register(struct i2c_client *client, + struct snd_soc_codec *codec, + void (*event)(struct snd_soc_codec *codec, int activate)) +{ + struct tda998x_priv *priv = i2c_get_clientdata(client); + + /* memorize the codec and the callback function */ + priv->codec = codec; + priv->event = event; +} +EXPORT_SYMBOL_GPL(tda998x_audio_register); + +void tda998x_audio_update(struct i2c_client *client, + int type, + u32 port) +{ + struct tda998x_priv *priv = i2c_get_clientdata(client); + + /* if the audio output is active, it may be a second start or a stop */ + if (type == 0 || (priv->audio_type & AUDIO_ACTIVE)) { + if (type == 0) { + priv->audio_type &= ~AUDIO_ACTIVE; /* stop */ + reg_write(priv, REG_ENA_AP, 0); + } + return; + } + + priv->audio_port = port; + + /* don't restart audio if same input type */ + if (type == priv->audio_type) { + priv->audio_type |= AUDIO_ACTIVE; + reg_write(priv, REG_ENA_AP, priv->audio_port); + return; + } + + priv->audio_type = type | AUDIO_ACTIVE; + + tda998x_configure_audio(priv, &priv->encoder->crtc->hwmode); +} +EXPORT_SYMBOL_GPL(tda998x_audio_update); + +/* send an audio event to the tda codec */ +static void send_audio_event(struct tda998x_priv *priv, + int event) +{ + if (!priv->event) + return; + priv->event(priv->codec, event); +} + /* DRM encoder functions */ /* this function is not called when no info->platform (DT support) */ @@ -840,7 +896,7 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) if (p->audio_cfg) { priv->audio_port = p->audio_cfg; - priv->audio_type = p->audio_format; + priv->audio_type = p->audio_format | AUDIO_ACTIVE; } } @@ -1105,9 +1161,10 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, tda998x_write_avi(priv, mode); - if (priv->audio_type) + if (priv->audio_type & AUDIO_ACTIVE) tda998x_configure_audio(priv, mode); } + send_audio_event(priv, priv->is_hdmi_sink); /* must be last register set: */ reg_write(priv, REG_TBG_CNTRL_0, 0); @@ -1263,6 +1320,7 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder, drm_mode_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); + send_audio_event(priv, priv->is_hdmi_sink); kfree(edid); } @@ -1349,6 +1407,8 @@ tda998x_encoder_init(struct i2c_client *client, if (!priv) return -ENOMEM; + i2c_set_clientdata(client, priv); + 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);