Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad49f860 authored by Liviu Dudau's avatar Liviu Dudau
Browse files

drm/arm: Add support for Mali Display Processors



Add support for the new family of Display Processors from ARM Ltd.
This commit adds basic support for Mali DP500, DP550 and DP650
parts, with only the display engine being supported at the moment.

Cc: David Brown <David.Brown@arm.com>
Cc: Brian Starkey <Brian.Starkey@arm.com>

Signed-off-by: default avatarLiviu Dudau <Liviu.Dudau@arm.com>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent ee6ea993
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -25,3 +25,19 @@ config DRM_HDLCD_SHOW_UNDERRUN
	  Enable this option to show in red colour the pixels that the
	  HDLCD device did not fetch from framebuffer due to underrun
	  conditions.

config DRM_MALI_DISPLAY
	tristate "ARM Mali Display Processor"
	depends on DRM && OF && (ARM || ARM64)
	depends on COMMON_CLK
	select DRM_ARM
	select DRM_KMS_HELPER
	select DRM_KMS_CMA_HELPER
	select DRM_GEM_CMA_HELPER
	select VIDEOMODE_HELPERS
	help
	  Choose this option if you want to compile the ARM Mali Display
	  Processor driver. It supports the DP500, DP550 and DP650 variants
	  of the hardware.

	  If compiled as a module it will be called mali-dp.
+2 −0
Original line number Diff line number Diff line
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
obj-$(CONFIG_DRM_HDLCD)	+= hdlcd.o
mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
obj-$(CONFIG_DRM_MALI_DISPLAY)	+= mali-dp.o
+216 −0
Original line number Diff line number Diff line
/*
 * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU licence.
 *
 * ARM Mali DP500/DP550/DP650 driver (crtc operations)
 */

#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <linux/clk.h>
#include <video/videomode.h>

#include "malidp_drv.h"
#include "malidp_hw.h"

static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
				   const struct drm_display_mode *mode,
				   struct drm_display_mode *adjusted_mode)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;

	/*
	 * check that the hardware can drive the required clock rate,
	 * but skip the check if the clock is meant to be disabled (req_rate = 0)
	 */
	long rate, req_rate = mode->crtc_clock * 1000;

	if (req_rate) {
		rate = clk_round_rate(hwdev->mclk, req_rate);
		if (rate < req_rate) {
			DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
					 mode->crtc_clock);
			return false;
		}

		rate = clk_round_rate(hwdev->pxlclk, req_rate);
		if (rate != req_rate) {
			DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
					 req_rate);
			return false;
		}
	}

	return true;
}

static void malidp_crtc_enable(struct drm_crtc *crtc)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	struct videomode vm;

	drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);

	clk_prepare_enable(hwdev->pxlclk);

	/* mclk needs to be set to the same or higher rate than pxlclk */
	clk_set_rate(hwdev->mclk, crtc->state->adjusted_mode.crtc_clock * 1000);
	clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);

	hwdev->modeset(hwdev, &vm);
	hwdev->leave_config_mode(hwdev);
	drm_crtc_vblank_on(crtc);
}

static void malidp_crtc_disable(struct drm_crtc *crtc)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;

	drm_crtc_vblank_off(crtc);
	hwdev->enter_config_mode(hwdev);
	clk_disable_unprepare(hwdev->pxlclk);
}

static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
				    struct drm_crtc_state *state)
{
	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
	struct malidp_hw_device *hwdev = malidp->dev;
	struct drm_plane *plane;
	const struct drm_plane_state *pstate;
	u32 rot_mem_free, rot_mem_usable;
	int rotated_planes = 0;

