[PATCH 4/4] drm: zte: add VGA driver support
Daniel Vetter
daniel at ffwll.ch
Mon Apr 3 09:23:51 UTC 2017
On Tue, Mar 21, 2017 at 08:15:56PM +0800, Shawn Guo wrote:
> From: Shawn Guo <shawn.guo at linaro.org>
>
> It adds VGA driver support, which needs to configure corresponding VOU
> interface in RGB_888 format, and thus the following changes are needed
> on zx_vou.
>
> - Rename the CSC block of Graphic Layer a bit to make it more specific,
> and add CSC of Channel to support RGB output.
> - Bypass Dither block for RGB output.
>
> Signed-off-by: Shawn Guo <shawn.guo at linaro.org>
> ---
> drivers/gpu/drm/zte/Makefile | 1 +
> drivers/gpu/drm/zte/zx_drm_drv.c | 1 +
> drivers/gpu/drm/zte/zx_drm_drv.h | 1 +
> drivers/gpu/drm/zte/zx_vga.c | 500 ++++++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/zte/zx_vga_regs.h | 36 +++
> drivers/gpu/drm/zte/zx_vou.c | 33 ++-
> drivers/gpu/drm/zte/zx_vou_regs.h | 12 +-
> 7 files changed, 580 insertions(+), 4 deletions(-)
> create mode 100644 drivers/gpu/drm/zte/zx_vga.c
> create mode 100644 drivers/gpu/drm/zte/zx_vga_regs.h
>
> diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
> index 01352b56c418..9df7766a7f9d 100644
> --- a/drivers/gpu/drm/zte/Makefile
> +++ b/drivers/gpu/drm/zte/Makefile
> @@ -3,6 +3,7 @@ zxdrm-y := \
> zx_hdmi.o \
> zx_plane.o \
> zx_tvenc.o \
> + zx_vga.o \
> zx_vou.o
>
> obj-$(CONFIG_DRM_ZTE) += zxdrm.o
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
> index 5c6944a1e72c..8a6892eeb44f 100644
> --- a/drivers/gpu/drm/zte/zx_drm_drv.c
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.c
> @@ -248,6 +248,7 @@ static int zx_drm_remove(struct platform_device *pdev)
> &zx_crtc_driver,
> &zx_hdmi_driver,
> &zx_tvenc_driver,
> + &zx_vga_driver,
> &zx_drm_platform_driver,
> };
>
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
> index 5ca035b079c7..2a8cdc5f8be4 100644
> --- a/drivers/gpu/drm/zte/zx_drm_drv.h
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.h
> @@ -14,6 +14,7 @@
> extern struct platform_driver zx_crtc_driver;
> extern struct platform_driver zx_hdmi_driver;
> extern struct platform_driver zx_tvenc_driver;
> +extern struct platform_driver zx_vga_driver;
>
> static inline u32 zx_readl(void __iomem *reg)
> {
> diff --git a/drivers/gpu/drm/zte/zx_vga.c b/drivers/gpu/drm/zte/zx_vga.c
> new file mode 100644
> index 000000000000..35eaf98458af
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_vga.c
> @@ -0,0 +1,500 @@
> +/*
> + * 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)
> + 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_mode_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 = {
> + .dpms = drm_atomic_helper_connector_dpms,
> + .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;
> +
> + encoder->possible_crtcs = VOU_CRTC_MASK;
> +
> + drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
> + DRM_MODE_ENCODER_DAC, NULL);
> + drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
> +
> + vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> + drm_connector_init(drm, connector, &zx_vga_connector_funcs,
> + DRM_MODE_CONNECTOR_VGA);
> + drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
> +
> + drm_mode_connector_attach_encoder(connector, encoder);
> +
> + return 0;
> +}
> +
> +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);
Since this really isn't a full-featured i2c adapter but a specialized
"give me the edid" thing I think you should look into drm_do_get_edid and
_not_ expose the i2c thing. That should simplify your code a lot, and
avoid any kind of synchronization issues between userspace i2c access and
your driver's access (which you atm don't handle at all).
With that fixed, on the entire series.
Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> +
> + 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) {
> + /*
> + * We should ideally set up VGA_DETECT_SEL_HAS_DEVICE bit here
> + * for unplug detection, but doing so will stop DDC bus from
> + * reading EDID later on. It looks like a HW limitation, and we
> + * work around it by defering the bit setup to .get_modes hook
> + * 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,
> + },
> +};
> diff --git a/drivers/gpu/drm/zte/zx_vga_regs.h b/drivers/gpu/drm/zte/zx_vga_regs.h
> new file mode 100644
> index 000000000000..feaa345fe6a6
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_vga_regs.h
> @@ -0,0 +1,36 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef __ZX_VGA_REGS_H__
> +#define __ZX_VGA_REGS_H__
> +
> +#define VGA_CMD_CFG 0x04
> +#define VGA_CMD_TRANS BIT(6)
> +#define VGA_CMD_COMBO BIT(5)
> +#define VGA_CMD_RW BIT(4)
> +#define VGA_SUB_ADDR 0x0c
> +#define VGA_DEVICE_ADDR 0x10
> +#define VGA_CLK_DIV_FS 0x14
> +#define VGA_RXF_CTRL 0x20
> +#define VGA_RX_FIFO_CLEAR BIT(7)
> +#define VGA_DATA 0x24
> +#define VGA_I2C_STATUS 0x28
> +#define VGA_DEVICE_DISCONNECTED BIT(7)
> +#define VGA_DEVICE_CONNECTED BIT(6)
> +#define VGA_CLEAR_IRQ BIT(4)
> +#define VGA_TRANS_DONE BIT(0)
> +#define VGA_RXF_STATUS 0x30
> +#define VGA_RXF_COUNT_SHIFT 2
> +#define VGA_RXF_COUNT_MASK GENMASK(7, 2)
> +#define VGA_AUTO_DETECT_PARA 0x34
> +#define VGA_AUTO_DETECT_SEL 0x38
> +#define VGA_DETECT_SEL_HAS_DEVICE BIT(1)
> +#define VGA_DETECT_SEL_NO_DEVICE BIT(0)
> +
> +#endif /* __ZX_VGA_REGS_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 2a2d90bd9425..cc5bdfc53e8e 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -23,6 +23,7 @@
> #include <drm/drm_plane_helper.h>
> #include <drm/drmP.h>
>
> +#include "zx_common_regs.h"
> #include "zx_drm_drv.h"
> #include "zx_plane.h"
> #include "zx_vou.h"
> @@ -122,6 +123,8 @@ struct zx_crtc {
> struct drm_plane *primary;
> struct zx_vou_hw *vou;
> void __iomem *chnreg;
> + void __iomem *chncsc;
> + void __iomem *dither;
> const struct zx_crtc_regs *regs;
> const struct zx_crtc_bits *bits;
> enum vou_chn_type chn_type;
> @@ -204,6 +207,11 @@ struct vou_inf {
> .clocks_en_bits = BIT(15),
> .clocks_sel_bits = BIT(11) | BIT(0),
> },
> + [VOU_VGA] = {
> + .data_sel = VOU_RGB_888,
> + .clocks_en_bits = BIT(1),
> + .clocks_sel_bits = BIT(10),
> + },
> };
>
> static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
> @@ -227,9 +235,26 @@ void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
> struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> struct zx_vou_hw *vou = zcrtc->vou;
> struct vou_inf *inf = &vou_infs[id];
> + void __iomem *dither = zcrtc->dither;
> + void __iomem *csc = zcrtc->chncsc;
> bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
> u32 data_sel_shift = id << 1;
>
> + if (inf->data_sel != VOU_YUV444) {
> + /* Enable channel CSC for RGB output */
> + zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
> + CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
> + zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
> + CSC_WORK_ENABLE);
> +
> + /* Bypass Dither block for RGB output */
> + zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
> + DITHER_BYSPASS);
> + } else {
> + zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
> + zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
> + }
> +
> /* Select data format */
> zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
> inf->data_sel << data_sel_shift);
> @@ -502,20 +527,24 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
>
> if (chn_type == VOU_CHN_MAIN) {
> zplane->layer = vou->osd + MAIN_GL_OFFSET;
> - zplane->csc = vou->osd + MAIN_CSC_OFFSET;
> + zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
> zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
> zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
> zplane->bits = &zx_gl_bits[0];
> zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
> + zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
> + zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
> zcrtc->regs = &main_crtc_regs;
> zcrtc->bits = &main_crtc_bits;
> } else {
> zplane->layer = vou->osd + AUX_GL_OFFSET;
> - zplane->csc = vou->osd + AUX_CSC_OFFSET;
> + zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
> zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
> zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
> zplane->bits = &zx_gl_bits[1];
> zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
> + zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
> + zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
> zcrtc->regs = &aux_crtc_regs;
> zcrtc->bits = &aux_crtc_bits;
> }
> diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
> index c066ef123434..5a218351b497 100644
> --- a/drivers/gpu/drm/zte/zx_vou_regs.h
> +++ b/drivers/gpu/drm/zte/zx_vou_regs.h
> @@ -13,13 +13,17 @@
>
> /* Sub-module offset */
> #define MAIN_GL_OFFSET 0x130
> -#define MAIN_CSC_OFFSET 0x580
> +#define MAIN_GL_CSC_OFFSET 0x580
> +#define MAIN_CHN_CSC_OFFSET 0x6c0
> #define MAIN_HBSC_OFFSET 0x820
> +#define MAIN_DITHER_OFFSET 0x960
> #define MAIN_RSZ_OFFSET 0x600 /* OTFPPU sub-module */
>
> #define AUX_GL_OFFSET 0x200
> -#define AUX_CSC_OFFSET 0x5d0
> +#define AUX_GL_CSC_OFFSET 0x5d0
> +#define AUX_CHN_CSC_OFFSET 0x710
> #define AUX_HBSC_OFFSET 0x860
> +#define AUX_DITHER_OFFSET 0x970
> #define AUX_RSZ_OFFSET 0x800
>
> #define OSD_VL0_OFFSET 0x040
> @@ -78,6 +82,10 @@
> #define CHN_INTERLACE_BUF_CTRL 0x24
> #define CHN_INTERLACE_EN BIT(2)
>
> +/* Dither registers */
> +#define OSD_DITHER_CTRL0 0x00
> +#define DITHER_BYSPASS BIT(31)
> +
> /* TIMING_CTRL registers */
> #define TIMING_TC_ENABLE 0x04
> #define AUX_TC_EN BIT(1)
> --
> 1.9.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list