123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- /*
- * Copyright (C) 2017 Sanechips Technology Co., Ltd.
- * Copyright 2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/clk.h>
- #include <linux/component.h>
- #include <linux/mfd/syscon.h>
- #include <linux/regmap.h>
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_crtc_helper.h>
- #include <drm/drmP.h>
- #include "zx_drm_drv.h"
- #include "zx_vga_regs.h"
- #include "zx_vou.h"
- struct zx_vga_pwrctrl {
- struct regmap *regmap;
- u32 reg;
- u32 mask;
- };
- struct zx_vga_i2c {
- struct i2c_adapter adap;
- struct mutex lock;
- };
- struct zx_vga {
- struct drm_connector connector;
- struct drm_encoder encoder;
- struct zx_vga_i2c *ddc;
- struct device *dev;
- void __iomem *mmio;
- struct clk *i2c_wclk;
- struct zx_vga_pwrctrl pwrctrl;
- struct completion complete;
- bool connected;
- };
- #define to_zx_vga(x) container_of(x, struct zx_vga, x)
- static void zx_vga_encoder_enable(struct drm_encoder *encoder)
- {
- struct zx_vga *vga = to_zx_vga(encoder);
- struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
- /* Set bit to power up VGA DACs */
- regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
- pwrctrl->mask);
- vou_inf_enable(VOU_VGA, encoder->crtc);
- }
- static void zx_vga_encoder_disable(struct drm_encoder *encoder)
- {
- struct zx_vga *vga = to_zx_vga(encoder);
- struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
- vou_inf_disable(VOU_VGA, encoder->crtc);
- /* Clear bit to power down VGA DACs */
- regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
- }
- static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = {
- .enable = zx_vga_encoder_enable,
- .disable = zx_vga_encoder_disable,
- };
- static const struct drm_encoder_funcs zx_vga_encoder_funcs = {
- .destroy = drm_encoder_cleanup,
- };
- static int zx_vga_connector_get_modes(struct drm_connector *connector)
- {
- struct zx_vga *vga = to_zx_vga(connector);
- struct edid *edid;
- int ret;
- /*
- * Clear both detection bits to switch I2C bus from device
- * detecting to EDID reading.
- */
- zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
- edid = drm_get_edid(connector, &vga->ddc->adap);
- if (!edid) {
- /*
- * If EDID reading fails, we set the device state into
- * disconnected. Locking is not required here, since the
- * VGA_AUTO_DETECT_SEL register write in irq handler cannot
- * be triggered when both detection bits are cleared as above.
- */
- zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
- VGA_DETECT_SEL_NO_DEVICE);
- vga->connected = false;
- return 0;
- }
- /*
- * As edid reading succeeds, device must be connected, so we set
- * up detection bit for unplug interrupt here.
- */
- zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
- drm_connector_update_edid_property(connector, edid);
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
- return ret;
- }
- static enum drm_mode_status
- zx_vga_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
- {
- return MODE_OK;
- }
- static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = {
- .get_modes = zx_vga_connector_get_modes,
- .mode_valid = zx_vga_connector_mode_valid,
- };
- static enum drm_connector_status
- zx_vga_connector_detect(struct drm_connector *connector, bool force)
- {
- struct zx_vga *vga = to_zx_vga(connector);
- return vga->connected ? connector_status_connected :
- connector_status_disconnected;
- }
- static const struct drm_connector_funcs zx_vga_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .detect = zx_vga_connector_detect,
- .destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- };
- static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga)
- {
- struct drm_encoder *encoder = &vga->encoder;
- struct drm_connector *connector = &vga->connector;
- struct device *dev = vga->dev;
- int ret;
- encoder->possible_crtcs = VOU_CRTC_MASK;
- ret = drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
- DRM_MODE_ENCODER_DAC, NULL);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret);
- return ret;
- };
- drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
- vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
- ret = drm_connector_init(drm, connector, &zx_vga_connector_funcs,
- DRM_MODE_CONNECTOR_VGA);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret);
- goto clean_encoder;
- };
- drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
- ret = drm_connector_attach_encoder(connector, encoder);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret);
- goto clean_connector;
- };
- return 0;
- clean_connector:
- drm_connector_cleanup(connector);
- clean_encoder:
- drm_encoder_cleanup(encoder);
- return ret;
- }
- static int zx_vga_pwrctrl_init(struct zx_vga *vga)
- {
- struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
- struct device *dev = vga->dev;
- struct of_phandle_args out_args;
- struct regmap *regmap;
- int ret;
- ret = of_parse_phandle_with_fixed_args(dev->of_node,
- "zte,vga-power-control", 2, 0, &out_args);
- if (ret)
- return ret;
- regmap = syscon_node_to_regmap(out_args.np);
- if (IS_ERR(regmap)) {
- ret = PTR_ERR(regmap);
- goto out;
- }
- pwrctrl->regmap = regmap;
- pwrctrl->reg = out_args.args[0];
- pwrctrl->mask = out_args.args[1];
- out:
- of_node_put(out_args.np);
- return ret;
- }
- static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg)
- {
- int len = msg->len;
- u8 *buf = msg->buf;
- u32 offset = 0;
- int i;
- reinit_completion(&vga->complete);
- /* Select combo write */
- zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO);
- zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0);
- while (len > 0) {
- u32 cnt;
- /* Clear RX FIFO */
- zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR,
- VGA_RX_FIFO_CLEAR);
- /* Data offset to read from */
- zx_writel(vga->mmio + VGA_SUB_ADDR, offset);
- /* Kick off the transfer */
- zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS,
- VGA_CMD_TRANS);
- if (!wait_for_completion_timeout(&vga->complete,
- msecs_to_jiffies(1000))) {
- DRM_DEV_ERROR(vga->dev, "transfer timeout\n");
- return -ETIMEDOUT;
- }
- cnt = zx_readl(vga->mmio + VGA_RXF_STATUS);
- cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT;
- /* FIFO status may report more data than we need to read */
- cnt = min_t(u32, len, cnt);
- for (i = 0; i < cnt; i++)
- *buf++ = zx_readl(vga->mmio + VGA_DATA);
- len -= cnt;
- offset += cnt;
- }
- return 0;
- }
- static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg)
- {
- /*
- * The DDC I2C adapter is only for reading EDID data, so we assume
- * that the write to this adapter must be the EDID data offset.
- */
- if ((msg->len != 1) || ((msg->addr != DDC_ADDR)))
- return -EINVAL;
- /* Hardware will take care of the slave address shifting */
- zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr);
- return 0;
- }
- static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
- int num)
- {
- struct zx_vga *vga = i2c_get_adapdata(adap);
- struct zx_vga_i2c *ddc = vga->ddc;
- int ret = 0;
- int i;
- mutex_lock(&ddc->lock);
- for (i = 0; i < num; i++) {
- if (msgs[i].flags & I2C_M_RD)
- ret = zx_vga_i2c_read(vga, &msgs[i]);
- else
- ret = zx_vga_i2c_write(vga, &msgs[i]);
- if (ret < 0)
- break;
- }
- if (!ret)
- ret = num;
- mutex_unlock(&ddc->lock);
- return ret;
- }
- static u32 zx_vga_i2c_func(struct i2c_adapter *adapter)
- {
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
- }
- static const struct i2c_algorithm zx_vga_algorithm = {
- .master_xfer = zx_vga_i2c_xfer,
- .functionality = zx_vga_i2c_func,
- };
- static int zx_vga_ddc_register(struct zx_vga *vga)
- {
- struct device *dev = vga->dev;
- struct i2c_adapter *adap;
- struct zx_vga_i2c *ddc;
- int ret;
- ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
- if (!ddc)
- return -ENOMEM;
- vga->ddc = ddc;
- mutex_init(&ddc->lock);
- adap = &ddc->adap;
- adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_DDC;
- adap->dev.parent = dev;
- adap->algo = &zx_vga_algorithm;
- snprintf(adap->name, sizeof(adap->name), "zx vga i2c");
- ret = i2c_add_adapter(adap);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret);
- return ret;
- }
- i2c_set_adapdata(adap, vga);
- return 0;
- }
- static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id)
- {
- struct zx_vga *vga = dev_id;
- drm_helper_hpd_irq_event(vga->connector.dev);
- return IRQ_HANDLED;
- }
- static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
- {
- struct zx_vga *vga = dev_id;
- u32 status;
- status = zx_readl(vga->mmio + VGA_I2C_STATUS);
- /* Clear interrupt status */
- zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
- VGA_CLEAR_IRQ);
- if (status & VGA_DEVICE_CONNECTED) {
- /*
- * Since VGA_DETECT_SEL bits need to be reset for switching DDC
- * bus from device detection to EDID read, rather than setting
- * up HAS_DEVICE bit here, we need to do that in .get_modes
- * hook for unplug detecting after EDID read succeeds.
- */
- vga->connected = true;
- return IRQ_WAKE_THREAD;
- }
- if (status & VGA_DEVICE_DISCONNECTED) {
- zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
- VGA_DETECT_SEL_NO_DEVICE);
- vga->connected = false;
- return IRQ_WAKE_THREAD;
- }
- if (status & VGA_TRANS_DONE) {
- complete(&vga->complete);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
- }
- static void zx_vga_hw_init(struct zx_vga *vga)
- {
- unsigned long ref = clk_get_rate(vga->i2c_wclk);
- int div;
- /*
- * Set up I2C fast speed divider per formula below to get 400kHz.
- * scl = ref / ((div + 1) * 4)
- */
- div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1;
- zx_writel(vga->mmio + VGA_CLK_DIV_FS, div);
- /* Set up device detection */
- zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80);
- zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE);
- /*
- * We need to poke monitor via DDC bus to get connection irq
- * start working.
- */
- zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR);
- zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS);
- }
- static int zx_vga_bind(struct device *dev, struct device *master, void *data)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct drm_device *drm = data;
- struct resource *res;
- struct zx_vga *vga;
- int irq;
- int ret;
- vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
- if (!vga)
- return -ENOMEM;
- vga->dev = dev;
- dev_set_drvdata(dev, vga);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- vga->mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(vga->mmio))
- return PTR_ERR(vga->mmio);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
- vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk");
- if (IS_ERR(vga->i2c_wclk)) {
- ret = PTR_ERR(vga->i2c_wclk);
- DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret);
- return ret;
- }
- ret = zx_vga_pwrctrl_init(vga);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
- return ret;
- }
- ret = zx_vga_ddc_register(vga);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret);
- return ret;
- }
- ret = zx_vga_register(drm, vga);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret);
- return ret;
- }
- init_completion(&vga->complete);
- ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler,
- zx_vga_irq_thread, IRQF_SHARED,
- dev_name(dev), vga);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret);
- return ret;
- }
- ret = clk_prepare_enable(vga->i2c_wclk);
- if (ret)
- return ret;
- zx_vga_hw_init(vga);
- return 0;
- }
- static void zx_vga_unbind(struct device *dev, struct device *master,
- void *data)
- {
- struct zx_vga *vga = dev_get_drvdata(dev);
- clk_disable_unprepare(vga->i2c_wclk);
- }
- static const struct component_ops zx_vga_component_ops = {
- .bind = zx_vga_bind,
- .unbind = zx_vga_unbind,
- };
- static int zx_vga_probe(struct platform_device *pdev)
- {
- return component_add(&pdev->dev, &zx_vga_component_ops);
- }
- static int zx_vga_remove(struct platform_device *pdev)
- {
- component_del(&pdev->dev, &zx_vga_component_ops);
- return 0;
- }
- static const struct of_device_id zx_vga_of_match[] = {
- { .compatible = "zte,zx296718-vga", },
- { /* end */ },
- };
- MODULE_DEVICE_TABLE(of, zx_vga_of_match);
- struct platform_driver zx_vga_driver = {
- .probe = zx_vga_probe,
- .remove = zx_vga_remove,
- .driver = {
- .name = "zx-vga",
- .of_match_table = zx_vga_of_match,
- },
- };
|