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