18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2016 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright 2016 ZTE Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <sound/hdmi-codec.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "zx_hdmi_regs.h" 288c2ecf20Sopenharmony_ci#include "zx_vou.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define ZX_HDMI_INFOFRAME_SIZE 31 318c2ecf20Sopenharmony_ci#define DDC_SEGMENT_ADDR 0x30 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct zx_hdmi_i2c { 348c2ecf20Sopenharmony_ci struct i2c_adapter adap; 358c2ecf20Sopenharmony_ci struct mutex lock; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct zx_hdmi { 398c2ecf20Sopenharmony_ci struct drm_connector connector; 408c2ecf20Sopenharmony_ci struct drm_encoder encoder; 418c2ecf20Sopenharmony_ci struct zx_hdmi_i2c *ddc; 428c2ecf20Sopenharmony_ci struct device *dev; 438c2ecf20Sopenharmony_ci struct drm_device *drm; 448c2ecf20Sopenharmony_ci void __iomem *mmio; 458c2ecf20Sopenharmony_ci struct clk *cec_clk; 468c2ecf20Sopenharmony_ci struct clk *osc_clk; 478c2ecf20Sopenharmony_ci struct clk *xclk; 488c2ecf20Sopenharmony_ci bool sink_is_hdmi; 498c2ecf20Sopenharmony_ci bool sink_has_audio; 508c2ecf20Sopenharmony_ci struct platform_device *audio_pdev; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return readl_relaxed(hdmi->mmio + offset * 4); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline void hdmi_writeb(struct zx_hdmi *hdmi, u16 offset, u8 val) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci writel_relaxed(val, hdmi->mmio + offset * 4); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void hdmi_writeb_mask(struct zx_hdmi *hdmi, u16 offset, 668c2ecf20Sopenharmony_ci u8 mask, u8 val) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u8 tmp; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci tmp = hdmi_readb(hdmi, offset); 718c2ecf20Sopenharmony_ci tmp = (tmp & ~mask) | (val & mask); 728c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, offset, tmp); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int zx_hdmi_infoframe_trans(struct zx_hdmi *hdmi, 768c2ecf20Sopenharmony_ci union hdmi_infoframe *frame, u8 fsel) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci u8 buffer[ZX_HDMI_INFOFRAME_SIZE]; 798c2ecf20Sopenharmony_ci int num; 808c2ecf20Sopenharmony_ci int i; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, TPI_INFO_FSEL, fsel); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci num = hdmi_infoframe_pack(frame, buffer, ZX_HDMI_INFOFRAME_SIZE); 858c2ecf20Sopenharmony_ci if (num < 0) { 868c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "failed to pack infoframe: %d\n", num); 878c2ecf20Sopenharmony_ci return num; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) 918c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, TPI_INFO_B0 + i, buffer[i]); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_INFO_EN, TPI_INFO_TRANS_RPT, 948c2ecf20Sopenharmony_ci TPI_INFO_TRANS_RPT); 958c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_INFO_EN, TPI_INFO_TRANS_EN, 968c2ecf20Sopenharmony_ci TPI_INFO_TRANS_EN); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return num; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int zx_hdmi_config_video_vsi(struct zx_hdmi *hdmi, 1028c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, 1088c2ecf20Sopenharmony_ci &hdmi->connector, 1098c2ecf20Sopenharmony_ci mode); 1108c2ecf20Sopenharmony_ci if (ret) { 1118c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "failed to get vendor infoframe: %d\n", 1128c2ecf20Sopenharmony_ci ret); 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_VSIF); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int zx_hdmi_config_video_avi(struct zx_hdmi *hdmi, 1208c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, 1268c2ecf20Sopenharmony_ci &hdmi->connector, 1278c2ecf20Sopenharmony_ci mode); 1288c2ecf20Sopenharmony_ci if (ret) { 1298c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "failed to get avi infoframe: %d\n", 1308c2ecf20Sopenharmony_ci ret); 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* We always use YUV444 for HDMI output. */ 1358c2ecf20Sopenharmony_ci frame.avi.colorspace = HDMI_COLORSPACE_YUV444; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AVI); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void zx_hdmi_encoder_mode_set(struct drm_encoder *encoder, 1418c2ecf20Sopenharmony_ci struct drm_display_mode *mode, 1428c2ecf20Sopenharmony_ci struct drm_display_mode *adj_mode) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = to_zx_hdmi(encoder); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (hdmi->sink_is_hdmi) { 1478c2ecf20Sopenharmony_ci zx_hdmi_config_video_avi(hdmi, mode); 1488c2ecf20Sopenharmony_ci zx_hdmi_config_video_vsi(hdmi, mode); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void zx_hdmi_phy_start(struct zx_hdmi *hdmi) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci /* Copy from ZTE BSP code */ 1558c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x222, 0x0); 1568c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x224, 0x4); 1578c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x909, 0x0); 1588c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b0, 0x90); 1598c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b1, 0x00); 1608c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b2, 0xa7); 1618c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b8, 0xaa); 1628c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b2, 0xa7); 1638c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b3, 0x0f); 1648c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b4, 0x0f); 1658c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b5, 0x55); 1668c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b7, 0x03); 1678c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b9, 0x12); 1688c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7ba, 0x32); 1698c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7bc, 0x68); 1708c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7be, 0x40); 1718c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7bf, 0x84); 1728c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7c1, 0x0f); 1738c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7c8, 0x02); 1748c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7c9, 0x03); 1758c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7ca, 0x40); 1768c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7dc, 0x31); 1778c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7e2, 0x04); 1788c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7e0, 0x06); 1798c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7cb, 0x68); 1808c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7f9, 0x02); 1818c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7b6, 0x02); 1828c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, 0x7f3, 0x0); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void zx_hdmi_hw_enable(struct zx_hdmi *hdmi) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci /* Enable pclk */ 1888c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, CLKPWD, CLKPWD_PDIDCK, CLKPWD_PDIDCK); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Enable HDMI for TX */ 1918c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, FUNC_SEL, FUNC_HDMI_EN, FUNC_HDMI_EN); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Enable deep color packet */ 1948c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, P2T_CTRL, P2T_DC_PKT_EN, P2T_DC_PKT_EN); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Enable HDMI/MHL mode for output */ 1978c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TEST_TXCTRL, TEST_TXCTRL_HDMI_MODE, 1988c2ecf20Sopenharmony_ci TEST_TXCTRL_HDMI_MODE); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Configure reg_qc_sel */ 2018c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, HDMICTL4, 0x3); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Enable interrupt */ 2048c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, INTR1_MASK, INTR1_MONITOR_DETECT, 2058c2ecf20Sopenharmony_ci INTR1_MONITOR_DETECT); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Start up phy */ 2088c2ecf20Sopenharmony_ci zx_hdmi_phy_start(hdmi); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void zx_hdmi_hw_disable(struct zx_hdmi *hdmi) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci /* Disable interrupt */ 2148c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, INTR1_MASK, INTR1_MONITOR_DETECT, 0); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Disable deep color packet */ 2178c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, P2T_CTRL, P2T_DC_PKT_EN, P2T_DC_PKT_EN); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Disable HDMI for TX */ 2208c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, FUNC_SEL, FUNC_HDMI_EN, 0); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Disable pclk */ 2238c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, CLKPWD, CLKPWD_PDIDCK, 0); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void zx_hdmi_encoder_enable(struct drm_encoder *encoder) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = to_zx_hdmi(encoder); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci clk_prepare_enable(hdmi->cec_clk); 2318c2ecf20Sopenharmony_ci clk_prepare_enable(hdmi->osc_clk); 2328c2ecf20Sopenharmony_ci clk_prepare_enable(hdmi->xclk); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci zx_hdmi_hw_enable(hdmi); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci vou_inf_enable(VOU_HDMI, encoder->crtc); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void zx_hdmi_encoder_disable(struct drm_encoder *encoder) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = to_zx_hdmi(encoder); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci vou_inf_disable(VOU_HDMI, encoder->crtc); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci zx_hdmi_hw_disable(hdmi); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci clk_disable_unprepare(hdmi->xclk); 2488c2ecf20Sopenharmony_ci clk_disable_unprepare(hdmi->osc_clk); 2498c2ecf20Sopenharmony_ci clk_disable_unprepare(hdmi->cec_clk); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs zx_hdmi_encoder_helper_funcs = { 2538c2ecf20Sopenharmony_ci .enable = zx_hdmi_encoder_enable, 2548c2ecf20Sopenharmony_ci .disable = zx_hdmi_encoder_disable, 2558c2ecf20Sopenharmony_ci .mode_set = zx_hdmi_encoder_mode_set, 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int zx_hdmi_connector_get_modes(struct drm_connector *connector) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = to_zx_hdmi(connector); 2618c2ecf20Sopenharmony_ci struct edid *edid; 2628c2ecf20Sopenharmony_ci int ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, &hdmi->ddc->adap); 2658c2ecf20Sopenharmony_ci if (!edid) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); 2698c2ecf20Sopenharmony_ci hdmi->sink_has_audio = drm_detect_monitor_audio(edid); 2708c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 2718c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 2728c2ecf20Sopenharmony_ci kfree(edid); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic enum drm_mode_status 2788c2ecf20Sopenharmony_cizx_hdmi_connector_mode_valid(struct drm_connector *connector, 2798c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci return MODE_OK; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic struct drm_connector_helper_funcs zx_hdmi_connector_helper_funcs = { 2858c2ecf20Sopenharmony_ci .get_modes = zx_hdmi_connector_get_modes, 2868c2ecf20Sopenharmony_ci .mode_valid = zx_hdmi_connector_mode_valid, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic enum drm_connector_status 2908c2ecf20Sopenharmony_cizx_hdmi_connector_detect(struct drm_connector *connector, bool force) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = to_zx_hdmi(connector); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return (hdmi_readb(hdmi, TPI_HPD_RSEN) & TPI_HPD_CONNECTION) ? 2958c2ecf20Sopenharmony_ci connector_status_connected : connector_status_disconnected; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs zx_hdmi_connector_funcs = { 2998c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 3008c2ecf20Sopenharmony_ci .detect = zx_hdmi_connector_detect, 3018c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 3028c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 3038c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 3048c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int zx_hdmi_register(struct drm_device *drm, struct zx_hdmi *hdmi) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &hdmi->encoder; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci encoder->possible_crtcs = VOU_CRTC_MASK; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); 3148c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &zx_hdmi_encoder_helper_funcs); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci drm_connector_init_with_ddc(drm, &hdmi->connector, 3198c2ecf20Sopenharmony_ci &zx_hdmi_connector_funcs, 3208c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_HDMIA, 3218c2ecf20Sopenharmony_ci &hdmi->ddc->adap); 3228c2ecf20Sopenharmony_ci drm_connector_helper_add(&hdmi->connector, 3238c2ecf20Sopenharmony_ci &zx_hdmi_connector_helper_funcs); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci drm_connector_attach_encoder(&hdmi->connector, encoder); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic irqreturn_t zx_hdmi_irq_thread(int irq, void *dev_id) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_id; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci drm_helper_hpd_irq_event(hdmi->connector.dev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_id; 3428c2ecf20Sopenharmony_ci u8 lstat; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci lstat = hdmi_readb(hdmi, L1_INTR_STAT); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Monitor detect/HPD interrupt */ 3478c2ecf20Sopenharmony_ci if (lstat & L1_INTR_STAT_INTR1) { 3488c2ecf20Sopenharmony_ci u8 stat; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci stat = hdmi_readb(hdmi, INTR1_STAT); 3518c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, INTR1_STAT, stat); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (stat & INTR1_MONITOR_DETECT) 3548c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return IRQ_NONE; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int zx_hdmi_audio_startup(struct device *dev, void *data) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 3638c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &hdmi->encoder; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void zx_hdmi_audio_shutdown(struct device *dev, void *data) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Disable audio input */ 3758c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic inline int zx_hdmi_audio_get_n(unsigned int fs) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci unsigned int n; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (fs && (fs % 44100) == 0) 3838c2ecf20Sopenharmony_ci n = 6272 * (fs / 44100); 3848c2ecf20Sopenharmony_ci else 3858c2ecf20Sopenharmony_ci n = fs * 128 / 1000; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return n; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int zx_hdmi_audio_hw_params(struct device *dev, 3918c2ecf20Sopenharmony_ci void *data, 3928c2ecf20Sopenharmony_ci struct hdmi_codec_daifmt *daifmt, 3938c2ecf20Sopenharmony_ci struct hdmi_codec_params *params) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 3968c2ecf20Sopenharmony_ci struct hdmi_audio_infoframe *cea = ¶ms->cea; 3978c2ecf20Sopenharmony_ci union hdmi_infoframe frame; 3988c2ecf20Sopenharmony_ci int n; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* We only support spdif for now */ 4018c2ecf20Sopenharmony_ci if (daifmt->fmt != HDMI_SPDIF) { 4028c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt); 4038c2ecf20Sopenharmony_ci return -EINVAL; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci switch (params->sample_width) { 4078c2ecf20Sopenharmony_ci case 16: 4088c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, 4098c2ecf20Sopenharmony_ci SPDIF_SAMPLE_SIZE_16BIT); 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case 20: 4128c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, 4138c2ecf20Sopenharmony_ci SPDIF_SAMPLE_SIZE_20BIT); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case 24: 4168c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, 4178c2ecf20Sopenharmony_ci SPDIF_SAMPLE_SIZE_24BIT); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n", 4218c2ecf20Sopenharmony_ci params->sample_width); 4228c2ecf20Sopenharmony_ci return -EINVAL; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* CTS is calculated by hardware, and we only need to take care of N */ 4268c2ecf20Sopenharmony_ci n = zx_hdmi_audio_get_n(params->sample_rate); 4278c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, N_SVAL1, n & 0xff); 4288c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, N_SVAL2, (n >> 8) & 0xff); 4298c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Enable spdif mode */ 4328c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Enable audio input */ 4358c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci memcpy(&frame.audio, cea, sizeof(*cea)); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int zx_hdmi_audio_mute(struct device *dev, void *data, 4438c2ecf20Sopenharmony_ci bool enable, int direction) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (enable) 4488c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 4498c2ecf20Sopenharmony_ci TPI_AUD_MUTE); 4508c2ecf20Sopenharmony_ci else 4518c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int zx_hdmi_audio_get_eld(struct device *dev, void *data, 4578c2ecf20Sopenharmony_ci uint8_t *buf, size_t len) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 4608c2ecf20Sopenharmony_ci struct drm_connector *connector = &hdmi->connector; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic const struct hdmi_codec_ops zx_hdmi_codec_ops = { 4688c2ecf20Sopenharmony_ci .audio_startup = zx_hdmi_audio_startup, 4698c2ecf20Sopenharmony_ci .hw_params = zx_hdmi_audio_hw_params, 4708c2ecf20Sopenharmony_ci .audio_shutdown = zx_hdmi_audio_shutdown, 4718c2ecf20Sopenharmony_ci .mute_stream = zx_hdmi_audio_mute, 4728c2ecf20Sopenharmony_ci .get_eld = zx_hdmi_audio_get_eld, 4738c2ecf20Sopenharmony_ci .no_capture_mute = 1, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct hdmi_codec_pdata zx_hdmi_codec_pdata = { 4778c2ecf20Sopenharmony_ci .ops = &zx_hdmi_codec_ops, 4788c2ecf20Sopenharmony_ci .spdif = 1, 4798c2ecf20Sopenharmony_ci}; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int zx_hdmi_audio_register(struct zx_hdmi *hdmi) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct platform_device *pdev; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME, 4868c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, 4878c2ecf20Sopenharmony_ci &zx_hdmi_codec_pdata, 4888c2ecf20Sopenharmony_ci sizeof(zx_hdmi_codec_pdata)); 4898c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) 4908c2ecf20Sopenharmony_ci return PTR_ERR(pdev); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci hdmi->audio_pdev = pdev; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int len = msg->len; 5008c2ecf20Sopenharmony_ci u8 *buf = msg->buf; 5018c2ecf20Sopenharmony_ci int retry = 0; 5028c2ecf20Sopenharmony_ci int ret = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Bits [9:8] of bytes */ 5058c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, ZX_DDC_DIN_CNT2, (len >> 8) & 0xff); 5068c2ecf20Sopenharmony_ci /* Bits [7:0] of bytes */ 5078c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, ZX_DDC_DIN_CNT1, len & 0xff); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Clear FIFO */ 5108c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, ZX_DDC_CMD, DDC_CMD_MASK, DDC_CMD_CLEAR_FIFO); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Kick off the read */ 5138c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, ZX_DDC_CMD, DDC_CMD_MASK, 5148c2ecf20Sopenharmony_ci DDC_CMD_SEQUENTIAL_READ); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci while (len > 0) { 5178c2ecf20Sopenharmony_ci int cnt, i; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* FIFO needs some time to get ready */ 5208c2ecf20Sopenharmony_ci usleep_range(500, 1000); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci cnt = hdmi_readb(hdmi, ZX_DDC_DOUT_CNT) & DDC_DOUT_CNT_MASK; 5238c2ecf20Sopenharmony_ci if (cnt == 0) { 5248c2ecf20Sopenharmony_ci if (++retry > 5) { 5258c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, 5268c2ecf20Sopenharmony_ci "DDC FIFO read timed out!"); 5278c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci continue; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) 5338c2ecf20Sopenharmony_ci *buf++ = hdmi_readb(hdmi, ZX_DDC_DATA); 5348c2ecf20Sopenharmony_ci len -= cnt; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int zx_hdmi_i2c_write(struct zx_hdmi *hdmi, struct i2c_msg *msg) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * The DDC I2C adapter is only for reading EDID data, so we assume 5448c2ecf20Sopenharmony_ci * that the write to this adapter must be the EDID data offset. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci if ((msg->len != 1) || 5478c2ecf20Sopenharmony_ci ((msg->addr != DDC_ADDR) && (msg->addr != DDC_SEGMENT_ADDR))) 5488c2ecf20Sopenharmony_ci return -EINVAL; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (msg->addr == DDC_SEGMENT_ADDR) 5518c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, ZX_DDC_SEGM, msg->addr << 1); 5528c2ecf20Sopenharmony_ci else if (msg->addr == DDC_ADDR) 5538c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, ZX_DDC_ADDR, msg->addr << 1); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci hdmi_writeb(hdmi, ZX_DDC_OFFSET, msg->buf[0]); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int zx_hdmi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 5618c2ecf20Sopenharmony_ci int num) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = i2c_get_adapdata(adap); 5648c2ecf20Sopenharmony_ci struct zx_hdmi_i2c *ddc = hdmi->ddc; 5658c2ecf20Sopenharmony_ci int i, ret = 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci mutex_lock(&ddc->lock); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Enable DDC master access */ 5708c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_DDC_MASTER_EN, HW_DDC_MASTER, HW_DDC_MASTER); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 5738c2ecf20Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, 5748c2ecf20Sopenharmony_ci "xfer: num: %d/%d, len: %d, flags: %#x\n", 5758c2ecf20Sopenharmony_ci i + 1, num, msgs[i].len, msgs[i].flags); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (msgs[i].flags & I2C_M_RD) 5788c2ecf20Sopenharmony_ci ret = zx_hdmi_i2c_read(hdmi, &msgs[i]); 5798c2ecf20Sopenharmony_ci else 5808c2ecf20Sopenharmony_ci ret = zx_hdmi_i2c_write(hdmi, &msgs[i]); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (ret < 0) 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (!ret) 5878c2ecf20Sopenharmony_ci ret = num; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Disable DDC master access */ 5908c2ecf20Sopenharmony_ci hdmi_writeb_mask(hdmi, TPI_DDC_MASTER_EN, HW_DDC_MASTER, 0); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci mutex_unlock(&ddc->lock); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return ret; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic u32 zx_hdmi_i2c_func(struct i2c_adapter *adapter) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct i2c_algorithm zx_hdmi_algorithm = { 6038c2ecf20Sopenharmony_ci .master_xfer = zx_hdmi_i2c_xfer, 6048c2ecf20Sopenharmony_ci .functionality = zx_hdmi_i2c_func, 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int zx_hdmi_ddc_register(struct zx_hdmi *hdmi) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 6108c2ecf20Sopenharmony_ci struct zx_hdmi_i2c *ddc; 6118c2ecf20Sopenharmony_ci int ret; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ddc = devm_kzalloc(hdmi->dev, sizeof(*ddc), GFP_KERNEL); 6148c2ecf20Sopenharmony_ci if (!ddc) 6158c2ecf20Sopenharmony_ci return -ENOMEM; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci hdmi->ddc = ddc; 6188c2ecf20Sopenharmony_ci mutex_init(&ddc->lock); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci adap = &ddc->adap; 6218c2ecf20Sopenharmony_ci adap->owner = THIS_MODULE; 6228c2ecf20Sopenharmony_ci adap->class = I2C_CLASS_DDC; 6238c2ecf20Sopenharmony_ci adap->dev.parent = hdmi->dev; 6248c2ecf20Sopenharmony_ci adap->algo = &zx_hdmi_algorithm; 6258c2ecf20Sopenharmony_ci snprintf(adap->name, sizeof(adap->name), "zx hdmi i2c"); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci ret = i2c_add_adapter(adap); 6288c2ecf20Sopenharmony_ci if (ret) { 6298c2ecf20Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "failed to add I2C adapter: %d\n", 6308c2ecf20Sopenharmony_ci ret); 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, hdmi); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int zx_hdmi_bind(struct device *dev, struct device *master, void *data) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 6428c2ecf20Sopenharmony_ci struct drm_device *drm = data; 6438c2ecf20Sopenharmony_ci struct resource *res; 6448c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi; 6458c2ecf20Sopenharmony_ci int irq; 6468c2ecf20Sopenharmony_ci int ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 6498c2ecf20Sopenharmony_ci if (!hdmi) 6508c2ecf20Sopenharmony_ci return -ENOMEM; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci hdmi->dev = dev; 6538c2ecf20Sopenharmony_ci hdmi->drm = drm; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci dev_set_drvdata(dev, hdmi); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6588c2ecf20Sopenharmony_ci hdmi->mmio = devm_ioremap_resource(dev, res); 6598c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->mmio)) { 6608c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->mmio); 6618c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap hdmi region: %d\n", ret); 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 6668c2ecf20Sopenharmony_ci if (irq < 0) 6678c2ecf20Sopenharmony_ci return irq; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci hdmi->cec_clk = devm_clk_get(hdmi->dev, "osc_cec"); 6708c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->cec_clk)) { 6718c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->cec_clk); 6728c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get cec_clk: %d\n", ret); 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci hdmi->osc_clk = devm_clk_get(hdmi->dev, "osc_clk"); 6778c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->osc_clk)) { 6788c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->osc_clk); 6798c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get osc_clk: %d\n", ret); 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci hdmi->xclk = devm_clk_get(hdmi->dev, "xclk"); 6848c2ecf20Sopenharmony_ci if (IS_ERR(hdmi->xclk)) { 6858c2ecf20Sopenharmony_ci ret = PTR_ERR(hdmi->xclk); 6868c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get xclk: %d\n", ret); 6878c2ecf20Sopenharmony_ci return ret; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci ret = zx_hdmi_ddc_register(hdmi); 6918c2ecf20Sopenharmony_ci if (ret) { 6928c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret); 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci ret = zx_hdmi_audio_register(hdmi); 6978c2ecf20Sopenharmony_ci if (ret) { 6988c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret); 6998c2ecf20Sopenharmony_ci return ret; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci ret = zx_hdmi_register(drm, hdmi); 7038c2ecf20Sopenharmony_ci if (ret) { 7048c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret); 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, zx_hdmi_irq_handler, 7098c2ecf20Sopenharmony_ci zx_hdmi_irq_thread, IRQF_SHARED, 7108c2ecf20Sopenharmony_ci dev_name(dev), hdmi); 7118c2ecf20Sopenharmony_ci if (ret) { 7128c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret); 7138c2ecf20Sopenharmony_ci return ret; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void zx_hdmi_unbind(struct device *dev, struct device *master, 7208c2ecf20Sopenharmony_ci void *data) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct zx_hdmi *hdmi = dev_get_drvdata(dev); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci hdmi->connector.funcs->destroy(&hdmi->connector); 7258c2ecf20Sopenharmony_ci hdmi->encoder.funcs->destroy(&hdmi->encoder); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (hdmi->audio_pdev) 7288c2ecf20Sopenharmony_ci platform_device_unregister(hdmi->audio_pdev); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic const struct component_ops zx_hdmi_component_ops = { 7328c2ecf20Sopenharmony_ci .bind = zx_hdmi_bind, 7338c2ecf20Sopenharmony_ci .unbind = zx_hdmi_unbind, 7348c2ecf20Sopenharmony_ci}; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int zx_hdmi_probe(struct platform_device *pdev) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &zx_hdmi_component_ops); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int zx_hdmi_remove(struct platform_device *pdev) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci component_del(&pdev->dev, &zx_hdmi_component_ops); 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic const struct of_device_id zx_hdmi_of_match[] = { 7488c2ecf20Sopenharmony_ci { .compatible = "zte,zx296718-hdmi", }, 7498c2ecf20Sopenharmony_ci { /* end */ }, 7508c2ecf20Sopenharmony_ci}; 7518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_hdmi_of_match); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistruct platform_driver zx_hdmi_driver = { 7548c2ecf20Sopenharmony_ci .probe = zx_hdmi_probe, 7558c2ecf20Sopenharmony_ci .remove = zx_hdmi_remove, 7568c2ecf20Sopenharmony_ci .driver = { 7578c2ecf20Sopenharmony_ci .name = "zx-hdmi", 7588c2ecf20Sopenharmony_ci .of_match_table = zx_hdmi_of_match, 7598c2ecf20Sopenharmony_ci }, 7608c2ecf20Sopenharmony_ci}; 761