	/*
	 * check if there is enough rotation memory available for planes
	 * that need 90° and 270° rotation. Each plane has set its required
	 * memory size in the ->plane_check() callback, here we only make
	 * sure that the sums are less that the total usable memory.
	 *
	 * The rotation memory allocation algorithm (for each plane):
	 *  a. If no more rotated planes exist, all remaining rotate
	 *     memory in the bank is available for use by the plane.
	 *  b. If other rotated planes exist, and plane's layer ID is
	 *     DE_VIDEO1, it can use all the memory from first bank if
	 *     secondary rotation memory bank is available, otherwise it can
	 *     use up to half the bank's memory.
	 *  c. If other rotated planes exist, and plane's layer ID is not
	 *     DE_VIDEO1, it can use half of the available memory
	 *
	 * Note: this algorithm assumes that the order in which the planes are
	 * checked always has DE_VIDEO1 plane first in the list if it is
	 * rotated. Because that is how we create the planes in the first
	 * place, under current DRM version things work, but if ever the order
	 * in which drm_atomic_crtc_state_for_each_plane() iterates over planes
	 * changes, we need to pre-sort the planes before validation.
	 */

	/* first count the number of rotated planes */
	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		if (pstate->rotation & MALIDP_ROTATED_MASK)
			rotated_planes++;
	}

	rot_mem_free = hwdev->rotation_memory[0];
	/*
	 * if we have more than 1 plane using rotation memory, use the second
	 * block of rotation memory as well
	 */
	if (rotated_planes > 1)
		rot_mem_free += hwdev->rotation_memory[1];

	/* now validate the rotation memory requirements */
	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
		struct malidp_plane *mp = to_malidp_plane(plane);
		struct malidp_plane_state *ms = to_malidp_plane_state(pstate);

		if (pstate->rotation & MALIDP_ROTATED_MASK) {
			/* process current plane */
			rotated_planes--;

			if (!rotated_planes) {
				/* no more rotated planes, we can use what's left */
				rot_mem_usable = rot_mem_free;
			} else {
				if ((mp->layer->id != DE_VIDEO1) ||
				    (hwdev->rotation_memory[1] == 0))
					rot_mem_usable = rot_mem_free / 2;
				else
					rot_mem_usable = hwdev->rotation_memory[0];
			}

			rot_mem_free -= rot_mem_usable;

			if (ms->rotmem_size > rot_mem_usable)
				return -EINVAL;
		}
	}

	return 0;
}

static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
	.mode_fixup = malidp_crtc_mode_fixup,
	.enable = malidp_crtc_enable,
	.disable = malidp_crtc_disable,
	.atomic_check = malidp_crtc_atomic_check,
};

static const struct drm_crtc_funcs malidp_crtc_funcs = {
	.destroy = drm_crtc_cleanup,
	.set_config = drm_atomic_helper_set_config,
	.page_flip = drm_atomic_helper_page_flip,
	.reset = drm_atomic_helper_crtc_reset,
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};

int malidp_crtc_init(struct drm_device *drm)
{
	struct malidp_drm *malidp = drm->dev_private;
	struct drm_plane *primary = NULL, *plane;
	int ret;

	ret = malidp_de_planes_init(drm);
	if (ret < 0) {
		DRM_ERROR("Failed to initialise planes\n");
		return ret;
	}

	drm_for_each_plane(plane, drm) {
		if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
			primary = plane;
			break;
		}
	}

	if (!primary) {
		DRM_ERROR("no primary plane found\n");
		ret = -EINVAL;
		goto crtc_cleanup_planes;
	}

	ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
					&malidp_crtc_funcs, NULL);

	if (!ret) {
		drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
		return 0;
	}

crtc_cleanup_planes:
	malidp_de_planes_destroy(drm);

	return ret;
}
+512 −0
Original line number Diff line number Diff line
/*
 * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU licence.
 *
 * ARM Mali DP500/DP550/DP650 KMS/DRM driver
 */

#include <linux/module.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_reserved_mem.h>

#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_of.h>

#include "malidp_drv.h"
#include "malidp_regs.h"
#include "malidp_hw.h"

#define MALIDP_CONF_VALID_TIMEOUT	250

