mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1552 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1552 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From cfb7f2ab82336ed05355246871d4d6f4885d7613 Mon Sep 17 00:00:00 2001
 | |
| From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
 | |
| Date: Tue, 19 Sep 2023 17:51:49 +0100
 | |
| Subject: [PATCH] drm: Add RP1 DPI driver
 | |
| 
 | |
| Add support for the RP1 DPI hardware.
 | |
| 
 | |
| Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
 | |
| ---
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/Kconfig       |  12 +
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/Makefile      |   5 +
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c     | 429 ++++++++++++++++++
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h     |  69 +++
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c | 510 ++++++++++++++++++++++
 | |
|  drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c  | 486 +++++++++++++++++++++
 | |
|  6 files changed, 1511 insertions(+)
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Kconfig
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Makefile
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
 | |
|  create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
 | |
| 
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig
 | |
| @@ -0,0 +1,12 @@
 | |
| +# SPDX-License-Identifier: GPL-2.0-only
 | |
| +config DRM_RP1_DPI
 | |
| +	tristate "DRM Support for RP1 DPI"
 | |
| +	depends on DRM
 | |
| +	select MFD_RP1
 | |
| +	select DRM_GEM_DMA_HELPER
 | |
| +	select DRM_KMS_HELPER
 | |
| +	select DRM_VRAM_HELPER
 | |
| +	select DRM_TTM
 | |
| +	select DRM_TTM_HELPER
 | |
| +	help
 | |
| +	  Choose this option to enable Video Out on RP1
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile
 | |
| @@ -0,0 +1,5 @@
 | |
| +# SPDX-License-Identifier: GPL-2.0-only
 | |
| +
 | |
| +drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o
 | |
| +
 | |
| +obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
 | |
| @@ -0,0 +1,429 @@
 | |
| +// SPDX-License-Identifier: GPL-2.0-or-later
 | |
| +/*
 | |
| + * DRM Driver for DPI output on Raspberry Pi RP1
 | |
| + *
 | |
| + * Copyright (c) 2023 Raspberry Pi Limited.
 | |
| + */
 | |
| +
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/errno.h>
 | |
| +#include <linux/string.h>
 | |
| +#include <linux/slab.h>
 | |
| +#include <linux/mm.h>
 | |
| +#include <linux/fb.h>
 | |
| +#include <linux/init.h>
 | |
| +#include <linux/delay.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/ioport.h>
 | |
| +#include <linux/list.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/clk.h>
 | |
| +#include <linux/printk.h>
 | |
| +#include <linux/console.h>
 | |
| +#include <linux/debugfs.h>
 | |
| +#include <linux/uaccess.h>
 | |
| +#include <linux/io.h>
 | |
| +#include <linux/dma-mapping.h>
 | |
| +#include <linux/cred.h>
 | |
| +#include <linux/media-bus-format.h>
 | |
| +#include <linux/pinctrl/consumer.h>
 | |
| +#include <drm/drm_drv.h>
 | |
| +#include <drm/drm_mm.h>
 | |
| +#include <drm/drm_fourcc.h>
 | |
| +#include <drm/drm_gem_atomic_helper.h>
 | |
| +#include <drm/drm_gem_dma_helper.h>
 | |
| +#include <drm/drm_atomic_helper.h>
 | |
| +#include <drm/drm_managed.h>
 | |
| +#include <drm/drm_crtc.h>
 | |
| +#include <drm/drm_crtc_helper.h>
 | |
| +#include <drm/drm_encoder.h>
 | |
| +#include <drm/drm_fb_helper.h>
 | |
| +#include <drm/drm_framebuffer.h>
 | |
| +#include <drm/drm_gem.h>
 | |
| +#include <drm/drm_gem_framebuffer_helper.h>
 | |
| +#include <drm/drm_simple_kms_helper.h>
 | |
| +#include <drm/drm_probe_helper.h>
 | |
| +#include <drm/drm_modeset_helper_vtables.h>
 | |
| +#include <drm/drm_vblank.h>
 | |
| +#include <drm/drm_of.h>
 | |
| +
 | |
| +#include "rp1_dpi.h"
 | |
| +
 | |
| +/*
 | |
| + * Default bus format, where not specified by a connector/bridge
 | |
| + * and not overridden by the OF property "default_bus_fmt".
 | |
| + * This value is for compatibility with vc4 and VGA666-style boards,
 | |
| + * even though RP1 hardware cannot achieve the full 18-bit depth
 | |
| + * with that pinout (MEDIA_BUS_FMT_RGB666_1X24_CPADHI is preferred).
 | |
| + */
 | |
| +static unsigned int default_bus_fmt = MEDIA_BUS_FMT_RGB666_1X18;
 | |
| +module_param(default_bus_fmt, uint, 0644);
 | |
| +
 | |
| +/* -------------------------------------------------------------- */
 | |
| +
 | |
| +static void rp1dpi_pipe_update(struct drm_simple_display_pipe *pipe,
 | |
| +			       struct drm_plane_state *old_state)
 | |
| +{
 | |
| +	struct drm_pending_vblank_event *event;
 | |
| +	unsigned long flags;
 | |
| +	struct drm_framebuffer *fb = pipe->plane.state->fb;
 | |
| +	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
 | |
| +	struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
 | |
| +	struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
 | |
| +	bool can_update = fb && dma_obj && dpi && dpi->pipe_enabled;
 | |
| +
 | |
| +	/* (Re-)start DPI-DMA where required; and update FB address */
 | |
| +	if (can_update) {
 | |
| +		if (!dpi->dpi_running || fb->format->format != dpi->cur_fmt) {
 | |
| +			if (dpi->dpi_running &&
 | |
| +			    fb->format->format != dpi->cur_fmt) {
 | |
| +				rp1dpi_hw_stop(dpi);
 | |
| +				dpi->dpi_running = false;
 | |
| +			}
 | |
| +			if (!dpi->dpi_running) {
 | |
| +				rp1dpi_hw_setup(dpi,
 | |
| +						fb->format->format,
 | |
| +						dpi->bus_fmt,
 | |
| +						dpi->de_inv,
 | |
| +						&pipe->crtc.state->mode);
 | |
| +				dpi->dpi_running = true;
 | |
| +			}
 | |
| +			dpi->cur_fmt = fb->format->format;
 | |
| +			drm_crtc_vblank_on(&pipe->crtc);
 | |
| +		}
 | |
| +		rp1dpi_hw_update(dpi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
 | |
| +	}
 | |
| +
 | |
| +	/* Arm VBLANK event (or call it immediately in some error cases) */
 | |
| +	spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
 | |
| +	event = pipe->crtc.state->event;
 | |
| +	if (event) {
 | |
| +		pipe->crtc.state->event = NULL;
 | |
| +		if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
 | |
| +			drm_crtc_arm_vblank_event(&pipe->crtc, event);
 | |
| +		else
 | |
| +			drm_crtc_send_vblank_event(&pipe->crtc, event);
 | |
| +	}
 | |
| +	spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
 | |
| +}
 | |
| +
 | |
| +static void rp1dpi_pipe_enable(struct drm_simple_display_pipe *pipe,
 | |
| +			       struct drm_crtc_state *crtc_state,
 | |
| +			      struct drm_plane_state *plane_state)
 | |
| +{
 | |
| +	static const unsigned int M = 1000000;
 | |
| +	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
 | |
| +	struct drm_connector *conn;
 | |
| +	struct drm_connector_list_iter conn_iter;
 | |
| +	unsigned int fpix, fdiv, fvco;
 | |
| +	int ret;
 | |
| +
 | |
| +	/* Look up the connector attached to DPI so we can get the
 | |
| +	 * bus_format.  Ideally the bridge would tell us the
 | |
| +	 * bus_format we want, but it doesn't yet, so assume that it's
 | |
| +	 * uniform throughout the bridge chain.
 | |
| +	 */
 | |
| +	dev_info(&dpi->pdev->dev, __func__);
 | |
| +	drm_connector_list_iter_begin(pipe->encoder.dev, &conn_iter);
 | |
| +	drm_for_each_connector_iter(conn, &conn_iter) {
 | |
| +		if (conn->encoder == &pipe->encoder) {
 | |
| +			dpi->de_inv = !!(conn->display_info.bus_flags &
 | |
| +							DRM_BUS_FLAG_DE_LOW);
 | |
| +			dpi->clk_inv = !!(conn->display_info.bus_flags &
 | |
| +						DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
 | |
| +			if (conn->display_info.num_bus_formats)
 | |
| +				dpi->bus_fmt = conn->display_info.bus_formats[0];
 | |
| +			break;
 | |
| +		}
 | |
| +	}
 | |
| +	drm_connector_list_iter_end(&conn_iter);
 | |
| +
 | |
| +	/* Set DPI clock to desired frequency. Currently (experimentally)
 | |
| +	 * we take control of the VideoPLL, to ensure we can generate it
 | |
| +	 * accurately. NB: this prevents concurrent use of DPI and VEC!
 | |
| +	 * Magic numbers ensure the parent clock is within [100MHz, 200MHz]
 | |
| +	 * with VCO in [1GHz, 1.33GHz]. The initial divide is by 6, 8 or 10.
 | |
| +	 */
 | |
| +	fpix = 1000 * pipe->crtc.state->mode.clock;
 | |
| +	fpix = clamp(fpix, 1 * M, 200 * M);
 | |
| +	fdiv = fpix;
 | |
| +	while (fdiv < 100 * M)
 | |
| +		fdiv *= 2;
 | |
| +	fvco = fdiv * 2 * DIV_ROUND_UP(500 * M, fdiv);
 | |
| +	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLCORE], fvco);
 | |
| +	if (ret)
 | |
| +		dev_err(&dpi->pdev->dev, "Failed to set PLL VCO to %u (%d)", fvco, ret);
 | |
| +	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLDIV], fdiv);
 | |
