sun4i_drv.c 8.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2015 Free Electrons
 * Copyright (C) 2015 NextThing Co
 *
 * Maxime Ripard <maxime.ripard@free-electrons.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

#include <linux/component.h>
#include <linux/of_graph.h>

#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
20
#include <drm/drm_fb_helper.h>
21
#include <drm/drm_of.h>
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

#include "sun4i_crtc.h"
#include "sun4i_drv.h"
#include "sun4i_framebuffer.h"
#include "sun4i_layer.h"

static const struct file_operations sun4i_drv_fops = {
	.owner		= THIS_MODULE,
	.open		= drm_open,
	.release	= drm_release,
	.unlocked_ioctl	= drm_ioctl,
	.compat_ioctl	= drm_compat_ioctl,
	.poll		= drm_poll,
	.read		= drm_read,
	.llseek		= no_llseek,
	.mmap		= drm_gem_cma_mmap,
};

static struct drm_driver sun4i_drv_driver = {
	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,

	/* Generic Operations */
	.fops			= &sun4i_drv_fops,
	.name			= "sun4i-drm",
	.desc			= "Allwinner sun4i Display Engine",
	.date			= "20150629",
	.major			= 1,
	.minor			= 0,

	/* GEM Operations */
	.dumb_create		= drm_gem_cma_dumb_create,
	.dumb_destroy		= drm_gem_dumb_destroy,
	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
55
	.gem_free_object_unlocked = drm_gem_cma_free_object,
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
	.gem_vm_ops		= &drm_gem_cma_vm_ops,

	/* PRIME Operations */
	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
	.gem_prime_import	= drm_gem_prime_import,
	.gem_prime_export	= drm_gem_prime_export,
	.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,

	/* Frame Buffer Operations */
};

72 73 74 75 76 77 78 79 80 81 82 83
static void sun4i_remove_framebuffers(void)
{
	struct apertures_struct *ap;

	ap = alloc_apertures(1);
	if (!ap)
		return;

	/* The framebuffer can be located anywhere in RAM */
	ap->ranges[0].base = 0;
	ap->ranges[0].size = ~0;

84
	drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
85 86 87
	kfree(ap);
}

88 89 90 91 92 93 94
static int sun4i_drv_bind(struct device *dev)
{
	struct drm_device *drm;
	struct sun4i_drv *drv;
	int ret;

	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
95 96
	if (IS_ERR(drm))
		return PTR_ERR(drm);
97 98 99 100 101 102 103 104

	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
	if (!drv) {
		ret = -ENOMEM;
		goto free_drm;
	}
	drm->dev_private = drv;

105 106 107 108 109
	/* drm_vblank_init calls kcalloc, which can fail */
	ret = drm_vblank_init(drm, 1);
	if (ret)
		goto free_drm;

110 111 112 113 114
	drm_mode_config_init(drm);

	ret = component_bind_all(drm->dev, drm);
	if (ret) {
		dev_err(drm->dev, "Couldn't bind all pipelines components\n");
115
		goto cleanup_mode_config;
116 117 118 119
	}

	/* Create our layers */
	drv->layers = sun4i_layers_init(drm);
C
Christophe JAILLET 已提交
120
	if (IS_ERR(drv->layers)) {
121
		dev_err(drm->dev, "Couldn't create the planes\n");
122
		ret = PTR_ERR(drv->layers);
123
		goto cleanup_mode_config;
124 125 126 127 128 129 130
	}

	/* Create our CRTC */
	drv->crtc = sun4i_crtc_init(drm);
	if (!drv->crtc) {
		dev_err(drm->dev, "Couldn't create the CRTC\n");
		ret = -EINVAL;
131
		goto cleanup_mode_config;
132 133 134
	}
	drm->irq_enabled = true;

135 136 137
	/* Remove early framebuffers (ie. simplefb) */
	sun4i_remove_framebuffers();

138 139 140 141 142
	/* Create our framebuffer */
	drv->fbdev = sun4i_framebuffer_init(drm);
	if (IS_ERR(drv->fbdev)) {
		dev_err(drm->dev, "Couldn't create our framebuffer\n");
		ret = PTR_ERR(drv->fbdev);
143
		goto cleanup_mode_config;
144 145 146 147 148 149 150
	}

	/* Enable connectors polling */
	drm_kms_helper_poll_init(drm);

	ret = drm_dev_register(drm, 0);
	if (ret)
151
		goto finish_poll;
152 153 154

	return 0;

155 156 157 158 159 160
finish_poll:
	drm_kms_helper_poll_fini(drm);
	sun4i_framebuffer_free(drm);
cleanup_mode_config:
	drm_mode_config_cleanup(drm);
	drm_vblank_cleanup(drm);
161 162 163 164 165 166 167 168 169 170 171 172
free_drm:
	drm_dev_unref(drm);
	return ret;
}

