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