| +	if (ret)
 | |
| +		dev_err(&dpi->pdev->dev, "Failed to set PLL output to %u (%d)", fdiv, ret);
 | |
| +	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_DPI], fpix);
 | |
| +	if (ret)
 | |
| +		dev_err(&dpi->pdev->dev, "Failed to set DPI clock to %u (%d)", fpix, ret);
 | |
| +
 | |
| +	rp1dpi_vidout_setup(dpi, dpi->clk_inv);
 | |
| +	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLCORE]);
 | |
| +	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLDIV]);
 | |
| +	pinctrl_pm_select_default_state(&dpi->pdev->dev);
 | |
| +	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_DPI]);
 | |
| +	dev_info(&dpi->pdev->dev, "Want %u /%u %u /%u %u; got VCO=%lu DIV=%lu DPI=%lu",
 | |
| +		 fvco, fvco / fdiv, fdiv, fdiv / fpix, fpix,
 | |
| +		 clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLCORE]),
 | |
| +		 clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLDIV]),
 | |
| +		 clk_get_rate(dpi->clocks[RP1DPI_CLK_DPI]));
 | |
| +
 | |
| +	/* Start DPI-DMA. pipe already has the new crtc and plane state. */
 | |
| +	dpi->pipe_enabled = true;
 | |
| +	dpi->cur_fmt = 0xdeadbeef;
 | |
| +	rp1dpi_pipe_update(pipe, 0);
 | |
| +}
 | |
| +
 | |
| +static void rp1dpi_pipe_disable(struct drm_simple_display_pipe *pipe)
 | |
| +{
 | |
| +	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
 | |
| +
 | |
| +	dev_info(&dpi->pdev->dev, __func__);
 | |
| +	drm_crtc_vblank_off(&pipe->crtc);
 | |
| +	if (dpi->dpi_running) {
 | |
| +		rp1dpi_hw_stop(dpi);
 | |
| +		dpi->dpi_running = false;
 | |
| +	}
 | |
| +	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
 | |
| +	pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
 | |
| +	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLDIV]);
 | |
| +	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLCORE]);
 | |
| +	dpi->pipe_enabled = false;
 | |
| +}
 | |
| +
 | |
| +static int rp1dpi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
 | |
