1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) Rockchip Electronics Co., Ltd. 4 * Zheng Yang <zhengyang@rock-chips.com> 5 * Andy Yan <andy.yan@rock-chips.com> 6 */ 7 #include <linux/err.h> 8 #include <linux/hw_bitfield.h> 9 #include <linux/mfd/syscon.h> 10 #include <linux/mod_devicetable.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 15 #include <drm/bridge/inno_hdmi.h> 16 #include <drm/drm_bridge_connector.h> 17 #include <drm/drm_managed.h> 18 #include <drm/drm_of.h> 19 20 #include "rockchip_drm_drv.h" 21 22 #define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16) 23 24 #define RK3036_GRF_SOC_CON2 0x148 25 #define RK3036_HDMI_PHSYNC BIT(4) 26 #define RK3036_HDMI_PVSYNC BIT(5) 27 28 enum inno_hdmi_dev_type { 29 RK3036_HDMI, 30 RK3128_HDMI, 31 }; 32 33 struct inno_hdmi_connector_state { 34 struct drm_connector_state base; 35 unsigned int colorimetry; 36 }; 37 38 struct rockchip_inno_hdmi { 39 struct inno_hdmi *base; 40 struct device *dev; 41 struct regmap *grf; 42 struct rockchip_encoder encoder; 43 }; 44 45 static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = { 46 { 74250000, 0x3f, 0xbb }, 47 { 165000000, 0x6f, 0xbb }, 48 { ~0UL, 0x00, 0x00 } 49 }; 50 51 static struct inno_hdmi_phy_config rk3128_hdmi_phy_configs[] = { 52 { 74250000, 0x3f, 0xaa }, 53 { 165000000, 0x5f, 0xaa }, 54 { ~0UL, 0x00, 0x00 } 55 }; 56 57 static void inno_hdmi_rk3036_enable(struct device *dev, struct drm_display_mode *mode) 58 { 59 struct rockchip_inno_hdmi *hdmi = dev_get_drvdata(dev); 60 int value, psync; 61 62 psync = mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0; 63 value = FIELD_PREP_WM16(RK3036_HDMI_PHSYNC, psync); 64 psync = mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0; 65 value |= FIELD_PREP_WM16(RK3036_HDMI_PVSYNC, psync); 66 regmap_write(hdmi->grf, RK3036_GRF_SOC_CON2, value); 67 } 68 69 static int inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder, 70 struct drm_crtc_state *crtc_state, 71 struct drm_connector_state *conn_state) 72 { 73 struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 74 75 s->output_mode = ROCKCHIP_OUT_MODE_P888; 76 s->output_type = DRM_MODE_CONNECTOR_HDMIA; 77 78 return 0; 79 } 80 81 static const struct drm_encoder_helper_funcs inno_hdmi_rockchip_encoder_helper_funcs = { 82 .atomic_check = inno_hdmi_encoder_atomic_check, 83 }; 84 85 static int inno_hdmi_rockchip_bind(struct device *dev, struct device *master, void *data) 86 { 87 struct drm_device *drm = data; 88 struct drm_connector *connector; 89 struct drm_encoder *encoder; 90 struct rockchip_inno_hdmi *hdmi; 91 const struct inno_hdmi_plat_data *plat_data; 92 int ret; 93 94 hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL); 95 if (!hdmi) 96 return -ENOMEM; 97 98 hdmi->dev = dev; 99 100 plat_data = of_device_get_match_data(hdmi->dev); 101 if (!plat_data) 102 return -EINVAL; 103 104 if (of_device_is_compatible(dev->of_node, "rockchip,rk3036-inno-hdmi")) { 105 hdmi->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); 106 if (IS_ERR(hdmi->grf)) 107 return dev_err_probe(dev, 108 PTR_ERR(hdmi->grf), "Unable to get rockchip,grf\n"); 109 } 110 111 encoder = &hdmi->encoder.encoder; 112 encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); 113 114 /* 115 * If we failed to find the CRTC(s) which this encoder is 116 * supposed to be connected to, it's because the CRTC has 117 * not been registered yet. Defer probing, and hope that 118 * the required CRTC is added later. 119 */ 120 if (encoder->possible_crtcs == 0) 121 return -EPROBE_DEFER; 122 123 ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL); 124 if (ret) 125 return ret; 126 127 drm_encoder_helper_add(encoder, &inno_hdmi_rockchip_encoder_helper_funcs); 128 129 dev_set_drvdata(dev, hdmi); 130 131 hdmi->base = inno_hdmi_bind(dev, encoder, plat_data); 132 133 connector = drm_bridge_connector_init(drm, encoder); 134 if (IS_ERR(connector)) { 135 ret = PTR_ERR(connector); 136 dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret); 137 return ret; 138 } 139 140 return 0; 141 } 142 143 static const struct component_ops inno_hdmi_rockchip_ops = { 144 .bind = inno_hdmi_rockchip_bind, 145 }; 146 147 static int inno_hdmi_rockchip_probe(struct platform_device *pdev) 148 { 149 return component_add(&pdev->dev, &inno_hdmi_rockchip_ops); 150 } 151 152 static void inno_hdmi_rockchip_remove(struct platform_device *pdev) 153 { 154 component_del(&pdev->dev, &inno_hdmi_rockchip_ops); 155 } 156 157 static const struct inno_hdmi_plat_ops rk3036_inno_hdmi_plat_ops = { 158 .enable = inno_hdmi_rk3036_enable, 159 }; 160 161 static const struct inno_hdmi_plat_data rk3036_inno_hdmi_plat_data = { 162 .ops = &rk3036_inno_hdmi_plat_ops, 163 .phy_configs = rk3036_hdmi_phy_configs, 164 .default_phy_config = &rk3036_hdmi_phy_configs[1], 165 }; 166 167 static const struct inno_hdmi_plat_data rk3128_inno_hdmi_plat_data = { 168 .phy_configs = rk3128_hdmi_phy_configs, 169 .default_phy_config = &rk3128_hdmi_phy_configs[1], 170 }; 171 172 static const struct of_device_id inno_hdmi_rockchip_dt_ids[] = { 173 { .compatible = "rockchip,rk3036-inno-hdmi", 174 .data = &rk3036_inno_hdmi_plat_data, 175 }, 176 { .compatible = "rockchip,rk3128-inno-hdmi", 177 .data = &rk3128_inno_hdmi_plat_data, 178 }, 179 {}, 180 }; 181 MODULE_DEVICE_TABLE(of, inno_hdmi_rockchip_dt_ids); 182 183 struct platform_driver inno_hdmi_driver = { 184 .probe = inno_hdmi_rockchip_probe, 185 .remove = inno_hdmi_rockchip_remove, 186 .driver = { 187 .name = "innohdmi-rockchip", 188 .of_match_table = inno_hdmi_rockchip_dt_ids, 189 }, 190 }; 191