/*
 * set the "config valid" bit and wait until the hardware acts on it
 */
static int malidp_set_and_wait_config_valid(struct drm_device *drm)
{
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;
	int ret;

	hwdev->set_config_valid(hwdev);
	/* don't wait for config_valid flag if we are in config mode */
	if (hwdev->in_config_mode(hwdev))
		return 0;

	ret = wait_event_interruptible_timeout(malidp->wq,
			atomic_read(&malidp->config_valid) == 1,
			msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));

	return (ret > 0) ? 0 : -ETIMEDOUT;
}

static void malidp_output_poll_changed(struct drm_device *drm)
{
	struct malidp_drm *malidp = drm->dev_private;

	drm_fbdev_cma_hotplug_event(malidp->fbdev);
}

static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
{
	struct drm_pending_vblank_event *event;
	struct drm_device *drm = state->dev;
	struct malidp_drm *malidp = drm->dev_private;
	int ret = malidp_set_and_wait_config_valid(drm);

	if (ret)
		DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n");

	event = malidp->crtc.state->event;
	if (event) {
		malidp->crtc.state->event = NULL;

		spin_lock_irq(&drm->event_lock);
		if (drm_crtc_vblank_get(&malidp->crtc) == 0)
			drm_crtc_arm_vblank_event(&malidp->crtc, event);
		else
			drm_crtc_send_vblank_event(&malidp->crtc, event);
		spin_unlock_irq(&drm->event_lock);
	}
	drm_atomic_helper_commit_hw_done(state);
}

static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
{
	struct drm_device *drm = state->dev;

	drm_atomic_helper_commit_modeset_disables(drm, state);
	drm_atomic_helper_commit_modeset_enables(drm, state);
	drm_atomic_helper_commit_planes(drm, state, true);

	malidp_atomic_commit_hw_done(state);

	drm_atomic_helper_wait_for_vblanks(drm, state);

	drm_atomic_helper_cleanup_planes(drm, state);
}

static struct drm_mode_config_helper_funcs malidp_mode_config_helpers = {
	.atomic_commit_tail = malidp_atomic_commit_tail,
};

static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
	.fb_create = drm_fb_cma_create,
	.output_poll_changed = malidp_output_poll_changed,
	.atomic_check = drm_atomic_helper_check,
	.atomic_commit = drm_atomic_helper_commit,
};

static int malidp_enable_vblank(struct drm_device *drm, unsigned int crtc)
{
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK,
			     hwdev->map.de_irq_map.vsync_irq);
	return 0;
}

static void malidp_disable_vblank(struct drm_device *drm, unsigned int pipe)
{
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK,
			      hwdev->map.de_irq_map.vsync_irq);
}

static int malidp_init(struct drm_device *drm)
{
	int ret;
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	drm_mode_config_init(drm);

	drm->mode_config.min_width = hwdev->min_line_size;
	drm->mode_config.min_height = hwdev->min_line_size;
	drm->mode_config.max_width = hwdev->max_line_size;
	drm->mode_config.max_height = hwdev->max_line_size;
	drm->mode_config.funcs = &malidp_mode_config_funcs;
	drm->mode_config.helper_private = &malidp_mode_config_helpers;

	ret = malidp_crtc_init(drm);
	if (ret) {
		drm_mode_config_cleanup(drm);
		return ret;
	}

	return 0;
}

static int malidp_irq_init(struct platform_device *pdev)
{
	int irq_de, irq_se, ret = 0;
	struct drm_device *drm = dev_get_drvdata(&pdev->dev);

	/* fetch the interrupts from DT */
	irq_de = platform_get_irq_byname(pdev, "DE");
	if (irq_de < 0) {
		DRM_ERROR("no 'DE' IRQ specified!\n");
		return irq_de;
	}
	irq_se = platform_get_irq_byname(pdev, "SE");
	if (irq_se < 0) {
		DRM_ERROR("no 'SE' IRQ specified!\n");
		return irq_se;
	}

	ret = malidp_de_irq_init(drm, irq_de);
	if (ret)
		return ret;

	ret = malidp_se_irq_init(drm, irq_se);
	if (ret) {
		malidp_de_irq_fini(drm);
		return ret;
	}

	return 0;
}