| +{
 | |
| +	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
 | |
| +
 | |
| +	if (dpi)
 | |
| +		rp1dpi_hw_vblank_ctrl(dpi, 1);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static void rp1dpi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
 | |
| +{
 | |
| +	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
 | |
| +
 | |
| +	if (dpi)
 | |
| +		rp1dpi_hw_vblank_ctrl(dpi, 0);
 | |
| +}
 | |
| +
 | |
| +static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = {
 | |
| +	.enable	    = rp1dpi_pipe_enable,
 | |
| +	.update	    = rp1dpi_pipe_update,
 | |
| +	.disable    = rp1dpi_pipe_disable,
 | |
| +	.prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
 | |
| +	.enable_vblank	= rp1dpi_pipe_enable_vblank,
 | |
| +	.disable_vblank = rp1dpi_pipe_disable_vblank,
 | |
| +};
 | |
| +
 | |
| +static const struct drm_mode_config_funcs rp1dpi_mode_funcs = {
 | |
| +	.fb_create = drm_gem_fb_create,
 | |
| +	.atomic_check = drm_atomic_helper_check,
 | |
| +	.atomic_commit = drm_atomic_helper_commit,
 | |
| +};
 | |
| +
 | |
| +static void rp1dpi_stopall(struct drm_device *drm)
 | |
| +{
 | |
| +	if (drm->dev_private) {
 | |
| +		struct rp1_dpi *dpi = drm->dev_private;
 | |
| +
 | |
| +		if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) {
 | |
| +			rp1dpi_hw_stop(dpi);
 | |
| +			clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
 | |
| +			dpi->dpi_running = false;
 | |
| +		}
 | |
| +		rp1dpi_vidout_poweroff(dpi);
 | |
| +		pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +DEFINE_DRM_GEM_DMA_FOPS(rp1dpi_fops);
 | |
| +
 | |
| +static struct drm_driver rp1dpi_driver = {
 | |
| +	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
 | |
| +	.fops			= &rp1dpi_fops,
 | |
| +	.name			= "drm-rp1-dpi",
 | |
| +	.desc			= "drm-rp1-dpi",
 | |
| +	.date			= "0",
 | |
| +	.major			= 1,
 | |
| +	.minor			= 0,
 | |
| +	DRM_GEM_DMA_DRIVER_OPS,
 | |
| +	.release		= rp1dpi_stopall,
 | |
| +};
 | |
| +
 | |
| +static const u32 rp1dpi_formats[] = {
 | |
| +	DRM_FORMAT_XRGB8888,
 | |
| +	DRM_FORMAT_XBGR8888,
 | |
| +	DRM_FORMAT_RGB888,
 | |
| +	DRM_FORMAT_BGR888,
 | |
| +	DRM_FORMAT_RGB565
 | |
| +};
 | |
| +
 | |
| +static int rp1dpi_platform_probe(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct device *dev = &pdev->dev;
 | |
| +	struct drm_device *drm;
 | |
| +	struct rp1_dpi *dpi;
 | |
| +	struct drm_bridge *bridge = NULL;
 | |
| +	struct drm_panel *panel;
 | |
| +	int i, ret;
 | |
| +
 | |
| +	dev_info(dev, __func__);
 | |
| +	ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0,
 | |
| +					  &panel, &bridge);
 | |
| +	if (ret) {
 | |
| +		dev_info(dev, "%s: bridge not found\n", __func__);
 | |
| +		return -EPROBE_DEFER;
 | |
| +	}
 | |
| +	if (panel) {
 | |
| +		bridge = devm_drm_panel_bridge_add(dev, panel);
 | |
| +		if (IS_ERR(bridge))
 | |
| +			return PTR_ERR(bridge);
 | |
| +	}
 | |
| +
 | |
| +	drm = drm_dev_alloc(&rp1dpi_driver, dev);
 | |
| +	if (IS_ERR(drm)) {
 | |
| +		dev_info(dev, "%s %d", __func__, (int)__LINE__);
 | |
| +		ret = PTR_ERR(drm);
 | |
| +		return ret;
 | |
| +	}
 | |
| +	dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);
 | |
| +	if (!dpi) {
 | |
| +		dev_info(dev, "%s %d", __func__, (int)__LINE__);
 | |
| +		drm_dev_put(drm);
 | |
| +		return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	init_completion(&dpi->finished);
 | |
| +	dpi->drm = drm;
 | |
| +	dpi->pdev = pdev;
 | |
| +	drm->dev_private = dpi;
 | |
| +	platform_set_drvdata(pdev, drm);
 | |
| +
 | |
| +	dpi->bus_fmt = default_bus_fmt;
 | |
| +	ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt);
 | |
| +
 | |
| +	for (i = 0; i < RP1DPI_NUM_HW_BLOCKS; i++) {
 | |
| +		dpi->hw_base[i] =
 | |
| +			devm_ioremap_resource(dev,
 | |
| +					      platform_get_resource(dpi->pdev, IORESOURCE_MEM, i));
 | |
| +		if (IS_ERR(dpi->hw_base[i])) {
 | |
| +			ret = PTR_ERR(dpi->hw_base[i]);
 | |
| +			dev_err(dev, "Error memory mapping regs[%d]\n", i);
 | |
| +			goto err_free_drm;
 | |
| +		}
 | |
| +	}
 | |
| +	ret = platform_get_irq(dpi->pdev, 0);
 | |
| +	if (ret > 0)
 | |
| +		ret = devm_request_irq(dev, ret, rp1dpi_hw_isr,
 | |
| +				       IRQF_SHARED, "rp1-dpi", dpi);
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "Unable to request interrupt\n");
 | |
| +		ret = -EINVAL;
 | |
| +		goto err_free_drm;
 | |
| +	}
 | |
| +	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
 | |
| +
 | |
| +	for (i = 0; i < RP1DPI_NUM_CLOCKS; i++) {
 | |
| +		static const char * const myclocknames[RP1DPI_NUM_CLOCKS] = {
 | |
| +			"dpiclk", "plldiv", "pllcore"
 | |
| +		};
 | |
| +		dpi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
 | |
| +		if (IS_ERR(dpi->clocks[i])) {
 | |
| +			ret = PTR_ERR(dpi->clocks[i]);
 | |
| +			goto err_free_drm;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	ret = drmm_mode_config_init(drm);
 | |
| +	if (ret)
 | |
| +		goto err_free_drm;
 | |
| +
 | |
| +	drm->mode_config.max_width  = 4096;
 | |
| +	drm->mode_config.max_height = 4096;
 | |
| +	drm->mode_config.fb_base    = 0;
 | |
| +	drm->mode_config.preferred_depth = 32;
 | |
| +	drm->mode_config.prefer_shadow	 = 0;
 | |
| +	drm->mode_config.prefer_shadow_fbdev = 1;
 | |
| +	drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
 | |
| +	drm->mode_config.funcs = &rp1dpi_mode_funcs;
 | |
| +	drm_vblank_init(drm, 1);
 | |
| +
 | |
| +	ret = drm_simple_display_pipe_init(drm,
 | |
| +					   &dpi->pipe,
 | |
| +					   &rp1dpi_pipe_funcs,
 | |
| +					   rp1dpi_formats,
 | |
| +					   ARRAY_SIZE(rp1dpi_formats),
 | |
| +					   NULL, NULL);
 | |
| +	if (!ret)
 | |
| +		ret = drm_simple_display_pipe_attach_bridge(&dpi->pipe, bridge);
 | |
| +	if (ret)
 | |
| +		goto err_free_drm;
 | |
| +
 | |
| +	drm_mode_config_reset(drm);
 | |
| +
 | |
| +	ret = drm_dev_register(drm, 0);
 | |
| +	if (ret)
 | |
| +		goto err_free_drm;
 | |
| +
 | |
| +	drm_fbdev_generic_setup(drm, 32);
 | |
| +
 | |
| +	dev_info(dev, "%s success\n", __func__);
 | |
| +	return ret;
 | |
| +
 | |
| +err_free_drm:
 | |
| +	dev_err(dev, "%s fail %d\n", __func__, ret);
 | |
| +	drm_dev_put(drm);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int rp1dpi_platform_remove(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct drm_device *drm = platform_get_drvdata(pdev);
 | |
| +
 | |
| +	rp1dpi_stopall(drm);
 | |
| +	drm_dev_unregister(drm);
 | |
| +	drm_atomic_helper_shutdown(drm);
 | |
| +	drm_dev_put(drm);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static void rp1dpi_platform_shutdown(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct drm_device *drm = platform_get_drvdata(pdev);
 | |
| +
 | |
| +	rp1dpi_stopall(drm);
 | |
| +}
 | |
| +
 | |
| +static const struct of_device_id rp1dpi_of_match[] = {
 | |
| +	{
 | |
| +		.compatible = "raspberrypi,rp1dpi",
 | |
| +	},
 | |
| +	{ /* sentinel */ },
 | |
| +};
 | |
| +
 | |
| +MODULE_DEVICE_TABLE(of, rp1dpi_of_match);
 | |
| +
 | |
| +static struct platform_driver rp1dpi_platform_driver = {
 | |
| +	.probe		= rp1dpi_platform_probe,
 | |
| +	.remove		= rp1dpi_platform_remove,
 | |
| +	.shutdown	= rp1dpi_platform_shutdown,
 | |
| +	.driver		= {
 | |
| +		.name	= DRIVER_NAME,
 | |
| +		.owner	= THIS_MODULE,
 | |
| +		.of_match_table = rp1dpi_of_match,
 | |
| +	},
 | |
| +};
 | |
| +
 | |
| +module_platform_driver(rp1dpi_platform_driver);
 | |
| +
 | |
| +MODULE_AUTHOR("Nick Hollinghurst");
 | |
| +MODULE_DESCRIPTION("DRM driver for DPI output on Raspberry Pi RP1");
 | |
| +MODULE_LICENSE("GPL");
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
 | |
| @@ -0,0 +1,69 @@
 | |
| +/* SPDX-License-Identifier: GPL-2.0 */
 | |
| +/*
 | |
| + * DRM Driver for DSI output on Raspberry Pi RP1
 | |
| + *
 | |
| + * Copyright (c) 2023 Raspberry Pi Limited.
 | |
| + */
 | |
| +
 | |
| +#include <linux/types.h>
 | |
| +#include <linux/io.h>
 | |
| +#include <linux/clk.h>
 | |
| +#include <drm/drm_device.h>
 | |
| +#include <drm/drm_simple_kms_helper.h>
 | |
| +
 | |
| +#define MODULE_NAME "drm-rp1-dpi"
 | |
| +#define DRIVER_NAME "drm-rp1-dpi"
 | |
| +
 | |
| +/* ---------------------------------------------------------------------- */
 | |
| +
 | |
| +#define RP1DPI_HW_BLOCK_DPI   0
 | |
| +#define RP1DPI_HW_BLOCK_CFG   1
 | |
| +#define RP1DPI_NUM_HW_BLOCKS  2
 | |
| +
 | |
| +#define RP1DPI_CLK_DPI      0
 | |
| +#define RP1DPI_CLK_PLLDIV   1
 | |
| +#define RP1DPI_CLK_PLLCORE  2
 | |
| +#define RP1DPI_NUM_CLOCKS   3
 | |
| +
 | |
| +/* ---------------------------------------------------------------------- */
 | |
| +
 | |
| +struct rp1_dpi {
 | |
| +	/* DRM and platform device pointers */
 | |
| +	struct drm_device *drm;
 | |
| +	struct platform_device *pdev;
 | |
| +
 | |
| +	/* Framework and helper objects */
 | |
| +	struct drm_simple_display_pipe pipe;
 | |
| +	struct drm_connector connector;
 | |
| +
 | |
| +	/* Clocks: Video PLL, its primary divider, and DPI clock. */
 | |
| +	struct clk *clocks[RP1DPI_NUM_CLOCKS];
 | |
| +
 | |
| +	/* Block (DPI, VOCFG) base addresses, and current state */
 | |
| +	void __iomem *hw_base[RP1DPI_NUM_HW_BLOCKS];
 | |
| +	u32 cur_fmt;
 | |
| +	u32 bus_fmt;
 | |
| +	bool de_inv, clk_inv;
 | |
| +	bool dpi_running, pipe_enabled;
 | |
| +	struct completion finished;
 | |
| +};
 | |
| +
 | |
| +/* ---------------------------------------------------------------------- */
 | |
| +/* Functions to control the DPI/DMA block				  */
 | |
| +
 | |
| +void rp1dpi_hw_setup(struct rp1_dpi *dpi,
 | |
| +		     u32 in_format,
 | |
| +		     u32 bus_format,
 | |
| +		     bool de_inv,
 | |
| +		     struct drm_display_mode const *mode);
 | |
| +void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride);
 | |
| +void rp1dpi_hw_stop(struct rp1_dpi *dpi);
 | |
| +int rp1dpi_hw_busy(struct rp1_dpi *dpi);
 | |
| +irqreturn_t rp1dpi_hw_isr(int irq, void *dev);
 | |
| +void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable);
 | |
| +
 | |
| +/* ---------------------------------------------------------------------- */
 | |
| +/* Functions to control the VIDEO OUT CFG block and check RP1 platform	  */
 | |
| +
 | |
| +void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge);
 | |
