18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Sanechips Technology Co., Ltd. 48c2ecf20Sopenharmony_ci * Copyright 2017 Linaro Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "zx_drm_drv.h" 208c2ecf20Sopenharmony_ci#include "zx_vga_regs.h" 218c2ecf20Sopenharmony_ci#include "zx_vou.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct zx_vga_pwrctrl { 248c2ecf20Sopenharmony_ci struct regmap *regmap; 258c2ecf20Sopenharmony_ci u32 reg; 268c2ecf20Sopenharmony_ci u32 mask; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct zx_vga_i2c { 308c2ecf20Sopenharmony_ci struct i2c_adapter adap; 318c2ecf20Sopenharmony_ci struct mutex lock; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct zx_vga { 358c2ecf20Sopenharmony_ci struct drm_connector connector; 368c2ecf20Sopenharmony_ci struct drm_encoder encoder; 378c2ecf20Sopenharmony_ci struct zx_vga_i2c *ddc; 388c2ecf20Sopenharmony_ci struct device *dev; 398c2ecf20Sopenharmony_ci void __iomem *mmio; 408c2ecf20Sopenharmony_ci struct clk *i2c_wclk; 418c2ecf20Sopenharmony_ci struct zx_vga_pwrctrl pwrctrl; 428c2ecf20Sopenharmony_ci struct completion complete; 438c2ecf20Sopenharmony_ci bool connected; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define to_zx_vga(x) container_of(x, struct zx_vga, x) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void zx_vga_encoder_enable(struct drm_encoder *encoder) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct zx_vga *vga = to_zx_vga(encoder); 518c2ecf20Sopenharmony_ci struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Set bit to power up VGA DACs */ 548c2ecf20Sopenharmony_ci regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 558c2ecf20Sopenharmony_ci pwrctrl->mask); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci vou_inf_enable(VOU_VGA, encoder->crtc); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void zx_vga_encoder_disable(struct drm_encoder *encoder) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct zx_vga *vga = to_zx_vga(encoder); 638c2ecf20Sopenharmony_ci struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci vou_inf_disable(VOU_VGA, encoder->crtc); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Clear bit to power down VGA DACs */ 688c2ecf20Sopenharmony_ci regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = { 728c2ecf20Sopenharmony_ci .enable = zx_vga_encoder_enable, 738c2ecf20Sopenharmony_ci .disable = zx_vga_encoder_disable, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int zx_vga_connector_get_modes(struct drm_connector *connector) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct zx_vga *vga = to_zx_vga(connector); 798c2ecf20Sopenharmony_ci struct edid *edid; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * Clear both detection bits to switch I2C bus from device 848c2ecf20Sopenharmony_ci * detecting to EDID reading. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, &vga->ddc->adap); 898c2ecf20Sopenharmony_ci if (!edid) { 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * If EDID reading fails, we set the device state into 928c2ecf20Sopenharmony_ci * disconnected. Locking is not required here, since the 938c2ecf20Sopenharmony_ci * VGA_AUTO_DETECT_SEL register write in irq handler cannot 948c2ecf20Sopenharmony_ci * be triggered when both detection bits are cleared as above. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 978c2ecf20Sopenharmony_ci VGA_DETECT_SEL_NO_DEVICE); 988c2ecf20Sopenharmony_ci vga->connected = false; 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * As edid reading succeeds, device must be connected, so we set 1048c2ecf20Sopenharmony_ci * up detection bit for unplug interrupt here. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 1098c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 1108c2ecf20Sopenharmony_ci kfree(edid); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic enum drm_mode_status 1168c2ecf20Sopenharmony_cizx_vga_connector_mode_valid(struct drm_connector *connector, 1178c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci return MODE_OK; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = { 1238c2ecf20Sopenharmony_ci .get_modes = zx_vga_connector_get_modes, 1248c2ecf20Sopenharmony_ci .mode_valid = zx_vga_connector_mode_valid, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic enum drm_connector_status 1288c2ecf20Sopenharmony_cizx_vga_connector_detect(struct drm_connector *connector, bool force) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct zx_vga *vga = to_zx_vga(connector); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return vga->connected ? connector_status_connected : 1338c2ecf20Sopenharmony_ci connector_status_disconnected; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs zx_vga_connector_funcs = { 1378c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1388c2ecf20Sopenharmony_ci .detect = zx_vga_connector_detect, 1398c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 1408c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 1418c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 1428c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int zx_vga_register(struct drm_device *drm, struct zx_vga *vga) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &vga->encoder; 1488c2ecf20Sopenharmony_ci struct drm_connector *connector = &vga->connector; 1498c2ecf20Sopenharmony_ci struct device *dev = vga->dev; 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci encoder->possible_crtcs = VOU_CRTC_MASK; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DAC); 1558c2ecf20Sopenharmony_ci if (ret) { 1568c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret); 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci vga->connector.polled = DRM_CONNECTOR_POLL_HPD; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = drm_connector_init_with_ddc(drm, connector, 1658c2ecf20Sopenharmony_ci &zx_vga_connector_funcs, 1668c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_VGA, 1678c2ecf20Sopenharmony_ci &vga->ddc->adap); 1688c2ecf20Sopenharmony_ci if (ret) { 1698c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret); 1708c2ecf20Sopenharmony_ci goto clean_encoder; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 1768c2ecf20Sopenharmony_ci if (ret) { 1778c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret); 1788c2ecf20Sopenharmony_ci goto clean_connector; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciclean_connector: 1848c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 1858c2ecf20Sopenharmony_ciclean_encoder: 1868c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int zx_vga_pwrctrl_init(struct zx_vga *vga) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; 1938c2ecf20Sopenharmony_ci struct device *dev = vga->dev; 1948c2ecf20Sopenharmony_ci struct of_phandle_args out_args; 1958c2ecf20Sopenharmony_ci struct regmap *regmap; 1968c2ecf20Sopenharmony_ci int ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(dev->of_node, 1998c2ecf20Sopenharmony_ci "zte,vga-power-control", 2, 0, &out_args); 2008c2ecf20Sopenharmony_ci if (ret) 2018c2ecf20Sopenharmony_ci return ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(out_args.np); 2048c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 2058c2ecf20Sopenharmony_ci ret = PTR_ERR(regmap); 2068c2ecf20Sopenharmony_ci goto out; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci pwrctrl->regmap = regmap; 2108c2ecf20Sopenharmony_ci pwrctrl->reg = out_args.args[0]; 2118c2ecf20Sopenharmony_ci pwrctrl->mask = out_args.args[1]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciout: 2148c2ecf20Sopenharmony_ci of_node_put(out_args.np); 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int len = msg->len; 2218c2ecf20Sopenharmony_ci u8 *buf = msg->buf; 2228c2ecf20Sopenharmony_ci u32 offset = 0; 2238c2ecf20Sopenharmony_ci int i; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci reinit_completion(&vga->complete); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Select combo write */ 2288c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO); 2298c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci while (len > 0) { 2328c2ecf20Sopenharmony_ci u32 cnt; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Clear RX FIFO */ 2358c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR, 2368c2ecf20Sopenharmony_ci VGA_RX_FIFO_CLEAR); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Data offset to read from */ 2398c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_SUB_ADDR, offset); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Kick off the transfer */ 2428c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, 2438c2ecf20Sopenharmony_ci VGA_CMD_TRANS); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&vga->complete, 2468c2ecf20Sopenharmony_ci msecs_to_jiffies(1000))) { 2478c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vga->dev, "transfer timeout\n"); 2488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci cnt = zx_readl(vga->mmio + VGA_RXF_STATUS); 2528c2ecf20Sopenharmony_ci cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT; 2538c2ecf20Sopenharmony_ci /* FIFO status may report more data than we need to read */ 2548c2ecf20Sopenharmony_ci cnt = min_t(u32, len, cnt); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) 2578c2ecf20Sopenharmony_ci *buf++ = zx_readl(vga->mmio + VGA_DATA); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci len -= cnt; 2608c2ecf20Sopenharmony_ci offset += cnt; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * The DDC I2C adapter is only for reading EDID data, so we assume 2708c2ecf20Sopenharmony_ci * that the write to this adapter must be the EDID data offset. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci if ((msg->len != 1) || ((msg->addr != DDC_ADDR))) 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Hardware will take care of the slave address shifting */ 2768c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 2828c2ecf20Sopenharmony_ci int num) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct zx_vga *vga = i2c_get_adapdata(adap); 2858c2ecf20Sopenharmony_ci struct zx_vga_i2c *ddc = vga->ddc; 2868c2ecf20Sopenharmony_ci int ret = 0; 2878c2ecf20Sopenharmony_ci int i; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mutex_lock(&ddc->lock); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 2928c2ecf20Sopenharmony_ci if (msgs[i].flags & I2C_M_RD) 2938c2ecf20Sopenharmony_ci ret = zx_vga_i2c_read(vga, &msgs[i]); 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci ret = zx_vga_i2c_write(vga, &msgs[i]); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (ret < 0) 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!ret) 3028c2ecf20Sopenharmony_ci ret = num; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci mutex_unlock(&ddc->lock); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic u32 zx_vga_i2c_func(struct i2c_adapter *adapter) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic const struct i2c_algorithm zx_vga_algorithm = { 3158c2ecf20Sopenharmony_ci .master_xfer = zx_vga_i2c_xfer, 3168c2ecf20Sopenharmony_ci .functionality = zx_vga_i2c_func, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int zx_vga_ddc_register(struct zx_vga *vga) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct device *dev = vga->dev; 3228c2ecf20Sopenharmony_ci struct i2c_adapter *adap; 3238c2ecf20Sopenharmony_ci struct zx_vga_i2c *ddc; 3248c2ecf20Sopenharmony_ci int ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL); 3278c2ecf20Sopenharmony_ci if (!ddc) 3288c2ecf20Sopenharmony_ci return -ENOMEM; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci vga->ddc = ddc; 3318c2ecf20Sopenharmony_ci mutex_init(&ddc->lock); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci adap = &ddc->adap; 3348c2ecf20Sopenharmony_ci adap->owner = THIS_MODULE; 3358c2ecf20Sopenharmony_ci adap->class = I2C_CLASS_DDC; 3368c2ecf20Sopenharmony_ci adap->dev.parent = dev; 3378c2ecf20Sopenharmony_ci adap->algo = &zx_vga_algorithm; 3388c2ecf20Sopenharmony_ci snprintf(adap->name, sizeof(adap->name), "zx vga i2c"); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = i2c_add_adapter(adap); 3418c2ecf20Sopenharmony_ci if (ret) { 3428c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret); 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci i2c_set_adapdata(adap, vga); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic irqreturn_t zx_vga_irq_thread(int irq, void *dev_id) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct zx_vga *vga = dev_id; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci drm_helper_hpd_irq_event(vga->connector.dev); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic irqreturn_t zx_vga_irq_handler(int irq, void *dev_id) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct zx_vga *vga = dev_id; 3638c2ecf20Sopenharmony_ci u32 status; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci status = zx_readl(vga->mmio + VGA_I2C_STATUS); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Clear interrupt status */ 3688c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ, 3698c2ecf20Sopenharmony_ci VGA_CLEAR_IRQ); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (status & VGA_DEVICE_CONNECTED) { 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * Since VGA_DETECT_SEL bits need to be reset for switching DDC 3748c2ecf20Sopenharmony_ci * bus from device detection to EDID read, rather than setting 3758c2ecf20Sopenharmony_ci * up HAS_DEVICE bit here, we need to do that in .get_modes 3768c2ecf20Sopenharmony_ci * hook for unplug detecting after EDID read succeeds. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci vga->connected = true; 3798c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (status & VGA_DEVICE_DISCONNECTED) { 3838c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 3848c2ecf20Sopenharmony_ci VGA_DETECT_SEL_NO_DEVICE); 3858c2ecf20Sopenharmony_ci vga->connected = false; 3868c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (status & VGA_TRANS_DONE) { 3908c2ecf20Sopenharmony_ci complete(&vga->complete); 3918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return IRQ_NONE; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void zx_vga_hw_init(struct zx_vga *vga) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci unsigned long ref = clk_get_rate(vga->i2c_wclk); 4008c2ecf20Sopenharmony_ci int div; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * Set up I2C fast speed divider per formula below to get 400kHz. 4048c2ecf20Sopenharmony_ci * scl = ref / ((div + 1) * 4) 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1; 4078c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_CLK_DIV_FS, div); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Set up device detection */ 4108c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80); 4118c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* 4148c2ecf20Sopenharmony_ci * We need to poke monitor via DDC bus to get connection irq 4158c2ecf20Sopenharmony_ci * start working. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR); 4188c2ecf20Sopenharmony_ci zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int zx_vga_bind(struct device *dev, struct device *master, void *data) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 4248c2ecf20Sopenharmony_ci struct drm_device *drm = data; 4258c2ecf20Sopenharmony_ci struct resource *res; 4268c2ecf20Sopenharmony_ci struct zx_vga *vga; 4278c2ecf20Sopenharmony_ci int irq; 4288c2ecf20Sopenharmony_ci int ret; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL); 4318c2ecf20Sopenharmony_ci if (!vga) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci vga->dev = dev; 4358c2ecf20Sopenharmony_ci dev_set_drvdata(dev, vga); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4388c2ecf20Sopenharmony_ci vga->mmio = devm_ioremap_resource(dev, res); 4398c2ecf20Sopenharmony_ci if (IS_ERR(vga->mmio)) 4408c2ecf20Sopenharmony_ci return PTR_ERR(vga->mmio); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4438c2ecf20Sopenharmony_ci if (irq < 0) 4448c2ecf20Sopenharmony_ci return irq; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk"); 4478c2ecf20Sopenharmony_ci if (IS_ERR(vga->i2c_wclk)) { 4488c2ecf20Sopenharmony_ci ret = PTR_ERR(vga->i2c_wclk); 4498c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret); 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ret = zx_vga_pwrctrl_init(vga); 4548c2ecf20Sopenharmony_ci if (ret) { 4558c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret); 4568c2ecf20Sopenharmony_ci return ret; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = zx_vga_ddc_register(vga); 4608c2ecf20Sopenharmony_ci if (ret) { 4618c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret); 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ret = zx_vga_register(drm, vga); 4668c2ecf20Sopenharmony_ci if (ret) { 4678c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret); 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci init_completion(&vga->complete); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler, 4748c2ecf20Sopenharmony_ci zx_vga_irq_thread, IRQF_SHARED, 4758c2ecf20Sopenharmony_ci dev_name(dev), vga); 4768c2ecf20Sopenharmony_ci if (ret) { 4778c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret); 4788c2ecf20Sopenharmony_ci return ret; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci ret = clk_prepare_enable(vga->i2c_wclk); 4828c2ecf20Sopenharmony_ci if (ret) 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci zx_vga_hw_init(vga); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic void zx_vga_unbind(struct device *dev, struct device *master, 4918c2ecf20Sopenharmony_ci void *data) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct zx_vga *vga = dev_get_drvdata(dev); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci clk_disable_unprepare(vga->i2c_wclk); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic const struct component_ops zx_vga_component_ops = { 4998c2ecf20Sopenharmony_ci .bind = zx_vga_bind, 5008c2ecf20Sopenharmony_ci .unbind = zx_vga_unbind, 5018c2ecf20Sopenharmony_ci}; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int zx_vga_probe(struct platform_device *pdev) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &zx_vga_component_ops); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int zx_vga_remove(struct platform_device *pdev) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci component_del(&pdev->dev, &zx_vga_component_ops); 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct of_device_id zx_vga_of_match[] = { 5158c2ecf20Sopenharmony_ci { .compatible = "zte,zx296718-vga", }, 5168c2ecf20Sopenharmony_ci { /* end */ }, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_vga_of_match); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistruct platform_driver zx_vga_driver = { 5218c2ecf20Sopenharmony_ci .probe = zx_vga_probe, 5228c2ecf20Sopenharmony_ci .remove = zx_vga_remove, 5238c2ecf20Sopenharmony_ci .driver = { 5248c2ecf20Sopenharmony_ci .name = "zx-vga", 5258c2ecf20Sopenharmony_ci .of_match_table = zx_vga_of_match, 5268c2ecf20Sopenharmony_ci }, 5278c2ecf20Sopenharmony_ci}; 528