static void malidp_lastclose(struct drm_device *drm)
{
	struct malidp_drm *malidp = drm->dev_private;

	drm_fbdev_cma_restore_mode(malidp->fbdev);
}

static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = drm_open,
	.release = drm_release,
	.unlocked_ioctl = drm_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = drm_compat_ioctl,
#endif
	.poll = drm_poll,
	.read = drm_read,
	.llseek = noop_llseek,
	.mmap = drm_gem_cma_mmap,
};

static struct drm_driver malidp_driver = {
	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
			   DRIVER_PRIME,
	.lastclose = malidp_lastclose,
	.get_vblank_counter = drm_vblank_no_hw_counter,
	.enable_vblank = malidp_enable_vblank,
	.disable_vblank = malidp_disable_vblank,
	.gem_free_object_unlocked = drm_gem_cma_free_object,
	.gem_vm_ops = &drm_gem_cma_vm_ops,
	.dumb_create = drm_gem_cma_dumb_create,
	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
	.dumb_destroy = drm_gem_dumb_destroy,
	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
	.gem_prime_export = drm_gem_prime_export,
	.gem_prime_import = drm_gem_prime_import,
	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
	.gem_prime_vmap = drm_gem_cma_prime_vmap,
	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
	.gem_prime_mmap = drm_gem_cma_prime_mmap,
	.fops = &fops,
	.name = "mali-dp",
	.desc = "ARM Mali Display Processor driver",
	.date = "20160106",
	.major = 1,
	.minor = 0,
};

static const struct of_device_id  malidp_drm_of_match[] = {
	{
		.compatible = "arm,mali-dp500",
		.data = &malidp_device[MALIDP_500]
	},
	{
		.compatible = "arm,mali-dp550",
		.data = &malidp_device[MALIDP_550]
	},
	{
		.compatible = "arm,mali-dp650",
		.data = &malidp_device[MALIDP_650]
	},
	{},
};
MODULE_DEVICE_TABLE(of, malidp_drm_of_match);

#define MAX_OUTPUT_CHANNELS	3