| +void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi);
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
 | |
| @@ -0,0 +1,510 @@
 | |
| +// SPDX-License-Identifier: GPL-2.0-or-later
 | |
| +/*
 | |
| + * DRM Driver for DPI output on Raspberry Pi RP1
 | |
| + *
 | |
| + * Copyright (c) 2023 Raspberry Pi Limited.
 | |
| + */
 | |
| +
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/errno.h>
 | |
| +#include <linux/mm.h>
 | |
| +#include <linux/delay.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/printk.h>
 | |
| +#include <linux/rp1_platform.h>
 | |
| +
 | |
| +#include "rp1_dpi.h"
 | |
| +
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_SEL
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Selects source: VEC or DPI
 | |
| +#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
 | |
| +#define VIDEO_OUT_CFG_SEL_BITS	 0x00000013
 | |
| +#define VIDEO_OUT_CFG_SEL_RESET	 0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_SEL_PCLK_INV
 | |
| +// Description : Select dpi_pclk output port polarity inversion.
 | |
| +#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS	  0x00000010
 | |
| +#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB	  4
 | |
| +#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB	  4
 | |
| +#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_SEL_PAD_MUX
 | |
| +// Description : VEC 1 DPI 0
 | |
| +#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET	 0x0
 | |
| +#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS	 0x00000002
 | |
| +#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB	 1
 | |
| +#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB	 1
 | |
| +#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_SEL_VDAC_MUX
 | |
| +// Description : VEC 1 DPI 0
 | |
| +#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS	  0x00000001
 | |
| +#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB	  0
 | |
| +#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB	  0
 | |
| +#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_VDAC_CFG
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Configure SNPS VDAC
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_BITS   0x1fffffff
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_RESET  0x0003ffff
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS   0x1c000000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB    28
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB    26
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENSC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS   0x03800000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB	   25
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB	   23
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS   0x00700000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB    22
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB    20
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS   0x00080000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB    19
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB    19
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS   0x00040000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB    18
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB    18
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
 | |
| +// Description : dac2 gain control
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET  0x3f
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS   0x0003f000
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB    17
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB    12
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
 | |
| +// Description : dac1 gain control
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET  0x3f
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS   0x00000fc0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB    11
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB    6
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
 | |
| +// Description : dac0 gain control
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET  0x3f
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS   0x0000003f
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB    5
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB    0
 | |
| +#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_VDAC_STATUS
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Read VDAC status
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_BITS	 0x00000017
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_RESET	 0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET	0x0
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS	0x00000010
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB	4
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB	4
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET  "-"
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS	  0x00000007
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB	  2
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB	  0
 | |
| +#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_MEM_PD
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Control memory power down
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_BITS   0x00000003
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_MEM_PD_VEC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET	0x0
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS	0x00000002
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB	1
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB	1
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_MEM_PD_DPI
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET	0x0
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS	0x00000001
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB	0
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB	0
 | |
| +#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_TEST_OVERRIDE
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS   0xffffffff
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS   0x80000000
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB    31
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB    31
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET	0x0
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS	0x40000000
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB	30
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB	30
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET  0x00000000
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS	  0x3fffffff
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB	  29
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB	  0
 | |
| +#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_INTR
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Raw Interrupts
 | |
| +#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
 | |
| +#define VIDEO_OUT_CFG_INTR_BITS	  0x00000003
 | |
| +#define VIDEO_OUT_CFG_INTR_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTR_DPI
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTR_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTR_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_INTR_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_INTR_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTR_VEC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTR_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTR_VEC_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_INTR_VEC_MSB    0
 | |
| +#define VIDEO_OUT_CFG_INTR_VEC_LSB    0
 | |
| +#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_INTE
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Interrupt Enable
 | |
| +#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
 | |
| +#define VIDEO_OUT_CFG_INTE_BITS	  0x00000003
 | |
| +#define VIDEO_OUT_CFG_INTE_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTE_DPI
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTE_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTE_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_INTE_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_INTE_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTE_VEC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTE_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTE_VEC_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_INTE_VEC_MSB    0
 | |
| +#define VIDEO_OUT_CFG_INTE_VEC_LSB    0
 | |
| +#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_INTF
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Interrupt Force
 | |
| +#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
 | |
| +#define VIDEO_OUT_CFG_INTF_BITS	  0x00000003
 | |