static void sun4i_drv_unbind(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);

	drm_dev_unregister(drm);
	drm_kms_helper_poll_fini(drm);
	sun4i_framebuffer_free(drm);
173
	drm_mode_config_cleanup(drm);
174 175 176 177 178 179 180 181 182 183 184
	drm_vblank_cleanup(drm);
	drm_dev_unref(drm);
}

static const struct component_master_ops sun4i_drv_master_ops = {
	.bind	= sun4i_drv_bind,
	.unbind	= sun4i_drv_unbind,
};

static bool sun4i_drv_node_is_frontend(struct device_node *node)
{
M
Maxime Ripard 已提交
185
	return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
186
		of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") ||
M
Maxime Ripard 已提交
187
		of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
188 189
}

M
Maxime Ripard 已提交
190 191
static bool sun4i_drv_node_is_tcon(struct device_node *node)
{
M
Maxime Ripard 已提交
192
	return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
193 194
		of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
		of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
M
Maxime Ripard 已提交
195
		of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
M
Maxime Ripard 已提交
196 197
}

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
static int compare_of(struct device *dev, void *data)
{
	DRM_DEBUG_DRIVER("Comparing of node %s with %s\n",
			 of_node_full_name(dev->of_node),
			 of_node_full_name(data));

	return dev->of_node == data;
}

static int sun4i_drv_add_endpoints(struct device *dev,
				   struct component_match **match,
				   struct device_node *node)
{
	struct device_node *port, *ep, *remote;
	int count = 0;

	/*
	 * We don't support the frontend for now, so we will never
	 * have a device bound. Just skip over it, but we still want
	 * the rest our pipeline to be added.
	 */
	if (!sun4i_drv_node_is_frontend(node) &&
	    !of_device_is_available(node))
		return 0;

	if (!sun4i_drv_node_is_frontend(node)) {
		/* Add current component */
		DRM_DEBUG_DRIVER("Adding component %s\n",
				 of_node_full_name(node));
227
		drm_of_component_match_add(dev, match, compare_of, node);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
		count++;
	}

	/* Inputs are listed first, then outputs */
	port = of_graph_get_port_by_id(node, 1);
	if (!port) {
		DRM_DEBUG_DRIVER("No output to bind\n");
		return count;
	}

	for_each_available_child_of_node(port, ep) {
		remote = of_graph_get_remote_port_parent(ep);
		if (!remote) {
			DRM_DEBUG_DRIVER("Error retrieving the output node\n");
			of_node_put(remote);
			continue;
		}

M
Maxime Ripard 已提交
246
		/*
M
Maxime Ripard 已提交
247 248
		 * If the node is our TCON, the first port is used for
		 * panel or bridges, and will not be part of the
M
Maxime Ripard 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
		 * component framework.
		 */
		if (sun4i_drv_node_is_tcon(node)) {
			struct of_endpoint endpoint;

			if (of_graph_parse_endpoint(ep, &endpoint)) {
				DRM_DEBUG_DRIVER("Couldn't parse endpoint\n");
				continue;
			}

			if (!endpoint.id) {
				DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n");
				continue;
			}
		}

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
		/* Walk down our tree */
		count += sun4i_drv_add_endpoints(dev, match, remote);

		of_node_put(remote);
	}

	return count;
}

static int sun4i_drv_probe(struct platform_device *pdev)
{
	struct component_match *match = NULL;
	struct device_node *np = pdev->dev.of_node;
	int i, count = 0;

	for (i = 0;; i++) {
		struct device_node *pipeline = of_parse_phandle(np,
								"allwinner,pipelines",
								i);
		if (!pipeline)
			break;

		count += sun4i_drv_add_endpoints(&pdev->dev, &match,
						pipeline);
289
		of_node_put(pipeline);
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

		DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
				 count, i);
	}

	if (count)
		return component_master_add_with_match(&pdev->dev,
						       &sun4i_drv_master_ops,
						       match);
	else
		return 0;
}

static int sun4i_drv_remove(struct platform_device *pdev)
{
	return 0;
}

static const struct of_device_id sun4i_drv_of_table[] = {
	{ .compatible = "allwinner,sun5i-a13-display-engine" },
310 311
	{ .compatible = "allwinner,sun6i-a31-display-engine" },
	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
M
Maxime Ripard 已提交
312
	{ .compatible = "allwinner,sun8i-a33-display-engine" },
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
	{ }
};
MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);

static struct platform_driver sun4i_drv_platform_driver = {
	.probe		= sun4i_drv_probe,
	.remove		= sun4i_drv_remove,
	.driver		= {
		.name		= "sun4i-drm",
		.of_match_table	= sun4i_drv_of_table,
	},
};
module_platform_driver(sun4i_drv_platform_driver);

MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Allwinner A10 Display Engine DRM/KMS Driver");
MODULE_LICENSE("GPL");