static int malidp_bind(struct device *dev)
{
	struct resource *res;
	struct drm_device *drm;
	struct malidp_drm *malidp;
	struct malidp_hw_device *hwdev;
	struct platform_device *pdev = to_platform_device(dev);
	/* number of lines for the R, G and B output */
	u8 output_width[MAX_OUTPUT_CHANNELS];
	int ret = 0, i;
	u32 version, out_depth = 0;

	malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL);
	if (!malidp)
		return -ENOMEM;

	hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL);
	if (!hwdev)
		return -ENOMEM;

	/*
	 * copy the associated data from malidp_drm_of_match to avoid
	 * having to keep a reference to the OF node after binding
	 */
	memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev));
	malidp->dev = hwdev;

	INIT_LIST_HEAD(&malidp->event_list);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	hwdev->regs = devm_ioremap_resource(dev, res);
	if (IS_ERR(hwdev->regs)) {
		DRM_ERROR("Failed to map control registers area\n");
		return PTR_ERR(hwdev->regs);
	}

	hwdev->pclk = devm_clk_get(dev, "pclk");
	if (IS_ERR(hwdev->pclk))
		return PTR_ERR(hwdev->pclk);

	hwdev->aclk = devm_clk_get(dev, "aclk");
	if (IS_ERR(hwdev->aclk))
		return PTR_ERR(hwdev->aclk);

	hwdev->mclk = devm_clk_get(dev, "mclk");
	if (IS_ERR(hwdev->mclk))
		return PTR_ERR(hwdev->mclk);

	hwdev->pxlclk = devm_clk_get(dev, "pxlclk");
	if (IS_ERR(hwdev->pxlclk))
		return PTR_ERR(hwdev->pxlclk);

	/* Get the optional framebuffer memory resource */
	ret = of_reserved_mem_device_init(dev);
	if (ret && ret != -ENODEV)
		return ret;

	drm = drm_dev_alloc(&malidp_driver, dev);
	if (!drm) {
		ret = -ENOMEM;
		goto alloc_fail;
	}

	/* Enable APB clock in order to get access to the registers */
	clk_prepare_enable(hwdev->pclk);
	/*
	 * Enable AXI clock and main clock so that prefetch can start once
	 * the registers are set
	 */
	clk_prepare_enable(hwdev->aclk);
	clk_prepare_enable(hwdev->mclk);

	ret = hwdev->query_hw(hwdev);
	if (ret) {
		DRM_ERROR("Invalid HW configuration\n");
		goto query_hw_fail;
	}

	version = malidp_hw_read(hwdev, hwdev->map.dc_base + MALIDP_DE_CORE_ID);
	DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16,
		 (version >> 12) & 0xf, (version >> 8) & 0xf);

	/* set the number of lines used for output of RGB data */
	ret = of_property_read_u8_array(dev->of_node,
					"arm,malidp-output-port-lines",
					output_width, MAX_OUTPUT_CHANNELS);
	if (ret)
		goto query_hw_fail;

	for (i = 0; i < MAX_OUTPUT_CHANNELS; i++)
		out_depth = (out_depth << 8) | (output_width[i] & 0xf);
	malidp_hw_write(hwdev, out_depth, hwdev->map.out_depth_base);

	drm->dev_private = malidp;
	dev_set_drvdata(dev, drm);
	atomic_set(&malidp->config_valid, 0);
	init_waitqueue_head(&malidp->wq);

	ret = malidp_init(drm);
	if (ret < 0)
		goto init_fail;

	ret = drm_dev_register(drm, 0);
	if (ret)
		goto register_fail;

	/* Set the CRTC's port so that the encoder component can find it */
	malidp->crtc.port = of_graph_get_next_endpoint(dev->of_node, NULL);

	ret = component_bind_all(dev, drm);
	of_node_put(malidp->crtc.port);

	if (ret) {
		DRM_ERROR("Failed to bind all components\n");
		goto bind_fail;
	}

	ret = malidp_irq_init(pdev);
	if (ret < 0)
		goto irq_init_fail;

	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
	if (ret < 0) {
		DRM_ERROR("failed to initialise vblank\n");
		goto vblank_fail;
	}

	drm_mode_config_reset(drm);

	malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
					   drm->mode_config.num_connector);

	if (IS_ERR(malidp->fbdev)) {
		ret = PTR_ERR(malidp->fbdev);
		malidp->fbdev = NULL;
		goto fbdev_fail;
	}

	drm_kms_helper_poll_init(drm);
	return 0;

fbdev_fail:
	drm_vblank_cleanup(drm);
vblank_fail:
	malidp_se_irq_fini(drm);
	malidp_de_irq_fini(drm);
irq_init_fail:
	component_unbind_all(dev, drm);
bind_fail:
	drm_dev_unregister(drm);
register_fail:
	malidp_de_planes_destroy(drm);
	drm_mode_config_cleanup(drm);
init_fail:
	drm->dev_private = NULL;
	dev_set_drvdata(dev, NULL);
query_hw_fail:
	clk_disable_unprepare(hwdev->mclk);
	clk_disable_unprepare(hwdev->aclk);
	clk_disable_unprepare(hwdev->pclk);
	drm_dev_unref(drm);
alloc_fail:
	of_reserved_mem_device_release(dev);

	return ret;
}

