18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Chrontel CH7033 Video Encoder Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019,2020 Lubomir Rintel 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/regmap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Page 0, Register 0x07 */ 208c2ecf20Sopenharmony_cienum { 218c2ecf20Sopenharmony_ci DRI_PD = BIT(3), 228c2ecf20Sopenharmony_ci IO_PD = BIT(5), 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Page 0, Register 0x08 */ 268c2ecf20Sopenharmony_cienum { 278c2ecf20Sopenharmony_ci DRI_PDDRI = GENMASK(7, 4), 288c2ecf20Sopenharmony_ci PDDAC = GENMASK(3, 1), 298c2ecf20Sopenharmony_ci PANEN = BIT(0), 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Page 0, Register 0x09 */ 338c2ecf20Sopenharmony_cienum { 348c2ecf20Sopenharmony_ci DPD = BIT(7), 358c2ecf20Sopenharmony_ci GCKOFF = BIT(6), 368c2ecf20Sopenharmony_ci TV_BP = BIT(5), 378c2ecf20Sopenharmony_ci SCLPD = BIT(4), 388c2ecf20Sopenharmony_ci SDPD = BIT(3), 398c2ecf20Sopenharmony_ci VGA_PD = BIT(2), 408c2ecf20Sopenharmony_ci HDBKPD = BIT(1), 418c2ecf20Sopenharmony_ci HDMI_PD = BIT(0), 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Page 0, Register 0x0a */ 458c2ecf20Sopenharmony_cienum { 468c2ecf20Sopenharmony_ci MEMINIT = BIT(7), 478c2ecf20Sopenharmony_ci MEMIDLE = BIT(6), 488c2ecf20Sopenharmony_ci MEMPD = BIT(5), 498c2ecf20Sopenharmony_ci STOP = BIT(4), 508c2ecf20Sopenharmony_ci LVDS_PD = BIT(3), 518c2ecf20Sopenharmony_ci HD_DVIB = BIT(2), 528c2ecf20Sopenharmony_ci HDCP_PD = BIT(1), 538c2ecf20Sopenharmony_ci MCU_PD = BIT(0), 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Page 0, Register 0x18 */ 578c2ecf20Sopenharmony_cienum { 588c2ecf20Sopenharmony_ci IDF = GENMASK(7, 4), 598c2ecf20Sopenharmony_ci INTEN = BIT(3), 608c2ecf20Sopenharmony_ci SWAP = GENMASK(2, 0), 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cienum { 648c2ecf20Sopenharmony_ci BYTE_SWAP_RGB = 0, 658c2ecf20Sopenharmony_ci BYTE_SWAP_RBG = 1, 668c2ecf20Sopenharmony_ci BYTE_SWAP_GRB = 2, 678c2ecf20Sopenharmony_ci BYTE_SWAP_GBR = 3, 688c2ecf20Sopenharmony_ci BYTE_SWAP_BRG = 4, 698c2ecf20Sopenharmony_ci BYTE_SWAP_BGR = 5, 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Page 0, Register 0x19 */ 738c2ecf20Sopenharmony_cienum { 748c2ecf20Sopenharmony_ci HPO_I = BIT(5), 758c2ecf20Sopenharmony_ci VPO_I = BIT(4), 768c2ecf20Sopenharmony_ci DEPO_I = BIT(3), 778c2ecf20Sopenharmony_ci CRYS_EN = BIT(2), 788c2ecf20Sopenharmony_ci GCLKFREQ = GENMASK(2, 0), 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Page 0, Register 0x2e */ 828c2ecf20Sopenharmony_cienum { 838c2ecf20Sopenharmony_ci HFLIP = BIT(7), 848c2ecf20Sopenharmony_ci VFLIP = BIT(6), 858c2ecf20Sopenharmony_ci DEPO_O = BIT(5), 868c2ecf20Sopenharmony_ci HPO_O = BIT(4), 878c2ecf20Sopenharmony_ci VPO_O = BIT(3), 888c2ecf20Sopenharmony_ci TE = GENMASK(2, 0), 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* Page 0, Register 0x2b */ 928c2ecf20Sopenharmony_cienum { 938c2ecf20Sopenharmony_ci SWAPS = GENMASK(7, 4), 948c2ecf20Sopenharmony_ci VFMT = GENMASK(3, 0), 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Page 0, Register 0x54 */ 988c2ecf20Sopenharmony_cienum { 998c2ecf20Sopenharmony_ci COMP_BP = BIT(7), 1008c2ecf20Sopenharmony_ci DAC_EN_T = BIT(6), 1018c2ecf20Sopenharmony_ci HWO_HDMI_HI = GENMASK(5, 3), 1028c2ecf20Sopenharmony_ci HOO_HDMI_HI = GENMASK(2, 0), 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Page 0, Register 0x57 */ 1068c2ecf20Sopenharmony_cienum { 1078c2ecf20Sopenharmony_ci FLDSEN = BIT(7), 1088c2ecf20Sopenharmony_ci VWO_HDMI_HI = GENMASK(5, 3), 1098c2ecf20Sopenharmony_ci VOO_HDMI_HI = GENMASK(2, 0), 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Page 0, Register 0x7e */ 1138c2ecf20Sopenharmony_cienum { 1148c2ecf20Sopenharmony_ci HDMI_LVDS_SEL = BIT(7), 1158c2ecf20Sopenharmony_ci DE_GEN = BIT(6), 1168c2ecf20Sopenharmony_ci PWM_INDEX_HI = BIT(5), 1178c2ecf20Sopenharmony_ci USE_DE = BIT(4), 1188c2ecf20Sopenharmony_ci R_INT = GENMASK(3, 0), 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Page 1, Register 0x07 */ 1228c2ecf20Sopenharmony_cienum { 1238c2ecf20Sopenharmony_ci BPCKSEL = BIT(7), 1248c2ecf20Sopenharmony_ci DRI_CMFB_EN = BIT(6), 1258c2ecf20Sopenharmony_ci CEC_PUEN = BIT(5), 1268c2ecf20Sopenharmony_ci CEC_T = BIT(3), 1278c2ecf20Sopenharmony_ci CKINV = BIT(2), 1288c2ecf20Sopenharmony_ci CK_TVINV = BIT(1), 1298c2ecf20Sopenharmony_ci DRI_CKS2 = BIT(0), 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Page 1, Register 0x08 */ 1338c2ecf20Sopenharmony_cienum { 1348c2ecf20Sopenharmony_ci DACG = BIT(6), 1358c2ecf20Sopenharmony_ci DACKTST = BIT(5), 1368c2ecf20Sopenharmony_ci DEDGEB = BIT(4), 1378c2ecf20Sopenharmony_ci SYO = BIT(3), 1388c2ecf20Sopenharmony_ci DRI_IT_LVDS = GENMASK(2, 1), 1398c2ecf20Sopenharmony_ci DISPON = BIT(0), 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* Page 1, Register 0x0c */ 1438c2ecf20Sopenharmony_cienum { 1448c2ecf20Sopenharmony_ci DRI_PLL_CP = GENMASK(7, 6), 1458c2ecf20Sopenharmony_ci DRI_PLL_DIVSEL = BIT(5), 1468c2ecf20Sopenharmony_ci DRI_PLL_N1_1 = BIT(4), 1478c2ecf20Sopenharmony_ci DRI_PLL_N1_0 = BIT(3), 1488c2ecf20Sopenharmony_ci DRI_PLL_N3_1 = BIT(2), 1498c2ecf20Sopenharmony_ci DRI_PLL_N3_0 = BIT(1), 1508c2ecf20Sopenharmony_ci DRI_PLL_CKTSTEN = BIT(0), 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Page 1, Register 0x6b */ 1548c2ecf20Sopenharmony_cienum { 1558c2ecf20Sopenharmony_ci VCO3CS = GENMASK(7, 6), 1568c2ecf20Sopenharmony_ci ICPGBK2_0 = GENMASK(5, 3), 1578c2ecf20Sopenharmony_ci DRI_VCO357SC = BIT(2), 1588c2ecf20Sopenharmony_ci PDPLL2 = BIT(1), 1598c2ecf20Sopenharmony_ci DRI_PD_SER = BIT(0), 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Page 1, Register 0x6c */ 1638c2ecf20Sopenharmony_cienum { 1648c2ecf20Sopenharmony_ci PLL2N11 = GENMASK(7, 4), 1658c2ecf20Sopenharmony_ci PLL2N5_4 = BIT(3), 1668c2ecf20Sopenharmony_ci PLL2N5_TOP = BIT(2), 1678c2ecf20Sopenharmony_ci DRI_PLL_PD = BIT(1), 1688c2ecf20Sopenharmony_ci PD_I2CM = BIT(0), 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Page 3, Register 0x28 */ 1728c2ecf20Sopenharmony_cienum { 1738c2ecf20Sopenharmony_ci DIFF_EN = GENMASK(7, 6), 1748c2ecf20Sopenharmony_ci CORREC_EN = GENMASK(5, 4), 1758c2ecf20Sopenharmony_ci VGACLK_BP = BIT(3), 1768c2ecf20Sopenharmony_ci HM_LV_SEL = BIT(2), 1778c2ecf20Sopenharmony_ci HD_VGA_SEL = BIT(1), 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* Page 3, Register 0x2a */ 1818c2ecf20Sopenharmony_cienum { 1828c2ecf20Sopenharmony_ci LVDSCLK_BP = BIT(7), 1838c2ecf20Sopenharmony_ci HDTVCLK_BP = BIT(6), 1848c2ecf20Sopenharmony_ci HDMICLK_BP = BIT(5), 1858c2ecf20Sopenharmony_ci HDTV_BP = BIT(4), 1868c2ecf20Sopenharmony_ci HDMI_BP = BIT(3), 1878c2ecf20Sopenharmony_ci THRWL = GENMASK(2, 0), 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* Page 4, Register 0x52 */ 1918c2ecf20Sopenharmony_cienum { 1928c2ecf20Sopenharmony_ci PGM_ARSTB = BIT(7), 1938c2ecf20Sopenharmony_ci MCU_ARSTB = BIT(6), 1948c2ecf20Sopenharmony_ci MCU_RETB = BIT(2), 1958c2ecf20Sopenharmony_ci RESETIB = BIT(1), 1968c2ecf20Sopenharmony_ci RESETDB = BIT(0), 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistruct ch7033_priv { 2008c2ecf20Sopenharmony_ci struct regmap *regmap; 2018c2ecf20Sopenharmony_ci struct drm_bridge *next_bridge; 2028c2ecf20Sopenharmony_ci struct drm_bridge bridge; 2038c2ecf20Sopenharmony_ci struct drm_connector connector; 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define conn_to_ch7033_priv(x) \ 2078c2ecf20Sopenharmony_ci container_of(x, struct ch7033_priv, connector) 2088c2ecf20Sopenharmony_ci#define bridge_to_ch7033_priv(x) \ 2098c2ecf20Sopenharmony_ci container_of(x, struct ch7033_priv, bridge) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic enum drm_connector_status ch7033_connector_detect( 2138c2ecf20Sopenharmony_ci struct drm_connector *connector, bool force) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ch7033_priv *priv = conn_to_ch7033_priv(connector); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return drm_bridge_detect(priv->next_bridge); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs ch7033_connector_funcs = { 2218c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 2228c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 2238c2ecf20Sopenharmony_ci .detect = ch7033_connector_detect, 2248c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 2258c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 2268c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int ch7033_connector_get_modes(struct drm_connector *connector) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct ch7033_priv *priv = conn_to_ch7033_priv(connector); 2328c2ecf20Sopenharmony_ci struct edid *edid; 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci edid = drm_bridge_get_edid(priv->next_bridge, connector); 2368c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 2378c2ecf20Sopenharmony_ci if (edid) { 2388c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 2398c2ecf20Sopenharmony_ci kfree(edid); 2408c2ecf20Sopenharmony_ci } else { 2418c2ecf20Sopenharmony_ci ret = drm_add_modes_noedid(connector, 1920, 1080); 2428c2ecf20Sopenharmony_ci drm_set_preferred_mode(connector, 1024, 768); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct drm_encoder *ch7033_connector_best_encoder( 2498c2ecf20Sopenharmony_ci struct drm_connector *connector) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct ch7033_priv *priv = conn_to_ch7033_priv(connector); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return priv->bridge.encoder; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs ch7033_connector_helper_funcs = { 2578c2ecf20Sopenharmony_ci .get_modes = ch7033_connector_get_modes, 2588c2ecf20Sopenharmony_ci .best_encoder = ch7033_connector_best_encoder, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void ch7033_hpd_event(void *arg, enum drm_connector_status status) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct ch7033_priv *priv = arg; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (priv->bridge.dev) 2668c2ecf20Sopenharmony_ci drm_helper_hpd_irq_event(priv->connector.dev); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int ch7033_bridge_attach(struct drm_bridge *bridge, 2708c2ecf20Sopenharmony_ci enum drm_bridge_attach_flags flags) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge); 2738c2ecf20Sopenharmony_ci struct drm_connector *connector = &priv->connector; 2748c2ecf20Sopenharmony_ci int ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = drm_bridge_attach(bridge->encoder, priv->next_bridge, bridge, 2778c2ecf20Sopenharmony_ci DRM_BRIDGE_ATTACH_NO_CONNECTOR); 2788c2ecf20Sopenharmony_ci if (ret) 2798c2ecf20Sopenharmony_ci return ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (priv->next_bridge->ops & DRM_BRIDGE_OP_DETECT) { 2858c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_CONNECT | 2888c2ecf20Sopenharmony_ci DRM_CONNECTOR_POLL_DISCONNECT; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD) { 2928c2ecf20Sopenharmony_ci drm_bridge_hpd_enable(priv->next_bridge, ch7033_hpd_event, 2938c2ecf20Sopenharmony_ci priv); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, 2978c2ecf20Sopenharmony_ci &ch7033_connector_helper_funcs); 2988c2ecf20Sopenharmony_ci ret = drm_connector_init_with_ddc(bridge->dev, &priv->connector, 2998c2ecf20Sopenharmony_ci &ch7033_connector_funcs, 3008c2ecf20Sopenharmony_ci priv->next_bridge->type, 3018c2ecf20Sopenharmony_ci priv->next_bridge->ddc); 3028c2ecf20Sopenharmony_ci if (ret) { 3038c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize connector\n"); 3048c2ecf20Sopenharmony_ci return ret; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return drm_connector_attach_encoder(&priv->connector, bridge->encoder); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void ch7033_bridge_detach(struct drm_bridge *bridge) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD) 3158c2ecf20Sopenharmony_ci drm_bridge_hpd_disable(priv->next_bridge); 3168c2ecf20Sopenharmony_ci drm_connector_cleanup(&priv->connector); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic enum drm_mode_status ch7033_bridge_mode_valid(struct drm_bridge *bridge, 3208c2ecf20Sopenharmony_ci const struct drm_display_info *info, 3218c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci if (mode->clock > 165000) 3248c2ecf20Sopenharmony_ci return MODE_CLOCK_HIGH; 3258c2ecf20Sopenharmony_ci if (mode->hdisplay >= 1920) 3268c2ecf20Sopenharmony_ci return MODE_BAD_HVALUE; 3278c2ecf20Sopenharmony_ci if (mode->vdisplay >= 1080) 3288c2ecf20Sopenharmony_ci return MODE_BAD_VVALUE; 3298c2ecf20Sopenharmony_ci return MODE_OK; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void ch7033_bridge_disable(struct drm_bridge *bridge) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x04); 3378c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x52, RESETDB, 0x00); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void ch7033_bridge_enable(struct drm_bridge *bridge) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x04); 3458c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x52, RESETDB, RESETDB); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void ch7033_bridge_mode_set(struct drm_bridge *bridge, 3498c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 3508c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge); 3538c2ecf20Sopenharmony_ci int hbporch = mode->hsync_start - mode->hdisplay; 3548c2ecf20Sopenharmony_ci int hsynclen = mode->hsync_end - mode->hsync_start; 3558c2ecf20Sopenharmony_ci int vbporch = mode->vsync_start - mode->vdisplay; 3568c2ecf20Sopenharmony_ci int vsynclen = mode->vsync_end - mode->vsync_start; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * Page 4 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x04); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Turn everything off to set all the registers to their defaults. */ 3648c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x52, 0x00); 3658c2ecf20Sopenharmony_ci /* Bring I/O block up. */ 3668c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x52, RESETIB); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * Page 0 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x00); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Bring up parts we need from the power down. */ 3748c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x07, DRI_PD | IO_PD, 0); 3758c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x08, DRI_PDDRI | PDDAC | PANEN, 0); 3768c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x09, DPD | GCKOFF | 3778c2ecf20Sopenharmony_ci HDMI_PD | VGA_PD, 0); 3788c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x0a, HD_DVIB, 0); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Horizontal input timing. */ 3818c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x0b, (mode->htotal >> 8) << 3 | 3828c2ecf20Sopenharmony_ci (mode->hdisplay >> 8)); 3838c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x0c, mode->hdisplay); 3848c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x0d, mode->htotal); 3858c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x0e, (hsynclen >> 8) << 3 | 3868c2ecf20Sopenharmony_ci (hbporch >> 8)); 3878c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x0f, hbporch); 3888c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x10, hsynclen); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Vertical input timing. */ 3918c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x11, (mode->vtotal >> 8) << 3 | 3928c2ecf20Sopenharmony_ci (mode->vdisplay >> 8)); 3938c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x12, mode->vdisplay); 3948c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x13, mode->vtotal); 3958c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x14, ((vsynclen >> 8) << 3) | 3968c2ecf20Sopenharmony_ci (vbporch >> 8)); 3978c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x15, vbporch); 3988c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x16, vsynclen); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Input color swap. */ 4018c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x18, SWAP, BYTE_SWAP_BGR); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Input clock and sync polarity. */ 4048c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x19, 0x1, mode->clock >> 16); 4058c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x19, HPO_I | VPO_I | GCLKFREQ, 4068c2ecf20Sopenharmony_ci (mode->flags & DRM_MODE_FLAG_PHSYNC) ? HPO_I : 0 | 4078c2ecf20Sopenharmony_ci (mode->flags & DRM_MODE_FLAG_PVSYNC) ? VPO_I : 0 | 4088c2ecf20Sopenharmony_ci mode->clock >> 16); 4098c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x1a, mode->clock >> 8); 4108c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x1b, mode->clock); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Horizontal output timing. */ 4138c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x1f, (mode->htotal >> 8) << 3 | 4148c2ecf20Sopenharmony_ci (mode->hdisplay >> 8)); 4158c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x20, mode->hdisplay); 4168c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x21, mode->htotal); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* Vertical output timing. */ 4198c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x25, (mode->vtotal >> 8) << 3 | 4208c2ecf20Sopenharmony_ci (mode->vdisplay >> 8)); 4218c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x26, mode->vdisplay); 4228c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x27, mode->vtotal); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* VGA channel bypass */ 4258c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x2b, VFMT, 9); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Output sync polarity. */ 4288c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x2e, HPO_O | VPO_O, 4298c2ecf20Sopenharmony_ci (mode->flags & DRM_MODE_FLAG_PHSYNC) ? HPO_O : 0 | 4308c2ecf20Sopenharmony_ci (mode->flags & DRM_MODE_FLAG_PVSYNC) ? VPO_O : 0); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* HDMI horizontal output timing. */ 4338c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x54, HWO_HDMI_HI | HOO_HDMI_HI, 4348c2ecf20Sopenharmony_ci (hsynclen >> 8) << 3 | 4358c2ecf20Sopenharmony_ci (hbporch >> 8)); 4368c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x55, hbporch); 4378c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x56, hsynclen); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* HDMI vertical output timing. */ 4408c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x57, VWO_HDMI_HI | VOO_HDMI_HI, 4418c2ecf20Sopenharmony_ci (vsynclen >> 8) << 3 | 4428c2ecf20Sopenharmony_ci (vbporch >> 8)); 4438c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x58, vbporch); 4448c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x59, vsynclen); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Pick HDMI, not LVDS. */ 4478c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x7e, HDMI_LVDS_SEL, HDMI_LVDS_SEL); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * Page 1 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x01); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* No idea what these do, but VGA is wobbly and blinky without them. */ 4558c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x07, CKINV, CKINV); 4568c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x08, DISPON, DISPON); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* DRI PLL */ 4598c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_DIVSEL, DRI_PLL_DIVSEL); 4608c2ecf20Sopenharmony_ci if (mode->clock <= 40000) { 4618c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 | 4628c2ecf20Sopenharmony_ci DRI_PLL_N1_0 | 4638c2ecf20Sopenharmony_ci DRI_PLL_N3_1 | 4648c2ecf20Sopenharmony_ci DRI_PLL_N3_0, 4658c2ecf20Sopenharmony_ci 0); 4668c2ecf20Sopenharmony_ci } else if (mode->clock < 80000) { 4678c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 | 4688c2ecf20Sopenharmony_ci DRI_PLL_N1_0 | 4698c2ecf20Sopenharmony_ci DRI_PLL_N3_1 | 4708c2ecf20Sopenharmony_ci DRI_PLL_N3_0, 4718c2ecf20Sopenharmony_ci DRI_PLL_N3_0 | 4728c2ecf20Sopenharmony_ci DRI_PLL_N1_0); 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x0c, DRI_PLL_N1_1 | 4758c2ecf20Sopenharmony_ci DRI_PLL_N1_0 | 4768c2ecf20Sopenharmony_ci DRI_PLL_N3_1 | 4778c2ecf20Sopenharmony_ci DRI_PLL_N3_0, 4788c2ecf20Sopenharmony_ci DRI_PLL_N3_1 | 4798c2ecf20Sopenharmony_ci DRI_PLL_N1_1); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* This seems to be color calibration for VGA. */ 4838c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x64, 0x29); /* LSB Blue */ 4848c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x65, 0x29); /* LSB Green */ 4858c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x66, 0x29); /* LSB Red */ 4868c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x67, 0x00); /* MSB Blue */ 4878c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x68, 0x00); /* MSB Green */ 4888c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x69, 0x00); /* MSB Red */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x6b, DRI_PD_SER, 0x00); 4918c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x6c, DRI_PLL_PD, 0x00); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* 4948c2ecf20Sopenharmony_ci * Page 3 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x03); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* More bypasses and apparently another HDMI/LVDS selector. */ 4998c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x28, VGACLK_BP | HM_LV_SEL, 5008c2ecf20Sopenharmony_ci VGACLK_BP | HM_LV_SEL); 5018c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, 0x2a, HDMICLK_BP | HDMI_BP, 5028c2ecf20Sopenharmony_ci HDMICLK_BP | HDMI_BP); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Page 4 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x04); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Output clock. */ 5108c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x10, mode->clock >> 16); 5118c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x11, mode->clock >> 8); 5128c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x12, mode->clock); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs ch7033_bridge_funcs = { 5168c2ecf20Sopenharmony_ci .attach = ch7033_bridge_attach, 5178c2ecf20Sopenharmony_ci .detach = ch7033_bridge_detach, 5188c2ecf20Sopenharmony_ci .mode_valid = ch7033_bridge_mode_valid, 5198c2ecf20Sopenharmony_ci .disable = ch7033_bridge_disable, 5208c2ecf20Sopenharmony_ci .enable = ch7033_bridge_enable, 5218c2ecf20Sopenharmony_ci .mode_set = ch7033_bridge_mode_set, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct regmap_config ch7033_regmap_config = { 5258c2ecf20Sopenharmony_ci .reg_bits = 8, 5268c2ecf20Sopenharmony_ci .val_bits = 8, 5278c2ecf20Sopenharmony_ci .max_register = 0x7f, 5288c2ecf20Sopenharmony_ci}; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int ch7033_probe(struct i2c_client *client, 5318c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 5348c2ecf20Sopenharmony_ci struct ch7033_priv *priv; 5358c2ecf20Sopenharmony_ci unsigned int val; 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!priv) 5408c2ecf20Sopenharmony_ci return -ENOMEM; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL, 5458c2ecf20Sopenharmony_ci &priv->next_bridge); 5468c2ecf20Sopenharmony_ci if (ret) 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci priv->regmap = devm_regmap_init_i2c(client, &ch7033_regmap_config); 5508c2ecf20Sopenharmony_ci if (IS_ERR(priv->regmap)) { 5518c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap init failed\n"); 5528c2ecf20Sopenharmony_ci return PTR_ERR(priv->regmap); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, 0x00, &val); 5568c2ecf20Sopenharmony_ci if (ret < 0) { 5578c2ecf20Sopenharmony_ci dev_err(&client->dev, "error reading the model id: %d\n", ret); 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci if ((val & 0xf7) != 0x56) { 5618c2ecf20Sopenharmony_ci dev_err(&client->dev, "the device is not a ch7033\n"); 5628c2ecf20Sopenharmony_ci return -ENODEV; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x03, 0x04); 5668c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, 0x51, &val); 5678c2ecf20Sopenharmony_ci if (ret < 0) { 5688c2ecf20Sopenharmony_ci dev_err(&client->dev, "error reading the model id: %d\n", ret); 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci if ((val & 0x0f) != 3) { 5728c2ecf20Sopenharmony_ci dev_err(&client->dev, "unknown revision %u\n", val); 5738c2ecf20Sopenharmony_ci return -ENODEV; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->bridge.list); 5778c2ecf20Sopenharmony_ci priv->bridge.funcs = &ch7033_bridge_funcs; 5788c2ecf20Sopenharmony_ci priv->bridge.of_node = dev->of_node; 5798c2ecf20Sopenharmony_ci drm_bridge_add(&priv->bridge); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci dev_info(dev, "Chrontel CH7033 Video Encoder\n"); 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int ch7033_remove(struct i2c_client *client) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 5888c2ecf20Sopenharmony_ci struct ch7033_priv *priv = dev_get_drvdata(dev); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci drm_bridge_remove(&priv->bridge); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic const struct of_device_id ch7033_dt_ids[] = { 5968c2ecf20Sopenharmony_ci { .compatible = "chrontel,ch7033", }, 5978c2ecf20Sopenharmony_ci { } 5988c2ecf20Sopenharmony_ci}; 5998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ch7033_dt_ids); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic const struct i2c_device_id ch7033_ids[] = { 6028c2ecf20Sopenharmony_ci { "ch7033", 0 }, 6038c2ecf20Sopenharmony_ci { } 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ch7033_ids); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic struct i2c_driver ch7033_driver = { 6088c2ecf20Sopenharmony_ci .probe = ch7033_probe, 6098c2ecf20Sopenharmony_ci .remove = ch7033_remove, 6108c2ecf20Sopenharmony_ci .driver = { 6118c2ecf20Sopenharmony_ci .name = "ch7033", 6128c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ch7033_dt_ids), 6138c2ecf20Sopenharmony_ci }, 6148c2ecf20Sopenharmony_ci .id_table = ch7033_ids, 6158c2ecf20Sopenharmony_ci}; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cimodule_i2c_driver(ch7033_driver); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 6208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Chrontel CH7033 Video Encoder Driver"); 6218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 622