| +#define VIDEO_OUT_CFG_INTF_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTF_DPI
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTF_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTF_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_INTF_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_INTF_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTF_VEC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTF_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTF_VEC_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_INTF_VEC_MSB    0
 | |
| +#define VIDEO_OUT_CFG_INTF_VEC_LSB    0
 | |
| +#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_INTS
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Interrupt status after masking & forcing
 | |
| +#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
 | |
| +#define VIDEO_OUT_CFG_INTS_BITS	  0x00000003
 | |
| +#define VIDEO_OUT_CFG_INTS_RESET  0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTS_DPI
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTS_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTS_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_INTS_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_INTS_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_INTS_VEC
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_INTS_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_INTS_VEC_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_INTS_VEC_MSB    0
 | |
| +#define VIDEO_OUT_CFG_INTS_VEC_LSB    0
 | |
| +#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_BLOCK_ID
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Block Identifier
 | |
| +//		 Hexadecimal representation of "VOCF"
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_BITS   0xffffffff
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_RESET  0x564f4346
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_MSB    31
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_LSB    0
 | |
| +#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_INSTANCE_ID
 | |
| +// JTAG access : synchronous
 | |
| +// Description : Block Instance Identifier
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_BITS	 0x0000000f
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_RESET	 0x00000000
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_MSB	 3
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_LSB	 0
 | |
| +#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_RSTSEQ_AUTO
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS	 0x00000007
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET	 0x00000007
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
 | |
| +// Description : 1 = reset is controlled by the sequencer
 | |
| +//		 0 = reset is controlled by rstseq_ctrl
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET  0x1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS   0x00000004
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
 | |
| +// Description : 1 = reset is controlled by the sequencer
 | |
| +//		 0 = reset is controlled by rstseq_ctrl
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET  0x1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
 | |
| +// Description : 1 = reset is controlled by the sequencer
 | |
| +//		 0 = reset is controlled by rstseq_ctrl
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET  0x1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS   0x00000007
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET  0x00000006
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
 | |
| +// Description : Is this reset parallel (i.e. not part of the sequence)
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET	 0x1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS	 0x00000004
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB	 2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB	 2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
 | |
| +// Description : Is this reset parallel (i.e. not part of the sequence)
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET	 0x1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS	 0x00000002
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB	 1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB	 1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
 | |
| +// Description : Is this reset parallel (i.e. not part of the sequence)
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET	0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS	0x00000001
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB	0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB	0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_RSTSEQ_CTRL
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS	 0x00000007
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET	 0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
 | |
| +// Description : 1 = keep the reset asserted
 | |
| +//		 0 = keep the reset deasserted
 | |
| +//		 This is ignored if rstseq_auto=1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS   0x00000004
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
 | |
| +// Description : 1 = keep the reset asserted
 | |
| +//		 0 = keep the reset deasserted
 | |
| +//		 This is ignored if rstseq_auto=1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
 | |
| +// Description : 1 = keep the reset asserted
 | |
| +//		 0 = keep the reset deasserted
 | |
| +//		 This is ignored if rstseq_auto=1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_RSTSEQ_TRIG
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS	 0x00000007
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET	 0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
 | |
| +// Description : Pulses the reset output
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS   0x00000004
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
 | |
| +// Description : Pulses the reset output
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
 | |
| +// Description : Pulses the reset output
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
 | |
| +// =============================================================================
 | |
| +// Register    : VIDEO_OUT_CFG_RSTSEQ_DONE
 | |
| +// JTAG access : synchronous
 | |
| +// Description : None
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS	 0x00000007
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET	 0x00000000
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
 | |
| +// Description : Indicates the current state of the reset
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS   0x00000004
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB    2
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
 | |
| +// Description : Indicates the current state of the reset
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS   0x00000002
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB    1
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
 | |
| +// -----------------------------------------------------------------------------
 | |
| +// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
 | |
| +// Description : Indicates the current state of the reset
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET  0x0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS   0x00000001
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB    0
 | |
| +#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
 | |
| +// =============================================================================
 | |
| +
 | |
| +#define CFG_WRITE(reg, val)  writel((val),  dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
 | |
| +#define CFG_READ(reg)	     readl(dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
 | |
| +
 | |
| +void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge)
 | |
| +{
 | |
| +	/*
 | |
| +	 * We assume DPI and VEC can't be used at the same time (due to
 | |
| +	 * clashing requirements for PLL_VIDEO, and potentially for VDAC).
 | |
| +	 * We therefore leave VEC memories powered down.
 | |
| +	 */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_VEC_BITS);
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE,
 | |
| +		  VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS);
 | |
| +
 | |
| +	/* DPI->Pads; DPI->VDAC; optionally flip PCLK polarity */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_SEL,
 | |
| +		  drive_negedge ? VIDEO_OUT_CFG_SEL_PCLK_INV_BITS : 0);
 | |
| +
 | |
| +	/* configure VDAC for 3 channels, bandgap on, 710mV swing */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
 | |
| +
 | |
| +	/* enable DPI interrupt */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_DPI_BITS);
 | |
| +}
 | |
| +
 | |
| +void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi)
 | |
| +{
 | |
| +	/* disable DPI interrupt */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
 | |
| +
 | |
| +	/* Ensure VDAC is turned off; power down DPI,VEC memories */
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
 | |
| +	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
 | |
| +}
 | |
| --- /dev/null
 | |
| +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
 | |
| @@ -0,0 +1,486 @@
 | |
| +// SPDX-License-Identifier: GPL-2.0-or-later
 | |
| +/*
 | |
| + * DRM Driver for DPI output on Raspberry Pi RP1
 | |
| + *
 | |
| + * Copyright (c) 2023 Raspberry Pi Limited.
 | |
| + */
 | |
| +
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/errno.h>
 | |
| +#include <linux/mm.h>
 | |
| +#include <linux/delay.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/media-bus-format.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/printk.h>
 | |
| +#include <drm/drm_fourcc.h>
 | |
| +#include <drm/drm_print.h>
 | |
| +#include <drm/drm_vblank.h>
 | |
| +
 | |
| +#include "rp1_dpi.h"
 | |
| +
 | |
| +// --- DPI DMA REGISTERS ---
 | |
| +
 | |
| +// Control
 | |
| +#define DPI_DMA_CONTROL				      0x0
 | |
| +#define DPI_DMA_CONTROL_ARM_SHIFT		      0
 | |
| +#define DPI_DMA_CONTROL_ARM_MASK		      BIT(DPI_DMA_CONTROL_ARM_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_ALIGN16_SHIFT		      2
 | |
| +#define DPI_DMA_CONTROL_ALIGN16_MASK		      BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT	      1
 | |
| +#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK	      BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT	      3
 | |
| +#define DPI_DMA_CONTROL_HIGH_WATER_MASK		      (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_DEN_POL_SHIFT		      12
 | |
| +#define DPI_DMA_CONTROL_DEN_POL_MASK		      BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT		      13
 | |
| +#define DPI_DMA_CONTROL_HSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT		      14
 | |
| +#define DPI_DMA_CONTROL_VSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_COLORM_SHIFT		      15
 | |
| +#define DPI_DMA_CONTROL_COLORM_MASK		      BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_SHUTDN_SHIFT		      16
 | |
| +#define DPI_DMA_CONTROL_SHUTDN_MASK		      BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_HBP_EN_SHIFT		      17
 | |
| +#define DPI_DMA_CONTROL_HBP_EN_MASK		      BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_HFP_EN_SHIFT		      18
 | |