static void malidp_unbind(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct malidp_drm *malidp = drm->dev_private;
	struct malidp_hw_device *hwdev = malidp->dev;

	if (malidp->fbdev) {
		drm_fbdev_cma_fini(malidp->fbdev);
		malidp->fbdev = NULL;
	}
	drm_kms_helper_poll_fini(drm);
	malidp_se_irq_fini(drm);
	malidp_de_irq_fini(drm);
	drm_vblank_cleanup(drm);
	component_unbind_all(dev, drm);
	drm_dev_unregister(drm);
	malidp_de_planes_destroy(drm);
	drm_mode_config_cleanup(drm);
	drm->dev_private = NULL;
	dev_set_drvdata(dev, NULL);
	clk_disable_unprepare(hwdev->mclk);
	clk_disable_unprepare(hwdev->aclk);
	clk_disable_unprepare(hwdev->pclk);
	drm_dev_unref(drm);
	of_reserved_mem_device_release(dev);
}

static const struct component_master_ops malidp_master_ops = {
	.bind = malidp_bind,
	.unbind = malidp_unbind,
};

static int malidp_compare_dev(struct device *dev, void *data)
{
	struct device_node *np = data;

	return dev->of_node == np;
}

static int malidp_platform_probe(struct platform_device *pdev)
{
	struct device_node *port, *ep;
	struct component_match *match = NULL;

	if (!pdev->dev.of_node)
		return -ENODEV;

	/* there is only one output port inside each device, find it */
	ep = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
	if (!ep)
		return -ENODEV;

	if (!of_device_is_available(ep)) {
		of_node_put(ep);
		return -ENODEV;
	}

	/* add the remote encoder port as component */
	port = of_graph_get_remote_port_parent(ep);
	of_node_put(ep);
	if (!port || !of_device_is_available(port)) {
		of_node_put(port);
		return -EAGAIN;
	}

	component_match_add(&pdev->dev, &match, malidp_compare_dev, port);
	return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
					       match);
}

static int malidp_platform_remove(struct platform_device *pdev)
{
	component_master_del(&pdev->dev, &malidp_master_ops);
	return 0;
}

static struct platform_driver malidp_platform_driver = {
	.probe		= malidp_platform_probe,
	.remove		= malidp_platform_remove,
	.driver	= {
		.name = "mali-dp",
		.of_match_table	= malidp_drm_of_match,
	},
};

module_platform_driver(malidp_platform_driver);

MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
MODULE_DESCRIPTION("ARM Mali DP DRM driver");
MODULE_LICENSE("GPL v2");
+54 −0
Original line number Diff line number Diff line
/*
 * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU licence.
 *
 * ARM Mali DP500/DP550/DP650 KMS/DRM driver structures
 */

#ifndef __MALIDP_DRV_H__
#define __MALIDP_DRV_H__

#include <linux/mutex.h>
#include <linux/wait.h>
#include "malidp_hw.h"

struct malidp_drm {
	struct malidp_hw_device *dev;
	struct drm_fbdev_cma *fbdev;
	struct list_head event_list;
	struct drm_crtc crtc;
	wait_queue_head_t wq;
	atomic_t config_valid;
};

#define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)

struct malidp_plane {
	struct drm_plane base;
	struct malidp_hw_device *hwdev;
	const struct malidp_layer *layer;
};

struct malidp_plane_state {
	struct drm_plane_state base;

	/* size of the required rotation memory if plane is rotated */
	u32 rotmem_size;
};

#define to_malidp_plane(x) container_of(x, struct malidp_plane, base)
#define to_malidp_plane_state(x) container_of(x, struct malidp_plane_state, base)

int malidp_de_planes_init(struct drm_device *drm);
void malidp_de_planes_destroy(struct drm_device *drm);
int malidp_crtc_init(struct drm_device *drm);

/* often used combination of rotational bits */
#define MALIDP_ROTATED_MASK	(BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))

#endif  /* __MALIDP_DRV_H__ */
Loading