From nobody Fri Dec 19 02:50:46 2025 Received: from mail-ej1-f43.google.com (mail-ej1-f43.google.com [209.85.218.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 88E1D41A81 for ; Thu, 4 Jul 2024 13:48:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720100898; cv=none; b=UJ2o59Cv1H/iEoj4QvHIDeNGWXuJl1yUi1YlFrRujS3AKhI3xeOMxTTG3Y7xyUyZCdgRte55/q5VkbZFsCjLjxydB9iN9XRnPKOI+xD/xDZjWd1n0xsHPpTbGeYkE1q6oBebaZ6KvJC9fXcPgPCYJQyRXpU93YLVZwfvu+xYdcE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1720100898; c=relaxed/simple; bh=v5bQjR1r2V6WZzJq+zmVg/5EWAMnR1R4sYOcyqtWs6c=; h=Message-ID:Date:MIME-Version:From:Subject:To:Cc:Content-Type; b=Jn6Mlv0KeEyQfJQObABrB+yhWG6jAJC1fnSngGlY8WMGmzg+67ANmmjs91/1+Xhdv3XE6Cp2Xk18cWzVQO+3LskgiM8R0xm9rFXWJhm1353rWgiXoEKViL08GJ1qrxc+zDUHOM67XtV/57ZIBPYPGht4BBYSvOOHAcQ+ZQGJumQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jIhzDx8S; arc=none smtp.client-ip=209.85.218.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jIhzDx8S" Received: by mail-ej1-f43.google.com with SMTP id a640c23a62f3a-a77c349bb81so24652766b.3 for ; Thu, 04 Jul 2024 06:48:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720100895; x=1720705695; darn=vger.kernel.org; h=content-transfer-encoding:content-language:cc:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=iI+L0uJTfei+H4iOkc8eBSpcQ6o2OxpGILgPP/6lgdA=; b=jIhzDx8SANHTIhcBXPsS+4bmlEjEataCu7TCp8919zbet86LBXUjBFCqboSaVYJKxG jFvBKamzcu1SMAdhuSE2Hs2PTxQgykOWV54fsWpIP6CKGI/ogl1ZGSF5DpbIlwP4Dzby 1tOLZDHo2ThXdNKCcepmuTtLxSaS/KiH+txhzZznqe9rjIrwmqGhheSAYWE9B4vb4r36 FCnM7wCVp6wBRl8lEeHOD7YZpwa9v/J7GOaciQg0BW5R6lJABktx5RiYEMz6Fx3VX0zt s7GGXoKpr2T2SS1IjjElSRsnFRRXbtNjHcCA3vSpFF7ryovAdOpvQRYMaTd4MIcH2gwQ m20w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720100895; x=1720705695; h=content-transfer-encoding:content-language:cc:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=iI+L0uJTfei+H4iOkc8eBSpcQ6o2OxpGILgPP/6lgdA=; b=hUxgpVTw93XPTzsUBYZYieNU78xzkVsq073MnwMi+oO0CkQk0a5os/+2Jg/QzmHJYx DzKcDiCDOOSMvrUcIZEW0nxwHYgV5ASCVGwxgHRW2FTNsphgVnB+JApTxBeWeNwac3z5 /CAo8RYGh3FIFLrk36d47Sa32vFWnrM8NlsLb1JTuJ2b35W/9pabfDgpZ4R3DnYXsqm5 88B/98W9W96ac38EIBxJZM5xZTngXMaYSgKAeawrSNB4Uv2y6ELBFOI8PrFeylyo/ogL AjVD/or6AYkXiOErikwPPmaY6E2BBoABScAJGtIh6o0MGlpXtsffJ4Us/u1CNdUXJsYT Y4Ow== X-Forwarded-Encrypted: i=1; AJvYcCUTTzg5fqeueR2u8uM8tCKv4FXiFaYN0eFuC0nOWjSM1xONrZ/YLSCLZ0wOHtWodQ8i0uZUtnS0FhTANITQudbylYL/x9WLnPO72e6q X-Gm-Message-State: AOJu0YySFgsXxss3yjU3vhk8hIuPFELFBVYYw0jeAcQjhL8DrsWoh+f8 bwzdVu4FJBEAdacryhgGXcQ/dIS8TWi+JIVcnCD0xtO8B8qyILCw X-Google-Smtp-Source: AGHT+IGYzrdYiH/Ow9cpga56hgcUYflT97C20uUsJxefko/mreRxTqvROdAf5Hix7M06ph6/Rpqu0Q== X-Received: by 2002:a17:906:2689:b0:a72:b1b1:eb3a with SMTP id a640c23a62f3a-a77ba46ce71mr103049466b.19.1720100894615; Thu, 04 Jul 2024 06:48:14 -0700 (PDT) Received: from ?IPV6:2a02:a449:4071:1:32d0:42ff:fe10:6983? (2a02-a449-4071-1-32d0-42ff-fe10-6983.fixed6.kpn.net. [2a02:a449:4071:1:32d0:42ff:fe10:6983]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a72b033f3a3sm572244266b.187.2024.07.04.06.48.13 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 04 Jul 2024 06:48:14 -0700 (PDT) Message-ID: <9f55fa12-51d9-4c85-a34f-0c42c61678e0@gmail.com> Date: Thu, 4 Jul 2024 15:48:13 +0200 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Johan Jonker Subject: [PATCH v10] drm/rockchip: rk3066_hdmi: add sound support To: heiko@sntech.de Cc: hjc@rock-chips.com, andy.yan@rock-chips.com, maarten.lankhorst@linux.intel.com, mripard@kernel.org, tzimmermann@suse.de, airlied@gmail.com, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org Content-Language: en-US Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add sound support to the RK3066 HDMI driver. The HDMI TX audio source is connected to I2S_8CH. Signed-off-by: Zheng Yang Signed-off-by: Johan Jonker --- Changed V10: s/audio_enable/audio_enabled hook to encoder->funcs->late_register move audio functions up to avoid adding a declaration Changed V9: Use late_register and early_unregister hooks to (un)register the "hdmi-audio-codec" driver. restyle Changed V8: return -EPROBE_DEFER as early as possible move rk3066_hdmi_audio_codec_init() function after rk3066_hdmi_register() restyle Changed V7: rebase --- drivers/gpu/drm/rockchip/Kconfig | 2 + drivers/gpu/drm/rockchip/rk3066_hdmi.c | 330 +++++++++++++++++++++++-- 2 files changed, 306 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kc= onfig index 1bf3e2829cd0..a32ee558408c 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -102,6 +102,8 @@ config ROCKCHIP_RGB config ROCKCHIP_RK3066_HDMI bool "Rockchip specific extensions for RK3066 HDMI" depends on DRM_ROCKCHIP + select SND_SOC_HDMI_CODEC if SND_SOC + select SND_SOC_ROCKCHIP_I2S if SND_SOC help This selects support for Rockchip SoC specific extensions for the RK3066 HDMI driver. If you want to enable diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockc= hip/rk3066_hdmi.c index 784de990da1b..e3b8faf89ae2 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -15,12 +15,20 @@ #include #include +#include + #include "rk3066_hdmi.h" #include "rockchip_drm_drv.h" #define DEFAULT_PLLA_RATE 30000000 +struct rk3066_hdmi_audio_info { + int channels; + int sample_rate; + int sample_width; +}; + struct hdmi_data_info { int vic; /* The CEA Video ID (VIC) of the current drm display mode. */ unsigned int enc_out_format; @@ -40,7 +48,6 @@ struct rk3066_hdmi_i2c { struct rk3066_hdmi { struct device *dev; - struct drm_device *drm_dev; struct regmap *grf_regmap; int irq; struct clk *hclk; @@ -54,6 +61,10 @@ struct rk3066_hdmi { unsigned int tmdsclk; + struct platform_device *audio_pdev; + struct rk3066_hdmi_audio_info audio; + bool audio_enabled; + struct hdmi_data_info hdmi_data; }; @@ -214,6 +225,241 @@ static int rk3066_hdmi_config_avi(struct rk3066_hdmi = *hdmi, HDMI_INFOFRAME_AVI, 0, 0, 0); } +static int rk3066_hdmi_config_aai(struct rk3066_hdmi *hdmi) +{ + union hdmi_infoframe frame; + int rc; + + rc =3D hdmi_audio_infoframe_init(&frame.audio); + + frame.audio.coding_type =3D HDMI_AUDIO_CODING_TYPE_STREAM; + frame.audio.sample_frequency =3D HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + frame.audio.sample_size =3D HDMI_AUDIO_SAMPLE_SIZE_STREAM; + frame.audio.channels =3D hdmi->audio.channels; + + return rk3066_hdmi_upload_frame(hdmi, rc, &frame, + HDMI_INFOFRAME_AAI, 0, 0, 0); +} + +static int rk3066_hdmi_audio_config(struct rk3066_hdmi *hdmi) +{ + u32 rate, channel, word_length, N, CTS; + struct rk3066_hdmi_audio_info *audio =3D &hdmi->audio; + u64 tmp; + + if (audio->channels < 3) + channel =3D HDMI_AUDIO_I2S_CHANNEL_1_2; + else if (audio->channels < 5) + channel =3D HDMI_AUDIO_I2S_CHANNEL_3_4; + else if (audio->channels < 7) + channel =3D HDMI_AUDIO_I2S_CHANNEL_5_6; + else + channel =3D HDMI_AUDIO_I2S_CHANNEL_7_8; + + switch (audio->sample_rate) { + case 32000: + rate =3D HDMI_AUDIO_SAMPLE_FRE_32000; + N =3D N_32K; + break; + case 44100: + rate =3D HDMI_AUDIO_SAMPLE_FRE_44100; + N =3D N_441K; + break; + case 48000: + rate =3D HDMI_AUDIO_SAMPLE_FRE_48000; + N =3D N_48K; + break; + case 88200: + rate =3D HDMI_AUDIO_SAMPLE_FRE_88200; + N =3D N_882K; + break; + case 96000: + rate =3D HDMI_AUDIO_SAMPLE_FRE_96000; + N =3D N_96K; + break; + case 176400: + rate =3D HDMI_AUDIO_SAMPLE_FRE_176400; + N =3D N_1764K; + break; + case 192000: + rate =3D HDMI_AUDIO_SAMPLE_FRE_192000; + N =3D N_192K; + break; + default: + DRM_DEV_ERROR(hdmi->dev, "no support for sample rate %d\n", + audio->sample_rate); + return -ENOENT; + } + + switch (audio->sample_width) { + case 16: + word_length =3D 0x02; + break; + case 20: + word_length =3D 0x0a; + break; + case 24: + word_length =3D 0x0b; + break; + default: + DRM_DEV_ERROR(hdmi->dev, "no support for word length %d\n", + audio->sample_width); + return -ENOENT; + } + + tmp =3D (u64)hdmi->tmdsclk * N; + do_div(tmp, 128 * audio->sample_rate); + CTS =3D tmp; + + /* Set_audio source I2S. */ + hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x00); + hdmi_writeb(hdmi, HDMI_AUDIO_CTRL2, 0x40); + hdmi_writeb(hdmi, HDMI_I2S_AUDIO_CTRL, + HDMI_AUDIO_I2S_FORMAT_STANDARD | channel); + hdmi_writeb(hdmi, HDMI_I2S_SWAP, 0x00); + hdmi_modb(hdmi, HDMI_AV_CTRL1, HDMI_AUDIO_SAMPLE_FRE_MASK, rate); + hdmi_writeb(hdmi, HDMI_AUDIO_SRC_NUM_AND_LENGTH, word_length); + + /* Set N value. */ + hdmi_modb(hdmi, HDMI_LR_SWAP_N3, + HDMI_AUDIO_N_19_16_MASK, (N >> 16) & 0x0F); + hdmi_writeb(hdmi, HDMI_N2, (N >> 8) & 0xFF); + hdmi_writeb(hdmi, HDMI_N1, N & 0xFF); + + /* Set CTS value. */ + hdmi_writeb(hdmi, HDMI_CTS_EXT1, CTS & 0xff); + hdmi_writeb(hdmi, HDMI_CTS_EXT2, (CTS >> 8) & 0xff); + hdmi_writeb(hdmi, HDMI_CTS_EXT3, (CTS >> 16) & 0xff); + + if (audio->channels > 2) + hdmi_modb(hdmi, HDMI_LR_SWAP_N3, + HDMI_AUDIO_LR_SWAP_MASK, + HDMI_AUDIO_LR_SWAP_SUBPACKET1); + rate =3D (~(rate >> 4)) & 0x0f; + hdmi_writeb(hdmi, HDMI_AUDIO_STA_BIT_CTRL1, rate); + hdmi_writeb(hdmi, HDMI_AUDIO_STA_BIT_CTRL2, 0); + + return rk3066_hdmi_config_aai(hdmi); +} + +static int rk3066_hdmi_audio_hw_params(struct device *dev, void *d, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct rk3066_hdmi *hdmi =3D dev_get_drvdata(dev); + struct drm_display_info *display =3D &hdmi->connector.display_info; + + if (!display->has_audio) { + DRM_DEV_ERROR(hdmi->dev, "no audio support\n"); + return -ENODEV; + } + + if (!hdmi->encoder.encoder.crtc) + return -ENODEV; + + switch (daifmt->fmt) { + case HDMI_I2S: + break; + default: + DRM_DEV_ERROR(dev, "invalid format %d\n", daifmt->fmt); + return -EINVAL; + } + + hdmi->audio.channels =3D params->channels; + hdmi->audio.sample_rate =3D params->sample_rate; + hdmi->audio.sample_width =3D params->sample_width; + + return rk3066_hdmi_audio_config(hdmi); +} + +static void rk3066_hdmi_audio_shutdown(struct device *dev, void *d) +{ + /* Do nothing. */ +} + +static int rk3066_hdmi_audio_mute_stream(struct device *dev, void *d, bool= mute, int direction) +{ + struct rk3066_hdmi *hdmi =3D dev_get_drvdata(dev); + struct drm_display_info *display =3D &hdmi->connector.display_info; + + if (!display->has_audio) { + DRM_DEV_ERROR(hdmi->dev, "no audio support\n"); + return -ENODEV; + } + + hdmi->audio_enabled =3D !mute; + + if (mute) + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, + HDMI_AUDIO_DISABLE, HDMI_AUDIO_DISABLE); + else + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_AUDIO_DISABLE, 0); + + /* + * Under power mode E we need to reset the audio capture logic to + * make the audio setting update. + */ + if (rk3066_hdmi_get_power_mode(hdmi) =3D=3D HDMI_SYS_POWER_MODE_E) { + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, + HDMI_AUDIO_CP_LOGIC_RESET_MASK, + HDMI_AUDIO_CP_LOGIC_RESET); + usleep_range(900, 1000); + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, + HDMI_AUDIO_CP_LOGIC_RESET_MASK, 0); + } + + return 0; +} + +static int rk3066_hdmi_audio_get_eld(struct device *dev, void *d, u8 *buf,= size_t len) +{ + struct rk3066_hdmi *hdmi =3D dev_get_drvdata(dev); + struct drm_mode_config *config =3D &hdmi->encoder.encoder.dev->mode_confi= g; + struct drm_connector *connector; + int ret =3D -ENODEV; + + mutex_lock(&config->mutex); + list_for_each_entry(connector, &config->connector_list, head) { + if (&hdmi->encoder.encoder =3D=3D connector->encoder) { + memcpy(buf, connector->eld, + min(sizeof(connector->eld), len)); + ret =3D 0; + } + } + mutex_unlock(&config->mutex); + + return ret; +} + +static const struct hdmi_codec_ops rk3066_hdmi_audio_codec_ops =3D { + .hw_params =3D rk3066_hdmi_audio_hw_params, + .audio_shutdown =3D rk3066_hdmi_audio_shutdown, + .mute_stream =3D rk3066_hdmi_audio_mute_stream, + .get_eld =3D rk3066_hdmi_audio_get_eld, + .no_capture_mute =3D 1, +}; + +static int rk3066_hdmi_audio_codec_init(struct rk3066_hdmi *hdmi) +{ + struct hdmi_codec_pdata rk3066_hdmi_codec_data =3D { + .i2s =3D 1, + .ops =3D &rk3066_hdmi_audio_codec_ops, + .max_i2s_channels =3D 8, + }; + + hdmi->audio.channels =3D 2; + hdmi->audio.sample_rate =3D 48000; + hdmi->audio.sample_width =3D 16; + hdmi->audio_enabled =3D false; + hdmi->audio_pdev =3D platform_device_register_data(hdmi->dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_NONE, + &rk3066_hdmi_codec_data, + sizeof(rk3066_hdmi_codec_data)); + + return PTR_ERR_OR_ZERO(hdmi->audio_pdev); +} + static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi, struct drm_display_mode *mode) { @@ -364,6 +610,7 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, HDMI_VIDEO_MODE_HDMI); rk3066_hdmi_config_avi(hdmi, mode); + rk3066_hdmi_audio_config(hdmi); } else { hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, 0); } @@ -380,9 +627,20 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, */ rk3066_hdmi_i2c_init(hdmi); - /* Unmute video output. */ + /* Unmute video and audio output. */ hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_VIDEO_AUDIO_DISABLE_MASK, HDMI_AUDIO_DISABLE); + if (hdmi->audio_enabled) { + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_AUDIO_DISABLE, 0); + /* Reset audio capture logic. */ + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, + HDMI_AUDIO_CP_LOGIC_RESET_MASK, + HDMI_AUDIO_CP_LOGIC_RESET); + usleep_range(900, 1000); + hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, + HDMI_AUDIO_CP_LOGIC_RESET_MASK, 0); + } + return 0; } @@ -431,6 +689,7 @@ static void rk3066_hdmi_encoder_disable(struct drm_enco= der *encoder, HDMI_AUDIO_CP_LOGIC_RESET); usleep_range(500, 510); } + rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); } @@ -447,6 +706,26 @@ rk3066_hdmi_encoder_atomic_check(struct drm_encoder *e= ncoder, return 0; } +static int rk3066_hdmi_encoder_late_register(struct drm_encoder *encoder) +{ + struct rk3066_hdmi *hdmi =3D encoder_to_rk3066_hdmi(encoder); + + return rk3066_hdmi_audio_codec_init(hdmi); +} + +static void rk3066_hdmi_encoder_early_unregister(struct drm_encoder *encod= er) +{ + struct rk3066_hdmi *hdmi =3D encoder_to_rk3066_hdmi(encoder); + + platform_device_unregister(hdmi->audio_pdev); +} + +static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs =3D { + .destroy =3D drm_encoder_cleanup, + .late_register =3D rk3066_hdmi_encoder_late_register, + .early_unregister =3D rk3066_hdmi_encoder_early_unregister, +}; + static const struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs =3D { .atomic_check =3D rk3066_hdmi_encoder_atomic_check, @@ -519,41 +798,28 @@ static void rk3066_hdmi_connector_destroy(struct drm_= connector *connector) } static const struct drm_connector_funcs rk3066_hdmi_connector_funcs =3D { - .fill_modes =3D rk3066_hdmi_probe_single_connector_modes, - .detect =3D rk3066_hdmi_connector_detect, - .destroy =3D rk3066_hdmi_connector_destroy, - .reset =3D drm_atomic_helper_connector_reset, + .fill_modes =3D rk3066_hdmi_probe_single_connector_modes, + .detect =3D rk3066_hdmi_connector_detect, + .destroy =3D rk3066_hdmi_connector_destroy, + .reset =3D drm_atomic_helper_connector_reset, .atomic_duplicate_state =3D drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state =3D drm_atomic_helper_connector_destroy_state, + .atomic_destroy_state =3D drm_atomic_helper_connector_destroy_state, }; static const struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs =3D { - .get_modes =3D rk3066_hdmi_connector_get_modes, - .mode_valid =3D rk3066_hdmi_connector_mode_valid, + .get_modes =3D rk3066_hdmi_connector_get_modes, + .mode_valid =3D rk3066_hdmi_connector_mode_valid, .best_encoder =3D rk3066_hdmi_connector_best_encoder, }; -static int -rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) +static int rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi= *hdmi) { struct drm_encoder *encoder =3D &hdmi->encoder.encoder; - struct device *dev =3D hdmi->dev; - - encoder->possible_crtcs =3D - drm_of_find_possible_crtcs(drm, dev->of_node); - - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (encoder->possible_crtcs =3D=3D 0) - return -EPROBE_DEFER; drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs); - drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); hdmi->connector.polled =3D DRM_CONNECTOR_POLL_HPD; @@ -740,6 +1006,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct= device *master, { struct platform_device *pdev =3D to_platform_device(dev); struct drm_device *drm =3D data; + struct drm_encoder *encoder; struct rk3066_hdmi *hdmi; int irq; int ret; @@ -748,8 +1015,19 @@ static int rk3066_hdmi_bind(struct device *dev, struc= t device *master, if (!hdmi) return -ENOMEM; + encoder =3D &hdmi->encoder.encoder; + encoder->possible_crtcs =3D drm_of_find_possible_crtcs(drm, dev->of_node); + + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs =3D=3D 0) + return -EPROBE_DEFER; + hdmi->dev =3D dev; - hdmi->drm_dev =3D drm; hdmi->regs =3D devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); -- 2.39.2