| +#define DPI_DMA_CONTROL_HFP_EN_MASK		      BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_VBP_EN_SHIFT		      19
 | |
| +#define DPI_DMA_CONTROL_VBP_EN_MASK		      BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_VFP_EN_SHIFT		      20
 | |
| +#define DPI_DMA_CONTROL_VFP_EN_MASK		      BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT		      21
 | |
| +#define DPI_DMA_CONTROL_HSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT		      22
 | |
| +#define DPI_DMA_CONTROL_VSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT	      23
 | |
| +#define DPI_DMA_CONTROL_FORCE_IMMED_MASK	      BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT	      24
 | |
| +#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK	      BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
 | |
| +#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT	      25
 | |
| +#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK	      BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
 | |
| +
 | |
| +// IRQ_ENABLES
 | |
| +#define DPI_DMA_IRQ_EN				      0x04
 | |
| +#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT		      0
 | |
| +#define DPI_DMA_IRQ_EN_DMA_READY_MASK		      BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT		      1
 | |
| +#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK		      BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT	      2
 | |
| +#define DPI_DMA_IRQ_EN_FRAME_START_MASK		      BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT	      3
 | |
| +#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK		      BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_TE_SHIFT			      4
 | |
| +#define DPI_DMA_IRQ_EN_TE_MASK			      BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_ERROR_SHIFT		      5
 | |
| +#define DPI_DMA_IRQ_EN_ERROR_MASK		      BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_MATCH_SHIFT		      6
 | |
| +#define DPI_DMA_IRQ_EN_MATCH_MASK		      BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
 | |
| +#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT		      16
 | |
| +#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK		      (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
 | |
| +
 | |
| +// IRQ_FLAGS
 | |
| +#define DPI_DMA_IRQ_FLAGS			      0x08
 | |
| +#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT	      0
 | |
| +#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT	      1
 | |
| +#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK	      BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT	      2
 | |
| +#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK	      BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT	      3
 | |
| +#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_TE_SHIFT		      4
 | |
| +#define DPI_DMA_IRQ_FLAGS_TE_MASK		      BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT		      5
 | |
| +#define DPI_DMA_IRQ_FLAGS_ERROR_MASK		      BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
 | |
| +#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT		      6
 | |
| +#define DPI_DMA_IRQ_FLAGS_MATCH_MASK		      BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
 | |
| +
 | |
| +// QOS
 | |
| +#define DPI_DMA_QOS				      0xC
 | |
| +#define DPI_DMA_QOS_DQOS_SHIFT			      0
 | |
| +#define DPI_DMA_QOS_DQOS_MASK			      (0xF << DPI_DMA_QOS_DQOS_SHIFT)
 | |
| +#define DPI_DMA_QOS_ULEV_SHIFT			      4
 | |
| +#define DPI_DMA_QOS_ULEV_MASK			      (0xF << DPI_DMA_QOS_ULEV_SHIFT)
 | |
| +#define DPI_DMA_QOS_UQOS_SHIFT			      8
 | |
| +#define DPI_DMA_QOS_UQOS_MASK			      (0xF << DPI_DMA_QOS_UQOS_SHIFT)
 | |
| +#define DPI_DMA_QOS_LLEV_SHIFT			      12
 | |
| +#define DPI_DMA_QOS_LLEV_MASK			      (0xF << DPI_DMA_QOS_LLEV_SHIFT)
 | |
| +#define DPI_DMA_QOS_LQOS_SHIFT			      16
 | |
| +#define DPI_DMA_QOS_LQOS_MASK			      (0xF << DPI_DMA_QOS_LQOS_SHIFT)
 | |
| +
 | |
| +// Panics
 | |
| +#define DPI_DMA_PANICS				     0x38
 | |
| +#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT	     0
 | |
| +#define DPI_DMA_PANICS_UPPER_COUNT_MASK		     \
 | |
| +				(0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
 | |
| +#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT	     16
 | |
| +#define DPI_DMA_PANICS_LOWER_COUNT_MASK		     \
 | |
| +				(0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
 | |
| +
 | |
| +// DMA Address Lower:
 | |
| +#define DPI_DMA_DMA_ADDR_L			     0x10
 | |
| +
 | |
| +// DMA Address Upper:
 | |
| +#define DPI_DMA_DMA_ADDR_H			     0x40
 | |
| +
 | |
| +// DMA stride
 | |
| +#define DPI_DMA_DMA_STRIDE			     0x14
 | |
| +
 | |
| +// Visible Area
 | |
| +#define DPI_DMA_VISIBLE_AREA			     0x18
 | |
| +#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT     0
 | |
| +#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
 | |
| +#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT    16
 | |
| +#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
 | |
| +
 | |
| +// Sync width
 | |
| +#define DPI_DMA_SYNC_WIDTH   0x1C
 | |
| +#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT	 0
 | |
| +#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
 | |
| +#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT	 16
 | |
| +#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
 | |
| +
 | |
| +// Back porch
 | |
| +#define DPI_DMA_BACK_PORCH   0x20
 | |
| +#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT	 0
 | |
| +#define DPI_DMA_BACK_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
 | |
| +#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT	 16
 | |
| +#define DPI_DMA_BACK_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
 | |
| +
 | |
| +// Front porch
 | |
| +#define DPI_DMA_FRONT_PORCH  0x24
 | |
| +#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT     0
 | |
| +#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
 | |
| +#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT     16
 | |
| +#define DPI_DMA_FRONT_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
 | |
| +
 | |
| +// Input masks
 | |
| +#define DPI_DMA_IMASK	 0x2C
 | |
| +#define DPI_DMA_IMASK_R_SHIFT	 0
 | |
| +#define DPI_DMA_IMASK_R_MASK	 (0x3FF << DPI_DMA_IMASK_R_SHIFT)
 | |
| +#define DPI_DMA_IMASK_G_SHIFT	 10
 | |
| +#define DPI_DMA_IMASK_G_MASK	 (0x3FF << DPI_DMA_IMASK_G_SHIFT)
 | |
| +#define DPI_DMA_IMASK_B_SHIFT	 20
 | |
| +#define DPI_DMA_IMASK_B_MASK	 (0x3FF << DPI_DMA_IMASK_B_SHIFT)
 | |
| +
 | |
| +// Output Masks
 | |
| +#define DPI_DMA_OMASK	 0x30
 | |
| +#define DPI_DMA_OMASK_R_SHIFT	 0
 | |
| +#define DPI_DMA_OMASK_R_MASK	 (0x3FF << DPI_DMA_OMASK_R_SHIFT)
 | |
| +#define DPI_DMA_OMASK_G_SHIFT	 10
 | |
| +#define DPI_DMA_OMASK_G_MASK	 (0x3FF << DPI_DMA_OMASK_G_SHIFT)
 | |
| +#define DPI_DMA_OMASK_B_SHIFT	 20
 | |
| +#define DPI_DMA_OMASK_B_MASK	 (0x3FF << DPI_DMA_OMASK_B_SHIFT)
 | |
| +
 | |
| +// Shifts
 | |
| +#define DPI_DMA_SHIFT	 0x28
 | |
| +#define DPI_DMA_SHIFT_IR_SHIFT	 0
 | |
| +#define DPI_DMA_SHIFT_IR_MASK	 (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
 | |
| +#define DPI_DMA_SHIFT_IG_SHIFT	 5
 | |
| +#define DPI_DMA_SHIFT_IG_MASK	 (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
 | |
| +#define DPI_DMA_SHIFT_IB_SHIFT	 10
 | |
| +#define DPI_DMA_SHIFT_IB_MASK	 (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
 | |
| +#define DPI_DMA_SHIFT_OR_SHIFT	 15
 | |
| +#define DPI_DMA_SHIFT_OR_MASK	 (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
 | |
| +#define DPI_DMA_SHIFT_OG_SHIFT	 20
 | |
| +#define DPI_DMA_SHIFT_OG_MASK	 (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
 | |
| +#define DPI_DMA_SHIFT_OB_SHIFT	 25
 | |
| +#define DPI_DMA_SHIFT_OB_MASK	 (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
 | |
| +
 | |
| +// Scaling
 | |
| +#define DPI_DMA_RGBSZ	 0x34
 | |
| +#define DPI_DMA_RGBSZ_BPP_SHIFT	 16
 | |
| +#define DPI_DMA_RGBSZ_BPP_MASK	 (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
 | |
| +#define DPI_DMA_RGBSZ_R_SHIFT	 0
 | |
| +#define DPI_DMA_RGBSZ_R_MASK	 (0xF << DPI_DMA_RGBSZ_R_SHIFT)
 | |
| +#define DPI_DMA_RGBSZ_G_SHIFT	 4
 | |
| +#define DPI_DMA_RGBSZ_G_MASK	 (0xF << DPI_DMA_RGBSZ_G_SHIFT)
 | |
| +#define DPI_DMA_RGBSZ_B_SHIFT	 8
 | |
| +#define DPI_DMA_RGBSZ_B_MASK	 (0xF << DPI_DMA_RGBSZ_B_SHIFT)
 | |
| +
 | |
| +// Status
 | |
| +#define DPI_DMA_STATUS  0x3c
 | |
| +
 | |
| +#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
 | |
| +
 | |
| +static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg)
 | |
| +{
 | |
| +	void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
 | |
| +
 | |
| +	return readl(addr);
 | |
| +}
 | |
| +
 | |
| +static void rp1dpi_hw_write(struct rp1_dpi *dpi, unsigned int reg, unsigned int val)
 | |
| +{
 | |
| +	void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
 | |
| +
 | |
| +	writel(val, addr);
 | |
| +}
 | |
| +
 | |
| +int rp1dpi_hw_busy(struct rp1_dpi *dpi)
 | |
| +{
 | |
| +	return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
 | |
| +}
 | |
| +
 | |
| +/* Table of supported input (in-memory/DMA) pixel formats. */
 | |
| +struct rp1dpi_ipixfmt {
 | |
| +	u32 format; /* DRM format code                           */
 | |
| +	u32 mask;   /* RGB masks (10 bits each, left justified)  */
 | |
| +	u32 shift;  /* RGB MSB positions in the memory word      */
 | |
| +	u32 rgbsz;  /* Shifts used for scaling; also (BPP/8-1)   */
 | |
| +};
 | |
| +
 | |
| +#define IMASK_RGB(r, g, b)	(BITS(DPI_DMA_IMASK_R, r)  | \
 | |
| +				 BITS(DPI_DMA_IMASK_G, g)  | \
 | |
| +				 BITS(DPI_DMA_IMASK_B, b))
 | |
