All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Vetter <daniel@ffwll.ch>
To: Shawn Guo <shawnguo@kernel.org>
Cc: Xin Zhou <zhou.xin8@zte.com.cn>,
	Daniel Vetter <daniel.vetter@ffwll.ch>,
	Baoyou Xie <xie.baoyou@zte.com.cn>,
	dri-devel@lists.freedesktop.org, Jun Nie <jun.nie@linaro.org>
Subject: Re: [PATCH 4/4] drm: zte: add VGA driver support
Date: Mon, 3 Apr 2017 11:23:51 +0200	[thread overview]
Message-ID: <20170403092351.owrqjpckok32tt6j@phenom.ffwll.local> (raw)
In-Reply-To: <1490098556-23853-5-git-send-email-shawnguo@kernel.org>

On Tue, Mar 21, 2017 at 08:15:56PM +0800, Shawn Guo wrote:
> From: Shawn Guo <shawn.guo@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@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@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
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2017-04-03  9:23 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-21 12:15 [PATCH 0/4] Add ZTE VGA driver support Shawn Guo
2017-03-21 12:15 ` [PATCH 1/4] drm: zte: do not enable clock auto-gating by default Shawn Guo
2017-04-04 17:48   ` Sean Paul
2017-03-21 12:15 ` [PATCH 2/4] drm: zte: move CSC register definitions into a common header Shawn Guo
2017-04-04 17:47   ` Sean Paul
2017-03-21 12:15 ` [PATCH 3/4] dt: add bindings for ZTE VGA device Shawn Guo
2017-03-21 12:15 ` [PATCH 4/4] drm: zte: add VGA driver support Shawn Guo
2017-04-03  9:23   ` Daniel Vetter [this message]
2017-04-04 13:47     ` Shawn Guo
2017-04-04 17:32       ` Sean Paul
2017-04-05  6:08         ` Shawn Guo
2017-04-05  6:25           ` Daniel Vetter
2017-04-04 17:46   ` Sean Paul
2017-04-05 14:08     ` Shawn Guo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170403092351.owrqjpckok32tt6j@phenom.ffwll.local \
    --to=daniel@ffwll.ch \
    --cc=daniel.vetter@ffwll.ch \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jun.nie@linaro.org \
    --cc=shawnguo@kernel.org \
    --cc=xie.baoyou@zte.com.cn \
    --cc=zhou.xin8@zte.com.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.