| +#define OMASK_RGB(r, g, b)	(BITS(DPI_DMA_OMASK_R, r)  | \
 | |
| +				 BITS(DPI_DMA_OMASK_G, g)  | \
 | |
| +				 BITS(DPI_DMA_OMASK_B, b))
 | |
| +#define ISHIFT_RGB(r, g, b)	(BITS(DPI_DMA_SHIFT_IR, r) | \
 | |
| +				 BITS(DPI_DMA_SHIFT_IG, g) | \
 | |
| +				 BITS(DPI_DMA_SHIFT_IB, b))
 | |
| +#define OSHIFT_RGB(r, g, b)	(BITS(DPI_DMA_SHIFT_OR, r) | \
 | |
| +				 BITS(DPI_DMA_SHIFT_OG, g) | \
 | |
| +				 BITS(DPI_DMA_SHIFT_OB, b))
 | |
| +
 | |
| +static const struct rp1dpi_ipixfmt my_formats[] = {
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_XRGB8888,
 | |
| +	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
 | |
| +	  .shift  = ISHIFT_RGB(23, 15, 7),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
 | |
| +	},
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_XBGR8888,
 | |
| +	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
 | |
| +	  .shift  = ISHIFT_RGB(7, 15, 23),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
 | |
| +	},
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_RGB888,
 | |
| +	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
 | |
| +	  .shift  = ISHIFT_RGB(23, 15, 7),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
 | |
| +	},
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_BGR888,
 | |
| +	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
 | |
| +	  .shift  = ISHIFT_RGB(7, 15, 23),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
 | |
| +	},
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_RGB565,
 | |
| +	  .mask	  = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
 | |
| +	  .shift  = ISHIFT_RGB(15, 10, 4),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
 | |
| +		    BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
 | |
| +	},
 | |
| +	{
 | |
| +	  .format = DRM_FORMAT_BGR565,
 | |
| +	  .mask	  = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
 | |
| +	  .shift  = ISHIFT_RGB(4, 10, 15),
 | |
| +	  .rgbsz  = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
 | |
| +		    BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
 | |
| +	}
 | |
| +};
 | |
| +
 | |
| +static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz)
 | |
| +{
 | |
| +	switch (bus_format) {
 | |
| +	case MEDIA_BUS_FMT_RGB565_1X16:
 | |
| +		if (*shift == ISHIFT_RGB(15, 10, 4)) {
 | |
| +			/* When framebuffer is RGB565, we can output RGB565 */
 | |
| +			*shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0);
 | |
| +			*rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
 | |
| +			return OMASK_RGB(0x3fc, 0x3fc, 0);
 | |
| +		}
 | |
| +
 | |
| +		/* due to a HW limitation, bit-depth is effectively RGB535 */
 | |
| +		*shift |= OSHIFT_RGB(19, 14, 6);
 | |
| +		*imask &= IMASK_RGB(0x3e0, 0x380, 0x3e0);
 | |
| +		*rgbsz = BITS(DPI_DMA_RGBSZ_G, 5) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
 | |
| +		return OMASK_RGB(0x3e0, 0x39c, 0x3e0);
 | |
| +
 | |
| +	case MEDIA_BUS_FMT_RGB666_1X18:
 | |
| +	case MEDIA_BUS_FMT_BGR666_1X18:
 | |
| +		/* due to a HW limitation, bit-depth is effectively RGB444 */
 | |
| +		*shift |= OSHIFT_RGB(23, 15, 7);
 | |
| +		*imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0);
 | |
| +		*rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
 | |
| +		return OMASK_RGB(0x330, 0x3c0, 0x3c0);
 | |
| +
 | |
| +	case MEDIA_BUS_FMT_RGB888_1X24:
 | |
| +	case MEDIA_BUS_FMT_BGR888_1X24:
 | |
| +	case MEDIA_BUS_FMT_RGB101010_1X30:
 | |
| +		/* The full 24 bits can be output. Note that RP1's internal wiring means
 | |
| +		 * that 8.8.8 to GPIO pads can share with 10.10.10 to the onboard VDAC.
 | |
| +		 */
 | |
| +		*shift |= OSHIFT_RGB(29, 19, 9);
 | |
| +		return OMASK_RGB(0x3fc, 0x3fc, 0x3fc);
 | |
| +
 | |
| +	default:
 | |
| +		/* RGB666_1x24_CPADHI, BGR666_1X24_CPADHI and "RGB565_666" formats */
 | |
| +		*shift |= OSHIFT_RGB(27, 17, 7);
 | |
| +		*rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
 | |
| +		return OMASK_RGB(0x3f0, 0x3f0, 0x3f0);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +#define BUS_FMT_IS_BGR(fmt) (				       \
 | |
| +		((fmt) == MEDIA_BUS_FMT_BGR666_1X18)        || \
 | |
| +		((fmt) == MEDIA_BUS_FMT_BGR666_1X24_CPADHI) || \
 | |
| +		((fmt) == MEDIA_BUS_FMT_BGR888_1X24))
 | |
| +
 | |
| +void rp1dpi_hw_setup(struct rp1_dpi *dpi,
 | |
| +		     u32 in_format, u32 bus_format, bool de_inv,
 | |
| +		    struct drm_display_mode const *mode)
 | |
| +{
 | |
| +	u32 shift, imask, omask, rgbsz;
 | |
| +	int i;
 | |
| +
 | |
| +	pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC",
 | |
| +		__func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
 | |
| +		mode->hdisplay, mode->vdisplay,
 | |
| +		mode->htotal, mode->vtotal,
 | |
| +		mode->clock,
 | |
| +		(mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
 | |
| +		(mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
 | |
| +		de_inv ? '-' : '+',
 | |
| +		dpi->clk_inv ? '-' : '+');
 | |
| +
 | |
| +	/*
 | |
| +	 * Configure all DPI/DMA block registers, except base address.
 | |
| +	 * DMA will not actually start until a FB base address is specified
 | |
| +	 * using rp1dpi_hw_update().
 | |
| +	 */
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
 | |
| +			BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
 | |
| +			BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
 | |
| +			BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
 | |
| +			BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
 | |
| +
 | |
| +	/* In these registers, "back porch" time includes sync width */
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
 | |
| +			BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
 | |
| +			BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
 | |
| +			BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
 | |
| +			BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
 | |
| +
 | |
| +	/* Input to output pixel format conversion */
 | |
| +	for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
 | |
| +		if (my_formats[i].format == in_format)
 | |
| +			break;
 | |
| +	}
 | |
| +	if (i >= ARRAY_SIZE(my_formats)) {
 | |
| +		pr_err("%s: bad input format\n", __func__);
 | |
| +		i = 4;
 | |
| +	}
 | |
| +	if (BUS_FMT_IS_BGR(bus_format))
 | |
| +		i ^= 1;
 | |
| +	shift = my_formats[i].shift;
 | |
| +	imask = my_formats[i].mask;
 | |
| +	rgbsz = my_formats[i].rgbsz;
 | |
| +	omask = set_output_format(bus_format, &shift, &imask, &rgbsz);
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_RGBSZ, rgbsz);
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_QOS,
 | |
| +			BITS(DPI_DMA_QOS_DQOS, 0x0) |
 | |
| +			BITS(DPI_DMA_QOS_ULEV, 0xb) |
 | |
| +			BITS(DPI_DMA_QOS_UQOS, 0x2) |
 | |
| +			BITS(DPI_DMA_QOS_LLEV, 0x8) |
 | |
| +			BITS(DPI_DMA_QOS_LQOS, 0x7));
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1);
 | |
| +	rp1dpi_hw_vblank_ctrl(dpi, 1);
 | |
| +
 | |
| +	i = rp1dpi_hw_busy(dpi);
 | |
| +	if (i)
 | |
| +		pr_warn("%s: Unexpectedly busy at start!", __func__);
 | |
| +
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_CONTROL,
 | |
| +			BITS(DPI_DMA_CONTROL_ARM,          !i) |
 | |
| +			BITS(DPI_DMA_CONTROL_AUTO_REPEAT,   1) |
 | |
| +			BITS(DPI_DMA_CONTROL_HIGH_WATER,  448) |
 | |
| +			BITS(DPI_DMA_CONTROL_DEN_POL,  de_inv) |
 | |
| +			BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) |
 | |
| +			BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
 | |
| +			BITS(DPI_DMA_CONTROL_COLORM,	   0) |
 | |
| +			BITS(DPI_DMA_CONTROL_SHUTDN,	   0) |
 | |
| +			BITS(DPI_DMA_CONTROL_HBP_EN,    (mode->htotal != mode->hsync_end))      |
 | |
| +			BITS(DPI_DMA_CONTROL_HFP_EN,    (mode->hsync_start != mode->hdisplay))  |
 | |
| +			BITS(DPI_DMA_CONTROL_VBP_EN,    (mode->vtotal != mode->vsync_end))      |
 | |
| +			BITS(DPI_DMA_CONTROL_VFP_EN,    (mode->vsync_start != mode->vdisplay))  |
 | |
| +			BITS(DPI_DMA_CONTROL_HSYNC_EN,  (mode->hsync_end != mode->hsync_start)) |
 | |
| +			BITS(DPI_DMA_CONTROL_VSYNC_EN,  (mode->vsync_end != mode->vsync_start)));
 | |
| +}
 | |
| +
 | |
| +void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride)
 | |
| +{
 | |
| +	u64 a = addr + offset;
 | |
| +
 | |
| +	/*
 | |
| +	 * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(),
 | |
| +	 * DMA starts immediately; if already running, the buffer will flip at
 | |
| +	 * the next vertical sync event.
 | |
| +	 */
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
 | |
| +}
 | |
| +
 | |
| +void rp1dpi_hw_stop(struct rp1_dpi *dpi)
 | |
| +{
 | |
| +	u32 ctrl;
 | |
| +
 | |
| +	/*
 | |
| +	 * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
 | |
| +	 * the current and any queued frame to end. "Force drain" flags are not used,
 | |
| +	 * as they seem to prevent DMA from re-starting properly; it's safer to wait.
 | |
| +	 */
 | |
| +	reinit_completion(&dpi->finished);
 | |
| +	ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL);
 | |
| +	ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl);
 | |
| +	if (!wait_for_completion_timeout(&dpi->finished, HZ / 10))
 | |
| +		drm_err(dpi->drm, "%s: timed out waiting for idle\n", __func__);
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0);
 | |
| +}
 | |
| +
 | |
| +void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable)
 | |
| +{
 | |
| +	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN,
 | |
| +			BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1)      |
 | |
| +			BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1)        |
 | |
| +			BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
 | |
| +			BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
 | |
| +}
 | |
| +
 | |
| +irqreturn_t rp1dpi_hw_isr(int irq, void *dev)
 | |
| +{
 | |
| +	struct rp1_dpi *dpi = dev;
 | |
| +	u32 u = rp1dpi_hw_read(dpi, DPI_DMA_IRQ_FLAGS);
 | |
| +
 | |
| +	if (u) {
 | |
| +		rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, u);
 | |
| +		if (dpi) {
 | |
| +			if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
 | |
| +				drm_err_ratelimited(dpi->drm,
 | |
| +						    "Underflow! (panics=0x%08x)\n",
 | |
| +						    rp1dpi_hw_read(dpi, DPI_DMA_PANICS));
 | |
| +			if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
 | |
| +				drm_crtc_handle_vblank(&dpi->pipe.crtc);
 | |
| +			if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
 | |
| +				complete(&dpi->finished);
 | |
| +		}
 | |
| +	}
 | |
| +	return u ? IRQ_HANDLED : IRQ_NONE;
 | |
| +}
 |